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