]> git.cameronkatri.com Git - mandoc.git/blob - html.c
skip printing the embedded style sheet if an external style is referenced
[mandoc.git] / html.c
1 /* $Id: html.c,v 1.222 2018/05/01 23:36:55 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "mandoc_aux.h"
32 #include "mandoc.h"
33 #include "roff.h"
34 #include "out.h"
35 #include "html.h"
36 #include "manconf.h"
37 #include "main.h"
38
39 struct htmldata {
40 const char *name;
41 int flags;
42 #define HTML_NOSTACK (1 << 0)
43 #define HTML_AUTOCLOSE (1 << 1)
44 #define HTML_NLBEFORE (1 << 2)
45 #define HTML_NLBEGIN (1 << 3)
46 #define HTML_NLEND (1 << 4)
47 #define HTML_NLAFTER (1 << 5)
48 #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER)
49 #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND)
50 #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE)
51 #define HTML_INDENT (1 << 6)
52 #define HTML_NOINDENT (1 << 7)
53 };
54
55 static const struct htmldata htmltags[TAG_MAX] = {
56 {"html", HTML_NLALL},
57 {"head", HTML_NLALL | HTML_INDENT},
58 {"body", HTML_NLALL},
59 {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
60 {"title", HTML_NLAROUND},
61 {"div", HTML_NLAROUND},
62 {"h1", HTML_NLAROUND},
63 {"h2", HTML_NLAROUND},
64 {"span", 0},
65 {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
66 {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
67 {"a", 0},
68 {"table", HTML_NLALL | HTML_INDENT},
69 {"colgroup", HTML_NLALL | HTML_INDENT},
70 {"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
71 {"tr", HTML_NLALL | HTML_INDENT},
72 {"td", HTML_NLAROUND},
73 {"li", HTML_NLAROUND | HTML_INDENT},
74 {"ul", HTML_NLALL | HTML_INDENT},
75 {"ol", HTML_NLALL | HTML_INDENT},
76 {"dl", HTML_NLALL | HTML_INDENT},
77 {"dt", HTML_NLAROUND},
78 {"dd", HTML_NLAROUND | HTML_INDENT},
79 {"pre", HTML_NLALL | HTML_NOINDENT},
80 {"var", 0},
81 {"cite", 0},
82 {"b", 0},
83 {"i", 0},
84 {"code", 0},
85 {"small", 0},
86 {"style", HTML_NLALL | HTML_INDENT},
87 {"math", HTML_NLALL | HTML_INDENT},
88 {"mrow", 0},
89 {"mi", 0},
90 {"mn", 0},
91 {"mo", 0},
92 {"msup", 0},
93 {"msub", 0},
94 {"msubsup", 0},
95 {"mfrac", 0},
96 {"msqrt", 0},
97 {"mfenced", 0},
98 {"mtable", 0},
99 {"mtr", 0},
100 {"mtd", 0},
101 {"munderover", 0},
102 {"munder", 0},
103 {"mover", 0},
104 };
105
106 static const char *const roffscales[SCALE_MAX] = {
107 "cm", /* SCALE_CM */
108 "in", /* SCALE_IN */
109 "pc", /* SCALE_PC */
110 "pt", /* SCALE_PT */
111 "em", /* SCALE_EM */
112 "em", /* SCALE_MM */
113 "ex", /* SCALE_EN */
114 "ex", /* SCALE_BU */
115 "em", /* SCALE_VS */
116 "ex", /* SCALE_FS */
117 };
118
119 static void a2width(const char *, struct roffsu *);
120 static void print_byte(struct html *, char);
121 static void print_endword(struct html *);
122 static void print_indent(struct html *);
123 static void print_word(struct html *, const char *);
124
125 static void print_ctag(struct html *, struct tag *);
126 static int print_escape(struct html *, char);
127 static int print_encode(struct html *, const char *, const char *, int);
128 static void print_href(struct html *, const char *, const char *, int);
129 static void print_metaf(struct html *, enum mandoc_esc);
130
131
132 void *
133 html_alloc(const struct manoutput *outopts)
134 {
135 struct html *h;
136
137 h = mandoc_calloc(1, sizeof(struct html));
138
139 h->tag = NULL;
140 h->style = outopts->style;
141 h->base_man = outopts->man;
142 h->base_includes = outopts->includes;
143 if (outopts->fragment)
144 h->oflags |= HTML_FRAGMENT;
145
146 return h;
147 }
148
149 void
150 html_free(void *p)
151 {
152 struct tag *tag;
153 struct html *h;
154
155 h = (struct html *)p;
156
157 while ((tag = h->tag) != NULL) {
158 h->tag = tag->next;
159 free(tag);
160 }
161
162 free(h);
163 }
164
165 void
166 print_gen_head(struct html *h)
167 {
168 struct tag *t;
169
170 print_otag(h, TAG_META, "?", "charset", "utf-8");
171 if (h->style != NULL) {
172 print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet",
173 h->style, "type", "text/css", "media", "all");
174 return;
175 }
176
177 /*
178 * Print a minimal embedded style sheet.
179 */
180
181 t = print_otag(h, TAG_STYLE, "");
182 print_text(h, "table.head, table.foot { width: 100%; }");
183 print_endline(h);
184 print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }");
185 print_endline(h);
186 print_text(h, "td.head-vol { text-align: center; }");
187 print_endline(h);
188 print_text(h, "div.Pp { margin: 1ex 0ex; }");
189 print_tagq(h, t);
190 }
191
192 static void
193 print_metaf(struct html *h, enum mandoc_esc deco)
194 {
195 enum htmlfont font;
196
197 switch (deco) {
198 case ESCAPE_FONTPREV:
199 font = h->metal;
200 break;
201 case ESCAPE_FONTITALIC:
202 font = HTMLFONT_ITALIC;
203 break;
204 case ESCAPE_FONTBOLD:
205 font = HTMLFONT_BOLD;
206 break;
207 case ESCAPE_FONTBI:
208 font = HTMLFONT_BI;
209 break;
210 case ESCAPE_FONT:
211 case ESCAPE_FONTROMAN:
212 font = HTMLFONT_NONE;
213 break;
214 default:
215 abort();
216 }
217
218 if (h->metaf) {
219 print_tagq(h, h->metaf);
220 h->metaf = NULL;
221 }
222
223 h->metal = h->metac;
224 h->metac = font;
225
226 switch (font) {
227 case HTMLFONT_ITALIC:
228 h->metaf = print_otag(h, TAG_I, "");
229 break;
230 case HTMLFONT_BOLD:
231 h->metaf = print_otag(h, TAG_B, "");
232 break;
233 case HTMLFONT_BI:
234 h->metaf = print_otag(h, TAG_B, "");
235 print_otag(h, TAG_I, "");
236 break;
237 default:
238 break;
239 }
240 }
241
242 char *
243 html_make_id(const struct roff_node *n)
244 {
245 const struct roff_node *nch;
246 char *buf, *cp;
247
248 for (nch = n->child; nch != NULL; nch = nch->next)
249 if (nch->type != ROFFT_TEXT)
250 return NULL;
251
252 buf = NULL;
253 deroff(&buf, n);
254 if (buf == NULL)
255 return NULL;
256
257 /* http://www.w3.org/TR/html5/dom.html#the-id-attribute */
258
259 for (cp = buf; *cp != '\0'; cp++)
260 if (*cp == ' ')
261 *cp = '_';
262
263 return buf;
264 }
265
266 int
267 html_strlen(const char *cp)
268 {
269 size_t rsz;
270 int skip, sz;
271
272 /*
273 * Account for escaped sequences within string length
274 * calculations. This follows the logic in term_strlen() as we
275 * must calculate the width of produced strings.
276 * Assume that characters are always width of "1". This is
277 * hacky, but it gets the job done for approximation of widths.
278 */
279
280 sz = 0;
281 skip = 0;
282 while (1) {
283 rsz = strcspn(cp, "\\");
284 if (rsz) {
285 cp += rsz;
286 if (skip) {
287 skip = 0;
288 rsz--;
289 }
290 sz += rsz;
291 }
292 if ('\0' == *cp)
293 break;
294 cp++;
295 switch (mandoc_escape(&cp, NULL, NULL)) {
296 case ESCAPE_ERROR:
297 return sz;
298 case ESCAPE_UNICODE:
299 case ESCAPE_NUMBERED:
300 case ESCAPE_SPECIAL:
301 case ESCAPE_OVERSTRIKE:
302 if (skip)
303 skip = 0;
304 else
305 sz++;
306 break;
307 case ESCAPE_SKIPCHAR:
308 skip = 1;
309 break;
310 default:
311 break;
312 }
313 }
314 return sz;
315 }
316
317 static int
318 print_escape(struct html *h, char c)
319 {
320
321 switch (c) {
322 case '<':
323 print_word(h, "&lt;");
324 break;
325 case '>':
326 print_word(h, "&gt;");
327 break;
328 case '&':
329 print_word(h, "&amp;");
330 break;
331 case '"':
332 print_word(h, "&quot;");
333 break;
334 case ASCII_NBRSP:
335 print_word(h, "&nbsp;");
336 break;
337 case ASCII_HYPH:
338 print_byte(h, '-');
339 break;
340 case ASCII_BREAK:
341 break;
342 default:
343 return 0;
344 }
345 return 1;
346 }
347
348 static int
349 print_encode(struct html *h, const char *p, const char *pend, int norecurse)
350 {
351 char numbuf[16];
352 struct tag *t;
353 const char *seq;
354 size_t sz;
355 int c, len, breakline, nospace;
356 enum mandoc_esc esc;
357 static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"',
358 ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' };
359
360 if (pend == NULL)
361 pend = strchr(p, '\0');
362
363 breakline = 0;
364 nospace = 0;
365
366 while (p < pend) {
367 if (HTML_SKIPCHAR & h->flags && '\\' != *p) {
368 h->flags &= ~HTML_SKIPCHAR;
369 p++;
370 continue;
371 }
372
373 for (sz = strcspn(p, rejs); sz-- && p < pend; p++)
374 print_byte(h, *p);
375
376 if (breakline &&
377 (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) {
378 t = print_otag(h, TAG_DIV, "");
379 print_text(h, "\\~");
380 print_tagq(h, t);
381 breakline = 0;
382 while (p < pend && (*p == ' ' || *p == ASCII_NBRSP))
383 p++;
384 continue;
385 }
386
387 if (p >= pend)
388 break;
389
390 if (*p == ' ') {
391 print_endword(h);
392 p++;
393 continue;
394 }
395
396 if (print_escape(h, *p++))
397 continue;
398
399 esc = mandoc_escape(&p, &seq, &len);
400 if (ESCAPE_ERROR == esc)
401 break;
402
403 switch (esc) {
404 case ESCAPE_FONT:
405 case ESCAPE_FONTPREV:
406 case ESCAPE_FONTBOLD:
407 case ESCAPE_FONTITALIC:
408 case ESCAPE_FONTBI:
409 case ESCAPE_FONTROMAN:
410 if (0 == norecurse)
411 print_metaf(h, esc);
412 continue;
413 case ESCAPE_SKIPCHAR:
414 h->flags |= HTML_SKIPCHAR;
415 continue;
416 default:
417 break;
418 }
419
420 if (h->flags & HTML_SKIPCHAR) {
421 h->flags &= ~HTML_SKIPCHAR;
422 continue;
423 }
424
425 switch (esc) {
426 case ESCAPE_UNICODE:
427 /* Skip past "u" header. */
428 c = mchars_num2uc(seq + 1, len - 1);
429 break;
430 case ESCAPE_NUMBERED:
431 c = mchars_num2char(seq, len);
432 if (c < 0)
433 continue;
434 break;
435 case ESCAPE_SPECIAL:
436 c = mchars_spec2cp(seq, len);
437 if (c <= 0)
438 continue;
439 break;
440 case ESCAPE_BREAK:
441 breakline = 1;
442 continue;
443 case ESCAPE_NOSPACE:
444 if ('\0' == *p)
445 nospace = 1;
446 continue;
447 case ESCAPE_OVERSTRIKE:
448 if (len == 0)
449 continue;
450 c = seq[len - 1];
451 break;
452 default:
453 continue;
454 }
455 if ((c < 0x20 && c != 0x09) ||
456 (c > 0x7E && c < 0xA0))
457 c = 0xFFFD;
458 if (c > 0x7E) {
459 (void)snprintf(numbuf, sizeof(numbuf), "&#x%.4X;", c);
460 print_word(h, numbuf);
461 } else if (print_escape(h, c) == 0)
462 print_byte(h, c);
463 }
464
465 return nospace;
466 }
467
468 static void
469 print_href(struct html *h, const char *name, const char *sec, int man)
470 {
471 const char *p, *pp;
472
473 pp = man ? h->base_man : h->base_includes;
474 while ((p = strchr(pp, '%')) != NULL) {
475 print_encode(h, pp, p, 1);
476 if (man && p[1] == 'S') {
477 if (sec == NULL)
478 print_byte(h, '1');
479 else
480 print_encode(h, sec, NULL, 1);
481 } else if ((man && p[1] == 'N') ||
482 (man == 0 && p[1] == 'I'))
483 print_encode(h, name, NULL, 1);
484 else
485 print_encode(h, p, p + 2, 1);
486 pp = p + 2;
487 }
488 if (*pp != '\0')
489 print_encode(h, pp, NULL, 1);
490 }
491
492 struct tag *
493 print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
494 {
495 va_list ap;
496 struct roffsu mysu, *su;
497 char numbuf[16];
498 struct tag *t;
499 const char *attr;
500 char *arg1, *arg2;
501 double v;
502 int i, have_style, tflags;
503
504 tflags = htmltags[tag].flags;
505
506 /* Push this tag onto the stack of open scopes. */
507
508 if ((tflags & HTML_NOSTACK) == 0) {
509 t = mandoc_malloc(sizeof(struct tag));
510 t->tag = tag;
511 t->next = h->tag;
512 h->tag = t;
513 } else
514 t = NULL;
515
516 if (tflags & HTML_NLBEFORE)
517 print_endline(h);
518 if (h->col == 0)
519 print_indent(h);
520 else if ((h->flags & HTML_NOSPACE) == 0) {
521 if (h->flags & HTML_KEEP)
522 print_word(h, "&#x00A0;");
523 else {
524 if (h->flags & HTML_PREKEEP)
525 h->flags |= HTML_KEEP;
526 print_endword(h);
527 }
528 }
529
530 if ( ! (h->flags & HTML_NONOSPACE))
531 h->flags &= ~HTML_NOSPACE;
532 else
533 h->flags |= HTML_NOSPACE;
534
535 /* Print out the tag name and attributes. */
536
537 print_byte(h, '<');
538 print_word(h, htmltags[tag].name);
539
540 va_start(ap, fmt);
541
542 have_style = 0;
543 while (*fmt != '\0') {
544 if (*fmt == 's') {
545 have_style = 1;
546 fmt++;
547 break;
548 }
549
550 /* Parse a non-style attribute and its arguments. */
551
552 arg1 = va_arg(ap, char *);
553 switch (*fmt++) {
554 case 'c':
555 attr = "class";
556 break;
557 case 'h':
558 attr = "href";
559 break;
560 case 'i':
561 attr = "id";
562 break;
563 case '?':
564 attr = arg1;
565 arg1 = va_arg(ap, char *);
566 break;
567 default:
568 abort();
569 }
570 arg2 = NULL;
571 if (*fmt == 'M')
572 arg2 = va_arg(ap, char *);
573 if (arg1 == NULL)
574 continue;
575
576 /* Print the non-style attributes. */
577
578 print_byte(h, ' ');
579 print_word(h, attr);
580 print_byte(h, '=');
581 print_byte(h, '"');
582 switch (*fmt) {
583 case 'I':
584 print_href(h, arg1, NULL, 0);
585 fmt++;
586 break;
587 case 'M':
588 print_href(h, arg1, arg2, 1);
589 fmt++;
590 break;
591 case 'R':
592 print_byte(h, '#');
593 print_encode(h, arg1, NULL, 1);
594 fmt++;
595 break;
596 case 'T':
597 print_encode(h, arg1, NULL, 1);
598 print_word(h, "\" title=\"");
599 print_encode(h, arg1, NULL, 1);
600 fmt++;
601 break;
602 default:
603 print_encode(h, arg1, NULL, 1);
604 break;
605 }
606 print_byte(h, '"');
607 }
608
609 /* Print out styles. */
610
611 while (*fmt != '\0') {
612 arg1 = NULL;
613 su = NULL;
614
615 /* First letter: input argument type. */
616
617 switch (*fmt++) {
618 case 'h':
619 i = va_arg(ap, int);
620 su = &mysu;
621 SCALE_HS_INIT(su, i);
622 break;
623 case 's':
624 arg1 = va_arg(ap, char *);
625 break;
626 case 'u':
627 su = va_arg(ap, struct roffsu *);
628 break;
629 case 'w':
630 if ((arg2 = va_arg(ap, char *)) != NULL) {
631 su = &mysu;
632 a2width(arg2, su);
633 }
634 if (*fmt == '*') {
635 if (su != NULL && su->unit == SCALE_EN &&
636 su->scale > 5.9 && su->scale < 6.1)
637 su = NULL;
638 fmt++;
639 }
640 if (*fmt == '+') {
641 if (su != NULL) {
642 /* Make even bold text fit. */
643 su->scale *= 1.2;
644 /* Add padding. */
645 su->scale += 3.0;
646 }
647 fmt++;
648 }
649 if (*fmt == '-') {
650 if (su != NULL)
651 su->scale *= -1.0;
652 fmt++;
653 }
654 break;
655 default:
656 abort();
657 }
658
659 /* Second letter: style name. */
660
661 switch (*fmt++) {
662 case 'h':
663 attr = "height";
664 break;
665 case 'i':
666 attr = "text-indent";
667 break;
668 case 'l':
669 attr = "margin-left";
670 break;
671 case 'w':
672 attr = "width";
673 break;
674 case 'W':
675 attr = "min-width";
676 break;
677 case '?':
678 attr = arg1;
679 arg1 = va_arg(ap, char *);
680 break;
681 default:
682 abort();
683 }
684 if (su == NULL && arg1 == NULL)
685 continue;
686
687 if (have_style == 1)
688 print_word(h, " style=\"");
689 else
690 print_byte(h, ' ');
691 print_word(h, attr);
692 print_byte(h, ':');
693 print_byte(h, ' ');
694 if (su != NULL) {
695 v = su->scale;
696 if (su->unit == SCALE_MM && (v /= 100.0) == 0.0)
697 v = 1.0;
698 else if (su->unit == SCALE_BU)
699 v /= 24.0;
700 (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v);
701 print_word(h, numbuf);
702 print_word(h, roffscales[su->unit]);
703 } else
704 print_word(h, arg1);
705 print_byte(h, ';');
706 have_style = 2;
707 }
708 if (have_style == 2)
709 print_byte(h, '"');
710
711 va_end(ap);
712
713 /* Accommodate for "well-formed" singleton escaping. */
714
715 if (HTML_AUTOCLOSE & htmltags[tag].flags)
716 print_byte(h, '/');
717
718 print_byte(h, '>');
719
720 if (tflags & HTML_NLBEGIN)
721 print_endline(h);
722 else
723 h->flags |= HTML_NOSPACE;
724
725 if (tflags & HTML_INDENT)
726 h->indent++;
727 if (tflags & HTML_NOINDENT)
728 h->noindent++;
729
730 return t;
731 }
732
733 static void
734 print_ctag(struct html *h, struct tag *tag)
735 {
736 int tflags;
737
738 /*
739 * Remember to close out and nullify the current
740 * meta-font and table, if applicable.
741 */
742 if (tag == h->metaf)
743 h->metaf = NULL;
744 if (tag == h->tblt)
745 h->tblt = NULL;
746
747 tflags = htmltags[tag->tag].flags;
748
749 if (tflags & HTML_INDENT)
750 h->indent--;
751 if (tflags & HTML_NOINDENT)
752 h->noindent--;
753 if (tflags & HTML_NLEND)
754 print_endline(h);
755 print_indent(h);
756 print_byte(h, '<');
757 print_byte(h, '/');
758 print_word(h, htmltags[tag->tag].name);
759 print_byte(h, '>');
760 if (tflags & HTML_NLAFTER)
761 print_endline(h);
762
763 h->tag = tag->next;
764 free(tag);
765 }
766
767 void
768 print_gen_decls(struct html *h)
769 {
770 print_word(h, "<!DOCTYPE html>");
771 print_endline(h);
772 }
773
774 void
775 print_gen_comment(struct html *h, struct roff_node *n)
776 {
777 int wantblank;
778
779 print_word(h, "<!-- This is an automatically generated file."
780 " Do not edit.");
781 h->indent = 1;
782 wantblank = 0;
783 while (n != NULL && n->type == ROFFT_COMMENT) {
784 if (strstr(n->string, "-->") == NULL &&
785 (wantblank || *n->string != '\0')) {
786 print_endline(h);
787 print_indent(h);
788 print_word(h, n->string);
789 wantblank = *n->string != '\0';
790 }
791 n = n->next;
792 }
793 if (wantblank)
794 print_endline(h);
795 print_word(h, " -->");
796 print_endline(h);
797 h->indent = 0;
798 }
799
800 void
801 print_text(struct html *h, const char *word)
802 {
803 if (h->col && (h->flags & HTML_NOSPACE) == 0) {
804 if ( ! (HTML_KEEP & h->flags)) {
805 if (HTML_PREKEEP & h->flags)
806 h->flags |= HTML_KEEP;
807 print_endword(h);
808 } else
809 print_word(h, "&#x00A0;");
810 }
811
812 assert(NULL == h->metaf);
813 switch (h->metac) {
814 case HTMLFONT_ITALIC:
815 h->metaf = print_otag(h, TAG_I, "");
816 break;
817 case HTMLFONT_BOLD:
818 h->metaf = print_otag(h, TAG_B, "");
819 break;
820 case HTMLFONT_BI:
821 h->metaf = print_otag(h, TAG_B, "");
822 print_otag(h, TAG_I, "");
823 break;
824 default:
825 print_indent(h);
826 break;
827 }
828
829 assert(word);
830 if ( ! print_encode(h, word, NULL, 0)) {
831 if ( ! (h->flags & HTML_NONOSPACE))
832 h->flags &= ~HTML_NOSPACE;
833 h->flags &= ~HTML_NONEWLINE;
834 } else
835 h->flags |= HTML_NOSPACE | HTML_NONEWLINE;
836
837 if (h->metaf) {
838 print_tagq(h, h->metaf);
839 h->metaf = NULL;
840 }
841
842 h->flags &= ~HTML_IGNDELIM;
843 }
844
845 void
846 print_tagq(struct html *h, const struct tag *until)
847 {
848 struct tag *tag;
849
850 while ((tag = h->tag) != NULL) {
851 print_ctag(h, tag);
852 if (until && tag == until)
853 return;
854 }
855 }
856
857 void
858 print_stagq(struct html *h, const struct tag *suntil)
859 {
860 struct tag *tag;
861
862 while ((tag = h->tag) != NULL) {
863 if (suntil && tag == suntil)
864 return;
865 print_ctag(h, tag);
866 }
867 }
868
869 void
870 print_paragraph(struct html *h)
871 {
872 struct tag *t;
873
874 t = print_otag(h, TAG_DIV, "c", "Pp");
875 print_tagq(h, t);
876 }
877
878
879 /***********************************************************************
880 * Low level output functions.
881 * They implement line breaking using a short static buffer.
882 ***********************************************************************/
883
884 /*
885 * Buffer one HTML output byte.
886 * If the buffer is full, flush and deactivate it and start a new line.
887 * If the buffer is inactive, print directly.
888 */
889 static void
890 print_byte(struct html *h, char c)
891 {
892 if ((h->flags & HTML_BUFFER) == 0) {
893 putchar(c);
894 h->col++;
895 return;
896 }
897
898 if (h->col + h->bufcol < sizeof(h->buf)) {
899 h->buf[h->bufcol++] = c;
900 return;
901 }
902
903 putchar('\n');
904 h->col = 0;
905 print_indent(h);
906 putchar(' ');
907 putchar(' ');
908 fwrite(h->buf, h->bufcol, 1, stdout);
909 putchar(c);
910 h->col = (h->indent + 1) * 2 + h->bufcol + 1;
911 h->bufcol = 0;
912 h->flags &= ~HTML_BUFFER;
913 }
914
915 /*
916 * If something was printed on the current output line, end it.
917 * Not to be called right after print_indent().
918 */
919 void
920 print_endline(struct html *h)
921 {
922 if (h->col == 0)
923 return;
924
925 if (h->bufcol) {
926 putchar(' ');
927 fwrite(h->buf, h->bufcol, 1, stdout);
928 h->bufcol = 0;
929 }
930 putchar('\n');
931 h->col = 0;
932 h->flags |= HTML_NOSPACE;
933 h->flags &= ~HTML_BUFFER;
934 }
935
936 /*
937 * Flush the HTML output buffer.
938 * If it is inactive, activate it.
939 */
940 static void
941 print_endword(struct html *h)
942 {
943 if (h->noindent) {
944 print_byte(h, ' ');
945 return;
946 }
947
948 if ((h->flags & HTML_BUFFER) == 0) {
949 h->col++;
950 h->flags |= HTML_BUFFER;
951 } else if (h->bufcol) {
952 putchar(' ');
953 fwrite(h->buf, h->bufcol, 1, stdout);
954 h->col += h->bufcol + 1;
955 }
956 h->bufcol = 0;
957 }
958
959 /*
960 * If at the beginning of a new output line,
961 * perform indentation and mark the line as containing output.
962 * Make sure to really produce some output right afterwards,
963 * but do not use print_otag() for producing it.
964 */
965 static void
966 print_indent(struct html *h)
967 {
968 size_t i;
969
970 if (h->col)
971 return;
972
973 if (h->noindent == 0) {
974 h->col = h->indent * 2;
975 for (i = 0; i < h->col; i++)
976 putchar(' ');
977 }
978 h->flags &= ~HTML_NOSPACE;
979 }
980
981 /*
982 * Print or buffer some characters
983 * depending on the current HTML output buffer state.
984 */
985 static void
986 print_word(struct html *h, const char *cp)
987 {
988 while (*cp != '\0')
989 print_byte(h, *cp++);
990 }
991
992 /*
993 * Calculate the scaling unit passed in a `-width' argument. This uses
994 * either a native scaling unit (e.g., 1i, 2m) or the string length of
995 * the value.
996 */
997 static void
998 a2width(const char *p, struct roffsu *su)
999 {
1000 const char *end;
1001
1002 end = a2roffsu(p, su, SCALE_MAX);
1003 if (end == NULL || *end != '\0') {
1004 su->unit = SCALE_EN;
1005 su->scale = html_strlen(p);
1006 } else if (su->scale < 0.0)
1007 su->scale = 0.0;
1008 }