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