Annotation of libwww/Library/src/HTAnchor.c, revision 1.6

1.1       timbl       1: /*     Hypertext "Anchor" Object                               HTAnchor.c
                      2: **     ==========================
                      3: **
                      4: ** An anchor represents a region of a hypertext document which is linked to
                      5: ** another anchor in the same or a different document.
                      6: **
                      7: ** History
                      8: **
                      9: **         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
                     10: **     24-Oct-1991 (JFG), written in C, browser-independant 
                     11: **     21-Nov-1991 (JFG), first complete version
                     12: **
                     13: **     (c) Copyright CERN 1991 - See Copyright.html
                     14: */
                     15: 
                     16: #define HASH_SIZE 101          /* Arbitrary prime. Memory/speed tradeoff */
                     17: 
                     18: #include <ctype.h>
                     19: #include "tcp.h"
                     20: #include "HTAnchor.h"
1.6     ! timbl      21: 
1.1       timbl      22: #include "HTUtils.h"
                     23: #include "HTParse.h"
1.6     ! timbl      24: #include "HTFWriter.h" /* for cache stuff */
1.1       timbl      25: 
                     26: typedef struct _HyperDoc Hyperdoc;
1.5       duns       27: #ifdef VMS
1.1       timbl      28: struct _HyperDoc {
                     29:        int junk;       /* VMS cannot handle pointers to undefined structs */
                     30: };
                     31: #endif
                     32: 
                     33: PRIVATE HTList **adult_table=0;  /* Point to table of lists of all parents */
                     34: 
                     35: /*                             Creation Methods
                     36: **                             ================
                     37: **
                     38: **     Do not use "new" by itself outside this module. In order to enforce
                     39: **     consistency, we insist that you furnish more information about the
                     40: **     anchor you are creating : use newWithParent or newWithAddress.
                     41: */
                     42: 
                     43: PRIVATE HTParentAnchor * HTParentAnchor_new
                     44:   NOARGS
                     45: {
                     46:   HTParentAnchor *newAnchor = 
                     47:     (HTParentAnchor *) calloc (1, sizeof (HTParentAnchor));  /* zero-filled */
                     48:   newAnchor->parent = newAnchor;
                     49:   return newAnchor;
                     50: }
                     51: 
                     52: PRIVATE HTChildAnchor * HTChildAnchor_new
                     53:   NOARGS
                     54: {
                     55:   return (HTChildAnchor *) calloc (1, sizeof (HTChildAnchor));  /* zero-filled */
                     56: }
                     57: 
                     58: 
                     59: /*     Case insensitive string comparison
                     60: **     ----------------------------------
                     61: ** On entry,
                     62: **     s       Points to one string, null terminated
                     63: **     t       points to the other.
                     64: ** On exit,
                     65: **     returns YES if the strings are equivalent ignoring case
                     66: **             NO if they differ in more than  their case.
                     67: */
                     68: 
                     69: PRIVATE BOOL equivalent
                     70:   ARGS2 (CONST char *,s, CONST char *,t)
                     71: {
                     72:   if (s && t) {  /* Make sure they point to something */
                     73:     for ( ; *s && *t ; s++, t++) {
                     74:         if (TOUPPER(*s) != TOUPPER(*t))
                     75:          return NO;
                     76:     }
                     77:     return TOUPPER(*s) == TOUPPER(*t);
                     78:   } else
                     79:     return s == t;  /* Two NULLs are equivalent, aren't they ? */
                     80: }
                     81: 
                     82: 
                     83: /*     Create new or find old sub-anchor
                     84: **     ---------------------------------
                     85: **
1.3       timbl      86: **     Me one is for a new anchor being edited into an existing
1.1       timbl      87: **     document. The parent anchor must already exist.
                     88: */
                     89: 
