1 /*
2  * $LynxId: HTMLGen.c,v 1.39 2013/05/03 20:53:46 tom Exp $
3  *
4  *		HTML Generator
5  *		==============
6  *
7  *	This version of the HTML object sends HTML markup to the output stream.
8  *
9  * Bugs:	Line wrapping is not done at all.
10  *		All data handled as PCDATA.
11  *		Should convert old XMP, LISTING and PLAINTEXT to PRE.
12  *
13  *	It is not obvious to me right now whether the HEAD should be generated
14  *	from the incomming data or the anchor.	Currently it is from the former
15  *	which is cleanest.
16  */
17 
18 #define HTSTREAM_INTERNAL 1
19 
20 #include <HTUtils.h>
21 
22 #define BUFFER_SIZE    200	/* Line buffer attempts to make neat breaks */
23 #define MAX_CLEANNESS	20
24 
25 /* Implements:
26 */
27 #include <HTMLGen.h>
28 
29 #include <HTMLDTD.h>
30 #include <HTStream.h>
31 #include <SGML.h>
32 #include <HTFormat.h>
33 
34 #ifdef USE_COLOR_STYLE
35 #include <LYCharUtils.h>
36 #include <AttrList.h>
37 #include <LYHash.h>
38 #include <LYStyle.h>
39 #endif
40 
41 #include <LYGlobalDefs.h>
42 #include <LYCurses.h>
43 #include <LYLeaks.h>
44 
45 #ifdef USE_COLOR_STYLE
46 char class_string[TEMPSTRINGSIZE + 1];
47 
48 static char *Style_className = NULL;
49 static int hcode;
50 #endif
51 
52 /*		HTML Object
53  *		-----------
54  */
55 struct _HTStream {
56     const HTStreamClass *isa;
57     HTStream *target;
58     HTStreamClass targetClass;	/* COPY for speed */
59 };
60 
61 struct _HTStructured {
62     const HTStructuredClass *isa;
63     HTStream *target;
64     HTStreamClass targetClass;	/* COPY for speed */
65 
66     char buffer[BUFFER_SIZE + 1];	/* 1for NL */
67     int buffer_maxchars;
68     char *write_pointer;
69     char *line_break[MAX_CLEANNESS + 1];
70     int cleanness;
71     BOOL overflowed;
72     BOOL delete_line_break_char[MAX_CLEANNESS + 1];
73     BOOL preformatted;
74     BOOL escape_specials;
75     BOOL in_attrval;
76 #ifdef USE_COLOR_STYLE
77     HText *text;
78 #endif
79 };
80 
81 /*	Flush Buffer
82  *	------------
83  */
84 
flush_breaks(HTStructured * me)85 static void flush_breaks(HTStructured * me)
86 {
87     int i;
88 
89     for (i = 0; i <= MAX_CLEANNESS; i++) {
90 	me->line_break[i] = NULL;
91     }
92 }
93 
HTMLGen_flush(HTStructured * me)94 static void HTMLGen_flush(HTStructured * me)
95 {
96     (*me->targetClass.put_block) (me->target,
97 				  me->buffer,
98 				  (int) (me->write_pointer - me->buffer));
99     me->write_pointer = me->buffer;
100     flush_breaks(me);
101     me->cleanness = 0;
102     me->delete_line_break_char[0] = NO;
103 }
104 
105 #ifdef USE_COLOR_STYLE
106 /*
107  * We need to flush our buffer each time before we effect a color style change,
108  * this also relies on the subsequent stage not doing any buffering - this is
109  * currently true, in cases where it matters the target stream should be the
110  * HTPlain converter.  The flushing currently prevents reasonable line breaking
111  * in lines with tags.  Since color styles help visual scanning of displayed
112  * source lines, and long lines are wrapped in GridText anyway, this is
113  * probably acceptable (or even A Good Thing - more to see in one screenful).
114  * The pointer to the HText structure is initialized here before we effect the
115  * first style change.  Getting it from the global HTMainText variable isn't
116  * very clean, since it relies on the fact that HText_new() has already been
117  * called for the current stream stack's document by the time we start
118  * processing the first element; we rely on HTMLGenerator's callers
119  * (HTMLParsedPresent in particular) to guarantee this when it matters.
120  * Normally the target stream will have been setup by HTPlainPresent, which
121  * does what we need in this respect.  (A check whether we have the right
122  * output stream could be done by checking that targetClass.name is
123  * "PlainPresenter" or similar.)
124  *
125  * All special color style handling is only done if LYPreparsedSource is set.
126  * We could always do it for displaying source generated by an internal
127  * gateway, but this makes the rule more simple for the user:  color styles are
128  * applied to html source only with the -preparsed flag.  - kw
129  */
do_cstyle_flush(HTStructured * me)130 static void do_cstyle_flush(HTStructured * me)
131 {
132     if (!me->text && LYPreparsedSource) {
133 	me->text = HTMainText;
134     }
135     if (me->text) {
136 	HTMLGen_flush(me);
137     }
138 }
139 #endif /* COLOR_STYLE */
140 
141 /*	Weighted optional line break
142  *
143  *	We keep track of all the breaks for when we chop the line
144  */
145 
allow_break(HTStructured * me,int new_cleanness,int dlbc)146 static void allow_break(HTStructured * me, int new_cleanness, int dlbc)
147 {
148     if (dlbc && me->write_pointer == me->buffer)
149 	dlbc = NO;
150     me->line_break[new_cleanness] =
151 	dlbc ? me->write_pointer - 1	/* Point to space */
152 	: me->write_pointer;	/* point to gap */
153     me->delete_line_break_char[new_cleanness] = (BOOLEAN) dlbc;
154     if (new_cleanness >= me->cleanness &&
155 	(me->overflowed || me->line_break[new_cleanness] > me->buffer))
156 	me->cleanness = new_cleanness;
157 }
158 
159 /*	Character handling
160  *	------------------
161  *
162  *	The tricky bits are the line break handling.  This attempts
163  *	to synchrononise line breaks on sentence or phrase ends.  This
164  *	is important if one stores SGML files in a line-oriented code
165  *	repository, so that if a small change is made, line ends don't
166  *	shift in a ripple-through to apparently change a large part of the
167  *	file.  We give extra "cleanness" to spaces appearing directly
168  *	after periods (full stops), [semi]colons and commas.
169  *	   This should make the source files easier to read and modify
170  *	by hand, too, though this is not a primary design consideration. TBL
171  */
HTMLGen_put_character(HTStructured * me,int c)172 static void HTMLGen_put_character(HTStructured * me, int c)
173 {
174     if (me->escape_specials && UCH(c) < 32) {
175 	if (c == HT_NON_BREAK_SPACE || c == HT_EN_SPACE ||
176 	    c == LY_SOFT_HYPHEN) {	/* recursion... */
177 	    HTMLGen_put_character(me, '&');
178 	    HTMLGen_put_character(me, '#');
179 	    HTMLGen_put_character(me, 'x');
180 	    switch (c) {
181 	    case HT_NON_BREAK_SPACE:	/* &#xA0; */
182 		HTMLGen_put_character(me, 'A');
183 		HTMLGen_put_character(me, '0');
184 		break;
185 	    case HT_EN_SPACE:	/* &#x2002; */
186 		HTMLGen_put_character(me, '2');
187 		HTMLGen_put_character(me, '0');
188 		HTMLGen_put_character(me, '0');
189 		HTMLGen_put_character(me, '2');
190 		break;
191 	    case LY_SOFT_HYPHEN:	/* &#xAD; */
192 		HTMLGen_put_character(me, 'A');
193 		HTMLGen_put_character(me, 'D');
194 		break;
195 	    }
196 	    c = ';';
197 	}
198     }
199 
200     *me->write_pointer++ = (char) c;
201 
202     if (c == '\n') {
203 	HTMLGen_flush(me);
204 	return;
205     }
206 
207     /* Figure our whether we can break at this point
208      */
209     if ((!me->preformatted && (c == ' ' || c == '\t'))) {
210 	int new_cleanness = 3;
211 
212 	if (me->write_pointer > (me->buffer + 1)) {
213 	    char delims[5];
214 	    char *p;
215 
216 	    strcpy(delims, ",;:.");	/* @@ english bias */
217 	    p = strchr(delims, me->write_pointer[-2]);
218 	    if (p)
219 		new_cleanness = (int) (p - delims + 6);
220 	    if (!me->in_attrval)
221 		new_cleanness += 10;
222 	}
223 	allow_break(me, new_cleanness, YES);
224     }
225 
226     /*
227      * Flush buffer out when full, or whenever the line is over the nominal
228      * maximum and we can break at all
229      */
230     if (me->write_pointer >= me->buffer + me->buffer_maxchars ||
231 	(me->overflowed && me->cleanness)) {
232 	if (me->cleanness) {
233 	    char line_break_char = me->line_break[me->cleanness][0];
234 	    char *saved = me->line_break[me->cleanness];
235 
236 	    if (me->delete_line_break_char[me->cleanness])
237 		saved++;
238 	    me->line_break[me->cleanness][0] = '\n';
239 	    (*me->targetClass.put_block) (me->target,
240 					  me->buffer,
241 					  (int) (me->line_break[me->cleanness] -
242 						 me->buffer + 1));
243 	    me->line_break[me->cleanness][0] = line_break_char;
244 	    {			/* move next line in */
245 		char *p = saved;
246 		char *q;
247 
248 		for (q = me->buffer; p < me->write_pointer;)
249 		    *q++ = *p++;
250 	    }
251 	    me->cleanness = 0;
252 	    /* Now we have to check whether ther are any perfectly good breaks
253 	     * which weren't good enough for the last line but may be good
254 	     * enough for the next
255 	     */
256 	    {
257 		int i;
258 
259 		for (i = 0; i <= MAX_CLEANNESS; i++) {
260 		    if (me->line_break[i] != NULL &&
261 			me->line_break[i] > saved) {
262 			me->line_break[i] = me->line_break[i] -
263 			    (saved - me->buffer);
264 			me->cleanness = i;
265 		    } else {
266 			me->line_break[i] = NULL;
267 		    }
268 		}
269 	    }
270 
271 	    me->delete_line_break_char[0] = 0;
272 	    me->write_pointer = me->write_pointer - (saved - me->buffer);
273 	    me->overflowed = NO;
274 
275 	} else {
276 	    (*me->targetClass.put_block) (me->target,
277 					  me->buffer,
278 					  me->buffer_maxchars);
279 	    me->write_pointer = me->buffer;
280 	    flush_breaks(me);
281 	    me->overflowed = YES;
282 	}
283     }
284 }
285 
286 /*	String handling
287  *	---------------
288  */
HTMLGen_put_string(HTStructured * me,const char * s)289 static void HTMLGen_put_string(HTStructured * me, const char *s)
290 {
291     const char *p;
292 
293     for (p = s; *p; p++)
294 	HTMLGen_put_character(me, *p);
295 }
296 
HTMLGen_write(HTStructured * me,const char * s,int l)297 static void HTMLGen_write(HTStructured * me, const char *s,
298 			  int l)
299 {
300     const char *p;
301 
302     for (p = s; p < (s + l); p++)
303 	HTMLGen_put_character(me, *p);
304 }
305 
306 /*	Start Element
307  *	-------------
308  *
309  * Within the opening tag, there may be spaces and the line may be broken at
310  * these spaces.
311  */
HTMLGen_start_element(HTStructured * me,int element_number,const BOOL * present,STRING2PTR value,int charset GCC_UNUSED,char ** insert GCC_UNUSED)312 static int HTMLGen_start_element(HTStructured * me, int element_number,
313 				 const BOOL *present,
314 				 STRING2PTR value,
315 				 int charset GCC_UNUSED,
316 				 char **insert GCC_UNUSED)
317 {
318     int i;
319     BOOL was_preformatted = me->preformatted;
320     HTTag *tag = &HTML_dtd.tags[element_number];
321 
322 #if defined(USE_COLOR_STYLE)
323     char *title = NULL;
324     char *title_tmp = NULL;
325 
326     if (LYPreparsedSource) {
327 	char *myHash = NULL;
328 
329 	/*
330 	 * Same logic as in HTML_start_element, copied from there.  - kw
331 	 */
332 	HTSprintf(&Style_className, ";%s", HTML_dtd.tags[element_number].name);
333 	StrAllocCopy(myHash, HTML_dtd.tags[element_number].name);
334 	if (class_string[0]) {
335 	    StrAllocCat(myHash, ".");
336 	    StrAllocCat(myHash, class_string);
337 	    HTSprintf(&Style_className, ".%s", class_string);
338 	}
339 	class_string[0] = '\0';
340 	strtolower(myHash);
341 	hcode = hash_code(myHash);
342 	strtolower(Style_className);
343 
344 	if (TRACE_STYLE) {
345 	    fprintf(tfp, "CSSTRIM:%s -> %d", myHash, hcode);
346 	    if (hashStyles[hcode].code != hcode) {
347 		char *rp = strrchr(myHash, '.');
348 
349 		fprintf(tfp, " (undefined) %s\n", myHash);
350 		if (rp) {
351 		    int hcd;
352 
353 		    *rp = '\0';	/* trim the class */
354 		    hcd = hash_code(myHash);
355 		    fprintf(tfp, "CSS:%s -> %d", myHash, hcd);
356 		    if (hashStyles[hcd].code != hcd)
357 			fprintf(tfp, " (undefined) %s\n", myHash);
358 		    else
359 			fprintf(tfp, " ca=%d\n", hashStyles[hcd].color);
360 		}
361 	    } else
362 		fprintf(tfp, " ca=%d\n", hashStyles[hcode].color);
363 	}
364 
365 	if (displayStyles[element_number + STARTAT].color > -2) {
366 	    CTRACE2(TRACE_STYLE,
367 		    (tfp, "CSSTRIM: start_element: top <%s>\n",
368 		     HTML_dtd.tags[element_number].name));
369 	    do_cstyle_flush(me);
370 	    HText_characterStyle(me->text, hcode, 1);
371 	}
372 	FREE(myHash);
373     }
374 #endif /* USE_COLOR_STYLE */
375     me->preformatted = YES;	/* free text within tags */
376     HTMLGen_put_character(me, '<');
377     HTMLGen_put_string(me, tag->name);
378     if (present) {
379 	BOOL had_attr = NO;
380 
381 	for (i = 0; i < tag->number_of_attributes; i++) {
382 	    if (present[i]) {
383 		had_attr = YES;
384 		HTMLGen_put_character(me, ' ');
385 		allow_break(me, 11, YES);
386 #ifdef USE_COLOR_STYLE
387 		/*
388 		 * Try to mimic HTML_start_element's special handling for
389 		 * HTML_LINK.  If applicable, color the displayed attribute /
390 		 * value pairs differently.  - kw
391 		 */
392 		if (LYPreparsedSource &&
393 		    element_number == HTML_LINK && !title &&
394 		    present[HTML_LINK_CLASS] && *value[HTML_LINK_CLASS] &&
395 		    !present[HTML_LINK_REV] &&
396 		    (present[HTML_LINK_REL] || present[HTML_LINK_HREF])) {
397 		    if (present[HTML_LINK_TITLE] && *value[HTML_LINK_TITLE]) {
398 			StrAllocCopy(title, value[HTML_LINK_TITLE]);
399 			LYTrimHead(title);
400 			LYTrimTail(title);
401 		    }
402 		    if ((!title || *title == '\0') && present[HTML_LINK_REL]) {
403 			StrAllocCopy(title, value[HTML_LINK_REL]);
404 		    }
405 		    if (title && *title) {
406 			HTSprintf0(&title_tmp, "link.%s.%s",
407 				   value[HTML_LINK_CLASS], title);
408 			CTRACE2(TRACE_STYLE,
409 				(tfp, "CSSTRIM:link=%s\n", title_tmp));
410 
411 			do_cstyle_flush(me);
412 			HText_characterStyle(me->text, hash_code(title_tmp), 1);
413 		    }
414 		}
415 #endif
416 		HTMLGen_put_string(me, tag->attributes[i].name);
417 		if (value[i]) {
418 		    me->preformatted = was_preformatted;
419 		    me->in_attrval = YES;
420 		    if (strchr(value[i], '"') == NULL) {
421 			HTMLGen_put_string(me, "=\"");
422 			HTMLGen_put_string(me, value[i]);
423 			HTMLGen_put_character(me, '"');
424 		    } else if (strchr(value[i], '\'') == NULL) {
425 			HTMLGen_put_string(me, "='");
426 			HTMLGen_put_string(me, value[i]);
427 			HTMLGen_put_character(me, '\'');
428 		    } else {	/* attribute value has both kinds of quotes */
429 			const char *p;
430 
431 			HTMLGen_put_string(me, "=\"");
432 			for (p = value[i]; *p; p++) {
433 			    if (*p != '"') {
434 				HTMLGen_put_character(me, *p);
435 			    } else {
436 				HTMLGen_put_string(me, "&#34;");
437 			    }
438 			}
439 			HTMLGen_put_character(me, '"');
440 		    }
441 		    me->preformatted = YES;
442 		    me->in_attrval = NO;
443 		}
444 	    }
445 	}
446 #ifdef USE_COLOR_STYLE
447 	if (had_attr && LYPreparsedSource && element_number == HTML_LINK) {
448 	    /*
449 	     * Clean up after special HTML_LINK handling - kw
450 	     */
451 	    if (title && *title) {
452 		do_cstyle_flush(me);
453 		HText_characterStyle(me->text, hash_code(title_tmp), 0);
454 		FREE(title_tmp);
455 	    }
456 	    FREE(title);
457 	}
458 #endif
459 	if (had_attr)
460 	    allow_break(me, 12, NO);
461     }
462     HTMLGen_put_string(me, ">");	/* got rid of \n LJM */
463 
464     /*
465      * Make very specific HTML assumption that PRE can't be nested!
466      */
467     me->preformatted = (BOOL) ((element_number == HTML_PRE)
468 			       ? YES
469 			       : was_preformatted);
470 
471     /*
472      * Can break after element start.
473      */
474     if (!me->preformatted && tag->contents != SGML_EMPTY) {
475 	if (HTML_dtd.tags[element_number].contents == SGML_ELEMENT)
476 	    allow_break(me, 15, NO);
477 	else
478 	    allow_break(me, 2, NO);
479     }
480 #if defined(USE_COLOR_STYLE)
481     /*
482      * Same logic as in HTML_start_element, copied from there.  - kw
483      */
484 
485     /* end really empty tags straight away */
486     if (LYPreparsedSource && ReallyEmptyTagNum(element_number)) {
487 	CTRACE2(TRACE_STYLE,
488 		(tfp, "STYLE:begin_element:ending EMPTY element style\n"));
489 	do_cstyle_flush(me);
490 	HText_characterStyle(me->text, hcode, STACK_OFF);
491 	TrimColorClass(HTML_dtd.tags[element_number].name,
492 		       Style_className, &hcode);
493     }
494 #endif /* USE_COLOR_STYLE */
495     if (element_number == HTML_OBJECT && tag->contents == SGML_LITTERAL) {
496 	/*
497 	 * These conditions only approximate the ones used in HTML.c.  Let our
498 	 * SGML parser know that further content is to be parsed normally not
499 	 * literally.  - kw
500 	 */
501 	if (!present) {
502 	    return HT_PARSER_OTHER_CONTENT;
503 	} else if (!present[HTML_OBJECT_DECLARE] &&
504 		   !(present[HTML_OBJECT_NAME] &&
505 		     value[HTML_OBJECT_NAME] && *value[HTML_OBJECT_NAME])) {
506 	    if (present[HTML_OBJECT_SHAPES] ||
507 		!(present[HTML_OBJECT_USEMAP] &&
508 		  value[HTML_OBJECT_USEMAP] && *value[HTML_OBJECT_USEMAP]))
509 		return HT_PARSER_OTHER_CONTENT;
510 	}
511     }
512     return HT_OK;
513 }
514 
515 /*		End Element
516  *		-----------
517  *
518  * When we end an element, the style must be returned to that in effect before
519  * that element.  Note that anchors (etc?) don't have an associated style, so
520  * that we must scan down the stack for an element with a defined style.  (In
521  * fact, the styles should be linked to the whole stack not just the top one.)
522  * TBL 921119
523  */
HTMLGen_end_element(HTStructured * me,int element_number,char ** insert GCC_UNUSED)524 static int HTMLGen_end_element(HTStructured * me, int element_number,
525 			       char **insert GCC_UNUSED)
526 {
527     if (!me->preformatted &&
528 	HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
529 	/*
530 	 * Can break before element end.
531 	 */
532 	if (HTML_dtd.tags[element_number].contents == SGML_ELEMENT)
533 	    allow_break(me, 14, NO);
534 	else
535 	    allow_break(me, 1, NO);
536     }
537     HTMLGen_put_string(me, "</");
538     HTMLGen_put_string(me, HTML_dtd.tags[element_number].name);
539     HTMLGen_put_character(me, '>');
540     if (element_number == HTML_PRE) {
541 	me->preformatted = NO;
542     }
543 #ifdef USE_COLOR_STYLE
544     /*
545      * Same logic as in HTML_end_element, copied from there.  - kw
546      */
547     TrimColorClass(HTML_dtd.tags[element_number].name,
548 		   Style_className, &hcode);
549 
550     if (LYPreparsedSource && !ReallyEmptyTagNum(element_number)) {
551 	CTRACE2(TRACE_STYLE,
552 		(tfp, "STYLE:end_element: ending non-EMPTY style\n"));
553 	do_cstyle_flush(me);
554 	HText_characterStyle(me->text, hcode, STACK_OFF);
555     }
556 #endif /* USE_COLOR_STYLE */
557     return HT_OK;
558 }
559 
560 /*		Expanding entities
561  *		------------------
562  *
563  */
HTMLGen_put_entity(HTStructured * me,int entity_number)564 static int HTMLGen_put_entity(HTStructured * me, int entity_number)
565 {
566     int nent = (int) HTML_dtd.number_of_entities;
567 
568     HTMLGen_put_character(me, '&');
569     if (entity_number < nent) {
570 	HTMLGen_put_string(me, HTML_dtd.entity_names[entity_number]);
571     }
572     HTMLGen_put_character(me, ';');
573     return HT_OK;
574 }
575 
576 /*	Free an HTML object
577  *	-------------------
578  *
579  */
HTMLGen_free(HTStructured * me)580 static void HTMLGen_free(HTStructured * me)
581 {
582     (*me->targetClass.put_character) (me->target, '\n');
583     HTMLGen_flush(me);
584     (*me->targetClass._free) (me->target);	/* ripple through */
585 #ifdef USE_COLOR_STYLE
586     FREE(Style_className);
587 #endif
588     FREE(me);
589 }
590 
PlainToHTML_free(HTStructured * me)591 static void PlainToHTML_free(HTStructured * me)
592 {
593     HTMLGen_end_element(me, HTML_PRE, 0);
594     HTMLGen_free(me);
595 }
596 
HTMLGen_abort(HTStructured * me,HTError e GCC_UNUSED)597 static void HTMLGen_abort(HTStructured * me, HTError e GCC_UNUSED)
598 {
599     HTMLGen_free(me);
600 #ifdef USE_COLOR_STYLE
601     FREE(Style_className);
602 #endif
603 }
604 
PlainToHTML_abort(HTStructured * me,HTError e GCC_UNUSED)605 static void PlainToHTML_abort(HTStructured * me, HTError e GCC_UNUSED)
606 {
607     PlainToHTML_free(me);
608 }
609 
610 /*	Structured Object Class
611  *	-----------------------
612  */
613 static const HTStructuredClass HTMLGeneration =		/* As opposed to print etc */
614 {
615     "HTMLGen",
616     HTMLGen_free,
617     HTMLGen_abort,
618     HTMLGen_put_character, HTMLGen_put_string, HTMLGen_write,
619     HTMLGen_start_element, HTMLGen_end_element,
620     HTMLGen_put_entity
621 };
622 
623 /*	Subclass-specific Methods
624  *	-------------------------
625  */
HTMLGenerator(HTStream * output)626 HTStructured *HTMLGenerator(HTStream *output)
627 {
628     HTStructured *me = (HTStructured *) malloc(sizeof(*me));
629 
630     if (me == NULL)
631 	outofmem(__FILE__, "HTMLGenerator");
632 
633     assert(me != NULL);
634 
635     me->isa = &HTMLGeneration;
636 
637     me->target = output;
638     me->targetClass = *me->target->isa;		/* Copy pointers to routines for speed */
639 
640     me->write_pointer = me->buffer;
641     flush_breaks(me);
642     me->line_break[0] = me->buffer;
643     me->cleanness = 0;
644     me->overflowed = NO;
645     me->delete_line_break_char[0] = NO;
646     me->preformatted = NO;
647     me->in_attrval = NO;
648 
649     /*
650      * For what line length should we attempt to wrap ?  - kw
651      */
652     if (!LYPreparsedSource) {
653 	me->buffer_maxchars = 80;	/* work as before - kw */
654     } else if (dump_output_width > 1) {
655 	me->buffer_maxchars = dump_output_width;	/* try to honor -width - kw */
656     } else if (dump_output_immediately) {
657 	me->buffer_maxchars = 80;	/* try to honor -width - kw */
658     } else {
659 	me->buffer_maxchars = (LYcolLimit - 1);
660 	if (me->buffer_maxchars < 38)	/* too narrow, let GridText deal */
661 	    me->buffer_maxchars = 40;
662     }
663     if (me->buffer_maxchars > 900)	/* likely not true - kw */
664 	me->buffer_maxchars = 78;
665     if (me->buffer_maxchars > BUFFER_SIZE)	/* must not be larger! */
666 	me->buffer_maxchars = BUFFER_SIZE - 2;
667 
668     /*
669      * If dump_output_immediately is set, there likely isn't anything after
670      * this stream to interpret the Lynx special chars.  Also if they get
671      * displayed via HTPlain, that will probably make non-breaking space chars
672      * etc.  invisible.  So let's translate them to numerical character
673      * references.  For debugging purposes we'll use the new hex format.
674      */
675     me->escape_specials = LYPreparsedSource;
676 
677 #ifdef USE_COLOR_STYLE
678     me->text = NULL;		/* Will be initialized when first needed. - kw */
679     FREE(Style_className);
680     class_string[0] = '\0';
681 #endif /* COLOR_STYLE */
682 
683     return me;
684 }
685 
686 /*	Stream Object Class
687  *	-------------------
688  *
689  *	This object just converts a plain text stream into HTML
690  *	It is officially a structured strem but only the stream bits exist.
691  *	This is just the easiest way of typecasting all the routines.
692  */
693 static const HTStructuredClass PlainToHTMLConversion =
694 {
695     "plaintexttoHTML",
696     HTMLGen_free,
697     PlainToHTML_abort,
698     HTMLGen_put_character,
699     HTMLGen_put_string,
700     HTMLGen_write,
701     NULL,			/* Structured stuff */
702     NULL,
703     NULL
704 };
705 
706 /*	HTConverter from plain text to HTML Stream
707  *	------------------------------------------
708  */
HTPlainToHTML(HTPresentation * pres GCC_UNUSED,HTParentAnchor * anchor GCC_UNUSED,HTStream * sink)709 HTStream *HTPlainToHTML(HTPresentation *pres GCC_UNUSED,
710 			HTParentAnchor *anchor GCC_UNUSED,
711 			HTStream *sink)
712 {
713     HTStructured *me = (HTStructured *) malloc(sizeof(*me));
714 
715     if (me == NULL)
716 	outofmem(__FILE__, "PlainToHTML");
717 
718     assert(me != NULL);
719 
720     me->isa = (const HTStructuredClass *) &PlainToHTMLConversion;
721 
722     /*
723      * Copy pointers to routines for speed.
724      */
725     me->target = sink;
726     me->targetClass = *me->target->isa;
727     me->write_pointer = me->buffer;
728     flush_breaks(me);
729     me->cleanness = 0;
730     me->overflowed = NO;
731     me->delete_line_break_char[0] = NO;
732     /* try to honor -width - kw */
733     me->buffer_maxchars = (dump_output_width > 1 ?
734 			   dump_output_width : 80);
735 
736     HTMLGen_put_string(me, "<HTML>\n<BODY>\n<PRE>\n");
737     me->preformatted = YES;
738     me->escape_specials = NO;
739     me->in_attrval = NO;
740     return (HTStream *) me;
741 }
742