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