1.2       timbl      90: PUBLIC HTChildAnchor * HTAnchor_findChild
1.1       timbl      91:   ARGS2 (HTParentAnchor *,parent, CONST char *,tag)
                     92: {
                     93:   HTChildAnchor *child;
                     94:   HTList *kids;
                     95: 
                     96:   if (! parent) {
                     97:     if (TRACE) printf ("HTAnchor_findChild called with NULL parent.\n");
                     98:     return NULL;
                     99:   }
                    100:   if (kids = parent->children) {  /* parent has children : search them */
                    101:     if (tag && *tag) {         /* TBL */
                    102:        while (child = HTList_nextObject (kids)) {
                    103:            if (equivalent(child->tag, tag)) { /* Case sensitive 920226 */
1.4       timbl     104:                if (TRACE) fprintf (stderr,
1.1       timbl     105:               "Child anchor %p of parent %p with name `%s' already exists.\n",
1.4       timbl     106:                    (void*)child, (void*)parent, tag);
1.1       timbl     107:                return child;
                    108:            }
                    109:        }
                    110:      }  /*  end if tag is void */
                    111:   } else  /* parent doesn't have any children yet : create family */
                    112:     parent->children = HTList_new ();
                    113: 
                    114:   child = HTChildAnchor_new ();
                    115:   if (TRACE) fprintf(stderr, "new Anchor %p named `%s' is child of %p\n",
1.4       timbl     116:        (void*)child, (int)tag ? tag : (CONST char *)"" , (void*)parent); /* int for apollo */
1.1       timbl     117:   HTList_addObject (parent->children, child);
                    118:   child->parent = parent;
                    119:   StrAllocCopy(child->tag, tag);
                    120:   return child;
                    121: }
                    122: 
                    123: 
                    124: /*     Create or find a child anchor with a possible link
                    125: **     --------------------------------------------------
                    126: **
                    127: **     Create new anchor with a given parent and possibly
                    128: **     a name, and possibly a link to a _relatively_ named anchor.
                    129: **     (Code originally in ParseHTML.h)
                    130: */
                    131: PUBLIC HTChildAnchor * HTAnchor_findChildAndLink
                    132:   ARGS4(
                    133:        HTParentAnchor *,parent,        /* May not be 0 */
                    134:        CONST char *,tag,       /* May be "" or 0 */
                    135:        CONST char *,href,      /* May be "" or 0 */
                    136:        HTLinkType *,ltype      /* May be 0 */
                    137:        )
                    138: {
                    139:   HTChildAnchor * child = HTAnchor_findChild(parent, tag);
                    140:   if (href && *href) {
1.2       timbl     141:     char * relative_to = HTAnchor_address((HTAnchor *) parent);
                    142:     char * parsed_address = HTParse(href, relative_to, PARSE_ALL);
                    143:     HTAnchor * dest = HTAnchor_findAddress(parsed_address);
1.1       timbl     144:     HTAnchor_link((HTAnchor *) child, dest, ltype);
                    145:     free(parsed_address);
1.2       timbl     146:     free(relative_to);
1.1       timbl     147:   }
                    148:   return child;
                    149: }
                    150: 
                    151: 
                    152: /*     Create new or find old named anchor
                    153: **     -----------------------------------
                    154: **
1.3       timbl     155: **     Me one is for a reference which is found in a document, and might
1.1       timbl     156: **     not be already loaded.
                    157: **     Note: You are not guaranteed a new anchor -- you might get an old one,
                    158: **     like with fonts.
                    159: */
                    160: 
                    161: HTAnchor * HTAnchor_findAddress
                    162:   ARGS1 (CONST char *,address)
                    163: {
                    164:   char *tag = HTParse (address, "", PARSE_ANCHOR);  /* Anchor tag specified ? */
                    165: 
                    166:   /* If the address represents a sub-anchor, we recursively load its parent,
                    167:      then we create a child anchor within that document. */
                    168:   if (*tag) {
                    169:     char *docAddress = HTParse(address, "", PARSE_ACCESS | PARSE_HOST |
                    170:                                            PARSE_PATH | PARSE_PUNCTUATION);
                    171:     HTParentAnchor * foundParent =
                    172:       (HTParentAnchor *) HTAnchor_findAddress (docAddress);
                    173:     HTChildAnchor * foundAnchor = HTAnchor_findChild (foundParent, tag);
                    174:     free (docAddress);
                    175:     free (tag);
                    176:     return (HTAnchor *) foundAnchor;
                    177:   }
                    178:   
1.3       timbl     179:   else { /* If the address has no anchor tag, 
                    180:            check whether we have this node */
1.1       timbl     181:     int hash;
                    182:     CONST char *p;
                    183:     HTList * adults;
                    184:     HTList *grownups;
                    185:     HTParentAnchor * foundAnchor;
                    186: 
                    187:     free (tag);
                    188:     
                    189:     /* Select list from hash table */
1.2       timbl     190:     for(p=address, hash=0; *p; p++)
                    191:        hash = (hash * 3 + (*(unsigned char*)p))
                    192:         % HASH_SIZE;
1.1       timbl     193:     if (!adult_table)
                    194:         adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
                    195:     if (!adult_table[hash]) adult_table[hash] = HTList_new();
                    196:     adults = adult_table[hash];
                    197: 
                    198:     /* Search list for anchor */
                    199:     grownups = adults;
                    200:     while (foundAnchor = HTList_nextObject (grownups)) {
1.2       timbl     201:        if (equivalent(foundAnchor->address, address)) {
1.1       timbl     202:        if (TRACE) fprintf(stderr, "Anchor %p with address `%s' already exists.\n",
1.4       timbl     203:                          (void*) foundAnchor, address);
1.1       timbl     204:        return (HTAnchor *) foundAnchor;
                    205:       }
                    206:     }
                    207:     
                    208:     /* Node not found : create new anchor */
                    209:     foundAnchor = HTParentAnchor_new ();
                    210:     if (TRACE) fprintf(stderr, "New anchor %p has hash %d and address `%s'\n",
1.4       timbl     211:        (void*)foundAnchor, hash, address);
1.1       timbl     212:     StrAllocCopy(foundAnchor->address, address);
                    213:     HTList_addObject (adults, foundAnchor);
                    214:     return (HTAnchor *) foundAnchor;
                    215:   }
                    216: }
                    217: 
                    218: 
                    219: /*     Delete an anchor and possibly related things (auto garbage collection)
                    220: **     --------------------------------------------
                    221: **
                    222: **     The anchor is only deleted if the corresponding document is not loaded.
                    223: **     All outgoing links from parent and children are deleted, and this anchor
                    224: **     is removed from the sources list of all its targets.
                    225: **     We also try to delete the targets whose documents are not loaded.
                    226: **     If this anchor's source list is empty, we delete it and its children.
                    227: */
                    228: 
                    229: PRIVATE void deleteLinks
