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

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

Webmaster