Annotation of libwww/Library/src/HTAnchor.c, revision 1.1.1.1
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:
88: PRIVATE HTChildAnchor * HTAnchor_findChild
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) {
139: char * parsed_address;
140: HTAnchor * dest;
141: parsed_address = HTParse(href, HTAnchor_address((HTAnchor *) parent),
142: PARSE_ALL);
143: dest = HTAnchor_findAddress(parsed_address);
144: HTAnchor_link((HTAnchor *) child, dest, ltype);
145: free(parsed_address);
146: }
147: return child;
148: }
149:
150:
151: /* Create new or find old named anchor
152: ** -----------------------------------
153: **
154: ** This one is for a reference which is found in a document, and might
155: ** not be already loaded.
156: ** Note: You are not guaranteed a new anchor -- you might get an old one,
157: ** like with fonts.
158: */
159:
160: HTAnchor * HTAnchor_findAddress
161: ARGS1 (CONST char *,address)
162: {
163: char *tag = HTParse (address, "", PARSE_ANCHOR); /* Anchor tag specified ? */
164:
165: /* If the address represents a sub-anchor, we recursively load its parent,
166: then we create a child anchor within that document. */
167: if (*tag) {
168: char *docAddress = HTParse(address, "", PARSE_ACCESS | PARSE_HOST |
169: PARSE_PATH | PARSE_PUNCTUATION);
170: HTParentAnchor * foundParent =
171: (HTParentAnchor *) HTAnchor_findAddress (docAddress);
172: HTChildAnchor * foundAnchor = HTAnchor_findChild (foundParent, tag);
173: free (docAddress);
174: free (tag);
175: return (HTAnchor *) foundAnchor;
176: }
177:
178: else { /* If the address has no anchor tag, check whether we have this node */
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 */
188: for(p=address, hash=0; *p; p++) hash = (hash * 3 + *p) % HASH_SIZE;
189: if (!adult_table)
190: adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
191: if (!adult_table[hash]) adult_table[hash] = HTList_new();
192: adults = adult_table[hash];
193:
194: /* Search list for anchor */
195: grownups = adults;
196: while (foundAnchor = HTList_nextObject (grownups)) {
197: if (equivalent(foundAnchor->address, address)) {
198: if (TRACE) fprintf(stderr, "Anchor %p with address `%s' already exists.\n",
199: foundAnchor, address);
200: return (HTAnchor *) foundAnchor;
201: }
202: }
203:
204: /* Node not found : create new anchor */
205: foundAnchor = HTParentAnchor_new ();
206: if (TRACE) fprintf(stderr, "New anchor %p has hash %d and address `%s'\n",
207: foundAnchor, hash, address);
208: StrAllocCopy(foundAnchor->address, address);
209: HTList_addObject (adults, foundAnchor);
210: return (HTAnchor *) foundAnchor;
211: }
212: }
213:
214:
215: /* Delete an anchor and possibly related things (auto garbage collection)
216: ** --------------------------------------------
217: **
218: ** The anchor is only deleted if the corresponding document is not loaded.
219: ** All outgoing links from parent and children are deleted, and this anchor
220: ** is removed from the sources list of all its targets.
221: ** We also try to delete the targets whose documents are not loaded.
222: ** If this anchor's source list is empty, we delete it and its children.
223: */
224:
225: PRIVATE void deleteLinks
226: ARGS1 (HTAnchor *,this)
227: {
228: if (! this)
229: return;
230:
231: /* Recursively try to delete target anchors */
232: if (this->mainLink.dest) {
233: HTParentAnchor *parent = this->mainLink.dest->parent;
234: HTList_removeObject (parent->sources, this);
235: if (! parent->document) /* Test here to avoid calling overhead */
236: HTAnchor_delete (parent);
237: }
238: if (this->links) { /* Extra destinations */
239: HTLink *target;
240: while (target = HTList_removeLastObject (this->links)) {
241: HTParentAnchor *parent = target->dest->parent;
242: HTList_removeObject (parent->sources, this);
243: if (! parent->document) /* Test here to avoid calling overhead */
244: HTAnchor_delete (parent);
245: }
246: }
247: }
248:
249: PUBLIC BOOL HTAnchor_delete
250: ARGS1 (HTParentAnchor *,this)
251: {
252: HTChildAnchor *child;
253:
254: /* Don't delete if document is loaded */
255: if (this->document)
256: return NO;
257:
258: /* Recursively try to delete target anchors */
259: deleteLinks ((HTAnchor *) this);
260:
261: if (! HTList_isEmpty (this->sources)) { /* There are still incoming links */
262: /* Delete all outgoing links from children, if any */
263: HTList *kids = this->children;
264: while (child = HTList_nextObject (kids))
265: deleteLinks ((HTAnchor *) child);
266: return NO; /* Parent not deleted */
267: }
268:
269: /* No more incoming links : kill everything */
270: /* First, recursively delete children */
271: while (child = HTList_removeLastObject (this->children)) {
272: deleteLinks ((HTAnchor *) child);
273: free (child->tag);
274: free (child);
275: }
276:
277: /* Now kill myself */
278: HTList_delete (this->children);
279: HTList_delete (this->sources);
280: free (this->address);
281: /* Devise a way to clean out the HTFormat if no longer needed (ref count?) */
282: free (this);
283: return YES; /* Parent deleted */
284: }
285:
286:
287: /* Move an anchor to the head of the list of its siblings
288: ** ------------------------------------------------------
289: **
290: ** This is to ensure that an anchor which might have already existed
291: ** is put in the correct order as we load the document.
292: */
293:
294: void HTAnchor_makeLastChild
295: ARGS1(HTChildAnchor *,this)
296: {
297: if (this->parent != (HTParentAnchor *) this) { /* Make sure it's a child */
298: HTList * siblings = this->parent->children;
299: HTList_removeObject (siblings, this);
300: HTList_addObject (siblings, this);
301: }
302: }
303:
304: /* Data access functions
305: ** ---------------------
306: */
307:
308: PUBLIC HTParentAnchor * HTAnchor_parent
309: ARGS1 (HTAnchor *,this)
310: {
311: return this ? this->parent : NULL;
312: }
313:
314: void HTAnchor_setDocument
315: ARGS2 (HTParentAnchor *,this, HyperDoc *,doc)
316: {
317: if (this)
318: this->document = doc;
319: }
320:
321: HyperDoc * HTAnchor_document
322: ARGS1 (HTParentAnchor *,this)
323: {
324: return this ? this->document : NULL;
325: }
326:
327:
328: /* We don't want code to change an address after anchor creation... yet ?
329: void HTAnchor_setAddress
330: ARGS2 (HTAnchor *,this, char *,addr)
331: {
332: if (this)
333: StrAllocCopy (this->parent->address, addr);
334: }
335: */
336:
337: char * HTAnchor_address
338: ARGS1 (HTAnchor *,this)
339: {
340: char *addr = NULL;
341: if (this) {
342: if (((HTParentAnchor *) this == this->parent) ||
343: !((HTChildAnchor *) this)->tag) { /* it's an adult or no tag */
344: StrAllocCopy (addr, this->parent->address);
345: }
346: else { /* it's a named child */
347: addr = malloc (2 + strlen (this->parent->address)
348: + strlen (((HTChildAnchor *) this)->tag));
349: if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
350: sprintf (addr, "%s#%s", this->parent->address,
351: ((HTChildAnchor *) this)->tag);
352: }
353: }
354: return addr;
355: }
356:
357:
358:
359: void HTAnchor_setFormat
360: ARGS2 (HTParentAnchor *,this, HTFormat *,form)
361: {
362: if (this)
363: this->format = form;
364: }
365:
366: HTFormat * HTAnchor_format
367: ARGS1 (HTParentAnchor *,this)
368: {
369: return this ? this->format : NULL;
370: }
371:
372:
373:
374: void HTAnchor_setIndex
375: ARGS1 (HTParentAnchor *,this)
376: {
377: if (this)
378: this->isIndex = YES;
379: }
380:
381: BOOL HTAnchor_isIndex
382: ARGS1 (HTParentAnchor *,this)
383: {
384: return this ? this->isIndex : NO;
385: }
386:
387:
388:
389: BOOL HTAnchor_hasChildren
390: ARGS1 (HTParentAnchor *,this)
391: {
392: return this ? ! HTList_isEmpty(this->children) : NO;
393: }
394:
395: /* Title handling
396: */
397: CONST char * HTAnchor_title
398: ARGS1 (HTParentAnchor *,this)
399: {
400: return this ? this->title : 0;
401: }
402:
403: void HTAnchor_setTitle
404: ARGS2(HTParentAnchor *,this, CONST char *,title)
405: {
406: StrAllocCopy(this->title, title);
407: }
408:
409: void HTAnchor_appendTitle
410: ARGS2(HTParentAnchor *,this, CONST char *,title)
411: {
412: StrAllocCat(this->title, title);
413: }
414:
415: /* Link this Anchor to another given one
416: ** -------------------------------------
417: */
418:
419: BOOL HTAnchor_link
420: ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
421: {
422: if (! (source && destination))
423: return NO; /* Can't link to/from non-existing anchor */
424: if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
425: if (! source->mainLink.dest) {
426: source->mainLink.dest = destination;
427: source->mainLink.type = type;
428: } else {
429: HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
430: if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
431: newLink->dest = destination;
432: newLink->type = type;
433: if (! source->links)
434: source->links = HTList_new ();
435: HTList_addObject (source->links, newLink);
436: }
437: if (!destination->parent->sources)
438: destination->parent->sources = HTList_new ();
439: HTList_addObject (destination->parent->sources, source);
440: return YES; /* Success */
441: }
442:
443:
444: /* Manipulation of links
445: ** ---------------------
446: */
447:
448: HTAnchor * HTAnchor_followMainLink
449: ARGS1 (HTAnchor *,this)
450: {
451: return this->mainLink.dest;
452: }
453:
454: HTAnchor * HTAnchor_followTypedLink
455: ARGS2 (HTAnchor *,this, HTLinkType *,type)
456: {
457: if (this->mainLink.type == type)
458: return this->mainLink.dest;
459: if (this->links) {
460: HTList *links = this->links;
461: HTLink *link;
462: while (link = HTList_nextObject (links))
463: if (link->type == type)
464: return link->dest;
465: }
466: return NULL; /* No link of this type */
467: }
468:
469: BOOL HTAnchor_makeMainLink
470: ARGS2 (HTAnchor *,this, HTLink *,movingLink)
471: {
472: /* Check that everything's OK */
473: if (! (this && HTList_removeObject (this->links, movingLink)))
474: return NO; /* link not found or NULL anchor */
475: else {
476: /* First push current main link onto top of links list */
477: HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
478: if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
479: memcpy (newLink, & this->mainLink, sizeof (HTLink));
480: HTList_addObject (this->links, newLink);
481:
482: /* Now make movingLink the new main link, and free it */
483: memcpy (& this->mainLink, movingLink, sizeof (HTLink));
484: free (movingLink);
485: return YES;
486: }
487: }
Webmaster