1.3       timbl     230:   ARGS1 (HTAnchor *,me)
1.1       timbl     231: {
1.3       timbl     232:   if (! me)
1.1       timbl     233:     return;
                    234: 
                    235:   /* Recursively try to delete target anchors */
1.3       timbl     236:   if (me->mainLink.dest) {
                    237:     HTParentAnchor *parent = me->mainLink.dest->parent;
                    238:     HTList_removeObject (parent->sources, me);
1.1       timbl     239:     if (! parent->document)  /* Test here to avoid calling overhead */
                    240:       HTAnchor_delete (parent);
                    241:   }
1.3       timbl     242:   if (me->links) {  /* Extra destinations */
1.1       timbl     243:     HTLink *target;
1.3       timbl     244:     while (target = HTList_removeLastObject (me->links)) {
1.1       timbl     245:       HTParentAnchor *parent = target->dest->parent;
1.3       timbl     246:       HTList_removeObject (parent->sources, me);
1.1       timbl     247:       if (! parent->document)  /* Test here to avoid calling overhead */
                    248:        HTAnchor_delete (parent);
                    249:     }
                    250:   }
                    251: }
                    252: 
                    253: PUBLIC BOOL HTAnchor_delete
1.3       timbl     254:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     255: {
                    256:   HTChildAnchor *child;
                    257: 
                    258:   /* Don't delete if document is loaded */
1.3       timbl     259:   if (me->document)
1.1       timbl     260:     return NO;
                    261: 
                    262:   /* Recursively try to delete target anchors */
1.3       timbl     263:   deleteLinks ((HTAnchor *) me);
1.1       timbl     264: 
1.3       timbl     265:   if (! HTList_isEmpty (me->sources)) {  /* There are still incoming links */
1.1       timbl     266:     /* Delete all outgoing links from children, if any */
1.3       timbl     267:     HTList *kids = me->children;
1.1       timbl     268:     while (child = HTList_nextObject (kids))
                    269:       deleteLinks ((HTAnchor *) child);
                    270:     return NO;  /* Parent not deleted */
                    271:   }
                    272: 
                    273:   /* No more incoming links : kill everything */
                    274:   /* First, recursively delete children */
1.3       timbl     275:   while (child = HTList_removeLastObject (me->children)) {
1.1       timbl     276:     deleteLinks ((HTAnchor *) child);
                    277:     free (child->tag);
                    278:     free (child);
                    279:   }
                    280: 
                    281:   /* Now kill myself */
1.3       timbl     282:   HTList_delete (me->children);
                    283:   HTList_delete (me->sources);
                    284:   free (me->address);
1.1       timbl     285:   /* Devise a way to clean out the HTFormat if no longer needed (ref count?) */
1.3       timbl     286:   free (me);
1.6     ! timbl     287:   if (me->cacheItems) {
        !           288:       HTCacheClear(me->cacheItems);
        !           289:   }
1.1       timbl     290:   return YES;  /* Parent deleted */
                    291: }
                    292: 
                    293: 
                    294: /*             Move an anchor to the head of the list of its siblings
                    295: **             ------------------------------------------------------
                    296: **
                    297: **     This is to ensure that an anchor which might have already existed
                    298: **     is put in the correct order as we load the document.
                    299: */
                    300: 
                    301: void HTAnchor_makeLastChild
