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