1 /*
2  * $LynxId: HTAnchor.c,v 1.74 2013/05/05 19:25:16 tom Exp $
3  *
4  *	Hypertext "Anchor" Object				HTAnchor.c
5  *	==========================
6  *
7  * An anchor represents a region of a hypertext document which is linked to
8  * another anchor in the same or a different document.
9  *
10  * History
11  *
12  *	   Nov 1990  Written in Objective-C for the NeXT browser (TBL)
13  *	24-Oct-1991 (JFG), written in C, browser-independent
14  *	21-Nov-1991 (JFG), first complete version
15  *
16  *	(c) Copyright CERN 1991 - See Copyright.html
17  */
18 
19 #define HASH_SIZE 1001		/* Arbitrary prime.  Memory/speed tradeoff */
20 
21 #include <HTUtils.h>
22 #include <HTAnchor.h>
23 #include <HTParse.h>
24 #include <HTString.h>
25 #include <UCAux.h>
26 #include <UCMap.h>
27 
28 #include <GridText.h>
29 #include <LYUtils.h>
30 #include <LYCharSets.h>
31 #include <LYUtils.h>
32 #include <LYLeaks.h>
33 
34 #define HASH_TYPE unsigned short
35 
36 #ifdef NOT_DEFINED
37 /*
38  *	This is the hashing function used to determine which list in the
39  *		adult_table a parent anchor should be put in.  This is a
40  *		much simpler function than the original used.
41  */
42 #define HASH_FUNCTION(cp_address) \
43 	( (HASH_TYPE)strlen(cp_address) *\
44 	  (HASH_TYPE)TOUPPER(*cp_address) % HASH_SIZE )
45 #endif /* NOT_DEFINED */
46 
47 /*
48  *	This is the original function.	We'll use it again. - FM
49  */
HASH_FUNCTION(const char * cp_address)50 static HASH_TYPE HASH_FUNCTION(const char *cp_address)
51 {
52     HASH_TYPE hash;
53     const unsigned char *p;
54 
55     for (p = (const unsigned char *) cp_address, hash = 0; *p; p++)
56 	hash = (HASH_TYPE) (hash * 3 + (*(const unsigned char *) p)) % HASH_SIZE;
57 
58     return (hash);
59 }
60 
61 typedef struct _HyperDoc Hyperdoc;
62 
63 #ifdef VMS
64 struct _HyperDoc {
65     int junk;			/* VMS cannot handle pointers to undefined structs */
66 };
67 #endif /* VMS */
68 
69 /* Table of lists of all parents */
70 static HTList adult_table[HASH_SIZE] =
71 {
72     {NULL, NULL}};
73 
74 /*				Creation Methods
75  *				================
76  *
77  *	Do not use "new" by itself outside this module.  In order to enforce
78  *	consistency, we insist that you furnish more information about the
79  *	anchor you are creating : use newWithParent or newWithAddress.
80  */
HTParentAnchor0_new(const char * address,unsigned hash)81 static HTParentAnchor0 *HTParentAnchor0_new(const char *address,
82 					    unsigned hash)
83 {
84     HTParentAnchor0 *newAnchor = typecalloc(HTParentAnchor0);
85 
86     if (newAnchor == NULL)
87 	outofmem(__FILE__, "HTParentAnchor0_new");
88 
89     assert(newAnchor != NULL);
90 
91     newAnchor->parent = newAnchor;	/* self */
92     StrAllocCopy(newAnchor->address, address);
93     newAnchor->adult_hash = (HASH_TYPE) hash;
94 
95     return (newAnchor);
96 }
97 
HTParentAnchor_new(HTParentAnchor0 * parent)98 static HTParentAnchor *HTParentAnchor_new(HTParentAnchor0 *parent)
99 {
100     HTParentAnchor *newAnchor = typecalloc(HTParentAnchor);
101 
102     if (newAnchor == NULL)
103 	outofmem(__FILE__, "HTParentAnchor_new");
104 
105     assert(newAnchor != NULL);
106 
107     newAnchor->parent = parent;	/* cross reference */
108     parent->info = newAnchor;	/* cross reference */
109     newAnchor->address = parent->address;	/* copy pointer */
110 
111     newAnchor->isISMAPScript = FALSE;	/* Lynx appends ?0,0 if TRUE. - FM */
112     newAnchor->isHEAD = FALSE;	/* HEAD request if TRUE. - FM */
113     newAnchor->safe = FALSE;	/* Safe. - FM */
114     newAnchor->no_cache = FALSE;	/* no-cache? - FM */
115     newAnchor->inBASE = FALSE;	/* duplicated from HTML.c/h */
116     newAnchor->content_length = 0;	/* Content-Length. - FM */
117     return (newAnchor);
118 }
119 
HTChildAnchor_new(HTParentAnchor0 * parent)120 static HTChildAnchor *HTChildAnchor_new(HTParentAnchor0 *parent)
121 {
122     HTChildAnchor *p = typecalloc(HTChildAnchor);
123 
124     if (p == NULL)
125 	outofmem(__FILE__, "HTChildAnchor_new");
126 
127     assert(p != NULL);
128 
129     p->parent = parent;		/* parent reference */
130     return p;
131 }
132 
HText_pool_ChildAnchor_new(HTParentAnchor * parent)133 static HTChildAnchor *HText_pool_ChildAnchor_new(HTParentAnchor *parent)
134 {
135     HTChildAnchor *p = (HTChildAnchor *) HText_pool_calloc((HText *) (parent->document),
136 							   (unsigned) sizeof(HTChildAnchor));
137 
138     if (p == NULL)
139 	outofmem(__FILE__, "HText_pool_ChildAnchor_new");
140 
141     assert(p != NULL);
142 
143     p->parent = parent->parent;	/* parent reference */
144     return p;
145 }
146 
147 #ifdef CASE_INSENSITIVE_ANCHORS
148 /* Case insensitive string comparison */
149 #define HT_EQUIV(a,b) (TOUPPER(a) == TOUPPER(b))
150 #else
151 /* Case sensitive string comparison */
152 #define HT_EQUIV(a,b) ((a) == (b))
153 #endif
154 
155 /*	Null-terminated string comparison
156  *	---------------------------------
157  * On entry,
158  *	s	Points to one string, null terminated
159  *	t	points to the other.
160  * On exit,
161  *	returns YES if the strings are equivalent
162  *		NO if they differ.
163  */
HTSEquivalent(const char * s,const char * t)164 static BOOL HTSEquivalent(const char *s,
165 			  const char *t)
166 {
167     if (s && t) {		/* Make sure they point to something */
168 	for (; *s && *t; s++, t++) {
169 	    if (!HT_EQUIV(*s, *t)) {
170 		return (NO);
171 	    }
172 	}
173 	return (BOOL) (HT_EQUIV(*s, *t));
174     } else {
175 	return (BOOL) (s == t);	/* Two NULLs are equivalent, aren't they ? */
176     }
177 }
178 
179 /*	Binary string comparison
180  *	------------------------
181  * On entry,
182  *	s	Points to one bstring
183  *	t	points to the other.
184  * On exit,
185  *	returns YES if the strings are equivalent
186  *		NO if they differ.
187  */
HTBEquivalent(const bstring * s,const bstring * t)188 static BOOL HTBEquivalent(const bstring *s,
189 			  const bstring *t)
190 {
191     if (s && t && BStrLen(s) == BStrLen(t)) {
192 	int j;
193 	int len = BStrLen(s);
194 
195 	for (j = 0; j < len; ++j) {
196 	    if (!HT_EQUIV(BStrData(s)[j], BStrData(t)[j])) {
197 		return (NO);
198 	    }
199 	}
200 	return (YES);
201     } else {
202 	return (BOOL) (s == t);	/* Two NULLs are equivalent, aren't they ? */
203     }
204 }
205 
206 /*
207  * Three-way compare function
208  */
compare_anchors(void * l,void * r)209 static int compare_anchors(void *l,
210 			   void *r)
211 {
212     const char *a = ((HTChildAnchor *) l)->tag;
213     const char *b = ((HTChildAnchor *) r)->tag;
214 
215     /* both tags are not NULL */
216 
217 #ifdef CASE_INSENSITIVE_ANCHORS
218     return strcasecomp(a, b);	/* Case insensitive */
219 #else
220     return strcmp(a, b);	/* Case sensitive - FM */
221 #endif /* CASE_INSENSITIVE_ANCHORS */
222 }
223 
224 /*	Create new or find old sub-anchor
225  *	---------------------------------
226  *
227  *	This one is for a named child.
228  *	The parent anchor must already exist.
229  */
HTAnchor_findNamedChild(HTParentAnchor0 * parent,const char * tag)230 static HTChildAnchor *HTAnchor_findNamedChild(HTParentAnchor0 *parent,
231 					      const char *tag)
232 {
233     HTChildAnchor *child;
234 
235     if (parent && tag && *tag) {	/* TBL */
236 	if (parent->children) {
237 	    /*
238 	     * Parent has children.  Search them.
239 	     */
240 	    HTChildAnchor sample;
241 
242 	    sample.tag = (char *) tag;	/* for compare_anchors() only */
243 
244 	    child = (HTChildAnchor *) HTBTree_search(parent->children, &sample);
245 	    if (child != NULL) {
246 		CTRACE((tfp,
247 			"Child anchor %p of parent %p with name `%s' already exists.\n",
248 			(void *) child, (void *) parent, tag));
249 		return (child);
250 	    }
251 	} else {		/* parent doesn't have any children yet : create family */
252 	    parent->children = HTBTree_new(compare_anchors);
253 	}
254 
255 	child = HTChildAnchor_new(parent);
256 	CTRACE((tfp, "HTAnchor: New Anchor %p named `%s' is child of %p\n",
257 		(void *) child,
258 		NonNull(tag),
259 		(void *) child->parent));
260 
261 	StrAllocCopy(child->tag, tag);	/* should be set before HTBTree_add */
262 	HTBTree_add(parent->children, child);
263 	return (child);
264 
265     } else {
266 	CTRACE((tfp, "HTAnchor_findNamedChild called with NULL parent.\n"));
267 	return (NULL);
268     }
269 
270 }
271 
272 /*
273  *	This one is for a new unnamed child being edited into an existing
274  *	document.  The parent anchor and the document must already exist.
275  *	(Just add new unnamed child).
276  */
HTAnchor_addChild(HTParentAnchor * parent)277 static HTChildAnchor *HTAnchor_addChild(HTParentAnchor *parent)
278 {
279     HTChildAnchor *child;
280 
281     if (!parent) {
282 	CTRACE((tfp, "HTAnchor_addChild called with NULL parent.\n"));
283 	return (NULL);
284     }
285 
286     child = HText_pool_ChildAnchor_new(parent);
287     CTRACE((tfp, "HTAnchor: New unnamed Anchor %p is child of %p\n",
288 	    (void *) child,
289 	    (void *) child->parent));
290 
291     child->tag = 0;
292     HTList_linkObject(&parent->children_notag, child, &child->_add_children_notag);
293 
294     return (child);
295 }
296 
297 static HTParentAnchor0 *HTAnchor_findAddress_in_adult_table(const DocAddress *newdoc);
298 
299 static BOOL HTAnchor_link(HTChildAnchor *child,
300 			  HTAnchor * destination,
301 			  HTLinkType *type);
302 
303 /*	Create or find a child anchor with a possible link
304  *	--------------------------------------------------
305  *
306  *	Create new anchor with a given parent and possibly
307  *	a name, and possibly a link to a _relatively_ named anchor.
308  *	(Code originally in ParseHTML.h)
309  */
HTAnchor_findChildAndLink(HTParentAnchor * parent,const char * tag,const char * href,HTLinkType * ltype)310 HTChildAnchor *HTAnchor_findChildAndLink(HTParentAnchor *parent,	/* May not be 0   */
311 					 const char *tag,	/* May be "" or 0 */
312 					 const char *href,	/* May be "" or 0 */
313 					 HTLinkType *ltype)	/* May be 0       */
314 {
315     HTChildAnchor *child;
316 
317     CTRACE((tfp, "Entered HTAnchor_findChildAndLink:  tag=`%s',%s href=`%s'\n",
318 	    NonNull(tag),
319 	    (ltype == HTInternalLink) ? " (internal link)" : "",
320 	    NonNull(href)));
321 
322     if (parent == 0) {
323 	child = 0;
324     } else {
325 	if (non_empty(tag)) {
326 	    child = HTAnchor_findNamedChild(parent->parent, tag);
327 	} else {
328 	    child = HTAnchor_addChild(parent);
329 	}
330 
331 	if (non_empty(href)) {
332 	    const char *fragment = NULL;
333 	    HTParentAnchor0 *dest;
334 
335 	    if (ltype == HTInternalLink && *href == '#') {
336 		dest = parent->parent;
337 	    } else {
338 		const char *relative_to = ((parent->inBASE && *href != '#')
339 					   ? parent->content_base
340 					   : parent->address);
341 		DocAddress parsed_doc;
342 
343 		parsed_doc.address = HTParse(href, relative_to,
344 					     PARSE_ALL_WITHOUT_ANCHOR);
345 
346 		parsed_doc.post_data = NULL;
347 		parsed_doc.post_content_type = NULL;
348 		if (ltype && parent->post_data && ltype == HTInternalLink) {
349 		    /* for internal links, find a destination with the same
350 		       post data if the source of the link has post data. - kw
351 		       Example: LYNXIMGMAP: */
352 		    parsed_doc.post_data = parent->post_data;
353 		    parsed_doc.post_content_type = parent->post_content_type;
354 		}
355 		parsed_doc.bookmark = NULL;
356 		parsed_doc.isHEAD = FALSE;
357 		parsed_doc.safe = FALSE;
358 
359 		dest = HTAnchor_findAddress_in_adult_table(&parsed_doc);
360 		FREE(parsed_doc.address);
361 	    }
362 
363 	    /*
364 	     * [from HTAnchor_findAddress()]
365 	     * If the address represents a sub-anchor, we load its parent (above),
366 	     * then we create a named child anchor within that parent.
367 	     */
368 	    fragment = (*href == '#') ? href + 1 : HTParseAnchor(href);
369 
370 	    if (*fragment)
371 		dest = (HTParentAnchor0 *) HTAnchor_findNamedChild(dest, fragment);
372 
373 	    if (tag && *tag) {
374 		if (child->dest) {	/* DUPLICATE_ANCHOR_NAME_WORKAROUND  - kw */
375 		    CTRACE((tfp,
376 			    "*** Duplicate ChildAnchor %p named `%s'",
377 			    (void *) child, tag));
378 		    if ((HTAnchor *) dest != child->dest || ltype != child->type) {
379 			CTRACE((tfp,
380 				", different dest %p or type, creating unnamed child\n",
381 				(void *) child->dest));
382 			child = HTAnchor_addChild(parent);
383 		    }
384 		}
385 	    }
386 	    HTAnchor_link(child, (HTAnchor *) dest, ltype);
387 	}
388     }
389     return child;
390 }
391 
392 /*	Create new or find old parent anchor
393  *	------------------------------------
394  *
395  *	Me one is for a reference which is found in a document, and might
396  *	not be already loaded.
397  *	Note: You are not guaranteed a new anchor -- you might get an old one,
398  *	like with fonts.
399  */
HTAnchor_findAddress(const DocAddress * newdoc)400 HTParentAnchor *HTAnchor_findAddress(const DocAddress *newdoc)
401 {
402     /* Anchor tag specified ? */
403     const char *tag = HTParseAnchor(newdoc->address);
404 
405     CTRACE((tfp, "Entered HTAnchor_findAddress\n"));
406 
407     /*
408      * If the address represents a sub-anchor, we load its parent, then we
409      * create a named child anchor within that parent.
410      */
411     if (*tag) {
412 	DocAddress parsed_doc;
413 	HTParentAnchor0 *foundParent;
414 
415 	parsed_doc.address = HTParse(newdoc->address, "",
416 				     PARSE_ALL_WITHOUT_ANCHOR);
417 	parsed_doc.post_data = newdoc->post_data;
418 	parsed_doc.post_content_type = newdoc->post_content_type;
419 	parsed_doc.bookmark = newdoc->bookmark;
420 	parsed_doc.isHEAD = newdoc->isHEAD;
421 	parsed_doc.safe = newdoc->safe;
422 
423 	foundParent = HTAnchor_findAddress_in_adult_table(&parsed_doc);
424 	(void) HTAnchor_findNamedChild(foundParent, tag);
425 	FREE(parsed_doc.address);
426 	return HTAnchor_parent((HTAnchor *) foundParent);
427     }
428     return HTAnchor_parent((HTAnchor *) HTAnchor_findAddress_in_adult_table(newdoc));
429 }
430 
431 /*  The address has no anchor tag, for sure.
432  */
HTAnchor_findAddress_in_adult_table(const DocAddress * newdoc)433 static HTParentAnchor0 *HTAnchor_findAddress_in_adult_table(const DocAddress *newdoc)
434 {
435     /*
436      * Check whether we have this node.
437      */
438     HASH_TYPE hash;
439     HTList *adults;
440     HTList *grownups;
441     HTParentAnchor0 *foundAnchor;
442     BOOL need_extra_info = (BOOL) (newdoc->post_data ||
443 				   newdoc->post_content_type ||
444 				   newdoc->bookmark ||
445 				   newdoc->isHEAD ||
446 				   newdoc->safe);
447 
448     /*
449      * We need not free adult_table[] atexit - it should be perfectly empty
450      * after free'ing all HTexts.  (There is an error if it is not empty at
451      * exit).  -LP
452      */
453 
454     /*
455      * Select list from hash table,
456      */
457     hash = HASH_FUNCTION(newdoc->address);
458     adults = &(adult_table[hash]);
459 
460     /*
461      * Search list for anchor.
462      */
463     grownups = adults;
464     while (NULL != (foundAnchor =
465 		    (HTParentAnchor0 *) HTList_nextObject(grownups))) {
466 	if (HTSEquivalent(foundAnchor->address, newdoc->address) &&
467 
468 	    ((!foundAnchor->info && !need_extra_info) ||
469 	     (foundAnchor->info &&
470 	      HTBEquivalent(foundAnchor->info->post_data, newdoc->post_data) &&
471 	      foundAnchor->info->isHEAD == newdoc->isHEAD))) {
472 	    CTRACE((tfp, "Anchor %p with address `%s' already exists.\n",
473 		    (void *) foundAnchor, newdoc->address));
474 	    return foundAnchor;
475 	}
476     }
477 
478     /*
479      * Node not found:  create new anchor.
480      */
481     foundAnchor = HTParentAnchor0_new(newdoc->address, hash);
482     CTRACE((tfp, "New anchor %p has hash %d and address `%s'\n",
483 	    (void *) foundAnchor, hash, newdoc->address));
484 
485     if (need_extra_info) {
486 	/* rare case, create a big structure */
487 	HTParentAnchor *p = HTParentAnchor_new(foundAnchor);
488 
489 	if (newdoc->post_data)
490 	    BStrCopy(p->post_data, newdoc->post_data);
491 	if (newdoc->post_content_type)
492 	    StrAllocCopy(p->post_content_type,
493 			 newdoc->post_content_type);
494 	if (newdoc->bookmark)
495 	    StrAllocCopy(p->bookmark, newdoc->bookmark);
496 	p->isHEAD = newdoc->isHEAD;
497 	p->safe = newdoc->safe;
498     }
499     HTList_linkObject(adults, foundAnchor, &foundAnchor->_add_adult);
500 
501     return foundAnchor;
502 }
503 
504 /*	Create new or find old named anchor - simple form
505  *	-------------------------------------------------
506  *
507  *     Like HTAnchor_findAddress, but simpler to use for simple cases.
508  *	No post data etc. can be supplied. - kw
509  */
HTAnchor_findSimpleAddress(const char * url)510 HTParentAnchor *HTAnchor_findSimpleAddress(const char *url)
511 {
512     DocAddress urldoc;
513 
514     urldoc.address = (char *) url;	/* ignore warning, it IS treated like const - kw */
515     urldoc.post_data = NULL;
516     urldoc.post_content_type = NULL;
517     urldoc.bookmark = NULL;
518     urldoc.isHEAD = FALSE;
519     urldoc.safe = FALSE;
520     return HTAnchor_findAddress(&urldoc);
521 }
522 
523 /*	Link me Anchor to another given one
524  *	-------------------------------------
525  */
HTAnchor_link(HTChildAnchor * child,HTAnchor * destination,HTLinkType * type)526 static BOOL HTAnchor_link(HTChildAnchor *child,
527 			  HTAnchor * destination,
528 			  HTLinkType *type)
529 {
530     if (!(child && destination))
531 	return (NO);		/* Can't link to/from non-existing anchor */
532 
533     CTRACE((tfp, "Linking child %p to anchor %p\n", (void *) child, (void *) destination));
534     if (child->dest) {
535 	CTRACE((tfp, "*** child anchor already has destination, exiting!\n"));
536 	return (NO);
537     }
538 
539     child->dest = destination;
540     child->type = type;
541 
542     if (child->parent != destination->parent)
543 	/* link only foreign children */
544 	HTList_linkObject(&destination->parent->sources, child, &child->_add_sources);
545 
546     return (YES);		/* Success */
547 }
548 
549 /*	Delete an anchor and possibly related things (auto garbage collection)
550  *	--------------------------------------------
551  *
552  *	The anchor is only deleted if the corresponding document is not loaded.
553  *	All outgoing links from children are deleted, and children are
554  *	removed from the sources lists of theirs targets.
555  *	We also try to delete the targets whose documents are not loaded.
556  *	If this anchor's sources list is empty, we delete it and its children.
557  */
558 
559 /*
560  *	Recursively try to delete destination anchor of this child.
561  *	In any event, this will tell destination anchor that we
562  *	no longer consider it a destination.
563  */
deleteLinks(HTChildAnchor * me)564 static void deleteLinks(HTChildAnchor *me)
565 {
566     /*
567      * Unregister me with our destination anchor's parent.
568      */
569     if (me->dest) {
570 	HTParentAnchor0 *parent = me->dest->parent;
571 
572 	/*
573 	 * Start.  Set the dest pointer to zero.
574 	 */
575 	me->dest = NULL;
576 
577 	/*
578 	 * Remove me from the parent's sources so that the parent knows one
579 	 * less anchor is its dest.
580 	 */
581 	if ((me->parent != parent) && !HTList_isEmpty(&parent->sources)) {
582 	    /*
583 	     * Really should only need to deregister once.
584 	     */
585 	    HTList_unlinkObject(&parent->sources, (void *) me);
586 	}
587 
588 	/*
589 	 * Recursive call.  Test here to avoid calling overhead.  Don't delete
590 	 * if document is loaded or being loaded.
591 	 */
592 	if ((me->parent != parent) &&
593 	    parent != NULL &&
594 	    !parent->underway &&
595 	    (!parent->info || !parent->info->document)) {
596 	    HTAnchor_delete(parent);
597 	}
598 
599 	/*
600 	 * At this point, we haven't a destination.  Set it to be so.  Leave
601 	 * the HTAtom pointed to by type up to other code to handle (reusable,
602 	 * near static).
603 	 */
604 	me->type = NULL;
605     }
606 }
607 
608 static void HTParentAnchor_free(HTParentAnchor *me);
609 
HTAnchor_delete(HTParentAnchor0 * me)610 BOOL HTAnchor_delete(HTParentAnchor0 *me)
611 {
612     /*
613      * Memory leaks fixed.
614      * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe
615      */
616     HTBTElement *ele;
617     HTChildAnchor *child;
618 
619     /*
620      * Do nothing if nothing to do.
621      */
622     if (!me) {
623 	return (NO);
624     }
625 
626     /*
627      * Don't delete if document is loaded or being loaded.
628      */
629     if (me->underway || (me->info && me->info->document)) {
630 	return (NO);
631     }
632 
633     /*
634      * Mark ourselves busy, so that recursive calls of this function on this
635      * HTParentAnchor0 will not free it from under our feet.  - kw
636      */
637     me->underway = TRUE;
638 
639     {
640 	/*
641 	 * Delete all outgoing links from named children.  Do not delete named
642 	 * children itself (may have incoming links).
643 	 */
644 	if (me->children) {
645 	    ele = HTBTree_next(me->children, NULL);
646 	    while (ele != NULL) {
647 		child = (HTChildAnchor *) HTBTree_object(ele);
648 		if (child->dest)
649 		    deleteLinks(child);
650 		ele = HTBTree_next(me->children, ele);
651 	    }
652 	}
653     }
654     me->underway = FALSE;
655 
656     /*
657      * There are still incoming links to this one (we are the
658      * destination of another anchor).
659      */
660     if (!HTList_isEmpty(&me->sources)) {
661 	/*
662 	 * Can't delete parent, still have sources.
663 	 */
664 	return (NO);
665     }
666 
667     /*
668      * No more incoming and outgoing links :  kill everything First, delete
669      * named children.
670      */
671     if (me->children) {
672 	ele = HTBTree_next(me->children, NULL);
673 	while (ele != NULL) {
674 	    child = (HTChildAnchor *) HTBTree_object(ele);
675 	    FREE(child->tag);
676 	    FREE(child);
677 	    ele = HTBTree_next(me->children, ele);
678 	}
679 	HTBTree_free(me->children);
680     }
681 
682     /*
683      * Delete the ParentAnchor, if any.  (Document was already deleted).
684      */
685     if (me->info) {
686 	HTParentAnchor_free(me->info);
687 	FREE(me->info);
688     }
689 
690     /*
691      * Remove ourselves from the hash table's list.
692      */
693     HTList_unlinkObject(&(adult_table[me->adult_hash]), (void *) me);
694 
695     /*
696      * Free the address.
697      */
698     FREE(me->address);
699 
700     /*
701      * Finally, kill the parent anchor passed in.
702      */
703     FREE(me);
704 
705     return (YES);
706 }
707 
708 /*
709  * Unnamed children (children_notag) have no sence without HText - delete them
710  * and their links if we are about to free HText.  Document currently exists.
711  * Called within HText_free().
712  */
HTAnchor_delete_links(HTParentAnchor * me)713 void HTAnchor_delete_links(HTParentAnchor *me)
714 {
715     HTList *cur;
716     HTChildAnchor *child;
717 
718     /*
719      * Do nothing if nothing to do.
720      */
721     if (!me || !me->document) {
722 	return;
723     }
724 
725     /*
726      * Mark ourselves busy, so that recursive calls on this HTParentAnchor0
727      * will not free it from under our feet.  - kw
728      */
729     me->parent->underway = TRUE;
730 
731     /*
732      * Delete all outgoing links from unnamed children.
733      */
734     if (!HTList_isEmpty(&me->children_notag)) {
735 	cur = &me->children_notag;
736 	while ((child =
737 		(HTChildAnchor *) HTList_unlinkLastObject(cur)) != 0) {
738 	    deleteLinks(child);
739 	    /* child allocated in HText pool, HText_free() will free it later */
740 	}
741     }
742     me->parent->underway = FALSE;
743 }
744 
HTParentAnchor_free(HTParentAnchor * me)745 static void HTParentAnchor_free(HTParentAnchor *me)
746 {
747     /*
748      * Delete the methods list.
749      */
750     if (me->methods) {
751 	/*
752 	 * Leave what the methods point to up in memory for other code (near
753 	 * static stuff).
754 	 */
755 	HTList_delete(me->methods);
756 	me->methods = NULL;
757     }
758 
759     /*
760      * Free up all allocated members.
761      */
762     FREE(me->charset);
763     FREE(me->isIndexAction);
764     FREE(me->isIndexPrompt);
765     FREE(me->title);
766     FREE(me->physical);
767     BStrFree(me->post_data);
768     FREE(me->post_content_type);
769     FREE(me->bookmark);
770     FREE(me->owner);
771     FREE(me->RevTitle);
772     FREE(me->citehost);
773 #ifdef USE_SOURCE_CACHE
774     HTAnchor_clearSourceCache(me);
775 #endif
776     if (me->FileCache) {
777 	FILE *fd;
778 
779 	if ((fd = fopen(me->FileCache, "r")) != NULL) {
780 	    fclose(fd);
781 	    (void) remove(me->FileCache);
782 	}
783 	FREE(me->FileCache);
784     }
785     FREE(me->SugFname);
786     FREE(me->cache_control);
787 #ifdef EXP_HTTP_HEADERS
788     HTChunkClear(&(me->http_headers));
789 #endif
790     FREE(me->content_type_params);
791     FREE(me->content_type);
792     FREE(me->content_language);
793     FREE(me->content_encoding);
794     FREE(me->content_base);
795     FREE(me->content_disposition);
796     FREE(me->content_location);
797     FREE(me->content_md5);
798     FREE(me->message_id);
799     FREE(me->subject);
800     FREE(me->date);
801     FREE(me->expires);
802 
803     FREE(me->last_modified);
804     FREE(me->ETag);
805     FREE(me->server);
806 #ifdef USE_COLOR_STYLE
807     FREE(me->style);
808 #endif
809 
810     /*
811      * Original code wanted a way to clean out the HTFormat if no longer needed
812      * (ref count?).  I'll leave it alone since those HTAtom objects are a
813      * little harder to know where they are being referenced all at one time.
814      * (near static)
815      */
816 
817     FREE(me->UCStages);
818     ImageMapList_free(me->imaps);
819 }
820 
821 #ifdef USE_SOURCE_CACHE
HTAnchor_clearSourceCache(HTParentAnchor * me)822 void HTAnchor_clearSourceCache(HTParentAnchor *me)
823 {
824     /*
825      * Clean up the source cache, if any.
826      */
827     if (me->source_cache_file) {
828 	CTRACE((tfp, "SourceCache: Removing file %s\n",
829 		me->source_cache_file));
830 	(void) LYRemoveTemp(me->source_cache_file);
831 	FREE(me->source_cache_file);
832     }
833     if (me->source_cache_chunk) {
834 	CTRACE((tfp, "SourceCache: Removing memory chunk %p\n",
835 		(void *) me->source_cache_chunk));
836 	HTChunkFree(me->source_cache_chunk);
837 	me->source_cache_chunk = NULL;
838     }
839 }
840 #endif /* USE_SOURCE_CACHE */
841 
842 /*	Data access functions
843  *	---------------------
844  */
HTAnchor_parent(HTAnchor * me)845 HTParentAnchor *HTAnchor_parent(HTAnchor * me)
846 {
847     if (!me)
848 	return NULL;
849 
850     if (me->parent->info)
851 	return me->parent->info;
852 
853     /* else: create a new structure */
854     return HTParentAnchor_new(me->parent);
855 }
856 
HTAnchor_setDocument(HTParentAnchor * me,HyperDoc * doc)857 void HTAnchor_setDocument(HTParentAnchor *me,
858 			  HyperDoc *doc)
859 {
860     if (me)
861 	me->document = doc;
862 }
863 
HTAnchor_document(HTParentAnchor * me)864 HyperDoc *HTAnchor_document(HTParentAnchor *me)
865 {
866     return (me ? me->document : NULL);
867 }
868 
HTAnchor_address(HTAnchor * me)869 char *HTAnchor_address(HTAnchor * me)
870 {
871     char *addr = NULL;
872 
873     if (me) {
874 	if (((HTParentAnchor0 *) me == me->parent) ||
875 	    ((HTParentAnchor *) me == me->parent->info) ||
876 	    !((HTChildAnchor *) me)->tag) {	/* it's an adult or no tag */
877 	    StrAllocCopy(addr, me->parent->address);
878 	} else {		/* it's a named child */
879 	    HTSprintf0(&addr, "%s#%s",
880 		       me->parent->address, ((HTChildAnchor *) me)->tag);
881 	}
882     }
883     return (addr);
884 }
885 
HTAnchor_setFormat(HTParentAnchor * me,HTFormat form)886 void HTAnchor_setFormat(HTParentAnchor *me,
887 			HTFormat form)
888 {
889     if (me)
890 	me->format = form;
891 }
892 
HTAnchor_format(HTParentAnchor * me)893 HTFormat HTAnchor_format(HTParentAnchor *me)
894 {
895     return (me ? me->format : NULL);
896 }
897 
HTAnchor_setIndex(HTParentAnchor * me,const char * address)898 void HTAnchor_setIndex(HTParentAnchor *me,
899 		       const char *address)
900 {
901     if (me) {
902 	me->isIndex = YES;
903 	StrAllocCopy(me->isIndexAction, address);
904     }
905 }
906 
HTAnchor_setPrompt(HTParentAnchor * me,const char * prompt)907 void HTAnchor_setPrompt(HTParentAnchor *me,
908 			const char *prompt)
909 {
910     if (me) {
911 	StrAllocCopy(me->isIndexPrompt, prompt);
912     }
913 }
914 
HTAnchor_isIndex(HTParentAnchor * me)915 BOOL HTAnchor_isIndex(HTParentAnchor *me)
916 {
917     return (BOOL) (me
918 		   ? me->isIndex
919 		   : NO);
920 }
921 
922 /*	Whether Anchor has been designated as an ISMAP link
923  *	(normally by presence of an ISMAP attribute on A or IMG) - KW
924  */
HTAnchor_isISMAPScript(HTAnchor * me)925 BOOL HTAnchor_isISMAPScript(HTAnchor * me)
926 {
927     return (BOOL) ((me && me->parent->info)
928 		   ? me->parent->info->isISMAPScript
929 		   : NO);
930 }
931 
932 #if defined(USE_COLOR_STYLE)
933 /*	Style handling.
934 */
HTAnchor_style(HTParentAnchor * me)935 const char *HTAnchor_style(HTParentAnchor *me)
936 {
937     return (me ? me->style : NULL);
938 }
939 
HTAnchor_setStyle(HTParentAnchor * me,const char * style)940 void HTAnchor_setStyle(HTParentAnchor *me,
941 		       const char *style)
942 {
943     if (me) {
944 	StrAllocCopy(me->style, style);
945     }
946 }
947 #endif
948 
949 /*	Title handling.
950 */
HTAnchor_title(HTParentAnchor * me)951 const char *HTAnchor_title(HTParentAnchor *me)
952 {
953     return (me ? me->title : NULL);
954 }
955 
HTAnchor_setTitle(HTParentAnchor * me,const char * title)956 void HTAnchor_setTitle(HTParentAnchor *me,
957 		       const char *title)
958 {
959     int i;
960 
961     if (me) {
962 	if (title) {
963 	    StrAllocCopy(me->title, title);
964 	    for (i = 0; me->title[i]; i++) {
965 		if (UCH(me->title[i]) == 1 ||
966 		    UCH(me->title[i]) == 2) {
967 		    me->title[i] = ' ';
968 		}
969 	    }
970 	} else {
971 	    CTRACE((tfp, "HTAnchor_setTitle: New title is NULL! "));
972 	    if (me->title) {
973 		CTRACE((tfp, "Old title was \"%s\".\n", me->title));
974 		FREE(me->title);
975 	    } else {
976 		CTRACE((tfp, "Old title was NULL.\n"));
977 	    }
978 	}
979     }
980 }
981 
HTAnchor_appendTitle(HTParentAnchor * me,const char * title)982 void HTAnchor_appendTitle(HTParentAnchor *me,
983 			  const char *title)
984 {
985     int i;
986 
987     if (me) {
988 	StrAllocCat(me->title, title);
989 	for (i = 0; me->title[i]; i++) {
990 	    if (UCH(me->title[i]) == 1 ||
991 		UCH(me->title[i]) == 2) {
992 		me->title[i] = ' ';
993 	    }
994 	}
995     }
996 }
997 
998 /*	Bookmark handling.
999 */
HTAnchor_bookmark(HTParentAnchor * me)1000 const char *HTAnchor_bookmark(HTParentAnchor *me)
1001 {
1002     return (me ? me->bookmark : NULL);
1003 }
1004 
HTAnchor_setBookmark(HTParentAnchor * me,const char * bookmark)1005 void HTAnchor_setBookmark(HTParentAnchor *me,
1006 			  const char *bookmark)
1007 {
1008     if (me)
1009 	StrAllocCopy(me->bookmark, bookmark);
1010 }
1011 
1012 /*	Owner handling.
1013 */
HTAnchor_owner(HTParentAnchor * me)1014 const char *HTAnchor_owner(HTParentAnchor *me)
1015 {
1016     return (me ? me->owner : NULL);
1017 }
1018 
HTAnchor_setOwner(HTParentAnchor * me,const char * owner)1019 void HTAnchor_setOwner(HTParentAnchor *me,
1020 		       const char *owner)
1021 {
1022     if (me) {
1023 	StrAllocCopy(me->owner, owner);
1024     }
1025 }
1026 
1027 /*	TITLE handling in LINKs with REV="made" or REV="owner". - FM
1028 */
HTAnchor_RevTitle(HTParentAnchor * me)1029 const char *HTAnchor_RevTitle(HTParentAnchor *me)
1030 {
1031     return (me ? me->RevTitle : NULL);
1032 }
1033 
HTAnchor_setRevTitle(HTParentAnchor * me,const char * title)1034 void HTAnchor_setRevTitle(HTParentAnchor *me,
1035 			  const char *title)
1036 {
1037     int i;
1038 
1039     if (me) {
1040 	StrAllocCopy(me->RevTitle, title);
1041 	for (i = 0; me->RevTitle[i]; i++) {
1042 	    if (UCH(me->RevTitle[i]) == 1 ||
1043 		UCH(me->RevTitle[i]) == 2) {
1044 		me->RevTitle[i] = ' ';
1045 	    }
1046 	}
1047     }
1048 }
1049 
1050 #ifndef DISABLE_BIBP
1051 /*	Citehost for bibp links from LINKs with REL="citehost". - RDC
1052 */
HTAnchor_citehost(HTParentAnchor * me)1053 const char *HTAnchor_citehost(HTParentAnchor *me)
1054 {
1055     return (me ? me->citehost : NULL);
1056 }
1057 
HTAnchor_setCitehost(HTParentAnchor * me,const char * citehost)1058 void HTAnchor_setCitehost(HTParentAnchor *me,
1059 			  const char *citehost)
1060 {
1061     if (me) {
1062 	StrAllocCopy(me->citehost, citehost);
1063     }
1064 }
1065 #endif /* !DISABLE_BIBP */
1066 
1067 /*	Suggested filename handling. - FM
1068  *	(will be loaded if we had a Content-Disposition
1069  *	 header or META element with filename=name.suffix)
1070  */
HTAnchor_SugFname(HTParentAnchor * me)1071 const char *HTAnchor_SugFname(HTParentAnchor *me)
1072 {
1073     return (me ? me->SugFname : NULL);
1074 }
1075 
1076 #ifdef EXP_HTTP_HEADERS
1077 /*	HTTP Headers.
1078 */
HTAnchor_http_headers(HTParentAnchor * me)1079 const char *HTAnchor_http_headers(HTParentAnchor *me)
1080 {
1081     return (me ? me->http_headers.data : NULL);
1082 }
1083 #endif
1084 
1085 /*	Content-Type handling (parameter list).
1086 */
HTAnchor_content_type_params(HTParentAnchor * me)1087 const char *HTAnchor_content_type_params(HTParentAnchor *me)
1088 {
1089     return (me ? me->content_type_params : NULL);
1090 }
1091 
1092 /*	Content-Encoding handling. - FM
1093  *	(will be loaded if we had a Content-Encoding
1094  *	 header.)
1095  */
HTAnchor_content_encoding(HTParentAnchor * me)1096 const char *HTAnchor_content_encoding(HTParentAnchor *me)
1097 {
1098     return (me ? me->content_encoding : NULL);
1099 }
1100 
1101 /*	Content-Type handling. - FM
1102 */
HTAnchor_content_type(HTParentAnchor * me)1103 const char *HTAnchor_content_type(HTParentAnchor *me)
1104 {
1105     return (me ? me->content_type : NULL);
1106 }
1107 
1108 /*	Last-Modified header handling. - FM
1109 */
HTAnchor_last_modified(HTParentAnchor * me)1110 const char *HTAnchor_last_modified(HTParentAnchor *me)
1111 {
1112     return (me ? me->last_modified : NULL);
1113 }
1114 
1115 /*	Date header handling. - FM
1116 */
HTAnchor_date(HTParentAnchor * me)1117 const char *HTAnchor_date(HTParentAnchor *me)
1118 {
1119     return (me ? me->date : NULL);
1120 }
1121 
1122 /*	Server header handling. - FM
1123 */
HTAnchor_server(HTParentAnchor * me)1124 const char *HTAnchor_server(HTParentAnchor *me)
1125 {
1126     return (me ? me->server : NULL);
1127 }
1128 
1129 /*	Safe header handling. - FM
1130 */
HTAnchor_safe(HTParentAnchor * me)1131 BOOL HTAnchor_safe(HTParentAnchor *me)
1132 {
1133     return (BOOL) (me ? me->safe : FALSE);
1134 }
1135 
1136 /*	Content-Base header handling. - FM
1137 */
HTAnchor_content_base(HTParentAnchor * me)1138 const char *HTAnchor_content_base(HTParentAnchor *me)
1139 {
1140     return (me ? me->content_base : NULL);
1141 }
1142 
1143 /*	Content-Location header handling. - FM
1144 */
HTAnchor_content_location(HTParentAnchor * me)1145 const char *HTAnchor_content_location(HTParentAnchor *me)
1146 {
1147     return (me ? me->content_location : NULL);
1148 }
1149 
1150 /*	Message-ID, used for mail replies - kw
1151 */
HTAnchor_messageID(HTParentAnchor * me)1152 const char *HTAnchor_messageID(HTParentAnchor *me)
1153 {
1154     return (me ? me->message_id : NULL);
1155 }
1156 
HTAnchor_setMessageID(HTParentAnchor * me,const char * messageid)1157 BOOL HTAnchor_setMessageID(HTParentAnchor *me,
1158 			   const char *messageid)
1159 {
1160     if (!(me && messageid && *messageid)) {
1161 	return FALSE;
1162     }
1163     StrAllocCopy(me->message_id, messageid);
1164     return TRUE;
1165 }
1166 
1167 /*	Subject, used for mail replies - kw
1168 */
HTAnchor_subject(HTParentAnchor * me)1169 const char *HTAnchor_subject(HTParentAnchor *me)
1170 {
1171     return (me ? me->subject : NULL);
1172 }
1173 
HTAnchor_setSubject(HTParentAnchor * me,const char * subject)1174 BOOL HTAnchor_setSubject(HTParentAnchor *me,
1175 			 const char *subject)
1176 {
1177     if (!(me && subject && *subject)) {
1178 	return FALSE;
1179     }
1180     StrAllocCopy(me->subject, subject);
1181     return TRUE;
1182 }
1183 
1184 /*	Manipulation of links
1185  *	---------------------
1186  */
HTAnchor_followLink(HTChildAnchor * me)1187 HTAnchor *HTAnchor_followLink(HTChildAnchor *me)
1188 {
1189     return (me->dest);
1190 }
1191 
HTAnchor_followTypedLink(HTChildAnchor * me,HTLinkType * type)1192 HTAnchor *HTAnchor_followTypedLink(HTChildAnchor *me,
1193 				   HTLinkType *type)
1194 {
1195     if (me->type == type)
1196 	return (me->dest);
1197     return (NULL);		/* No link of me type */
1198 }
1199 
1200 /*	Methods List
1201  *	------------
1202  */
HTAnchor_methods(HTParentAnchor * me)1203 HTList *HTAnchor_methods(HTParentAnchor *me)
1204 {
1205     if (!me->methods) {
1206 	me->methods = HTList_new();
1207     }
1208     return (me->methods);
1209 }
1210 
1211 /*	Protocol
1212  *	--------
1213  */
HTAnchor_protocol(HTParentAnchor * me)1214 void *HTAnchor_protocol(HTParentAnchor *me)
1215 {
1216     return (me->protocol);
1217 }
1218 
HTAnchor_setProtocol(HTParentAnchor * me,void * protocol)1219 void HTAnchor_setProtocol(HTParentAnchor *me,
1220 			  void *protocol)
1221 {
1222     me->protocol = protocol;
1223 }
1224 
1225 /*	Physical Address
1226  *	----------------
1227  */
HTAnchor_physical(HTParentAnchor * me)1228 char *HTAnchor_physical(HTParentAnchor *me)
1229 {
1230     return (me->physical);
1231 }
1232 
HTAnchor_setPhysical(HTParentAnchor * me,char * physical)1233 void HTAnchor_setPhysical(HTParentAnchor *me,
1234 			  char *physical)
1235 {
1236     if (me) {
1237 	StrAllocCopy(me->physical, physical);
1238     }
1239 }
1240 
1241 #ifdef DEBUG
show_stages(HTParentAnchor * me,const char * tag,int which_stage)1242 static void show_stages(HTParentAnchor *me, const char *tag, int which_stage)
1243 {
1244     int j;
1245 
1246     CTRACE((tfp, "Stages %s*%s", NonNull(me->charset), tag));
1247     for (j = 0; j < UCT_STAGEMAX; j++) {
1248 	CTRACE((tfp, " "));
1249 	if (j == which_stage)
1250 	    CTRACE((tfp, "("));
1251 	CTRACE((tfp, "%d:%d:%s",
1252 		j,
1253 		me->UCStages->s[j].LYhndl,
1254 		NonNull(me->UCStages->s[j].C.MIMEname)));
1255 	if (j == which_stage)
1256 	    CTRACE((tfp, ")"));
1257     }
1258     CTRACE((tfp, "\n"));
1259 }
1260 #else
1261 #define show_stages(me,tag,which_stage)		/* nothing */
1262 #endif
1263 
1264 /*
1265  *  We store charset info in the HTParentAnchor object, for several
1266  *  "stages".  (See UCDefs.h)
1267  *  A stream method is supposed to know what stage in the model it is.
1268  *
1269  *  General model	MIME	 ->  parser  ->  structured  ->  HText
1270  *  e.g., text/html
1271  *	from HTTP:	HTMIME.c ->  SGML.c  ->  HTML.c      ->  GridText.c
1272  *     text/plain
1273  *	from file:	HTFile.c ->  HTPlain.c		     ->  GridText.c
1274  *
1275  *  The lock/set_by is used to lock e.g. a charset set by an explicit
1276  *  HTTP MIME header against overriding by a HTML META tag - the MIME
1277  *  header has higher priority.  Defaults (from -assume_.. options etc.)
1278  *  will not override charset explicitly given by server.
1279  *
1280  *  Some advantages of keeping this in the HTAnchor:
1281  *  - Global variables are bad.
1282  *  - Can remember a charset given by META tag when toggling to SOURCE view.
1283  *  - Can remember a charset given by <A CHARSET=...> href in another doc.
1284  *
1285  *  We don't modify the HTParentAnchor's charset element
1286  *  here, that one will only be set when explicitly given.
1287  */
HTAnchor_getUCInfoStage(HTParentAnchor * me,int which_stage)1288 LYUCcharset *HTAnchor_getUCInfoStage(HTParentAnchor *me,
1289 				     int which_stage)
1290 {
1291     LYUCcharset *result = NULL;
1292 
1293     if (me) {
1294 	if (!me->UCStages) {
1295 	    int i;
1296 	    int chndl = UCLYhndl_for_unspec;	/* always >= 0 */
1297 	    UCAnchorInfo *stages = typecalloc(UCAnchorInfo);
1298 
1299 	    if (stages == NULL)
1300 		outofmem(__FILE__, "HTAnchor_getUCInfoStage");
1301 
1302 	    assert(stages != NULL);
1303 
1304 	    for (i = 0; i < UCT_STAGEMAX; i++) {
1305 		stages->s[i].C.MIMEname = "";
1306 		stages->s[i].LYhndl = -1;
1307 	    }
1308 	    if (me->charset) {
1309 		chndl = UCGetLYhndl_byMIME(me->charset);
1310 		if (chndl < 0)
1311 		    chndl = UCLYhndl_for_unrec;
1312 		if (chndl < 0)
1313 		    /*
1314 		     * UCLYhndl_for_unrec not defined :-(
1315 		     * fallback to UCLYhndl_for_unspec which always valid.
1316 		     */
1317 		    chndl = UCLYhndl_for_unspec;	/* always >= 0 */
1318 	    }
1319 	    MemCpy(&stages->s[UCT_STAGE_MIME].C, &LYCharSet_UC[chndl],
1320 		   sizeof(LYUCcharset));
1321 
1322 	    stages->s[UCT_STAGE_MIME].lock = UCT_SETBY_DEFAULT;
1323 	    stages->s[UCT_STAGE_MIME].LYhndl = chndl;
1324 	    me->UCStages = stages;
1325 	}
1326 	result = (&me->UCStages->s[which_stage].C);
1327 	show_stages(me, "_getUCInfoStage", which_stage);
1328     }
1329     return (result);
1330 }
1331 
HTAnchor_getUCLYhndl(HTParentAnchor * me,int which_stage)1332 int HTAnchor_getUCLYhndl(HTParentAnchor *me,
1333 			 int which_stage)
1334 {
1335     if (me) {
1336 	if (!me->UCStages) {
1337 	    /*
1338 	     * This will allocate and initialize, if not yet done.
1339 	     */
1340 	    (void) HTAnchor_getUCInfoStage(me, which_stage);
1341 	}
1342 	if (me->UCStages->s[which_stage].lock > UCT_SETBY_NONE) {
1343 	    return (me->UCStages->s[which_stage].LYhndl);
1344 	}
1345     }
1346     return (-1);
1347 }
1348 
1349 #ifdef CAN_SWITCH_DISPLAY_CHARSET
setup_switch_display_charset(HTParentAnchor * me,int h)1350 static void setup_switch_display_charset(HTParentAnchor *me, int h)
1351 {
1352     if (!Switch_Display_Charset(h, SWITCH_DISPLAY_CHARSET_MAYBE))
1353 	return;
1354     HTAnchor_setUCInfoStage(me, current_char_set,
1355 			    UCT_STAGE_HTEXT, UCT_SETBY_MIME);	/* highest priorty! */
1356     HTAnchor_setUCInfoStage(me, current_char_set,
1357 			    UCT_STAGE_STRUCTURED, UCT_SETBY_MIME);	/* highest priorty! */
1358     CTRACE((tfp,
1359 	    "changing UCInfoStage: HTEXT/STRUCTURED stages charset='%s'.\n",
1360 	    LYCharSet_UC[current_char_set].MIMEname));
1361 }
1362 #endif
1363 
HTAnchor_setUCInfoStage(HTParentAnchor * me,int LYhndl,int which_stage,int set_by)1364 LYUCcharset *HTAnchor_setUCInfoStage(HTParentAnchor *me,
1365 				     int LYhndl,
1366 				     int which_stage,
1367 				     int set_by)
1368 {
1369     if (me) {
1370 	/*
1371 	 * This will allocate and initialize, if not yet done.
1372 	 */
1373 	LYUCcharset *p = HTAnchor_getUCInfoStage(me, which_stage);
1374 
1375 	/*
1376 	 * Can we override?
1377 	 */
1378 	if (set_by >= me->UCStages->s[which_stage].lock) {
1379 #ifdef CAN_SWITCH_DISPLAY_CHARSET
1380 	    int ohandle = me->UCStages->s[which_stage].LYhndl;
1381 #endif
1382 	    me->UCStages->s[which_stage].lock = set_by;
1383 	    me->UCStages->s[which_stage].LYhndl = LYhndl;
1384 	    if (LYhndl >= 0) {
1385 		MemCpy(p, &LYCharSet_UC[LYhndl], sizeof(LYUCcharset));
1386 
1387 #ifdef CAN_SWITCH_DISPLAY_CHARSET
1388 		/* Allow a switch to a more suitable display charset */
1389 		if (LYhndl != ohandle && which_stage == UCT_STAGE_PARSER)
1390 		    setup_switch_display_charset(me, LYhndl);
1391 #endif
1392 	    } else {
1393 		p->UChndl = -1;
1394 	    }
1395 	    show_stages(me, "_setUCInfoStage", which_stage);
1396 	    return (p);
1397 	}
1398     }
1399     return (NULL);
1400 }
1401 
HTAnchor_resetUCInfoStage(HTParentAnchor * me,int LYhndl,int which_stage,int set_by)1402 LYUCcharset *HTAnchor_resetUCInfoStage(HTParentAnchor *me,
1403 				       int LYhndl,
1404 				       int which_stage,
1405 				       int set_by)
1406 {
1407     LYUCcharset *result = NULL;
1408     int ohandle;
1409 
1410     if (me && me->UCStages) {
1411 	me->UCStages->s[which_stage].lock = set_by;
1412 	ohandle = me->UCStages->s[which_stage].LYhndl;
1413 	me->UCStages->s[which_stage].LYhndl = LYhndl;
1414 #ifdef CAN_SWITCH_DISPLAY_CHARSET
1415 	/* Allow a switch to a more suitable display charset */
1416 	if (LYhndl >= 0 && LYhndl != ohandle
1417 	    && which_stage == UCT_STAGE_PARSER)
1418 	    setup_switch_display_charset(me, LYhndl);
1419 #else
1420 	(void) ohandle;
1421 #endif
1422 	show_stages(me, "_resetUCInfoStage", which_stage);
1423 	result = (&me->UCStages->s[which_stage].C);
1424     }
1425     return result;
1426 }
1427 
1428 /*
1429  *  A set_by of (-1) means use the lock value from the from_stage.
1430  */
HTAnchor_copyUCInfoStage(HTParentAnchor * me,int to_stage,int from_stage,int set_by)1431 LYUCcharset *HTAnchor_copyUCInfoStage(HTParentAnchor *me,
1432 				      int to_stage,
1433 				      int from_stage,
1434 				      int set_by)
1435 {
1436     if (me) {
1437 	/*
1438 	 * This will allocate and initialize, if not yet done.
1439 	 */
1440 	LYUCcharset *p_from = HTAnchor_getUCInfoStage(me, from_stage);
1441 	LYUCcharset *p_to = HTAnchor_getUCInfoStage(me, to_stage);
1442 
1443 	/*
1444 	 * Can we override?
1445 	 */
1446 	if (set_by == -1)
1447 	    set_by = me->UCStages->s[from_stage].lock;
1448 	if (set_by == UCT_SETBY_NONE)
1449 	    set_by = UCT_SETBY_DEFAULT;
1450 	if (set_by >= me->UCStages->s[to_stage].lock) {
1451 #ifdef CAN_SWITCH_DISPLAY_CHARSET
1452 	    int ohandle = me->UCStages->s[to_stage].LYhndl;
1453 #endif
1454 	    me->UCStages->s[to_stage].lock = set_by;
1455 	    me->UCStages->s[to_stage].LYhndl =
1456 		me->UCStages->s[from_stage].LYhndl;
1457 #ifdef CAN_SWITCH_DISPLAY_CHARSET
1458 	    /* Allow a switch to a more suitable display charset */
1459 	    if (me->UCStages->s[to_stage].LYhndl >= 0
1460 		&& me->UCStages->s[to_stage].LYhndl != ohandle
1461 		&& to_stage == UCT_STAGE_PARSER)
1462 		setup_switch_display_charset(me,
1463 					     me->UCStages->s[to_stage].LYhndl);
1464 #endif
1465 	    if (p_to != p_from)
1466 		MemCpy(p_to, p_from, sizeof(LYUCcharset));
1467 
1468 	    return (p_to);
1469 	}
1470     }
1471     return (NULL);
1472 }
1473