1.3       timbl     302:   ARGS1(HTChildAnchor *,me)
1.1       timbl     303: {
1.3       timbl     304:   if (me->parent != (HTParentAnchor *) me) {  /* Make sure it's a child */
                    305:     HTList * siblings = me->parent->children;
                    306:     HTList_removeObject (siblings, me);
                    307:     HTList_addObject (siblings, me);
1.1       timbl     308:   }
                    309: }
                    310: 
                    311: /*     Data access functions
                    312: **     ---------------------
                    313: */
                    314: 
                    315: PUBLIC HTParentAnchor * HTAnchor_parent
1.3       timbl     316:   ARGS1 (HTAnchor *,me)
1.1       timbl     317: {
1.3       timbl     318:   return me ? me->parent : NULL;
1.1       timbl     319: }
                    320: 
                    321: void HTAnchor_setDocument
1.3       timbl     322:   ARGS2 (HTParentAnchor *,me, HyperDoc *,doc)
1.1       timbl     323: {
1.3       timbl     324:   if (me)
                    325:     me->document = doc;
1.1       timbl     326: }
                    327: 
                    328: HyperDoc * HTAnchor_document
1.3       timbl     329:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     330: {
1.3       timbl     331:   return me ? me->document : NULL;
1.1       timbl     332: }
                    333: 
                    334: 
                    335: /* We don't want code to change an address after anchor creation... yet ?
                    336: void HTAnchor_setAddress
1.3       timbl     337:   ARGS2 (HTAnchor *,me, char *,addr)
1.1       timbl     338: {
1.3       timbl     339:   if (me)
                    340:     StrAllocCopy (me->parent->address, addr);
1.1       timbl     341: }
                    342: */
                    343: 
                    344: char * HTAnchor_address
