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