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