1.3       timbl     345:   ARGS1 (HTAnchor *,me)
1.1       timbl     346: {
                    347:   char *addr = NULL;
1.3       timbl     348:   if (me) {
                    349:     if (((HTParentAnchor *) me == me->parent) ||
                    350:        !((HTChildAnchor *) me)->tag) {  /* it's an adult or no tag */
                    351:       StrAllocCopy (addr, me->parent->address);
1.1       timbl     352:     }
                    353:     else {  /* it's a named child */
1.3       timbl     354:       addr = malloc (2 + strlen (me->parent->address)
                    355:                     + strlen (((HTChildAnchor *) me)->tag));
1.1       timbl     356:       if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
1.3       timbl     357:       sprintf (addr, "%s#%s", me->parent->address,
                    358:               ((HTChildAnchor *) me)->tag);
1.1       timbl     359:     }
                    360:   }
                    361:   return addr;
                    362: }
                    363: 
                    364: 
                    365: 
                    366: void HTAnchor_setFormat
1.3       timbl     367:   ARGS2 (HTParentAnchor *,me, HTFormat ,form)
1.1       timbl     368: {
1.3       timbl     369:   if (me)
                    370:     me->format = form;
1.1       timbl     371: }
                    372: 
1.2       timbl     373: HTFormat HTAnchor_format
1.3       timbl     374:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     375: {
1.3       timbl     376:   return me ? me->format : NULL;
1.1       timbl     377: }
                    378: 
                    379: 
                    380: 
                    381: void HTAnchor_setIndex
1.3       timbl     382:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     383: {
1.3       timbl     384:   if (me)
                    385:     me->isIndex = YES;
1.1       timbl     386: }
                    387: 
                    388: BOOL HTAnchor_isIndex
1.3       timbl     389:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     390: {
1.3       timbl     391:   return me ? me->isIndex : NO;
1.1       timbl     392: }
                    393: 
                    394: 
                    395: 
                    396: BOOL HTAnchor_hasChildren
1.3       timbl     397:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     398: {
1.3       timbl     399:   return me ? ! HTList_isEmpty(me->children) : NO;
1.1       timbl     400: }
                    401: 
                    402: /*     Title handling
                    403: */
                    404: CONST char * HTAnchor_title
1.3       timbl     405:   ARGS1 (HTParentAnchor *,me)
1.1       timbl     406: {
1.3       timbl     407:   return me ? me->title : 0;
1.1       timbl     408: }
                    409: 
                    410: void HTAnchor_setTitle
1.3       timbl     411:   ARGS2(HTParentAnchor *,me, CONST char *,title)
1.1       timbl     412: {
1.3       timbl     413:   StrAllocCopy(me->title, title);
1.1       timbl     414: }
                    415: 
                    416: void HTAnchor_appendTitle
1.3       timbl     417:   ARGS2(HTParentAnchor *,me, CONST char *,title)
1.1       timbl     418: {
1.3       timbl     419:   StrAllocCat(me->title, title);
1.1       timbl     420: }
                    421: 
1.3       timbl     422: /*     Link me Anchor to another given one
1.1       timbl     423: **     -------------------------------------
                    424: */
                    425: 
                    426: BOOL HTAnchor_link
                    427:   ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
                    428: {
                    429:   if (! (source && destination))
                    430:     return NO;  /* Can't link to/from non-existing anchor */
                    431:   if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
                    432:   if (! source->mainLink.dest) {
                    433:     source->mainLink.dest = destination;
                    434:     source->mainLink.type = type;
                    435:   } else {
                    436:     HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
                    437:     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
                    438:     newLink->dest = destination;
                    439:     newLink->type = type;
                    440:     if (! source->links)
                    441:       source->links = HTList_new ();
                    442:     HTList_addObject (source->links, newLink);
                    443:   }
                    444:   if (!destination->parent->sources)
                    445:     destination->parent->sources = HTList_new ();
                    446:   HTList_addObject (destination->parent->sources, source);
                    447:   return YES;  /* Success */
                    448: }
                    449: 
                    450: 
                    451: /*     Manipulation of links
                    452: **     ---------------------
                    453: */
                    454: 
                    455: HTAnchor * HTAnchor_followMainLink
