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