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: /*   */
182 HTMLGen_put_character(me, 'A');
183 HTMLGen_put_character(me, '0');
184 break;
185 case HT_EN_SPACE: /*   */
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: /* ­ */
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, """);
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