]> git.cameronkatri.com Git - mandoc.git/blob - html.c
When checking the validity of cross references with -Tlint,
[mandoc.git] / html.c
1 /* $Id: html.c,v 1.215 2017/06/23 02:32:12 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011-2015, 2017 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
172 /*
173 * Print a default style-sheet.
174 */
175
176 t = print_otag(h, TAG_STYLE, "");
177 print_text(h, "table.head, table.foot { width: 100%; }");
178 print_endline(h);
179 print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }");
180 print_endline(h);
181 print_text(h, "td.head-vol { text-align: center; }");
182 print_endline(h);
183 print_text(h, "div.Pp { margin: 1ex 0ex; }");
184 print_tagq(h, t);
185
186 if (h->style)
187 print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet",
188 h->style, "type", "text/css", "media", "all");
189 }
190
191 static void
192 print_metaf(struct html *h, enum mandoc_esc deco)
193 {
194 enum htmlfont font;
195
196 switch (deco) {
197 case ESCAPE_FONTPREV:
198 font = h->metal;
199 break;
200 case ESCAPE_FONTITALIC:
201 font = HTMLFONT_ITALIC;
202 break;
203 case ESCAPE_FONTBOLD:
204 font = HTMLFONT_BOLD;
205 break;
206 case ESCAPE_FONTBI:
207 font = HTMLFONT_BI;
208 break;
209 case ESCAPE_FONT:
210 case ESCAPE_FONTROMAN:
211 font = HTMLFONT_NONE;
212 break;
213 default:
214 abort();
215 }
216
217 if (h->metaf) {
218 print_tagq(h, h->metaf);
219 h->metaf = NULL;
220 }
221
222 h->metal = h->metac;
223 h->metac = font;
224
225 switch (font) {
226 case HTMLFONT_ITALIC:
227 h->metaf = print_otag(h, TAG_I, "");
228 break;
229 case HTMLFONT_BOLD:
230 h->metaf = print_otag(h, TAG_B, "");
231 break;
232 case HTMLFONT_BI:
233 h->metaf = print_otag(h, TAG_B, "");
234 print_otag(h, TAG_I, "");
235 break;
236 default:
237 break;
238 }
239 }
240
241 char *
242 html_make_id(const struct roff_node *n)
243 {
244 const struct roff_node *nch;
245 char *buf, *cp;
246
247 for (nch = n->child; nch != NULL; nch = nch->next)
248 if (nch->type != ROFFT_TEXT)
249 return NULL;
250
251 buf = NULL;
252 deroff(&buf, n);
253
254 /* http://www.w3.org/TR/html5/dom.html#the-id-attribute */
255
256 for (cp = buf; *cp != '\0'; cp++)
257 if (*cp == ' ')
258 *cp = '_';
259
260 return buf;
261 }
262
263 int
264 html_strlen(const char *cp)
265 {
266 size_t rsz;
267 int skip, sz;
268
269 /*
270 * Account for escaped sequences within string length
271 * calculations. This follows the logic in term_strlen() as we
272 * must calculate the width of produced strings.
273 * Assume that characters are always width of "1". This is
274 * hacky, but it gets the job done for approximation of widths.
275 */
276
277 sz = 0;
278 skip = 0;
279 while (1) {
280 rsz = strcspn(cp, "\\");
281 if (rsz) {
282 cp += rsz;
283 if (skip) {
284 skip = 0;
285 rsz--;
286 }
287 sz += rsz;
288 }
289 if ('\0' == *cp)
290 break;
291 cp++;
292 switch (mandoc_escape(&cp, NULL, NULL)) {
293 case ESCAPE_ERROR:
294 return sz;
295 case ESCAPE_UNICODE:
296 case ESCAPE_NUMBERED:
297 case ESCAPE_SPECIAL:
298 case ESCAPE_OVERSTRIKE:
299 if (skip)
300 skip = 0;
301 else
302 sz++;
303 break;
304 case ESCAPE_SKIPCHAR:
305 skip = 1;
306 break;
307 default:
308 break;
309 }
310 }
311 return sz;
312 }
313
314 static int
315 print_escape(struct html *h, char c)
316 {
317
318 switch (c) {
319 case '<':
320 print_word(h, "&lt;");
321 break;
322 case '>':
323 print_word(h, "&gt;");
324 break;
325 case '&':
326 print_word(h, "&amp;");
327 break;
328 case '"':
329 print_word(h, "&quot;");
330 break;
331 case ASCII_NBRSP:
332 print_word(h, "&nbsp;");
333 break;
334 case ASCII_HYPH:
335 print_byte(h, '-');
336 break;
337 case ASCII_BREAK:
338 break;
339 default:
340 return 0;
341 }
342 return 1;
343 }
344
345 static int
346 print_encode(struct html *h, const char *p, const char *pend, int norecurse)
347 {
348 char numbuf[16];
349 struct tag *t;
350 const char *seq;
351 size_t sz;
352 int c, len, breakline, nospace;
353 enum mandoc_esc esc;
354 static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"',
355 ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' };
356
357 if (pend == NULL)
358 pend = strchr(p, '\0');
359
360 breakline = 0;
361 nospace = 0;
362
363 while (p < pend) {
364 if (HTML_SKIPCHAR & h->flags && '\\' != *p) {
365 h->flags &= ~HTML_SKIPCHAR;
366 p++;
367 continue;
368 }
369
370 for (sz = strcspn(p, rejs); sz-- && p < pend; p++)
371 print_byte(h, *p);
372
373 if (breakline &&
374 (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) {
375 t = print_otag(h, TAG_DIV, "");
376 print_text(h, "\\~");
377 print_tagq(h, t);
378 breakline = 0;
379 while (p < pend && (*p == ' ' || *p == ASCII_NBRSP))
380 p++;
381 continue;
382 }
383
384 if (p >= pend)
385 break;
386
387 if (*p == ' ') {
388 print_endword(h);
389 p++;
390 continue;
391 }
392
393 if (print_escape(h, *p++))
394 continue;
395
396 esc = mandoc_escape(&p, &seq, &len);
397 if (ESCAPE_ERROR == esc)
398 break;
399
400 switch (esc) {
401 case ESCAPE_FONT:
402 case ESCAPE_FONTPREV:
403 case ESCAPE_FONTBOLD:
404 case ESCAPE_FONTITALIC:
405 case ESCAPE_FONTBI:
406 case ESCAPE_FONTROMAN:
407 if (0 == norecurse)
408 print_metaf(h, esc);
409 continue;
410 case ESCAPE_SKIPCHAR:
411 h->flags |= HTML_SKIPCHAR;
412 continue;
413 default:
414 break;
415 }
416
417 if (h->flags & HTML_SKIPCHAR) {
418 h->flags &= ~HTML_SKIPCHAR;
419 continue;
420 }
421
422 switch (esc) {
423 case ESCAPE_UNICODE:
424 /* Skip past "u" header. */
425 c = mchars_num2uc(seq + 1, len - 1);
426 break;
427 case ESCAPE_NUMBERED:
428 c = mchars_num2char(seq, len);
429 if (c < 0)
430 continue;
431 break;
432 case ESCAPE_SPECIAL:
433 c = mchars_spec2cp(seq, len);
434 if (c <= 0)
435 continue;
436 break;
437 case ESCAPE_BREAK:
438 breakline = 1;
439 continue;
440 case ESCAPE_NOSPACE:
441 if ('\0' == *p)
442 nospace = 1;
443 continue;
444 case ESCAPE_OVERSTRIKE:
445 if (len == 0)
446 continue;
447 c = seq[len - 1];
448 break;
449 default:
450 continue;
451 }
452 if ((c < 0x20 && c != 0x09) ||
453 (c > 0x7E && c < 0xA0))
454 c = 0xFFFD;
455 if (c > 0x7E) {
456 (void)snprintf(numbuf, sizeof(numbuf), "&#%d;", c);
457 print_word(h, numbuf);
458 } else if (print_escape(h, c) == 0)
459 print_byte(h, c);
460 }
461
462 return nospace;
463 }
464
465 static void
466 print_href(struct html *h, const char *name, const char *sec, int man)
467 {
468 const char *p, *pp;
469
470 pp = man ? h->base_man : h->base_includes;
471 while ((p = strchr(pp, '%')) != NULL) {
472 print_encode(h, pp, p, 1);
473 if (man && p[1] == 'S') {
474 if (sec == NULL)
475 print_byte(h, '1');
476 else
477 print_encode(h, sec, NULL, 1);
478 } else if ((man && p[1] == 'N') ||
479 (man == 0 && p[1] == 'I'))
480 print_encode(h, name, NULL, 1);
481 else
482 print_encode(h, p, p + 2, 1);
483 pp = p + 2;
484 }
485 if (*pp != '\0')
486 print_encode(h, pp, NULL, 1);
487 }
488
489 struct tag *
490 print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
491 {
492 va_list ap;
493 struct roffsu mysu, *su;
494 char numbuf[16];
495 struct tag *t;
496 const char *attr;
497 char *arg1, *arg2;
498 double v;
499 int i, have_style, tflags;
500
501 tflags = htmltags[tag].flags;
502
503 /* Push this tag onto the stack of open scopes. */
504
505 if ((tflags & HTML_NOSTACK) == 0) {
506 t = mandoc_malloc(sizeof(struct tag));
507 t->tag = tag;
508 t->next = h->tag;
509 h->tag = t;
510 } else
511 t = NULL;
512
513 if (tflags & HTML_NLBEFORE)
514 print_endline(h);
515 if (h->col == 0)
516 print_indent(h);
517 else if ((h->flags & HTML_NOSPACE) == 0) {
518 if (h->flags & HTML_KEEP)
519 print_word(h, "&#160;");
520 else {
521 if (h->flags & HTML_PREKEEP)
522 h->flags |= HTML_KEEP;
523 print_endword(h);
524 }
525 }
526
527 if ( ! (h->flags & HTML_NONOSPACE))
528 h->flags &= ~HTML_NOSPACE;
529 else
530 h->flags |= HTML_NOSPACE;
531
532 /* Print out the tag name and attributes. */
533
534 print_byte(h, '<');
535 print_word(h, htmltags[tag].name);
536
537 va_start(ap, fmt);
538
539 have_style = 0;
540 while (*fmt != '\0') {
541 if (*fmt == 's') {
542 have_style = 1;
543 fmt++;
544 break;
545 }
546
547 /* Parse a non-style attribute and its arguments. */
548
549 arg1 = va_arg(ap, char *);
550 switch (*fmt++) {
551 case 'c':
552 attr = "class";
553 break;
554 case 'h':
555 attr = "href";
556 break;
557 case 'i':
558 attr = "id";
559 break;
560 case '?':
561 attr = arg1;
562 arg1 = va_arg(ap, char *);
563 break;
564 default:
565 abort();
566 }
567 arg2 = NULL;
568 if (*fmt == 'M')
569 arg2 = va_arg(ap, char *);
570 if (arg1 == NULL)
571 continue;
572
573 /* Print the non-style attributes. */
574
575 print_byte(h, ' ');
576 print_word(h, attr);
577 print_byte(h, '=');
578 print_byte(h, '"');
579 switch (*fmt) {
580 case 'I':
581 print_href(h, arg1, NULL, 0);
582 fmt++;
583 break;
584 case 'M':
585 print_href(h, arg1, arg2, 1);
586 fmt++;
587 break;
588 case 'R':
589 print_byte(h, '#');
590 print_encode(h, arg1, NULL, 1);
591 fmt++;
592 break;
593 case 'T':
594 print_encode(h, arg1, NULL, 1);
595 print_word(h, "\" title=\"");
596 print_encode(h, arg1, NULL, 1);
597 fmt++;
598 break;
599 default:
600 print_encode(h, arg1, NULL, 1);
601 break;
602 }
603 print_byte(h, '"');
604 }
605
606 /* Print out styles. */
607
608 while (*fmt != '\0') {
609 arg1 = NULL;
610 su = NULL;
611
612 /* First letter: input argument type. */
613
614 switch (*fmt++) {
615 case 'h':
616 i = va_arg(ap, int);
617 su = &mysu;
618 SCALE_HS_INIT(su, i);
619 break;
620 case 's':
621 arg1 = va_arg(ap, char *);
622 break;
623 case 'u':
624 su = va_arg(ap, struct roffsu *);
625 break;
626 case 'v':
627 i = va_arg(ap, int);
628 su = &mysu;
629 SCALE_VS_INIT(su, i);
630 break;
631 case 'w':
632 if ((arg2 = va_arg(ap, char *)) == NULL)
633 break;
634 su = &mysu;
635 a2width(arg2, su);
636 if (*fmt == '+') {
637 /* Increase to make even bold text fit. */
638 su->scale *= 1.2;
639 /* Add padding. */
640 su->scale += 3.0;
641 fmt++;
642 }
643 if (*fmt == '-') {
644 su->scale *= -1.0;
645 fmt++;
646 }
647 break;
648 default:
649 abort();
650 }
651
652 /* Second letter: style name. */
653
654 switch (*fmt++) {
655 case 'b':
656 attr = "margin-bottom";
657 break;
658 case 'h':
659 attr = "height";
660 break;
661 case 'i':
662 attr = "text-indent";
663 break;
664 case 'l':
665 attr = "margin-left";
666 break;
667 case 't':
668 attr = "margin-top";
669 break;
670 case 'w':
671 attr = "width";
672 break;
673 case 'W':
674 attr = "min-width";
675 break;
676 case '?':
677 attr = arg1;
678 arg1 = va_arg(ap, char *);
679 break;
680 default:
681 abort();
682 }
683 if (su == NULL && arg1 == NULL)
684 continue;
685
686 if (have_style == 1)
687 print_word(h, " style=\"");
688 else
689 print_byte(h, ' ');
690 print_word(h, attr);
691 print_byte(h, ':');
692 print_byte(h, ' ');
693 if (su != NULL) {
694 v = su->scale;
695 if (su->unit == SCALE_MM && (v /= 100.0) == 0.0)
696 v = 1.0;
697 else if (su->unit == SCALE_BU)
698 v /= 24.0;
699 (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v);
700 print_word(h, numbuf);
701 print_word(h, roffscales[su->unit]);
702 } else
703 print_word(h, arg1);
704 print_byte(h, ';');
705 have_style = 2;
706 }
707 if (have_style == 2)
708 print_byte(h, '"');
709
710 va_end(ap);
711
712 /* Accommodate for "well-formed" singleton escaping. */
713
714 if (HTML_AUTOCLOSE & htmltags[tag].flags)
715 print_byte(h, '/');
716
717 print_byte(h, '>');
718
719 if (tflags & HTML_NLBEGIN)
720 print_endline(h);
721 else
722 h->flags |= HTML_NOSPACE;
723
724 if (tflags & HTML_INDENT)
725 h->indent++;
726 if (tflags & HTML_NOINDENT)
727 h->noindent++;
728
729 return t;
730 }
731
732 static void
733 print_ctag(struct html *h, struct tag *tag)
734 {
735 int tflags;
736
737 /*
738 * Remember to close out and nullify the current
739 * meta-font and table, if applicable.
740 */
741 if (tag == h->metaf)
742 h->metaf = NULL;
743 if (tag == h->tblt)
744 h->tblt = NULL;
745
746 tflags = htmltags[tag->tag].flags;
747
748 if (tflags & HTML_INDENT)
749 h->indent--;
750 if (tflags & HTML_NOINDENT)
751 h->noindent--;
752 if (tflags & HTML_NLEND)
753 print_endline(h);
754 print_indent(h);
755 print_byte(h, '<');
756 print_byte(h, '/');
757 print_word(h, htmltags[tag->tag].name);
758 print_byte(h, '>');
759 if (tflags & HTML_NLAFTER)
760 print_endline(h);
761
762 h->tag = tag->next;
763 free(tag);
764 }
765
766 void
767 print_gen_decls(struct html *h)
768 {
769 print_word(h, "<!DOCTYPE html>");
770 print_endline(h);
771 }
772
773 void
774 print_text(struct html *h, const char *word)
775 {
776 if (h->col && (h->flags & HTML_NOSPACE) == 0) {
777 if ( ! (HTML_KEEP & h->flags)) {
778 if (HTML_PREKEEP & h->flags)
779 h->flags |= HTML_KEEP;
780 print_endword(h);
781 } else
782 print_word(h, "&#160;");
783 }
784
785 assert(NULL == h->metaf);
786 switch (h->metac) {
787 case HTMLFONT_ITALIC:
788 h->metaf = print_otag(h, TAG_I, "");
789 break;
790 case HTMLFONT_BOLD:
791 h->metaf = print_otag(h, TAG_B, "");
792 break;
793 case HTMLFONT_BI:
794 h->metaf = print_otag(h, TAG_B, "");
795 print_otag(h, TAG_I, "");
796 break;
797 default:
798 print_indent(h);
799 break;
800 }
801
802 assert(word);
803 if ( ! print_encode(h, word, NULL, 0)) {
804 if ( ! (h->flags & HTML_NONOSPACE))
805 h->flags &= ~HTML_NOSPACE;
806 h->flags &= ~HTML_NONEWLINE;
807 } else
808 h->flags |= HTML_NOSPACE | HTML_NONEWLINE;
809
810 if (h->metaf) {
811 print_tagq(h, h->metaf);
812 h->metaf = NULL;
813 }
814
815 h->flags &= ~HTML_IGNDELIM;
816 }
817
818 void
819 print_tagq(struct html *h, const struct tag *until)
820 {
821 struct tag *tag;
822
823 while ((tag = h->tag) != NULL) {
824 print_ctag(h, tag);
825 if (until && tag == until)
826 return;
827 }
828 }
829
830 void
831 print_stagq(struct html *h, const struct tag *suntil)
832 {
833 struct tag *tag;
834
835 while ((tag = h->tag) != NULL) {
836 if (suntil && tag == suntil)
837 return;
838 print_ctag(h, tag);
839 }
840 }
841
842 void
843 print_paragraph(struct html *h)
844 {
845 struct tag *t;
846
847 t = print_otag(h, TAG_DIV, "c", "Pp");
848 print_tagq(h, t);
849 }
850
851
852 /***********************************************************************
853 * Low level output functions.
854 * They implement line breaking using a short static buffer.
855 ***********************************************************************/
856
857 /*
858 * Buffer one HTML output byte.
859 * If the buffer is full, flush and deactivate it and start a new line.
860 * If the buffer is inactive, print directly.
861 */
862 static void
863 print_byte(struct html *h, char c)
864 {
865 if ((h->flags & HTML_BUFFER) == 0) {
866 putchar(c);
867 h->col++;
868 return;
869 }
870
871 if (h->col + h->bufcol < sizeof(h->buf)) {
872 h->buf[h->bufcol++] = c;
873 return;
874 }
875
876 putchar('\n');
877 h->col = 0;
878 print_indent(h);
879 putchar(' ');
880 putchar(' ');
881 fwrite(h->buf, h->bufcol, 1, stdout);
882 putchar(c);
883 h->col = (h->indent + 1) * 2 + h->bufcol + 1;
884 h->bufcol = 0;
885 h->flags &= ~HTML_BUFFER;
886 }
887
888 /*
889 * If something was printed on the current output line, end it.
890 * Not to be called right after print_indent().
891 */
892 void
893 print_endline(struct html *h)
894 {
895 if (h->col == 0)
896 return;
897
898 if (h->bufcol) {
899 putchar(' ');
900 fwrite(h->buf, h->bufcol, 1, stdout);
901 h->bufcol = 0;
902 }
903 putchar('\n');
904 h->col = 0;
905 h->flags |= HTML_NOSPACE;
906 h->flags &= ~HTML_BUFFER;
907 }
908
909 /*
910 * Flush the HTML output buffer.
911 * If it is inactive, activate it.
912 */
913 static void
914 print_endword(struct html *h)
915 {
916 if (h->noindent) {
917 print_byte(h, ' ');
918 return;
919 }
920
921 if ((h->flags & HTML_BUFFER) == 0) {
922 h->col++;
923 h->flags |= HTML_BUFFER;
924 } else if (h->bufcol) {
925 putchar(' ');
926 fwrite(h->buf, h->bufcol, 1, stdout);
927 h->col += h->bufcol + 1;
928 }
929 h->bufcol = 0;
930 }
931
932 /*
933 * If at the beginning of a new output line,
934 * perform indentation and mark the line as containing output.
935 * Make sure to really produce some output right afterwards,
936 * but do not use print_otag() for producing it.
937 */
938 static void
939 print_indent(struct html *h)
940 {
941 size_t i;
942
943 if (h->col)
944 return;
945
946 if (h->noindent == 0) {
947 h->col = h->indent * 2;
948 for (i = 0; i < h->col; i++)
949 putchar(' ');
950 }
951 h->flags &= ~HTML_NOSPACE;
952 }
953
954 /*
955 * Print or buffer some characters
956 * depending on the current HTML output buffer state.
957 */
958 static void
959 print_word(struct html *h, const char *cp)
960 {
961 while (*cp != '\0')
962 print_byte(h, *cp++);
963 }
964
965 /*
966 * Calculate the scaling unit passed in a `-width' argument. This uses
967 * either a native scaling unit (e.g., 1i, 2m) or the string length of
968 * the value.
969 */
970 static void
971 a2width(const char *p, struct roffsu *su)
972 {
973 const char *end;
974
975 end = a2roffsu(p, su, SCALE_MAX);
976 if (end == NULL || *end != '\0') {
977 su->unit = SCALE_EN;
978 su->scale = html_strlen(p);
979 } else if (su->scale < 0.0)
980 su->scale = 0.0;
981 }