]> git.cameronkatri.com Git - mandoc.git/blob - html.c
Cleanup, no functional change:
[mandoc.git] / html.c
1 /* $Id: html.c,v 1.245 2018/12/04 18:29:38 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 #include <sys/stat.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdarg.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "mandoc_aux.h"
34 #include "mandoc_ohash.h"
35 #include "mandoc.h"
36 #include "roff.h"
37 #include "out.h"
38 #include "html.h"
39 #include "manconf.h"
40 #include "main.h"
41
42 struct htmldata {
43 const char *name;
44 int flags;
45 #define HTML_NOSTACK (1 << 0)
46 #define HTML_AUTOCLOSE (1 << 1)
47 #define HTML_NLBEFORE (1 << 2)
48 #define HTML_NLBEGIN (1 << 3)
49 #define HTML_NLEND (1 << 4)
50 #define HTML_NLAFTER (1 << 5)
51 #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER)
52 #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND)
53 #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE)
54 #define HTML_INDENT (1 << 6)
55 #define HTML_NOINDENT (1 << 7)
56 };
57
58 static const struct htmldata htmltags[TAG_MAX] = {
59 {"html", HTML_NLALL},
60 {"head", HTML_NLALL | HTML_INDENT},
61 {"body", HTML_NLALL},
62 {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
63 {"title", HTML_NLAROUND},
64 {"div", HTML_NLAROUND},
65 {"div", 0},
66 {"h1", HTML_NLAROUND},
67 {"h2", HTML_NLAROUND},
68 {"span", 0},
69 {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
70 {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL},
71 {"a", 0},
72 {"table", HTML_NLALL | HTML_INDENT},
73 {"tr", HTML_NLALL | HTML_INDENT},
74 {"td", HTML_NLAROUND},
75 {"li", HTML_NLAROUND | HTML_INDENT},
76 {"ul", HTML_NLALL | HTML_INDENT},
77 {"ol", HTML_NLALL | HTML_INDENT},
78 {"dl", HTML_NLALL | HTML_INDENT},
79 {"dt", HTML_NLAROUND},
80 {"dd", HTML_NLAROUND | HTML_INDENT},
81 {"pre", HTML_NLALL | HTML_NOINDENT},
82 {"var", 0},
83 {"cite", 0},
84 {"b", 0},
85 {"i", 0},
86 {"code", 0},
87 {"small", 0},
88 {"style", HTML_NLALL | HTML_INDENT},
89 {"math", HTML_NLALL | HTML_INDENT},
90 {"mrow", 0},
91 {"mi", 0},
92 {"mn", 0},
93 {"mo", 0},
94 {"msup", 0},
95 {"msub", 0},
96 {"msubsup", 0},
97 {"mfrac", 0},
98 {"msqrt", 0},
99 {"mfenced", 0},
100 {"mtable", 0},
101 {"mtr", 0},
102 {"mtd", 0},
103 {"munderover", 0},
104 {"munder", 0},
105 {"mover", 0},
106 };
107
108 /* Avoid duplicate HTML id= attributes. */
109 static struct ohash id_unique;
110
111 static void print_byte(struct html *, char);
112 static void print_endword(struct html *);
113 static void print_indent(struct html *);
114 static void print_word(struct html *, const char *);
115
116 static void print_ctag(struct html *, struct tag *);
117 static int print_escape(struct html *, char);
118 static int print_encode(struct html *, const char *, const char *, int);
119 static void print_href(struct html *, const char *, const char *, int);
120 static void print_metaf(struct html *, enum mandoc_esc);
121
122
123 void *
124 html_alloc(const struct manoutput *outopts)
125 {
126 struct html *h;
127
128 h = mandoc_calloc(1, sizeof(struct html));
129
130 h->tag = NULL;
131 h->style = outopts->style;
132 if ((h->base_man1 = outopts->man) == NULL)
133 h->base_man2 = NULL;
134 else if ((h->base_man2 = strchr(h->base_man1, ';')) != NULL)
135 *h->base_man2++ = '\0';
136 h->base_includes = outopts->includes;
137 if (outopts->fragment)
138 h->oflags |= HTML_FRAGMENT;
139 if (outopts->toc)
140 h->oflags |= HTML_TOC;
141
142 mandoc_ohash_init(&id_unique, 4, 0);
143
144 return h;
145 }
146
147 void
148 html_free(void *p)
149 {
150 struct tag *tag;
151 struct html *h;
152 char *cp;
153 unsigned int slot;
154
155 h = (struct html *)p;
156 while ((tag = h->tag) != NULL) {
157 h->tag = tag->next;
158 free(tag);
159 }
160 free(h);
161
162 cp = ohash_first(&id_unique, &slot);
163 while (cp != NULL) {
164 free(cp);
165 cp = ohash_next(&id_unique, &slot);
166 }
167 ohash_delete(&id_unique);
168 }
169
170 void
171 print_gen_head(struct html *h)
172 {
173 struct tag *t;
174
175 print_otag(h, TAG_META, "?", "charset", "utf-8");
176 if (h->style != NULL) {
177 print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet",
178 h->style, "type", "text/css", "media", "all");
179 return;
180 }
181
182 /*
183 * Print a minimal embedded style sheet.
184 */
185
186 t = print_otag(h, TAG_STYLE, "");
187 print_text(h, "table.head, table.foot { width: 100%; }");
188 print_endline(h);
189 print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }");
190 print_endline(h);
191 print_text(h, "td.head-vol { text-align: center; }");
192 print_endline(h);
193 print_text(h, "div.Pp { margin: 1ex 0ex; }");
194 print_endline(h);
195 print_text(h, "div.Nd, div.Bf, div.Op { display: inline; }");
196 print_endline(h);
197 print_text(h, "span.Pa, span.Ad { font-style: italic; }");
198 print_endline(h);
199 print_text(h, "span.Ms { font-weight: bold; }");
200 print_endline(h);
201 print_text(h, "dl.Bl-diag ");
202 print_byte(h, '>');
203 print_text(h, " dt { font-weight: bold; }");
204 print_endline(h);
205 print_text(h, "code.Nm, code.Fl, code.Cm, code.Ic, "
206 "code.In, code.Fd, code.Fn,");
207 print_endline(h);
208 print_text(h, "code.Cd { font-weight: bold; "
209 "font-family: inherit; }");
210 print_tagq(h, t);
211 }
212
213 static void
214 print_metaf(struct html *h, enum mandoc_esc deco)
215 {
216 enum htmlfont font;
217
218 switch (deco) {
219 case ESCAPE_FONTPREV:
220 font = h->metal;
221 break;
222 case ESCAPE_FONTITALIC:
223 font = HTMLFONT_ITALIC;
224 break;
225 case ESCAPE_FONTBOLD:
226 font = HTMLFONT_BOLD;
227 break;
228 case ESCAPE_FONTBI:
229 font = HTMLFONT_BI;
230 break;
231 case ESCAPE_FONTCW:
232 font = HTMLFONT_CW;
233 break;
234 case ESCAPE_FONT:
235 case ESCAPE_FONTROMAN:
236 font = HTMLFONT_NONE;
237 break;
238 default:
239 abort();
240 }
241
242 if (h->metaf) {
243 print_tagq(h, h->metaf);
244 h->metaf = NULL;
245 }
246
247 h->metal = h->metac;
248 h->metac = font;
249
250 switch (font) {
251 case HTMLFONT_ITALIC:
252 h->metaf = print_otag(h, TAG_I, "");
253 break;
254 case HTMLFONT_BOLD:
255 h->metaf = print_otag(h, TAG_B, "");
256 break;
257 case HTMLFONT_BI:
258 h->metaf = print_otag(h, TAG_B, "");
259 print_otag(h, TAG_I, "");
260 break;
261 case HTMLFONT_CW:
262 h->metaf = print_otag(h, TAG_SPAN, "c", "Li");
263 break;
264 default:
265 break;
266 }
267 }
268
269 char *
270 html_make_id(const struct roff_node *n, int unique)
271 {
272 const struct roff_node *nch;
273 char *buf, *bufs, *cp;
274 unsigned int slot;
275 int suffix;
276
277 for (nch = n->child; nch != NULL; nch = nch->next)
278 if (nch->type != ROFFT_TEXT)
279 return NULL;
280
281 buf = NULL;
282 deroff(&buf, n);
283 if (buf == NULL)
284 return NULL;
285
286 /*
287 * In ID attributes, only use ASCII characters that are
288 * permitted in URL-fragment strings according to the
289 * explicit list at:
290 * https://url.spec.whatwg.org/#url-fragment-string
291 */
292
293 for (cp = buf; *cp != '\0'; cp++)
294 if (isalnum((unsigned char)*cp) == 0 &&
295 strchr("!$&'()*+,-./:;=?@_~", *cp) == NULL)
296 *cp = '_';
297
298 if (unique == 0)
299 return buf;
300
301 /* Avoid duplicate HTML id= attributes. */
302
303 bufs = NULL;
304 suffix = 1;
305 slot = ohash_qlookup(&id_unique, buf);
306 cp = ohash_find(&id_unique, slot);
307 if (cp != NULL) {
308 while (cp != NULL) {
309 free(bufs);
310 if (++suffix > 127) {
311 free(buf);
312 return NULL;
313 }
314 mandoc_asprintf(&bufs, "%s_%d", buf, suffix);
315 slot = ohash_qlookup(&id_unique, bufs);
316 cp = ohash_find(&id_unique, slot);
317 }
318 free(buf);
319 buf = bufs;
320 }
321 ohash_insert(&id_unique, slot, buf);
322 return buf;
323 }
324
325 static int
326 print_escape(struct html *h, char c)
327 {
328
329 switch (c) {
330 case '<':
331 print_word(h, "&lt;");
332 break;
333 case '>':
334 print_word(h, "&gt;");
335 break;
336 case '&':
337 print_word(h, "&amp;");
338 break;
339 case '"':
340 print_word(h, "&quot;");
341 break;
342 case ASCII_NBRSP:
343 print_word(h, "&nbsp;");
344 break;
345 case ASCII_HYPH:
346 print_byte(h, '-');
347 break;
348 case ASCII_BREAK:
349 break;
350 default:
351 return 0;
352 }
353 return 1;
354 }
355
356 static int
357 print_encode(struct html *h, const char *p, const char *pend, int norecurse)
358 {
359 char numbuf[16];
360 const char *seq;
361 size_t sz;
362 int c, len, breakline, nospace;
363 enum mandoc_esc esc;
364 static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"',
365 ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' };
366
367 if (pend == NULL)
368 pend = strchr(p, '\0');
369
370 breakline = 0;
371 nospace = 0;
372
373 while (p < pend) {
374 if (HTML_SKIPCHAR & h->flags && '\\' != *p) {
375 h->flags &= ~HTML_SKIPCHAR;
376 p++;
377 continue;
378 }
379
380 for (sz = strcspn(p, rejs); sz-- && p < pend; p++)
381 print_byte(h, *p);
382
383 if (breakline &&
384 (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) {
385 print_otag(h, TAG_BR, "");
386 breakline = 0;
387 while (p < pend && (*p == ' ' || *p == ASCII_NBRSP))
388 p++;
389 continue;
390 }
391
392 if (p >= pend)
393 break;
394
395 if (*p == ' ') {
396 print_endword(h);
397 p++;
398 continue;
399 }
400
401 if (print_escape(h, *p++))
402 continue;
403
404 esc = mandoc_escape(&p, &seq, &len);
405 if (ESCAPE_ERROR == esc)
406 break;
407
408 switch (esc) {
409 case ESCAPE_FONT:
410 case ESCAPE_FONTPREV:
411 case ESCAPE_FONTBOLD:
412 case ESCAPE_FONTITALIC:
413 case ESCAPE_FONTBI:
414 case ESCAPE_FONTCW:
415 case ESCAPE_FONTROMAN:
416 if (0 == norecurse) {
417 h->flags |= HTML_NOSPACE;
418 print_metaf(h, esc);
419 h->flags &= ~HTML_NOSPACE;
420 }
421 continue;
422 case ESCAPE_SKIPCHAR:
423 h->flags |= HTML_SKIPCHAR;
424 continue;
425 default:
426 break;
427 }
428
429 if (h->flags & HTML_SKIPCHAR) {
430 h->flags &= ~HTML_SKIPCHAR;
431 continue;
432 }
433
434 switch (esc) {
435 case ESCAPE_UNICODE:
436 /* Skip past "u" header. */
437 c = mchars_num2uc(seq + 1, len - 1);
438 break;
439 case ESCAPE_NUMBERED:
440 c = mchars_num2char(seq, len);
441 if (c < 0)
442 continue;
443 break;
444 case ESCAPE_SPECIAL:
445 c = mchars_spec2cp(seq, len);
446 if (c <= 0)
447 continue;
448 break;
449 case ESCAPE_DEVICE:
450 print_word(h, "html");
451 continue;
452 case ESCAPE_BREAK:
453 breakline = 1;
454 continue;
455 case ESCAPE_NOSPACE:
456 if ('\0' == *p)
457 nospace = 1;
458 continue;
459 case ESCAPE_OVERSTRIKE:
460 if (len == 0)
461 continue;
462 c = seq[len - 1];
463 break;
464 default:
465 continue;
466 }
467 if ((c < 0x20 && c != 0x09) ||
468 (c > 0x7E && c < 0xA0))
469 c = 0xFFFD;
470 if (c > 0x7E) {
471 (void)snprintf(numbuf, sizeof(numbuf), "&#x%.4X;", c);
472 print_word(h, numbuf);
473 } else if (print_escape(h, c) == 0)
474 print_byte(h, c);
475 }
476
477 return nospace;
478 }
479
480 static void
481 print_href(struct html *h, const char *name, const char *sec, int man)
482 {
483 struct stat sb;
484 const char *p, *pp;
485 char *filename;
486
487 if (man) {
488 pp = h->base_man1;
489 if (h->base_man2 != NULL) {
490 mandoc_asprintf(&filename, "%s.%s", name, sec);
491 if (stat(filename, &sb) == -1)
492 pp = h->base_man2;
493 free(filename);
494 }
495 } else
496 pp = h->base_includes;
497
498 while ((p = strchr(pp, '%')) != NULL) {
499 print_encode(h, pp, p, 1);
500 if (man && p[1] == 'S') {
501 if (sec == NULL)
502 print_byte(h, '1');
503 else
504 print_encode(h, sec, NULL, 1);
505 } else if ((man && p[1] == 'N') ||
506 (man == 0 && p[1] == 'I'))
507 print_encode(h, name, NULL, 1);
508 else
509 print_encode(h, p, p + 2, 1);
510 pp = p + 2;
511 }
512 if (*pp != '\0')
513 print_encode(h, pp, NULL, 1);
514 }
515
516 struct tag *
517 print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
518 {
519 va_list ap;
520 struct tag *t;
521 const char *attr;
522 char *arg1, *arg2;
523 int style_written, tflags;
524
525 tflags = htmltags[tag].flags;
526
527 /* Push this tag onto the stack of open scopes. */
528
529 if ((tflags & HTML_NOSTACK) == 0) {
530 t = mandoc_malloc(sizeof(struct tag));
531 t->tag = tag;
532 t->next = h->tag;
533 h->tag = t;
534 } else
535 t = NULL;
536
537 if (tflags & HTML_NLBEFORE)
538 print_endline(h);
539 if (h->col == 0)
540 print_indent(h);
541 else if ((h->flags & HTML_NOSPACE) == 0) {
542 if (h->flags & HTML_KEEP)
543 print_word(h, "&#x00A0;");
544 else {
545 if (h->flags & HTML_PREKEEP)
546 h->flags |= HTML_KEEP;
547 print_endword(h);
548 }
549 }
550
551 if ( ! (h->flags & HTML_NONOSPACE))
552 h->flags &= ~HTML_NOSPACE;
553 else
554 h->flags |= HTML_NOSPACE;
555
556 /* Print out the tag name and attributes. */
557
558 print_byte(h, '<');
559 print_word(h, htmltags[tag].name);
560
561 va_start(ap, fmt);
562
563 while (*fmt != '\0' && *fmt != 's') {
564
565 /* Parse attributes and arguments. */
566
567 arg1 = va_arg(ap, char *);
568 arg2 = NULL;
569 switch (*fmt++) {
570 case 'c':
571 attr = "class";
572 break;
573 case 'h':
574 attr = "href";
575 break;
576 case 'i':
577 attr = "id";
578 break;
579 case '?':
580 attr = arg1;
581 arg1 = va_arg(ap, char *);
582 break;
583 default:
584 abort();
585 }
586 if (*fmt == 'M')
587 arg2 = va_arg(ap, char *);
588 if (arg1 == NULL)
589 continue;
590
591 /* Print the attributes. */
592
593 print_byte(h, ' ');
594 print_word(h, attr);
595 print_byte(h, '=');
596 print_byte(h, '"');
597 switch (*fmt) {
598 case 'I':
599 print_href(h, arg1, NULL, 0);
600 fmt++;
601 break;
602 case 'M':
603 print_href(h, arg1, arg2, 1);
604 fmt++;
605 break;
606 case 'R':
607 print_byte(h, '#');
608 print_encode(h, arg1, NULL, 1);
609 fmt++;
610 break;
611 case 'T':
612 print_encode(h, arg1, NULL, 1);
613 print_word(h, "\" title=\"");
614 print_encode(h, arg1, NULL, 1);
615 fmt++;
616 break;
617 default:
618 print_encode(h, arg1, NULL, 1);
619 break;
620 }
621 print_byte(h, '"');
622 }
623
624 style_written = 0;
625 while (*fmt++ == 's') {
626 arg1 = va_arg(ap, char *);
627 arg2 = va_arg(ap, char *);
628 if (arg2 == NULL)
629 continue;
630 print_byte(h, ' ');
631 if (style_written == 0) {
632 print_word(h, "style=\"");
633 style_written = 1;
634 }
635 print_word(h, arg1);
636 print_byte(h, ':');
637 print_byte(h, ' ');
638 print_word(h, arg2);
639 print_byte(h, ';');
640 }
641 if (style_written)
642 print_byte(h, '"');
643
644 va_end(ap);
645
646 /* Accommodate for "well-formed" singleton escaping. */
647
648 if (HTML_AUTOCLOSE & htmltags[tag].flags)
649 print_byte(h, '/');
650
651 print_byte(h, '>');
652
653 if (tflags & HTML_NLBEGIN)
654 print_endline(h);
655 else
656 h->flags |= HTML_NOSPACE;
657
658 if (tflags & HTML_INDENT)
659 h->indent++;
660 if (tflags & HTML_NOINDENT)
661 h->noindent++;
662
663 return t;
664 }
665
666 static void
667 print_ctag(struct html *h, struct tag *tag)
668 {
669 int tflags;
670
671 /*
672 * Remember to close out and nullify the current
673 * meta-font and table, if applicable.
674 */
675 if (tag == h->metaf)
676 h->metaf = NULL;
677 if (tag == h->tblt)
678 h->tblt = NULL;
679
680 tflags = htmltags[tag->tag].flags;
681
682 if (tflags & HTML_INDENT)
683 h->indent--;
684 if (tflags & HTML_NOINDENT)
685 h->noindent--;
686 if (tflags & HTML_NLEND)
687 print_endline(h);
688 print_indent(h);
689 print_byte(h, '<');
690 print_byte(h, '/');
691 print_word(h, htmltags[tag->tag].name);
692 print_byte(h, '>');
693 if (tflags & HTML_NLAFTER)
694 print_endline(h);
695
696 h->tag = tag->next;
697 free(tag);
698 }
699
700 void
701 print_gen_decls(struct html *h)
702 {
703 print_word(h, "<!DOCTYPE html>");
704 print_endline(h);
705 }
706
707 void
708 print_gen_comment(struct html *h, struct roff_node *n)
709 {
710 int wantblank;
711
712 print_word(h, "<!-- This is an automatically generated file."
713 " Do not edit.");
714 h->indent = 1;
715 wantblank = 0;
716 while (n != NULL && n->type == ROFFT_COMMENT) {
717 if (strstr(n->string, "-->") == NULL &&
718 (wantblank || *n->string != '\0')) {
719 print_endline(h);
720 print_indent(h);
721 print_word(h, n->string);
722 wantblank = *n->string != '\0';
723 }
724 n = n->next;
725 }
726 if (wantblank)
727 print_endline(h);
728 print_word(h, " -->");
729 print_endline(h);
730 h->indent = 0;
731 }
732
733 void
734 print_text(struct html *h, const char *word)
735 {
736 if (h->col && (h->flags & HTML_NOSPACE) == 0) {
737 if ( ! (HTML_KEEP & h->flags)) {
738 if (HTML_PREKEEP & h->flags)
739 h->flags |= HTML_KEEP;
740 print_endword(h);
741 } else
742 print_word(h, "&#x00A0;");
743 }
744
745 assert(NULL == h->metaf);
746 switch (h->metac) {
747 case HTMLFONT_ITALIC:
748 h->metaf = print_otag(h, TAG_I, "");
749 break;
750 case HTMLFONT_BOLD:
751 h->metaf = print_otag(h, TAG_B, "");
752 break;
753 case HTMLFONT_BI:
754 h->metaf = print_otag(h, TAG_B, "");
755 print_otag(h, TAG_I, "");
756 break;
757 case HTMLFONT_CW:
758 h->metaf = print_otag(h, TAG_SPAN, "c", "Li");
759 break;
760 default:
761 print_indent(h);
762 break;
763 }
764
765 assert(word);
766 if ( ! print_encode(h, word, NULL, 0)) {
767 if ( ! (h->flags & HTML_NONOSPACE))
768 h->flags &= ~HTML_NOSPACE;
769 h->flags &= ~HTML_NONEWLINE;
770 } else
771 h->flags |= HTML_NOSPACE | HTML_NONEWLINE;
772
773 if (h->metaf) {
774 print_tagq(h, h->metaf);
775 h->metaf = NULL;
776 }
777
778 h->flags &= ~HTML_IGNDELIM;
779 }
780
781 void
782 print_tagq(struct html *h, const struct tag *until)
783 {
784 struct tag *tag;
785
786 while ((tag = h->tag) != NULL) {
787 print_ctag(h, tag);
788 if (until && tag == until)
789 return;
790 }
791 }
792
793 void
794 print_stagq(struct html *h, const struct tag *suntil)
795 {
796 struct tag *tag;
797
798 while ((tag = h->tag) != NULL) {
799 if (suntil && tag == suntil)
800 return;
801 print_ctag(h, tag);
802 }
803 }
804
805 void
806 print_paragraph(struct html *h)
807 {
808 struct tag *t;
809
810 t = print_otag(h, TAG_DIV, "c", "Pp");
811 print_tagq(h, t);
812 }
813
814
815 /***********************************************************************
816 * Low level output functions.
817 * They implement line breaking using a short static buffer.
818 ***********************************************************************/
819
820 /*
821 * Buffer one HTML output byte.
822 * If the buffer is full, flush and deactivate it and start a new line.
823 * If the buffer is inactive, print directly.
824 */
825 static void
826 print_byte(struct html *h, char c)
827 {
828 if ((h->flags & HTML_BUFFER) == 0) {
829 putchar(c);
830 h->col++;
831 return;
832 }
833
834 if (h->col + h->bufcol < sizeof(h->buf)) {
835 h->buf[h->bufcol++] = c;
836 return;
837 }
838
839 putchar('\n');
840 h->col = 0;
841 print_indent(h);
842 putchar(' ');
843 putchar(' ');
844 fwrite(h->buf, h->bufcol, 1, stdout);
845 putchar(c);
846 h->col = (h->indent + 1) * 2 + h->bufcol + 1;
847 h->bufcol = 0;
848 h->flags &= ~HTML_BUFFER;
849 }
850
851 /*
852 * If something was printed on the current output line, end it.
853 * Not to be called right after print_indent().
854 */
855 void
856 print_endline(struct html *h)
857 {
858 if (h->col == 0)
859 return;
860
861 if (h->bufcol) {
862 putchar(' ');
863 fwrite(h->buf, h->bufcol, 1, stdout);
864 h->bufcol = 0;
865 }
866 putchar('\n');
867 h->col = 0;
868 h->flags |= HTML_NOSPACE;
869 h->flags &= ~HTML_BUFFER;
870 }
871
872 /*
873 * Flush the HTML output buffer.
874 * If it is inactive, activate it.
875 */
876 static void
877 print_endword(struct html *h)
878 {
879 if (h->noindent) {
880 print_byte(h, ' ');
881 return;
882 }
883
884 if ((h->flags & HTML_BUFFER) == 0) {
885 h->col++;
886 h->flags |= HTML_BUFFER;
887 } else if (h->bufcol) {
888 putchar(' ');
889 fwrite(h->buf, h->bufcol, 1, stdout);
890 h->col += h->bufcol + 1;
891 }
892 h->bufcol = 0;
893 }
894
895 /*
896 * If at the beginning of a new output line,
897 * perform indentation and mark the line as containing output.
898 * Make sure to really produce some output right afterwards,
899 * but do not use print_otag() for producing it.
900 */
901 static void
902 print_indent(struct html *h)
903 {
904 size_t i;
905
906 if (h->col)
907 return;
908
909 if (h->noindent == 0) {
910 h->col = h->indent * 2;
911 for (i = 0; i < h->col; i++)
912 putchar(' ');
913 }
914 h->flags &= ~HTML_NOSPACE;
915 }
916
917 /*
918 * Print or buffer some characters
919 * depending on the current HTML output buffer state.
920 */
921 static void
922 print_word(struct html *h, const char *cp)
923 {
924 while (*cp != '\0')
925 print_byte(h, *cp++);
926 }