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