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