Annotation of src/usr.bin/mandoc/html.c, Revision 1.64
1.64 ! schwarze 1: /* $OpenBSD: html.c,v 1.63 2017/01/08 16:38:04 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;
39: #define HTML_CLRLINE (1 << 0)
40: #define HTML_NOSTACK (1 << 1)
1.6 schwarze 41: #define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */
1.1 schwarze 42: };
43:
44: static const struct htmldata htmltags[TAG_MAX] = {
45: {"html", HTML_CLRLINE}, /* TAG_HTML */
46: {"head", HTML_CLRLINE}, /* TAG_HEAD */
47: {"body", HTML_CLRLINE}, /* TAG_BODY */
1.6 schwarze 48: {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
1.1 schwarze 49: {"title", HTML_CLRLINE}, /* TAG_TITLE */
50: {"div", HTML_CLRLINE}, /* TAG_DIV */
51: {"h1", 0}, /* TAG_H1 */
52: {"h2", 0}, /* TAG_H2 */
53: {"span", 0}, /* TAG_SPAN */
1.8 schwarze 54: {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
1.6 schwarze 55: {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
1.1 schwarze 56: {"a", 0}, /* TAG_A */
57: {"table", HTML_CLRLINE}, /* TAG_TABLE */
1.18 schwarze 58: {"tbody", HTML_CLRLINE}, /* TAG_TBODY */
1.6 schwarze 59: {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
1.1 schwarze 60: {"tr", HTML_CLRLINE}, /* TAG_TR */
61: {"td", HTML_CLRLINE}, /* TAG_TD */
62: {"li", HTML_CLRLINE}, /* TAG_LI */
63: {"ul", HTML_CLRLINE}, /* TAG_UL */
64: {"ol", HTML_CLRLINE}, /* TAG_OL */
1.18 schwarze 65: {"dl", HTML_CLRLINE}, /* TAG_DL */
66: {"dt", HTML_CLRLINE}, /* TAG_DT */
67: {"dd", HTML_CLRLINE}, /* TAG_DD */
68: {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
69: {"pre", HTML_CLRLINE }, /* TAG_PRE */
1.19 schwarze 70: {"b", 0 }, /* TAG_B */
71: {"i", 0 }, /* TAG_I */
1.20 schwarze 72: {"code", 0 }, /* TAG_CODE */
73: {"small", 0 }, /* TAG_SMALL */
1.42 schwarze 74: {"style", HTML_CLRLINE}, /* TAG_STYLE */
1.43 schwarze 75: {"math", HTML_CLRLINE}, /* TAG_MATH */
76: {"mrow", 0}, /* TAG_MROW */
77: {"mi", 0}, /* TAG_MI */
78: {"mo", 0}, /* TAG_MO */
79: {"msup", 0}, /* TAG_MSUP */
80: {"msub", 0}, /* TAG_MSUB */
81: {"msubsup", 0}, /* TAG_MSUBSUP */
82: {"mfrac", 0}, /* TAG_MFRAC */
83: {"msqrt", 0}, /* TAG_MSQRT */
84: {"mfenced", 0}, /* TAG_MFENCED */
85: {"mtable", 0}, /* TAG_MTABLE */
86: {"mtr", 0}, /* TAG_MTR */
87: {"mtd", 0}, /* TAG_MTD */
1.44 schwarze 88: {"munderover", 0}, /* TAG_MUNDEROVER */
89: {"munder", 0}, /* TAG_MUNDER*/
90: {"mover", 0}, /* TAG_MOVER*/
1.5 schwarze 91: };
92:
1.26 schwarze 93: static const char *const roffscales[SCALE_MAX] = {
94: "cm", /* SCALE_CM */
95: "in", /* SCALE_IN */
96: "pc", /* SCALE_PC */
97: "pt", /* SCALE_PT */
98: "em", /* SCALE_EM */
99: "em", /* SCALE_MM */
100: "ex", /* SCALE_EN */
101: "ex", /* SCALE_BU */
102: "em", /* SCALE_VS */
103: "ex", /* SCALE_FS */
104: };
1.5 schwarze 105:
1.64 ! schwarze 106: static void a2width(const char *, struct roffsu *);
1.26 schwarze 107: static void bufncat(struct html *, const char *, size_t);
1.54 schwarze 108: static void print_ctag(struct html *, struct tag *);
1.38 schwarze 109: static int print_escape(char);
1.26 schwarze 110: static int print_encode(struct html *, const char *, int);
111: static void print_metaf(struct html *, enum mandoc_esc);
112: static void print_attr(struct html *, const char *, const char *);
1.5 schwarze 113:
1.35 schwarze 114:
1.50 schwarze 115: void *
1.61 schwarze 116: html_alloc(const struct manoutput *outopts)
1.1 schwarze 117: {
118: struct html *h;
119:
1.24 schwarze 120: h = mandoc_calloc(1, sizeof(struct html));
1.1 schwarze 121:
1.2 schwarze 122: h->tags.head = NULL;
1.56 schwarze 123: h->style = outopts->style;
124: h->base_man = outopts->man;
125: h->base_includes = outopts->includes;
126: if (outopts->fragment)
127: h->oflags |= HTML_FRAGMENT;
1.1 schwarze 128:
1.58 schwarze 129: return h;
1.1 schwarze 130: }
131:
132: void
133: html_free(void *p)
134: {
135: struct tag *tag;
136: struct html *h;
137:
138: h = (struct html *)p;
139:
1.2 schwarze 140: while ((tag = h->tags.head) != NULL) {
1.35 schwarze 141: h->tags.head = tag->next;
1.1 schwarze 142: free(tag);
143: }
144:
145: free(h);
146: }
147:
148: void
149: print_gen_head(struct html *h)
150: {
1.42 schwarze 151: struct tag *t;
152:
1.64 ! schwarze 153: print_otag(h, TAG_META, "?", "charset", "utf-8");
1.1 schwarze 154:
1.42 schwarze 155: /*
156: * Print a default style-sheet.
157: */
1.64 ! schwarze 158: t = print_otag(h, TAG_STYLE, "");
1.42 schwarze 159: print_text(h, "table.head, table.foot { width: 100%; }\n"
160: "td.head-rtitle, td.foot-os { text-align: right; }\n"
161: "td.head-vol { text-align: center; }\n"
162: "table.foot td { width: 50%; }\n"
163: "table.head td { width: 33%; }\n"
164: "div.spacer { margin: 1em 0; }\n");
165: print_tagq(h, t);
1.1 schwarze 166:
1.64 ! schwarze 167: if (h->style)
! 168: print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet",
! 169: h->style, "type", "text/css", "media", "all");
1.1 schwarze 170: }
171:
1.5 schwarze 172: static void
1.26 schwarze 173: print_metaf(struct html *h, enum mandoc_esc deco)
1.5 schwarze 174: {
175: enum htmlfont font;
1.1 schwarze 176:
1.5 schwarze 177: switch (deco) {
1.35 schwarze 178: case ESCAPE_FONTPREV:
1.5 schwarze 179: font = h->metal;
180: break;
1.35 schwarze 181: case ESCAPE_FONTITALIC:
1.5 schwarze 182: font = HTMLFONT_ITALIC;
183: break;
1.35 schwarze 184: case ESCAPE_FONTBOLD:
1.5 schwarze 185: font = HTMLFONT_BOLD;
186: break;
1.35 schwarze 187: case ESCAPE_FONTBI:
1.31 schwarze 188: font = HTMLFONT_BI;
189: break;
1.35 schwarze 190: case ESCAPE_FONT:
191: case ESCAPE_FONTROMAN:
1.5 schwarze 192: font = HTMLFONT_NONE;
193: break;
194: default:
195: abort();
1.1 schwarze 196: }
197:
1.20 schwarze 198: if (h->metaf) {
199: print_tagq(h, h->metaf);
200: h->metaf = NULL;
201: }
202:
203: h->metal = h->metac;
204: h->metac = font;
205:
1.31 schwarze 206: switch (font) {
1.35 schwarze 207: case HTMLFONT_ITALIC:
1.64 ! schwarze 208: h->metaf = print_otag(h, TAG_I, "");
1.31 schwarze 209: break;
1.35 schwarze 210: case HTMLFONT_BOLD:
1.64 ! schwarze 211: h->metaf = print_otag(h, TAG_B, "");
1.31 schwarze 212: break;
1.35 schwarze 213: case HTMLFONT_BI:
1.64 ! schwarze 214: h->metaf = print_otag(h, TAG_B, "");
! 215: print_otag(h, TAG_I, "");
1.31 schwarze 216: break;
217: default:
218: break;
219: }
1.1 schwarze 220: }
221:
1.26 schwarze 222: int
223: html_strlen(const char *cp)
224: {
1.30 schwarze 225: size_t rsz;
226: int skip, sz;
1.26 schwarze 227:
228: /*
229: * Account for escaped sequences within string length
230: * calculations. This follows the logic in term_strlen() as we
231: * must calculate the width of produced strings.
232: * Assume that characters are always width of "1". This is
233: * hacky, but it gets the job done for approximation of widths.
234: */
235:
236: sz = 0;
1.30 schwarze 237: skip = 0;
238: while (1) {
239: rsz = strcspn(cp, "\\");
240: if (rsz) {
241: cp += rsz;
242: if (skip) {
243: skip = 0;
244: rsz--;
245: }
246: sz += rsz;
247: }
248: if ('\0' == *cp)
249: break;
250: cp++;
251: switch (mandoc_escape(&cp, NULL, NULL)) {
1.35 schwarze 252: case ESCAPE_ERROR:
1.58 schwarze 253: return sz;
1.35 schwarze 254: case ESCAPE_UNICODE:
255: case ESCAPE_NUMBERED:
256: case ESCAPE_SPECIAL:
1.55 schwarze 257: case ESCAPE_OVERSTRIKE:
1.30 schwarze 258: if (skip)
259: skip = 0;
260: else
261: sz++;
262: break;
1.35 schwarze 263: case ESCAPE_SKIPCHAR:
1.30 schwarze 264: skip = 1;
1.26 schwarze 265: break;
266: default:
267: break;
268: }
269: }
1.58 schwarze 270: return sz;
1.26 schwarze 271: }
1.1 schwarze 272:
1.5 schwarze 273: static int
1.38 schwarze 274: print_escape(char c)
275: {
276:
277: switch (c) {
278: case '<':
279: printf("<");
280: break;
281: case '>':
282: printf(">");
283: break;
284: case '&':
285: printf("&");
286: break;
287: case '"':
288: printf(""");
289: break;
290: case ASCII_NBRSP:
1.60 schwarze 291: printf(" ");
1.38 schwarze 292: break;
293: case ASCII_HYPH:
294: putchar('-');
1.59 schwarze 295: break;
1.38 schwarze 296: case ASCII_BREAK:
297: break;
298: default:
1.58 schwarze 299: return 0;
1.38 schwarze 300: }
1.58 schwarze 301: return 1;
1.38 schwarze 302: }
303:
304: static int
1.5 schwarze 305: print_encode(struct html *h, const char *p, int norecurse)
1.1 schwarze 306: {
1.4 schwarze 307: size_t sz;
1.26 schwarze 308: int c, len, nospace;
1.5 schwarze 309: const char *seq;
1.26 schwarze 310: enum mandoc_esc esc;
1.37 schwarze 311: static const char rejs[9] = { '\\', '<', '>', '&', '"',
1.33 schwarze 312: ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' };
1.5 schwarze 313:
314: nospace = 0;
1.1 schwarze 315:
1.26 schwarze 316: while ('\0' != *p) {
1.30 schwarze 317: if (HTML_SKIPCHAR & h->flags && '\\' != *p) {
318: h->flags &= ~HTML_SKIPCHAR;
319: p++;
320: continue;
321: }
322:
1.9 schwarze 323: sz = strcspn(p, rejs);
1.4 schwarze 324:
325: fwrite(p, 1, sz, stdout);
1.26 schwarze 326: p += (int)sz;
1.4 schwarze 327:
1.26 schwarze 328: if ('\0' == *p)
329: break;
330:
1.38 schwarze 331: if (print_escape(*p++))
1.33 schwarze 332: continue;
1.4 schwarze 333:
1.26 schwarze 334: esc = mandoc_escape(&p, &seq, &len);
335: if (ESCAPE_ERROR == esc)
336: break;
1.5 schwarze 337:
1.26 schwarze 338: switch (esc) {
1.35 schwarze 339: case ESCAPE_FONT:
340: case ESCAPE_FONTPREV:
341: case ESCAPE_FONTBOLD:
342: case ESCAPE_FONTITALIC:
343: case ESCAPE_FONTBI:
344: case ESCAPE_FONTROMAN:
1.30 schwarze 345: if (0 == norecurse)
346: print_metaf(h, esc);
347: continue;
1.35 schwarze 348: case ESCAPE_SKIPCHAR:
1.30 schwarze 349: h->flags |= HTML_SKIPCHAR;
350: continue;
351: default:
352: break;
353: }
354:
355: if (h->flags & HTML_SKIPCHAR) {
356: h->flags &= ~HTML_SKIPCHAR;
357: continue;
358: }
359:
360: switch (esc) {
1.35 schwarze 361: case ESCAPE_UNICODE:
1.38 schwarze 362: /* Skip past "u" header. */
1.26 schwarze 363: c = mchars_num2uc(seq + 1, len - 1);
364: break;
1.35 schwarze 365: case ESCAPE_NUMBERED:
1.26 schwarze 366: c = mchars_num2char(seq, len);
1.51 schwarze 367: if (c < 0)
368: continue;
1.26 schwarze 369: break;
1.35 schwarze 370: case ESCAPE_SPECIAL:
1.61 schwarze 371: c = mchars_spec2cp(seq, len);
1.51 schwarze 372: if (c <= 0)
373: continue;
1.26 schwarze 374: break;
1.35 schwarze 375: case ESCAPE_NOSPACE:
1.26 schwarze 376: if ('\0' == *p)
377: nospace = 1;
1.49 schwarze 378: continue;
1.55 schwarze 379: case ESCAPE_OVERSTRIKE:
380: if (len == 0)
381: continue;
382: c = seq[len - 1];
383: break;
1.5 schwarze 384: default:
1.49 schwarze 385: continue;
1.5 schwarze 386: }
1.51 schwarze 387: if ((c < 0x20 && c != 0x09) ||
388: (c > 0x7E && c < 0xA0))
1.49 schwarze 389: c = 0xFFFD;
390: if (c > 0x7E)
391: printf("&#%d;", c);
392: else if ( ! print_escape(c))
393: putchar(c);
1.1 schwarze 394: }
1.5 schwarze 395:
1.58 schwarze 396: return nospace;
1.1 schwarze 397: }
398:
1.6 schwarze 399: static void
400: print_attr(struct html *h, const char *key, const char *val)
401: {
402: printf(" %s=\"", key);
403: (void)print_encode(h, val, 1);
404: putchar('\"');
405: }
406:
1.1 schwarze 407: struct tag *
1.64 ! schwarze 408: print_otag(struct html *h, enum htmltag tag, const char *fmt, ...)
1.1 schwarze 409: {
1.64 ! schwarze 410: va_list ap;
! 411: struct roffsu mysu, *su;
1.1 schwarze 412: struct tag *t;
1.64 ! schwarze 413: char *s;
! 414: int i, have_style;
1.1 schwarze 415:
1.6 schwarze 416: /* Push this tags onto the stack of open scopes. */
417:
1.1 schwarze 418: if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
1.24 schwarze 419: t = mandoc_malloc(sizeof(struct tag));
1.1 schwarze 420: t->tag = tag;
1.2 schwarze 421: t->next = h->tags.head;
422: h->tags.head = t;
1.1 schwarze 423: } else
424: t = NULL;
425:
426: if ( ! (HTML_NOSPACE & h->flags))
1.12 schwarze 427: if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
428: /* Manage keeps! */
429: if ( ! (HTML_KEEP & h->flags)) {
430: if (HTML_PREKEEP & h->flags)
431: h->flags |= HTML_KEEP;
432: putchar(' ');
433: } else
434: printf(" ");
435: }
1.1 schwarze 436:
1.13 schwarze 437: if ( ! (h->flags & HTML_NONOSPACE))
438: h->flags &= ~HTML_NOSPACE;
1.14 schwarze 439: else
440: h->flags |= HTML_NOSPACE;
1.13 schwarze 441:
1.6 schwarze 442: /* Print out the tag name and attributes. */
443:
1.1 schwarze 444: printf("<%s", htmltags[tag].name);
1.64 ! schwarze 445:
! 446: va_start(ap, fmt);
! 447:
! 448: have_style = 0;
! 449: while (*fmt != '\0') {
! 450: if (*fmt == 's') {
! 451: printf(" style=\"");
! 452: have_style = 1;
! 453: fmt++;
! 454: break;
! 455: }
! 456: s = va_arg(ap, char *);
! 457: switch (*fmt++) {
! 458: case 'c':
! 459: print_attr(h, "class", s);
! 460: break;
! 461: case 'h':
! 462: print_attr(h, "href", s);
! 463: break;
! 464: case 'i':
! 465: print_attr(h, "id", s);
! 466: break;
! 467: case '?':
! 468: print_attr(h, s, va_arg(ap, char *));
! 469: break;
! 470: default:
! 471: abort();
! 472: }
! 473: }
! 474:
! 475: /* Print out styles. */
! 476:
! 477: s = NULL;
! 478: su = &mysu;
! 479: while (*fmt != '\0') {
! 480:
! 481: /* First letter: input argument type. */
! 482:
! 483: switch (*fmt++) {
! 484: case 'h':
! 485: i = va_arg(ap, int);
! 486: SCALE_HS_INIT(su, i);
! 487: break;
! 488: case 's':
! 489: s = va_arg(ap, char *);
! 490: break;
! 491: case 'u':
! 492: su = va_arg(ap, struct roffsu *);
! 493: break;
! 494: case 'v':
! 495: i = va_arg(ap, int);
! 496: SCALE_VS_INIT(su, i);
! 497: break;
! 498: case 'w':
! 499: s = va_arg(ap, char *);
! 500: a2width(s, su);
! 501: break;
! 502: default:
! 503: abort();
! 504: }
! 505:
! 506: /* Second letter: style name. */
! 507:
! 508: bufinit(h);
! 509: switch (*fmt++) {
! 510: case 'b':
! 511: bufcat_su(h, "margin-bottom", su);
! 512: break;
! 513: case 'h':
! 514: bufcat_su(h, "height", su);
! 515: break;
! 516: case 'i':
! 517: bufcat_su(h, "text-indent", su);
! 518: break;
! 519: case 'l':
! 520: bufcat_su(h, "margin-left", su);
! 521: break;
! 522: case 't':
! 523: bufcat_su(h, "margin-top", su);
! 524: break;
! 525: case 'w':
! 526: bufcat_su(h, "width", su);
! 527: break;
! 528: case 'W':
! 529: bufcat_su(h, "min-width", su);
! 530: break;
! 531: case '?':
! 532: bufcat_style(h, s, va_arg(ap, char *));
! 533: break;
! 534: default:
! 535: abort();
! 536: }
! 537: printf("%s", h->buf);
! 538: }
! 539: if (have_style)
! 540: putchar('"');
! 541:
! 542: va_end(ap);
1.6 schwarze 543:
1.42 schwarze 544: /* Accommodate for "well-formed" singleton escaping. */
1.6 schwarze 545:
546: if (HTML_AUTOCLOSE & htmltags[tag].flags)
1.42 schwarze 547: putchar('/');
1.6 schwarze 548:
1.4 schwarze 549: putchar('>');
1.1 schwarze 550:
551: h->flags |= HTML_NOSPACE;
1.18 schwarze 552:
553: if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
554: putchar('\n');
555:
1.58 schwarze 556: return t;
1.1 schwarze 557: }
558:
559: static void
1.54 schwarze 560: print_ctag(struct html *h, struct tag *tag)
1.1 schwarze 561: {
1.35 schwarze 562:
1.54 schwarze 563: /*
564: * Remember to close out and nullify the current
565: * meta-font and table, if applicable.
566: */
567: if (tag == h->metaf)
568: h->metaf = NULL;
569: if (tag == h->tblt)
570: h->tblt = NULL;
571:
572: printf("</%s>", htmltags[tag->tag].name);
573: if (HTML_CLRLINE & htmltags[tag->tag].flags) {
1.1 schwarze 574: h->flags |= HTML_NOSPACE;
1.4 schwarze 575: putchar('\n');
1.35 schwarze 576: }
1.54 schwarze 577:
578: h->tags.head = tag->next;
579: free(tag);
1.1 schwarze 580: }
581:
582: void
1.6 schwarze 583: print_gen_decls(struct html *h)
584: {
585:
1.42 schwarze 586: puts("<!DOCTYPE html>");
1.1 schwarze 587: }
588:
589: void
1.12 schwarze 590: print_text(struct html *h, const char *word)
1.1 schwarze 591: {
592:
1.12 schwarze 593: if ( ! (HTML_NOSPACE & h->flags)) {
594: /* Manage keeps! */
595: if ( ! (HTML_KEEP & h->flags)) {
596: if (HTML_PREKEEP & h->flags)
597: h->flags |= HTML_KEEP;
598: putchar(' ');
599: } else
600: printf(" ");
601: }
1.1 schwarze 602:
1.20 schwarze 603: assert(NULL == h->metaf);
1.31 schwarze 604: switch (h->metac) {
1.35 schwarze 605: case HTMLFONT_ITALIC:
1.64 ! schwarze 606: h->metaf = print_otag(h, TAG_I, "");
1.31 schwarze 607: break;
1.35 schwarze 608: case HTMLFONT_BOLD:
1.64 ! schwarze 609: h->metaf = print_otag(h, TAG_B, "");
1.31 schwarze 610: break;
1.35 schwarze 611: case HTMLFONT_BI:
1.64 ! schwarze 612: h->metaf = print_otag(h, TAG_B, "");
! 613: print_otag(h, TAG_I, "");
1.31 schwarze 614: break;
615: default:
616: break;
617: }
1.20 schwarze 618:
1.12 schwarze 619: assert(word);
1.28 schwarze 620: if ( ! print_encode(h, word, 0)) {
1.13 schwarze 621: if ( ! (h->flags & HTML_NONOSPACE))
622: h->flags &= ~HTML_NOSPACE;
1.53 schwarze 623: h->flags &= ~HTML_NONEWLINE;
1.28 schwarze 624: } else
1.53 schwarze 625: h->flags |= HTML_NOSPACE | HTML_NONEWLINE;
1.20 schwarze 626:
627: if (h->metaf) {
628: print_tagq(h, h->metaf);
629: h->metaf = NULL;
630: }
1.17 schwarze 631:
632: h->flags &= ~HTML_IGNDELIM;
1.1 schwarze 633: }
634:
635: void
636: print_tagq(struct html *h, const struct tag *until)
637: {
638: struct tag *tag;
639:
1.2 schwarze 640: while ((tag = h->tags.head) != NULL) {
1.54 schwarze 641: print_ctag(h, tag);
1.1 schwarze 642: if (until && tag == until)
643: return;
644: }
645: }
646:
647: void
648: print_stagq(struct html *h, const struct tag *suntil)
649: {
650: struct tag *tag;
651:
1.2 schwarze 652: while ((tag = h->tags.head) != NULL) {
1.1 schwarze 653: if (suntil && tag == suntil)
654: return;
1.54 schwarze 655: print_ctag(h, tag);
1.1 schwarze 656: }
657: }
1.42 schwarze 658:
659: void
660: print_paragraph(struct html *h)
661: {
662: struct tag *t;
663:
1.64 ! schwarze 664: t = print_otag(h, TAG_DIV, "c", "spacer");
1.42 schwarze 665: print_tagq(h, t);
666: }
667:
1.64 ! schwarze 668:
! 669: /*
! 670: * Calculate the scaling unit passed in a `-width' argument. This uses
! 671: * either a native scaling unit (e.g., 1i, 2m) or the string length of
! 672: * the value.
! 673: */
! 674: static void
! 675: a2width(const char *p, struct roffsu *su)
! 676: {
! 677: if (a2roffsu(p, su, SCALE_MAX) < 2) {
! 678: su->unit = SCALE_EN;
! 679: su->scale = html_strlen(p);
! 680: } else if (su->scale < 0.0)
! 681: su->scale = 0.0;
! 682: }
1.1 schwarze 683:
684: void
685: bufinit(struct html *h)
686: {
687:
688: h->buf[0] = '\0';
689: h->buflen = 0;
690: }
691:
692: void
693: bufcat_style(struct html *h, const char *key, const char *val)
694: {
695:
696: bufcat(h, key);
1.26 schwarze 697: bufcat(h, ":");
1.1 schwarze 698: bufcat(h, val);
1.26 schwarze 699: bufcat(h, ";");
1.1 schwarze 700: }
701:
702: void
703: bufcat(struct html *h, const char *p)
704: {
1.36 schwarze 705:
706: /*
707: * XXX This is broken and not easy to fix.
708: * When using the -Oincludes option, buffmt_includes()
709: * may pass in strings overrunning BUFSIZ, causing a crash.
710: */
1.1 schwarze 711:
1.26 schwarze 712: h->buflen = strlcat(h->buf, p, BUFSIZ);
713: assert(h->buflen < BUFSIZ);
1.1 schwarze 714: }
715:
716: void
1.26 schwarze 717: bufcat_fmt(struct html *h, const char *fmt, ...)
1.1 schwarze 718: {
719: va_list ap;
720:
721: va_start(ap, fmt);
1.35 schwarze 722: (void)vsnprintf(h->buf + (int)h->buflen,
723: BUFSIZ - h->buflen - 1, fmt, ap);
1.1 schwarze 724: va_end(ap);
725: h->buflen = strlen(h->buf);
726: }
727:
1.26 schwarze 728: static void
1.1 schwarze 729: bufncat(struct html *h, const char *p, size_t sz)
730: {
731:
1.26 schwarze 732: assert(h->buflen + sz + 1 < BUFSIZ);
733: strncat(h->buf, p, sz);
1.1 schwarze 734: h->buflen += sz;
735: }
736:
737: void
738: buffmt_includes(struct html *h, const char *name)
739: {
740: const char *p, *pp;
741:
742: pp = h->base_includes;
1.35 schwarze 743:
1.26 schwarze 744: bufinit(h);
1.1 schwarze 745: while (NULL != (p = strchr(pp, '%'))) {
746: bufncat(h, pp, (size_t)(p - pp));
747: switch (*(p + 1)) {
1.63 schwarze 748: case 'I':
1.1 schwarze 749: bufcat(h, name);
750: break;
751: default:
752: bufncat(h, p, 2);
753: break;
754: }
755: pp = p + 2;
756: }
757: if (pp)
758: bufcat(h, pp);
759: }
760:
761: void
1.35 schwarze 762: buffmt_man(struct html *h, const char *name, const char *sec)
1.1 schwarze 763: {
764: const char *p, *pp;
765:
766: pp = h->base_man;
1.35 schwarze 767:
1.26 schwarze 768: bufinit(h);
1.1 schwarze 769: while (NULL != (p = strchr(pp, '%'))) {
770: bufncat(h, pp, (size_t)(p - pp));
771: switch (*(p + 1)) {
1.35 schwarze 772: case 'S':
1.1 schwarze 773: bufcat(h, sec ? sec : "1");
774: break;
1.35 schwarze 775: case 'N':
1.32 schwarze 776: bufcat_fmt(h, "%s", name);
1.1 schwarze 777: break;
778: default:
779: bufncat(h, p, 2);
780: break;
781: }
782: pp = p + 2;
783: }
784: if (pp)
785: bufcat(h, pp);
786: }
787:
788: void
789: bufcat_su(struct html *h, const char *p, const struct roffsu *su)
790: {
791: double v;
792:
793: v = su->scale;
1.26 schwarze 794: if (SCALE_MM == su->unit && 0.0 == (v /= 100.0))
795: v = 1.0;
1.40 schwarze 796: else if (SCALE_BU == su->unit)
797: v /= 24.0;
1.1 schwarze 798:
1.26 schwarze 799: bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]);
1.1 schwarze 800: }
801:
1.3 schwarze 802: void
1.26 schwarze 803: bufcat_id(struct html *h, const char *src)
1.3 schwarze 804: {
805:
1.62 bentley 806: /* Cf. <http://www.w3.org/TR/html5/dom.html#the-id-attribute>. */
1.3 schwarze 807:
1.62 bentley 808: for (; '\0' != *src; src++)
809: bufncat(h, *src == ' ' ? "_" : src, 1);
1.3 schwarze 810: }