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