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