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

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: 
        !            88: PRIVATE HTChildAnchor * HTAnchor_findChild
        !            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) {
        !           139:     char * parsed_address;
        !           140:     HTAnchor * dest;
        !           141:     parsed_address = HTParse(href, HTAnchor_address((HTAnchor *) parent),
        !           142:                             PARSE_ALL);
        !           143:     dest = HTAnchor_findAddress(parsed_address);
        !           144:     HTAnchor_link((HTAnchor *) child, dest, ltype);
        !           145:     free(parsed_address);
        !           146:   }
        !           147:   return child;
        !           148: }
        !           149: 
        !           150: 
        !           151: /*     Create new or find old named anchor
        !           152: **     -----------------------------------
        !           153: **
        !           154: **     This one is for a reference which is found in a document, and might
        !           155: **     not be already loaded.
        !           156: **     Note: You are not guaranteed a new anchor -- you might get an old one,
        !           157: **     like with fonts.
        !           158: */
        !           159: 
        !           160: HTAnchor * HTAnchor_findAddress
        !           161:   ARGS1 (CONST char *,address)
        !           162: {
        !           163:   char *tag = HTParse (address, "", PARSE_ANCHOR);  /* Anchor tag specified ? */
        !           164: 
        !           165:   /* If the address represents a sub-anchor, we recursively load its parent,
        !           166:      then we create a child anchor within that document. */
        !           167:   if (*tag) {
        !           168:     char *docAddress = HTParse(address, "", PARSE_ACCESS | PARSE_HOST |
        !           169:                                            PARSE_PATH | PARSE_PUNCTUATION);
        !           170:     HTParentAnchor * foundParent =
        !           171:       (HTParentAnchor *) HTAnchor_findAddress (docAddress);
        !           172:     HTChildAnchor * foundAnchor = HTAnchor_findChild (foundParent, tag);
        !           173:     free (docAddress);
        !           174:     free (tag);
        !           175:     return (HTAnchor *) foundAnchor;
        !           176:   }
        !           177:   
        !           178:   else { /* If the address has no anchor tag, check whether we have this node */
        !           179:     int hash;
        !           180:     CONST char *p;
        !           181:     HTList * adults;
        !           182:     HTList *grownups;
        !           183:     HTParentAnchor * foundAnchor;
        !           184: 
        !           185:     free (tag);
        !           186:     
        !           187:     /* Select list from hash table */
        !           188:     for(p=address, hash=0; *p; p++) hash = (hash * 3 + *p) % HASH_SIZE;
        !           189:     if (!adult_table)
        !           190:         adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
        !           191:     if (!adult_table[hash]) adult_table[hash] = HTList_new();
        !           192:     adults = adult_table[hash];
        !           193: 
        !           194:     /* Search list for anchor */
        !           195:     grownups = adults;
        !           196:     while (foundAnchor = HTList_nextObject (grownups)) {
        !           197:       if (equivalent(foundAnchor->address, address)) {
        !           198:        if (TRACE) fprintf(stderr, "Anchor %p with address `%s' already exists.\n",
        !           199:                          foundAnchor, address);
        !           200:        return (HTAnchor *) foundAnchor;
        !           201:       }
        !           202:     }
        !           203:     
        !           204:     /* Node not found : create new anchor */
        !           205:     foundAnchor = HTParentAnchor_new ();
        !           206:     if (TRACE) fprintf(stderr, "New anchor %p has hash %d and address `%s'\n",
        !           207:        foundAnchor, hash, address);
        !           208:     StrAllocCopy(foundAnchor->address, address);
        !           209:     HTList_addObject (adults, foundAnchor);
        !           210:     return (HTAnchor *) foundAnchor;
        !           211:   }
        !           212: }
        !           213: 
        !           214: 
        !           215: /*     Delete an anchor and possibly related things (auto garbage collection)
        !           216: **     --------------------------------------------
        !           217: **
        !           218: **     The anchor is only deleted if the corresponding document is not loaded.
        !           219: **     All outgoing links from parent and children are deleted, and this anchor
        !           220: **     is removed from the sources list of all its targets.
        !           221: **     We also try to delete the targets whose documents are not loaded.
        !           222: **     If this anchor's source list is empty, we delete it and its children.
        !           223: */
        !           224: 
        !           225: PRIVATE void deleteLinks
        !           226:   ARGS1 (HTAnchor *,this)
        !           227: {
        !           228:   if (! this)
        !           229:     return;
        !           230: 
        !           231:   /* Recursively try to delete target anchors */
        !           232:   if (this->mainLink.dest) {
        !           233:     HTParentAnchor *parent = this->mainLink.dest->parent;
        !           234:     HTList_removeObject (parent->sources, this);
        !           235:     if (! parent->document)  /* Test here to avoid calling overhead */
        !           236:       HTAnchor_delete (parent);
        !           237:   }
        !           238:   if (this->links) {  /* Extra destinations */
        !           239:     HTLink *target;
        !           240:     while (target = HTList_removeLastObject (this->links)) {
        !           241:       HTParentAnchor *parent = target->dest->parent;
        !           242:       HTList_removeObject (parent->sources, this);
        !           243:       if (! parent->document)  /* Test here to avoid calling overhead */
        !           244:        HTAnchor_delete (parent);
        !           245:     }
        !           246:   }
        !           247: }
        !           248: 
        !           249: PUBLIC BOOL HTAnchor_delete
        !           250:   ARGS1 (HTParentAnchor *,this)
        !           251: {
        !           252:   HTChildAnchor *child;
        !           253: 
        !           254:   /* Don't delete if document is loaded */
        !           255:   if (this->document)
        !           256:     return NO;
        !           257: 
        !           258:   /* Recursively try to delete target anchors */
        !           259:   deleteLinks ((HTAnchor *) this);
        !           260: 
        !           261:   if (! HTList_isEmpty (this->sources)) {  /* There are still incoming links */
        !           262:     /* Delete all outgoing links from children, if any */
        !           263:     HTList *kids = this->children;
        !           264:     while (child = HTList_nextObject (kids))
        !           265:       deleteLinks ((HTAnchor *) child);
        !           266:     return NO;  /* Parent not deleted */
        !           267:   }
        !           268: 
        !           269:   /* No more incoming links : kill everything */
        !           270:   /* First, recursively delete children */
        !           271:   while (child = HTList_removeLastObject (this->children)) {
        !           272:     deleteLinks ((HTAnchor *) child);
        !           273:     free (child->tag);
        !           274:     free (child);
        !           275:   }
        !           276: 
        !           277:   /* Now kill myself */
        !           278:   HTList_delete (this->children);
        !           279:   HTList_delete (this->sources);
        !           280:   free (this->address);
        !           281:   /* Devise a way to clean out the HTFormat if no longer needed (ref count?) */
        !           282:   free (this);
        !           283:   return YES;  /* Parent deleted */
        !           284: }
        !           285: 
        !           286: 
        !           287: /*             Move an anchor to the head of the list of its siblings
        !           288: **             ------------------------------------------------------
        !           289: **
        !           290: **     This is to ensure that an anchor which might have already existed
        !           291: **     is put in the correct order as we load the document.
        !           292: */
        !           293: 
        !           294: void HTAnchor_makeLastChild
        !           295:   ARGS1(HTChildAnchor *,this)
        !           296: {
        !           297:   if (this->parent != (HTParentAnchor *) this) {  /* Make sure it's a child */
        !           298:     HTList * siblings = this->parent->children;
        !           299:     HTList_removeObject (siblings, this);
        !           300:     HTList_addObject (siblings, this);
        !           301:   }
        !           302: }
        !           303: 
        !           304: /*     Data access functions
        !           305: **     ---------------------
        !           306: */
        !           307: 
        !           308: PUBLIC HTParentAnchor * HTAnchor_parent
        !           309:   ARGS1 (HTAnchor *,this)
        !           310: {
        !           311:   return this ? this->parent : NULL;
        !           312: }
        !           313: 
        !           314: void HTAnchor_setDocument
        !           315:   ARGS2 (HTParentAnchor *,this, HyperDoc *,doc)
        !           316: {
        !           317:   if (this)
        !           318:     this->document = doc;
        !           319: }
        !           320: 
        !           321: HyperDoc * HTAnchor_document
        !           322:   ARGS1 (HTParentAnchor *,this)
        !           323: {
        !           324:   return this ? this->document : NULL;
        !           325: }
        !           326: 
        !           327: 
        !           328: /* We don't want code to change an address after anchor creation... yet ?
        !           329: void HTAnchor_setAddress
        !           330:   ARGS2 (HTAnchor *,this, char *,addr)
        !           331: {
        !           332:   if (this)
        !           333:     StrAllocCopy (this->parent->address, addr);
        !           334: }
        !           335: */
        !           336: 
        !           337: char * HTAnchor_address
        !           338:   ARGS1 (HTAnchor *,this)
        !           339: {
        !           340:   char *addr = NULL;
        !           341:   if (this) {
        !           342:     if (((HTParentAnchor *) this == this->parent) ||
        !           343:        !((HTChildAnchor *) this)->tag) {  /* it's an adult or no tag */
        !           344:       StrAllocCopy (addr, this->parent->address);
        !           345:     }
        !           346:     else {  /* it's a named child */
        !           347:       addr = malloc (2 + strlen (this->parent->address)
        !           348:                     + strlen (((HTChildAnchor *) this)->tag));
        !           349:       if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
        !           350:       sprintf (addr, "%s#%s", this->parent->address,
        !           351:               ((HTChildAnchor *) this)->tag);
        !           352:     }
        !           353:   }
        !           354:   return addr;
        !           355: }
        !           356: 
        !           357: 
        !           358: 
        !           359: void HTAnchor_setFormat
        !           360:   ARGS2 (HTParentAnchor *,this, HTFormat *,form)
        !           361: {
        !           362:   if (this)
        !           363:     this->format = form;
        !           364: }
        !           365: 
        !           366: HTFormat * HTAnchor_format
        !           367:   ARGS1 (HTParentAnchor *,this)
        !           368: {
        !           369:   return this ? this->format : NULL;
        !           370: }
        !           371: 
        !           372: 
        !           373: 
        !           374: void HTAnchor_setIndex
        !           375:   ARGS1 (HTParentAnchor *,this)
        !           376: {
        !           377:   if (this)
        !           378:     this->isIndex = YES;
        !           379: }
        !           380: 
        !           381: BOOL HTAnchor_isIndex
        !           382:   ARGS1 (HTParentAnchor *,this)
        !           383: {
        !           384:   return this ? this->isIndex : NO;
        !           385: }
        !           386: 
        !           387: 
        !           388: 
        !           389: BOOL HTAnchor_hasChildren
        !           390:   ARGS1 (HTParentAnchor *,this)
        !           391: {
        !           392:   return this ? ! HTList_isEmpty(this->children) : NO;
        !           393: }
        !           394: 
        !           395: /*     Title handling
        !           396: */
        !           397: CONST char * HTAnchor_title
        !           398:   ARGS1 (HTParentAnchor *,this)
        !           399: {
        !           400:   return this ? this->title : 0;
        !           401: }
        !           402: 
        !           403: void HTAnchor_setTitle
        !           404:   ARGS2(HTParentAnchor *,this, CONST char *,title)
        !           405: {
        !           406:   StrAllocCopy(this->title, title);
        !           407: }
        !           408: 
        !           409: void HTAnchor_appendTitle
        !           410:   ARGS2(HTParentAnchor *,this, CONST char *,title)
        !           411: {
        !           412:   StrAllocCat(this->title, title);
        !           413: }
        !           414: 
        !           415: /*     Link this Anchor to another given one
        !           416: **     -------------------------------------
        !           417: */
        !           418: 
        !           419: BOOL HTAnchor_link
        !           420:   ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
        !           421: {
        !           422:   if (! (source && destination))
        !           423:     return NO;  /* Can't link to/from non-existing anchor */
        !           424:   if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
        !           425:   if (! source->mainLink.dest) {
        !           426:     source->mainLink.dest = destination;
        !           427:     source->mainLink.type = type;
        !           428:   } else {
        !           429:     HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
        !           430:     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
        !           431:     newLink->dest = destination;
        !           432:     newLink->type = type;
        !           433:     if (! source->links)
        !           434:       source->links = HTList_new ();
        !           435:     HTList_addObject (source->links, newLink);
        !           436:   }
        !           437:   if (!destination->parent->sources)
        !           438:     destination->parent->sources = HTList_new ();
        !           439:   HTList_addObject (destination->parent->sources, source);
        !           440:   return YES;  /* Success */
        !           441: }
        !           442: 
        !           443: 
        !           444: /*     Manipulation of links
        !           445: **     ---------------------
        !           446: */
        !           447: 
        !           448: HTAnchor * HTAnchor_followMainLink
        !           449:   ARGS1 (HTAnchor *,this)
        !           450: {
        !           451:   return this->mainLink.dest;
        !           452: }
        !           453: 
        !           454: HTAnchor * HTAnchor_followTypedLink
        !           455:   ARGS2 (HTAnchor *,this, HTLinkType *,type)
        !           456: {
        !           457:   if (this->mainLink.type == type)
        !           458:     return this->mainLink.dest;
        !           459:   if (this->links) {
        !           460:     HTList *links = this->links;
        !           461:     HTLink *link;
        !           462:     while (link = HTList_nextObject (links))
        !           463:       if (link->type == type)
        !           464:        return link->dest;
        !           465:   }
        !           466:   return NULL;  /* No link of this type */
        !           467: }
        !           468: 
        !           469: BOOL HTAnchor_makeMainLink
        !           470:   ARGS2 (HTAnchor *,this, HTLink *,movingLink)
        !           471: {
        !           472:   /* Check that everything's OK */
        !           473:   if (! (this && HTList_removeObject (this->links, movingLink)))
        !           474:     return NO;  /* link not found or NULL anchor */
        !           475:   else {
        !           476:     /* First push current main link onto top of links list */
        !           477:     HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
        !           478:     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
        !           479:     memcpy (newLink, & this->mainLink, sizeof (HTLink));
        !           480:     HTList_addObject (this->links, newLink);
        !           481: 
        !           482:     /* Now make movingLink the new main link, and free it */
        !           483:     memcpy (& this->mainLink, movingLink, sizeof (HTLink));
        !           484:     free (movingLink);
        !           485:     return YES;
        !           486:   }
        !           487: }

Webmaster