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