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(""");
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> ");
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