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