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