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