1 /*
2  * $LynxId: HTML.c,v 1.161 2013/06/12 09:18:40 tom Exp $
3  *
4  *		Structured stream to Rich hypertext converter
5  *		============================================
6  *
7  *	This generates a hypertext object.  It converts from the
8  *	structured stream interface of HTML events into the style-
9  *	oriented interface of the HText.h interface.  This module is
10  *	only used in clients and should not be linked into servers.
11  *
12  *	Override this module if making a new GUI browser.
13  *
14  *   Being Overidden
15  *
16  */
17 
18 #define HTSTREAM_INTERNAL 1
19 
20 #include <HTUtils.h>
21 
22 #define Lynx_HTML_Handler
23 #include <HTChunk.h>
24 #include <HText.h>
25 #include <HTStyle.h>
26 #include <HTML.h>
27 
28 #include <HTCJK.h>
29 #include <HTAtom.h>
30 #include <HTAnchor.h>
31 #include <HTMLGen.h>
32 #include <HTParse.h>
33 #include <HTList.h>
34 #include <UCMap.h>
35 #include <UCDefs.h>
36 #include <UCAux.h>
37 
38 #include <LYGlobalDefs.h>
39 #include <LYCharUtils.h>
40 #include <LYCharSets.h>
41 
42 #include <HTAlert.h>
43 #include <HTForms.h>
44 #include <HTNestedList.h>
45 #include <GridText.h>
46 #include <LYStrings.h>
47 #include <LYUtils.h>
48 #include <LYMap.h>
49 #include <LYList.h>
50 #include <LYBookmark.h>
51 #include <LYHistory.h>
52 
53 #ifdef VMS
54 #include <LYCurses.h>
55 #endif /* VMS */
56 
57 #ifdef USE_PRETTYSRC
58 #include <LYPrettySrc.h>
59 #endif
60 
61 #ifdef USE_COLOR_STYLE
62 #include <SGML.h>
63 #include <AttrList.h>
64 #include <LYHash.h>
65 #include <LYStyle.h>
66 #undef SELECTED_STYLES
67 #define pHText_changeStyle(X,Y,Z) {}
68 
69 #if OMIT_SCN_KEEPING
70 # define HCODE_TO_STACK_OFF(x) /*(CSHASHSIZE+1)*/ 88888		/*special value. */
71 #else
72 # define HCODE_TO_STACK_OFF(x) x	/*pass computed value */
73 #endif
74 
75 #endif /* USE_COLOR_STYLE */
76 
77 #ifdef USE_SOURCE_CACHE
78 #include <HTAccess.h>
79 #endif
80 
81 #include <LYCurses.h>
82 #include <LYJustify.h>
83 
84 #include <LYexit.h>
85 #include <LYLeaks.h>
86 
87 #define STACKLEVEL(me) ((me->stack + MAX_NESTING - 1) - me->sp)
88 
89 #define DFT_TEXTAREA_COLS 60
90 #define DFT_TEXTAREA_ROWS 4
91 
92 #define MAX_TEXTAREA_COLS LYcolLimit
93 #define MAX_TEXTAREA_ROWS (3 * LYlines)
94 
95 #define LimitValue(name, value) \
96  	if (name > value) { \
97 		CTRACE((tfp, "Limited " #name " to %d, was %d\n", \
98 			value, name)); \
99 		name = value; \
100 	}
101 
102 struct _HTStream {
103     const HTStreamClass *isa;
104 #ifdef USE_SOURCE_CACHE
105     HTParentAnchor *anchor;
106     FILE *fp;
107     char *filename;
108     HTChunk *chunk;
109     HTChunk *last_chunk;	/* the last chunk in a chain! */
110     const HTStreamClass *actions;
111     HTStream *target;
112     int status;
113 #else
114     /* .... */
115 #endif
116 };
117 
118 static HTStyleSheet *styleSheet = NULL;		/* Application-wide */
119 
120 /*	Module-wide style cache
121 */
122 static HTStyle *styles[HTML_ELEMENTS + LYNX_HTML_EXTRA_ELEMENTS];
123 
124 					   /* adding 24 nested list styles  */
125 					   /* and 3 header alignment styles */
126 					   /* and 3 div alignment styles    */
127 static HTStyle *default_style = NULL;
128 
129 const char *LYToolbarName = "LynxPseudoToolbar";
130 
131 /* used to turn off a style if the HTML author forgot to
132 static int i_prior_style = -1;
133  */
134 
135 /*
136  *	Private function....
137  */
138 static int HTML_end_element(HTStructured * me, int element_number,
139 			    char **include);
140 
141 static int HTML_start_element(HTStructured * me, int element_number,
142 			      const BOOL *present,
143 			      STRING2PTR value,
144 			      int tag_charset,
145 			      char **include);
146 
147 /*
148  * If we have verbose_img set, display labels for images.
149  */
150 #define VERBOSE_IMG(value,src_type,string) \
151       ((verbose_img) ? (newtitle = MakeNewTitle(value,src_type)): string)
152 
153 static char *MakeNewTitle(STRING2PTR value, int src_type);
154 static char *MakeNewImageValue(STRING2PTR value);
155 static char *MakeNewMapValue(STRING2PTR value, const char *mapstr);
156 
157 /*	Set an internal flag that the next call to a stack-affecting method
158  *	is only internal and the stack manipulation should be skipped. - kw
159  */
160 #define SET_SKIP_STACK(el_num) if (HTML_dtd.tags[el_num].contents != SGML_EMPTY) \
161 						{ me->skip_stack++; }
162 
strtolower(char * i)163 void strtolower(char *i)
164 {
165     if (!i)
166 	return;
167     while (*i) {
168 	*i = (char) TOLOWER(*i);
169 	i++;
170     }
171 }
172 
173 /*		Flattening the style structure
174  *		------------------------------
175  *
176  * On the NeXT, and on any read-only browser, it is simpler for the text to
177  * have a sequence of styles, rather than a nested tree of styles.  In this
178  * case we have to flatten the structure as it arrives from SGML tags into a
179  * sequence of styles.
180  */
181 
182 /*
183  *  If style really needs to be set, call this.
184  */
actually_set_style(HTStructured * me)185 void actually_set_style(HTStructured * me)
186 {
187     if (!me->text) {		/* First time through */
188 	LYGetChartransInfo(me);
189 	UCSetTransParams(&me->T,
190 			 me->UCLYhndl, me->UCI,
191 			 HTAnchor_getUCLYhndl(me->node_anchor,
192 					      UCT_STAGE_HTEXT),
193 			 HTAnchor_getUCInfoStage(me->node_anchor,
194 						 UCT_STAGE_HTEXT));
195 	me->text = HText_new2(me->node_anchor, me->target);
196 	HText_beginAppend(me->text);
197 	HText_setStyle(me->text, me->new_style);
198 	me->in_word = NO;
199 	LYCheckForContentBase(me);
200     } else {
201 	HText_setStyle(me->text, me->new_style);
202     }
203 
204     me->old_style = me->new_style;
205     me->style_change = NO;
206 }
207 
208 /*
209  *  If you THINK you need to change style, call this.
210  */
change_paragraph_style(HTStructured * me,HTStyle * style)211 static void change_paragraph_style(HTStructured * me, HTStyle *style)
212 {
213     if (me->new_style != style) {
214 	me->style_change = YES;
215 	me->new_style = style;
216     }
217     me->in_word = NO;
218 }
219 
220 /*
221  * Return true if we should write a message (to LYNXMESSAGES, or the trace
222  * file) telling about some bad HTML that we've found.
223  */
LYBadHTML(HTStructured * me)224 BOOL LYBadHTML(HTStructured * me)
225 {
226     BOOL code = FALSE;
227 
228     switch ((enumBadHtml) cfg_bad_html) {
229     case BAD_HTML_IGNORE:
230 	break;
231     case BAD_HTML_TRACE:
232 	code = TRUE;
233 	break;
234     case BAD_HTML_MESSAGE:
235 	code = TRUE;
236 	break;
237     case BAD_HTML_WARN:
238 	/*
239 	 * If we're already tracing, do not add a warning.
240 	 */
241 	if (!TRACE && !me->inBadHTML) {
242 	    HTUserMsg(BAD_HTML_USE_TRACE);
243 	    me->inBadHTML = TRUE;
244 	}
245 	code = TRACE;
246 	break;
247     }
248     return code;
249 }
250 
251 /*
252  * Handle the formatted message.
253  */
LYShowBadHTML(const char * message)254 void LYShowBadHTML(const char *message)
255 {
256     switch ((enumBadHtml) cfg_bad_html) {
257     case BAD_HTML_IGNORE:
258 	break;
259     case BAD_HTML_TRACE:
260 	CTRACE((tfp, "%s", message));
261 	break;
262     case BAD_HTML_MESSAGE:
263 	CTRACE((tfp, "%s", message));
264 	LYstore_message(message);
265 	break;
266     case BAD_HTML_WARN:
267 	CTRACE((tfp, "%s", message));
268 	break;
269     }
270 }
271 
272 /*_________________________________________________________________________
273  *
274  *			A C T I O N	R O U T I N E S
275  */
276 
277 /* FIXME:  this should be amended to do the substitution only when not in a
278  * multibyte stream.
279  */
280 #ifdef EXP_JAPANESE_SPACES
281 #define FIX_JAPANESE_SPACES \
282 	(HTCJK == CHINESE || HTCJK == JAPANESE || HTCJK == TAIPEI)
283 	/* don't replace '\n' with ' ' if Chinese or Japanese - HN
284 	 */
285 #else
286 #define FIX_JAPANESE_SPACES 0
287 #endif
288 
289 /*	Character handling
290  *	------------------
291  */
HTML_put_character(HTStructured * me,int c)292 void HTML_put_character(HTStructured * me, int c)
293 {
294     unsigned uc = UCH(c);
295 
296     /*
297      * Ignore all non-MAP content when just scanning a document for MAPs.  - FM
298      */
299     if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT)
300 	return;
301 
302     c = (int) uc;
303 
304     /*
305      * Do EOL conversion if needed.  - FM
306      *
307      * Convert EOL styles:
308      *   macintosh:  cr    --> lf
309      *   ascii:      cr-lf --> lf
310      *   unix:       lf    --> lf
311      */
312     if ((me->lastraw == '\r') && c == '\n') {
313 	me->lastraw = -1;
314 	return;
315     }
316     me->lastraw = c;
317     if (c == '\r') {
318 	c = '\n';
319 	uc = UCH(c);
320     }
321 
322     /*
323      * Handle SGML_LITTERAL tags that have HTChunk elements.  - FM
324      */
325     switch (me->sp[0].tag_number) {
326 
327     case HTML_COMMENT:
328 	return;			/* Do Nothing */
329 
330     case HTML_TITLE:
331 	if (c == LY_SOFT_HYPHEN)
332 	    return;
333 	if (c != '\n' && c != '\t' && c != '\r') {
334 	    HTChunkPutc(&me->title, uc);
335 	} else if (FIX_JAPANESE_SPACES) {
336 	    if (c == '\t') {
337 		HTChunkPutc(&me->title, ' ');
338 	    } else {
339 		return;
340 	    }
341 	} else {
342 	    HTChunkPutc(&me->title, ' ');
343 	}
344 	return;
345 
346     case HTML_STYLE:
347 	HTChunkPutc(&me->style_block, uc);
348 	return;
349 
350     case HTML_SCRIPT:
351 	HTChunkPutc(&me->script, uc);
352 	return;
353 
354     case HTML_OBJECT:
355 	HTChunkPutc(&me->object, uc);
356 	return;
357 
358     case HTML_TEXTAREA:
359 	HTChunkPutc(&me->textarea, uc);
360 	return;
361 
362     case HTML_SELECT:
363     case HTML_OPTION:
364 	HTChunkPutc(&me->option, uc);
365 	return;
366 
367     case HTML_MATH:
368 	HTChunkPutc(&me->math, uc);
369 	return;
370 
371     default:
372 	if (me->inSELECT) {
373 	    /*
374 	     * If we are within a SELECT not caught by the cases above -
375 	     * HTML_SELECT or HTML_OPTION may not be the last element pushed on
376 	     * the style stack if there were invalid markup tags within a
377 	     * SELECT element.  For error recovery, treat text as part of the
378 	     * OPTION text, it is probably meant to show up as user-visible
379 	     * text.  Having A as an open element while in SELECT is really
380 	     * sick, don't make anchor text part of the option text in that
381 	     * case since the option text will probably just be discarded.  -
382 	     * kw
383 	     */
384 	    if (me->sp[0].tag_number == HTML_A)
385 		break;
386 	    HTChunkPutc(&me->option, uc);
387 	    return;
388 	}
389 	break;
390     }				/* end first switch */
391 
392     /*
393      * Handle all other tag content.  - FM
394      */
395     switch (me->sp[0].tag_number) {
396 
397     case HTML_PRE:		/* Formatted text */
398 	/*
399 	 * We guarantee that the style is up-to-date in begin_litteral. But we
400 	 * still want to strip \rs.
401 	 */
402 	if (c != '\r' &&
403 	    !(c == '\n' && me->inLABEL && !me->inP) &&
404 	    !(c == '\n' && !me->inPRE)) {
405 	    me->inP = TRUE;
406 	    me->inLABEL = FALSE;
407 	    HText_appendCharacter(me->text, c);
408 	}
409 	me->inPRE = TRUE;
410 	break;
411 
412     case HTML_LISTING:		/* Literal text */
413     case HTML_XMP:
414     case HTML_PLAINTEXT:
415 	/*
416 	 * We guarantee that the style is up-to-date in begin_litteral.  But we
417 	 * still want to strip \rs.
418 	 */
419 	if (c != '\r') {
420 	    me->inP = TRUE;
421 	    me->inLABEL = FALSE;
422 	    HText_appendCharacter(me->text, c);
423 	}
424 	break;
425 
426     default:
427 	/*
428 	 * Free format text.
429 	 */
430 	if (me->sp->style->id == ST_Preformatted) {
431 	    if (c != '\r' &&
432 		!(c == '\n' && me->inLABEL && !me->inP) &&
433 		!(c == '\n' && !me->inPRE)) {
434 		me->inP = TRUE;
435 		me->inLABEL = FALSE;
436 		HText_appendCharacter(me->text, c);
437 	    }
438 	    me->inPRE = TRUE;
439 
440 	} else if (me->sp->style->id == ST_Listing ||
441 		   me->sp->style->id == ST_Example) {
442 	    if (c != '\r') {
443 		me->inP = TRUE;
444 		me->inLABEL = FALSE;
445 		HText_appendCharacter(me->text, c);
446 	    }
447 
448 	} else {
449 	    if (me->style_change) {
450 		if ((c == '\n') || (c == ' '))
451 		    return;	/* Ignore it */
452 		UPDATE_STYLE;
453 	    }
454 	    if (c == '\n') {
455 		if (!FIX_JAPANESE_SPACES) {
456 		    if (me->in_word) {
457 			if (HText_getLastChar(me->text) != ' ') {
458 			    me->inP = TRUE;
459 			    me->inLABEL = FALSE;
460 			    HText_appendCharacter(me->text, ' ');
461 			}
462 			me->in_word = NO;
463 		    }
464 		}
465 
466 	    } else if (c == ' ' || c == '\t') {
467 		if (HText_getLastChar(me->text) != ' ') {
468 		    me->inP = TRUE;
469 		    me->inLABEL = FALSE;
470 		    HText_appendCharacter(me->text, ' ');
471 		}
472 
473 	    } else if (c == '\r') {
474 		/* ignore */
475 
476 	    } else {
477 		me->inP = TRUE;
478 		me->inLABEL = FALSE;
479 		HText_appendCharacter(me->text, c);
480 		me->in_word = YES;
481 	    }
482 	}
483     }				/* end second switch */
484 
485     if (c == '\n' || c == '\t') {
486 	HText_setLastChar(me->text, ' ');	/* set it to a generic separator */
487     } else {
488 	HText_setLastChar(me->text, c);
489     }
490 }
491 
492 /*	String handling
493  *	---------------
494  *
495  *	This is written separately from put_character because the loop can
496  *	in some cases be promoted to a higher function call level for speed.
497  */
HTML_put_string(HTStructured * me,const char * s)498 void HTML_put_string(HTStructured * me, const char *s)
499 {
500 #ifdef USE_PRETTYSRC
501     char *translated_string = NULL;
502 #endif
503 
504     if (s == NULL || (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT))
505 	return;
506 #ifdef USE_PRETTYSRC
507     if (psrc_convert_string) {
508 	StrAllocCopy(translated_string, s);
509 	TRANSLATE_AND_UNESCAPE_ENTITIES(&translated_string, TRUE, FALSE);
510 	s = (const char *) translated_string;
511     }
512 #endif
513 
514     switch (me->sp[0].tag_number) {
515 
516     case HTML_COMMENT:
517 	break;			/* Do Nothing */
518 
519     case HTML_TITLE:
520 	HTChunkPuts(&me->title, s);
521 	break;
522 
523     case HTML_STYLE:
524 	HTChunkPuts(&me->style_block, s);
525 	break;
526 
527     case HTML_SCRIPT:
528 	HTChunkPuts(&me->script, s);
529 	break;
530 
531     case HTML_PRE:		/* Formatted text */
532     case HTML_LISTING:		/* Literal text */
533     case HTML_XMP:
534     case HTML_PLAINTEXT:
535 	/*
536 	 * We guarantee that the style is up-to-date in begin_litteral
537 	 */
538 	HText_appendText(me->text, s);
539 	break;
540 
541     case HTML_OBJECT:
542 	HTChunkPuts(&me->object, s);
543 	break;
544 
545     case HTML_TEXTAREA:
546 	HTChunkPuts(&me->textarea, s);
547 	break;
548 
549     case HTML_SELECT:
550     case HTML_OPTION:
551 	HTChunkPuts(&me->option, s);
552 	break;
553 
554     case HTML_MATH:
555 	HTChunkPuts(&me->math, s);
556 	break;
557 
558     default:			/* Free format text? */
559 	if (!me->sp->style->freeFormat) {
560 	    /*
561 	     * If we are within a preformatted text style not caught by the
562 	     * cases above (HTML_PRE or similar may not be the last element
563 	     * pushed on the style stack).  - kw
564 	     */
565 #ifdef USE_PRETTYSRC
566 	    if (psrc_view) {
567 		/*
568 		 * We do this so that a raw '\r' in the string will not be
569 		 * interpreted as an internal request to break a line - passing
570 		 * '\r' to HText_appendText is treated by it as a request to
571 		 * insert a blank line - VH
572 		 */
573 		for (; *s; ++s)
574 		    HTML_put_character(me, *s);
575 	    } else
576 #endif
577 		HText_appendText(me->text, s);
578 	    break;
579 	} else {
580 	    const char *p = s;
581 	    char c;
582 
583 	    if (me->style_change) {
584 		for (; *p && ((*p == '\n') || (*p == '\r') ||
585 			      (*p == ' ') || (*p == '\t')); p++) ;	/* Ignore leaders */
586 		if (!*p)
587 		    break;
588 		UPDATE_STYLE;
589 	    }
590 	    for (; *p; p++) {
591 		if (*p == 13 && p[1] != 10) {
592 		    /*
593 		     * Treat any '\r' which is not followed by '\n' as '\n', to
594 		     * account for macintosh lineend in ALT attributes etc.  -
595 		     * kw
596 		     */
597 		    c = '\n';
598 		} else {
599 		    c = *p;
600 		}
601 		if (me->style_change) {
602 		    if ((c == '\n') || (c == ' ') || (c == '\t'))
603 			continue;	/* Ignore it */
604 		    UPDATE_STYLE;
605 		}
606 		if (c == '\n') {
607 		    if (!FIX_JAPANESE_SPACES) {
608 			if (me->in_word) {
609 			    if (HText_getLastChar(me->text) != ' ')
610 				HText_appendCharacter(me->text, ' ');
611 			    me->in_word = NO;
612 			}
613 		    }
614 
615 		} else if (c == ' ' || c == '\t') {
616 		    if (HText_getLastChar(me->text) != ' ')
617 			HText_appendCharacter(me->text, ' ');
618 
619 		} else if (c == '\r') {
620 		    /* ignore */
621 		} else {
622 		    HText_appendCharacter(me->text, c);
623 		    me->in_word = YES;
624 		}
625 
626 		/* set the Last Character */
627 		if (c == '\n' || c == '\t') {
628 		    /* set it to a generic separator */
629 		    HText_setLastChar(me->text, ' ');
630 		} else if (c == '\r' &&
631 			   HText_getLastChar(me->text) == ' ') {
632 		    /*
633 		     * \rs are ignored.  In order to keep collapsing spaces
634 		     * correctly, we must default back to the previous
635 		     * separator, if there was one.  So we set LastChar to a
636 		     * generic separator.
637 		     */
638 		    HText_setLastChar(me->text, ' ');
639 		} else {
640 		    HText_setLastChar(me->text, c);
641 		}
642 
643 	    }			/* for */
644 	}
645     }				/* end switch */
646 #ifdef USE_PRETTYSRC
647     if (psrc_convert_string) {
648 	psrc_convert_string = FALSE;
649 	FREE(translated_string);
650     }
651 #endif
652 }
653 
654 /*	Buffer write
655  *	------------
656  */
HTML_write(HTStructured * me,const char * s,int l)657 void HTML_write(HTStructured * me, const char *s, int l)
658 {
659     const char *p;
660     const char *e = s + l;
661 
662     if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT)
663 	return;
664 
665     for (p = s; p < e; p++)
666 	HTML_put_character(me, *p);
667 }
668 
669 /*
670  *  "Internal links" are hyperlinks whose source and destination are
671  *  within the same document, and for which the destination is given
672  *  as a URL Reference with an empty URL, but possibly with a non-empty
673  *  #fragment.	(This terminology re URL-Reference vs. URL follows the
674  *  Fielding URL syntax and semantics drafts).
675  *  Differences:
676  *  (1) The document's base (in whatever way it is given) is not used for
677  *	resolving internal link references.
678  *  (2) Activating an internal link should not result in a new retrieval
679  *	of a copy of the document.
680  *  (3) Internal links are the only way to refer with a hyperlink to a document
681  *	(or a location in it) which is only known as the result of a POST
682  *	request (doesn't have a URL from which the document can be retrieved
683  *	with GET), and can only be used from within that document.
684  *
685  * *If track_internal_links is true, we keep track of whether a
686  *  link destination was given as an internal link.  This information is
687  *  recorded in the type of the link between anchor objects, and is available
688  *  to the HText object and the mainloop from there.  URL References to
689  *  internal destinations are still resolved into an absolute form before
690  *  being passed on, but using the current stream's retrieval address instead
691  *  of the base URL.
692  *  Examples:  (replace [...] to have a valid absolute URL)
693  *  In document retrieved from [...]/mypath/mydoc.htm w/ base [...]/otherpath/
694  *  a. HREF="[...]/mypath/mydoc.htm"	  -> [...]/mypath/mydoc.htm
695  *  b. HREF="[...]/mypath/mydoc.htm#frag" -> [...]/mypath/mydoc.htm#frag
696  *  c. HREF="mydoc.htm"			  -> [...]/otherpath/mydoc.htm
697  *  d. HREF="mydoc.htm#frag"		  -> [...]/otherpath/mydoc.htm#frag
698  *  e. HREF=""		      -> [...]/mypath/mydoc.htm      (marked internal)
699  *  f. HREF="#frag"	      -> [...]/mypath/mydoc.htm#frag (marked internal)
700  *
701  * *If track_internal_links is false, URL-less URL-References are
702  *  resolved differently from URL-References with a non-empty URL (using the
703  *  current stream's retrieval address instead of the base), but we make no
704  *  further distinction.  Resolution is then as in the examples above, execept
705  *  that there is no "(marked internal)".
706  *
707  * *Note that this doesn't apply to form ACTIONs (always resolved using base,
708  *  never marked internal).  Also other references encountered or generated
709  *  are not marked internal, whether they have a URL or not, if in a given
710  *  context an internal link makes no sense (e.g., IMG SRC=).
711  */
712 
713 /* A flag is used to keep track of whether an "URL reference" encountered
714    had a real "URL" or not.  In the latter case, it will be marked as
715    "internal".	The flag is set before we start messing around with the
716    string (resolution of relative URLs etc.).  This variable only used
717    locally here, don't confuse with LYinternal_flag which is for
718    overriding non-caching similar to LYoverride_no_cache. - kw */
719 #define CHECK_FOR_INTERN(flag,s) \
720    	flag = (BOOLEAN) (((s) && (*(s)=='#' || *(s)=='\0')) ? TRUE : FALSE)
721 
722 /* Last argument to pass to HTAnchor_findChildAndLink() calls,
723    just an abbreviation. - kw */
724 #define INTERN_CHK(flag) (HTLinkType *)((flag) ? HTInternalLink : NULL)
725 #define INTERN_LT         INTERN_CHK(intern_flag)
726 
727 #ifdef USE_COLOR_STYLE
728 static char *Style_className = 0;
729 static char *Style_className_end = 0;
730 static size_t Style_className_len = 0;
731 static int hcode;
732 
733 #ifdef LY_FIND_LEAKS
free_Style_className(void)734 static void free_Style_className(void)
735 {
736     FREE(Style_className);
737 }
738 #endif
739 
addClassName(const char * prefix,const char * actual,size_t length)740 static void addClassName(const char *prefix,
741 			 const char *actual,
742 			 size_t length)
743 {
744     size_t offset = strlen(prefix);
745     size_t have = (unsigned) (Style_className_end - Style_className);
746     size_t need = (offset + length + 1);
747 
748     if ((have + need) >= Style_className_len) {
749 	Style_className_len += 1024 + 2 * (have + need);
750 	if (Style_className == 0) {
751 	    Style_className = typeMallocn(char, Style_className_len);
752 	} else {
753 	    Style_className = typeRealloc(char, Style_className, Style_className_len);
754 	}
755 	if (Style_className == NULL)
756 	    outofmem(__FILE__, "addClassName");
757 	assert(Style_className != NULL);
758 	Style_className_end = Style_className + have;
759     }
760     if (offset)
761 	strcpy(Style_className_end, prefix);
762     if (length)
763 	memcpy(Style_className_end + offset, actual, length);
764     Style_className_end[offset + length] = '\0';
765     strtolower(Style_className_end);
766 
767     Style_className_end += (offset + length);
768 }
769 #else
770 #define addClassName(prefix, actual, length)	/* nothing */
771 #endif
772 
773 #ifdef USE_PRETTYSRC
774 
HTMLSRC_apply_markup(HTStructured * context,HTlexeme lexeme,int start,int tag_charset)775 static void HTMLSRC_apply_markup(HTStructured * context, HTlexeme lexeme, int start,
776 				 int tag_charset)
777 {
778     HT_tagspec *ts = *((start ? lexeme_start : lexeme_end) + lexeme);
779 
780     while (ts) {
781 #ifdef USE_COLOR_STYLE
782 	if (ts->start) {
783 	    current_tag_style = ts->style;
784 	    force_current_tag_style = TRUE;
785 	    forced_classname = ts->class_name;
786 	    force_classname = TRUE;
787 	}
788 #endif
789 	CTRACE((tfp, ts->start ? "SRCSTART %d\n" : "SRCSTOP %d\n", (int) lexeme));
790 	if (ts->start)
791 	    HTML_start_element(context,
792 			       (int) ts->element,
793 			       ts->present,
794 			       (STRING2PTR) ts->value,
795 			       tag_charset,
796 			       NULL);
797 	else
798 	    HTML_end_element(context,
799 			     (int) ts->element,
800 			     NULL);
801 	ts = ts->next;
802     }
803 }
804 
805 #  define START TRUE
806 #  define STOP FALSE
807 
808 #  define PSRCSTART(x)	HTMLSRC_apply_markup(me,HTL_##x,START,tag_charset)
809 #  define PSRCSTOP(x)  HTMLSRC_apply_markup(me,HTL_##x,STOP,tag_charset)
810 
811 #  define PUTC(x) HTML_put_character(me,x)
812 #  define PUTS(x) HTML_put_string(me,x)
813 
814 #endif /* USE_PRETTYSRC */
815 
LYStartArea(HTStructured * obj,const char * href,const char * alt,const char * title,int tag_charset)816 static void LYStartArea(HTStructured * obj, const char *href,
817 			const char *alt,
818 			const char *title,
819 			int tag_charset)
820 {
821     BOOL new_present[HTML_AREA_ATTRIBUTES];
822     const char *new_value[HTML_AREA_ATTRIBUTES];
823     int i;
824 
825     for (i = 0; i < HTML_AREA_ATTRIBUTES; i++)
826 	new_present[i] = NO;
827 
828     if (alt) {
829 	new_present[HTML_AREA_ALT] = YES;
830 	new_value[HTML_AREA_ALT] = (const char *) alt;
831     }
832     if (non_empty(title)) {
833 	new_present[HTML_AREA_TITLE] = YES;
834 	new_value[HTML_AREA_TITLE] = (const char *) title;
835     }
836     if (href) {
837 	new_present[HTML_AREA_HREF] = YES;
838 	new_value[HTML_AREA_HREF] = (const char *) href;
839     }
840 
841     (*obj->isa->start_element) (obj, HTML_AREA, new_present, new_value,
842 				tag_charset, 0);
843 }
844 
LYHandleFIG(HTStructured * me,const BOOL * present,STRING2PTR value,int isobject,int imagemap,const char * id,const char * src,int convert,int start,BOOL * intern_flag GCC_UNUSED)845 static void LYHandleFIG(HTStructured * me, const BOOL *present,
846 			STRING2PTR value,
847 			int isobject,
848 			int imagemap,
849 			const char *id,
850 			const char *src,
851 			int convert,
852 			int start,
853 			BOOL *intern_flag GCC_UNUSED)
854 {
855     if (start == TRUE) {
856 	me->inFIG = TRUE;
857 	if (me->inA) {
858 	    SET_SKIP_STACK(HTML_A);
859 	    HTML_end_element(me, HTML_A, NULL);
860 	}
861 	if (!isobject) {
862 	    LYEnsureDoubleSpace(me);
863 	    LYResetParagraphAlignment(me);
864 	    me->inFIGwithP = TRUE;
865 	} else {
866 	    me->inFIGwithP = FALSE;
867 	    HTML_put_character(me, ' ');	/* space char may be ignored */
868 	}
869 	if (non_empty(id)) {
870 	    if (present && convert) {
871 		CHECK_ID(HTML_FIG_ID);
872 	    } else
873 		LYHandleID(me, id);
874 	}
875 	me->in_word = NO;
876 	me->inP = FALSE;
877 
878 	if (clickable_images && non_empty(src)) {
879 	    char *href = NULL;
880 
881 	    StrAllocCopy(href, src);
882 	    CHECK_FOR_INTERN(*intern_flag, href);
883 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
884 	    if (*href) {
885 		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
886 							 NULL,	/* Tag */
887 							 href,	/* Addresss */
888 							 INTERN_CHK(*intern_flag));	/* Type */
889 		HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
890 		if (me->inBoldH == FALSE)
891 		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
892 		HTML_put_string(me, (isobject
893 				     ? (imagemap
894 					? "(IMAGE)"
895 					: "(OBJECT)")
896 				     : "[FIGURE]"));
897 		if (me->inBoldH == FALSE)
898 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
899 		HText_endAnchor(me->text, 0);
900 		HTML_put_character(me, '-');
901 		HTML_put_character(me, ' ');	/* space char may be ignored */
902 		me->in_word = NO;
903 	    }
904 	    FREE(href);
905 	}
906     } else {			/* handle end tag */
907 	if (me->inFIGwithP) {
908 	    LYEnsureDoubleSpace(me);
909 	} else {
910 	    HTML_put_character(me, ' ');	/* space char may be ignored */
911 	}
912 	LYResetParagraphAlignment(me);
913 	me->inFIGwithP = FALSE;
914 	me->inFIG = FALSE;
915 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
916 	if (me->List_Nesting_Level >= 0) {
917 	    UPDATE_STYLE;
918 	    HText_NegateLineOne(me->text);
919 	}
920     }
921 }
922 
clear_objectdata(HTStructured * me)923 static void clear_objectdata(HTStructured * me)
924 {
925     if (me) {
926 	HTChunkClear(&me->object);
927 	me->object_started = FALSE;
928 	me->object_declare = FALSE;
929 	me->object_shapes = FALSE;
930 	me->object_ismap = FALSE;
931 	FREE(me->object_usemap);
932 	FREE(me->object_id);
933 	FREE(me->object_title);
934 	FREE(me->object_data);
935 	FREE(me->object_type);
936 	FREE(me->object_classid);
937 	FREE(me->object_codebase);
938 	FREE(me->object_codetype);
939 	FREE(me->object_name);
940     }
941 }
942 
943 #define HTParseALL(pp,pconst)  \
944 	{ char* free_me = *pp; \
945 	  *pp = HTParse(*pp, pconst, PARSE_ALL); \
946 	  FREE(free_me);       \
947 	}
948 
949 /*	Start Element
950  *	-------------
951  */
HTML_start_element(HTStructured * me,int element_number,const BOOL * present,STRING2PTR value,int tag_charset,char ** include)952 static int HTML_start_element(HTStructured * me, int element_number,
953 			      const BOOL *present,
954 			      STRING2PTR value,
955 			      int tag_charset,
956 			      char **include)
957 {
958     char *alt_string = NULL;
959     char *id_string = NULL;
960     char *newtitle = NULL;
961     char **pdoctitle = NULL;
962     char *href = NULL;
963     char *map_href = NULL;
964     char *title = NULL;
965     char *I_value = NULL;
966     char *I_name = NULL;
967     char *temp = NULL;
968     const char *Base = NULL;
969     int dest_char_set = -1;
970     HTParentAnchor *dest = NULL;	/* An anchor's destination */
971     BOOL dest_ismap = FALSE;	/* Is dest an image map script? */
972     HTChildAnchor *ID_A = NULL;	/* HTML_foo_ID anchor */
973     int url_type = 0, i = 0;
974     char *cp = NULL;
975     HTMLElement ElementNumber = (HTMLElement) element_number;
976     BOOL intern_flag = FALSE;
977     short stbl_align = HT_ALIGN_NONE;
978     int status = HT_OK;
979 
980 #ifdef USE_COLOR_STYLE
981     char *class_name;
982     int class_used = 0;
983 #endif
984 
985 #ifdef USE_PRETTYSRC
986     if (psrc_view && !sgml_in_psrc_was_initialized) {
987 	if (!psrc_nested_call) {
988 	    HTTag *tag = &HTML_dtd.tags[element_number];
989 	    char buf[200];
990 	    const char *p;
991 
992 	    if (psrc_first_tag) {
993 		psrc_first_tag = FALSE;
994 		/* perform the special actions on the begining of the document.
995 		   It's assumed that all lynx modules start generating html
996 		   from tag (ie not a text) so we are able to trap this moment
997 		   and initialize.
998 		 */
999 		psrc_nested_call = TRUE;
1000 		HTML_start_element(me, HTML_BODY, NULL, NULL, tag_charset, NULL);
1001 		HTML_start_element(me, HTML_PRE, NULL, NULL, tag_charset, NULL);
1002 		PSRCSTART(entire);
1003 		psrc_nested_call = FALSE;
1004 	    }
1005 
1006 	    psrc_nested_call = TRUE;
1007 	    /*write markup for tags and exit */
1008 	    PSRCSTART(abracket);
1009 	    PUTC('<');
1010 	    PSRCSTOP(abracket);
1011 	    PSRCSTART(tag);
1012 	    if (tagname_transform != 0)
1013 		PUTS(tag->name);
1014 	    else {
1015 		LYStrNCpy(buf, tag->name, sizeof(buf) - 1);
1016 		LYLowerCase(buf);
1017 		PUTS(buf);
1018 	    }
1019 	    if (present) {
1020 		for (i = 0; i < tag->number_of_attributes; i++)
1021 		    if (present[i]) {
1022 			PUTC(' ');
1023 			PSRCSTART(attrib);
1024 			if (attrname_transform != 0)
1025 			    PUTS(tag->attributes[i].name);
1026 			else {
1027 			    LYStrNCpy(buf,
1028 				      tag->attributes[i].name,
1029 				      sizeof(buf) - 1);
1030 			    LYLowerCase(buf);
1031 			    PUTS(buf);
1032 			}
1033 			if (value[i]) {
1034 			    char q = '"';
1035 
1036 			    /*0 in dquotes, 1 - in quotes, 2 mixed */
1037 			    char kind = (char) (!strchr(value[i], '"') ?
1038 						0 :
1039 						!strchr(value[i], '\'') ?
1040 						q = '\'', 1 :
1041 						2);
1042 
1043 			    PUTC('=');
1044 			    PSRCSTOP(attrib);
1045 			    PSRCSTART(attrval);
1046 			    PUTC(q);
1047 			    /*is it special ? */
1048 			    if (tag->attributes[i].type == HTMLA_ANAME) {
1049 				HTStartAnchor(me, value[i], NULL);
1050 				HTML_end_element(me, HTML_A, NULL);
1051 			    } else if (tag->attributes[i].type == HTMLA_HREF) {
1052 				PSRCSTART(href);
1053 				HTStartAnchor(me, NULL, value[i]);
1054 			    }
1055 			    if (kind != 2)
1056 				PUTS(value[i]);
1057 			    else
1058 				for (p = value[i]; *p; p++)
1059 				    if (*p != '"')
1060 					PUTC(*p);
1061 				    else
1062 					PUTS("&#34;");
1063 			    /*is it special ? */
1064 			    if (tag->attributes[i].type == HTMLA_HREF) {
1065 				HTML_end_element(me, HTML_A, NULL);
1066 				PSRCSTOP(href);
1067 			    }
1068 			    PUTC(q);
1069 			    PSRCSTOP(attrval);
1070 			}	/* if value */
1071 		    }		/* if present[i] */
1072 	    }			/* if present */
1073 	    PSRCSTOP(tag);
1074 	    PSRCSTART(abracket);
1075 	    PUTC('>');
1076 	    PSRCSTOP(abracket);
1077 	    psrc_nested_call = FALSE;
1078 	    return HT_OK;
1079 	}			/*if (!psrc_nested_call) */
1080 	/*fall through */
1081     }
1082 #endif /* USE_PRETTYSRC */
1083 
1084     if (LYMapsOnly) {
1085 	if (!(ElementNumber == HTML_MAP || ElementNumber == HTML_AREA ||
1086 	      ElementNumber == HTML_BASE || ElementNumber == HTML_OBJECT ||
1087 	      ElementNumber == HTML_A)) {
1088 	    return HT_OK;
1089 	}
1090     } else if (!me->text) {
1091 	UPDATE_STYLE;
1092     } {
1093 	/*  me->tag_charset  is charset for attribute values.  */
1094 	int j = ((tag_charset < 0) ? me->UCLYhndl : tag_charset);
1095 
1096 	if ((me->tag_charset != j) || (j < 0 /* for trace entry */ )) {
1097 	    CTRACE((tfp, "me->tag_charset: %d -> %d", me->tag_charset, j));
1098 	    CTRACE((tfp, " (me->UCLYhndl: %d, tag_charset: %d)\n",
1099 		    me->UCLYhndl, tag_charset));
1100 	    me->tag_charset = j;
1101 	}
1102     }
1103 
1104 /* this should be done differently */
1105 #if defined(USE_COLOR_STYLE)
1106 
1107     addClassName(";",
1108 		 HTML_dtd.tags[element_number].name,
1109 		 (size_t) HTML_dtd.tags[element_number].name_len);
1110 
1111     class_name = (force_classname ? forced_classname : class_string);
1112     force_classname = FALSE;
1113 
1114     if (force_current_tag_style == FALSE) {
1115 	current_tag_style = (class_name[0]
1116 			     ? -1
1117 			     : cached_tag_styles[element_number]);
1118     } else {
1119 	force_current_tag_style = FALSE;
1120     }
1121 
1122     CTRACE2(TRACE_STYLE, (tfp, "CSS.elt:<%s>\n", HTML_dtd.tags[element_number].name));
1123 
1124     if (current_tag_style == -1) {	/* Append class_name */
1125 	hcode = hash_code_lowercase_on_fly(HTML_dtd.tags[element_number].name);
1126 	if (class_name[0]) {
1127 	    int ohcode = hcode;
1128 
1129 	    hcode = hash_code_aggregate_char('.', hcode);
1130 	    hcode = hash_code_aggregate_lower_str(class_name, hcode);
1131 	    if (!hashStyles[hcode].name) {	/* None such -> classless version */
1132 		hcode = ohcode;
1133 		CTRACE2(TRACE_STYLE,
1134 			(tfp,
1135 			 "STYLE.start_element: <%s> (class <%s> not configured), hcode=%d.\n",
1136 			 HTML_dtd.tags[element_number].name, class_name, hcode));
1137 	    } else {
1138 		addClassName(".", class_name, strlen(class_name));
1139 
1140 		CTRACE2(TRACE_STYLE,
1141 			(tfp, "STYLE.start_element: <%s>.<%s>, hcode=%d.\n",
1142 			 HTML_dtd.tags[element_number].name, class_name, hcode));
1143 		class_used = 1;
1144 	    }
1145 	}
1146 
1147 	class_string[0] = '\0';
1148 
1149     } else {			/* (current_tag_style!=-1)  */
1150 	if (class_name[0]) {
1151 	    addClassName(".", class_name, strlen(class_name));
1152 	    class_string[0] = '\0';
1153 	}
1154 	hcode = current_tag_style;
1155 	CTRACE2(TRACE_STYLE,
1156 		(tfp, "STYLE.start_element: <%s>, hcode=%d.\n",
1157 		 HTML_dtd.tags[element_number].name, hcode));
1158 	current_tag_style = -1;
1159     }
1160 
1161 #if !OMIT_SCN_KEEPING		/* Can be done in other cases too... */
1162     if (!class_used && ElementNumber == HTML_INPUT) {	/* For some other too? */
1163 	const char *type = "";
1164 	int ohcode = hcode;
1165 
1166 	if (present && present[HTML_INPUT_TYPE] && value[HTML_INPUT_TYPE])
1167 	    type = value[HTML_INPUT_TYPE];
1168 
1169 	hcode = hash_code_aggregate_lower_str(".type.", hcode);
1170 	hcode = hash_code_aggregate_lower_str(type, hcode);
1171 	if (!hashStyles[hcode].name) {	/* None such -> classless version */
1172 	    hcode = ohcode;
1173 	    CTRACE2(TRACE_STYLE,
1174 		    (tfp, "STYLE.start_element: type <%s> not configured.\n",
1175 		     type));
1176 	} else {
1177 	    addClassName(".type.", type, strlen(type));
1178 
1179 	    CTRACE2(TRACE_STYLE,
1180 		    (tfp, "STYLE.start_element: <%s>.type.<%s>, hcode=%d.\n",
1181 		     HTML_dtd.tags[element_number].name, type, hcode));
1182 	}
1183     }
1184 #endif /* !OMIT_SCN_KEEPING */
1185 
1186     HText_characterStyle(me->text, hcode, STACK_ON);
1187 #endif /* USE_COLOR_STYLE */
1188 
1189     /*
1190      * Handle the start tag.  - FM
1191      */
1192     switch (ElementNumber) {
1193 
1194     case HTML_HTML:
1195 	break;
1196 
1197     case HTML_HEAD:
1198 	break;
1199 
1200     case HTML_BASE:
1201 	if (present && present[HTML_BASE_HREF] && !local_host_only &&
1202 	    non_empty(value[HTML_BASE_HREF])) {
1203 	    char *base = NULL;
1204 	    const char *related = NULL;
1205 
1206 	    StrAllocCopy(base, value[HTML_BASE_HREF]);
1207 	    CTRACE((tfp, "*HTML_BASE: initial href=`%s'\n", NonNull(base)));
1208 
1209 	    if (!(url_type = LYLegitimizeHREF(me, &base, TRUE, TRUE))) {
1210 		CTRACE((tfp, "HTML: BASE '%s' is not an absolute URL.\n",
1211 			NonNull(base)));
1212 		if (me->inBadBASE == FALSE)
1213 		    HTAlert(BASE_NOT_ABSOLUTE);
1214 		me->inBadBASE = TRUE;
1215 	    }
1216 
1217 	    if (url_type == LYNXIMGMAP_URL_TYPE) {
1218 		/*
1219 		 * These have a non-standard form, basically strip the prefix
1220 		 * or the code below would insert a nonsense host into the
1221 		 * pseudo URL.  These should never occur where they would be
1222 		 * used for resolution of relative URLs anyway.  We can also
1223 		 * strip the #map part.  - kw
1224 		 */
1225 		temp = base;
1226 		base = HTParse(base + 11, "", PARSE_ALL_WITHOUT_ANCHOR);
1227 		FREE(temp);
1228 	    }
1229 
1230 	    /*
1231 	     * Get parent's address for defaulted fields.
1232 	     */
1233 	    related = me->node_anchor->address;
1234 
1235 	    /*
1236 	     * Create the access field.
1237 	     */
1238 	    temp = HTParse(base, related, PARSE_ACCESS + PARSE_PUNCTUATION);
1239 	    StrAllocCopy(me->base_href, temp);
1240 	    FREE(temp);
1241 
1242 	    /*
1243 	     * Create the host[:port] field.
1244 	     */
1245 	    temp = HTParse(base, "", PARSE_HOST + PARSE_PUNCTUATION);
1246 	    if (!StrNCmp(temp, "//", 2)) {
1247 		StrAllocCat(me->base_href, temp);
1248 		if (!strcmp(me->base_href, "file://")) {
1249 		    StrAllocCat(me->base_href, "localhost");
1250 		}
1251 	    } else {
1252 		if (isFILE_URL(me->base_href)) {
1253 		    StrAllocCat(me->base_href, "//localhost");
1254 		} else if (strcmp(me->base_href, STR_NEWS_URL)) {
1255 		    FREE(temp);
1256 		    StrAllocCat(me->base_href, (temp = HTParse(related, "",
1257 							       PARSE_HOST + PARSE_PUNCTUATION)));
1258 		}
1259 	    }
1260 	    FREE(temp);
1261 
1262 	    /*
1263 	     * Create the path field.
1264 	     */
1265 	    temp = HTParse(base, "", PARSE_PATH + PARSE_PUNCTUATION);
1266 	    if (*temp != '\0') {
1267 		char *p = strchr(temp, '?');
1268 
1269 		if (p)
1270 		    *p = '\0';
1271 		p = strrchr(temp, '/');
1272 		if (p)
1273 		    *(p + 1) = '\0';	/* strip after the last slash */
1274 
1275 		StrAllocCat(me->base_href, temp);
1276 	    } else if (!strcmp(me->base_href, STR_NEWS_URL)) {
1277 		StrAllocCat(me->base_href, "*");
1278 	    } else if (isNEWS_URL(me->base_href) ||
1279 		       isNNTP_URL(me->base_href) ||
1280 		       isSNEWS_URL(me->base_href)) {
1281 		StrAllocCat(me->base_href, "/*");
1282 	    } else {
1283 		StrAllocCat(me->base_href, "/");
1284 	    }
1285 	    FREE(temp);
1286 	    FREE(base);
1287 
1288 	    me->inBASE = TRUE;
1289 	    me->node_anchor->inBASE = TRUE;
1290 	    StrAllocCopy(me->node_anchor->content_base, me->base_href);
1291 	    /* me->base_href is a valid URL */
1292 
1293 	    CTRACE((tfp, "*HTML_BASE: final href=`%s'\n", me->base_href));
1294 	}
1295 	break;
1296 
1297     case HTML_META:
1298 	if (present)
1299 	    LYHandleMETA(me, present, value, include);
1300 	break;
1301 
1302     case HTML_TITLE:
1303 	HTChunkClear(&me->title);
1304 	break;
1305 
1306     case HTML_LINK:
1307 	intern_flag = FALSE;
1308 	if (present && present[HTML_LINK_HREF]) {
1309 	    CHECK_FOR_INTERN(intern_flag, value[HTML_LINK_HREF]);
1310 	    /*
1311 	     * Prepare to do housekeeping on the reference.  - FM
1312 	     */
1313 	    if (isEmpty(value[HTML_LINK_HREF])) {
1314 		Base = (me->inBASE)
1315 		    ? me->base_href
1316 		    : me->node_anchor->address;
1317 		StrAllocCopy(href, Base);
1318 	    } else {
1319 		StrAllocCopy(href, value[HTML_LINK_HREF]);
1320 		(void) LYLegitimizeHREF(me, &href, TRUE, TRUE);
1321 
1322 		Base = (me->inBASE && *href != '\0' && *href != '#')
1323 		    ? me->base_href
1324 		    : me->node_anchor->address;
1325 		HTParseALL(&href, Base);
1326 	    }
1327 
1328 	    /*
1329 	     * Handle links with a REV attribute.  - FM
1330 	     * Handle REV="made" or REV="owner".  - LM & FM
1331 	     * Handle REL="author" -TD
1332 	     */
1333 	    if (present &&
1334 		((present[HTML_LINK_REV] &&
1335 		  value[HTML_LINK_REV] &&
1336 		  (!strcasecomp("made", value[HTML_LINK_REV]) ||
1337 		   !strcasecomp("owner", value[HTML_LINK_REV]))) ||
1338 		 (present[HTML_LINK_REL] &&
1339 		  value[HTML_LINK_REL] &&
1340 		  (!strcasecomp("author", value[HTML_LINK_REL]))))) {
1341 		/*
1342 		 * Load the owner element.  - FM
1343 		 */
1344 		HTAnchor_setOwner(me->node_anchor, href);
1345 		CTRACE((tfp, "HTML: DOC OWNER '%s' found\n", href));
1346 		FREE(href);
1347 
1348 		/*
1349 		 * Load the RevTitle element if a TITLE attribute and value
1350 		 * are present.  - FM
1351 		 */
1352 		if (present && present[HTML_LINK_TITLE] &&
1353 		    value[HTML_LINK_TITLE] &&
1354 		    *value[HTML_LINK_TITLE] != '\0') {
1355 		    StrAllocCopy(title, value[HTML_LINK_TITLE]);
1356 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
1357 		    LYTrimHead(title);
1358 		    LYTrimTail(title);
1359 		    if (*title != '\0')
1360 			HTAnchor_setRevTitle(me->node_anchor, title);
1361 		    FREE(title);
1362 		}
1363 		break;
1364 	    }
1365 
1366 	    /*
1367 	     * Handle REL links.  - FM
1368 	     */
1369 
1370 	    if (present &&
1371 		present[HTML_LINK_REL] && value[HTML_LINK_REL]) {
1372 		/*
1373 		 * Ignore style sheets, for now.  - FM
1374 		 *
1375 		 * lss and css have different syntax - lynx shouldn't try to
1376 		 * parse them now (it tries to parse them as lss, so it exits
1377 		 * with error message on the 1st non-empty line) - VH
1378 		 */
1379 #ifndef USE_COLOR_STYLE
1380 		if (!strcasecomp(value[HTML_LINK_REL], "StyleSheet") ||
1381 		    !strcasecomp(value[HTML_LINK_REL], "Style")) {
1382 		    CTRACE2(TRACE_STYLE,
1383 			    (tfp, "HTML: StyleSheet link found.\n"));
1384 		    CTRACE2(TRACE_STYLE,
1385 			    (tfp, "        StyleSheets not yet implemented.\n"));
1386 		    FREE(href);
1387 		    break;
1388 		}
1389 #endif /* ! USE_COLOR_STYLE */
1390 
1391 		/*
1392 		 * Ignore anything not registered in the 28-Mar-95 IETF HTML
1393 		 * 3.0 draft and W3C HTML 3.2 draft, or not appropriate for
1394 		 * Lynx banner links in the expired Maloney and Quin relrev
1395 		 * draft.  We'll make this more efficient when the situation
1396 		 * stabilizes, and for now, we'll treat "Banner" as another
1397 		 * toolbar element.  - FM
1398 		 */
1399 		if (!strcasecomp(value[HTML_LINK_REL], "Home") ||
1400 		    !strcasecomp(value[HTML_LINK_REL], "ToC") ||
1401 		    !strcasecomp(value[HTML_LINK_REL], "Contents") ||
1402 		    !strcasecomp(value[HTML_LINK_REL], "Index") ||
1403 		    !strcasecomp(value[HTML_LINK_REL], "Glossary") ||
1404 		    !strcasecomp(value[HTML_LINK_REL], "Copyright") ||
1405 		    !strcasecomp(value[HTML_LINK_REL], "Help") ||
1406 		    !strcasecomp(value[HTML_LINK_REL], "Search") ||
1407 		    !strcasecomp(value[HTML_LINK_REL], "Bookmark") ||
1408 		    !strcasecomp(value[HTML_LINK_REL], "Banner") ||
1409 		    !strcasecomp(value[HTML_LINK_REL], "Top") ||
1410 		    !strcasecomp(value[HTML_LINK_REL], "Origin") ||
1411 		    !strcasecomp(value[HTML_LINK_REL], "Navigator") ||
1412 		    !strcasecomp(value[HTML_LINK_REL], "Disclaimer") ||
1413 		    !strcasecomp(value[HTML_LINK_REL], "Author") ||
1414 		    !strcasecomp(value[HTML_LINK_REL], "Editor") ||
1415 		    !strcasecomp(value[HTML_LINK_REL], "Publisher") ||
1416 		    !strcasecomp(value[HTML_LINK_REL], "Trademark") ||
1417 		    !strcasecomp(value[HTML_LINK_REL], "Hotlist") ||
1418 		    !strcasecomp(value[HTML_LINK_REL], "Begin") ||
1419 		    !strcasecomp(value[HTML_LINK_REL], "First") ||
1420 		    !strcasecomp(value[HTML_LINK_REL], "End") ||
1421 		    !strcasecomp(value[HTML_LINK_REL], "Last") ||
1422 		    !strcasecomp(value[HTML_LINK_REL], "Documentation") ||
1423 		    !strcasecomp(value[HTML_LINK_REL], "Biblioentry") ||
1424 		    !strcasecomp(value[HTML_LINK_REL], "Bibliography") ||
1425 		    !strcasecomp(value[HTML_LINK_REL], "Start") ||
1426 		    !strcasecomp(value[HTML_LINK_REL], "Appendix")) {
1427 		    StrAllocCopy(title, value[HTML_LINK_REL]);
1428 		    pdoctitle = &title;		/* for setting HTAnchor's title */
1429 		} else if (!strcasecomp(value[HTML_LINK_REL], "Up") ||
1430 			   !strcasecomp(value[HTML_LINK_REL], "Next") ||
1431 			   !strcasecomp(value[HTML_LINK_REL], "Previous") ||
1432 			   !strcasecomp(value[HTML_LINK_REL], "Prev") ||
1433 			   !strcasecomp(value[HTML_LINK_REL], "Child") ||
1434 			   !strcasecomp(value[HTML_LINK_REL], "Sibling") ||
1435 			   !strcasecomp(value[HTML_LINK_REL], "Parent") ||
1436 			   !strcasecomp(value[HTML_LINK_REL], "Meta") ||
1437 			   !strcasecomp(value[HTML_LINK_REL], "URC") ||
1438 			   !strcasecomp(value[HTML_LINK_REL], "Pointer") ||
1439 			   !strcasecomp(value[HTML_LINK_REL], "Translation") ||
1440 			   !strcasecomp(value[HTML_LINK_REL], "Definition") ||
1441 			   !strcasecomp(value[HTML_LINK_REL], "Alternate") ||
1442 			   !strcasecomp(value[HTML_LINK_REL], "Section") ||
1443 			   !strcasecomp(value[HTML_LINK_REL], "Subsection") ||
1444 			   !strcasecomp(value[HTML_LINK_REL], "Chapter")) {
1445 		    StrAllocCopy(title, value[HTML_LINK_REL]);
1446 		    /* not setting target HTAnchor's title, for these
1447 		       links of highly relative character.  Instead,
1448 		       try to remember the REL attribute as a property
1449 		       of the link (but not the destination), in the
1450 		       (otherwise underused) link type in a special format;
1451 		       the LIST page generation code may later use it. - kw */
1452 		    if (!intern_flag) {
1453 			StrAllocCopy(temp, "RelTitle: ");
1454 			StrAllocCat(temp, value[HTML_LINK_REL]);
1455 		    }
1456 #ifndef DISABLE_BIBP
1457 		} else if (!strcasecomp(value[HTML_LINK_REL], "citehost")) {
1458 		    /*  Citehost determination for bibp links. - RDC */
1459 		    HTAnchor_setCitehost(me->node_anchor, href);
1460 		    CTRACE((tfp, "HTML: citehost '%s' found\n", href));
1461 		    FREE(href);
1462 		    break;
1463 #endif
1464 		} else {
1465 		    CTRACE((tfp, "HTML: LINK with REL=\"%s\" ignored.\n",
1466 			    value[HTML_LINK_REL]));
1467 		    FREE(href);
1468 		    break;
1469 		}
1470 	    }
1471 	} else if (present &&
1472 		   present[HTML_LINK_REL] && value[HTML_LINK_REL]) {
1473 	    /*
1474 	     * If no HREF was specified, handle special REL links with
1475 	     * self-designated HREFs.  - FM
1476 	     */
1477 	    if (!strcasecomp(value[HTML_LINK_REL], "Home")) {
1478 		StrAllocCopy(href, LynxHome);
1479 	    } else if (!strcasecomp(value[HTML_LINK_REL], "Help")) {
1480 		StrAllocCopy(href, helpfile);
1481 	    } else if (!strcasecomp(value[HTML_LINK_REL], "Index")) {
1482 		StrAllocCopy(href, indexfile);
1483 	    } else {
1484 		CTRACE((tfp,
1485 			"HTML: LINK with REL=\"%s\" and no HREF ignored.\n",
1486 			value[HTML_LINK_REL]));
1487 		break;
1488 	    }
1489 	    StrAllocCopy(title, value[HTML_LINK_REL]);
1490 	    pdoctitle = &title;
1491 	}
1492 	if (href) {
1493 	    /*
1494 	     * Create a title (link name) from the TITLE value, if present, or
1495 	     * default to the REL value that was loaded into title.  - FM
1496 	     */
1497 	    if (present && present[HTML_LINK_TITLE] &&
1498 		non_empty(value[HTML_LINK_TITLE])) {
1499 		StrAllocCopy(title, value[HTML_LINK_TITLE]);
1500 		TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
1501 		LYTrimHead(title);
1502 		LYTrimTail(title);
1503 		pdoctitle = &title;
1504 		FREE(temp);	/* forget about recording RelTitle - kw */
1505 	    }
1506 	    if (isEmpty(title)) {
1507 		FREE(href);
1508 		FREE(title);
1509 		break;
1510 	    }
1511 
1512 	    if (me->inA) {
1513 		/*
1514 		 * Ugh!  The LINK tag, which is a HEAD element, is in an
1515 		 * Anchor, which is BODY element.  All we can do is close the
1516 		 * Anchor and cross our fingers.  - FM
1517 		 */
1518 		SET_SKIP_STACK(HTML_A);
1519 		HTML_end_element(me, HTML_A, include);
1520 	    }
1521 
1522 	    /*
1523 	     * Create anchors for the links that simulate a toolbar.  - FM
1524 	     */
1525 	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
1526 						     NULL,	/* Tag */
1527 						     href,	/* Addresss */
1528 						     (temp
1529 						      ? (HTLinkType *)
1530 						      HTAtom_for(temp)
1531 						      : INTERN_LT));	/* Type */
1532 	    FREE(temp);
1533 	    if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
1534 		 )) != NULL) {
1535 		if (pdoctitle && !HTAnchor_title(dest))
1536 		    HTAnchor_setTitle(dest, *pdoctitle);
1537 
1538 		/* Don't allow CHARSET attribute to change *this* document's
1539 		   charset assumption. - kw */
1540 		if (dest == me->node_anchor)
1541 		    dest = NULL;
1542 		if (present[HTML_LINK_CHARSET] &&
1543 		    non_empty(value[HTML_LINK_CHARSET])) {
1544 		    dest_char_set = UCGetLYhndl_byMIME(value[HTML_LINK_CHARSET]);
1545 		    if (dest_char_set < 0)
1546 			dest_char_set = UCLYhndl_for_unrec;
1547 		}
1548 		if (dest && dest_char_set >= 0)
1549 		    HTAnchor_setUCInfoStage(dest, dest_char_set,
1550 					    UCT_STAGE_PARSER,
1551 					    UCT_SETBY_LINK);
1552 	    }
1553 	    UPDATE_STYLE;
1554 	    if (!HText_hasToolbar(me->text) &&
1555 		(ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
1556 						  LYToolbarName,	/* Tag */
1557 						  NULL,		/* Addresss */
1558 						  (HTLinkType *) 0))) {		/* Type */
1559 		HText_appendCharacter(me->text, '#');
1560 		HText_setLastChar(me->text, ' ');	/* absorb white space */
1561 		HText_beginAnchor(me->text, me->inUnderline, ID_A);
1562 		HText_endAnchor(me->text, 0);
1563 		HText_setToolbar(me->text);
1564 	    } else {
1565 		/*
1566 		 * Add collapsible space to separate link from previous
1567 		 * generated links.  - kw
1568 		 */
1569 		HTML_put_character(me, ' ');
1570 	    }
1571 	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
1572 	    if (me->inBoldH == FALSE)
1573 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
1574 #ifdef USE_COLOR_STYLE
1575 	    if (present && present[HTML_LINK_CLASS] &&
1576 		non_empty(value[HTML_LINK_CLASS])) {
1577 		char *tmp = 0;
1578 
1579 		HTSprintf0(&tmp, "link.%s.%s", value[HTML_LINK_CLASS], title);
1580 		CTRACE2(TRACE_STYLE,
1581 			(tfp, "STYLE.link: using style <%s>\n", tmp));
1582 
1583 		HText_characterStyle(me->text, hash_code(tmp), STACK_ON);
1584 		HTML_put_string(me, title);
1585 		HTML_put_string(me, " (");
1586 		HTML_put_string(me, value[HTML_LINK_CLASS]);
1587 		HTML_put_string(me, ")");
1588 		HText_characterStyle(me->text, hash_code(tmp), STACK_OFF);
1589 		FREE(tmp);
1590 	    } else
1591 #endif
1592 		HTML_put_string(me, title);
1593 	    if (me->inBoldH == FALSE)
1594 		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
1595 	    HText_endAnchor(me->text, 0);
1596 	}
1597 	FREE(href);
1598 	FREE(title);
1599 	break;
1600 
1601     case HTML_ISINDEX:
1602 	if (((present)) &&
1603 	    ((present[HTML_ISINDEX_HREF] && value[HTML_ISINDEX_HREF]) ||
1604 	     (present[HTML_ISINDEX_ACTION] && value[HTML_ISINDEX_ACTION]))) {
1605 	    /*
1606 	     * Lynx was supporting ACTION, which never made it into the HTML
1607 	     * 2.0 specs.  HTML 3.0 uses HREF, so we'll use that too, but allow
1608 	     * use of ACTION as an alternate until people have fully switched
1609 	     * over.  - FM
1610 	     */
1611 	    if (present[HTML_ISINDEX_HREF] && value[HTML_ISINDEX_HREF])
1612 		StrAllocCopy(href, value[HTML_ISINDEX_HREF]);
1613 	    else
1614 		StrAllocCopy(href, value[HTML_ISINDEX_ACTION]);
1615 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
1616 
1617 	    Base = (me->inBASE && *href != '\0' && *href != '#')
1618 		? me->base_href
1619 		: me->node_anchor->address;
1620 	    HTParseALL(&href, Base);
1621 	    HTAnchor_setIndex(me->node_anchor, href);
1622 	    FREE(href);
1623 
1624 	} else {
1625 	    Base = (me->inBASE) ?
1626 		me->base_href : me->node_anchor->address;
1627 	    HTAnchor_setIndex(me->node_anchor, Base);
1628 	}
1629 	/*
1630 	 * Support HTML 3.0 PROMPT attribute.  - FM
1631 	 */
1632 	if (present &&
1633 	    present[HTML_ISINDEX_PROMPT] &&
1634 	    non_empty(value[HTML_ISINDEX_PROMPT])) {
1635 	    StrAllocCopy(temp, value[HTML_ISINDEX_PROMPT]);
1636 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&temp, TRUE, FALSE);
1637 	    LYTrimHead(temp);
1638 	    LYTrimTail(temp);
1639 	    if (*temp != '\0') {
1640 		StrAllocCat(temp, " ");
1641 		HTAnchor_setPrompt(me->node_anchor, temp);
1642 	    } else {
1643 		HTAnchor_setPrompt(me->node_anchor, ENTER_DATABASE_QUERY);
1644 	    }
1645 	    FREE(temp);
1646 	} else {
1647 	    HTAnchor_setPrompt(me->node_anchor, ENTER_DATABASE_QUERY);
1648 	}
1649 	break;
1650 
1651     case HTML_NEXTID:
1652 	break;
1653 
1654     case HTML_STYLE:
1655 	/*
1656 	 * We're getting it as Literal text, which, for now, we'll just ignore.
1657 	 * - FM
1658 	 */
1659 	HTChunkClear(&me->style_block);
1660 	break;
1661 
1662     case HTML_SCRIPT:
1663 	/*
1664 	 * We're getting it as Literal text, which, for now, we'll just ignore.
1665 	 * - FM
1666 	 */
1667 	HTChunkClear(&me->script);
1668 	break;
1669 
1670     case HTML_BODY:
1671 	CHECK_ID(HTML_BODY_ID);
1672 	if (HText_hasToolbar(me->text))
1673 	    HText_appendParagraph(me->text);
1674 	break;
1675 
1676     case HTML_FRAMESET:
1677 	break;
1678 
1679     case HTML_FRAME:
1680 	if (present && present[HTML_FRAME_NAME] &&
1681 	    non_empty(value[HTML_FRAME_NAME])) {
1682 	    StrAllocCopy(id_string, value[HTML_FRAME_NAME]);
1683 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&id_string, TRUE, FALSE);
1684 	    LYTrimHead(id_string);
1685 	    LYTrimTail(id_string);
1686 	}
1687 	if (present && present[HTML_FRAME_SRC] &&
1688 	    non_empty(value[HTML_FRAME_SRC])) {
1689 	    StrAllocCopy(href, value[HTML_FRAME_SRC]);
1690 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
1691 
1692 	    if (me->inA) {
1693 		SET_SKIP_STACK(HTML_A);
1694 		HTML_end_element(me, HTML_A, include);
1695 	    }
1696 	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
1697 						     NULL,	/* Tag */
1698 						     href,	/* Addresss */
1699 						     (HTLinkType *) 0);		/* Type */
1700 	    CAN_JUSTIFY_PUSH(FALSE);
1701 	    LYEnsureSingleSpace(me);
1702 	    if (me->inUnderline == FALSE)
1703 		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
1704 	    HTML_put_string(me, "FRAME:");
1705 	    if (me->inUnderline == FALSE)
1706 		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
1707 	    HTML_put_character(me, ' ');
1708 
1709 	    me->in_word = NO;
1710 	    CHECK_ID(HTML_FRAME_ID);
1711 	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
1712 	    if (me->inBoldH == FALSE)
1713 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
1714 	    HTML_put_string(me, (id_string ? id_string : href));
1715 	    FREE(href);
1716 	    if (me->inBoldH == FALSE)
1717 		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
1718 	    HText_endAnchor(me->text, 0);
1719 	    LYEnsureSingleSpace(me);
1720 	    CAN_JUSTIFY_POP;
1721 	} else {
1722 	    CHECK_ID(HTML_FRAME_ID);
1723 	}
1724 	FREE(id_string);
1725 	break;
1726 
1727     case HTML_NOFRAMES:
1728 	LYEnsureDoubleSpace(me);
1729 	LYResetParagraphAlignment(me);
1730 	break;
1731 
1732     case HTML_IFRAME:
1733 	if (present && present[HTML_IFRAME_NAME] &&
1734 	    non_empty(value[HTML_IFRAME_NAME])) {
1735 	    StrAllocCopy(id_string, value[HTML_IFRAME_NAME]);
1736 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&id_string, TRUE, FALSE);
1737 	    LYTrimHead(id_string);
1738 	    LYTrimTail(id_string);
1739 	}
1740 	if (present && present[HTML_IFRAME_SRC] &&
1741 	    non_empty(value[HTML_IFRAME_SRC])) {
1742 	    StrAllocCopy(href, value[HTML_IFRAME_SRC]);
1743 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
1744 
1745 	    if (me->inA)
1746 		HTML_end_element(me, HTML_A, include);
1747 
1748 	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
1749 						     NULL,	/* Tag */
1750 						     href,	/* Addresss */
1751 						     (HTLinkType *) 0);		/* Type */
1752 	    LYEnsureDoubleSpace(me);
1753 	    CAN_JUSTIFY_PUSH_F
1754 		LYResetParagraphAlignment(me);
1755 	    if (me->inUnderline == FALSE)
1756 		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
1757 	    HTML_put_string(me, "IFRAME:");
1758 	    if (me->inUnderline == FALSE)
1759 		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
1760 	    HTML_put_character(me, ' ');
1761 
1762 	    me->in_word = NO;
1763 	    CHECK_ID(HTML_IFRAME_ID);
1764 	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
1765 	    if (me->inBoldH == FALSE)
1766 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
1767 	    HTML_put_string(me, (id_string ? id_string : href));
1768 	    FREE(href);
1769 	    if (me->inBoldH == FALSE)
1770 		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
1771 	    HText_endAnchor(me->text, 0);
1772 	    LYEnsureSingleSpace(me);
1773 	    CAN_JUSTIFY_POP;
1774 	} else {
1775 	    CHECK_ID(HTML_IFRAME_ID);
1776 	}
1777 	FREE(id_string);
1778 	break;
1779 
1780     case HTML_BANNER:
1781     case HTML_MARQUEE:
1782 	change_paragraph_style(me, styles[HTML_BANNER]);
1783 	UPDATE_STYLE;
1784 	if (me->sp->tag_number == (int) ElementNumber)
1785 	    LYEnsureDoubleSpace(me);
1786 	/*
1787 	 * Treat this as a toolbar if we don't have one yet, and we are in the
1788 	 * first half of the first page.  - FM
1789 	 */
1790 	if ((!HText_hasToolbar(me->text) &&
1791 	     HText_getLines(me->text) < (display_lines / 2)) &&
1792 	    (ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
1793 					      LYToolbarName,	/* Tag */
1794 					      NULL,	/* Addresss */
1795 					      (HTLinkType *) 0))) {	/* Type */
1796 	    HText_beginAnchor(me->text, me->inUnderline, ID_A);
1797 	    HText_endAnchor(me->text, 0);
1798 	    HText_setToolbar(me->text);
1799 	}
1800 	CHECK_ID(HTML_GEN_ID);
1801 	break;
1802 
1803     case HTML_CENTER:
1804     case HTML_DIV:
1805 	if (me->Division_Level < (MAX_NESTING - 1)) {
1806 	    me->Division_Level++;
1807 	} else {
1808 	    CTRACE((tfp,
1809 		    "HTML: ****** Maximum nesting of %d divisions exceeded!\n",
1810 		    MAX_NESTING));
1811 	}
1812 	if (me->inP)
1813 	    LYEnsureSingleSpace(me);	/* always at least break line - kw */
1814 	if (ElementNumber == HTML_CENTER) {
1815 	    me->DivisionAlignments[me->Division_Level] = HT_CENTER;
1816 	    change_paragraph_style(me, styles[HTML_DCENTER]);
1817 	    UPDATE_STYLE;
1818 	    me->current_default_alignment = styles[HTML_DCENTER]->alignment;
1819 	} else if (me->List_Nesting_Level >= 0 &&
1820 		   !(present && present[HTML_DIV_ALIGN] &&
1821 		     value[HTML_DIV_ALIGN] &&
1822 		     (!strcasecomp(value[HTML_DIV_ALIGN], "center") ||
1823 		      !strcasecomp(value[HTML_DIV_ALIGN], "right")))) {
1824 	    if (present && present[HTML_DIV_ALIGN])
1825 		me->current_default_alignment = HT_LEFT;
1826 	    else if (me->Division_Level == 0)
1827 		me->current_default_alignment = HT_LEFT;
1828 	    else if (me->sp[0].tag_number == HTML_UL ||
1829 		     me->sp[0].tag_number == HTML_OL ||
1830 		     me->sp[0].tag_number == HTML_MENU ||
1831 		     me->sp[0].tag_number == HTML_DIR ||
1832 		     me->sp[0].tag_number == HTML_LI ||
1833 		     me->sp[0].tag_number == HTML_LH ||
1834 		     me->sp[0].tag_number == HTML_DD)
1835 		me->current_default_alignment = HT_LEFT;
1836 	    LYHandlePlike(me, present, value, include, HTML_DIV_ALIGN, TRUE);
1837 	    me->DivisionAlignments[me->Division_Level] = (short)
1838 		me->current_default_alignment;
1839 	} else if (present && present[HTML_DIV_ALIGN] &&
1840 		   non_empty(value[HTML_DIV_ALIGN])) {
1841 	    if (!strcasecomp(value[HTML_DIV_ALIGN], "center")) {
1842 		me->DivisionAlignments[me->Division_Level] = HT_CENTER;
1843 		change_paragraph_style(me, styles[HTML_DCENTER]);
1844 		UPDATE_STYLE;
1845 		me->current_default_alignment = styles[HTML_DCENTER]->alignment;
1846 	    } else if (!strcasecomp(value[HTML_DIV_ALIGN], "right")) {
1847 		me->DivisionAlignments[me->Division_Level] = HT_RIGHT;
1848 		change_paragraph_style(me, styles[HTML_DRIGHT]);
1849 		UPDATE_STYLE;
1850 		me->current_default_alignment = styles[HTML_DRIGHT]->alignment;
1851 	    } else {
1852 		me->DivisionAlignments[me->Division_Level] = HT_LEFT;
1853 		change_paragraph_style(me, styles[HTML_DLEFT]);
1854 		UPDATE_STYLE;
1855 		me->current_default_alignment = styles[HTML_DLEFT]->alignment;
1856 	    }
1857 	} else {
1858 	    me->DivisionAlignments[me->Division_Level] = HT_LEFT;
1859 	    change_paragraph_style(me, styles[HTML_DLEFT]);
1860 	    UPDATE_STYLE;
1861 	    me->current_default_alignment = styles[HTML_DLEFT]->alignment;
1862 	}
1863 	CHECK_ID(HTML_DIV_ID);
1864 	break;
1865 
1866     case HTML_H1:
1867     case HTML_H2:
1868     case HTML_H3:
1869     case HTML_H4:
1870     case HTML_H5:
1871     case HTML_H6:
1872 	/*
1873 	 * Close the previous style if not done by HTML doc.  Added to get rid
1874 	 * of core dumps in BAD HTML on the net.
1875 	 *              GAB 07-07-94
1876 	 * But then again, these are actually allowed to nest.  I guess I have
1877 	 * to depend on the HTML writers correct style.
1878 	 *              GAB 07-12-94
1879 	 if (i_prior_style != -1) {
1880 	 HTML_end_element(me, i_prior_style);
1881 	 }
1882 	 i_prior_style = ElementNumber;
1883 	 */
1884 
1885 	/*
1886 	 * Check whether we have an H# in a list, and if so, treat it as an LH.
1887 	 * - FM
1888 	 */
1889 	if ((me->List_Nesting_Level >= 0) &&
1890 	    (me->sp[0].tag_number == HTML_UL ||
1891 	     me->sp[0].tag_number == HTML_OL ||
1892 	     me->sp[0].tag_number == HTML_MENU ||
1893 	     me->sp[0].tag_number == HTML_DIR ||
1894 	     me->sp[0].tag_number == HTML_LI)) {
1895 	    if (HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY) {
1896 		ElementNumber = HTML_LH;
1897 	    } else {
1898 		me->new_style = me->sp[0].style;
1899 		ElementNumber = (HTMLElement) me->sp[0].tag_number;
1900 		UPDATE_STYLE;
1901 	    }
1902 	    /*
1903 	     * Some authors use H# headers as a substitute for FONT, so check
1904 	     * if this one immediately followed an LI.  If so, both me->inP and
1905 	     * me->in_word will be FALSE (though the line might not be empty
1906 	     * due to a bullet and/or nbsp) and we can assume it is just for a
1907 	     * FONT change.  We thus will not create another line break nor add
1908 	     * to the current left indentation.  - FM
1909 	     */
1910 	    if (!(me->inP == FALSE && me->in_word == NO)) {
1911 		HText_appendParagraph(me->text);
1912 		HTML_put_character(me, HT_NON_BREAK_SPACE);
1913 		HText_setLastChar(me->text, ' ');
1914 		me->in_word = NO;
1915 		me->inP = FALSE;
1916 	    }
1917 	    CHECK_ID(HTML_H_ID);
1918 	    break;
1919 	}
1920 
1921 	if (present && present[HTML_H_ALIGN] &&
1922 	    non_empty(value[HTML_H_ALIGN])) {
1923 	    if (!strcasecomp(value[HTML_H_ALIGN], "center"))
1924 		change_paragraph_style(me, styles[HTML_HCENTER]);
1925 	    else if (!strcasecomp(value[HTML_H_ALIGN], "right"))
1926 		change_paragraph_style(me, styles[HTML_HRIGHT]);
1927 	    else if (!strcasecomp(value[HTML_H_ALIGN], "left") ||
1928 		     !strcasecomp(value[HTML_H_ALIGN], "justify"))
1929 		change_paragraph_style(me, styles[HTML_HLEFT]);
1930 	    else
1931 		change_paragraph_style(me, styles[ElementNumber]);
1932 	} else if (me->Division_Level >= 0) {
1933 	    if (me->DivisionAlignments[me->Division_Level] == HT_CENTER) {
1934 		change_paragraph_style(me, styles[HTML_HCENTER]);
1935 	    } else if (me->DivisionAlignments[me->Division_Level] == HT_LEFT) {
1936 		change_paragraph_style(me, styles[HTML_HLEFT]);
1937 	    } else if (me->DivisionAlignments[me->Division_Level] == HT_RIGHT) {
1938 		change_paragraph_style(me, styles[HTML_HRIGHT]);
1939 	    }
1940 	} else {
1941 	    change_paragraph_style(me, styles[ElementNumber]);
1942 	}
1943 	UPDATE_STYLE;
1944 	CHECK_ID(HTML_H_ID);
1945 
1946 	if ((bold_headers == TRUE ||
1947 	     (ElementNumber == HTML_H1 && bold_H1 == TRUE)) &&
1948 	    (styles[ElementNumber]->font & HT_BOLD)) {
1949 	    if (me->inBoldA == FALSE && me->inBoldH == FALSE) {
1950 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
1951 	    }
1952 	    me->inBoldH = TRUE;
1953 	}
1954 	break;
1955 
1956     case HTML_P:
1957 	LYHandlePlike(me, present, value, include, HTML_P_ALIGN, TRUE);
1958 	CHECK_ID(HTML_P_ID);
1959 	break;
1960 
1961     case HTML_BR:
1962 	UPDATE_STYLE;
1963 	CHECK_ID(HTML_GEN_ID);
1964 	/* Add a \r (new line) if these three conditions are true:
1965 	 *   1. We are not collapsing BRs, and
1966 	 *   2. The previous line has text on it, or
1967 	 *   3. This line has text on it.
1968 	 * Otherwise, don't do anything. -DH 980814, TD 980827
1969 	 */
1970 	if ((LYCollapseBRs == FALSE &&
1971 	     !HText_PreviousLineEmpty(me->text, FALSE)) ||
1972 	    !HText_LastLineEmpty(me->text, FALSE)) {
1973 	    HText_setLastChar(me->text, ' ');	/* absorb white space */
1974 	    HText_appendCharacter(me->text, '\r');
1975 	}
1976 	me->in_word = NO;
1977 	me->inP = FALSE;
1978 	break;
1979 
1980     case HTML_WBR:
1981 	UPDATE_STYLE;
1982 	CHECK_ID(HTML_GEN_ID);
1983 	HText_setBreakPoint(me->text);
1984 	break;
1985 
1986     case HTML_HY:
1987     case HTML_SHY:
1988 	UPDATE_STYLE;
1989 	CHECK_ID(HTML_GEN_ID);
1990 	HText_appendCharacter(me->text, LY_SOFT_HYPHEN);
1991 	break;
1992 
1993     case HTML_HR:
1994 	{
1995 	    int width;
1996 
1997 	    /*
1998 	     * Start a new line only if we had printable characters following
1999 	     * the previous newline, or remove the previous line if both it and
2000 	     * the last line are blank.  - FM
2001 	     */
2002 	    UPDATE_STYLE;
2003 	    if (!HText_LastLineEmpty(me->text, FALSE)) {
2004 		HText_setLastChar(me->text, ' ');	/* absorb white space */
2005 		HText_appendCharacter(me->text, '\r');
2006 	    } else if (HText_PreviousLineEmpty(me->text, FALSE)) {
2007 		HText_RemovePreviousLine(me->text);
2008 	    }
2009 	    me->in_word = NO;
2010 	    me->inP = FALSE;
2011 
2012 	    /*
2013 	     * Add an ID link if needed.  - FM
2014 	     */
2015 	    CHECK_ID(HTML_HR_ID);
2016 
2017 	    /*
2018 	     * Center lines within the current margins, if a right or left
2019 	     * ALIGNment is not specified.  If WIDTH="#%" is given and not
2020 	     * garbage, use that to calculate the width, otherwise use the
2021 	     * default width.  - FM
2022 	     */
2023 	    if (present && present[HTML_HR_ALIGN] && value[HTML_HR_ALIGN]) {
2024 		if (!strcasecomp(value[HTML_HR_ALIGN], "right")) {
2025 		    me->sp->style->alignment = HT_RIGHT;
2026 		} else if (!strcasecomp(value[HTML_HR_ALIGN], "left")) {
2027 		    me->sp->style->alignment = HT_LEFT;
2028 		} else {
2029 		    me->sp->style->alignment = HT_CENTER;
2030 		}
2031 	    } else {
2032 		me->sp->style->alignment = HT_CENTER;
2033 	    }
2034 	    width = LYcolLimit -
2035 		me->new_style->leftIndent - me->new_style->rightIndent;
2036 	    if (present && present[HTML_HR_WIDTH] && value[HTML_HR_WIDTH] &&
2037 		isdigit(UCH(*value[HTML_HR_WIDTH])) &&
2038 		value[HTML_HR_WIDTH][strlen(value[HTML_HR_WIDTH]) - 1] == '%') {
2039 		char *percent = NULL;
2040 		int Percent, Width;
2041 
2042 		StrAllocCopy(percent, value[HTML_HR_WIDTH]);
2043 		percent[strlen(percent) - 1] = '\0';
2044 		Percent = atoi(percent);
2045 		if (Percent > 100 || Percent < 1)
2046 		    width -= 5;
2047 		else {
2048 		    Width = (width * Percent) / 100;
2049 		    if (Width < 1)
2050 			width = 1;
2051 		    else
2052 			width = Width;
2053 		}
2054 		FREE(percent);
2055 	    } else {
2056 		width -= 5;
2057 	    }
2058 	    for (i = 0; i < width; i++)
2059 		HTML_put_character(me, '_');
2060 	    HText_appendCharacter(me->text, '\r');
2061 	    me->in_word = NO;
2062 	    me->inP = FALSE;
2063 
2064 	    /*
2065 	     * Reset the alignment appropriately for the division and/or block.
2066 	     * - FM
2067 	     */
2068 	    if (me->List_Nesting_Level < 0 &&
2069 		me->Division_Level >= 0) {
2070 		me->sp->style->alignment =
2071 		    me->DivisionAlignments[me->Division_Level];
2072 	    } else if (me->sp->style->id == ST_HeadingCenter ||
2073 		       me->sp->style->id == ST_Heading1) {
2074 		me->sp->style->alignment = HT_CENTER;
2075 	    } else if (me->sp->style->id == ST_HeadingRight) {
2076 		me->sp->style->alignment = HT_RIGHT;
2077 	    } else {
2078 		me->sp->style->alignment = HT_LEFT;
2079 	    }
2080 
2081 	    /*
2082 	     * Add a blank line and set the second line indentation for lists
2083 	     * and addresses, or a paragraph separator for other blocks.  - FM
2084 	     */
2085 	    if (me->List_Nesting_Level >= 0 ||
2086 		me->sp[0].tag_number == HTML_ADDRESS) {
2087 		HText_setLastChar(me->text, ' ');	/* absorb white space */
2088 		HText_appendCharacter(me->text, '\r');
2089 	    } else {
2090 		HText_appendParagraph(me->text);
2091 	    }
2092 	}
2093 	break;
2094 
2095     case HTML_TAB:
2096 	if (!present) {		/* Bad tag.  Must have at least one attribute. - FM */
2097 	    CTRACE((tfp, "HTML: TAB tag has no attributes.  Ignored.\n"));
2098 	    break;
2099 	}
2100 	/*
2101 	 * If page author is using TAB within a TABLE, it's probably formatted
2102 	 * specifically to work well for Lynx without simple table tracking
2103 	 * code.  Cancel tracking, it would only make things worse.  - kw
2104 	 */
2105 	HText_cancelStbl(me->text);
2106 	UPDATE_STYLE;
2107 
2108 	CANT_JUSTIFY_THIS_LINE;
2109 	if (present[HTML_TAB_ALIGN] && value[HTML_TAB_ALIGN] &&
2110 	    (strcasecomp(value[HTML_TAB_ALIGN], "left") ||
2111 	     !(present[HTML_TAB_TO] || present[HTML_TAB_INDENT]))) {
2112 	    /*
2113 	     * Just ensure a collapsible space, until we have the ALIGN and DP
2114 	     * attributes implemented.  - FM
2115 	     */
2116 	    HTML_put_character(me, ' ');
2117 	    CTRACE((tfp,
2118 		    "HTML: ALIGN not 'left'.  Using space instead of TAB.\n"));
2119 
2120 	} else if (!LYoverride_default_alignment(me) &&
2121 		   me->current_default_alignment != HT_LEFT) {
2122 	    /*
2123 	     * Just ensure a collapsible space, until we can replace
2124 	     * HText_getCurrentColumn() in GridText.c with code which doesn't
2125 	     * require that the alignment be HT_LEFT.  - FM
2126 	     */
2127 	    HTML_put_character(me, ' ');
2128 	    CTRACE((tfp, "HTML: Not HT_LEFT.  Using space instead of TAB.\n"));
2129 
2130 	} else if ((present[HTML_TAB_TO] &&
2131 		    non_empty(value[HTML_TAB_TO])) ||
2132 		   (present[HTML_TAB_INDENT] &&
2133 		    value[HTML_TAB_INDENT] &&
2134 		    isdigit(UCH(*value[HTML_TAB_INDENT])))) {
2135 	    int column, target = -1;
2136 	    int enval = 2;
2137 
2138 	    column = HText_getCurrentColumn(me->text);
2139 	    if (present[HTML_TAB_TO] &&
2140 		non_empty(value[HTML_TAB_TO])) {
2141 		/*
2142 		 * TO has priority over INDENT if both are present.  - FM
2143 		 */
2144 		StrAllocCopy(temp, value[HTML_TAB_TO]);
2145 		TRANSLATE_AND_UNESCAPE_TO_STD(&temp);
2146 		if (*temp) {
2147 		    target = HText_getTabIDColumn(me->text, temp);
2148 		}
2149 	    } else if (isEmpty(temp) && present[HTML_TAB_INDENT] &&
2150 		       value[HTML_TAB_INDENT] &&
2151 		       isdigit(UCH(*value[HTML_TAB_INDENT]))) {
2152 		/*
2153 		 * The INDENT value is in "en" (enval per column) units.
2154 		 * Divide it by enval, rounding odd values up.  - FM
2155 		 */
2156 		target =
2157 		    (int) (((1.0 * atoi(value[HTML_TAB_INDENT])) / enval) + (0.5));
2158 	    }
2159 	    FREE(temp);
2160 	    /*
2161 	     * If we are being directed to a column too far to the left or
2162 	     * right, just add a collapsible space, otherwise, add the
2163 	     * appropriate number of spaces.  - FM
2164 	     */
2165 
2166 	    if (target < column ||
2167 		target > HText_getMaximumColumn(me->text)) {
2168 		HTML_put_character(me, ' ');
2169 		CTRACE((tfp,
2170 			"HTML: Column out of bounds.  Using space instead of TAB.\n"));
2171 	    } else {
2172 		for (i = column; i < target; i++)
2173 		    HText_appendCharacter(me->text, ' ');
2174 		HText_setLastChar(me->text, ' ');	/* absorb white space */
2175 	    }
2176 	}
2177 	me->in_word = NO;
2178 
2179 	/*
2180 	 * If we have an ID attribute, save it together with the value of the
2181 	 * column we've reached.  - FM
2182 	 */
2183 	if (present[HTML_TAB_ID] &&
2184 	    non_empty(value[HTML_TAB_ID])) {
2185 	    StrAllocCopy(temp, value[HTML_TAB_ID]);
2186 	    TRANSLATE_AND_UNESCAPE_TO_STD(&temp);
2187 	    if (*temp)
2188 		HText_setTabID(me->text, temp);
2189 	    FREE(temp);
2190 	}
2191 	break;
2192 
2193     case HTML_BASEFONT:
2194 	break;
2195 
2196     case HTML_FONT:
2197 
2198 	/*
2199 	 * FONT *may* have been declared SGML_EMPTY in HTMLDTD.c, and
2200 	 * SGML_character() in SGML.c *may* check for a FONT end tag to call
2201 	 * HTML_end_element() directly (with a check in that to bypass
2202 	 * decrementing of the HTML parser's stack).  Or this may have been
2203 	 * really a </FONT> end tag, for which some incarnations of SGML.c
2204 	 * would fake a <FONT> start tag instead.  - fm & kw
2205 	 *
2206 	 * But if we have an open FONT, DON'T close that one now, since FONT
2207 	 * tags can be legally nested AFAIK, and Lynx currently doesn't do
2208 	 * anything with them anyway...  - kw
2209 	 */
2210 #ifdef NOTUSED_FOTEMODS
2211 	if (me->inFONT == TRUE)
2212 	    HTML_end_element(me, HTML_FONT, &include);
2213 #endif /* NOTUSED_FOTEMODS */
2214 
2215 	/*
2216 	 * Set flag to know we are in a FONT container, and add code to do
2217 	 * something about it, someday.  - FM
2218 	 */
2219 	me->inFONT = TRUE;
2220 	break;
2221 
2222     case HTML_B:		/* Physical character highlighting */
2223     case HTML_BLINK:
2224     case HTML_I:
2225     case HTML_U:
2226 
2227     case HTML_CITE:		/* Logical character highlighting */
2228     case HTML_EM:
2229     case HTML_STRONG:
2230 	UPDATE_STYLE;
2231 	me->Underline_Level++;
2232 	CHECK_ID(HTML_GEN_ID);
2233 	/*
2234 	 * Ignore this if inside of a bold anchor or header.  Can't display
2235 	 * both underline and bold at same time.
2236 	 */
2237 	if (me->inBoldA == TRUE || me->inBoldH == TRUE) {
2238 	    CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level));
2239 	    break;
2240 	}
2241 	if (me->inUnderline == FALSE) {
2242 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
2243 	    me->inUnderline = TRUE;
2244 	    CTRACE((tfp, "Beginning underline\n"));
2245 	} else {
2246 	    CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level));
2247 	}
2248 	break;
2249 
2250     case HTML_ABBR:		/* Miscellaneous character containers */
2251     case HTML_ACRONYM:
2252     case HTML_AU:
2253     case HTML_AUTHOR:
2254     case HTML_BIG:
2255     case HTML_CODE:
2256     case HTML_DFN:
2257     case HTML_KBD:
2258     case HTML_SAMP:
2259     case HTML_SMALL:
2260     case HTML_TT:
2261     case HTML_VAR:
2262 	CHECK_ID(HTML_GEN_ID);
2263 	break;			/* ignore */
2264 
2265     case HTML_SUP:
2266 	HText_appendCharacter(me->text, '^');
2267 	CHECK_ID(HTML_GEN_ID);
2268 	break;
2269 
2270     case HTML_SUB:
2271 	HText_appendCharacter(me->text, '[');
2272 	CHECK_ID(HTML_GEN_ID);
2273 	break;
2274 
2275     case HTML_DEL:
2276     case HTML_S:
2277     case HTML_STRIKE:
2278 	CHECK_ID(HTML_GEN_ID);
2279 	if (me->inUnderline == FALSE)
2280 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
2281 	HTML_put_string(me, "[DEL:");
2282 	if (me->inUnderline == FALSE)
2283 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
2284 	HTML_put_character(me, ' ');
2285 	me->in_word = NO;
2286 	break;
2287 
2288     case HTML_INS:
2289 	CHECK_ID(HTML_GEN_ID);
2290 	if (me->inUnderline == FALSE)
2291 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
2292 	HTML_put_string(me, "[INS:");
2293 	if (me->inUnderline == FALSE)
2294 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
2295 	HTML_put_character(me, ' ');
2296 	me->in_word = NO;
2297 	break;
2298 
2299     case HTML_Q:
2300 	CHECK_ID(HTML_GEN_ID);
2301 	/*
2302 	 * Should check LANG and/or DIR attributes, and the
2303 	 * me->node_anchor->charset and/or yet to be added structure elements,
2304 	 * to determine whether we should use chevrons, but for now we'll
2305 	 * always use double- or single-quotes.  - FM
2306 	 */
2307 	if (!(me->Quote_Level & 1))
2308 	    HTML_put_character(me, '"');
2309 	else
2310 	    HTML_put_character(me, '`');
2311 	me->Quote_Level++;
2312 	break;
2313 
2314     case HTML_PRE:		/* Formatted text */
2315 	/*
2316 	 * Set our inPRE flag to FALSE so that a newline immediately following
2317 	 * the PRE start tag will be ignored.  HTML_put_character() will set it
2318 	 * to TRUE when the first character within the PRE block is received.
2319 	 * - FM
2320 	 */
2321 	me->inPRE = FALSE;
2322 	/* FALLTHRU */
2323     case HTML_LISTING:		/* Literal text */
2324 	/* FALLTHRU */
2325     case HTML_XMP:
2326 	/* FALLTHRU */
2327     case HTML_PLAINTEXT:
2328 	change_paragraph_style(me, styles[ElementNumber]);
2329 	UPDATE_STYLE;
2330 	CHECK_ID(HTML_GEN_ID);
2331 	if (me->comment_end)
2332 	    HText_appendText(me->text, me->comment_end);
2333 	break;
2334 
2335     case HTML_BLOCKQUOTE:
2336     case HTML_BQ:
2337 	change_paragraph_style(me, styles[ElementNumber]);
2338 	UPDATE_STYLE;
2339 	if (me->sp->tag_number == (int) ElementNumber)
2340 	    LYEnsureDoubleSpace(me);
2341 	CHECK_ID(HTML_BQ_ID);
2342 	break;
2343 
2344     case HTML_NOTE:
2345 	change_paragraph_style(me, styles[ElementNumber]);
2346 	UPDATE_STYLE;
2347 	if (me->sp->tag_number == (int) ElementNumber)
2348 	    LYEnsureDoubleSpace(me);
2349 	CHECK_ID(HTML_NOTE_ID);
2350 	{
2351 	    char *note = NULL;
2352 
2353 	    /*
2354 	     * Indicate the type of NOTE.
2355 	     */
2356 	    if (present && present[HTML_NOTE_CLASS] &&
2357 		value[HTML_NOTE_CLASS] &&
2358 		(!strcasecomp(value[HTML_NOTE_CLASS], "CAUTION") ||
2359 		 !strcasecomp(value[HTML_NOTE_CLASS], "WARNING"))) {
2360 		StrAllocCopy(note, value[HTML_NOTE_CLASS]);
2361 		LYUpperCase(note);
2362 		StrAllocCat(note, ":");
2363 	    } else if (present && present[HTML_NOTE_ROLE] &&
2364 		       value[HTML_NOTE_ROLE] &&
2365 		       (!strcasecomp(value[HTML_NOTE_ROLE], "CAUTION") ||
2366 			!strcasecomp(value[HTML_NOTE_ROLE], "WARNING"))) {
2367 		StrAllocCopy(note, value[HTML_NOTE_ROLE]);
2368 		LYUpperCase(note);
2369 		StrAllocCat(note, ":");
2370 	    } else {
2371 		StrAllocCopy(note, "NOTE:");
2372 	    }
2373 	    if (me->inUnderline == FALSE)
2374 		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
2375 	    HTML_put_string(me, note);
2376 	    if (me->inUnderline == FALSE)
2377 		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
2378 	    HTML_put_character(me, ' ');
2379 	    CAN_JUSTIFY_START;
2380 	    FREE(note);
2381 	}
2382 	CAN_JUSTIFY_START;
2383 	me->inLABEL = TRUE;
2384 	me->in_word = NO;
2385 	me->inP = FALSE;
2386 	break;
2387 
2388     case HTML_ADDRESS:
2389 	if (me->List_Nesting_Level < 0) {
2390 	    change_paragraph_style(me, styles[ElementNumber]);
2391 	    UPDATE_STYLE;
2392 	    if (me->sp->tag_number == (int) ElementNumber)
2393 		LYEnsureDoubleSpace(me);
2394 	} else {
2395 	    LYHandlePlike(me, present, value, include, -1, TRUE);
2396 	}
2397 	CHECK_ID(HTML_ADDRESS_ID);
2398 	break;
2399 
2400     case HTML_DL:
2401 	me->List_Nesting_Level++;	/* increment the List nesting level */
2402 	if (me->List_Nesting_Level <= 0) {
2403 	    change_paragraph_style(me, present && present[HTML_DL_COMPACT]
2404 				   ? styles[HTML_DLC] : styles[HTML_DL]);
2405 
2406 	} else if (me->List_Nesting_Level >= 6) {
2407 	    change_paragraph_style(me, present && present[HTML_DL_COMPACT]
2408 				   ? styles[HTML_DLC6] : styles[HTML_DL6]);
2409 
2410 	} else {
2411 	    change_paragraph_style(me, present && present[HTML_DL_COMPACT]
2412 				   ? styles[(HTML_DLC1 - 1) + me->List_Nesting_Level]
2413 				   : styles[(HTML_DL1 - 1) + me->List_Nesting_Level]);
2414 	}
2415 	UPDATE_STYLE;		/* update to the new style */
2416 	CHECK_ID(HTML_DL_ID);
2417 
2418 	break;
2419 
2420     case HTML_DLC:
2421 	me->List_Nesting_Level++;	/* increment the List nesting level */
2422 	if (me->List_Nesting_Level <= 0) {
2423 	    change_paragraph_style(me, styles[HTML_DLC]);
2424 
2425 	} else if (me->List_Nesting_Level >= 6) {
2426 	    change_paragraph_style(me, styles[HTML_DLC6]);
2427 
2428 	} else {
2429 	    change_paragraph_style(me,
2430 				   styles[(HTML_DLC1 - 1) + me->List_Nesting_Level]);
2431 	}
2432 	UPDATE_STYLE;		/* update to the new style */
2433 	CHECK_ID(HTML_DL_ID);
2434 	break;
2435 
2436     case HTML_DT:
2437 	CHECK_ID(HTML_GEN_ID);
2438 	if (!me->style_change) {
2439 	    BOOL in_line_1 = HText_inLineOne(me->text);
2440 	    HTCoord saved_spaceBefore = me->sp->style->spaceBefore;
2441 	    HTCoord saved_spaceAfter = me->sp->style->spaceAfter;
2442 
2443 	    /*
2444 	     * If there are several DT elements and this is not the first, and
2445 	     * the preceding DT element's first (and normally only) line has
2446 	     * not yet been ended, suppress intervening blank line by
2447 	     * temporarily modifying the paragraph style in place.  Ugly but
2448 	     * there's ample precedence.  - kw
2449 	     */
2450 	    if (in_line_1) {
2451 		me->sp->style->spaceBefore = 0;		/* temporary change */
2452 		me->sp->style->spaceAfter = 0;	/* temporary change */
2453 	    }
2454 	    HText_appendParagraph(me->text);
2455 	    me->sp->style->spaceBefore = saved_spaceBefore;	/* undo */
2456 	    me->sp->style->spaceAfter = saved_spaceAfter;	/* undo */
2457 	    me->in_word = NO;
2458 	    me->sp->style->alignment = HT_LEFT;
2459 	}
2460 	me->inP = FALSE;
2461 	break;
2462 
2463     case HTML_DD:
2464 	CHECK_ID(HTML_GEN_ID);
2465 	HText_setLastChar(me->text, ' ');	/* absorb white space */
2466 	if (!me->style_change) {
2467 	    if (!HText_LastLineEmpty(me->text, FALSE)) {
2468 		HText_appendCharacter(me->text, '\r');
2469 	    } else {
2470 		HText_NegateLineOne(me->text);
2471 	    }
2472 	} else {
2473 	    UPDATE_STYLE;
2474 	    HText_appendCharacter(me->text, '\t');
2475 	}
2476 	me->sp->style->alignment = HT_LEFT;
2477 	me->in_word = NO;
2478 	me->inP = FALSE;
2479 	break;
2480 
2481     case HTML_OL:
2482 	/*
2483 	 * Set the default TYPE.
2484 	 */
2485 	me->OL_Type[(me->List_Nesting_Level < 11 ?
2486 		     me->List_Nesting_Level + 1 : 11)] = '1';
2487 
2488 	/*
2489 	 * Check whether we have a starting sequence number, or want to
2490 	 * continue the numbering from a previous OL in this nest.  - FM
2491 	 */
2492 	if (present && (present[HTML_OL_SEQNUM] || present[HTML_OL_START])) {
2493 	    int seqnum;
2494 
2495 	    /*
2496 	     * Give preference to the valid HTML 3.0 SEQNUM attribute name over
2497 	     * the Netscape START attribute name (too bad the Netscape
2498 	     * developers didn't read the HTML 3.0 specs before re-inventing
2499 	     * the "wheel" as "we'll").  - FM
2500 	     */
2501 	    if (present[HTML_OL_SEQNUM] &&
2502 		non_empty(value[HTML_OL_SEQNUM])) {
2503 		seqnum = atoi(value[HTML_OL_SEQNUM]);
2504 	    } else if (present[HTML_OL_START] &&
2505 		       non_empty(value[HTML_OL_START])) {
2506 		seqnum = atoi(value[HTML_OL_START]);
2507 	    } else {
2508 		seqnum = 1;
2509 	    }
2510 
2511 	    /*
2512 	     * Don't allow negative numbers less than or equal to our flags, or
2513 	     * numbers less than 1 if an Alphabetic or Roman TYPE.  - FM
2514 	     */
2515 	    if (present[HTML_OL_TYPE] && value[HTML_OL_TYPE]) {
2516 		if (*value[HTML_OL_TYPE] == 'A') {
2517 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2518 				 me->List_Nesting_Level + 1 : 11)] = 'A';
2519 		    if (seqnum < 1)
2520 			seqnum = 1;
2521 		} else if (*value[HTML_OL_TYPE] == 'a') {
2522 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2523 				 me->List_Nesting_Level + 1 : 11)] = 'a';
2524 		    if (seqnum < 1)
2525 			seqnum = 1;
2526 		} else if (*value[HTML_OL_TYPE] == 'I') {
2527 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2528 				 me->List_Nesting_Level + 1 : 11)] = 'I';
2529 		    if (seqnum < 1)
2530 			seqnum = 1;
2531 		} else if (*value[HTML_OL_TYPE] == 'i') {
2532 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2533 				 me->List_Nesting_Level + 1 : 11)] = 'i';
2534 		    if (seqnum < 1)
2535 			seqnum = 1;
2536 		} else {
2537 		    if (seqnum <= OL_VOID)
2538 			seqnum = OL_VOID + 1;
2539 		}
2540 	    } else if (seqnum <= OL_VOID) {
2541 		seqnum = OL_VOID + 1;
2542 	    }
2543 
2544 	    me->OL_Counter[(me->List_Nesting_Level < 11 ?
2545 			    me->List_Nesting_Level + 1 : 11)] = seqnum;
2546 
2547 	} else if (present && present[HTML_OL_CONTINUE]) {
2548 	    me->OL_Counter[me->List_Nesting_Level < 11 ?
2549 			   me->List_Nesting_Level + 1 : 11] = OL_CONTINUE;
2550 
2551 	} else {
2552 	    me->OL_Counter[(me->List_Nesting_Level < 11 ?
2553 			    me->List_Nesting_Level + 1 : 11)] = 1;
2554 	    if (present && present[HTML_OL_TYPE] && value[HTML_OL_TYPE]) {
2555 		if (*value[HTML_OL_TYPE] == 'A') {
2556 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2557 				 me->List_Nesting_Level + 1 : 11)] = 'A';
2558 		} else if (*value[HTML_OL_TYPE] == 'a') {
2559 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2560 				 me->List_Nesting_Level + 1 : 11)] = 'a';
2561 		} else if (*value[HTML_OL_TYPE] == 'I') {
2562 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2563 				 me->List_Nesting_Level + 1 : 11)] = 'I';
2564 		} else if (*value[HTML_OL_TYPE] == 'i') {
2565 		    me->OL_Type[(me->List_Nesting_Level < 11 ?
2566 				 me->List_Nesting_Level + 1 : 11)] = 'i';
2567 		}
2568 	    }
2569 	}
2570 	me->List_Nesting_Level++;
2571 
2572 	if (me->List_Nesting_Level <= 0) {
2573 	    change_paragraph_style(me, styles[ElementNumber]);
2574 
2575 	} else if (me->List_Nesting_Level >= 6) {
2576 	    change_paragraph_style(me, styles[HTML_OL6]);
2577 
2578 	} else {
2579 	    change_paragraph_style(me,
2580 				   styles[HTML_OL1 + me->List_Nesting_Level - 1]);
2581 	}
2582 	UPDATE_STYLE;		/* update to the new style */
2583 	CHECK_ID(HTML_OL_ID);
2584 	break;
2585 
2586     case HTML_UL:
2587 	me->List_Nesting_Level++;
2588 
2589 	if (me->List_Nesting_Level <= 0) {
2590 	    if (!(present && present[HTML_UL_PLAIN]) &&
2591 		!(present && present[HTML_UL_TYPE] &&
2592 		  value[HTML_UL_TYPE] &&
2593 		  0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) {
2594 		change_paragraph_style(me, styles[ElementNumber]);
2595 	    } else {
2596 		change_paragraph_style(me, styles[HTML_DIR]);
2597 		ElementNumber = HTML_DIR;
2598 	    }
2599 
2600 	} else if (me->List_Nesting_Level >= 6) {
2601 	    if (!(present && present[HTML_UL_PLAIN]) &&
2602 		!(present && present[HTML_UL_TYPE] &&
2603 		  value[HTML_UL_TYPE] &&
2604 		  0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) {
2605 		change_paragraph_style(me, styles[HTML_OL6]);
2606 	    } else {
2607 		change_paragraph_style(me, styles[HTML_MENU6]);
2608 		ElementNumber = HTML_DIR;
2609 	    }
2610 
2611 	} else {
2612 	    if (!(present && present[HTML_UL_PLAIN]) &&
2613 		!(present && present[HTML_UL_TYPE] &&
2614 		  value[HTML_UL_TYPE] &&
2615 		  0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) {
2616 		change_paragraph_style(me,
2617 				       styles[HTML_OL1 + me->List_Nesting_Level
2618 					      - 1]);
2619 	    } else {
2620 		change_paragraph_style(me,
2621 				       styles[HTML_MENU1 + me->List_Nesting_Level
2622 					      - 1]);
2623 		ElementNumber = HTML_DIR;
2624 	    }
2625 	}
2626 	UPDATE_STYLE;		/* update to the new style */
2627 	CHECK_ID(HTML_UL_ID);
2628 	break;
2629 
2630     case HTML_MENU:
2631     case HTML_DIR:
2632 	me->List_Nesting_Level++;
2633 
2634 	if (me->List_Nesting_Level <= 0) {
2635 	    change_paragraph_style(me, styles[ElementNumber]);
2636 
2637 	} else if (me->List_Nesting_Level >= 6) {
2638 	    change_paragraph_style(me, styles[HTML_MENU6]);
2639 
2640 	} else {
2641 	    change_paragraph_style(me,
2642 				   styles[HTML_MENU1 + me->List_Nesting_Level
2643 					  - 1]);
2644 	}
2645 	UPDATE_STYLE;		/* update to the new style */
2646 	CHECK_ID(HTML_UL_ID);
2647 	break;
2648 
2649     case HTML_LH:
2650 	UPDATE_STYLE;		/* update to the new style */
2651 	HText_appendParagraph(me->text);
2652 	CHECK_ID(HTML_GEN_ID);
2653 	HTML_put_character(me, HT_NON_BREAK_SPACE);
2654 	HText_setLastChar(me->text, ' ');
2655 	me->in_word = NO;
2656 	me->inP = FALSE;
2657 	break;
2658 
2659     case HTML_LI:
2660 	UPDATE_STYLE;		/* update to the new style */
2661 	HText_appendParagraph(me->text);
2662 	me->sp->style->alignment = HT_LEFT;
2663 	CHECK_ID(HTML_LI_ID);
2664 	{
2665 	    int surrounding_tag_number = me->sp[0].tag_number;
2666 
2667 	    /*
2668 	     * No, a LI should never occur directly within another LI, but this
2669 	     * may result from incomplete error recovery.  So check one more
2670 	     * surrounding level in this case.  - kw
2671 	     */
2672 	    if (surrounding_tag_number == HTML_LI &&
2673 		me->sp < (me->stack + MAX_NESTING - 1))
2674 		surrounding_tag_number = me->sp[1].tag_number;
2675 	    if (surrounding_tag_number == HTML_OL) {
2676 		char number_string[20];
2677 		int counter, seqnum;
2678 		char seqtype;
2679 
2680 		counter = me->List_Nesting_Level < 11 ?
2681 		    me->List_Nesting_Level : 11;
2682 		if (present && present[HTML_LI_TYPE] && value[HTML_LI_TYPE]) {
2683 		    if (*value[HTML_LI_TYPE] == '1') {
2684 			me->OL_Type[counter] = '1';
2685 		    } else if (*value[HTML_LI_TYPE] == 'A') {
2686 			me->OL_Type[counter] = 'A';
2687 		    } else if (*value[HTML_LI_TYPE] == 'a') {
2688 			me->OL_Type[counter] = 'a';
2689 		    } else if (*value[HTML_LI_TYPE] == 'I') {
2690 			me->OL_Type[counter] = 'I';
2691 		    } else if (*value[HTML_LI_TYPE] == 'i') {
2692 			me->OL_Type[counter] = 'i';
2693 		    }
2694 		}
2695 		if (present && present[HTML_LI_VALUE] &&
2696 		    ((value[HTML_LI_VALUE] != NULL) &&
2697 		     (*value[HTML_LI_VALUE] != '\0')) &&
2698 		    ((isdigit(UCH(*value[HTML_LI_VALUE]))) ||
2699 		     (*value[HTML_LI_VALUE] == '-' &&
2700 		      isdigit(UCH(*(value[HTML_LI_VALUE] + 1)))))) {
2701 		    seqnum = atoi(value[HTML_LI_VALUE]);
2702 		    if (seqnum <= OL_VOID)
2703 			seqnum = OL_VOID + 1;
2704 		    seqtype = me->OL_Type[counter];
2705 		    if (seqtype != '1' && seqnum < 1)
2706 			seqnum = 1;
2707 		    me->OL_Counter[counter] = seqnum + 1;
2708 		} else if (me->OL_Counter[counter] >= OL_VOID) {
2709 		    seqnum = me->OL_Counter[counter]++;
2710 		    seqtype = me->OL_Type[counter];
2711 		    if (seqtype != '1' && seqnum < 1) {
2712 			seqnum = 1;
2713 			me->OL_Counter[counter] = seqnum + 1;
2714 		    }
2715 		} else {
2716 		    seqnum = me->Last_OL_Count + 1;
2717 		    seqtype = me->Last_OL_Type;
2718 		    for (i = (counter - 1); i >= 0; i--) {
2719 			if (me->OL_Counter[i] > OL_VOID) {
2720 			    seqnum = me->OL_Counter[i]++;
2721 			    seqtype = me->OL_Type[i];
2722 			    i = 0;
2723 			}
2724 		    }
2725 		}
2726 		if (seqtype == 'A') {
2727 		    strcpy(number_string, LYUppercaseA_OL_String(seqnum));
2728 		} else if (seqtype == 'a') {
2729 		    strcpy(number_string, LYLowercaseA_OL_String(seqnum));
2730 		} else if (seqtype == 'I') {
2731 		    strcpy(number_string, LYUppercaseI_OL_String(seqnum));
2732 		} else if (seqtype == 'i') {
2733 		    strcpy(number_string, LYLowercaseI_OL_String(seqnum));
2734 		} else {
2735 		    sprintf(number_string, "%2d.", seqnum);
2736 		}
2737 		me->Last_OL_Count = seqnum;
2738 		me->Last_OL_Type = seqtype;
2739 		/*
2740 		 * Hack, because there is no append string!
2741 		 */
2742 		for (i = 0; number_string[i] != '\0'; i++)
2743 		    if (number_string[i] == ' ')
2744 			HTML_put_character(me, HT_NON_BREAK_SPACE);
2745 		    else
2746 			HTML_put_character(me, number_string[i]);
2747 
2748 		/*
2749 		 * Use HTML_put_character so that any other spaces coming
2750 		 * through will be collapsed.  We'll use nbsp, so it won't
2751 		 * break at the spacing character if there are no spaces in the
2752 		 * subsequent text up to the right margin, but will declare it
2753 		 * as a normal space to ensure collapsing if a normal space
2754 		 * does immediately follow it.  - FM
2755 		 */
2756 		HTML_put_character(me, HT_NON_BREAK_SPACE);
2757 		HText_setLastChar(me->text, ' ');
2758 	    } else if (surrounding_tag_number == HTML_UL) {
2759 		/*
2760 		 * Hack, because there is no append string!
2761 		 */
2762 		HTML_put_character(me, HT_NON_BREAK_SPACE);
2763 		HTML_put_character(me, HT_NON_BREAK_SPACE);
2764 		switch (me->List_Nesting_Level % 7) {
2765 		case 0:
2766 		    HTML_put_character(me, '*');
2767 		    break;
2768 		case 1:
2769 		    HTML_put_character(me, '+');
2770 		    break;
2771 		case 2:
2772 		    HTML_put_character(me, 'o');
2773 		    break;
2774 		case 3:
2775 		    HTML_put_character(me, '#');
2776 		    break;
2777 		case 4:
2778 		    HTML_put_character(me, '@');
2779 		    break;
2780 		case 5:
2781 		    HTML_put_character(me, '-');
2782 		    break;
2783 		case 6:
2784 		    HTML_put_character(me, '=');
2785 		    break;
2786 
2787 		}
2788 		/*
2789 		 * Keep using HTML_put_character so that any other spaces
2790 		 * coming through will be collapsed.  We use nbsp, so we won't
2791 		 * wrap at the spacing character if there are no spaces in the
2792 		 * subsequent text up to the right margin, but will declare it
2793 		 * as a normal space to ensure collapsing if a normal space
2794 		 * does immediately follow it.  - FM
2795 		 */
2796 		HTML_put_character(me, HT_NON_BREAK_SPACE);
2797 		HText_setLastChar(me->text, ' ');
2798 	    } else {
2799 		/*
2800 		 * Hack, because there is no append string!
2801 		 */
2802 		HTML_put_character(me, HT_NON_BREAK_SPACE);
2803 		HTML_put_character(me, HT_NON_BREAK_SPACE);
2804 		HText_setLastChar(me->text, ' ');
2805 	    }
2806 	}
2807 	CAN_JUSTIFY_START;
2808 	me->in_word = NO;
2809 	me->inP = FALSE;
2810 	break;
2811 
2812     case HTML_SPAN:
2813 	CHECK_ID(HTML_GEN_ID);
2814 	/*
2815 	 * Should check LANG and/or DIR attributes, and the
2816 	 * me->node_anchor->charset and/or yet to be added structure elements,
2817 	 * and do something here.  - FM
2818 	 */
2819 	break;
2820 
2821     case HTML_BDO:
2822 	CHECK_ID(HTML_GEN_ID);
2823 	/*
2824 	 * Should check DIR (and LANG) attributes, and the
2825 	 * me->node_anchor->charset and/or yet to be added structure elements,
2826 	 * and do something here.  - FM
2827 	 */
2828 	break;
2829 
2830     case HTML_SPOT:
2831 	CHECK_ID(HTML_GEN_ID);
2832 	break;
2833 
2834     case HTML_FN:
2835 	change_paragraph_style(me, styles[ElementNumber]);
2836 	UPDATE_STYLE;
2837 	if (me->sp->tag_number == (int) ElementNumber)
2838 	    LYEnsureDoubleSpace(me);
2839 	CHECK_ID(HTML_GEN_ID);
2840 	if (me->inUnderline == FALSE)
2841 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
2842 	HTML_put_string(me, "FOOTNOTE:");
2843 	if (me->inUnderline == FALSE)
2844 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
2845 	HTML_put_character(me, ' ');
2846 	CAN_JUSTIFY_START
2847 	    me->inLABEL = TRUE;
2848 	me->in_word = NO;
2849 	me->inP = FALSE;
2850 	break;
2851 
2852     case HTML_A:
2853 	/*
2854 	 * If we are looking for client-side image maps, then handle an A
2855 	 * within a MAP that has a COORDS attribute as an AREA tag.
2856 	 * Unfortunately we lose the anchor text this way for the LYNXIMGMAP,
2857 	 * we would have to do much more parsing to collect it.  After
2858 	 * potentially handling the A as AREA, always return immediately if
2859 	 * only looking for image maps, without pushing anything on the style
2860 	 * stack.  - kw
2861 	 */
2862 	if (me->map_address && present && present[HTML_A_COORDS])
2863 	    LYStartArea(me,
2864 			present[HTML_A_HREF] ? value[HTML_A_HREF] : NULL,
2865 			NULL,
2866 			present[HTML_A_TITLE] ? value[HTML_A_TITLE] : NULL,
2867 			tag_charset);
2868 	if (LYMapsOnly) {
2869 	    return HT_OK;
2870 	}
2871 	/*
2872 	 * A may have been declared SGML_EMPTY in HTMLDTD.c, and
2873 	 * SGML_character() in SGML.c may check for an A end tag to call
2874 	 * HTML_end_element() directly (with a check in that to bypass
2875 	 * decrementing of the HTML parser's stack), so if we have an open A,
2876 	 * close that one now.  - FM & kw
2877 	 */
2878 	if (me->inA) {
2879 	    SET_SKIP_STACK(HTML_A);
2880 	    HTML_end_element(me, HTML_A, include);
2881 	}
2882 	/*
2883 	 * Set to know we are in an anchor.
2884 	 */
2885 	me->inA = TRUE;
2886 
2887 	/*
2888 	 * Load id_string if we have an ID or NAME.  - FM
2889 	 */
2890 	if (present && present[HTML_A_ID] &&
2891 	    non_empty(value[HTML_A_ID])) {
2892 	    StrAllocCopy(id_string, value[HTML_A_ID]);
2893 	} else if (present && present[HTML_A_NAME] &&
2894 		   non_empty(value[HTML_A_NAME])) {
2895 	    StrAllocCopy(id_string, value[HTML_A_NAME]);
2896 	}
2897 	if (id_string)
2898 	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
2899 
2900 	/*
2901 	 * Handle the reference.  - FM
2902 	 */
2903 	if (present && present[HTML_A_HREF]) {
2904 	    /*
2905 	     * Set to know we are making the content bold.
2906 	     */
2907 	    me->inBoldA = TRUE;
2908 
2909 	    if (isEmpty(value[HTML_A_HREF]))
2910 		StrAllocCopy(href, "#");
2911 	    else
2912 		StrAllocCopy(href, value[HTML_A_HREF]);
2913 	    CHECK_FOR_INTERN(intern_flag, href);	/* '#' */
2914 
2915 	    if (intern_flag) { /*** FAST WAY: ***/
2916 		TRANSLATE_AND_UNESCAPE_TO_STD(&href);
2917 
2918 	    } else {
2919 		url_type = LYLegitimizeHREF(me, &href, TRUE, TRUE);
2920 
2921 		/*
2922 		 * Deal with our ftp gateway kludge.  - FM
2923 		 */
2924 		if (!url_type && !StrNCmp(href, "/foo/..", 7) &&
2925 		    (isFTP_URL(me->node_anchor->address) ||
2926 		     isFILE_URL(me->node_anchor->address))) {
2927 		    for (i = 0; (href[i] = href[i + 7]) != 0; i++) ;
2928 		}
2929 	    }
2930 
2931 	    if (present[HTML_A_ISMAP])	/*??? */
2932 		intern_flag = FALSE;
2933 	} else {
2934 	    if (bold_name_anchors == TRUE) {
2935 		me->inBoldA = TRUE;
2936 	    }
2937 	}
2938 
2939 	if (present && present[HTML_A_TYPE] && value[HTML_A_TYPE]) {
2940 	    StrAllocCopy(temp, value[HTML_A_TYPE]);
2941 	    if (!intern_flag &&
2942 		!strcasecomp(value[HTML_A_TYPE], HTAtom_name(HTInternalLink)) &&
2943 		!LYIsUIPage3(me->node_anchor->address, UIP_LIST_PAGE, 0) &&
2944 		!LYIsUIPage3(me->node_anchor->address, UIP_ADDRLIST_PAGE, 0) &&
2945 		!isLYNXIMGMAP(me->node_anchor->address)) {
2946 		/* Some kind of spoof?
2947 		 * Found TYPE="internal link" but not in a valid context
2948 		 * where we have written it. - kw
2949 		 */
2950 		CTRACE((tfp, "HTML: Found invalid HREF=\"%s\" TYPE=\"%s\"!\n",
2951 			href, temp));
2952 		FREE(temp);
2953 	    }
2954 	}
2955 
2956 	me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
2957 						 id_string,	/* Tag */
2958 						 href,	/* Address */
2959 						 (temp
2960 						  ? (HTLinkType *)
2961 						  HTAtom_for(temp)
2962 						  : INTERN_LT));	/* Type */
2963 	FREE(temp);
2964 	FREE(id_string);
2965 
2966 	if (me->CurrentA && present) {
2967 	    if (present[HTML_A_TITLE] &&
2968 		non_empty(value[HTML_A_TITLE])) {
2969 		StrAllocCopy(title, value[HTML_A_TITLE]);
2970 		TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
2971 		LYTrimHead(title);
2972 		LYTrimTail(title);
2973 		if (*title == '\0') {
2974 		    FREE(title);
2975 		}
2976 	    }
2977 	    if (present[HTML_A_ISMAP])
2978 		dest_ismap = TRUE;
2979 	    if (present[HTML_A_CHARSET] &&
2980 		non_empty(value[HTML_A_CHARSET])) {
2981 		/*
2982 		 * Set up to load the anchor's chartrans structures
2983 		 * appropriately for the current display character set if it
2984 		 * can handle what's claimed.  - FM
2985 		 */
2986 		StrAllocCopy(temp, value[HTML_A_CHARSET]);
2987 		TRANSLATE_AND_UNESCAPE_TO_STD(&temp);
2988 		dest_char_set = UCGetLYhndl_byMIME(temp);
2989 		if (dest_char_set < 0) {
2990 		    dest_char_set = UCLYhndl_for_unrec;
2991 		}
2992 	    }
2993 	    if (title != NULL || dest_ismap == TRUE || dest_char_set >= 0) {
2994 		dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
2995 		    );
2996 	    }
2997 	    if (dest && title != NULL && HTAnchor_title(dest) == NULL)
2998 		HTAnchor_setTitle(dest, title);
2999 	    if (dest && dest_ismap)
3000 		dest->isISMAPScript = TRUE;
3001 	    /* Don't allow CHARSET attribute to change *this* document's
3002 	       charset assumption. - kw */
3003 	    if (dest && dest != me->node_anchor && dest_char_set >= 0) {
3004 		/*
3005 		 * Load the anchor's chartrans structures.  This should be done
3006 		 * more intelligently when setting up the structured object,
3007 		 * but it gets the job done for now.  - FM
3008 		 */
3009 		HTAnchor_setUCInfoStage(dest, dest_char_set,
3010 					UCT_STAGE_MIME,
3011 					UCT_SETBY_DEFAULT);
3012 		HTAnchor_setUCInfoStage(dest, dest_char_set,
3013 					UCT_STAGE_PARSER,
3014 					UCT_SETBY_LINK);
3015 	    }
3016 	    FREE(temp);
3017 	    dest = NULL;
3018 	    FREE(title);
3019 	}
3020 	me->CurrentANum = HText_beginAnchor(me->text,
3021 					    me->inUnderline, me->CurrentA);
3022 	if (me->inBoldA == TRUE && me->inBoldH == FALSE)
3023 	    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3024 #if defined(NOTUSED_FOTEMODS)
3025 	/*
3026 	 * Close an HREF-less NAMED-ed now if we aren't making their content
3027 	 * bold, and let the check in HTML_end_element() deal with any dangling
3028 	 * end tag this creates.  - FM
3029 	 */
3030 	if (href == NULL && me->inBoldA == FALSE) {
3031 	    SET_SKIP_STACK(HTML_A);
3032 	    HTML_end_element(me, HTML_A, include);
3033 	}
3034 #else
3035 	/*Close an HREF-less NAMED-ed now if force_empty_hrefless_a was
3036 	   requested - VH */
3037 	if (href == NULL && force_empty_hrefless_a) {
3038 	    SET_SKIP_STACK(HTML_A);
3039 	    HTML_end_element(me, HTML_A, include);
3040 	}
3041 #endif
3042 	FREE(href);
3043 	break;
3044 
3045     case HTML_IMG:		/* Images */
3046 	/*
3047 	 * If we're in an anchor, get the destination, and if it's a clickable
3048 	 * image for the current anchor, set our flags for faking a 0,0
3049 	 * coordinate pair, which typically returns the image's default.  - FM
3050 	 */
3051 	if (me->inA && me->CurrentA) {
3052 	    if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
3053 		 )) != NULL) {
3054 		if (dest->isISMAPScript == TRUE) {
3055 		    dest_ismap = TRUE;
3056 		    CTRACE((tfp, "HTML: '%s' is an ISMAP script\n",
3057 			    dest->address));
3058 		} else if (present && present[HTML_IMG_ISMAP]) {
3059 		    dest_ismap = TRUE;
3060 		    dest->isISMAPScript = TRUE;
3061 		    CTRACE((tfp, "HTML: Designating '%s' as an ISMAP script\n",
3062 			    dest->address));
3063 		}
3064 	    }
3065 	}
3066 
3067 	intern_flag = FALSE;	/* unless set below - kw */
3068 	/*
3069 	 * If there's a USEMAP, resolve it.  - FM
3070 	 */
3071 	if (present && present[HTML_IMG_USEMAP] &&
3072 	    non_empty(value[HTML_IMG_USEMAP])) {
3073 	    StrAllocCopy(map_href, value[HTML_IMG_USEMAP]);
3074 	    CHECK_FOR_INTERN(intern_flag, map_href);
3075 	    (void) LYLegitimizeHREF(me, &map_href, TRUE, TRUE);
3076 	    /*
3077 	     * If map_href ended up zero-length or otherwise doesn't have a
3078 	     * hash, it can't be valid, so ignore it.  - FM
3079 	     */
3080 	    if (findPoundSelector(map_href) == NULL) {
3081 		FREE(map_href);
3082 	    }
3083 	}
3084 
3085 	/*
3086 	 * Handle a MAP reference if we have one at this point.  - FM
3087 	 */
3088 	if (map_href) {
3089 	    /*
3090 	     * If the MAP reference doesn't yet begin with a scheme, check
3091 	     * whether a base tag is in effect.  - FM
3092 	     */
3093 	    /*
3094 	     * If the USEMAP value is a lone fragment and LYSeekFragMAPinCur is
3095 	     * set, we'll use the current document's URL for resolving.
3096 	     * Otherwise use the BASE.  - kw
3097 	     */
3098 	    Base = ((me->inBASE &&
3099 		     !(*map_href == '#' && LYSeekFragMAPinCur == TRUE))
3100 		    ? me->base_href
3101 		    : me->node_anchor->address);
3102 	    HTParseALL(&map_href, Base);
3103 
3104 	    /*
3105 	     * Prepend our client-side MAP access field.  - FM
3106 	     */
3107 	    StrAllocCopy(temp, STR_LYNXIMGMAP);
3108 	    StrAllocCat(temp, map_href);
3109 	    StrAllocCopy(map_href, temp);
3110 	    FREE(temp);
3111 	}
3112 
3113 	/*
3114 	 * Check whether we want to suppress the server-side ISMAP link if a
3115 	 * client-side MAP is present.  - FM
3116 	 */
3117 	if (LYNoISMAPifUSEMAP && map_href && dest_ismap) {
3118 	    dest_ismap = FALSE;
3119 	    dest = NULL;
3120 	}
3121 
3122 	/*
3123 	 * Check for a TITLE attribute.  - FM
3124 	 */
3125 	if (present && present[HTML_IMG_TITLE] &&
3126 	    non_empty(value[HTML_IMG_TITLE])) {
3127 	    StrAllocCopy(title, value[HTML_IMG_TITLE]);
3128 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
3129 	    LYTrimHead(title);
3130 	    LYTrimTail(title);
3131 	    if (*title == '\0') {
3132 		FREE(title);
3133 	    }
3134 	}
3135 
3136 	/*
3137 	 * If there's an ALT string, use it, unless the ALT string is
3138 	 * zero-length or just spaces and we are making all SRCs links or have
3139 	 * a USEMAP link.  - FM
3140 	 */
3141 	if (((present) &&
3142 	     (present[HTML_IMG_ALT] && value[HTML_IMG_ALT])) &&
3143 	    (!clickable_images ||
3144 	     ((clickable_images || map_href) &&
3145 	      *value[HTML_IMG_ALT] != '\0'))) {
3146 	    StrAllocCopy(alt_string, value[HTML_IMG_ALT]);
3147 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
3148 					    me->UsePlainSpace, me->HiddenValue);
3149 	    /*
3150 	     * If it's all spaces and we are making SRC or USEMAP links, treat
3151 	     * it as zero-length.  - FM
3152 	     */
3153 	    if (clickable_images || map_href) {
3154 		LYTrimHead(alt_string);
3155 		LYTrimTail(alt_string);
3156 		if (*alt_string == '\0') {
3157 		    if (map_href) {
3158 			StrAllocCopy(alt_string, (title ? title :
3159 						  (temp = MakeNewMapValue(value,
3160 									  "USEMAP"))));
3161 			FREE(temp);
3162 		    } else if (dest_ismap) {
3163 			StrAllocCopy(alt_string, (title ? title :
3164 						  (temp = MakeNewMapValue(value,
3165 									  "ISMAP"))));
3166 			FREE(temp);
3167 
3168 		    } else if (me->inA == TRUE && dest) {
3169 			StrAllocCopy(alt_string, (title ?
3170 						  title :
3171 						  VERBOSE_IMG(value, HTML_IMG_SRC,
3172 							      "[LINK]")));
3173 
3174 		    } else {
3175 			StrAllocCopy(alt_string,
3176 				     (title ? title :
3177 				      ((present &&
3178 					present[HTML_IMG_ISOBJECT]) ?
3179 				       "(OBJECT)" :
3180 				       VERBOSE_IMG(value, HTML_IMG_SRC,
3181 						   "[INLINE]"))));
3182 		    }
3183 		}
3184 	    }
3185 
3186 	} else if (map_href) {
3187 	    StrAllocCopy(alt_string, (title ? title :
3188 				      (temp = MakeNewMapValue(value, "USEMAP"))));
3189 	    FREE(temp);
3190 
3191 	} else if ((dest_ismap == TRUE) ||
3192 		   (me->inA && present && present[HTML_IMG_ISMAP])) {
3193 	    StrAllocCopy(alt_string, (title ? title :
3194 				      (temp = MakeNewMapValue(value, "ISMAP"))));
3195 	    FREE(temp);
3196 
3197 	} else if (me->inA == TRUE && dest) {
3198 	    StrAllocCopy(alt_string, (title ?
3199 				      title :
3200 				      VERBOSE_IMG(value, HTML_IMG_SRC,
3201 						  "[LINK]")));
3202 
3203 	} else {
3204 	    if (pseudo_inline_alts || clickable_images)
3205 		StrAllocCopy(alt_string, (title ? title :
3206 					  ((present &&
3207 					    present[HTML_IMG_ISOBJECT]) ?
3208 					   "(OBJECT)" :
3209 					   VERBOSE_IMG(value, HTML_IMG_SRC,
3210 						       "[INLINE]"))));
3211 	    else
3212 		StrAllocCopy(alt_string, NonNull(title));
3213 	}
3214 	if (*alt_string == '\0' && map_href) {
3215 	    StrAllocCopy(alt_string, (temp = MakeNewMapValue(value, "USEMAP")));
3216 	    FREE(temp);
3217 	}
3218 
3219 	CTRACE((tfp, "HTML IMG: USEMAP=%d ISMAP=%d ANCHOR=%d PARA=%d\n",
3220 		map_href ? 1 : 0,
3221 		(dest_ismap == TRUE) ? 1 : 0,
3222 		me->inA, me->inP));
3223 
3224 	/*
3225 	 * Check for an ID attribute.  - FM
3226 	 */
3227 	if (present && present[HTML_IMG_ID] &&
3228 	    non_empty(value[HTML_IMG_ID])) {
3229 	    StrAllocCopy(id_string, value[HTML_IMG_ID]);
3230 	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
3231 	    if (*id_string == '\0') {
3232 		FREE(id_string);
3233 	    }
3234 	}
3235 
3236 	/*
3237 	 * Create links to the SRC for all images, if desired.  - FM
3238 	 */
3239 	if (clickable_images &&
3240 	    present && present[HTML_IMG_SRC] &&
3241 	    non_empty(value[HTML_IMG_SRC])) {
3242 	    StrAllocCopy(href, value[HTML_IMG_SRC]);
3243 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
3244 
3245 	    /*
3246 	     * If it's an ISMAP and/or USEMAP, or graphic for an anchor, end
3247 	     * that anchor and start one for the SRC.  - FM
3248 	     */
3249 	    if (me->inA) {
3250 		/*
3251 		 * If we have a USEMAP, end this anchor and start a new one for
3252 		 * the client-side MAP.  - FM
3253 		 */
3254 		if (map_href) {
3255 		    if (dest_ismap) {
3256 			HTML_put_character(me, ' ');
3257 			me->in_word = NO;
3258 			HTML_put_string(me,
3259 					(temp = MakeNewMapValue(value, "ISMAP")));
3260 			FREE(temp);
3261 		    } else if (dest) {
3262 			HTML_put_character(me, ' ');
3263 			me->in_word = NO;
3264 			HTML_put_string(me, "[LINK]");
3265 		    }
3266 		    if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
3267 			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3268 		    }
3269 		    me->inBoldA = FALSE;
3270 		    HText_endAnchor(me->text, me->CurrentANum);
3271 		    me->CurrentANum = 0;
3272 		    if (dest_ismap || dest)
3273 			HTML_put_character(me, '-');
3274 		    if (id_string) {
3275 			if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3276 							      id_string,	/* Tag */
3277 							      NULL,	/* Addresss */
3278 							      0)) != NULL) {	/* Type */
3279 			    HText_beginAnchor(me->text, me->inUnderline, ID_A);
3280 			    HText_endAnchor(me->text, 0);
3281 			}
3282 		    }
3283 		    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3284 							     NULL,	/* Tag */
3285 							     map_href,	/* Addresss */
3286 							     INTERN_LT);	/* Type */
3287 		    if (me->CurrentA && title) {
3288 			if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
3289 			     )) != NULL) {
3290 			    if (!HTAnchor_title(dest))
3291 				HTAnchor_setTitle(dest, title);
3292 			}
3293 		    }
3294 		    me->CurrentANum = HText_beginAnchor(me->text,
3295 							me->inUnderline,
3296 							me->CurrentA);
3297 		    if (me->inBoldA == FALSE && me->inBoldH == FALSE) {
3298 			HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3299 		    }
3300 		    me->inBoldA = TRUE;
3301 		} else {
3302 		    HTML_put_character(me, ' ');	/* space char may be ignored */
3303 		    me->in_word = NO;
3304 		}
3305 		HTML_put_string(me, alt_string);
3306 		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
3307 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3308 		}
3309 		me->inBoldA = FALSE;
3310 		HText_endAnchor(me->text, me->CurrentANum);
3311 		me->CurrentANum = 0;
3312 		HTML_put_character(me, '-');
3313 		FREE(newtitle);
3314 		StrAllocCopy(alt_string,
3315 			     ((present &&
3316 			       present[HTML_IMG_ISOBJECT]) ?
3317 			      ((map_href || dest_ismap) ?
3318 			       "(IMAGE)" : "(OBJECT)") :
3319 			      VERBOSE_IMG(value, HTML_IMG_SRC, "[IMAGE]")));
3320 		if (id_string && !map_href) {
3321 		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3322 							  id_string,	/* Tag */
3323 							  NULL,		/* Addresss */
3324 							  0)) != NULL) {	/* Type */
3325 			HText_beginAnchor(me->text, me->inUnderline, ID_A);
3326 			HText_endAnchor(me->text, 0);
3327 		    }
3328 		}
3329 	    } else if (map_href) {
3330 		HTML_put_character(me, ' ');	/* space char may be ignored */
3331 		me->in_word = NO;
3332 		if (id_string) {
3333 		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3334 							  id_string,	/* Tag */
3335 							  NULL,		/* Addresss */
3336 							  0)) != NULL) {	/* Type */
3337 			HText_beginAnchor(me->text, me->inUnderline, ID_A);
3338 			HText_endAnchor(me->text, 0);
3339 		    }
3340 		}
3341 		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3342 							 NULL,	/* Tag */
3343 							 map_href,	/* Addresss */
3344 							 INTERN_LT);	/* Type */
3345 		if (me->CurrentA && title) {
3346 		    if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
3347 			 )) != NULL) {
3348 			if (!HTAnchor_title(dest))
3349 			    HTAnchor_setTitle(dest, title);
3350 		    }
3351 		}
3352 		me->CurrentANum = HText_beginAnchor(me->text,
3353 						    me->inUnderline,
3354 						    me->CurrentA);
3355 		if (me->inBoldA == FALSE && me->inBoldH == FALSE)
3356 		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3357 		me->inBoldA = TRUE;
3358 		HTML_put_string(me, alt_string);
3359 		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
3360 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3361 		}
3362 		me->inBoldA = FALSE;
3363 		HText_endAnchor(me->text, me->CurrentANum);
3364 		me->CurrentANum = 0;
3365 		HTML_put_character(me, '-');
3366 		FREE(newtitle);
3367 		StrAllocCopy(alt_string,
3368 			     ((present &&
3369 			       present[HTML_IMG_ISOBJECT]) ?
3370 			      "(IMAGE)" :
3371 			      VERBOSE_IMG(value, HTML_IMG_SRC, "[IMAGE]")));
3372 	    } else {
3373 		HTML_put_character(me, ' ');	/* space char may be ignored */
3374 		me->in_word = NO;
3375 		if (id_string) {
3376 		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3377 							  id_string,	/* Tag */
3378 							  NULL,		/* Addresss */
3379 							  0)) != NULL) {	/* Type */
3380 			HText_beginAnchor(me->text, me->inUnderline, ID_A);
3381 			HText_endAnchor(me->text, 0);
3382 		    }
3383 		}
3384 	    }
3385 
3386 	    /*
3387 	     * Create the link to the SRC.  - FM
3388 	     */
3389 	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3390 						     NULL,	/* Tag */
3391 						     href,	/* Addresss */
3392 						     (HTLinkType *) 0);		/* Type */
3393 	    FREE(href);
3394 	    me->CurrentANum = HText_beginAnchor(me->text,
3395 						me->inUnderline,
3396 						me->CurrentA);
3397 	    if (me->inBoldH == FALSE)
3398 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3399 	    HTML_put_string(me, alt_string);
3400 	    if (!me->inA) {
3401 		if (me->inBoldH == FALSE)
3402 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3403 		HText_endAnchor(me->text, me->CurrentANum);
3404 		me->CurrentANum = 0;
3405 		HTML_put_character(me, ' ');	/* space char may be ignored */
3406 		me->in_word = NO;
3407 	    } else {
3408 		HTML_put_character(me, ' ');	/* space char may be ignored */
3409 		me->in_word = NO;
3410 		me->inBoldA = TRUE;
3411 	    }
3412 	} else if (map_href) {
3413 	    if (me->inA) {
3414 		/*
3415 		 * We're in an anchor and have a USEMAP, so end the anchor and
3416 		 * start a new one for the client-side MAP.  - FM
3417 		 */
3418 		if (dest_ismap) {
3419 		    HTML_put_character(me, ' ');	/* space char may be ignored */
3420 		    me->in_word = NO;
3421 		    HTML_put_string(me, (temp = MakeNewMapValue(value, "ISMAP")));
3422 		    FREE(temp);
3423 		} else if (dest) {
3424 		    HTML_put_character(me, ' ');	/* space char may be ignored */
3425 		    me->in_word = NO;
3426 		    HTML_put_string(me, "[LINK]");
3427 		}
3428 		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
3429 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3430 		}
3431 		me->inBoldA = FALSE;
3432 		HText_endAnchor(me->text, me->CurrentANum);
3433 		me->CurrentANum = 0;
3434 		if (dest_ismap || dest) {
3435 		    HTML_put_character(me, '-');
3436 		}
3437 	    } else {
3438 		HTML_put_character(me, ' ');
3439 		me->in_word = NO;
3440 	    }
3441 	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3442 						     NULL,	/* Tag */
3443 						     map_href,	/* Addresss */
3444 						     INTERN_LT);	/* Type */
3445 	    if (me->CurrentA && title) {
3446 		if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
3447 		     )) != NULL) {
3448 		    if (!HTAnchor_title(dest))
3449 			HTAnchor_setTitle(dest, title);
3450 		}
3451 	    }
3452 	    me->CurrentANum = HText_beginAnchor(me->text,
3453 						me->inUnderline,
3454 						me->CurrentA);
3455 	    if (me->inBoldA == FALSE && me->inBoldH == FALSE) {
3456 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3457 	    }
3458 	    me->inBoldA = TRUE;
3459 	    HTML_put_string(me, alt_string);
3460 	    if (!me->inA) {
3461 		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
3462 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3463 		}
3464 		me->inBoldA = FALSE;
3465 		HText_endAnchor(me->text, me->CurrentANum);
3466 		me->CurrentANum = 0;
3467 	    }
3468 	} else {
3469 	    /*
3470 	     * Just put in the ALT or pseudo-ALT string for the current anchor
3471 	     * or inline, with an ID link if indicated.  - FM
3472 	     */
3473 	    HTML_put_character(me, ' ');	/* space char may be ignored */
3474 	    me->in_word = NO;
3475 	    if (id_string) {
3476 		if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3477 						      id_string,	/* Tag */
3478 						      NULL,	/* Addresss */
3479 						      (HTLinkType *) 0)) != NULL) {	/* Type */
3480 		    HText_beginAnchor(me->text, me->inUnderline, ID_A);
3481 		    HText_endAnchor(me->text, 0);
3482 		}
3483 	    }
3484 	    HTML_put_string(me, alt_string);
3485 	    HTML_put_character(me, ' ');	/* space char may be ignored */
3486 	    me->in_word = NO;
3487 	}
3488 	FREE(map_href);
3489 	FREE(alt_string);
3490 	FREE(id_string);
3491 	FREE(title);
3492 	FREE(newtitle);
3493 	dest = NULL;
3494 	break;
3495 
3496     case HTML_MAP:
3497 	/*
3498 	 * Load id_string if we have a NAME or ID.  - FM
3499 	 */
3500 	if (present && present[HTML_MAP_NAME] &&
3501 	    non_empty(value[HTML_MAP_NAME])) {
3502 	    StrAllocCopy(id_string, value[HTML_MAP_NAME]);
3503 	} else if (present && present[HTML_MAP_ID] &&
3504 		   non_empty(value[HTML_MAP_ID])) {
3505 	    StrAllocCopy(id_string, value[HTML_MAP_ID]);
3506 	}
3507 	if (id_string) {
3508 	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
3509 	    if (*id_string == '\0') {
3510 		FREE(id_string);
3511 	    }
3512 	}
3513 
3514 	/*
3515 	 * Generate a target anchor in this place in the containing document.
3516 	 * MAP can now contain block markup, if it doesn't contain any AREAs
3517 	 * (or A anchors with COORDS converted to AREAs) the current location
3518 	 * can be used as a fallback for following a USEMAP link.  - kw
3519 	 */
3520 	if (!LYMapsOnly)
3521 	    LYHandleID(me, id_string);
3522 
3523 	/*
3524 	 * Load map_address.  - FM
3525 	 */
3526 	if (id_string) {
3527 	    /*
3528 	     * The MAP must be in the current stream, even if it had a BASE
3529 	     * tag, so we'll use its address here, but still use the BASE, if
3530 	     * present, when resolving the AREA elements in its content,
3531 	     * unless the AREA's HREF is a lone fragment and
3532 	     * LYSeekFragAREAinCur is set.  - FM && KW
3533 	     */
3534 	    StrAllocCopy(me->map_address, me->node_anchor->address);
3535 	    if ((cp = strchr(me->map_address, '#')) != NULL)
3536 		*cp = '\0';
3537 	    StrAllocCat(me->map_address, "#");
3538 	    StrAllocCat(me->map_address, id_string);
3539 	    FREE(id_string);
3540 	    if (present && present[HTML_MAP_TITLE] &&
3541 		non_empty(value[HTML_MAP_TITLE])) {
3542 		StrAllocCopy(title, value[HTML_MAP_TITLE]);
3543 		TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
3544 		LYTrimHead(title);
3545 		LYTrimTail(title);
3546 		if (*title == '\0') {
3547 		    FREE(title);
3548 		}
3549 	    }
3550 	    LYAddImageMap(me->map_address, title, me->node_anchor);
3551 	    FREE(title);
3552 	}
3553 	break;
3554 
3555     case HTML_AREA:
3556 	if (me->map_address &&
3557 	    present && present[HTML_AREA_HREF] &&
3558 	    non_empty(value[HTML_AREA_HREF])) {
3559 	    /*
3560 	     * Resolve the HREF.  - FM
3561 	     */
3562 	    StrAllocCopy(href, value[HTML_AREA_HREF]);
3563 	    CHECK_FOR_INTERN(intern_flag, href);
3564 	    (void) LYLegitimizeHREF(me, &href, TRUE, TRUE);
3565 
3566 	    /*
3567 	     * Check whether a BASE tag is in effect, and use it for resolving,
3568 	     * even though we used this stream's address for locating the MAP
3569 	     * itself, unless the HREF is a lone fragment and
3570 	     * LYSeekFragAREAinCur is set.  - FM
3571 	     */
3572 	    Base = (((me->inBASE && *href != '\0') &&
3573 		     !(*href == '#' && LYSeekFragAREAinCur == TRUE))
3574 		    ? me->base_href
3575 		    : me->node_anchor->address);
3576 	    HTParseALL(&href, Base);
3577 
3578 	    /*
3579 	     * Check for an ALT.  - FM
3580 	     */
3581 	    if (present[HTML_AREA_ALT] &&
3582 		non_empty(value[HTML_AREA_ALT])) {
3583 		StrAllocCopy(alt_string, value[HTML_AREA_ALT]);
3584 	    } else if (present[HTML_AREA_TITLE] &&
3585 		       non_empty(value[HTML_AREA_TITLE])) {
3586 		/*
3587 		 * Use the TITLE as an ALT.  - FM
3588 		 */
3589 		StrAllocCopy(alt_string, value[HTML_AREA_TITLE]);
3590 	    }
3591 	    if (alt_string != NULL) {
3592 		TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
3593 						me->UsePlainSpace,
3594 						me->HiddenValue);
3595 		/*
3596 		 * Make sure it's not just space(s).  - FM
3597 		 */
3598 		LYTrimHead(alt_string);
3599 		LYTrimTail(alt_string);
3600 		if (*alt_string == '\0') {
3601 		    StrAllocCopy(alt_string, href);
3602 		}
3603 	    } else {
3604 		/*
3605 		 * Use the HREF as an ALT.  - FM
3606 		 */
3607 		StrAllocCopy(alt_string, href);
3608 	    }
3609 
3610 	    LYAddMapElement(me->map_address, href, alt_string,
3611 			    me->node_anchor, intern_flag);
3612 	    FREE(href);
3613 	    FREE(alt_string);
3614 	}
3615 	break;
3616 
3617     case HTML_PARAM:
3618 	/*
3619 	 * We may need to look at this someday to deal with MAPs, OBJECTs or
3620 	 * APPLETs optimally, but just ignore it for now.  - FM
3621 	 */
3622 	break;
3623 
3624     case HTML_BODYTEXT:
3625 	CHECK_ID(HTML_BODYTEXT_ID);
3626 	/*
3627 	 * We may need to look at this someday to deal with OBJECTs optimally,
3628 	 * but just ignore it for now.  - FM
3629 	 */
3630 	break;
3631 
3632     case HTML_TEXTFLOW:
3633 	CHECK_ID(HTML_BODYTEXT_ID);
3634 	/*
3635 	 * We may need to look at this someday to deal with APPLETs optimally,
3636 	 * but just ignore it for now.  - FM
3637 	 */
3638 	break;
3639 
3640     case HTML_FIG:
3641 	if (present)
3642 	    LYHandleFIG(me, present, value,
3643 			present[HTML_FIG_ISOBJECT],
3644 			present[HTML_FIG_IMAGEMAP],
3645 			present[HTML_FIG_ID] ? value[HTML_FIG_ID] : NULL,
3646 			present[HTML_FIG_SRC] ? value[HTML_FIG_SRC] : NULL,
3647 			YES, TRUE, &intern_flag);
3648 	else
3649 	    LYHandleFIG(me, NULL, NULL,
3650 			0,
3651 			0,
3652 			NULL,
3653 			NULL, YES, TRUE, &intern_flag);
3654 	break;
3655 
3656     case HTML_OBJECT:
3657 	if (!me->object_started) {
3658 	    /*
3659 	     * This is an outer OBJECT start tag, i.e., not a nested OBJECT, so
3660 	     * save its relevant attributes.  - FM
3661 	     */
3662 	    if (present) {
3663 		if (present[HTML_OBJECT_DECLARE])
3664 		    me->object_declare = TRUE;
3665 		if (present[HTML_OBJECT_SHAPES])
3666 		    me->object_shapes = TRUE;
3667 		if (present[HTML_OBJECT_ISMAP])
3668 		    me->object_ismap = TRUE;
3669 		if (present[HTML_OBJECT_USEMAP] &&
3670 		    non_empty(value[HTML_OBJECT_USEMAP])) {
3671 		    StrAllocCopy(me->object_usemap, value[HTML_OBJECT_USEMAP]);
3672 		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_usemap);
3673 		    if (*me->object_usemap == '\0') {
3674 			FREE(me->object_usemap);
3675 		    }
3676 		}
3677 		if (present[HTML_OBJECT_ID] &&
3678 		    non_empty(value[HTML_OBJECT_ID])) {
3679 		    StrAllocCopy(me->object_id, value[HTML_OBJECT_ID]);
3680 		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_id);
3681 		    if (*me->object_id == '\0') {
3682 			FREE(me->object_id);
3683 		    }
3684 		}
3685 		if (present[HTML_OBJECT_TITLE] &&
3686 		    non_empty(value[HTML_OBJECT_TITLE])) {
3687 		    StrAllocCopy(me->object_title, value[HTML_OBJECT_TITLE]);
3688 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_title, TRUE, FALSE);
3689 		    LYTrimHead(me->object_title);
3690 		    LYTrimTail(me->object_title);
3691 		    if (me->object_title == '\0') {
3692 			FREE(me->object_title);
3693 		    }
3694 		}
3695 		if (present[HTML_OBJECT_DATA] &&
3696 		    non_empty(value[HTML_OBJECT_DATA])) {
3697 		    StrAllocCopy(me->object_data, value[HTML_OBJECT_DATA]);
3698 		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_data);
3699 		    if (*me->object_data == '\0') {
3700 			FREE(me->object_data);
3701 		    }
3702 		}
3703 		if (present[HTML_OBJECT_TYPE] &&
3704 		    non_empty(value[HTML_OBJECT_TYPE])) {
3705 		    StrAllocCopy(me->object_type, value[HTML_OBJECT_TYPE]);
3706 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_type, TRUE, FALSE);
3707 		    LYTrimHead(me->object_type);
3708 		    LYTrimTail(me->object_type);
3709 		    if (me->object_type == '\0') {
3710 			FREE(me->object_type);
3711 		    }
3712 		}
3713 		if (present[HTML_OBJECT_CLASSID] &&
3714 		    non_empty(value[HTML_OBJECT_CLASSID])) {
3715 		    StrAllocCopy(me->object_classid,
3716 				 value[HTML_OBJECT_CLASSID]);
3717 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_classid, TRUE, FALSE);
3718 		    LYTrimHead(me->object_classid);
3719 		    LYTrimTail(me->object_classid);
3720 		    if (me->object_classid == '\0') {
3721 			FREE(me->object_classid);
3722 		    }
3723 		}
3724 		if (present[HTML_OBJECT_CODEBASE] &&
3725 		    non_empty(value[HTML_OBJECT_CODEBASE])) {
3726 		    StrAllocCopy(me->object_codebase,
3727 				 value[HTML_OBJECT_CODEBASE]);
3728 		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_codebase);
3729 		    if (*me->object_codebase == '\0') {
3730 			FREE(me->object_codebase);
3731 		    }
3732 		}
3733 		if (present[HTML_OBJECT_CODETYPE] &&
3734 		    non_empty(value[HTML_OBJECT_CODETYPE])) {
3735 		    StrAllocCopy(me->object_codetype,
3736 				 value[HTML_OBJECT_CODETYPE]);
3737 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_codetype,
3738 						    TRUE,
3739 						    FALSE);
3740 		    LYTrimHead(me->object_codetype);
3741 		    LYTrimTail(me->object_codetype);
3742 		    if (me->object_codetype == '\0') {
3743 			FREE(me->object_codetype);
3744 		    }
3745 		}
3746 		if (present[HTML_OBJECT_NAME] &&
3747 		    non_empty(value[HTML_OBJECT_NAME])) {
3748 		    StrAllocCopy(me->object_name, value[HTML_OBJECT_NAME]);
3749 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_name, TRUE, FALSE);
3750 		    LYTrimHead(me->object_name);
3751 		    LYTrimTail(me->object_name);
3752 		    if (me->object_name == '\0') {
3753 			FREE(me->object_name);
3754 		    }
3755 		}
3756 	    }
3757 	    /*
3758 	     * If we can determine now that we are not going to do anything
3759 	     * special to the OBJECT element's SGML contents, like skipping it
3760 	     * completely or collecting it up in order to add something after
3761 	     * it, then generate any output that should be emitted in the place
3762 	     * of the OBJECT start tag NOW, then don't initialize special
3763 	     * handling but return, letting our SGML parser know that further
3764 	     * content is to be parsed normally not literally.  We could defer
3765 	     * this until we have collected the contents and then recycle the
3766 	     * contents (as was previously always done), but that has a higher
3767 	     * chance of completely losing content in case of nesting errors in
3768 	     * the input, incomplete transmissions, etc.  - kw
3769 	     */
3770 	    if ((!present ||
3771 		 (me->object_declare == FALSE && me->object_name == NULL &&
3772 		  me->object_shapes == FALSE && me->object_usemap == NULL))) {
3773 		if (!LYMapsOnly) {
3774 		    if (!clickable_images || me->object_data == NULL ||
3775 			!(me->object_data != NULL &&
3776 			  me->object_classid == NULL &&
3777 			  me->object_codebase == NULL &&
3778 			  me->object_codetype == NULL))
3779 			FREE(me->object_data);
3780 		    if (me->object_data) {
3781 			HTStartAnchor5(me,
3782 				       (me->object_id
3783 					? value[HTML_OBJECT_ID]
3784 					: NULL),
3785 				       value[HTML_OBJECT_DATA],
3786 				       value[HTML_OBJECT_TYPE],
3787 				       tag_charset);
3788 			if ((me->object_type != NULL) &&
3789 			    !strncasecomp(me->object_type, "image/", 6))
3790 			    HTML_put_string(me, "(IMAGE)");
3791 			else
3792 			    HTML_put_string(me, "(OBJECT)");
3793 			HTML_end_element(me, HTML_A, NULL);
3794 		    } else if (me->object_id)
3795 			LYHandleID(me, me->object_id);
3796 		}
3797 		clear_objectdata(me);
3798 		/*
3799 		 * We do NOT want the HTML_put_* functions that are going to be
3800 		 * called for the OBJECT's character content to add to the
3801 		 * chunk, so we don't push on the stack.  Instead we keep a
3802 		 * counter for open OBJECT tags that are treated this way, so
3803 		 * HTML_end_element can skip handling the corresponding end tag
3804 		 * that is going to arrive unexpectedly as far as our stack is
3805 		 * concerned.
3806 		 */
3807 		status = HT_PARSER_OTHER_CONTENT;
3808 		if (me->sp[0].tag_number == HTML_FIG &&
3809 		    me->objects_figged_open > 0) {
3810 		    ElementNumber = (HTMLElement) HTML_OBJECT_M;
3811 		} else {
3812 		    me->objects_mixed_open++;
3813 		    SET_SKIP_STACK(HTML_OBJECT);
3814 		}
3815 	    } else if (me->object_declare == FALSE && me->object_name == NULL &&
3816 		       me->object_shapes == TRUE) {
3817 		LYHandleFIG(me, present, value,
3818 			    1,
3819 			    1 || me->object_ismap,
3820 			    me->object_id,
3821 			    ((me->object_data && !me->object_classid)
3822 			     ? value[HTML_OBJECT_DATA]
3823 			     : NULL),
3824 			    NO, TRUE, &intern_flag);
3825 		clear_objectdata(me);
3826 		status = HT_PARSER_OTHER_CONTENT;
3827 		me->objects_figged_open++;
3828 		ElementNumber = HTML_FIG;
3829 
3830 	    } else {
3831 		/*
3832 		 * Set flag that we are accumulating OBJECT content.  - FM
3833 		 */
3834 		me->object_started = TRUE;
3835 	    }
3836 	}
3837 	break;
3838 
3839     case HTML_OVERLAY:
3840 	if (clickable_images && me->inFIG &&
3841 	    present && present[HTML_OVERLAY_SRC] &&
3842 	    non_empty(value[HTML_OVERLAY_SRC])) {
3843 	    StrAllocCopy(href, value[HTML_OVERLAY_SRC]);
3844 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
3845 	    if (*href) {
3846 
3847 		if (me->inA) {
3848 		    SET_SKIP_STACK(HTML_A);
3849 		    HTML_end_element(me, HTML_A, include);
3850 		}
3851 		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3852 							 NULL,	/* Tag */
3853 							 href,	/* Addresss */
3854 							 (HTLinkType *) 0);	/* Type */
3855 		HTML_put_character(me, ' ');
3856 		HText_appendCharacter(me->text, '+');
3857 		me->CurrentANum = HText_beginAnchor(me->text,
3858 						    me->inUnderline,
3859 						    me->CurrentA);
3860 		if (me->inBoldH == FALSE)
3861 		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3862 		HTML_put_string(me, "[OVERLAY]");
3863 		if (me->inBoldH == FALSE)
3864 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3865 		HText_endAnchor(me->text, me->CurrentANum);
3866 		HTML_put_character(me, ' ');
3867 		me->in_word = NO;
3868 	    }
3869 	    FREE(href);
3870 	}
3871 	break;
3872 
3873     case HTML_APPLET:
3874 	me->inAPPLET = TRUE;
3875 	me->inAPPLETwithP = FALSE;
3876 	HTML_put_character(me, ' ');	/* space char may be ignored */
3877 	/*
3878 	 * Load id_string if we have an ID or NAME.  - FM
3879 	 */
3880 	if (present && present[HTML_APPLET_ID] &&
3881 	    non_empty(value[HTML_APPLET_ID])) {
3882 	    StrAllocCopy(id_string, value[HTML_APPLET_ID]);
3883 	} else if (present && present[HTML_APPLET_NAME] &&
3884 		   non_empty(value[HTML_APPLET_NAME])) {
3885 	    StrAllocCopy(id_string, value[HTML_APPLET_NAME]);
3886 	}
3887 	if (id_string) {
3888 	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
3889 	    LYHandleID(me, id_string);
3890 	    FREE(id_string);
3891 	}
3892 	me->in_word = NO;
3893 
3894 	/*
3895 	 * If there's an ALT string, use it, unless the ALT string is
3896 	 * zero-length and we are making all sources links.  - FM
3897 	 */
3898 	if (present && present[HTML_APPLET_ALT] && value[HTML_APPLET_ALT] &&
3899 	    (!clickable_images ||
3900 	     (clickable_images && *value[HTML_APPLET_ALT] != '\0'))) {
3901 	    StrAllocCopy(alt_string, value[HTML_APPLET_ALT]);
3902 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
3903 					    me->UsePlainSpace, me->HiddenValue);
3904 	    /*
3905 	     * If it's all spaces and we are making sources links, treat it as
3906 	     * zero-length.  - FM
3907 	     */
3908 	    if (clickable_images) {
3909 		LYTrimHead(alt_string);
3910 		LYTrimTail(alt_string);
3911 		if (*alt_string == '\0') {
3912 		    StrAllocCopy(alt_string, "[APPLET]");
3913 		}
3914 	    }
3915 
3916 	} else {
3917 	    if (clickable_images)
3918 		StrAllocCopy(alt_string, "[APPLET]");
3919 	    else
3920 		StrAllocCopy(alt_string, "");
3921 	}
3922 
3923 	/*
3924 	 * If we're making all sources links, get the source.  - FM
3925 	 */
3926 	if (clickable_images && present && present[HTML_APPLET_CODE] &&
3927 	    non_empty(value[HTML_APPLET_CODE])) {
3928 	    char *base = NULL;
3929 
3930 	    Base = (me->inBASE)
3931 		? me->base_href
3932 		: me->node_anchor->address;
3933 	    /*
3934 	     * Check for a CODEBASE attribute.  - FM
3935 	     */
3936 	    if (present[HTML_APPLET_CODEBASE] &&
3937 		non_empty(value[HTML_APPLET_CODEBASE])) {
3938 		StrAllocCopy(base, value[HTML_APPLET_CODEBASE]);
3939 		LYRemoveBlanks(base);
3940 		TRANSLATE_AND_UNESCAPE_TO_STD(&base);
3941 		/*
3942 		 * Force it to be a directory.  - FM
3943 		 */
3944 		if (*base == '\0')
3945 		    StrAllocCopy(base, "/");
3946 		LYAddHtmlSep(&base);
3947 		LYLegitimizeHREF(me, &base, TRUE, FALSE);
3948 
3949 		HTParseALL(&base, Base);
3950 	    }
3951 
3952 	    StrAllocCopy(href, value[HTML_APPLET_CODE]);
3953 	    LYLegitimizeHREF(me, &href, TRUE, FALSE);
3954 	    HTParseALL(&href, (base ? base : Base));
3955 	    FREE(base);
3956 
3957 	    if (*href) {
3958 		if (me->inA) {
3959 		    if (me->inBoldA == TRUE && me->inBoldH == FALSE)
3960 			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3961 		    HText_endAnchor(me->text, me->CurrentANum);
3962 		    HTML_put_character(me, '-');
3963 		}
3964 		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
3965 							 NULL,	/* Tag */
3966 							 href,	/* Addresss */
3967 							 (HTLinkType *) 0);	/* Type */
3968 		me->CurrentANum = HText_beginAnchor(me->text,
3969 						    me->inUnderline,
3970 						    me->CurrentA);
3971 		if (me->inBoldH == FALSE)
3972 		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
3973 		HTML_put_string(me, alt_string);
3974 		if (me->inA == FALSE) {
3975 		    if (me->inBoldH == FALSE)
3976 			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
3977 		    HText_endAnchor(me->text, me->CurrentANum);
3978 		    me->CurrentANum = 0;
3979 		}
3980 		HTML_put_character(me, ' ');	/* space char may be ignored */
3981 		me->in_word = NO;
3982 	    }
3983 	    FREE(href);
3984 	} else if (*alt_string) {
3985 	    /*
3986 	     * Just put up the ALT string, if non-zero.  - FM
3987 	     */
3988 	    HTML_put_string(me, alt_string);
3989 	    HTML_put_character(me, ' ');	/* space char may be ignored */
3990 	    me->in_word = NO;
3991 	}
3992 	FREE(alt_string);
3993 	FREE(id_string);
3994 	break;
3995 
3996     case HTML_BGSOUND:
3997 	/*
3998 	 * If we're making all sources links, get the source.  - FM
3999 	 */
4000 	if (clickable_images && present && present[HTML_BGSOUND_SRC] &&
4001 	    non_empty(value[HTML_BGSOUND_SRC])) {
4002 	    StrAllocCopy(href, value[HTML_BGSOUND_SRC]);
4003 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
4004 	    if (*href == '\0') {
4005 		FREE(href);
4006 		break;
4007 	    }
4008 
4009 	    if (me->inA) {
4010 		if (me->inBoldA == TRUE && me->inBoldH == FALSE)
4011 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
4012 		HText_endAnchor(me->text, me->CurrentANum);
4013 		HTML_put_character(me, '-');
4014 	    } else {
4015 		HTML_put_character(me, ' ');	/* space char may be ignored */
4016 		me->in_word = NO;
4017 	    }
4018 	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
4019 						     NULL,	/* Tag */
4020 						     href,	/* Addresss */
4021 						     (HTLinkType *) 0);		/* Type */
4022 	    me->CurrentANum = HText_beginAnchor(me->text,
4023 						me->inUnderline,
4024 						me->CurrentA);
4025 	    if (me->inBoldH == FALSE)
4026 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
4027 	    HTML_put_string(me, "[BGSOUND]");
4028 	    if (me->inA == FALSE) {
4029 		if (me->inBoldH == FALSE)
4030 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
4031 		HText_endAnchor(me->text, me->CurrentANum);
4032 		me->CurrentANum = 0;
4033 	    }
4034 	    HTML_put_character(me, ' ');	/* space char may be ignored */
4035 	    me->in_word = NO;
4036 	    FREE(href);
4037 	}
4038 	break;
4039 
4040     case HTML_EMBED:
4041 	if (pseudo_inline_alts || clickable_images)
4042 	    HTML_put_character(me, ' ');	/* space char may be ignored */
4043 	/*
4044 	 * Load id_string if we have an ID or NAME.  - FM
4045 	 */
4046 	if (present && present[HTML_EMBED_ID] &&
4047 	    non_empty(value[HTML_EMBED_ID])) {
4048 	    StrAllocCopy(id_string, value[HTML_EMBED_ID]);
4049 	} else if (present && present[HTML_EMBED_NAME] &&
4050 		   non_empty(value[HTML_EMBED_NAME])) {
4051 	    StrAllocCopy(id_string, value[HTML_EMBED_NAME]);
4052 	}
4053 	if (id_string) {
4054 	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
4055 	    LYHandleID(me, id_string);
4056 	    FREE(id_string);
4057 	}
4058 	if (pseudo_inline_alts || clickable_images)
4059 	    me->in_word = NO;
4060 
4061 	/*
4062 	 * If there's an ALT string, use it, unless the ALT string is
4063 	 * zero-length and we are making all sources links.  - FM
4064 	 */
4065 	if (present && present[HTML_EMBED_ALT] && value[HTML_EMBED_ALT] &&
4066 	    (!clickable_images ||
4067 	     (clickable_images && *value[HTML_EMBED_ALT] != '\0'))) {
4068 	    StrAllocCopy(alt_string, value[HTML_EMBED_ALT]);
4069 	    TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
4070 					    me->UsePlainSpace, me->HiddenValue);
4071 	    /*
4072 	     * If it's all spaces and we are making sources links, treat it as
4073 	     * zero-length.  - FM
4074 	     */
4075 	    if (clickable_images) {
4076 		LYTrimHead(alt_string);
4077 		LYTrimTail(alt_string);
4078 		if (*alt_string == '\0') {
4079 		    StrAllocCopy(alt_string, "[EMBED]");
4080 		}
4081 	    }
4082 	} else {
4083 	    if (pseudo_inline_alts || clickable_images)
4084 		StrAllocCopy(alt_string, "[EMBED]");
4085 	    else
4086 		StrAllocCopy(alt_string, "");
4087 	}
4088 
4089 	/*
4090 	 * If we're making all sources links, get the source.  - FM
4091 	 */
4092 	if (clickable_images && present && present[HTML_EMBED_SRC] &&
4093 	    non_empty(value[HTML_EMBED_SRC])) {
4094 	    StrAllocCopy(href, value[HTML_EMBED_SRC]);
4095 	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
4096 	    if (*href) {
4097 		if (me->inA) {
4098 		    if (me->inBoldA == TRUE && me->inBoldH == FALSE)
4099 			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
4100 		    HText_endAnchor(me->text, me->CurrentANum);
4101 		    HTML_put_character(me, '-');
4102 		}
4103 		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
4104 							 NULL,	/* Tag */
4105 							 href,	/* Addresss */
4106 							 (HTLinkType *) 0);	/* Type */
4107 		me->CurrentANum = HText_beginAnchor(me->text,
4108 						    me->inUnderline,
4109 						    me->CurrentA);
4110 		if (me->inBoldH == FALSE)
4111 		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
4112 		HTML_put_string(me, alt_string);
4113 		if (me->inBoldH == FALSE)
4114 		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
4115 		if (me->inA == FALSE) {
4116 		    if (me->inBoldH == FALSE)
4117 			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
4118 		    HText_endAnchor(me->text, me->CurrentANum);
4119 		    me->CurrentANum = 0;
4120 		}
4121 		HTML_put_character(me, ' ');
4122 		me->in_word = NO;
4123 	    }
4124 	    FREE(href);
4125 	} else if (*alt_string) {
4126 	    /*
4127 	     * Just put up the ALT string, if non-zero.  - FM
4128 	     */
4129 	    HTML_put_string(me, alt_string);
4130 	    HTML_put_character(me, ' ');	/* space char may be ignored */
4131 	    me->in_word = NO;
4132 	}
4133 	FREE(alt_string);
4134 	FREE(id_string);
4135 	break;
4136 
4137     case HTML_CREDIT:
4138 	LYEnsureDoubleSpace(me);
4139 	LYResetParagraphAlignment(me);
4140 	me->inCREDIT = TRUE;
4141 	CHECK_ID(HTML_GEN_ID);
4142 	if (me->inUnderline == FALSE)
4143 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
4144 	HTML_put_string(me, "CREDIT:");
4145 	if (me->inUnderline == FALSE)
4146 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
4147 	HTML_put_character(me, ' ');
4148 	CAN_JUSTIFY_START;
4149 
4150 	if (me->inFIG)
4151 	    /*
4152 	     * Assume all text in the FIG container is intended to be
4153 	     * paragraphed.  - FM
4154 	     */
4155 	    me->inFIGwithP = TRUE;
4156 
4157 	if (me->inAPPLET)
4158 	    /*
4159 	     * Assume all text in the APPLET container is intended to be
4160 	     * paragraphed.  - FM
4161 	     */
4162 	    me->inAPPLETwithP = TRUE;
4163 
4164 	me->inLABEL = TRUE;
4165 	me->in_word = NO;
4166 	me->inP = FALSE;
4167 	break;
4168 
4169     case HTML_CAPTION:
4170 	LYEnsureDoubleSpace(me);
4171 	LYResetParagraphAlignment(me);
4172 	me->inCAPTION = TRUE;
4173 	CHECK_ID(HTML_CAPTION_ID);
4174 	if (me->inUnderline == FALSE)
4175 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
4176 	HTML_put_string(me, "CAPTION:");
4177 	if (me->inUnderline == FALSE)
4178 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
4179 	HTML_put_character(me, ' ');
4180 	CAN_JUSTIFY_START;
4181 
4182 	if (me->inFIG)
4183 	    /*
4184 	     * Assume all text in the FIG container is intended to be
4185 	     * paragraphed.  - FM
4186 	     */
4187 	    me->inFIGwithP = TRUE;
4188 
4189 	if (me->inAPPLET)
4190 	    /*
4191 	     * Assume all text in the APPLET container is intended to be
4192 	     * paragraphed.  - FM
4193 	     */
4194 	    me->inAPPLETwithP = TRUE;
4195 
4196 	me->inLABEL = TRUE;
4197 	me->in_word = NO;
4198 	me->inP = FALSE;
4199 	break;
4200 
4201     case HTML_FORM:
4202 	{
4203 	    char *action = NULL;
4204 	    char *method = NULL;
4205 	    char *enctype = NULL;
4206 	    const char *accept_cs = NULL;
4207 
4208 	    HTChildAnchor *source;
4209 	    HTAnchor *link_dest;
4210 
4211 	    /*
4212 	     * FORM may have been declared SGML_EMPTY in HTMLDTD.c, and
4213 	     * SGML_character() in SGML.c may check for a FORM end tag to call
4214 	     * HTML_end_element() directly (with a check in that to bypass
4215 	     * decrementing of the HTML parser's stack), so if we have an open
4216 	     * FORM, close that one now.  - FM
4217 	     */
4218 	    if (me->inFORM) {
4219 		CTRACE((tfp, "HTML: Missing FORM end tag.  Faking it!\n"));
4220 		SET_SKIP_STACK(HTML_FORM);
4221 		HTML_end_element(me, HTML_FORM, include);
4222 	    }
4223 
4224 	    /*
4225 	     * Set to know we are in a new form.
4226 	     */
4227 	    me->inFORM = TRUE;
4228 	    EMIT_IFDEF_USE_JUSTIFY_ELTS(form_in_htext = TRUE);
4229 
4230 	    if (present && present[HTML_FORM_ACCEPT_CHARSET]) {
4231 		accept_cs = (value[HTML_FORM_ACCEPT_CHARSET]
4232 			     ? value[HTML_FORM_ACCEPT_CHARSET]
4233 			     : "UNKNOWN");
4234 	    }
4235 
4236 	    Base = (me->inBASE)
4237 		? me->base_href
4238 		: me->node_anchor->address;
4239 
4240 	    if (present && present[HTML_FORM_ACTION] &&
4241 		value[HTML_FORM_ACTION]) {
4242 
4243 		StrAllocCopy(action, value[HTML_FORM_ACTION]);
4244 		LYLegitimizeHREF(me, &action, TRUE, TRUE);
4245 
4246 		/*
4247 		 * Check whether a base tag is in effect.  Note that actions
4248 		 * always are resolved w.r.t.  to the base, even if the action
4249 		 * is empty.  - FM
4250 		 */
4251 		HTParseALL(&action, Base);
4252 
4253 	    } else {
4254 		StrAllocCopy(action, Base);
4255 	    }
4256 
4257 	    source = HTAnchor_findChildAndLink(me->node_anchor,
4258 					       NULL,
4259 					       action,
4260 					       (HTLinkType *) 0);
4261 	    if ((link_dest = HTAnchor_followLink(source)) != NULL) {
4262 		/*
4263 		 * Memory leak fixed.  05-28-94 Lynx 2-3-1 Garrett Arch Blythe
4264 		 */
4265 		char *cp_freeme = HTAnchor_address(link_dest);
4266 
4267 		if (cp_freeme != NULL) {
4268 		    StrAllocCopy(action, cp_freeme);
4269 		    FREE(cp_freeme);
4270 		} else {
4271 		    StrAllocCopy(action, "");
4272 		}
4273 	    }
4274 
4275 	    if (present && present[HTML_FORM_METHOD])
4276 		StrAllocCopy(method, (value[HTML_FORM_METHOD]
4277 				      ? value[HTML_FORM_METHOD]
4278 				      : "GET"));
4279 
4280 	    if (present && present[HTML_FORM_ENCTYPE] &&
4281 		non_empty(value[HTML_FORM_ENCTYPE])) {
4282 		StrAllocCopy(enctype, value[HTML_FORM_ENCTYPE]);
4283 		LYLowerCase(enctype);
4284 	    }
4285 
4286 	    if (present) {
4287 		/*
4288 		 * Check for a TITLE attribute, and if none is present, check
4289 		 * for a SUBJECT attribute as a synonym.  - FM
4290 		 */
4291 		if (present[HTML_FORM_TITLE] &&
4292 		    non_empty(value[HTML_FORM_TITLE])) {
4293 		    StrAllocCopy(title, value[HTML_FORM_TITLE]);
4294 		} else if (present[HTML_FORM_SUBJECT] &&
4295 			   non_empty(value[HTML_FORM_SUBJECT])) {
4296 		    StrAllocCopy(title, value[HTML_FORM_SUBJECT]);
4297 		}
4298 		if (non_empty(title)) {
4299 		    TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
4300 		    LYTrimHead(title);
4301 		    LYTrimTail(title);
4302 		    if (*title == '\0') {
4303 			FREE(title);
4304 		    }
4305 		}
4306 	    }
4307 
4308 	    HText_beginForm(action, method, enctype, title, accept_cs);
4309 
4310 	    FREE(action);
4311 	    FREE(method);
4312 	    FREE(enctype);
4313 	    FREE(title);
4314 	}
4315 	CHECK_ID(HTML_FORM_ID);
4316 	break;
4317 
4318     case HTML_FIELDSET:
4319 	LYEnsureDoubleSpace(me);
4320 	LYResetParagraphAlignment(me);
4321 	CHECK_ID(HTML_GEN_ID);
4322 	break;
4323 
4324     case HTML_LEGEND:
4325 	LYEnsureDoubleSpace(me);
4326 	LYResetParagraphAlignment(me);
4327 	CHECK_ID(HTML_CAPTION_ID);
4328 	break;
4329 
4330     case HTML_LABEL:
4331 	CHECK_ID(HTML_LABEL_ID);
4332 	break;
4333 
4334     case HTML_KEYGEN:
4335 	CHECK_ID(HTML_KEYGEN_ID);
4336 	break;
4337 
4338     case HTML_BUTTON:
4339 	{
4340 	    InputFieldData I;
4341 	    int chars;
4342 
4343 	    /* init */
4344 	    memset(&I, 0, sizeof(I));
4345 	    I.name_cs = ATTR_CS_IN;
4346 	    I.value_cs = ATTR_CS_IN;
4347 
4348 	    UPDATE_STYLE;
4349 	    if (present &&
4350 		present[HTML_BUTTON_TYPE] &&
4351 		value[HTML_BUTTON_TYPE]) {
4352 		if (!strcasecomp(value[HTML_BUTTON_TYPE], "submit") ||
4353 		    !strcasecomp(value[HTML_BUTTON_TYPE], "reset")) {
4354 		    /*
4355 		     * It's a button for submitting or resetting a form.  - FM
4356 		     */
4357 		    I.type = value[HTML_BUTTON_TYPE];
4358 		} else {
4359 		    /*
4360 		     * Ugh, it's a button for a script.  - FM
4361 		     */
4362 		    I.type = value[HTML_BUTTON_TYPE];
4363 		}
4364 	    } else {
4365 		/* default, if no type given, is a submit button */
4366 		I.type = "submit";
4367 	    }
4368 
4369 	    /*
4370 	     * Before any input field, add a collapsible space if we're not in
4371 	     * a PRE block, to promote a wrap there for any long values that
4372 	     * would extend past the right margin from our current position in
4373 	     * the line.  If we are in a PRE block, start a new line if the
4374 	     * last line already is within 6 characters of the wrap point for
4375 	     * PRE blocks.  - FM
4376 	     */
4377 	    if (me->sp[0].tag_number != HTML_PRE && !me->inPRE &&
4378 		me->sp->style->freeFormat) {
4379 		HTML_put_character(me, ' ');
4380 		me->in_word = NO;
4381 	    } else if (HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 6)) {
4382 		HTML_put_character(me, '\n');
4383 		me->in_word = NO;
4384 	    }
4385 	    HTML_put_character(me, '(');
4386 
4387 	    if (!(present && present[HTML_BUTTON_NAME] &&
4388 		  value[HTML_BUTTON_NAME])) {
4389 		I.name = "";
4390 	    } else if (strchr(value[HTML_BUTTON_NAME], '&') == NULL) {
4391 		I.name = value[HTML_BUTTON_NAME];
4392 	    } else {
4393 		StrAllocCopy(I_name, value[HTML_BUTTON_NAME]);
4394 		UNESCAPE_FIELDNAME_TO_STD(&I_name);
4395 		I.name = I_name;
4396 	    }
4397 
4398 	    if (present && present[HTML_BUTTON_VALUE] &&
4399 		non_empty(value[HTML_BUTTON_VALUE])) {
4400 		/*
4401 		 * Convert any HTML entities or decimal escaping.  - FM
4402 		 */
4403 		StrAllocCopy(I_value, value[HTML_BUTTON_VALUE]);
4404 		me->UsePlainSpace = TRUE;
4405 		TRANSLATE_AND_UNESCAPE_ENTITIES(&I_value, TRUE, me->HiddenValue);
4406 		me->UsePlainSpace = FALSE;
4407 		I.value = I_value;
4408 		/*
4409 		 * Convert any newlines or tabs to spaces, and trim any lead or
4410 		 * trailing spaces.  - FM
4411 		 */
4412 		LYReduceBlanks(I.value);
4413 	    } else if (!strcasecomp(I.type, "button")) {
4414 		if (non_empty(I.name)) {
4415 		    StrAllocCopy(I.value, I.name);
4416 		} else {
4417 		    StrAllocCopy(I.value, "BUTTON");
4418 		}
4419 	    } else if (I.value == 0) {
4420 		StrAllocCopy(I.value, "BUTTON");
4421 	    }
4422 
4423 	    if (present && present[HTML_BUTTON_READONLY])
4424 		I.readonly = YES;
4425 
4426 	    if (present && present[HTML_BUTTON_DISABLED])
4427 		I.disabled = YES;
4428 
4429 	    if (present && present[HTML_BUTTON_CLASS] &&	/* Not yet used. */
4430 		non_empty(value[HTML_BUTTON_CLASS]))
4431 		I.iclass = value[HTML_BUTTON_CLASS];
4432 
4433 	    if (present && present[HTML_BUTTON_ID] &&
4434 		non_empty(value[HTML_BUTTON_ID])) {
4435 		I.id = value[HTML_BUTTON_ID];
4436 		CHECK_ID(HTML_BUTTON_ID);
4437 	    }
4438 
4439 	    if (present && present[HTML_BUTTON_LANG] &&		/* Not yet used. */
4440 		non_empty(value[HTML_BUTTON_LANG]))
4441 		I.lang = value[HTML_BUTTON_LANG];
4442 
4443 	    chars = HText_beginInput(me->text, me->inUnderline, &I);
4444 	    /*
4445 	     * Submit and reset buttons have values which don't change, so
4446 	     * HText_beginInput() sets I.value to the string which should be
4447 	     * displayed, and we'll enter that instead of underscore
4448 	     * placeholders into the HText structure to see it instead of
4449 	     * underscores when dumping or printing.  We also won't worry about
4450 	     * a wrap in PRE blocks, because the line editor never is invoked
4451 	     * for submit or reset buttons.  - LE & FM
4452 	     */
4453 	    if (me->sp[0].tag_number == HTML_PRE ||
4454 		!me->sp->style->freeFormat) {
4455 		/*
4456 		 * We have a submit or reset button in a PRE block, so output
4457 		 * the entire value from the markup.  If it extends to the
4458 		 * right margin, it will wrap there, and only the portion
4459 		 * before that wrap will be hightlighted on screen display
4460 		 * (Yuk!) but we may as well show the rest of the full value on
4461 		 * the next or more lines.  - FM
4462 		 */
4463 		while (I.value[i])
4464 		    HTML_put_character(me, I.value[i++]);
4465 	    } else {
4466 		/*
4467 		 * The submit or reset button is not in a PRE block.  Note that
4468 		 * if a wrap occurs before outputting the entire value, the
4469 		 * wrapped portion will not be highlighted or clearly indicated
4470 		 * as part of the link for submission or reset (Yuk!).  We'll
4471 		 * replace any spaces in the submit or reset button value with
4472 		 * nbsp, to promote a wrap at the space we ensured would be
4473 		 * present before the start of the string, as when we use all
4474 		 * underscores instead of the INPUT's actual value, but we
4475 		 * could still get a wrap at the right margin, instead, if the
4476 		 * value is greater than a line width for the current style.
4477 		 * Also, if chars somehow ended up longer than the length of
4478 		 * the actual value (shouldn't have), we'll continue padding
4479 		 * with nbsp up to the length of chars.  - FM
4480 		 */
4481 		for (i = 0; I.value[i]; i++) {
4482 		    HTML_put_character(me,
4483 				       (char) ((I.value[i] == ' ')
4484 					       ? HT_NON_BREAK_SPACE
4485 					       : I.value[i]));
4486 		}
4487 		while (i++ < chars) {
4488 		    HTML_put_character(me, HT_NON_BREAK_SPACE);
4489 		}
4490 	    }
4491 	    HTML_put_character(me, ')');
4492 	    if (me->sp[0].tag_number != HTML_PRE &&
4493 		me->sp->style->freeFormat) {
4494 		HTML_put_character(me, ' ');
4495 		me->in_word = NO;
4496 	    }
4497 	    FREE(I_value);
4498 	    FREE(I_name);
4499 	}
4500 	break;
4501 
4502     case HTML_INPUT:
4503 	{
4504 	    InputFieldData I;
4505 	    int chars;
4506 	    BOOL UseALTasVALUE = FALSE;
4507 	    BOOL HaveSRClink = FALSE;
4508 	    char *ImageSrc = NULL;
4509 	    BOOL IsSubmitOrReset = FALSE;
4510 	    HTkcode kcode = NOKANJI;
4511 	    HTkcode specified_kcode = NOKANJI;
4512 
4513 	    /* init */
4514 	    memset(&I, 0, sizeof(I));
4515 	    I.name_cs = ATTR_CS_IN;
4516 	    I.value_cs = ATTR_CS_IN;
4517 
4518 	    UPDATE_STYLE;
4519 
4520 	    /*
4521 	     * Before any input field, add a collapsible space if we're not in
4522 	     * a PRE block, to promote a wrap there for any long values that
4523 	     * would extend past the right margin from our current position in
4524 	     * the line.  If we are in a PRE block, start a new line if the
4525 	     * last line already is within 6 characters of the wrap point for
4526 	     * PRE blocks.  - FM
4527 	     */
4528 	    if (me->sp[0].tag_number != HTML_PRE && !me->inPRE &&
4529 		me->sp->style->freeFormat) {
4530 		HTML_put_character(me, ' ');
4531 		me->in_word = NO;
4532 	    } else if (HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 6)) {
4533 		HTML_put_character(me, '\n');
4534 		me->in_word = NO;
4535 	    }
4536 
4537 	    /*
4538 	     * Get the TYPE and make sure we can handle it.  - FM
4539 	     */
4540 	    if (present && present[HTML_INPUT_TYPE] &&
4541 		non_empty(value[HTML_INPUT_TYPE])) {
4542 		const char *not_impl = NULL;
4543 		char *usingval = NULL;
4544 
4545 		I.type = value[HTML_INPUT_TYPE];
4546 
4547 		if (!strcasecomp(I.type, "range")) {
4548 		    if (present[HTML_INPUT_MIN] &&
4549 			non_empty(value[HTML_INPUT_MIN]))
4550 			I.min = value[HTML_INPUT_MIN];
4551 		    if (present[HTML_INPUT_MAX] &&
4552 			non_empty(value[HTML_INPUT_MAX]))
4553 			I.max = value[HTML_INPUT_MAX];
4554 		    /*
4555 		     * Not yet implemented.
4556 		     */
4557 #ifdef NOTDEFINED
4558 		    not_impl = "[RANGE Input]";
4559 		    if (me->inFORM)
4560 			HText_DisableCurrentForm();
4561 #endif /* NOTDEFINED */
4562 		    CTRACE((tfp, "HTML: Ignoring TYPE=\"range\"\n"));
4563 		    break;
4564 
4565 		} else if (!strcasecomp(I.type, "file")) {
4566 		    if (present[HTML_INPUT_ACCEPT] &&
4567 			non_empty(value[HTML_INPUT_ACCEPT]))
4568 			I.accept = value[HTML_INPUT_ACCEPT];
4569 #ifndef USE_FILE_UPLOAD
4570 		    not_impl = "[FILE Input]";
4571 		    CTRACE((tfp, "Attempting to fake as: %s\n", I.type));
4572 #ifdef NOTDEFINED
4573 		    if (me->inFORM)
4574 			HText_DisableCurrentForm();
4575 #endif /* NOTDEFINED */
4576 		    CTRACE((tfp, "HTML: Ignoring TYPE=\"file\"\n"));
4577 #endif /* USE_FILE_UPLOAD */
4578 
4579 		} else if (!strcasecomp(I.type, "button")) {
4580 		    /*
4581 		     * Ugh, a button for a script.
4582 		     */
4583 		    not_impl = "[BUTTON Input]";
4584 		}
4585 		if (not_impl != NULL) {
4586 		    if (me->inUnderline == FALSE) {
4587 			HText_appendCharacter(me->text,
4588 					      LY_UNDERLINE_START_CHAR);
4589 		    }
4590 		    HTML_put_string(me, not_impl);
4591 		    if (usingval != NULL) {
4592 			HTML_put_string(me, usingval);
4593 			FREE(usingval);
4594 		    } else {
4595 			HTML_put_string(me, " (not implemented)");
4596 		    }
4597 		    if (me->inUnderline == FALSE) {
4598 			HText_appendCharacter(me->text,
4599 					      LY_UNDERLINE_END_CHAR);
4600 		    }
4601 		}
4602 	    }
4603 
4604 	    CTRACE((tfp, "Ok, we're trying type=[%s]\n", NONNULL(I.type)));
4605 
4606 	    /*
4607 	     * Check for an unclosed TEXTAREA.
4608 	     */
4609 	    if (me->inTEXTAREA) {
4610 		if (LYBadHTML(me)) {
4611 		    LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag.\n");
4612 		}
4613 	    }
4614 
4615 	    /*
4616 	     * Check for an unclosed SELECT, try to close it if found.
4617 	     */
4618 	    if (me->inSELECT) {
4619 		CTRACE((tfp, "HTML: Missing SELECT end tag, faking it...\n"));
4620 		if (me->sp->tag_number != HTML_SELECT) {
4621 		    SET_SKIP_STACK(HTML_SELECT);
4622 		}
4623 		HTML_end_element(me, HTML_SELECT, include);
4624 	    }
4625 
4626 	    /*
4627 	     * Handle the INPUT as for a FORM.  - FM
4628 	     */
4629 	    if (!(present && present[HTML_INPUT_NAME] &&
4630 		  non_empty(value[HTML_INPUT_NAME]))) {
4631 		I.name = "";
4632 	    } else if (strchr(value[HTML_INPUT_NAME], '&') == NULL) {
4633 		I.name = value[HTML_INPUT_NAME];
4634 	    } else {
4635 		StrAllocCopy(I_name, value[HTML_INPUT_NAME]);
4636 		UNESCAPE_FIELDNAME_TO_STD(&I_name);
4637 		I.name = I_name;
4638 	    }
4639 
4640 	    if ((present && present[HTML_INPUT_ALT] &&
4641 		 non_empty(value[HTML_INPUT_ALT]) &&
4642 		 I.type && !strcasecomp(I.type, "image")) &&
4643 		!(present && present[HTML_INPUT_VALUE] &&
4644 		  non_empty(value[HTML_INPUT_VALUE]))) {
4645 		/*
4646 		 * This is a TYPE="image" using an ALT rather than VALUE
4647 		 * attribute to indicate the link string for text clients or
4648 		 * GUIs with image loading off, so set the flag to use that as
4649 		 * if it were a VALUE attribute.  - FM
4650 		 */
4651 		UseALTasVALUE = TRUE;
4652 	    }
4653 	    if (verbose_img && !clickable_images &&
4654 		present && present[HTML_INPUT_SRC] &&
4655 		non_empty(value[HTML_INPUT_SRC]) &&
4656 		I.type && !strcasecomp(I.type, "image")) {
4657 		ImageSrc = MakeNewImageValue(value);
4658 	    } else if (clickable_images == TRUE &&
4659 		       present && present[HTML_INPUT_SRC] &&
4660 		       non_empty(value[HTML_INPUT_SRC]) &&
4661 		       I.type && !strcasecomp(I.type, "image")) {
4662 		StrAllocCopy(href, value[HTML_INPUT_SRC]);
4663 		/*
4664 		 * We have a TYPE="image" with a non-zero-length SRC attribute
4665 		 * and want clickable images.  Make the SRC's value a link if
4666 		 * it's still not zero-length legitimizing it.  - FM
4667 		 */
4668 		LYLegitimizeHREF(me, &href, TRUE, TRUE);
4669 		if (*href) {
4670 
4671 		    if (me->inA) {
4672 			SET_SKIP_STACK(HTML_A);
4673 			HTML_end_element(me, HTML_A, include);
4674 		    }
4675 		    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
4676 							     NULL,	/* Tag */
4677 							     href,	/* Addresss */
4678 							     (HTLinkType *) 0);		/* Type */
4679 		    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
4680 		    if (me->inBoldH == FALSE)
4681 			HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
4682 		    HTML_put_string(me, VERBOSE_IMG(value,
4683 						    HTML_INPUT_SRC,
4684 						    "[IMAGE]"));
4685 		    FREE(newtitle);
4686 		    if (me->inBoldH == FALSE)
4687 			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
4688 		    HText_endAnchor(me->text, 0);
4689 		    HTML_put_character(me, '-');
4690 		    HaveSRClink = TRUE;
4691 		}
4692 		FREE(href);
4693 	    }
4694 	    CTRACE((tfp, "2.Ok, we're trying type=[%s] (present=%p)\n",
4695 		    NONNULL(I.type),
4696 		    present));
4697 	    /* text+file don't go in here */
4698 	    if ((UseALTasVALUE == TRUE) ||
4699 		(present && present[HTML_INPUT_VALUE] &&
4700 		 value[HTML_INPUT_VALUE] &&
4701 		 (*value[HTML_INPUT_VALUE] ||
4702 		  (I.type && (!strcasecomp(I.type, "checkbox") ||
4703 			      !strcasecomp(I.type, "radio")))))) {
4704 
4705 		/*
4706 		 * Convert any HTML entities or decimal escaping.  - FM
4707 		 */
4708 		int CurrentCharSet = current_char_set;
4709 		BOOL CurrentEightBitRaw = HTPassEightBitRaw;
4710 		BOOLEAN CurrentUseDefaultRawMode = LYUseDefaultRawMode;
4711 		HTCJKlang CurrentHTCJK = HTCJK;
4712 
4713 		if (I.type && !strcasecomp(I.type, "hidden")) {
4714 		    me->HiddenValue = TRUE;
4715 		    current_char_set = LATIN1;	/* Default ISO-Latin1 */
4716 		    LYUseDefaultRawMode = TRUE;
4717 		    HTMLSetCharacterHandling(current_char_set);
4718 		}
4719 
4720 		CTRACE((tfp, "3.Ok, we're trying type=[%s]\n", NONNULL(I.type)));
4721 		if (!I.type)
4722 		    me->UsePlainSpace = TRUE;
4723 		else if (!strcasecomp(I.type, "text") ||
4724 #ifdef USE_FILE_UPLOAD
4725 			 !strcasecomp(I.type, "file") ||
4726 #endif
4727 			 !strcasecomp(I.type, "submit") ||
4728 			 !strcasecomp(I.type, "image") ||
4729 			 !strcasecomp(I.type, "reset")) {
4730 		    CTRACE((tfp, "normal field type: %s\n", NONNULL(I.type)));
4731 		    me->UsePlainSpace = TRUE;
4732 		}
4733 
4734 		StrAllocCopy(I_value,
4735 			     ((UseALTasVALUE == TRUE)
4736 			      ? value[HTML_INPUT_ALT]
4737 			      : value[HTML_INPUT_VALUE]));
4738 		if (me->UsePlainSpace && !me->HiddenValue) {
4739 		    I.value_cs = current_char_set;
4740 		}
4741 		CTRACE((tfp, "4.Ok, we're trying type=[%s]\n", NONNULL(I.type)));
4742 		TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value,
4743 						 ATTR_CS_IN,
4744 						 I.value_cs,
4745 						 (BOOL) (me->UsePlainSpace &&
4746 							 !me->HiddenValue),
4747 						 me->UsePlainSpace,
4748 						 me->HiddenValue);
4749 		I.value = I_value;
4750 		if (me->UsePlainSpace == TRUE) {
4751 		    /*
4752 		     * Convert any newlines or tabs to spaces, and trim any
4753 		     * lead or trailing spaces.  - FM
4754 		     */
4755 		    LYReduceBlanks(I.value);
4756 		}
4757 		me->UsePlainSpace = FALSE;
4758 
4759 		if (I.type && !strcasecomp(I.type, "hidden")) {
4760 		    me->HiddenValue = FALSE;
4761 		    current_char_set = CurrentCharSet;
4762 		    LYUseDefaultRawMode = CurrentUseDefaultRawMode;
4763 		    HTMLSetCharacterHandling(current_char_set);
4764 		    HTPassEightBitRaw = CurrentEightBitRaw;
4765 		    HTCJK = CurrentHTCJK;
4766 		}
4767 	    } else if (HaveSRClink == TRUE) {
4768 		/*
4769 		 * We put up an [IMAGE] link and '-' for a TYPE="image" and
4770 		 * didn't get a VALUE or ALT string, so fake a "Submit" value.
4771 		 * If we didn't put up a link, then HText_beginInput() will use
4772 		 * "[IMAGE]-Submit".  - FM
4773 		 */
4774 		StrAllocCopy(I_value, "Submit");
4775 		I.value = I_value;
4776 	    } else if (ImageSrc) {
4777 		/* [IMAGE]-Submit with verbose images and not clickable images.
4778 		 * Use ImageSrc if no other alt or value is supplied. --LE
4779 		 */
4780 		I.value = ImageSrc;
4781 	    }
4782 #if 0
4783 	    if (present && present[HTML_INPUT_READONLY])
4784 		I.readonly = YES;
4785 #endif
4786 	    if (present && present[HTML_INPUT_CHECKED])
4787 		I.checked = YES;
4788 	    if (present && present[HTML_INPUT_SIZE] &&
4789 		non_empty(value[HTML_INPUT_SIZE]))
4790 		I.size = atoi(value[HTML_INPUT_SIZE]);
4791 	    LimitValue(I.size, MAX_LINE);
4792 	    if (present && present[HTML_INPUT_MAXLENGTH] &&
4793 		non_empty(value[HTML_INPUT_MAXLENGTH]))
4794 		I.maxlength = value[HTML_INPUT_MAXLENGTH];
4795 	    if (present && present[HTML_INPUT_DISABLED])
4796 		I.disabled = YES;
4797 
4798 	    if (present && present[HTML_INPUT_ACCEPT_CHARSET]) {	/* Not yet used. */
4799 		I.accept_cs = (value[HTML_INPUT_ACCEPT_CHARSET]
4800 			       ? value[HTML_INPUT_ACCEPT_CHARSET]
4801 			       : "UNKNOWN");
4802 	    }
4803 	    if (present && present[HTML_INPUT_ALIGN] &&		/* Not yet used. */
4804 		non_empty(value[HTML_INPUT_ALIGN]))
4805 		I.align = value[HTML_INPUT_ALIGN];
4806 	    if (present && present[HTML_INPUT_CLASS] &&		/* Not yet used. */
4807 		non_empty(value[HTML_INPUT_CLASS]))
4808 		I.iclass = value[HTML_INPUT_CLASS];
4809 	    if (present && present[HTML_INPUT_ERROR] &&		/* Not yet used. */
4810 		non_empty(value[HTML_INPUT_ERROR]))
4811 		I.error = value[HTML_INPUT_ERROR];
4812 	    if (present && present[HTML_INPUT_HEIGHT] &&	/* Not yet used. */
4813 		non_empty(value[HTML_INPUT_HEIGHT]))
4814 		I.height = value[HTML_INPUT_HEIGHT];
4815 	    if (present && present[HTML_INPUT_WIDTH] &&		/* Not yet used. */
4816 		non_empty(value[HTML_INPUT_WIDTH]))
4817 		I.width = value[HTML_INPUT_WIDTH];
4818 	    if (present && present[HTML_INPUT_ID] &&
4819 		non_empty(value[HTML_INPUT_ID])) {
4820 		I.id = value[HTML_INPUT_ID];
4821 		CHECK_ID(HTML_INPUT_ID);
4822 	    }
4823 	    if (present && present[HTML_INPUT_LANG] &&	/* Not yet used. */
4824 		non_empty(value[HTML_INPUT_LANG]))
4825 		I.lang = value[HTML_INPUT_LANG];
4826 	    if (present && present[HTML_INPUT_MD] &&	/* Not yet used. */
4827 		non_empty(value[HTML_INPUT_MD]))
4828 		I.md = value[HTML_INPUT_MD];
4829 
4830 	    chars = HText_beginInput(me->text, me->inUnderline, &I);
4831 	    CTRACE((tfp,
4832 		    "I.%s have %d chars, or something\n",
4833 		    NONNULL(I.type),
4834 		    chars));
4835 	    /*
4836 	     * Submit and reset buttons have values which don't change, so
4837 	     * HText_beginInput() sets I.value to the string which should be
4838 	     * displayed, and we'll enter that instead of underscore
4839 	     * placeholders into the HText structure to see it instead of
4840 	     * underscores when dumping or printing.  We also won't worry about
4841 	     * a wrap in PRE blocks, because the line editor never is invoked
4842 	     * for submit or reset buttons.  - LE & FM
4843 	     */
4844 	    if (I.type &&
4845 		(!strcasecomp(I.type, "submit") ||
4846 		 !strcasecomp(I.type, "reset") ||
4847 		 !strcasecomp(I.type, "image")))
4848 		IsSubmitOrReset = TRUE;
4849 
4850 	    if (I.type && chars == 3 &&
4851 		!strcasecomp(I.type, "radio")) {
4852 		/*
4853 		 * Put a (_) placeholder, and one space (collapsible) before
4854 		 * the label that is expected to follow.  - FM
4855 		 */
4856 		HTML_put_string(me, "(_)");
4857 		HText_endInput(me->text);
4858 		chars = 0;
4859 		me->in_word = YES;
4860 		if (me->sp[0].tag_number != HTML_PRE &&
4861 		    me->sp->style->freeFormat) {
4862 		    HTML_put_character(me, ' ');
4863 		    me->in_word = NO;
4864 		}
4865 	    } else if (I.type && chars == 3 &&
4866 		       !strcasecomp(I.type, "checkbox")) {
4867 		/*
4868 		 * Put a [_] placeholder, and one space (collapsible) before
4869 		 * the label that is expected to follow.  - FM
4870 		 */
4871 		HTML_put_string(me, "[_]");
4872 		HText_endInput(me->text);
4873 		chars = 0;
4874 		me->in_word = YES;
4875 		if (me->sp[0].tag_number != HTML_PRE &&
4876 		    me->sp->style->freeFormat) {
4877 		    HTML_put_character(me, ' ');
4878 		    me->in_word = NO;
4879 		}
4880 	    } else if ((me->sp[0].tag_number == HTML_PRE ||
4881 			!me->sp->style->freeFormat)
4882 		       && chars > 6 &&
4883 		       IsSubmitOrReset == FALSE) {
4884 		/*
4885 		 * This is not a submit or reset button, and we are in a PRE
4886 		 * block with a field intended to exceed 6 character widths.
4887 		 * The code inadequately handles INPUT fields in PRE tags if
4888 		 * wraps occur (at the right margin) for the underscore
4889 		 * placeholders.  We'll put up a minimum of 6 underscores,
4890 		 * since we should have wrapped artificially, above, if the
4891 		 * INPUT begins within 6 columns of the right margin, and if
4892 		 * any more would exceed the wrap column, we'll ignore them.
4893 		 * Note that if we somehow get tripped up and a wrap still does
4894 		 * occur before all 6 of the underscores are output, the
4895 		 * wrapped ones won't be treated as part of the editing window,
4896 		 * nor be highlighted when not editing (Yuk!).  - FM
4897 		 */
4898 		for (i = 0; i < 6; i++) {
4899 		    HTML_put_character(me, '_');
4900 		    chars--;
4901 		}
4902 	    }
4903 	    CTRACE((tfp, "I.%s, %d\n", NONNULL(I.type), IsSubmitOrReset));
4904 	    if (IsSubmitOrReset == FALSE) {
4905 		/*
4906 		 * This is not a submit or reset button, so output the rest of
4907 		 * the underscore placeholders, if any more are needed.  - FM
4908 		 */
4909 		if (chars > 0) {
4910 		    for (; chars > 0; chars--)
4911 			HTML_put_character(me, '_');
4912 		    HText_endInput(me->text);
4913 		}
4914 	    } else {
4915 		if (HTCJK == JAPANESE) {
4916 		    kcode = HText_getKcode(me->text);
4917 		    HText_updateKcode(me->text, kanji_code);
4918 		    specified_kcode = HText_getSpecifiedKcode(me->text);
4919 		    HText_updateSpecifiedKcode(me->text, kanji_code);
4920 		}
4921 		if (me->sp[0].tag_number == HTML_PRE ||
4922 		    !me->sp->style->freeFormat) {
4923 		    /*
4924 		     * We have a submit or reset button in a PRE block, so
4925 		     * output the entire value from the markup.  If it extends
4926 		     * to the right margin, it will wrap there, and only the
4927 		     * portion before that wrap will be hightlighted on screen
4928 		     * display (Yuk!) but we may as well show the rest of the
4929 		     * full value on the next or more lines.  - FM
4930 		     */
4931 		    while (I.value[i])
4932 			HTML_put_character(me, I.value[i++]);
4933 		} else {
4934 		    /*
4935 		     * The submit or reset button is not in a PRE block.  Note
4936 		     * that if a wrap occurs before outputting the entire
4937 		     * value, the wrapped portion will not be highlighted or
4938 		     * clearly indicated as part of the link for submission or
4939 		     * reset (Yuk!).  We'll replace any spaces in the submit or
4940 		     * reset button value with nbsp, to promote a wrap at the
4941 		     * space we ensured would be present before the start of
4942 		     * the string, as when we use all underscores instead of
4943 		     * the INPUT's actual value, but we could still get a wrap
4944 		     * at the right margin, instead, if the value is greater
4945 		     * than a line width for the current style.  Also, if chars
4946 		     * somehow ended up longer than the length of the actual
4947 		     * value (shouldn't have), we'll continue padding with nbsp
4948 		     * up to the length of chars.  - FM
4949 		     */
4950 		    for (i = 0; I.value[i]; i++)
4951 			HTML_put_character(me,
4952 					   (char) (I.value[i] == ' '
4953 						   ? HT_NON_BREAK_SPACE
4954 						   : I.value[i]));
4955 		    while (i++ < chars)
4956 			HTML_put_character(me, HT_NON_BREAK_SPACE);
4957 		}
4958 		if (HTCJK == JAPANESE) {
4959 		    HText_updateKcode(me->text, kcode);
4960 		    HText_updateSpecifiedKcode(me->text, specified_kcode);
4961 		}
4962 	    }
4963 	    if (chars != 0) {
4964 		HText_endInput(me->text);
4965 	    }
4966 	    FREE(ImageSrc);
4967 	    FREE(I_value);
4968 	    FREE(I_name);
4969 	}
4970 	break;
4971 
4972     case HTML_TEXTAREA:
4973 	/*
4974 	 * Set to know we are in a textarea.
4975 	 */
4976 	me->inTEXTAREA = TRUE;
4977 
4978 	/*
4979 	 * Get ready for the value.
4980 	 */
4981 	HTChunkClear(&me->textarea);
4982 	if (present && present[HTML_TEXTAREA_NAME] &&
4983 	    value[HTML_TEXTAREA_NAME]) {
4984 	    StrAllocCopy(me->textarea_name, value[HTML_TEXTAREA_NAME]);
4985 	    me->textarea_name_cs = ATTR_CS_IN;
4986 	    if (strchr(value[HTML_TEXTAREA_NAME], '&') != NULL) {
4987 		UNESCAPE_FIELDNAME_TO_STD(&me->textarea_name);
4988 	    }
4989 	} else {
4990 	    StrAllocCopy(me->textarea_name, "");
4991 	}
4992 
4993 	if (present && present[HTML_TEXTAREA_ACCEPT_CHARSET]) {
4994 	    if (value[HTML_TEXTAREA_ACCEPT_CHARSET]) {
4995 		StrAllocCopy(me->textarea_accept_cs, value[HTML_TEXTAREA_ACCEPT_CHARSET]);
4996 		TRANSLATE_AND_UNESCAPE_TO_STD(&me->textarea_accept_cs);
4997 	    } else {
4998 		StrAllocCopy(me->textarea_accept_cs, "UNKNOWN");
4999 	    }
5000 	} else {
5001 	    FREE(me->textarea_accept_cs);
5002 	}
5003 
5004 	if (present && present[HTML_TEXTAREA_COLS] &&
5005 	    value[HTML_TEXTAREA_COLS] &&
5006 	    isdigit(UCH(*value[HTML_TEXTAREA_COLS]))) {
5007 	    me->textarea_cols = atoi(value[HTML_TEXTAREA_COLS]);
5008 	} else {
5009 	    int width;
5010 
5011 	    width = LYcolLimit -
5012 		me->new_style->leftIndent - me->new_style->rightIndent;
5013 	    if (dump_output_immediately)	/* don't waste too much for this */
5014 		width = HTMIN(width, DFT_TEXTAREA_COLS);
5015 	    if (width > 1 && (width - 1) * 6 < MAX_LINE - 3 -
5016 		me->new_style->leftIndent - me->new_style->rightIndent)
5017 		me->textarea_cols = width;
5018 	    else
5019 		me->textarea_cols = DFT_TEXTAREA_COLS;
5020 	}
5021 	LimitValue(me->textarea_cols, MAX_TEXTAREA_COLS);
5022 
5023 	if (present && present[HTML_TEXTAREA_ROWS] &&
5024 	    value[HTML_TEXTAREA_ROWS] &&
5025 	    isdigit(UCH(*value[HTML_TEXTAREA_ROWS]))) {
5026 	    me->textarea_rows = atoi(value[HTML_TEXTAREA_ROWS]);
5027 	} else {
5028 	    me->textarea_rows = DFT_TEXTAREA_ROWS;
5029 	}
5030 	LimitValue(me->textarea_rows, MAX_TEXTAREA_ROWS);
5031 
5032 	/*
5033 	 * Lynx treats disabled and readonly textareas the same -
5034 	 * unmodifiable in either case.
5035 	 */
5036 	me->textarea_readonly = NO;
5037 	if (present && present[HTML_TEXTAREA_READONLY])
5038 	    me->textarea_readonly = YES;
5039 
5040 	me->textarea_disabled = NO;
5041 	if (present && present[HTML_TEXTAREA_DISABLED])
5042 	    me->textarea_disabled = YES;
5043 
5044 	if (present && present[HTML_TEXTAREA_ID]
5045 	    && non_empty(value[HTML_TEXTAREA_ID])) {
5046 	    StrAllocCopy(id_string, value[HTML_TEXTAREA_ID]);
5047 	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
5048 	    if ((id_string != '\0') &&
5049 		(ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
5050 						  id_string,	/* Tag */
5051 						  NULL,		/* Addresss */
5052 						  (HTLinkType *) 0))) {		/* Type */
5053 		HText_beginAnchor(me->text, me->inUnderline, ID_A);
5054 		HText_endAnchor(me->text, 0);
5055 		StrAllocCopy(me->textarea_id, id_string);
5056 	    } else {
5057 		FREE(me->textarea_id);
5058 	    }
5059 	    FREE(id_string);
5060 	} else {
5061 	    FREE(me->textarea_id);
5062 	}
5063 	break;
5064 
5065     case HTML_SELECT:
5066 	/*
5067 	 * Check for an already open SELECT block.  - FM
5068 	 */
5069 	if (me->inSELECT) {
5070 	    if (LYBadHTML(me)) {
5071 		LYShowBadHTML("Bad HTML: SELECT start tag in SELECT element.  Faking SELECT end tag. *****\n");
5072 	    }
5073 	    if (me->sp->tag_number != HTML_SELECT) {
5074 		SET_SKIP_STACK(HTML_SELECT);
5075 	    }
5076 	    HTML_end_element(me, HTML_SELECT, include);
5077 	}
5078 
5079 	/*
5080 	 * Start a new SELECT block. - FM
5081 	 */
5082 	LYHandleSELECT(me,
5083 		       present, (STRING2PTR) value,
5084 		       include,
5085 		       TRUE);
5086 	break;
5087 
5088     case HTML_OPTION:
5089 	{
5090 	    /*
5091 	     * An option is a special case of an input field.
5092 	     */
5093 	    InputFieldData I;
5094 
5095 	    /*
5096 	     * Make sure we're in a select tag.
5097 	     */
5098 	    if (!me->inSELECT) {
5099 		if (LYBadHTML(me)) {
5100 		    LYShowBadHTML("Bad HTML: OPTION tag not within SELECT tag\n");
5101 		}
5102 
5103 		/*
5104 		 * Too likely to cause a crash, so we'll ignore it.  - FM
5105 		 */
5106 		break;
5107 	    }
5108 
5109 	    if (!me->first_option) {
5110 		/*
5111 		 * Finish the data off.
5112 		 */
5113 		HTChunkTerminate(&me->option);
5114 
5115 		/*
5116 		 * Finish the previous option @@@@@
5117 		 */
5118 		HText_setLastOptionValue(me->text,
5119 					 me->option.data,
5120 					 me->LastOptionValue,
5121 					 MIDDLE_ORDER,
5122 					 me->LastOptionChecked,
5123 					 me->UCLYhndl,
5124 					 ATTR_CS_IN);
5125 	    }
5126 
5127 	    /*
5128 	     * If it's not a multiple option list and select popups are
5129 	     * enabled, then don't use the checkbox/button method, and don't
5130 	     * put anything on the screen yet.
5131 	     */
5132 	    if (me->first_option ||
5133 		HTCurSelectGroupType == F_CHECKBOX_TYPE ||
5134 		LYSelectPopups == FALSE) {
5135 		if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
5136 		    LYSelectPopups == FALSE) {
5137 		    /*
5138 		     * Start a newline before each option.
5139 		     */
5140 		    LYEnsureSingleSpace(me);
5141 		} else {
5142 		    /*
5143 		     * Add option list designation character.
5144 		     */
5145 		    HText_appendCharacter(me->text, '[');
5146 		    me->in_word = YES;
5147 		}
5148 
5149 		/*
5150 		 * Inititialize.
5151 		 */
5152 		memset(&I, 0, sizeof(I));
5153 		I.name_cs = -1;
5154 		I.value_cs = current_char_set;
5155 
5156 		I.type = "OPTION";
5157 
5158 		if ((present && present[HTML_OPTION_SELECTED]) ||
5159 		    (me->first_option && LYSelectPopups == FALSE &&
5160 		     HTCurSelectGroupType == F_RADIO_TYPE))
5161 		    I.checked = YES;
5162 
5163 		if (present && present[HTML_OPTION_VALUE] &&
5164 		    value[HTML_OPTION_VALUE]) {
5165 		    /*
5166 		     * Convert any HTML entities or decimal escaping.  - FM
5167 		     */
5168 		    StrAllocCopy(I_value, value[HTML_OPTION_VALUE]);
5169 		    me->HiddenValue = TRUE;
5170 		    TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value,
5171 						     ATTR_CS_IN,
5172 						     ATTR_CS_IN,
5173 						     NO,
5174 						     me->UsePlainSpace, me->HiddenValue);
5175 		    I.value_cs = ATTR_CS_IN;
5176 		    me->HiddenValue = FALSE;
5177 
5178 		    I.value = I_value;
5179 		}
5180 
5181 		if (me->select_disabled ||
5182 		    (0 && present && present[HTML_OPTION_DISABLED])) {
5183 		    /* 2009/5/25 - suppress check for "disabled" attribute
5184 		     * for Debian #525934 -TD
5185 		     */
5186 		    I.disabled = YES;
5187 		}
5188 
5189 		if (present && present[HTML_OPTION_ID]
5190 		    && non_empty(value[HTML_OPTION_ID])) {
5191 		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
5192 							  value[HTML_OPTION_ID],	/* Tag */
5193 							  NULL,		/* Addresss */
5194 							  0)) != NULL) {	/* Type */
5195 			HText_beginAnchor(me->text, me->inUnderline, ID_A);
5196 			HText_endAnchor(me->text, 0);
5197 			I.id = value[HTML_OPTION_ID];
5198 		    }
5199 		}
5200 
5201 		HText_beginInput(me->text, me->inUnderline, &I);
5202 
5203 		if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
5204 		    /*
5205 		     * Put a "[_]" placeholder, and one space (collapsible)
5206 		     * before the label that is expected to follow.  - FM
5207 		     */
5208 		    HText_appendCharacter(me->text, '[');
5209 		    HText_appendCharacter(me->text, '_');
5210 		    HText_appendCharacter(me->text, ']');
5211 		    HText_appendCharacter(me->text, ' ');
5212 		    HText_setLastChar(me->text, ' ');	/* absorb white space */
5213 		    me->in_word = NO;
5214 		} else if (LYSelectPopups == FALSE) {
5215 		    /*
5216 		     * Put a "(_)" placeholder, and one space (collapsible)
5217 		     * before the label that is expected to follow.  - FM
5218 		     */
5219 		    HText_appendCharacter(me->text, '(');
5220 		    HText_appendCharacter(me->text, '_');
5221 		    HText_appendCharacter(me->text, ')');
5222 		    HText_appendCharacter(me->text, ' ');
5223 		    HText_setLastChar(me->text, ' ');	/* absorb white space */
5224 		    me->in_word = NO;
5225 		}
5226 	    }
5227 
5228 	    /*
5229 	     * Get ready for the next value.
5230 	     */
5231 	    HTChunkClear(&me->option);
5232 	    if ((present && present[HTML_OPTION_SELECTED]) ||
5233 		(me->first_option && LYSelectPopups == FALSE &&
5234 		 HTCurSelectGroupType == F_RADIO_TYPE))
5235 		me->LastOptionChecked = TRUE;
5236 	    else
5237 		me->LastOptionChecked = FALSE;
5238 	    me->first_option = FALSE;
5239 
5240 	    if (present && present[HTML_OPTION_VALUE] &&
5241 		value[HTML_OPTION_VALUE]) {
5242 		if (!I_value) {
5243 		    /*
5244 		     * Convert any HTML entities or decimal escaping.  - FM
5245 		     */
5246 		    StrAllocCopy(I_value, value[HTML_OPTION_VALUE]);
5247 		    me->HiddenValue = TRUE;
5248 		    TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value,
5249 						     ATTR_CS_IN,
5250 						     ATTR_CS_IN,
5251 						     NO,
5252 						     me->UsePlainSpace, me->HiddenValue);
5253 		    me->HiddenValue = FALSE;
5254 		}
5255 		StrAllocCopy(me->LastOptionValue, I_value);
5256 	    } else {
5257 		StrAllocCopy(me->LastOptionValue, me->option.data);
5258 	    }
5259 
5260 	    /*
5261 	     * If this is a popup option, print its option for use in selecting
5262 	     * option by number.  - LE
5263 	     */
5264 	    if (HTCurSelectGroupType == F_RADIO_TYPE &&
5265 		LYSelectPopups &&
5266 		fields_are_numbered()) {
5267 		char marker[8];
5268 		int opnum = HText_getOptionNum(me->text);
5269 
5270 		if (opnum > 0 && opnum < 100000) {
5271 		    sprintf(marker, "(%d)", opnum);
5272 		    HTML_put_string(me, marker);
5273 		    for (i = (int) strlen(marker); i < 5; ++i) {
5274 			HTML_put_character(me, '_');
5275 		    }
5276 		}
5277 	    }
5278 	    FREE(I_value);
5279 	}
5280 	break;
5281 
5282     case HTML_TABLE:
5283 	/*
5284 	 * Not fully implemented.  Just treat as a division with respect to any
5285 	 * ALIGN attribute, with a default of HT_LEFT, or leave as a PRE block
5286 	 * if we are presently in one.  - FM
5287 	 *
5288 	 * Also notify simple table tracking code unless in a preformatted
5289 	 * section, or (currently) non-left alignment.
5290 	 *
5291 	 * If page author is using a TABLE within PRE, it's probably formatted
5292 	 * specifically to work well for Lynx without simple table tracking
5293 	 * code.  Cancel tracking, it would only make things worse.  - kw
5294 	 */
5295 #ifdef EXP_NESTED_TABLES
5296 	if (!nested_tables)
5297 #endif
5298 	    HText_cancelStbl(me->text);
5299 
5300 	if (me->inA) {
5301 	    SET_SKIP_STACK(HTML_A);
5302 	    HTML_end_element(me, HTML_A, include);
5303 	}
5304 	if (me->Underline_Level > 0) {
5305 	    SET_SKIP_STACK(HTML_U);
5306 	    HTML_end_element(me, HTML_U, include);
5307 	}
5308 	me->inTABLE = TRUE;
5309 	if (me->sp->style->id == ST_Preformatted) {
5310 	    UPDATE_STYLE;
5311 	    CHECK_ID(HTML_TABLE_ID);
5312 	    break;
5313 	}
5314 	if (me->Division_Level < (MAX_NESTING - 1)) {
5315 	    me->Division_Level++;
5316 	} else {
5317 	    CTRACE((tfp,
5318 		    "HTML: ****** Maximum nesting of %d divisions/tables exceeded!\n",
5319 		    MAX_NESTING));
5320 	}
5321 	if (present && present[HTML_TABLE_ALIGN] &&
5322 	    non_empty(value[HTML_TABLE_ALIGN])) {
5323 	    if (!strcasecomp(value[HTML_TABLE_ALIGN], "center")) {
5324 		if (no_table_center) {
5325 		    me->DivisionAlignments[me->Division_Level] = HT_LEFT;
5326 		    change_paragraph_style(me, styles[HTML_DLEFT]);
5327 		    UPDATE_STYLE;
5328 		    me->current_default_alignment =
5329 			styles[HTML_DLEFT]->alignment;
5330 		} else {
5331 		    me->DivisionAlignments[me->Division_Level] = HT_CENTER;
5332 		    change_paragraph_style(me, styles[HTML_DCENTER]);
5333 		    UPDATE_STYLE;
5334 		    me->current_default_alignment =
5335 			styles[HTML_DCENTER]->alignment;
5336 		}
5337 
5338 		stbl_align = HT_CENTER;
5339 
5340 	    } else if (!strcasecomp(value[HTML_TABLE_ALIGN], "right")) {
5341 		me->DivisionAlignments[me->Division_Level] = HT_RIGHT;
5342 		change_paragraph_style(me, styles[HTML_DRIGHT]);
5343 		UPDATE_STYLE;
5344 		me->current_default_alignment = styles[HTML_DRIGHT]->alignment;
5345 		stbl_align = HT_RIGHT;
5346 	    } else {
5347 		me->DivisionAlignments[me->Division_Level] = HT_LEFT;
5348 		change_paragraph_style(me, styles[HTML_DLEFT]);
5349 		UPDATE_STYLE;
5350 		me->current_default_alignment = styles[HTML_DLEFT]->alignment;
5351 		if (!strcasecomp(value[HTML_TABLE_ALIGN], "left") ||
5352 		    !strcasecomp(value[HTML_TABLE_ALIGN], "justify"))
5353 		    stbl_align = HT_LEFT;
5354 	    }
5355 	} else {
5356 	    me->DivisionAlignments[me->Division_Level] = HT_LEFT;
5357 	    change_paragraph_style(me, styles[HTML_DLEFT]);
5358 	    UPDATE_STYLE;
5359 	    me->current_default_alignment = styles[HTML_DLEFT]->alignment;
5360 	    /* stbl_align remains HT_ALIGN_NONE */
5361 	}
5362 	CHECK_ID(HTML_TABLE_ID);
5363 	HText_startStblTABLE(me->text, stbl_align);
5364 	break;
5365 
5366     case HTML_TR:
5367 	/*
5368 	 * Not fully implemented.  Just start a new row, if needed, act on an
5369 	 * ALIGN attribute if present, and check for an ID link.  - FM
5370 	 * Also notify simple table tracking code.  - kw
5371 	 */
5372 	if (me->inA) {
5373 	    SET_SKIP_STACK(HTML_A);
5374 	    HTML_end_element(me, HTML_A, include);
5375 	}
5376 	if (me->Underline_Level > 0) {
5377 	    SET_SKIP_STACK(HTML_U);
5378 	    HTML_end_element(me, HTML_U, include);
5379 	}
5380 	UPDATE_STYLE;
5381 	if (!HText_LastLineEmpty(me->text, FALSE)) {
5382 	    HText_setLastChar(me->text, ' ');	/* absorb white space */
5383 	    HText_appendCharacter(me->text, '\r');
5384 	}
5385 	me->in_word = NO;
5386 
5387 	if (me->sp->style->id == ST_Preformatted) {
5388 	    CHECK_ID(HTML_TR_ID);
5389 	    me->inP = FALSE;
5390 /*	    HText_cancelStbl(me->text);  seems unnecessary here - kw */
5391 	    break;
5392 	}
5393 	if (LYoverride_default_alignment(me)) {
5394 	    me->sp->style->alignment = styles[me->sp[0].tag_number]->alignment;
5395 	} else if (me->List_Nesting_Level >= 0 ||
5396 		   ((me->Division_Level < 0) &&
5397 		    (me->sp->style->id == ST_Normal ||
5398 		     me->sp->style->id == ST_Preformatted))) {
5399 	    me->sp->style->alignment = HT_LEFT;
5400 	} else {
5401 	    me->sp->style->alignment = (short) me->current_default_alignment;
5402 	}
5403 	if (present && present[HTML_TR_ALIGN] && value[HTML_TR_ALIGN]) {
5404 	    if (!strcasecomp(value[HTML_TR_ALIGN], "center") &&
5405 		!(me->List_Nesting_Level >= 0 && !me->inP)) {
5406 		if (no_table_center)
5407 		    me->sp->style->alignment = HT_LEFT;
5408 		else
5409 		    me->sp->style->alignment = HT_CENTER;
5410 		stbl_align = HT_CENTER;
5411 	    } else if (!strcasecomp(value[HTML_TR_ALIGN], "right") &&
5412 		       !(me->List_Nesting_Level >= 0 && !me->inP)) {
5413 		me->sp->style->alignment = HT_RIGHT;
5414 		stbl_align = HT_RIGHT;
5415 	    } else if (!strcasecomp(value[HTML_TR_ALIGN], "left") ||
5416 		       !strcasecomp(value[HTML_TR_ALIGN], "justify")) {
5417 		me->sp->style->alignment = HT_LEFT;
5418 		stbl_align = HT_LEFT;
5419 	    }
5420 	}
5421 
5422 	CHECK_ID(HTML_TR_ID);
5423 	me->inP = FALSE;
5424 	HText_startStblTR(me->text, stbl_align);
5425 	break;
5426 
5427     case HTML_THEAD:
5428     case HTML_TFOOT:
5429     case HTML_TBODY:
5430 	HText_endStblTR(me->text);
5431 	/*
5432 	 * Not fully implemented.  Just check for an ID link.  - FM
5433 	 */
5434 	if (me->inA) {
5435 	    SET_SKIP_STACK(HTML_A);
5436 	    HTML_end_element(me, HTML_A, include);
5437 	}
5438 	if (me->Underline_Level > 0) {
5439 	    SET_SKIP_STACK(HTML_U);
5440 	    HTML_end_element(me, HTML_U, include);
5441 	}
5442 	UPDATE_STYLE;
5443 	if (me->inTABLE) {
5444 	    if (present && present[HTML_TR_ALIGN] && value[HTML_TR_ALIGN]) {
5445 		if (!strcasecomp(value[HTML_TR_ALIGN], "center")) {
5446 		    stbl_align = HT_CENTER;
5447 		} else if (!strcasecomp(value[HTML_TR_ALIGN], "right")) {
5448 		    stbl_align = HT_RIGHT;
5449 		} else if (!strcasecomp(value[HTML_TR_ALIGN], "left") ||
5450 			   !strcasecomp(value[HTML_TR_ALIGN], "justify")) {
5451 		    stbl_align = HT_LEFT;
5452 		}
5453 	    }
5454 	    HText_startStblRowGroup(me->text, stbl_align);
5455 	}
5456 	CHECK_ID(HTML_TR_ID);
5457 	break;
5458 
5459     case HTML_COL:
5460     case HTML_COLGROUP:
5461 	/*
5462 	 * Not fully implemented.  Just check for an ID link.  - FM
5463 	 */
5464 	if (me->inA) {
5465 	    SET_SKIP_STACK(HTML_A);
5466 	    HTML_end_element(me, HTML_A, include);
5467 	}
5468 	if (me->Underline_Level > 0) {
5469 	    SET_SKIP_STACK(HTML_U);
5470 	    HTML_end_element(me, HTML_U, include);
5471 	}
5472 	UPDATE_STYLE;
5473 	if (me->inTABLE) {
5474 	    int span = 1;
5475 
5476 	    if (present && present[HTML_COL_SPAN] &&
5477 		value[HTML_COL_SPAN] &&
5478 		isdigit(UCH(*value[HTML_COL_SPAN])))
5479 		span = atoi(value[HTML_COL_SPAN]);
5480 	    if (present && present[HTML_COL_ALIGN] && value[HTML_COL_ALIGN]) {
5481 		if (!strcasecomp(value[HTML_COL_ALIGN], "center")) {
5482 		    stbl_align = HT_CENTER;
5483 		} else if (!strcasecomp(value[HTML_COL_ALIGN], "right")) {
5484 		    stbl_align = HT_RIGHT;
5485 		} else if (!strcasecomp(value[HTML_COL_ALIGN], "left") ||
5486 			   !strcasecomp(value[HTML_COL_ALIGN], "justify")) {
5487 		    stbl_align = HT_LEFT;
5488 		}
5489 	    }
5490 	    HText_startStblCOL(me->text, span, stbl_align,
5491 			       (BOOL) (ElementNumber == HTML_COLGROUP));
5492 	}
5493 	CHECK_ID(HTML_COL_ID);
5494 	break;
5495 
5496     case HTML_TH:
5497     case HTML_TD:
5498 	if (me->inA) {
5499 	    SET_SKIP_STACK(HTML_A);
5500 	    HTML_end_element(me, HTML_A, include);
5501 	}
5502 	if (me->Underline_Level > 0) {
5503 	    SET_SKIP_STACK(HTML_U);
5504 	    HTML_end_element(me, HTML_U, include);
5505 	}
5506 	UPDATE_STYLE;
5507 	CHECK_ID(HTML_TD_ID);
5508 	/*
5509 	 * Not fully implemented.  Just add a collapsible space and break - FM
5510 	 * Also notify simple table tracking code.  - kw
5511 	 */
5512 	HTML_put_character(me, ' ');
5513 	{
5514 	    int colspan = 1, rowspan = 1;
5515 
5516 	    if (present && present[HTML_TD_COLSPAN] &&
5517 		value[HTML_TD_COLSPAN] &&
5518 		isdigit(UCH(*value[HTML_TD_COLSPAN])))
5519 		colspan = atoi(value[HTML_TD_COLSPAN]);
5520 	    if (present && present[HTML_TD_ROWSPAN] &&
5521 		value[HTML_TD_ROWSPAN] &&
5522 		isdigit(UCH(*value[HTML_TD_ROWSPAN])))
5523 		rowspan = atoi(value[HTML_TD_ROWSPAN]);
5524 	    if (present && present[HTML_TD_ALIGN] && value[HTML_TD_ALIGN]) {
5525 		if (!strcasecomp(value[HTML_TD_ALIGN], "center")) {
5526 		    stbl_align = HT_CENTER;
5527 		} else if (!strcasecomp(value[HTML_TD_ALIGN], "right")) {
5528 		    stbl_align = HT_RIGHT;
5529 		} else if (!strcasecomp(value[HTML_TD_ALIGN], "left") ||
5530 			   !strcasecomp(value[HTML_TD_ALIGN], "justify")) {
5531 		    stbl_align = HT_LEFT;
5532 		}
5533 	    }
5534 	    HText_startStblTD(me->text, colspan, rowspan, stbl_align,
5535 			      (BOOL) (ElementNumber == HTML_TH));
5536 	}
5537 	me->in_word = NO;
5538 	break;
5539 
5540     case HTML_MATH:
5541 	/*
5542 	 * We're getting it as Literal text, which, until we can process it,
5543 	 * we'll display as is, within brackets to alert the user.  - FM
5544 	 */
5545 	HTChunkClear(&me->math);
5546 	CHECK_ID(HTML_GEN_ID);
5547 	break;
5548 
5549     default:
5550 	break;
5551 
5552     }				/* end switch */
5553 
5554     if (ElementNumber >= HTML_ELEMENTS ||
5555 	HTML_dtd.tags[ElementNumber].contents != SGML_EMPTY) {
5556 	if (me->skip_stack > 0) {
5557 	    CTRACE((tfp,
5558 		    "HTML:begin_element: internal call (level %d), leaving on stack - `%s'\n",
5559 		    me->skip_stack, NONNULL(GetHTStyleName(me->sp->style))));
5560 	    me->skip_stack--;
5561 	    return status;
5562 	}
5563 	if (me->sp == me->stack) {
5564 	    if (me->stack_overrun == FALSE) {
5565 		HTAlert(HTML_STACK_OVERRUN);
5566 		CTRACE((tfp,
5567 			"HTML: ****** Maximum nesting of %d tags exceeded!\n",
5568 			MAX_NESTING));
5569 		me->stack_overrun = TRUE;
5570 	    }
5571 	    return HT_ERROR;
5572 	}
5573 
5574 	CTRACE((tfp,
5575 		"HTML:begin_element[%d]: adding style to stack - %s (%s)\n",
5576 		(int) STACKLEVEL(me),
5577 		NONNULL(GetHTStyleName(me->new_style)),
5578 		HTML_dtd.tags[ElementNumber].name));
5579 	(me->sp)--;
5580 	me->sp[0].style = me->new_style;	/* Stack new style */
5581 	me->sp[0].tag_number = ElementNumber;
5582 #ifdef USE_JUSTIFY_ELTS
5583 	if (wait_for_this_stacked_elt < 0 &&
5584 	    HTML_dtd.tags[ElementNumber].can_justify == FALSE)
5585 	    wait_for_this_stacked_elt = (int) (me->stack - me->sp) + MAX_NESTING;
5586 #endif
5587     }
5588 #ifdef USE_JUSTIFY_ELTS
5589     if (in_DT && ElementNumber == HTML_DD)
5590 	in_DT = FALSE;
5591     else if (ElementNumber == HTML_DT)
5592 	in_DT = TRUE;
5593 #endif
5594 
5595 #if defined(USE_COLOR_STYLE)
5596 /* end really empty tags straight away */
5597 
5598     if (ReallyEmptyTagNum(element_number)) {
5599 	CTRACE2(TRACE_STYLE,
5600 		(tfp, "STYLE.begin_element:ending \"EMPTY\" element style\n"));
5601 	HText_characterStyle(me->text, HCODE_TO_STACK_OFF(hcode), STACK_OFF);
5602 
5603 #  if !OMIT_SCN_KEEPING
5604 	FastTrimColorClass(HTML_dtd.tags[element_number].name,
5605 			   HTML_dtd.tags[element_number].name_len,
5606 			   Style_className,
5607 			   &Style_className_end, &hcode);
5608 #  endif
5609     }
5610 #endif /* USE_COLOR_STYLE */
5611     return status;
5612 }
5613 
5614 /*		End Element
5615  *		-----------
5616  *
5617  *	When we end an element, the style must be returned to that
5618  *	in effect before that element.	Note that anchors (etc?)
5619  *	don't have an associated style, so that we must scan down the
5620  *	stack for an element with a defined style. (In fact, the styles
5621  *	should be linked to the whole stack not just the top one.)
5622  *	TBL 921119
5623  */
HTML_end_element(HTStructured * me,int element_number,char ** include)5624 static int HTML_end_element(HTStructured * me, int element_number,
5625 			    char **include)
5626 {
5627     static char empty[1];
5628 
5629     int i = 0;
5630     int status = HT_OK;
5631     char *temp = NULL, *cp = NULL;
5632     BOOL BreakFlag = FALSE;
5633     BOOL intern_flag = FALSE;
5634 
5635 #ifdef USE_COLOR_STYLE
5636     BOOL skip_stack_requested = FALSE;
5637 #endif
5638     EMIT_IFDEF_USE_JUSTIFY_ELTS(BOOL reached_awaited_stacked_elt = FALSE);
5639 
5640 #ifdef USE_PRETTYSRC
5641     if (psrc_view && !sgml_in_psrc_was_initialized) {
5642 	if (!psrc_nested_call) {
5643 	    HTTag *tag = &HTML_dtd.tags[element_number];
5644 	    char buf[200];
5645 	    int tag_charset = 0;
5646 
5647 	    psrc_nested_call = TRUE;
5648 	    PSRCSTART(abracket);
5649 	    PUTS("</");
5650 	    PSRCSTOP(abracket);
5651 	    PSRCSTART(tag);
5652 	    if (tagname_transform != 0)
5653 		PUTS(tag->name);
5654 	    else {
5655 		LYStrNCpy(buf, tag->name, sizeof(buf) - 1);
5656 		LYLowerCase(buf);
5657 		PUTS(buf);
5658 	    }
5659 	    PSRCSTOP(tag);
5660 	    PSRCSTART(abracket);
5661 	    PUTC('>');
5662 	    PSRCSTOP(abracket);
5663 	    psrc_nested_call = FALSE;
5664 	    return HT_OK;
5665 	}
5666 	/*fall through */
5667     }
5668 #endif
5669 
5670     if ((me->sp >= (me->stack + MAX_NESTING - 1) ||
5671 	 element_number != me->sp[0].tag_number) &&
5672 	HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
5673 	CTRACE((tfp,
5674 		"HTML: end of element %s when expecting end of %s\n",
5675 		HTML_dtd.tags[element_number].name,
5676 		(me->sp == me->stack + MAX_NESTING - 1) ? "none" :
5677 		(me->sp->tag_number < 0) ? "*invalid tag*" :
5678 		(me->sp->tag_number >= HTML_ELEMENTS) ? "special tag" :
5679 		HTML_dtd.tags[me->sp->tag_number].name));
5680     }
5681 
5682     /*
5683      * If we're seeking MAPs, skip everything that's not a MAP or AREA tag.  -
5684      * FM
5685      */
5686     if (LYMapsOnly) {
5687 	if (!(element_number == HTML_MAP || element_number == HTML_AREA ||
5688 	      element_number == HTML_OBJECT)) {
5689 	    return HT_OK;
5690 	}
5691     }
5692 
5693     /*
5694      * Pop state off stack if we didn't declare the element SGML_EMPTY in
5695      * HTMLDTD.c.  - FM & KW
5696      */
5697     if (HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
5698 #ifdef USE_COLOR_STYLE
5699 	skip_stack_requested = (BOOL) (me->skip_stack > 0);
5700 #endif
5701 	if ((element_number != me->sp[0].tag_number) &&
5702 	    me->skip_stack <= 0 &&
5703 	    HTML_dtd.tags[HTML_LH].contents != SGML_EMPTY &&
5704 	    (me->sp[0].tag_number == HTML_UL ||
5705 	     me->sp[0].tag_number == HTML_OL ||
5706 	     me->sp[0].tag_number == HTML_MENU ||
5707 	     me->sp[0].tag_number == HTML_DIR ||
5708 	     me->sp[0].tag_number == HTML_LI) &&
5709 	    (element_number == HTML_H1 ||
5710 	     element_number == HTML_H2 ||
5711 	     element_number == HTML_H3 ||
5712 	     element_number == HTML_H4 ||
5713 	     element_number == HTML_H5 ||
5714 	     element_number == HTML_H6)) {
5715 	    /*
5716 	     * Set the break flag if we're popping a dummy HTML_LH substituted
5717 	     * for an HTML_H# encountered in a list.
5718 	     */
5719 	    BreakFlag = TRUE;
5720 	}
5721 	if (me->skip_stack == 0 && element_number == HTML_OBJECT &&
5722 	    me->sp[0].tag_number == HTML_OBJECT_M &&
5723 	    (me->sp < (me->stack + MAX_NESTING - 1)))
5724 	    me->sp[0].tag_number = HTML_OBJECT;
5725 	if (me->skip_stack > 0) {
5726 	    CTRACE2(TRACE_STYLE,
5727 		    (tfp,
5728 		     "HTML:end_element: Internal call (level %d), leaving on stack - %s\n",
5729 		     me->skip_stack, NONNULL(GetHTStyleName(me->sp->style))));
5730 	    me->skip_stack--;
5731 	} else if (element_number == HTML_OBJECT &&
5732 		   me->sp[0].tag_number != HTML_OBJECT &&
5733 		   me->sp[0].tag_number != HTML_OBJECT_M &&
5734 		   me->objects_mixed_open > 0 &&
5735 		   !(me->objects_figged_open > 0 &&
5736 		     me->sp[0].tag_number == HTML_FIG)) {
5737 	    /*
5738 	     * Ignore non-corresponding OBJECT tags that we didn't push because
5739 	     * the SGML parser was supposed to go on parsing the contents
5740 	     * non-literally.  - kw
5741 	     */
5742 	    CTRACE2(TRACE_STYLE,
5743 		    (tfp, "HTML:end_element[%d]: %s (level %d), %s - %s\n",
5744 		     (int) STACKLEVEL(me),
5745 		     "Special OBJECT handling", me->objects_mixed_open,
5746 		     "leaving on stack",
5747 		     NONNULL(GetHTStyleName(me->sp->style))));
5748 	    me->objects_mixed_open--;
5749 	} else if (me->stack_overrun == TRUE &&
5750 		   element_number != me->sp[0].tag_number) {
5751 	    /*
5752 	     * Ignore non-corresponding tags if we had a stack overrun.  This
5753 	     * is not a completely fail-safe strategy for protection against
5754 	     * any seriously adverse consequences of a stack overrun, and the
5755 	     * rendering of the document will not be as intended, but we expect
5756 	     * overruns to be rare, and this should offer reasonable protection
5757 	     * against crashes if an overrun does occur.  - FM
5758 	     */
5759 	    return HT_OK;	/* let's pretend... */
5760 	} else if (element_number == HTML_SELECT &&
5761 		   me->sp[0].tag_number != HTML_SELECT) {
5762 	    /*
5763 	     * Ignore non-corresponding SELECT tags, since we probably popped
5764 	     * it and closed the SELECT block to deal with markup which amounts
5765 	     * to a nested SELECT, or an out of order FORM end tag.  - FM
5766 	     */
5767 	    return HT_OK;
5768 	} else if ((element_number != me->sp[0].tag_number) &&
5769 		   HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY &&
5770 		   (me->sp[0].tag_number == HTML_UL ||
5771 		    me->sp[0].tag_number == HTML_OL ||
5772 		    me->sp[0].tag_number == HTML_MENU ||
5773 		    me->sp[0].tag_number == HTML_DIR ||
5774 		    me->sp[0].tag_number == HTML_LI) &&
5775 		   (element_number == HTML_H1 ||
5776 		    element_number == HTML_H2 ||
5777 		    element_number == HTML_H3 ||
5778 		    element_number == HTML_H4 ||
5779 		    element_number == HTML_H5 ||
5780 		    element_number == HTML_H6)) {
5781 	    /*
5782 	     * It's an H# for which we substituted an HTML_LH, which we've
5783 	     * declared as SGML_EMPTY, so just return.  - FM
5784 	     */
5785 	    return HT_OK;
5786 	} else if (me->sp < (me->stack + MAX_NESTING - 1)) {
5787 #ifdef USE_JUSTIFY_ELTS
5788 	    if (wait_for_this_stacked_elt == me->stack - me->sp + MAX_NESTING)
5789 		reached_awaited_stacked_elt = TRUE;
5790 #endif
5791 	    if (element_number == HTML_OBJECT) {
5792 		if (me->sp[0].tag_number == HTML_FIG &&
5793 		    me->objects_figged_open > 0) {
5794 		    /*
5795 		     * It's an OBJECT for which we substituted a FIG, so pop
5796 		     * the FIG and pretend that's what we are being called for.
5797 		     * - kw
5798 		     */
5799 		    CTRACE2(TRACE_STYLE,
5800 			    (tfp,
5801 			     "HTML:end_element[%d]: %s (level %d), %s - %s\n",
5802 			     (int) STACKLEVEL(me),
5803 			     "Special OBJECT->FIG handling",
5804 			     me->objects_figged_open,
5805 			     "treating as end FIG",
5806 			     NONNULL(GetHTStyleName(me->sp->style))));
5807 		    me->objects_figged_open--;
5808 		    element_number = HTML_FIG;
5809 		}
5810 	    }
5811 	    (me->sp)++;
5812 	    CTRACE2(TRACE_STYLE,
5813 		    (tfp,
5814 		     "HTML:end_element[%d]: Popped style off stack - %s\n",
5815 		     (int) STACKLEVEL(me),
5816 		     NONNULL(GetHTStyleName(me->sp->style))));
5817 	} else {
5818 	    CTRACE2(TRACE_STYLE, (tfp,
5819 				  "Stack underflow error!  Tried to pop off more styles than exist in stack\n"));
5820 	}
5821     }
5822     if (BreakFlag == TRUE) {
5823 #ifdef USE_JUSTIFY_ELTS
5824 	if (reached_awaited_stacked_elt)
5825 	    wait_for_this_stacked_elt = -1;
5826 #endif
5827 	return HT_OK;		/* let's pretend... */
5828     }
5829 
5830     /*
5831      * Check for unclosed TEXTAREA.  - FM
5832      */
5833     if (me->inTEXTAREA && element_number != HTML_TEXTAREA) {
5834 	if (LYBadHTML(me)) {
5835 	    LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag\n");
5836 	}
5837     }
5838 
5839     if (!me->text && !LYMapsOnly) {
5840 	UPDATE_STYLE;
5841     }
5842 
5843     /*
5844      * Handle the end tag.  - FM
5845      */
5846     switch (element_number) {
5847 
5848     case HTML_HTML:
5849 	if (me->inA || me->inSELECT || me->inTEXTAREA) {
5850 	    if (LYBadHTML(me)) {
5851 		char *msg = NULL;
5852 
5853 		HTSprintf0(&msg,
5854 			   "Bad HTML: %s%s%s%s%s not closed before HTML end tag *****\n",
5855 			   me->inSELECT ? "SELECT" : "",
5856 			   (me->inSELECT && me->inTEXTAREA) ? ", " : "",
5857 			   me->inTEXTAREA ? "TEXTAREA" : "",
5858 			   (((me->inSELECT || me->inTEXTAREA) && me->inA)
5859 			    ? ", "
5860 			    : ""),
5861 			   me->inA ? "A" : "");
5862 		LYShowBadHTML(msg);
5863 		FREE(msg);
5864 	    }
5865 	}
5866 	break;
5867 
5868     case HTML_HEAD:
5869 	if (me->inBASE &&
5870 	    (LYIsUIPage3(me->node_anchor->address, UIP_LIST_PAGE, 0) ||
5871 	     LYIsUIPage3(me->node_anchor->address, UIP_ADDRLIST_PAGE, 0))) {
5872 	    /* If we are parsing the List Page, and have a BASE after we are
5873 	     * done with the HEAD element, propagate it back to the node_anchor
5874 	     * object.  The base should have been inserted by showlist() to
5875 	     * record what document the List Page is about, and other functions
5876 	     * may later look for it in the anchor.  - kw
5877 	     */
5878 	    StrAllocCopy(me->node_anchor->content_base, me->base_href);
5879 	}
5880 	if (HText_hasToolbar(me->text))
5881 	    HText_appendParagraph(me->text);
5882 	break;
5883 
5884     case HTML_TITLE:
5885 	HTChunkTerminate(&me->title);
5886 	HTAnchor_setTitle(me->node_anchor, me->title.data);
5887 	HTChunkClear(&me->title);
5888 	/*
5889 	 * Check if it's a bookmark file, and if so, and multiple bookmark
5890 	 * support is on, or it's off but this isn't the default bookmark file
5891 	 * (e.g., because it was on before, and this is another bookmark file
5892 	 * that has been retrieved as a previous document), insert the current
5893 	 * description string and filepath for it.  We pass the strings back to
5894 	 * the SGML parser so that any 8 bit or multibyte/CJK characters will
5895 	 * be handled by the parser's state and charset routines.  - FM
5896 	 */
5897 	if (non_empty(me->node_anchor->bookmark)) {
5898 	    if ((LYMultiBookmarks != MBM_OFF) ||
5899 		(non_empty(bookmark_page) &&
5900 		 strcmp(me->node_anchor->bookmark, bookmark_page))) {
5901 		if (!include)
5902 		    include = &me->xinclude;
5903 		for (i = 0; i <= MBM_V_MAXFILES; i++) {
5904 		    if (MBM_A_subbookmark[i] &&
5905 			!strcmp(MBM_A_subbookmark[i],
5906 				me->node_anchor->bookmark)) {
5907 			StrAllocCat(*include, "<H2><EM>");
5908 			StrAllocCat(*include, gettext("Description:"));
5909 			StrAllocCat(*include, "</EM> ");
5910 			StrAllocCopy(temp,
5911 				     ((MBM_A_subdescript[i] &&
5912 				       *MBM_A_subdescript[i]) ?
5913 				      MBM_A_subdescript[i] : gettext("(none)")));
5914 			LYEntify(&temp, TRUE);
5915 			StrAllocCat(*include, temp);
5916 			StrAllocCat(*include, "<BR><EM>&nbsp;&nbsp;&nbsp;");
5917 			StrAllocCat(*include, gettext("Filepath:"));
5918 			StrAllocCat(*include, "</EM> ");
5919 			StrAllocCopy(temp,
5920 				     ((MBM_A_subbookmark[i] &&
5921 				       *MBM_A_subbookmark[i])
5922 				      ? MBM_A_subbookmark[i]
5923 				      : gettext("(unknown)")));
5924 			LYEntify(&temp, TRUE);
5925 			StrAllocCat(*include, temp);
5926 			FREE(temp);
5927 			StrAllocCat(*include, "</H2>");
5928 			break;
5929 		    }
5930 		}
5931 	    }
5932 	}
5933 	break;
5934 
5935     case HTML_STYLE:
5936 	/*
5937 	 * We're getting it as Literal text, which, for now, we'll just ignore.
5938 	 * - FM
5939 	 */
5940 	HTChunkTerminate(&me->style_block);
5941 	CTRACE2(TRACE_STYLE,
5942 		(tfp, "HTML: STYLE content =\n%s\n",
5943 		 me->style_block.data));
5944 	HTChunkClear(&me->style_block);
5945 	break;
5946 
5947     case HTML_SCRIPT:
5948 	/*
5949 	 * We're getting it as Literal text, which, for now, we'll just ignore.
5950 	 * - FM
5951 	 */
5952 	HTChunkTerminate(&me->script);
5953 	CTRACE((tfp, "HTML: SCRIPT content =\n%s\n",
5954 		me->script.data));
5955 	HTChunkClear(&me->script);
5956 	break;
5957 
5958     case HTML_BODY:
5959 	if (me->inA || me->inSELECT || me->inTEXTAREA) {
5960 	    if (LYBadHTML(me)) {
5961 		char *msg = NULL;
5962 
5963 		HTSprintf0(&msg,
5964 			   "Bad HTML: %s%s%s%s%s not closed before BODY end tag *****\n",
5965 			   me->inSELECT ? "SELECT" : "",
5966 			   (me->inSELECT && me->inTEXTAREA) ? ", " : "",
5967 			   me->inTEXTAREA ? "TEXTAREA" : "",
5968 			   (((me->inSELECT || me->inTEXTAREA) && me->inA)
5969 			    ? ", "
5970 			    : ""),
5971 			   me->inA ? "A" : "");
5972 		LYShowBadHTML(msg);
5973 		FREE(msg);
5974 	    }
5975 	}
5976 	break;
5977 
5978     case HTML_FRAMESET:
5979 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
5980 	break;
5981 
5982     case HTML_NOFRAMES:
5983     case HTML_IFRAME:
5984 	LYEnsureDoubleSpace(me);
5985 	LYResetParagraphAlignment(me);
5986 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
5987 	break;
5988 
5989     case HTML_BANNER:
5990     case HTML_MARQUEE:
5991     case HTML_BLOCKQUOTE:
5992     case HTML_BQ:
5993     case HTML_ADDRESS:
5994 	/*
5995 	 * Set flag to know that style has ended.  Fall through.
5996 	 i_prior_style = -1;
5997 	 */
5998 	change_paragraph_style(me, me->sp->style);
5999 	UPDATE_STYLE;
6000 	if (me->sp->tag_number == element_number)
6001 	    LYEnsureDoubleSpace(me);
6002 	if (me->List_Nesting_Level >= 0)
6003 	    HText_NegateLineOne(me->text);
6004 	break;
6005 
6006     case HTML_CENTER:
6007     case HTML_DIV:
6008 	if (me->Division_Level >= 0)
6009 	    me->Division_Level--;
6010 	if (me->Division_Level >= 0) {
6011 	    if (me->sp->style->alignment !=
6012 		me->DivisionAlignments[me->Division_Level]) {
6013 		if (me->inP)
6014 		    LYEnsureSingleSpace(me);
6015 		me->sp->style->alignment =
6016 		    me->DivisionAlignments[me->Division_Level];
6017 	    }
6018 	}
6019 	change_paragraph_style(me, me->sp->style);
6020 	if (me->style_change) {
6021 	    actually_set_style(me);
6022 	    if (me->List_Nesting_Level >= 0)
6023 		HText_NegateLineOne(me->text);
6024 	} else if (me->inP)
6025 	    LYEnsureSingleSpace(me);
6026 	me->current_default_alignment = me->sp->style->alignment;
6027 	break;
6028 
6029     case HTML_H1:		/* header styles */
6030     case HTML_H2:
6031     case HTML_H3:
6032     case HTML_H4:
6033     case HTML_H5:
6034     case HTML_H6:
6035 	if (me->Division_Level >= 0) {
6036 	    me->sp->style->alignment =
6037 		me->DivisionAlignments[me->Division_Level];
6038 	} else if (me->sp->style->id == ST_HeadingCenter ||
6039 		   me->sp->style->id == ST_Heading1) {
6040 	    me->sp->style->alignment = HT_CENTER;
6041 	} else if (me->sp->style->id == ST_HeadingRight) {
6042 	    me->sp->style->alignment = HT_RIGHT;
6043 	} else {
6044 	    me->sp->style->alignment = HT_LEFT;
6045 	}
6046 	change_paragraph_style(me, me->sp->style);
6047 	UPDATE_STYLE;
6048 	if (styles[element_number]->font & HT_BOLD) {
6049 	    if (me->inBoldA == FALSE && me->inBoldH == TRUE) {
6050 		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
6051 	    }
6052 	    me->inBoldH = FALSE;
6053 	}
6054 	if (me->List_Nesting_Level >= 0)
6055 	    HText_NegateLineOne(me->text);
6056 	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
6057 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
6058 	    me->inUnderline = TRUE;
6059 	}
6060 	break;
6061 
6062     case HTML_P:
6063 	LYHandlePlike(me,
6064 		      (const BOOL *) 0, (STRING2PTR) 0,
6065 		      include, 0,
6066 		      FALSE);
6067 	break;
6068 
6069     case HTML_FONT:
6070 	me->inFONT = FALSE;
6071 	break;
6072 
6073     case HTML_B:		/* Physical character highlighting */
6074     case HTML_BLINK:
6075     case HTML_I:
6076     case HTML_U:
6077 
6078     case HTML_CITE:		/* Logical character highlighting */
6079     case HTML_EM:
6080     case HTML_STRONG:
6081 	/*
6082 	 * Ignore any emphasis end tags if the Underline_Level is not set.  -
6083 	 * FM
6084 	 */
6085 	if (me->Underline_Level <= 0)
6086 	    break;
6087 
6088 	/*
6089 	 * Adjust the Underline level counter, and turn off underlining if
6090 	 * appropriate.  - FM
6091 	 */
6092 	me->Underline_Level--;
6093 	if (me->inUnderline && me->Underline_Level < 1) {
6094 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
6095 	    me->inUnderline = FALSE;
6096 	    CTRACE((tfp, "Ending underline\n"));
6097 	} else {
6098 	    CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level));
6099 	}
6100 	break;
6101 
6102     case HTML_ABBR:		/* Miscellaneous character containers */
6103     case HTML_ACRONYM:
6104     case HTML_AU:
6105     case HTML_AUTHOR:
6106     case HTML_BIG:
6107     case HTML_CODE:
6108     case HTML_DFN:
6109     case HTML_KBD:
6110     case HTML_SAMP:
6111     case HTML_SMALL:
6112     case HTML_SUP:
6113     case HTML_TT:
6114     case HTML_VAR:
6115 	break;
6116 
6117     case HTML_SUB:
6118 	HText_appendCharacter(me->text, ']');
6119 	break;
6120 
6121     case HTML_DEL:
6122     case HTML_S:
6123     case HTML_STRIKE:
6124 	HTML_put_character(me, ' ');
6125 	if (me->inUnderline == FALSE)
6126 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
6127 	HTML_put_string(me, ":DEL]");
6128 	if (me->inUnderline == FALSE)
6129 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
6130 	HTML_put_character(me, ' ');
6131 	me->in_word = NO;
6132 	break;
6133 
6134     case HTML_INS:
6135 	HTML_put_character(me, ' ');
6136 	if (me->inUnderline == FALSE)
6137 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
6138 	HTML_put_string(me, ":INS]");
6139 	if (me->inUnderline == FALSE)
6140 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
6141 	HTML_put_character(me, ' ');
6142 	me->in_word = NO;
6143 	break;
6144 
6145     case HTML_Q:
6146 	if (me->Quote_Level > 0)
6147 	    me->Quote_Level--;
6148 	/*
6149 	 * Should check LANG and/or DIR attributes, and the
6150 	 * me->node_anchor->charset and/or yet to be added structure elements,
6151 	 * to determine whether we should use chevrons, but for now we'll
6152 	 * always use double- or single-quotes.  - FM
6153 	 */
6154 	if (!(me->Quote_Level & 1))
6155 	    HTML_put_character(me, '"');
6156 	else
6157 	    HTML_put_character(me, '\'');
6158 	break;
6159 
6160     case HTML_PRE:		/* Formatted text */
6161 	/*
6162 	 * Set to know that we are no longer in a PRE block.
6163 	 */
6164 	HText_appendCharacter(me->text, '\n');
6165 	me->inPRE = FALSE;
6166 	/* FALLTHRU */
6167     case HTML_LISTING:		/* Literal text */
6168 	/* FALLTHRU */
6169     case HTML_XMP:
6170 	/* FALLTHRU */
6171     case HTML_PLAINTEXT:
6172 	if (me->comment_start)
6173 	    HText_appendText(me->text, me->comment_start);
6174 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6175 	if (me->List_Nesting_Level >= 0) {
6176 	    UPDATE_STYLE;
6177 	    HText_NegateLineOne(me->text);
6178 	}
6179 	break;
6180 
6181     case HTML_NOTE:
6182     case HTML_FN:
6183 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6184 	UPDATE_STYLE;
6185 	if (me->sp->tag_number == element_number)
6186 	    LYEnsureDoubleSpace(me);
6187 	if (me->List_Nesting_Level >= 0)
6188 	    HText_NegateLineOne(me->text);
6189 	me->inLABEL = FALSE;
6190 	break;
6191 
6192     case HTML_OL:
6193 	me->OL_Counter[me->List_Nesting_Level < 11 ?
6194 		       me->List_Nesting_Level : 11] = OL_VOID;
6195 	/* FALLTHRU */
6196     case HTML_DL:
6197 	/* FALLTHRU */
6198     case HTML_UL:
6199 	/* FALLTHRU */
6200     case HTML_MENU:
6201 	/* FALLTHRU */
6202     case HTML_DIR:
6203 	me->List_Nesting_Level--;
6204 	CTRACE((tfp, "HTML_end_element: Reducing List Nesting Level to %d\n",
6205 		me->List_Nesting_Level));
6206 #ifdef USE_JUSTIFY_ELTS
6207 	if (element_number == HTML_DL)
6208 	    in_DT = FALSE;	/*close the term that was without definition. */
6209 #endif
6210 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6211 	UPDATE_STYLE;
6212 	if (me->List_Nesting_Level >= 0)
6213 	    LYEnsureSingleSpace(me);
6214 	break;
6215 
6216     case HTML_SPAN:
6217 	/*
6218 	 * Should undo anything we did based on LANG and/or DIR attributes, and
6219 	 * the me->node_anchor->charset and/or yet to be added structure
6220 	 * elements.  - FM
6221 	 */
6222 	break;
6223 
6224     case HTML_BDO:
6225 	/*
6226 	 * Should undo anything we did based on DIR (and/or LANG) attributes,
6227 	 * and the me->node_anchor->charset and/or yet to be added structure
6228 	 * elements.  - FM
6229 	 */
6230 	break;
6231 
6232     case HTML_A:
6233 	/*
6234 	 * Ignore any spurious A end tags.  - FM
6235 	 */
6236 	if (me->inA == FALSE)
6237 	    break;
6238 	/*
6239 	 * Set to know that we are no longer in an anchor.
6240 	 */
6241 	me->inA = FALSE;
6242 #ifdef MARK_HIDDEN_LINKS
6243 	if (non_empty(hidden_link_marker) &&
6244 	    HText_isAnchorBlank(me->text, me->CurrentANum)) {
6245 	    HText_appendText(me->text, hidden_link_marker);
6246 	}
6247 #endif
6248 	UPDATE_STYLE;
6249 	if (me->inBoldA == TRUE && me->inBoldH == FALSE)
6250 	    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
6251 	HText_endAnchor(me->text, me->CurrentANum);
6252 	me->CurrentANum = 0;
6253 	me->inBoldA = FALSE;
6254 	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
6255 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
6256 	    me->inUnderline = TRUE;
6257 	}
6258 	break;
6259 
6260     case HTML_MAP:
6261 	FREE(me->map_address);
6262 	break;
6263 
6264     case HTML_BODYTEXT:
6265 	/*
6266 	 * We may need to look at this someday to deal with OBJECTs optimally,
6267 	 * but just ignore it for now.  - FM
6268 	 */
6269 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6270 	break;
6271 
6272     case HTML_TEXTFLOW:
6273 	/*
6274 	 * We may need to look at this someday to deal with APPLETs optimally,
6275 	 * but just ignore it for now.  - FM
6276 	 */
6277 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6278 	break;
6279 
6280     case HTML_FIG:
6281 	LYHandleFIG(me, NULL, NULL,
6282 		    0,
6283 		    0,
6284 		    NULL,
6285 		    NULL, NO, FALSE, &intern_flag);
6286 	break;
6287 
6288     case HTML_OBJECT:
6289 	/*
6290 	 * Finish the data off.
6291 	 */
6292 	{
6293 	    int s = 0, e = 0;
6294 	    char *start = NULL, *first_end = NULL, *last_end = NULL;
6295 	    char *first_map = NULL, *last_map = NULL;
6296 	    BOOL have_param = FALSE;
6297 	    char *data = NULL;
6298 
6299 	    HTChunkTerminate(&me->object);
6300 	    data = me->object.data;
6301 	    while ((cp = strchr(data, '<')) != NULL) {
6302 		/*
6303 		 * Look for nested OBJECTs.  This procedure could get tripped
6304 		 * up if invalid comments are present in the content, or if an
6305 		 * OBJECT end tag is present in a quoted attribute.  - FM
6306 		 */
6307 		if (!StrNCmp(cp, "<!--", 4)) {
6308 		    data = LYFindEndOfComment(cp);
6309 		    cp = data;
6310 		} else if (s == 0 && !strncasecomp(cp, "<PARAM", 6) &&
6311 			   !IsNmChar(cp[6])) {
6312 		    have_param = TRUE;
6313 		} else if (!strncasecomp(cp, "<OBJECT", 7) &&
6314 			   !IsNmChar(cp[7])) {
6315 		    if (s == 0)
6316 			start = cp;
6317 		    s++;
6318 		} else if (!strncasecomp(cp, "</OBJECT", 8) &&
6319 			   !IsNmChar(cp[8])) {
6320 		    if (e == 0)
6321 			first_end = cp;
6322 		    last_end = cp;
6323 		    e++;
6324 		} else if (!strncasecomp(cp, "<MAP", 4) &&
6325 			   !IsNmChar(cp[4])) {
6326 		    if (!first_map)
6327 			first_map = cp;
6328 		    last_map = cp;
6329 		} else if (!strncasecomp(cp, "</MAP", 5) &&
6330 			   !IsNmChar(cp[5])) {
6331 		    last_map = cp;
6332 		}
6333 		data = ++cp;
6334 	    }
6335 	    if (s < e) {
6336 		/*
6337 		 * We had more end tags than start tags, so we have bad HTML or
6338 		 * otherwise misparsed.  - FM
6339 		 */
6340 		if (LYBadHTML(me)) {
6341 		    char *msg = NULL;
6342 
6343 		    HTSprintf0(&msg,
6344 			       "Bad HTML: Unmatched OBJECT start and end tags.  Discarding content:\n%s\n",
6345 			       me->object.data);
6346 		    LYShowBadHTML(msg);
6347 		    FREE(msg);
6348 		}
6349 		goto End_Object;
6350 	    }
6351 	    if (s > e) {
6352 		if (!me->object_declare && !me->object_name &&
6353 		    !(me->object_shapes && !LYMapsOnly) &&
6354 		    !(me->object_usemap != NULL && !LYMapsOnly) &&
6355 		    !(clickable_images && !LYMapsOnly &&
6356 		      me->object_data != NULL &&
6357 		      !have_param &&
6358 		      me->object_classid == NULL &&
6359 		      me->object_codebase == NULL &&
6360 		      me->object_codetype == NULL)) {
6361 		    /*
6362 		     * We have nested OBJECT tags, and not yet all of the end
6363 		     * tags, but have a case where the content needs to be
6364 		     * parsed again (not dropped) and where we don't want to
6365 		     * output anything special at the point when we
6366 		     * *do* have accumulated all the end tags.  So recycle
6367 		     * the incomplete contents now, and signal the SGML parser
6368 		     * that it should not regard the current OBJECT ended but
6369 		     * should treat its contents as mixed.  Normally these
6370 		     * cases would have already handled in the real
6371 		     * start_element call, so this block may not be necessary.
6372 		     * - kw
6373 		     */
6374 		    CTRACE((tfp, "%s:\n%s\n",
6375 			    "HTML: Nested OBJECT tags.  Recycling incomplete contents",
6376 			    me->object.data));
6377 		    status = HT_PARSER_OTHER_CONTENT;
6378 		    me->object.size--;
6379 		    HTChunkPuts(&me->object, "</OBJECT>");
6380 		    if (!include)	/* error, should not happen */
6381 			include = &me->xinclude;
6382 		    StrnAllocCat(*include, me->object.data, me->object.size);
6383 		    clear_objectdata(me);
6384 		    /* an internal fake call to keep our stack happy: */
6385 		    HTML_start_element(me, HTML_OBJECT, NULL, NULL,
6386 				       me->tag_charset, include);
6387 		    break;
6388 		}
6389 		/*
6390 		 * We have nested OBJECT tags, and not yet all of the end tags,
6391 		 * and we want the end tags.  So restore an end tag to the
6392 		 * content, and signal to the SGML parser that it should resume
6393 		 * the accumulation of OBJECT content (after calling back to
6394 		 * start_element) in a way that is equivalent to passing it a
6395 		 * dummy start tag.  - FM, kw
6396 		 */
6397 		CTRACE((tfp, "HTML: Nested OBJECT tags.  Recycling.\n"));
6398 		status = HT_PARSER_REOPEN_ELT;
6399 		me->object.size--;
6400 		HTChunkPuts(&me->object, "</OBJECT>");
6401 		if (!LYMapsOnly)
6402 		    change_paragraph_style(me, me->sp->style);
6403 		break;
6404 	    }
6405 
6406 	    /*
6407 	     * OBJECT start and end tags are fully matched, assuming we weren't
6408 	     * tripped up by comments or quoted attributes.  - FM
6409 	     */
6410 	    CTRACE((tfp, "HTML:OBJECT content:\n%s\n", me->object.data));
6411 
6412 	    /*
6413 	     * OBJECTs with DECLARE should be saved but not instantiated, and
6414 	     * if nested, can have only other DECLAREd OBJECTs.  Until we have
6415 	     * code to handle these, we'll just create an anchor for the ID, if
6416 	     * present, and discard the content (sigh 8-).  - FM
6417 	     */
6418 	    if (me->object_declare == TRUE) {
6419 		if (non_empty(me->object_id) && !LYMapsOnly)
6420 		    LYHandleID(me, me->object_id);
6421 		CTRACE((tfp, "HTML: DECLAREd OBJECT.  Ignoring!\n"));
6422 		goto End_Object;
6423 	    }
6424 
6425 	    /*
6426 	     * OBJECTs with NAME are for FORM submissions.  We'll just create
6427 	     * an anchor for the ID, if present, and discard the content until
6428 	     * we have code to handle these.  (sigh 8-).  - FM
6429 	     */
6430 	    if (me->object_name != NULL && !LYMapsOnly) {
6431 		if (non_empty(me->object_id))
6432 		    LYHandleID(me, me->object_id);
6433 		CTRACE((tfp, "HTML: NAMEd OBJECT.  Ignoring!\n"));
6434 		goto End_Object;
6435 	    }
6436 
6437 	    /*
6438 	     * Deal with any nested OBJECTs by descending to the inner-most
6439 	     * OBJECT.  - FM
6440 	     */
6441 	    if (s > 0) {
6442 		if (start != NULL &&
6443 		    first_end != NULL && first_end > start) {
6444 		    /*
6445 		     * Minumum requirements for the ad hoc parsing to have
6446 		     * succeeded are met.  We'll hope that it did succeed.  -
6447 		     * FM
6448 		     */
6449 		    if (LYMapsOnly) {
6450 			/*
6451 			 * Well we don't need to do this any more, nested
6452 			 * objects should either not get here any more at all
6453 			 * or can be handled fine by other code below.  Leave
6454 			 * in place for now as a special case for LYMapsOnly.
6455 			 * - kw
6456 			 */
6457 			if (LYMapsOnly && (!last_map || last_map < first_end))
6458 			    *first_end = '\0';
6459 			else
6460 			    e = 0;
6461 			data = NULL;
6462 			if (LYMapsOnly && (!first_map || first_map > start))
6463 			    StrAllocCopy(data, start);
6464 			else
6465 			    StrAllocCopy(data, me->object.data);
6466 			if (e > 0) {
6467 			    for (i = e; i > 0; i--) {
6468 				StrAllocCat(data, "</OBJECT>");
6469 			    }
6470 			}
6471 			if (!include)	/* error, should not happen */
6472 			    include = &me->xinclude;
6473 			StrAllocCat(*include, data);
6474 			CTRACE((tfp, "HTML: Recycling nested OBJECT%s.\n",
6475 				(s > 1) ? "s" : ""));
6476 			FREE(data);
6477 			goto End_Object;
6478 		    }
6479 		} else {
6480 		    if (LYBadHTML(me)) {
6481 			LYShowBadHTML("Bad HTML: Unmatched OBJECT start and end tags.  Discarding content.\n");
6482 		    }
6483 		    goto End_Object;
6484 		}
6485 	    }
6486 
6487 	    /*
6488 	     * If its content has SHAPES, convert it to FIG.  - FM
6489 	     *
6490 	     * This is now handled in our start_element without using include
6491 	     * if the SGML parser cooperates, so this block may be unnecessary.
6492 	     * - kw
6493 	     */
6494 	    if (me->object_shapes == TRUE && !LYMapsOnly) {
6495 		CTRACE((tfp, "HTML: OBJECT has SHAPES.  Converting to FIG.\n"));
6496 		if (!include)	/* error, should not happen */
6497 		    include = &me->xinclude;
6498 		StrAllocCat(*include, "<FIG ISOBJECT IMAGEMAP");
6499 		if (me->object_ismap == TRUE)
6500 		    StrAllocCat(*include, " IMAGEMAP");
6501 		if (me->object_id != NULL) {
6502 		    StrAllocCat(*include, " ID=\"");
6503 		    StrAllocCat(*include, me->object_id);
6504 		    StrAllocCat(*include, "\"");
6505 		}
6506 		if (me->object_data != NULL &&
6507 		    me->object_classid == NULL) {
6508 		    StrAllocCat(*include, " SRC=\"");
6509 		    StrAllocCat(*include, me->object_data);
6510 		    StrAllocCat(*include, "\"");
6511 		}
6512 		StrAllocCat(*include, ">");
6513 		me->object.size--;
6514 		HTChunkPuts(&me->object, "</FIG>");
6515 		HTChunkTerminate(&me->object);
6516 		StrAllocCat(*include, me->object.data);
6517 		goto End_Object;
6518 	    }
6519 
6520 	    /*
6521 	     * If it has a USEMAP attribute and didn't have SHAPES, convert it
6522 	     * to IMG.  - FM
6523 	     */
6524 	    if (me->object_usemap != NULL && !LYMapsOnly) {
6525 		CTRACE((tfp, "HTML: OBJECT has USEMAP.  Converting to IMG.\n"));
6526 
6527 		if (!include)	/* error, should not happen */
6528 		    include = &me->xinclude;
6529 		StrAllocCat(*include, "<IMG ISOBJECT");
6530 		if (me->object_id != NULL) {
6531 		    /*
6532 		     * Pass the ID.  - FM
6533 		     */
6534 		    StrAllocCat(*include, " ID=\"");
6535 		    StrAllocCat(*include, me->object_id);
6536 		    StrAllocCat(*include, "\"");
6537 		}
6538 		if (me->object_data != NULL &&
6539 		    me->object_classid == NULL) {
6540 		    /*
6541 		     * We have DATA with no CLASSID, so let's hope it'
6542 		     * equivalent to an SRC.  - FM
6543 		     */
6544 		    StrAllocCat(*include, " SRC=\"");
6545 		    StrAllocCat(*include, me->object_data);
6546 		    StrAllocCat(*include, "\"");
6547 		}
6548 		if (me->object_title != NULL) {
6549 		    /*
6550 		     * Use the TITLE for both the MAP and the IMGs ALT.  - FM
6551 		     */
6552 		    StrAllocCat(*include, " TITLE=\"");
6553 		    StrAllocCat(*include, me->object_title);
6554 		    StrAllocCat(*include, "\" ALT=\"");
6555 		    StrAllocCat(*include, me->object_title);
6556 		    StrAllocCat(*include, "\"");
6557 		}
6558 		/*
6559 		 * Add the USEMAP, and an ISMAP if present.  - FM
6560 		 */
6561 		if (me->object_usemap != NULL) {
6562 		    StrAllocCat(*include, " USEMAP=\"");
6563 		    StrAllocCat(*include, me->object_usemap);
6564 		    if (me->object_ismap == TRUE)
6565 			StrAllocCat(*include, "\" ISMAP>");
6566 		    else
6567 			StrAllocCat(*include, "\">");
6568 		} else {
6569 		    StrAllocCat(*include, ">");
6570 		}
6571 		/*
6572 		 * Add the content if it has <MAP, since that may be the MAP
6573 		 * this usemap points to.  But if we have nested objects, try
6574 		 * to eliminate portions that cannot contribute to the quest
6575 		 * for MAP.  This is not perfect, we may get too much content;
6576 		 * this seems preferable over losing too much.  - kw
6577 		 */
6578 		if (first_map) {
6579 		    if (s == 0) {
6580 			StrAllocCat(*include, me->object.data);
6581 			CTRACE((tfp,
6582 				"HTML: MAP found, recycling object contents.\n"));
6583 			goto End_Object;
6584 		    }
6585 		    /* s > 0 and s == e */
6586 		    data = NULL;
6587 		    if (last_map < start) {
6588 			*start = '\0';
6589 			i = 0;
6590 		    } else if (last_map < first_end) {
6591 			*first_end = '\0';
6592 			i = e;
6593 		    } else if (last_map < last_end) {
6594 			*last_end = '\0';
6595 			i = 1;
6596 		    } else {
6597 			i = 0;
6598 		    }
6599 		    if (first_map > last_end) {
6600 			/* fake empty object to keep stacks stack happy */
6601 			StrAllocCopy(data, "<OBJECT><");
6602 			StrAllocCat(data, last_end + 1);
6603 			i = 0;
6604 		    } else if (first_map > start) {
6605 			StrAllocCopy(data, start);
6606 		    } else {
6607 			StrAllocCopy(data, me->object.data);
6608 		    }
6609 		    for (; i > 0; i--) {
6610 			StrAllocCat(data, "</OBJECT>");
6611 		    }
6612 		    CTRACE((tfp, "%s:\n%s\n",
6613 			    "HTML: MAP and nested OBJECT tags.  Recycling parts",
6614 			    data));
6615 		    StrAllocCat(*include, data);
6616 		    FREE(data);
6617 		}
6618 		goto End_Object;
6619 	    }
6620 
6621 	    /*
6622 	     * Add an ID link if needed.  - FM
6623 	     */
6624 	    if (non_empty(me->object_id) && !LYMapsOnly)
6625 		LYHandleID(me, me->object_id);
6626 
6627 	    /*
6628 	     * Add the OBJECTs content if not empty.  - FM
6629 	     */
6630 	    if (me->object.size > 1) {
6631 		if (!include)	/* error, should not happen */
6632 		    include = &me->xinclude;
6633 		StrAllocCat(*include, me->object.data);
6634 	    }
6635 
6636 	    /*
6637 	     * Create a link to the DATA, if desired, and we can rule out that
6638 	     * it involves scripting code.  This a risky thing to do, but we
6639 	     * can toggle clickable_images mode off if it really screws things
6640 	     * up, and so we may as well give it a try.  - FM
6641 	     */
6642 	    if (clickable_images) {
6643 		if (!LYMapsOnly &&
6644 		    me->object_data != NULL &&
6645 		    !have_param &&
6646 		    me->object_classid == NULL &&
6647 		    me->object_codebase == NULL &&
6648 		    me->object_codetype == NULL) {
6649 		    /*
6650 		     * We have a DATA value and no need for scripting code, so
6651 		     * close the current Anchor, if one is open, and add an
6652 		     * Anchor for this source.  If we also have a TYPE value,
6653 		     * check whether it's an image or not, and set the link
6654 		     * name accordingly.  - FM
6655 		     */
6656 		    if (!include)	/* error, should not happen */
6657 			include = &me->xinclude;
6658 		    if (me->inA)
6659 			StrAllocCat(*include, "</A>");
6660 		    StrAllocCat(*include, " -<A HREF=\"");
6661 		    StrAllocCat(*include, me->object_data);
6662 		    StrAllocCat(*include, "\">");
6663 		    if ((me->object_type != NULL) &&
6664 			!strncasecomp(me->object_type, "image/", 6)) {
6665 			StrAllocCat(*include, "(IMAGE)");
6666 		    } else {
6667 			StrAllocCat(*include, "(OBJECT)");
6668 		    }
6669 		    StrAllocCat(*include, "</A> ");
6670 		}
6671 	    }
6672 	}
6673 
6674 	/*
6675 	 * Re-intialize all of the OBJECT elements.  - FM
6676 	 */
6677       End_Object:
6678 	clear_objectdata(me);
6679 
6680 	if (!LYMapsOnly)
6681 	    change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6682 	break;
6683 
6684     case HTML_APPLET:
6685 	if (me->inAPPLETwithP) {
6686 	    LYEnsureDoubleSpace(me);
6687 	} else {
6688 	    HTML_put_character(me, ' ');	/* space char may be ignored */
6689 	}
6690 	LYResetParagraphAlignment(me);
6691 	me->inAPPLETwithP = FALSE;
6692 	me->inAPPLET = FALSE;
6693 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6694 	break;
6695 
6696     case HTML_CAPTION:
6697 	LYEnsureDoubleSpace(me);
6698 	LYResetParagraphAlignment(me);
6699 	me->inCAPTION = FALSE;
6700 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6701 	me->inLABEL = FALSE;
6702 	break;
6703 
6704     case HTML_CREDIT:
6705 	LYEnsureDoubleSpace(me);
6706 	LYResetParagraphAlignment(me);
6707 	me->inCREDIT = FALSE;
6708 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6709 	me->inLABEL = FALSE;
6710 	break;
6711 
6712     case HTML_FORM:
6713 	/*
6714 	 * Check if we had a FORM start tag, and issue a message if not, but
6715 	 * fall through to check for an open SELECT and ensure that the
6716 	 * FORM-related globals in GridText.c are initialized.  - FM
6717 	 */
6718 	if (!me->inFORM) {
6719 	    if (LYBadHTML(me)) {
6720 		LYShowBadHTML("Bad HTML: Unmatched FORM end tag\n");
6721 	    }
6722 	}
6723 	EMIT_IFDEF_USE_JUSTIFY_ELTS(form_in_htext = FALSE);
6724 
6725 	/*
6726 	 * Check if we still have a SELECT element open.  FORM may have been
6727 	 * declared SGML_EMPTY in HTMLDTD.c, and in that case SGML_character()
6728 	 * in SGML.c is not able to ensure correct nesting; or it may have
6729 	 * failed to enforce valid nesting.  If a SELECT is open, issue a
6730 	 * message, then call HTML_end_element() directly (with a check in that
6731 	 * to bypass decrementing of the HTML parser's stack) to close the
6732 	 * SELECT.  - kw
6733 	 */
6734 	if (me->inSELECT) {
6735 	    if (LYBadHTML(me)) {
6736 		LYShowBadHTML("Bad HTML: Open SELECT at FORM end. Faking SELECT end tag. *****\n");
6737 	    }
6738 	    if (me->sp->tag_number != HTML_SELECT) {
6739 		SET_SKIP_STACK(HTML_SELECT);
6740 	    }
6741 	    HTML_end_element(me, HTML_SELECT, include);
6742 	}
6743 
6744 	/*
6745 	 * Set to know that we are no longer in an form.
6746 	 */
6747 	me->inFORM = FALSE;
6748 
6749 	HText_endForm(me->text);
6750 	/*
6751 	 * If we are in a list and are on the first line with no text following
6752 	 * a bullet or number, don't force a newline.  This could happen if we
6753 	 * were called from HTML_start_element() due to a missing FORM end tag.
6754 	 * - FM
6755 	 */
6756 	if (!(me->List_Nesting_Level >= 0 && !me->inP))
6757 	    LYEnsureSingleSpace(me);
6758 	break;
6759 
6760     case HTML_FIELDSET:
6761 	LYEnsureDoubleSpace(me);
6762 	LYResetParagraphAlignment(me);
6763 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6764 	break;
6765 
6766     case HTML_LEGEND:
6767 	LYEnsureDoubleSpace(me);
6768 	LYResetParagraphAlignment(me);
6769 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
6770 	break;
6771 
6772     case HTML_LABEL:
6773 	break;
6774 
6775     case HTML_BUTTON:
6776 	break;
6777 
6778     case HTML_TEXTAREA:
6779 	{
6780 	    InputFieldData I;
6781 	    int chars;
6782 	    char *data;
6783 
6784 	    /*
6785 	     * Make sure we had a textarea start tag.
6786 	     */
6787 	    if (!me->inTEXTAREA) {
6788 		if (LYBadHTML(me)) {
6789 		    LYShowBadHTML("Bad HTML: Unmatched TEXTAREA end tag\n");
6790 		}
6791 		break;
6792 	    }
6793 
6794 	    /*
6795 	     * Set to know that we are no longer in a textarea tag.
6796 	     */
6797 	    me->inTEXTAREA = FALSE;
6798 
6799 	    /*
6800 	     * Initialize.
6801 	     */
6802 	    memset(&I, 0, sizeof(I));
6803 	    I.value_cs = current_char_set;
6804 
6805 	    UPDATE_STYLE;
6806 	    /*
6807 	     * Before any input field add a space if necessary.
6808 	     */
6809 	    HTML_put_character(me, ' ');
6810 	    me->in_word = NO;
6811 	    /*
6812 	     * Add a return.
6813 	     */
6814 	    HText_appendCharacter(me->text, '\r');
6815 
6816 	    /*
6817 	     * Finish the data off.
6818 	     */
6819 	    HTChunkTerminate(&me->textarea);
6820 	    FREE(temp);
6821 
6822 	    I.type = "textarea";
6823 	    I.size = me->textarea_cols;
6824 	    I.name = me->textarea_name;
6825 	    I.name_cs = me->textarea_name_cs;
6826 	    I.accept_cs = me->textarea_accept_cs;
6827 	    me->textarea_accept_cs = NULL;
6828 	    I.disabled = me->textarea_disabled;
6829 	    I.readonly = me->textarea_readonly;
6830 	    I.id = me->textarea_id;
6831 
6832 	    /*
6833 	     * Transform the TEXTAREA content as needed, then parse it into
6834 	     * individual lines to be handled as a series series of INPUT
6835 	     * fields (ugh!).  Any raw 8-bit or multibyte characters already
6836 	     * have been handled in relation to the display character set in
6837 	     * SGML_character().
6838 	     *
6839 	     * If TEXTAREA is handled as SGML_LITTERAL (the old way), we need
6840 	     * to SGML-unescape any character references and NCRs here.
6841 	     * Otherwise this will already have happened in the SGML.c parsing.
6842 	     * - kw
6843 	     */
6844 	    me->UsePlainSpace = TRUE;
6845 
6846 	    if (HTML_dtd.tags[element_number].contents == SGML_LITTERAL) {
6847 		TRANSLATE_AND_UNESCAPE_ENTITIES6(&me->textarea.data,
6848 						 me->UCLYhndl,
6849 						 current_char_set,
6850 						 NO,
6851 						 me->UsePlainSpace, me->HiddenValue);
6852 	    } else {
6853 		/*
6854 		 * This shouldn't have anything to do, normally, but just in
6855 		 * case...  There shouldn't be lynx special character codes in
6856 		 * the chunk ("DTD" flag Tgf_nolyspcl tells SGML.c not to
6857 		 * generate them).  If there were, we could set the last
6858 		 * parameter ('Back') below to YES, which would take them out
6859 		 * of the data.  The data may however contain non break space,
6860 		 * soft hyphen, or en space etc., in the me->UCLYhndl character
6861 		 * encoding.  If that's a problem, perhaps for the (line or
6862 		 * other) editor, setting 'Back' to YES should also help to
6863 		 * always convert them to plain spaces (or drop them).  - kw
6864 		 */
6865 		TRANSLATE_HTML7(&me->textarea.data,
6866 				me->UCLYhndl,
6867 				current_char_set,
6868 				NO,
6869 				me->UsePlainSpace, me->HiddenValue,
6870 				NO);
6871 	    }
6872 	    data = me->textarea.data;
6873 
6874 	    /*
6875 	     * Trim any trailing newlines and skip any lead newlines.  - FM
6876 	     */
6877 	    if (*data != '\0') {
6878 		cp = (data + strlen(data)) - 1;
6879 		while (cp >= data && *cp == '\n') {
6880 		    *cp-- = '\0';
6881 		}
6882 		while (*data == '\n') {
6883 		    data++;
6884 		}
6885 	    }
6886 	    /*
6887 	     * Load the first text line, or set up for all blank rows.  - FM
6888 	     */
6889 	    if ((cp = strchr(data, '\n')) != NULL) {
6890 		*cp = '\0';
6891 		StrAllocCopy(temp, data);
6892 		*cp = '\n';
6893 		data = (cp + 1);
6894 	    } else {
6895 		if (*data != '\0') {
6896 		    StrAllocCopy(temp, data);
6897 		} else {
6898 		    FREE(temp);
6899 		}
6900 		data = empty;
6901 	    }
6902 	    /*
6903 	     * Display at least the requested number of text lines and/or blank
6904 	     * rows.  - FM
6905 	     */
6906 	    for (i = 0; i < me->textarea_rows; i++) {
6907 		int j;
6908 
6909 		for (j = 0; temp && temp[j]; j++) {
6910 		    if (temp[j] == '\r')
6911 			temp[j] = (char) (temp[j + 1] ? ' ' : '\0');
6912 		}
6913 		I.value = temp;
6914 		chars = HText_beginInput(me->text, me->inUnderline, &I);
6915 		for (; chars > 0; chars--)
6916 		    HTML_put_character(me, '_');
6917 		HText_appendCharacter(me->text, '\r');
6918 		if (*data != '\0') {
6919 		    if (*data == '\n') {
6920 			FREE(temp);
6921 			data++;
6922 		    } else if ((cp = strchr(data, '\n')) != NULL) {
6923 			*cp = '\0';
6924 			StrAllocCopy(temp, data);
6925 			*cp = '\n';
6926 			data = (cp + 1);
6927 		    } else {
6928 			StrAllocCopy(temp, data);
6929 			data = empty;
6930 		    }
6931 		} else {
6932 		    FREE(temp);
6933 		}
6934 	    }
6935 	    /*
6936 	     * Check for more data lines than the rows attribute.  We add them
6937 	     * to the display, because we support only horizontal and not also
6938 	     * vertical scrolling.  - FM
6939 	     */
6940 	    while (*data != '\0' || temp != NULL) {
6941 		int j;
6942 
6943 		for (j = 0; temp && temp[j]; j++) {
6944 		    if (temp[j] == '\r')
6945 			temp[j] = (char) (temp[j + 1] ? ' ' : '\0');
6946 		}
6947 		I.value = temp;
6948 		(void) HText_beginInput(me->text, me->inUnderline, &I);
6949 		for (chars = me->textarea_cols; chars > 0; chars--)
6950 		    HTML_put_character(me, '_');
6951 		HText_appendCharacter(me->text, '\r');
6952 		if (*data == '\n') {
6953 		    FREE(temp);
6954 		    data++;
6955 		} else if ((cp = strchr(data, '\n')) != NULL) {
6956 		    *cp = '\0';
6957 		    StrAllocCopy(temp, data);
6958 		    *cp = '\n';
6959 		    data = (cp + 1);
6960 		} else if (*data != '\0') {
6961 		    StrAllocCopy(temp, data);
6962 		    data = empty;
6963 		} else {
6964 		    FREE(temp);
6965 		}
6966 	    }
6967 	    FREE(temp);
6968 	    cp = NULL;
6969 	    me->UsePlainSpace = FALSE;
6970 
6971 	    HTChunkClear(&me->textarea);
6972 	    FREE(me->textarea_name);
6973 	    me->textarea_name_cs = -1;
6974 	    FREE(me->textarea_id);
6975 	    break;
6976 	}
6977 
6978     case HTML_SELECT:
6979 	{
6980 	    char *ptr = NULL;
6981 
6982 	    /*
6983 	     * Make sure we had a select start tag.
6984 	     */
6985 	    if (!me->inSELECT) {
6986 		if (LYBadHTML(me)) {
6987 		    LYShowBadHTML("Bad HTML: Unmatched SELECT end tag *****\n");
6988 		}
6989 		break;
6990 	    }
6991 
6992 	    /*
6993 	     * Set to know that we are no longer in a select tag.
6994 	     */
6995 	    me->inSELECT = FALSE;
6996 
6997 	    /*
6998 	     * Clear the disable attribute.
6999 	     */
7000 	    me->select_disabled = FALSE;
7001 
7002 	    /*
7003 	     * Make sure we're in a form.
7004 	     */
7005 	    if (!me->inFORM) {
7006 		if (LYBadHTML(me)) {
7007 		    LYShowBadHTML("Bad HTML: SELECT end tag not within FORM element *****\n");
7008 		}
7009 		/*
7010 		 * Hopefully won't crash, so we'll ignore it.  - kw
7011 		 */
7012 	    }
7013 
7014 	    /*
7015 	     * Finish the data off.
7016 	     */
7017 	    HTChunkTerminate(&me->option);
7018 	    /*
7019 	     * Finish the previous option.
7020 	     */
7021 	    if (!me->first_option)
7022 		ptr = HText_setLastOptionValue(me->text,
7023 					       me->option.data,
7024 					       me->LastOptionValue,
7025 					       LAST_ORDER,
7026 					       me->LastOptionChecked,
7027 					       me->UCLYhndl,
7028 					       ATTR_CS_IN);
7029 	    FREE(me->LastOptionValue);
7030 
7031 	    me->LastOptionChecked = FALSE;
7032 
7033 	    if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
7034 		LYSelectPopups == FALSE) {
7035 		/*
7036 		 * Start a newline after the last checkbox/button option.
7037 		 */
7038 		LYEnsureSingleSpace(me);
7039 	    } else {
7040 		/*
7041 		 * Output popup box with the default option to screen, but use
7042 		 * non-breaking spaces for output.
7043 		 */
7044 		if (ptr &&
7045 		    (me->sp[0].tag_number == HTML_PRE || me->inPRE == TRUE ||
7046 		     !me->sp->style->freeFormat) &&
7047 		    strlen(ptr) > 6) {
7048 		    /*
7049 		     * The code inadequately handles OPTION fields in PRE tags.
7050 		     * We'll put up a minimum of 6 characters, and if any more
7051 		     * would exceed the wrap column, we'll ignore them.
7052 		     */
7053 		    for (i = 0; i < 6; i++) {
7054 			if (*ptr == ' ')
7055 			    HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
7056 			else
7057 			    HText_appendCharacter(me->text, *ptr);
7058 			ptr++;
7059 		    }
7060 		}
7061 		for (; non_empty(ptr); ptr++) {
7062 		    if (*ptr == ' ')
7063 			HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
7064 		    else {
7065 			HTkcode kcode = NOKANJI;
7066 			HTkcode specified_kcode = NOKANJI;
7067 
7068 			if (HTCJK == JAPANESE) {
7069 			    kcode = HText_getKcode(me->text);
7070 			    HText_updateKcode(me->text, kanji_code);
7071 			    specified_kcode = HText_getSpecifiedKcode(me->text);
7072 			    HText_updateSpecifiedKcode(me->text, kanji_code);
7073 			}
7074 			HText_appendCharacter(me->text, *ptr);
7075 			if (HTCJK == JAPANESE) {
7076 			    HText_updateKcode(me->text, kcode);
7077 			    HText_updateSpecifiedKcode(me->text, specified_kcode);
7078 			}
7079 		    }
7080 		}
7081 		/*
7082 		 * Add end option character.
7083 		 */
7084 		if (!me->first_option) {
7085 		    HText_appendCharacter(me->text, ']');
7086 		    HText_endInput(me->text);
7087 		    HText_setLastChar(me->text, ']');
7088 		    me->in_word = YES;
7089 		}
7090 	    }
7091 	    HTChunkClear(&me->option);
7092 
7093 	    if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
7094 		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
7095 		me->inUnderline = TRUE;
7096 	    }
7097 	    if (me->needBoldH == TRUE && me->inBoldH == FALSE) {
7098 		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
7099 		me->inBoldH = TRUE;
7100 		me->needBoldH = FALSE;
7101 	    }
7102 	}
7103 	break;
7104 
7105     case HTML_TABLE:
7106 #ifdef EXP_NESTED_TABLES
7107 	if (!nested_tables)
7108 #endif
7109 	    me->inTABLE = FALSE;
7110 
7111 	if (me->sp->style->id == ST_Preformatted) {
7112 	    break;
7113 	}
7114 	if (me->Division_Level >= 0)
7115 	    me->Division_Level--;
7116 	if (me->Division_Level >= 0)
7117 	    me->sp->style->alignment =
7118 		me->DivisionAlignments[me->Division_Level];
7119 	change_paragraph_style(me, me->sp->style);
7120 	UPDATE_STYLE;
7121 
7122 #ifdef EXP_NESTED_TABLES
7123 	if (nested_tables) {
7124 	    me->inTABLE = HText_endStblTABLE(me->text);
7125 	} else {
7126 	    HText_endStblTABLE(me->text);
7127 	}
7128 #else
7129 	HText_endStblTABLE(me->text);
7130 #endif
7131 
7132 	me->current_default_alignment = me->sp->style->alignment;
7133 	if (me->List_Nesting_Level >= 0)
7134 	    HText_NegateLineOne(me->text);
7135 	break;
7136 
7137 /* These TABLE related elements may now not be SGML_EMPTY. - kw */
7138     case HTML_TR:
7139 	HText_endStblTR(me->text);
7140 	if (!HText_LastLineEmpty(me->text, FALSE)) {
7141 	    HText_setLastChar(me->text, ' ');	/* absorb next white space */
7142 	    HText_appendCharacter(me->text, '\r');
7143 	}
7144 	me->in_word = NO;
7145 	break;
7146 
7147     case HTML_THEAD:
7148     case HTML_TFOOT:
7149     case HTML_TBODY:
7150 	break;
7151 
7152     case HTML_COLGROUP:
7153 	if (me->inTABLE)
7154 	    HText_endStblCOLGROUP(me->text);
7155 	break;
7156 
7157     case HTML_TH:
7158     case HTML_TD:
7159 	HText_endStblTD(me->text);
7160 	break;
7161 
7162 /* More stuff that may now not be SGML_EMPTY any more: */
7163     case HTML_DT:
7164     case HTML_DD:
7165     case HTML_LH:
7166     case HTML_LI:
7167     case HTML_OVERLAY:
7168 	break;
7169 
7170     case HTML_MATH:
7171 	/*
7172 	 * We're getting it as Literal text, which, until we can process it,
7173 	 * we'll display as is, within brackets to alert the user.  - FM
7174 	 */
7175 	HTChunkPutc(&me->math, ' ');
7176 	HTChunkTerminate(&me->math);
7177 	if (me->math.size > 2) {
7178 	    LYEnsureSingleSpace(me);
7179 	    if (me->inUnderline == FALSE)
7180 		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
7181 	    HTML_put_string(me, "[MATH:");
7182 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
7183 	    HTML_put_character(me, ' ');
7184 	    HTML_put_string(me, me->math.data);
7185 	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
7186 	    HTML_put_string(me, ":MATH]");
7187 	    if (me->inUnderline == FALSE)
7188 		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
7189 	    LYEnsureSingleSpace(me);
7190 	}
7191 	HTChunkClear(&me->math);
7192 	break;
7193 
7194     default:
7195 	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
7196 	break;
7197 
7198     }				/* switch */
7199 
7200 #ifdef USE_JUSTIFY_ELTS
7201     if (reached_awaited_stacked_elt)
7202 	wait_for_this_stacked_elt = -1;
7203 #endif
7204 
7205     if (me->xinclude) {
7206 	HText_appendText(me->text, " *** LYNX ERROR ***\rUnparsed data:\r");
7207 	HText_appendText(me->text, me->xinclude);
7208 	FREE(me->xinclude);
7209     }
7210 #ifdef USE_COLOR_STYLE
7211     if (!skip_stack_requested) {	/*don't emit stylechanges if skipped stack element - VH */
7212 # if !OMIT_SCN_KEEPING
7213 	FastTrimColorClass(HTML_dtd.tags[element_number].name,
7214 			   HTML_dtd.tags[element_number].name_len,
7215 			   Style_className,
7216 			   &Style_className_end, &hcode);
7217 #  endif
7218 
7219 	if (!ReallyEmptyTagNum(element_number)) {
7220 	    CTRACE2(TRACE_STYLE,
7221 		    (tfp,
7222 		     "STYLE.end_element: ending non-\"EMPTY\" style <%s...>\n",
7223 		     HTML_dtd.tags[element_number].name));
7224 	    HText_characterStyle(me->text, HCODE_TO_STACK_OFF(hcode), STACK_OFF);
7225 	}
7226     }
7227 #endif /* USE_COLOR_STYLE */
7228     return status;
7229 }
7230 
7231 /*		Expanding entities
7232  *		------------------
7233  */
7234 /*	(In fact, they all shrink!)
7235 */
HTML_put_entity(HTStructured * me,int entity_number)7236 int HTML_put_entity(HTStructured * me, int entity_number)
7237 {
7238     int nent = (int) HTML_dtd.number_of_entities;
7239 
7240     if (entity_number < nent) {
7241 	HTML_put_string(me, p_entity_values[entity_number]);
7242 	return HT_OK;
7243     }
7244     return HT_CANNOT_TRANSLATE;
7245 }
7246 
7247 /*	Free an HTML object
7248  *	-------------------
7249  *
7250  *	If the document is empty, the text object will not yet exist.
7251  *	So we could in fact abandon creating the document and return
7252  *	an error code.	In fact an empty document is an important type
7253  *	of document, so we don't.
7254  *
7255  *	If non-interactive, everything is freed off.   No: crashes -listrefs
7256  *	Otherwise, the interactive object is left.
7257  */
HTML_free(HTStructured * me)7258 static void HTML_free(HTStructured * me)
7259 {
7260     char *include = NULL;
7261 
7262     if (LYMapsOnly && !me->text) {
7263 	/*
7264 	 * We only handled MAP, AREA and BASE tags, and didn't create an HText
7265 	 * structure for the document nor want one now, so just make sure we
7266 	 * free anything that might have been allocated.  - FM
7267 	 */
7268 	FREE(me->base_href);
7269 	FREE(me->map_address);
7270 	clear_objectdata(me);
7271 	FREE(me->xinclude);
7272 	FREE(me);
7273 	return;
7274     }
7275 
7276     UPDATE_STYLE;		/* Creates empty document here! */
7277     if (me->comment_end)
7278 	HTML_put_string(me, me->comment_end);
7279     if (me->text) {
7280 	/*
7281 	 * Emphasis containers, A, FONT, and FORM may be declared SGML_EMPTY in
7282 	 * HTMLDTD.c, and SGML_character() in SGML.c may check for their end
7283 	 * tags to call HTML_end_element() directly (with a check in that to
7284 	 * bypass decrementing of the HTML parser's stack).  So if we still
7285 	 * have the emphasis (Underline) on, or any open A, FONT, or FORM
7286 	 * containers, turn it off or close them now.  - FM & kw
7287 	 *
7288 	 * IF those tags are not declared SGML_EMPTY, but we let the SGML.c
7289 	 * parser take care of correctly stacked ordering, and of correct
7290 	 * wind-down on end-of-stream (in SGML_free SGML_abort), THEN these and
7291 	 * other checks here in HTML.c should not be necessary.  Still it can't
7292 	 * hurt to include them.  - kw
7293 	 */
7294 	if (me->inUnderline) {
7295 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
7296 	    me->inUnderline = FALSE;
7297 	    me->Underline_Level = 0;
7298 	    CTRACE((tfp, "HTML_free: Ending underline\n"));
7299 	}
7300 	if (me->inA) {
7301 	    HTML_end_element(me, HTML_A, &include);
7302 	    me->inA = FALSE;
7303 	    CTRACE((tfp, "HTML_free: Ending HTML_A\n"));
7304 	}
7305 	if (me->inFONT) {
7306 	    HTML_end_element(me, HTML_FONT, &include);
7307 	    me->inFONT = FALSE;
7308 	}
7309 	if (me->inFORM) {
7310 	    HTML_end_element(me, HTML_FORM, &include);
7311 	    me->inFORM = FALSE;
7312 	}
7313 	if (me->option.size > 0) {
7314 	    /*
7315 	     * If we still have data in the me->option chunk after forcing a
7316 	     * close of a still-open form, something must have gone very wrong.
7317 	     * - kw
7318 	     */
7319 	    if (LYBadHTML(me)) {
7320 		LYShowBadHTML("Bad HTML: SELECT or OPTION not ended properly *****\n");
7321 	    }
7322 	    HTChunkTerminate(&me->option);
7323 	    /*
7324 	     * Output the left-over data as text, maybe it was invalid markup
7325 	     * meant to be shown somewhere.  - kw
7326 	     */
7327 	    CTRACE((tfp, "HTML_free: ***** leftover option data: %s\n",
7328 		    me->option.data));
7329 	    HTML_put_string(me, me->option.data);
7330 	    HTChunkClear(&me->option);
7331 	}
7332 	if (me->textarea.size > 0) {
7333 	    /*
7334 	     * If we still have data in the me->textarea chunk after forcing a
7335 	     * close of a still-open form, something must have gone very wrong.
7336 	     * - kw
7337 	     */
7338 	    if (LYBadHTML(me)) {
7339 		LYShowBadHTML("Bad HTML: TEXTAREA not used properly *****\n");
7340 	    }
7341 	    HTChunkTerminate(&me->textarea);
7342 	    /*
7343 	     * Output the left-over data as text, maybe it was invalid markup
7344 	     * meant to be shown somewhere.  - kw
7345 	     */
7346 	    CTRACE((tfp, "HTML_free: ***** leftover textarea data: %s\n",
7347 		    me->textarea.data));
7348 	    HTML_put_string(me, me->textarea.data);
7349 	    HTChunkClear(&me->textarea);
7350 	}
7351 	/*
7352 	 * If we're interactive and have hidden links but no visible links, add
7353 	 * a message informing the user about this and suggesting use of the
7354 	 * 'l'ist command.  - FM
7355 	 */
7356 	if (!dump_output_immediately &&
7357 	    HText_sourceAnchors(me->text) < 1 &&
7358 	    HText_HiddenLinkCount(me->text) > 0) {
7359 	    HTML_start_element(me, HTML_P, 0, 0, -1, &include);
7360 	    HTML_put_character(me, '[');
7361 	    HTML_start_element(me, HTML_EM, 0, 0, -1, &include);
7362 	    HTML_put_string(me,
7363 			    gettext("Document has only hidden links.  Use the 'l'ist command."));
7364 	    HTML_end_element(me, HTML_EM, &include);
7365 	    HTML_put_character(me, ']');
7366 	    HTML_end_element(me, HTML_P, &include);
7367 	}
7368 	if (me->xinclude) {
7369 	    HText_appendText(me->text, " *** LYNX ERROR ***\rUnparsed data:\r");
7370 	    HText_appendText(me->text, me->xinclude);
7371 	    FREE(me->xinclude);
7372 	}
7373 
7374 	/*
7375 	 * Now call the cleanup function.  - FM
7376 	 */
7377 	HText_endAppend(me->text);
7378     }
7379     if (me->option.size > 0) {
7380 	/*
7381 	 * If we still have data in the me->option chunk after forcing a close
7382 	 * of a still-open form, something must have gone very wrong.  - kw
7383 	 */
7384 	if (LYBadHTML(me)) {
7385 	    LYShowBadHTML("Bad HTML: SELECT or OPTION not ended properly *****\n");
7386 	}
7387 	if (TRACE) {
7388 	    HTChunkTerminate(&me->option);
7389 	    CTRACE((tfp, "HTML_free: ***** leftover option data: %s\n",
7390 		    me->option.data));
7391 	}
7392 	HTChunkClear(&me->option);
7393     }
7394     if (me->textarea.size > 0) {
7395 	/*
7396 	 * If we still have data in the me->textarea chunk after forcing a
7397 	 * close of a still-open form, something must have gone very wrong.  -
7398 	 * kw
7399 	 */
7400 	if (LYBadHTML(me)) {
7401 	    LYShowBadHTML("Bad HTML: TEXTAREA not used properly *****\n");
7402 	}
7403 	if (TRACE) {
7404 	    HTChunkTerminate(&me->textarea);
7405 	    CTRACE((tfp, "HTML_free: ***** leftover textarea data: %s\n",
7406 		    me->textarea.data));
7407 	}
7408 	HTChunkClear(&me->textarea);
7409     }
7410 
7411     if (me->target) {
7412 	(*me->targetClass._free) (me->target);
7413     }
7414     if (me->sp && me->sp->style && GetHTStyleName(me->sp->style)) {
7415 	if (me->sp->style->id == ST_DivCenter ||
7416 	    me->sp->style->id == ST_HeadingCenter ||
7417 	    me->sp->style->id == ST_Heading1) {
7418 	    me->sp->style->alignment = HT_CENTER;
7419 	} else if (me->sp->style->id == ST_DivRight ||
7420 		   me->sp->style->id == ST_HeadingRight) {
7421 	    me->sp->style->alignment = HT_RIGHT;
7422 	} else {
7423 	    me->sp->style->alignment = HT_LEFT;
7424 	}
7425 	styles[HTML_PRE]->alignment = HT_LEFT;
7426     }
7427     FREE(me->base_href);
7428     FREE(me->map_address);
7429     FREE(me->LastOptionValue);
7430     clear_objectdata(me);
7431     FREE(me);
7432 }
7433 
HTML_abort(HTStructured * me,HTError e)7434 static void HTML_abort(HTStructured * me, HTError e)
7435 {
7436     char *include = NULL;
7437 
7438     if (me->text) {
7439 	/*
7440 	 * If we have emphasis on, or open A, FONT, or FORM containers, turn it
7441 	 * off or close them now.  - FM
7442 	 */
7443 	if (me->inUnderline) {
7444 	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
7445 	    me->inUnderline = FALSE;
7446 	    me->Underline_Level = 0;
7447 	}
7448 	if (me->inA) {
7449 	    HTML_end_element(me, HTML_A, &include);
7450 	    me->inA = FALSE;
7451 	}
7452 	if (me->inFONT) {
7453 	    HTML_end_element(me, HTML_FONT, &include);
7454 	    me->inFONT = FALSE;
7455 	}
7456 	if (me->inFORM) {
7457 	    HTML_end_element(me, HTML_FORM, &include);
7458 	    me->inFORM = FALSE;
7459 	}
7460 
7461 	/*
7462 	 * Now call the cleanup function.  - FM
7463 	 */
7464 	HText_endAppend(me->text);
7465     }
7466 
7467     if (me->option.size > 0) {
7468 	/*
7469 	 * If we still have data in the me->option chunk after forcing a close
7470 	 * of a still-open form, something must have gone very wrong.  - kw
7471 	 */
7472 	if (TRACE) {
7473 	    CTRACE((tfp,
7474 		    "HTML_abort: SELECT or OPTION not ended properly *****\n"));
7475 	    HTChunkTerminate(&me->option);
7476 	    CTRACE((tfp, "HTML_abort: ***** leftover option data: %s\n",
7477 		    me->option.data));
7478 	}
7479 	HTChunkClear(&me->option);
7480     }
7481     if (me->textarea.size > 0) {
7482 	/*
7483 	 * If we still have data in the me->textarea chunk after forcing a
7484 	 * close of a still-open form, something must have gone very wrong.  -
7485 	 * kw
7486 	 */
7487 	if (TRACE) {
7488 	    CTRACE((tfp, "HTML_abort: TEXTAREA not used properly *****\n"));
7489 	    HTChunkTerminate(&me->textarea);
7490 	    CTRACE((tfp, "HTML_abort: ***** leftover textarea data: %s\n",
7491 		    me->textarea.data));
7492 	}
7493 	HTChunkClear(&me->textarea);
7494     }
7495 
7496     if (me->target) {
7497 	(*me->targetClass._abort) (me->target, e);
7498     }
7499     if (me->sp && me->sp->style && GetHTStyleName(me->sp->style)) {
7500 	if (me->sp->style->id == ST_DivCenter ||
7501 	    me->sp->style->id == ST_HeadingCenter ||
7502 	    me->sp->style->id == ST_Heading1) {
7503 	    me->sp->style->alignment = HT_CENTER;
7504 	} else if (me->sp->style->id == ST_DivRight ||
7505 		   me->sp->style->id == ST_HeadingRight) {
7506 	    me->sp->style->alignment = HT_RIGHT;
7507 	} else {
7508 	    me->sp->style->alignment = HT_LEFT;
7509 	}
7510 	styles[HTML_PRE]->alignment = HT_LEFT;
7511     }
7512     FREE(me->base_href);
7513     FREE(me->map_address);
7514     FREE(me->textarea_name);
7515     FREE(me->textarea_accept_cs);
7516     FREE(me->textarea_id);
7517     FREE(me->LastOptionValue);
7518     FREE(me->xinclude);
7519     clear_objectdata(me);
7520     FREE(me);
7521 }
7522 
7523 /*	Get Styles from style sheet
7524  *	---------------------------
7525  */
get_styles(void)7526 static void get_styles(void)
7527 {
7528     HTStyle **st = NULL;
7529 
7530     styleSheet = DefaultStyle(&st);	/* sets st[] array */
7531 
7532     default_style = st[ST_Normal];
7533 
7534     styles[HTML_H1] = st[ST_Heading1];
7535     styles[HTML_H2] = st[ST_Heading2];
7536     styles[HTML_H3] = st[ST_Heading3];
7537     styles[HTML_H4] = st[ST_Heading4];
7538     styles[HTML_H5] = st[ST_Heading5];
7539     styles[HTML_H6] = st[ST_Heading6];
7540     styles[HTML_HCENTER] = st[ST_HeadingCenter];
7541     styles[HTML_HLEFT] = st[ST_HeadingLeft];
7542     styles[HTML_HRIGHT] = st[ST_HeadingRight];
7543 
7544     styles[HTML_DCENTER] = st[ST_DivCenter];
7545     styles[HTML_DLEFT] = st[ST_DivLeft];
7546     styles[HTML_DRIGHT] = st[ST_DivRight];
7547 
7548     styles[HTML_DL] = st[ST_Glossary];
7549     /* nested list styles */
7550     styles[HTML_DL1] = st[ST_Glossary1];
7551     styles[HTML_DL2] = st[ST_Glossary2];
7552     styles[HTML_DL3] = st[ST_Glossary3];
7553     styles[HTML_DL4] = st[ST_Glossary4];
7554     styles[HTML_DL5] = st[ST_Glossary5];
7555     styles[HTML_DL6] = st[ST_Glossary6];
7556 
7557     styles[HTML_UL] =
7558 	styles[HTML_OL] = st[ST_List];
7559     /* nested list styles */
7560     styles[HTML_OL1] = st[ST_List1];
7561     styles[HTML_OL2] = st[ST_List2];
7562     styles[HTML_OL3] = st[ST_List3];
7563     styles[HTML_OL4] = st[ST_List4];
7564     styles[HTML_OL5] = st[ST_List5];
7565     styles[HTML_OL6] = st[ST_List6];
7566 
7567     styles[HTML_MENU] =
7568 	styles[HTML_DIR] = st[ST_Menu];
7569     /* nested list styles */
7570     styles[HTML_MENU1] = st[ST_Menu1];
7571     styles[HTML_MENU2] = st[ST_Menu2];
7572     styles[HTML_MENU3] = st[ST_Menu3];
7573     styles[HTML_MENU4] = st[ST_Menu4];
7574     styles[HTML_MENU5] = st[ST_Menu5];
7575     styles[HTML_MENU6] = st[ST_Menu6];
7576 
7577     styles[HTML_DLC] = st[ST_GlossaryCompact];
7578     /* nested list styles */
7579     styles[HTML_DLC1] = st[ST_GlossaryCompact1];
7580     styles[HTML_DLC2] = st[ST_GlossaryCompact2];
7581     styles[HTML_DLC3] = st[ST_GlossaryCompact3];
7582     styles[HTML_DLC4] = st[ST_GlossaryCompact4];
7583     styles[HTML_DLC5] = st[ST_GlossaryCompact5];
7584     styles[HTML_DLC6] = st[ST_GlossaryCompact6];
7585 
7586     styles[HTML_ADDRESS] = st[ST_Address];
7587     styles[HTML_BANNER] = st[ST_Banner];
7588     styles[HTML_BLOCKQUOTE] = st[ST_Blockquote];
7589     styles[HTML_BQ] = st[ST_Bq];
7590     styles[HTML_FN] = st[ST_Footnote];
7591     styles[HTML_NOTE] = st[ST_Note];
7592     styles[HTML_PLAINTEXT] =
7593 	styles[HTML_XMP] = st[ST_Example];
7594     styles[HTML_PRE] = st[ST_Preformatted];
7595     styles[HTML_LISTING] = st[ST_Listing];
7596 }
7597 
7598 /*
7599  * If we're called from another module, make sure we've initialized styles
7600  * array first.
7601  */
LYstyles(int style_number)7602 HTStyle *LYstyles(int style_number)
7603 {
7604     if (styles[style_number] == 0)
7605 	get_styles();
7606     return styles[style_number];
7607 }
7608 
7609 /*				P U B L I C
7610 */
7611 
7612 /*	Structured Object Class
7613  *	-----------------------
7614  */
7615 const HTStructuredClass HTMLPresentation =	/* As opposed to print etc */
7616 {
7617     "Lynx_HTML_Handler",
7618     HTML_free,
7619     HTML_abort,
7620     HTML_put_character, HTML_put_string, HTML_write,
7621     HTML_start_element, HTML_end_element,
7622     HTML_put_entity
7623 };
7624 
7625 /*		New Structured Text object
7626  *		--------------------------
7627  *
7628  *	The structured stream can generate either presentation,
7629  *	or plain text, or HTML.
7630  */
HTML_new(HTParentAnchor * anchor,HTFormat format_out,HTStream * stream)7631 HTStructured *HTML_new(HTParentAnchor *anchor,
7632 		       HTFormat format_out,
7633 		       HTStream *stream)
7634 {
7635 
7636     HTStructured *me;
7637 
7638     CTRACE((tfp, "start HTML_new\n"));
7639 
7640     if (format_out != WWW_PLAINTEXT && format_out != WWW_PRESENT) {
7641 	HTStream *intermediate = HTStreamStack(WWW_HTML, format_out,
7642 					       stream, anchor);
7643 
7644 	if (intermediate)
7645 	    return HTMLGenerator(intermediate);
7646 	fprintf(stderr, "\n** Internal error: can't parse HTML to %s\n",
7647 		HTAtom_name(format_out));
7648 	exit_immediately(EXIT_FAILURE);
7649     }
7650 
7651     me = typecalloc(HTStructured);
7652     if (me == NULL)
7653 	outofmem(__FILE__, "HTML_new");
7654     assert(me != NULL);
7655 
7656     /*
7657      * This used to call 'get_styles()' only on the first time through this
7658      * function.  However, if the user reloads a page with ^R, the styles[]
7659      * array is not necessarily the same as it was from 'get_styles()'.  So
7660      * we reinitialize the whole thing.
7661      */
7662     get_styles();
7663 
7664     me->isa = &HTMLPresentation;
7665     me->node_anchor = anchor;
7666 
7667     me->CurrentA = NULL;
7668     me->CurrentANum = 0;
7669     me->base_href = NULL;
7670     me->map_address = NULL;
7671 
7672     HTChunkInit(&me->title, 128);
7673 
7674     HTChunkInit(&me->object, 128);
7675     me->object_started = FALSE;
7676     me->object_declare = FALSE;
7677     me->object_shapes = FALSE;
7678     me->object_ismap = FALSE;
7679     me->object_id = NULL;
7680     me->object_title = NULL;
7681     me->object_data = NULL;
7682     me->object_type = NULL;
7683     me->object_classid = NULL;
7684     me->object_codebase = NULL;
7685     me->object_codetype = NULL;
7686     me->object_usemap = NULL;
7687     me->object_name = NULL;
7688 
7689     HTChunkInit(&me->option, 128);
7690     me->first_option = TRUE;
7691     me->LastOptionValue = NULL;
7692     me->LastOptionChecked = FALSE;
7693     me->select_disabled = FALSE;
7694 
7695     HTChunkInit(&me->textarea, 128);
7696     me->textarea_name = NULL;
7697     me->textarea_name_cs = -1;
7698     me->textarea_accept_cs = NULL;
7699     me->textarea_cols = 0;
7700     me->textarea_rows = 4;
7701     me->textarea_id = NULL;
7702 
7703     HTChunkInit(&me->math, 128);
7704 
7705     HTChunkInit(&me->style_block, 128);
7706 
7707     HTChunkInit(&me->script, 128);
7708 
7709     me->text = 0;
7710     me->style_change = YES;	/* Force check leading to text creation */
7711     me->new_style = default_style;
7712     me->old_style = 0;
7713     me->current_default_alignment = HT_LEFT;
7714     me->sp = (me->stack + MAX_NESTING - 1);
7715     me->skip_stack = 0;
7716     me->sp->tag_number = -1;	/* INVALID */
7717     me->sp->style = default_style;	/* INVALID */
7718     me->sp->style->alignment = HT_LEFT;
7719     me->stack_overrun = FALSE;
7720 
7721     me->Division_Level = -1;
7722     me->Underline_Level = 0;
7723     me->Quote_Level = 0;
7724 
7725     me->UsePlainSpace = FALSE;
7726     me->HiddenValue = FALSE;
7727     me->lastraw = -1;
7728 
7729     /*
7730      * Used for nested lists.  - FM
7731      */
7732     me->List_Nesting_Level = -1;	/* counter for list nesting level */
7733     LYZero_OL_Counter(me);	/* Initializes OL_Counter[] and OL_Type[] */
7734     me->Last_OL_Count = 0;	/* last count in ordered lists */
7735     me->Last_OL_Type = '1';	/* last type in ordered lists */
7736 
7737     me->inA = FALSE;
7738     me->inAPPLET = FALSE;
7739     me->inAPPLETwithP = FALSE;
7740     me->inBadBASE = FALSE;
7741     me->inBadHREF = FALSE;
7742     me->inBadHTML = FALSE;
7743     me->inBASE = FALSE;
7744     me->node_anchor->inBASE = FALSE;
7745     me->inBoldA = FALSE;
7746     me->inBoldH = FALSE;
7747     me->inCAPTION = FALSE;
7748     me->inCREDIT = FALSE;
7749     me->inFIG = FALSE;
7750     me->inFIGwithP = FALSE;
7751     me->inFONT = FALSE;
7752     me->inFORM = FALSE;
7753     me->inLABEL = FALSE;
7754     me->inP = FALSE;
7755     me->inPRE = FALSE;
7756     me->inSELECT = FALSE;
7757     me->inTABLE = FALSE;
7758     me->inUnderline = FALSE;
7759 
7760     me->needBoldH = FALSE;
7761 
7762     me->comment_start = NULL;
7763     me->comment_end = NULL;
7764 
7765 #ifdef USE_COLOR_STYLE
7766 #ifdef LY_FIND_LEAKS
7767     if (Style_className == 0) {
7768 	atexit(free_Style_className);
7769     }
7770 #endif
7771     addClassName("", "", (size_t) 0);
7772     class_string[0] = '\0';
7773 #endif
7774 
7775     /*
7776      * Create a chartrans stage info structure for the anchor, if it does not
7777      * exist already (in which case the default MIME stage info will be loaded
7778      * as well), and load the HTML stage info into me->UCI and me->UCLYhndl.  -
7779      * FM
7780      */
7781     LYGetChartransInfo(me);
7782     UCTransParams_clear(&me->T);
7783 
7784     /*
7785      * Load the existing or default input charset info into the holding
7786      * elements.  We'll believe what is indicated for UCT_STAGE_PARSER.  - FM
7787      */
7788     me->inUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
7789 					  UCT_STAGE_PARSER);
7790     if (me->inUCLYhndl < 0) {
7791 	me->inUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
7792 					      UCT_STAGE_MIME);
7793 	me->inUCI = HTAnchor_getUCInfoStage(me->node_anchor,
7794 					    UCT_STAGE_MIME);
7795     } else {
7796 	me->inUCI = HTAnchor_getUCInfoStage(me->node_anchor,
7797 					    UCT_STAGE_PARSER);
7798     }
7799 
7800     /*
7801      * Load the existing or default output charset info into the holding
7802      * elements, UCT_STAGE_STRUCTURED should be the same as UCT_STAGE_TEXT at
7803      * this point, but we could check, perhaps.  - FM
7804      */
7805     me->outUCI = HTAnchor_getUCInfoStage(me->node_anchor,
7806 					 UCT_STAGE_STRUCTURED);
7807     me->outUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
7808 					   UCT_STAGE_STRUCTURED);
7809 
7810     me->target = stream;
7811     if (stream)
7812 	me->targetClass = *stream->isa;		/* Copy pointers */
7813 
7814     return (HTStructured *) me;
7815 }
7816 
7817 #ifdef USE_SOURCE_CACHE
7818 
7819 /*
7820  * A flag set by a file write error.  Used for only generating an alert the
7821  * first time such an error happens, since Lynx should still be usable if the
7822  * temp space becomes full, and an alert each time a cache file cannot be
7823  * written would be annoying.  Reset when lynx.cfg is being reloaded (user may
7824  * change SOURCE_CACHE setting).  - kw
7825  */
7826 BOOLEAN source_cache_file_error = FALSE;
7827 
7828 /*
7829  * Pass-thru cache HTStream
7830  */
7831 
CacheThru_do_free(HTStream * me)7832 static void CacheThru_do_free(HTStream *me)
7833 {
7834     if (me->anchor->source_cache_file) {
7835 	CTRACE((tfp, "SourceCacheWriter: Removing previous file %s\n",
7836 		me->anchor->source_cache_file));
7837 	(void) LYRemoveTemp(me->anchor->source_cache_file);
7838 	FREE(me->anchor->source_cache_file);
7839     }
7840     if (me->anchor->source_cache_chunk) {
7841 	CTRACE((tfp, "SourceCacheWriter: Removing previous memory chunk %p\n",
7842 		(void *) me->anchor->source_cache_chunk));
7843 	HTChunkFree(me->anchor->source_cache_chunk);
7844 	me->anchor->source_cache_chunk = NULL;
7845     }
7846     if (me->fp) {
7847 	fflush(me->fp);
7848 	if (ferror(me->fp))
7849 	    me->status = HT_ERROR;
7850 	LYCloseTempFP(me->fp);
7851 	if (me->status == HT_OK) {
7852 	    char *cp_freeme = 0;
7853 
7854 	    me->anchor->source_cache_file = me->filename;
7855 	    CTRACE((tfp,
7856 		    "SourceCacheWriter: Committing file %s for URL %s to anchor\n",
7857 		    me->filename,
7858 		    cp_freeme = HTAnchor_address((HTAnchor *) me->anchor)));
7859 	    FREE(cp_freeme);
7860 	} else {
7861 	    if (source_cache_file_error == FALSE) {
7862 		HTAlert(gettext("Source cache error - disk full?"));
7863 		source_cache_file_error = TRUE;
7864 	    }
7865 	    (void) LYRemoveTemp(me->filename);
7866 	    me->anchor->source_cache_file = NULL;
7867 	}
7868     } else if (me->status != HT_OK) {
7869 	if (me->chunk) {
7870 	    CTRACE((tfp, "SourceCacheWriter: memory chunk %p had errors.\n",
7871 		    (void *) me->chunk));
7872 	    HTChunkFree(me->chunk);
7873 	    me->chunk = me->last_chunk = NULL;
7874 	}
7875 	HTAlert(gettext("Source cache error - not enough memory!"));
7876     }
7877     if (me->chunk) {
7878 	char *cp_freeme = NULL;
7879 
7880 	me->anchor->source_cache_chunk = me->chunk;
7881 	CTRACE((tfp,
7882 		"SourceCacheWriter: Committing memory chunk %p for URL %s to anchor\n",
7883 		(void *) me->chunk,
7884 		cp_freeme = HTAnchor_address((HTAnchor *) me->anchor)));
7885 	FREE(cp_freeme);
7886     }
7887 }
7888 
CacheThru_free(HTStream * me)7889 static void CacheThru_free(HTStream *me)
7890 {
7891     CacheThru_do_free(me);
7892     (*me->actions->_free) (me->target);
7893     FREE(me);
7894 }
7895 
CacheThru_abort(HTStream * me,HTError e)7896 static void CacheThru_abort(HTStream *me, HTError e)
7897 {
7898     if (me->fp)
7899 	LYCloseTempFP(me->fp);
7900     if (LYCacheSourceForAborted == SOURCE_CACHE_FOR_ABORTED_DROP) {
7901 	if (me->filename) {
7902 	    CTRACE((tfp, "SourceCacheWriter: Removing active file %s\n",
7903 		    me->filename));
7904 	    (void) LYRemoveTemp(me->filename);
7905 	    FREE(me->filename);
7906 	}
7907 	if (me->chunk) {
7908 	    CTRACE((tfp,
7909 		    "SourceCacheWriter: Removing active memory chunk %p\n",
7910 		    (void *) me->chunk));
7911 	    HTChunkFree(me->chunk);
7912 	}
7913     } else {
7914 	me->status = HT_OK;	/*fake it */
7915 	CacheThru_do_free(me);
7916     }
7917     (*me->actions->_abort) (me->target, e);
7918     FREE(me);
7919 }
7920 
7921 /*
7922  * FIXME: never used!
7923  */
CacheThru_put_character(HTStream * me,int c_in)7924 static void CacheThru_put_character(HTStream *me, int c_in)
7925 {
7926     if (me->status == HT_OK) {
7927 	if (me->fp) {
7928 	    fputc(c_in, me->fp);
7929 	} else if (me->chunk) {
7930 	    me->last_chunk = HTChunkPutc2(me->last_chunk, c_in);
7931 	    if (me->last_chunk == NULL || me->last_chunk->allocated == 0)
7932 		me->status = HT_ERROR;
7933 	}
7934     }
7935     (*me->actions->put_character) (me->target, c_in);
7936 }
7937 
7938 /*
7939  * FIXME: never used!
7940  */
CacheThru_put_string(HTStream * me,const char * str)7941 static void CacheThru_put_string(HTStream *me, const char *str)
7942 {
7943     if (me->status == HT_OK) {
7944 	if (me->fp) {
7945 	    fputs(str, me->fp);
7946 	} else if (me->chunk) {
7947 	    me->last_chunk = HTChunkPuts2(me->last_chunk, str);
7948 	    if (me->last_chunk == NULL || me->last_chunk->allocated == 0)
7949 		me->status = HT_ERROR;
7950 	}
7951     }
7952     (*me->actions->put_string) (me->target, str);
7953 }
7954 
CacheThru_write(HTStream * me,const char * str,int l)7955 static void CacheThru_write(HTStream *me, const char *str, int l)
7956 {
7957     if (me->status == HT_OK && l != 0) {
7958 	if (me->fp) {
7959 	    if (fwrite(str, (size_t) 1, (size_t) l, me->fp) < (size_t) l
7960 		|| ferror(me->fp)) {
7961 		me->status = HT_ERROR;
7962 	    }
7963 	} else if (me->chunk) {
7964 	    me->last_chunk = HTChunkPutb2(me->last_chunk, str, l);
7965 	    if (me->last_chunk == NULL || me->last_chunk->allocated == 0)
7966 		me->status = HT_ERROR;
7967 	}
7968     }
7969     (*me->actions->put_block) (me->target, str, l);
7970 }
7971 
7972 static const HTStreamClass PassThruCache =
7973 {
7974     "PassThruCache",
7975     CacheThru_free,
7976     CacheThru_abort,
7977     CacheThru_put_character,
7978     CacheThru_put_string,
7979     CacheThru_write
7980 };
7981 
CacheThru_new(HTParentAnchor * anchor,HTStream * target)7982 static HTStream *CacheThru_new(HTParentAnchor *anchor,
7983 			       HTStream *target)
7984 {
7985     char *cp_freeme = NULL;
7986     char filename[LY_MAXPATH];
7987     HTStream *stream = NULL;
7988     HTProtocol *p = (HTProtocol *) anchor->protocol;
7989 
7990     /*
7991      * Neatly and transparently vanish if source caching is disabled.
7992      */
7993     if (LYCacheSource == SOURCE_CACHE_NONE)
7994 	return target;
7995 
7996 #ifndef DEBUG_SOURCE_CACHE
7997     /*  Only remote HTML documents may benefit from HTreparse_document(),  */
7998     /*  oh, assume http protocol:                                          */
7999     if (strcmp(p->name, "http") != 0
8000 	&& strcmp(p->name, "https") != 0) {
8001 	CTRACE((tfp, "SourceCacheWriter: Protocol is \"%s\"; not cached\n", p->name));
8002 	return target;
8003     }
8004 #else
8005     /* all HTStreams will be cached */
8006 #endif
8007 
8008     CTRACE((tfp, "start CacheThru_new\n"));
8009 
8010     stream = (HTStream *) malloc(sizeof(*stream));
8011     if (!stream)
8012 	outofmem(__FILE__, "CacheThru_new");
8013 
8014     assert(stream != NULL);
8015 
8016     stream->isa = &PassThruCache;
8017     stream->anchor = anchor;
8018     stream->fp = NULL;
8019     stream->filename = NULL;
8020     stream->chunk = NULL;
8021     stream->target = target;
8022     stream->actions = target->isa;
8023     stream->status = HT_OK;
8024 
8025     if (LYCacheSource == SOURCE_CACHE_FILE) {
8026 
8027 	if (anchor->source_cache_file) {
8028 	    CTRACE((tfp,
8029 		    "SourceCacheWriter: If successful, will replace source cache file %s\n",
8030 		    anchor->source_cache_file));
8031 	}
8032 
8033 	/*
8034 	 * We open the temp file in binary mode to make sure that
8035 	 * end-of-line stuff and high-bit Latin-1 (or other) characters
8036 	 * don't get munged; this way, the file should (knock on wood)
8037 	 * contain exactly what came in from the network.
8038 	 */
8039 	if (!(stream->fp = LYOpenTemp(filename, HTML_SUFFIX, BIN_W))) {
8040 	    CTRACE((tfp,
8041 		    "SourceCacheWriter: Cannot open source cache file for URL %s\n",
8042 		    cp_freeme = HTAnchor_address((HTAnchor *) anchor)));
8043 	    FREE(stream);
8044 	    FREE(cp_freeme);
8045 	    return target;
8046 	}
8047 
8048 	StrAllocCopy(stream->filename, filename);
8049 
8050 	CTRACE((tfp,
8051 		"SourceCacheWriter: Caching source for URL %s in file %s\n",
8052 		cp_freeme = HTAnchor_address((HTAnchor *) anchor),
8053 		filename));
8054 	FREE(cp_freeme);
8055     }
8056 
8057     if (LYCacheSource == SOURCE_CACHE_MEMORY) {
8058 	if (anchor->source_cache_chunk) {
8059 	    CTRACE((tfp,
8060 		    "SourceCacheWriter: If successful, will replace memory chunk %p\n",
8061 		    (void *) anchor->source_cache_chunk));
8062 	}
8063 	stream->chunk = stream->last_chunk = HTChunkCreateMayFail(4096, 1);
8064 	if (!stream->chunk)	/* failed already? pretty bad... - kw */
8065 	    stream->status = HT_ERROR;
8066 
8067 	CTRACE((tfp,
8068 		"SourceCacheWriter: Caching source for URL %s in memory chunk %p\n",
8069 		cp_freeme = HTAnchor_address((HTAnchor *) anchor),
8070 		(void *) stream->chunk));
8071 	FREE(cp_freeme);
8072     }
8073 
8074     return stream;
8075 }
8076 #else
8077 #define CacheThru_new(anchor, target) target
8078 #endif
8079 
8080 /*	HTConverter for HTML to plain text
8081  *	----------------------------------
8082  *
8083  *	This will convert from HTML to presentation or plain text.
8084  *
8085  *	It is registered in HTInit.c, but never actually used by lynx.
8086  *	- kw 1999-03-15
8087  */
HTMLToPlain(HTPresentation * pres,HTParentAnchor * anchor,HTStream * sink)8088 HTStream *HTMLToPlain(HTPresentation *pres,
8089 		      HTParentAnchor *anchor,
8090 		      HTStream *sink)
8091 {
8092     CTRACE((tfp, "HTMLToPlain calling CacheThru_new\n"));
8093     return CacheThru_new(anchor,
8094 			 SGML_new(&HTML_dtd, anchor,
8095 				  HTML_new(anchor, pres->rep_out, sink)));
8096 }
8097 
8098 /*	HTConverter for HTML source to plain text
8099  *	-----------------------------------------
8100  *
8101  *	This will preparse HTML and convert back to presentation or plain text.
8102  *
8103  *	It is registered in HTInit.c and used by lynx if invoked with
8104  *	-preparsed.  The stream generated here will be fed with HTML text,
8105  *	It feeds that to the SGML.c parser, which in turn feeds an HTMLGen.c
8106  *	structured stream for regenerating flat text; the latter should
8107  *	end up being handled as text/plain. - kw
8108  */
HTMLParsedPresent(HTPresentation * pres,HTParentAnchor * anchor,HTStream * sink)8109 HTStream *HTMLParsedPresent(HTPresentation *pres,
8110 			    HTParentAnchor *anchor,
8111 			    HTStream *sink)
8112 {
8113     HTStream *intermediate = sink;
8114 
8115     if (!intermediate) {
8116 	/*
8117 	 * Trick to prevent HTPlainPresent from translating again.  Temporarily
8118 	 * change UCT_STAGE_PARSER setting in anchor while the HTPlain stream
8119 	 * is initialized, so that HTPlain sees its input and output charsets
8120 	 * as the same.  - kw
8121 	 */
8122 	int old_parser_cset = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_PARSER);
8123 	int structured_cset = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_STRUCTURED);
8124 
8125 	if (structured_cset < 0)
8126 	    structured_cset = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_HTEXT);
8127 	if (structured_cset < 0)
8128 	    structured_cset = current_char_set;
8129 	HTAnchor_setUCInfoStage(anchor, structured_cset,
8130 				UCT_STAGE_PARSER, UCT_SETBY_MIME);
8131 	if (pres->rep_out == WWW_SOURCE) {
8132 	    /*  same effect as
8133 	       intermediate = HTPlainPresent(pres, anchor, NULL);
8134 	       just written in a more general way:
8135 	     */
8136 	    intermediate = HTStreamStack(WWW_PLAINTEXT, WWW_PRESENT,
8137 					 NULL, anchor);
8138 	} else {
8139 	    /*  this too should amount to calling HTPlainPresent: */
8140 	    intermediate = HTStreamStack(WWW_PLAINTEXT, pres->rep_out,
8141 					 NULL, anchor);
8142 	}
8143 	if (old_parser_cset != structured_cset) {
8144 	    HTAnchor_resetUCInfoStage(anchor, old_parser_cset,
8145 				      UCT_STAGE_PARSER, UCT_SETBY_NONE);
8146 	    if (old_parser_cset >= 0) {
8147 		HTAnchor_setUCInfoStage(anchor, old_parser_cset,
8148 					UCT_STAGE_PARSER,
8149 					UCT_SETBY_DEFAULT + 1);
8150 	    }
8151 	}
8152     }
8153     if (!intermediate)
8154 	return NULL;
8155     CTRACE((tfp, "HTMLParsedPresent calling CacheThru_new\n"));
8156     return CacheThru_new(anchor,
8157 			 SGML_new(&HTML_dtd, anchor,
8158 				  HTMLGenerator(intermediate)));
8159 }
8160 
8161 /*	HTConverter for HTML to C code
8162  *	------------------------------
8163  *
8164  *	C code is like plain text but all non-preformatted code
8165  *	is commented out.
8166  *	This will convert from HTML to presentation or plain text.
8167  *
8168  *	It is registered in HTInit.c, but normally not used by lynx.
8169  *	- kw 1999-03-15
8170  */
HTMLToC(HTPresentation * pres GCC_UNUSED,HTParentAnchor * anchor,HTStream * sink)8171 HTStream *HTMLToC(HTPresentation *pres GCC_UNUSED,
8172 		  HTParentAnchor *anchor,
8173 		  HTStream *sink)
8174 {
8175     HTStructured *html;
8176 
8177     if (sink)
8178 	(*sink->isa->put_string) (sink, "/* ");		/* Before even title */
8179     html = HTML_new(anchor, WWW_PLAINTEXT, sink);
8180     html->comment_start = "/* ";
8181     html->comment_end = " */\n";	/* Must start in col 1 for cpp */
8182     if (!sink)
8183 	HTML_put_string(html, html->comment_start);
8184     CTRACE((tfp, "HTMLToC calling CacheThru_new\n"));
8185     return CacheThru_new(anchor,
8186 			 SGML_new(&HTML_dtd, anchor, html));
8187 }
8188 
8189 /*	Presenter for HTML
8190  *	------------------
8191  *
8192  *	This will convert from HTML to presentation or plain text.
8193  *
8194  * (Comment from original libwww:)
8195  *	Override this if you have a windows version
8196  */
8197 #ifndef GUI
HTMLPresent(HTPresentation * pres GCC_UNUSED,HTParentAnchor * anchor,HTStream * sink GCC_UNUSED)8198 HTStream *HTMLPresent(HTPresentation *pres GCC_UNUSED,
8199 		      HTParentAnchor *anchor,
8200 		      HTStream *sink GCC_UNUSED)
8201 {
8202     CTRACE((tfp, "HTMLPresent calling CacheThru_new\n"));
8203     return CacheThru_new(anchor,
8204 			 SGML_new(&HTML_dtd, anchor,
8205 				  HTML_new(anchor, WWW_PRESENT, NULL)));
8206 }
8207 #endif /* !GUI */
8208 
8209 /* (Comments from original libwww:) */
8210 /*	Record error message as a hypertext object
8211  *	------------------------------------------
8212  *
8213  *	The error message should be marked as an error so that
8214  *	it can be reloaded later.
8215  *	This implementation just throws up an error message
8216  *	and leaves the document unloaded.
8217  *	A smarter implementation would load an error document,
8218  *	marking at such so that it is retried on reload.
8219  *
8220  * On entry,
8221  *	sink	is a stream to the output device if any
8222  *	number	is the HTTP error number
8223  *	message is the human readable message.
8224  *
8225  * On exit,
8226  *	returns a negative number to indicate lack of success in the load.
8227  */
8228 /* (We don't actually do any of that hypertext stuff for errors,
8229    the trivial implementation for lynx just generates a message
8230    and returns. - kw 1999-03-15)
8231 */
HTLoadError(HTStream * sink GCC_UNUSED,int number,const char * message)8232 int HTLoadError(HTStream *sink GCC_UNUSED, int number,
8233 		const char *message)
8234 {
8235     HTAlert(message);		/* @@@@@@@@@@@@@@@@@@@ */
8236     return -number;
8237 }
8238 
MakeNewTitle(STRING2PTR value,int src_type)8239 static char *MakeNewTitle(STRING2PTR value, int src_type)
8240 {
8241     char *ptr;
8242     char *newtitle = NULL;
8243 
8244     StrAllocCopy(newtitle, "[");
8245     if (value != 0 && value[src_type] != 0) {
8246 	ptr = strrchr(value[src_type], '/');
8247 	if (!ptr) {
8248 	    StrAllocCat(newtitle, value[src_type]);
8249 	} else {
8250 	    StrAllocCat(newtitle, ptr + 1);
8251 	}
8252     } else {
8253 	ptr = 0;
8254     }
8255 #ifdef SH_EX			/* 1998/04/02 (Thu) 16:02:00 */
8256 
8257     /* for proxy server 1998/12/19 (Sat) 11:53:30 */
8258     if (AS_casecomp(newtitle + 1, "internal-gopher-menu") == 0) {
8259 	StrAllocCopy(newtitle, "+");
8260     } else if (AS_casecomp(newtitle + 1, "internal-gopher-unknown") == 0) {
8261 	StrAllocCopy(newtitle, " ");
8262     } else {
8263 	/* normal title */
8264 	ptr = strrchr(newtitle, '.');
8265 	if (ptr) {
8266 	    if (AS_casecomp(ptr, ".gif") == 0)
8267 		*ptr = '\0';
8268 	    else if (AS_casecomp(ptr, ".jpg") == 0)
8269 		*ptr = '\0';
8270 	    else if (AS_casecomp(ptr, ".jpeg") == 0)
8271 		*ptr = '\0';
8272 	}
8273 	StrAllocCat(newtitle, "]");
8274     }
8275 #else
8276     StrAllocCat(newtitle, "]");
8277 #endif
8278     return newtitle;
8279 }
8280 
MakeNewImageValue(STRING2PTR value)8281 static char *MakeNewImageValue(STRING2PTR value)
8282 {
8283     char *ptr;
8284     char *newtitle = NULL;
8285 
8286     StrAllocCopy(newtitle, "[");
8287     ptr = (value[HTML_INPUT_SRC]
8288 	   ? strrchr(value[HTML_INPUT_SRC], '/')
8289 	   : 0);
8290     if (!ptr) {
8291 	StrAllocCat(newtitle, value[HTML_INPUT_SRC]);
8292     } else {
8293 	StrAllocCat(newtitle, ptr + 1);
8294     }
8295     StrAllocCat(newtitle, "]-Submit");
8296     return newtitle;
8297 }
8298 
MakeNewMapValue(STRING2PTR value,const char * mapstr)8299 static char *MakeNewMapValue(STRING2PTR value, const char *mapstr)
8300 {
8301     char *ptr;
8302     char *newtitle = NULL;
8303 
8304     StrAllocCopy(newtitle, "[");
8305     StrAllocCat(newtitle, mapstr);	/* ISMAP or USEMAP */
8306     if (verbose_img && non_empty(value[HTML_IMG_SRC])) {
8307 	StrAllocCat(newtitle, ":");
8308 	ptr = strrchr(value[HTML_IMG_SRC], '/');
8309 	if (!ptr) {
8310 	    StrAllocCat(newtitle, value[HTML_IMG_SRC]);
8311 	} else {
8312 	    StrAllocCat(newtitle, ptr + 1);
8313 	}
8314     }
8315     StrAllocCat(newtitle, "]");
8316     return newtitle;
8317 }
8318