1.3       timbl     456:   ARGS1 (HTAnchor *,me)
1.1       timbl     457: {
1.3       timbl     458:   return me->mainLink.dest;
1.1       timbl     459: }
                    460: 
                    461: HTAnchor * HTAnchor_followTypedLink
1.3       timbl     462:   ARGS2 (HTAnchor *,me, HTLinkType *,type)
1.1       timbl     463: {
1.3       timbl     464:   if (me->mainLink.type == type)
                    465:     return me->mainLink.dest;
                    466:   if (me->links) {
                    467:     HTList *links = me->links;
1.1       timbl     468:     HTLink *link;
                    469:     while (link = HTList_nextObject (links))
                    470:       if (link->type == type)
                    471:        return link->dest;
                    472:   }
1.3       timbl     473:   return NULL;  /* No link of me type */
1.1       timbl     474: }
                    475: 
1.2       timbl     476: 
                    477: /*     Make main link
                    478: */
1.1       timbl     479: BOOL HTAnchor_makeMainLink
1.3       timbl     480:   ARGS2 (HTAnchor *,me, HTLink *,movingLink)
1.1       timbl     481: {
                    482:   /* Check that everything's OK */
1.3       timbl     483:   if (! (me && HTList_removeObject (me->links, movingLink)))
1.1       timbl     484:     return NO;  /* link not found or NULL anchor */
                    485:   else {
                    486:     /* First push current main link onto top of links list */
                    487:     HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
                    488:     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
1.3       timbl     489:     memcpy (newLink, & me->mainLink, sizeof (HTLink));
                    490:     HTList_addObject (me->links, newLink);
1.1       timbl     491: 
                    492:     /* Now make movingLink the new main link, and free it */
1.3       timbl     493:     memcpy (& me->mainLink, movingLink, sizeof (HTLink));
1.1       timbl     494:     free (movingLink);
                    495:     return YES;
                    496:   }
1.2       timbl     497: }
                    498: 
                    499: 
                    500: /*     Methods List
                    501: **     ------------
                    502: */
                    503: 
1.3       timbl     504: PUBLIC HTList * HTAnchor_methods ARGS1(HTParentAnchor *, me)
1.2       timbl     505: {
1.3       timbl     506:     if (!me->methods) {
                    507:         me->methods = HTList_new();
1.2       timbl     508:     }
1.3       timbl     509:     return me->methods;
1.2       timbl     510: }
                    511: 
                    512: /*     Protocol
                    513: **     --------
                    514: */
                    515: 
1.3       timbl     516: PUBLIC void * HTAnchor_protocol ARGS1(HTParentAnchor *, me)
1.2       timbl     517: {
1.3       timbl     518:     return me->protocol;
1.2       timbl     519: }
                    520: 
1.3       timbl     521: PUBLIC void HTAnchor_setProtocol ARGS2(HTParentAnchor *, me,
1.2       timbl     522:        void*,  protocol)
                    523: {
1.3       timbl     524:     me->protocol = protocol;
1.2       timbl     525: }
                    526: 
                    527: /*     Physical Address
                    528: **     ----------------
                    529: */
                    530: 
1.3       timbl     531: PUBLIC char * HTAnchor_physical ARGS1(HTParentAnchor *, me)
1.2       timbl     532: {
1.3       timbl     533:     return me->physical;
1.2       timbl     534: }
                    535: 
1.3       timbl     536: PUBLIC void HTAnchor_setPhysical ARGS2(HTParentAnchor *, me,
1.2       timbl     537:        char *, physical)
                    538: {
1.3       timbl     539:     StrAllocCopy(me->physical, physical);
1.1       timbl     540: }

Webmaster