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