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