1*95c635efSGarrett D'Amore /* $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */ 2*95c635efSGarrett D'Amore /* 3*95c635efSGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*95c635efSGarrett D'Amore * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> 5*95c635efSGarrett D'Amore * 6*95c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 7*95c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 8*95c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 9*95c635efSGarrett D'Amore * 10*95c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*95c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*95c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*95c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*95c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*95c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*95c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*95c635efSGarrett D'Amore */ 18*95c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H 19*95c635efSGarrett D'Amore #include "config.h" 20*95c635efSGarrett D'Amore #endif 21*95c635efSGarrett D'Amore 22*95c635efSGarrett D'Amore #include <sys/types.h> 23*95c635efSGarrett D'Amore 24*95c635efSGarrett D'Amore #include <assert.h> 25*95c635efSGarrett D'Amore #include <ctype.h> 26*95c635efSGarrett D'Amore #include <stdarg.h> 27*95c635efSGarrett D'Amore #include <stdio.h> 28*95c635efSGarrett D'Amore #include <stdint.h> 29*95c635efSGarrett D'Amore #include <stdlib.h> 30*95c635efSGarrett D'Amore #include <string.h> 31*95c635efSGarrett D'Amore #include <unistd.h> 32*95c635efSGarrett D'Amore 33*95c635efSGarrett D'Amore #include "mandoc.h" 34*95c635efSGarrett D'Amore #include "libmandoc.h" 35*95c635efSGarrett D'Amore #include "out.h" 36*95c635efSGarrett D'Amore #include "html.h" 37*95c635efSGarrett D'Amore #include "main.h" 38*95c635efSGarrett D'Amore 39*95c635efSGarrett D'Amore struct htmldata { 40*95c635efSGarrett D'Amore const char *name; 41*95c635efSGarrett D'Amore int flags; 42*95c635efSGarrett D'Amore #define HTML_CLRLINE (1 << 0) 43*95c635efSGarrett D'Amore #define HTML_NOSTACK (1 << 1) 44*95c635efSGarrett D'Amore #define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */ 45*95c635efSGarrett D'Amore }; 46*95c635efSGarrett D'Amore 47*95c635efSGarrett D'Amore static const struct htmldata htmltags[TAG_MAX] = { 48*95c635efSGarrett D'Amore {"html", HTML_CLRLINE}, /* TAG_HTML */ 49*95c635efSGarrett D'Amore {"head", HTML_CLRLINE}, /* TAG_HEAD */ 50*95c635efSGarrett D'Amore {"body", HTML_CLRLINE}, /* TAG_BODY */ 51*95c635efSGarrett D'Amore {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */ 52*95c635efSGarrett D'Amore {"title", HTML_CLRLINE}, /* TAG_TITLE */ 53*95c635efSGarrett D'Amore {"div", HTML_CLRLINE}, /* TAG_DIV */ 54*95c635efSGarrett D'Amore {"h1", 0}, /* TAG_H1 */ 55*95c635efSGarrett D'Amore {"h2", 0}, /* TAG_H2 */ 56*95c635efSGarrett D'Amore {"span", 0}, /* TAG_SPAN */ 57*95c635efSGarrett D'Amore {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */ 58*95c635efSGarrett D'Amore {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */ 59*95c635efSGarrett D'Amore {"a", 0}, /* TAG_A */ 60*95c635efSGarrett D'Amore {"table", HTML_CLRLINE}, /* TAG_TABLE */ 61*95c635efSGarrett D'Amore {"tbody", HTML_CLRLINE}, /* TAG_TBODY */ 62*95c635efSGarrett D'Amore {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */ 63*95c635efSGarrett D'Amore {"tr", HTML_CLRLINE}, /* TAG_TR */ 64*95c635efSGarrett D'Amore {"td", HTML_CLRLINE}, /* TAG_TD */ 65*95c635efSGarrett D'Amore {"li", HTML_CLRLINE}, /* TAG_LI */ 66*95c635efSGarrett D'Amore {"ul", HTML_CLRLINE}, /* TAG_UL */ 67*95c635efSGarrett D'Amore {"ol", HTML_CLRLINE}, /* TAG_OL */ 68*95c635efSGarrett D'Amore {"dl", HTML_CLRLINE}, /* TAG_DL */ 69*95c635efSGarrett D'Amore {"dt", HTML_CLRLINE}, /* TAG_DT */ 70*95c635efSGarrett D'Amore {"dd", HTML_CLRLINE}, /* TAG_DD */ 71*95c635efSGarrett D'Amore {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */ 72*95c635efSGarrett D'Amore {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */ 73*95c635efSGarrett D'Amore {"pre", HTML_CLRLINE }, /* TAG_PRE */ 74*95c635efSGarrett D'Amore {"b", 0 }, /* TAG_B */ 75*95c635efSGarrett D'Amore {"i", 0 }, /* TAG_I */ 76*95c635efSGarrett D'Amore {"code", 0 }, /* TAG_CODE */ 77*95c635efSGarrett D'Amore {"small", 0 }, /* TAG_SMALL */ 78*95c635efSGarrett D'Amore }; 79*95c635efSGarrett D'Amore 80*95c635efSGarrett D'Amore static const char *const htmlattrs[ATTR_MAX] = { 81*95c635efSGarrett D'Amore "http-equiv", /* ATTR_HTTPEQUIV */ 82*95c635efSGarrett D'Amore "content", /* ATTR_CONTENT */ 83*95c635efSGarrett D'Amore "name", /* ATTR_NAME */ 84*95c635efSGarrett D'Amore "rel", /* ATTR_REL */ 85*95c635efSGarrett D'Amore "href", /* ATTR_HREF */ 86*95c635efSGarrett D'Amore "type", /* ATTR_TYPE */ 87*95c635efSGarrett D'Amore "media", /* ATTR_MEDIA */ 88*95c635efSGarrett D'Amore "class", /* ATTR_CLASS */ 89*95c635efSGarrett D'Amore "style", /* ATTR_STYLE */ 90*95c635efSGarrett D'Amore "width", /* ATTR_WIDTH */ 91*95c635efSGarrett D'Amore "id", /* ATTR_ID */ 92*95c635efSGarrett D'Amore "summary", /* ATTR_SUMMARY */ 93*95c635efSGarrett D'Amore "align", /* ATTR_ALIGN */ 94*95c635efSGarrett D'Amore "colspan", /* ATTR_COLSPAN */ 95*95c635efSGarrett D'Amore }; 96*95c635efSGarrett D'Amore 97*95c635efSGarrett D'Amore static const char *const roffscales[SCALE_MAX] = { 98*95c635efSGarrett D'Amore "cm", /* SCALE_CM */ 99*95c635efSGarrett D'Amore "in", /* SCALE_IN */ 100*95c635efSGarrett D'Amore "pc", /* SCALE_PC */ 101*95c635efSGarrett D'Amore "pt", /* SCALE_PT */ 102*95c635efSGarrett D'Amore "em", /* SCALE_EM */ 103*95c635efSGarrett D'Amore "em", /* SCALE_MM */ 104*95c635efSGarrett D'Amore "ex", /* SCALE_EN */ 105*95c635efSGarrett D'Amore "ex", /* SCALE_BU */ 106*95c635efSGarrett D'Amore "em", /* SCALE_VS */ 107*95c635efSGarrett D'Amore "ex", /* SCALE_FS */ 108*95c635efSGarrett D'Amore }; 109*95c635efSGarrett D'Amore 110*95c635efSGarrett D'Amore static void bufncat(struct html *, const char *, size_t); 111*95c635efSGarrett D'Amore static void print_ctag(struct html *, enum htmltag); 112*95c635efSGarrett D'Amore static int print_encode(struct html *, const char *, int); 113*95c635efSGarrett D'Amore static void print_metaf(struct html *, enum mandoc_esc); 114*95c635efSGarrett D'Amore static void print_attr(struct html *, const char *, const char *); 115*95c635efSGarrett D'Amore static void *ml_alloc(char *, enum htmltype); 116*95c635efSGarrett D'Amore 117*95c635efSGarrett D'Amore static void * 118*95c635efSGarrett D'Amore ml_alloc(char *outopts, enum htmltype type) 119*95c635efSGarrett D'Amore { 120*95c635efSGarrett D'Amore struct html *h; 121*95c635efSGarrett D'Amore const char *toks[5]; 122*95c635efSGarrett D'Amore char *v; 123*95c635efSGarrett D'Amore 124*95c635efSGarrett D'Amore toks[0] = "style"; 125*95c635efSGarrett D'Amore toks[1] = "man"; 126*95c635efSGarrett D'Amore toks[2] = "includes"; 127*95c635efSGarrett D'Amore toks[3] = "fragment"; 128*95c635efSGarrett D'Amore toks[4] = NULL; 129*95c635efSGarrett D'Amore 130*95c635efSGarrett D'Amore h = mandoc_calloc(1, sizeof(struct html)); 131*95c635efSGarrett D'Amore 132*95c635efSGarrett D'Amore h->type = type; 133*95c635efSGarrett D'Amore h->tags.head = NULL; 134*95c635efSGarrett D'Amore h->symtab = mchars_alloc(); 135*95c635efSGarrett D'Amore 136*95c635efSGarrett D'Amore while (outopts && *outopts) 137*95c635efSGarrett D'Amore switch (getsubopt(&outopts, UNCONST(toks), &v)) { 138*95c635efSGarrett D'Amore case (0): 139*95c635efSGarrett D'Amore h->style = v; 140*95c635efSGarrett D'Amore break; 141*95c635efSGarrett D'Amore case (1): 142*95c635efSGarrett D'Amore h->base_man = v; 143*95c635efSGarrett D'Amore break; 144*95c635efSGarrett D'Amore case (2): 145*95c635efSGarrett D'Amore h->base_includes = v; 146*95c635efSGarrett D'Amore break; 147*95c635efSGarrett D'Amore case (3): 148*95c635efSGarrett D'Amore h->oflags |= HTML_FRAGMENT; 149*95c635efSGarrett D'Amore break; 150*95c635efSGarrett D'Amore default: 151*95c635efSGarrett D'Amore break; 152*95c635efSGarrett D'Amore } 153*95c635efSGarrett D'Amore 154*95c635efSGarrett D'Amore return(h); 155*95c635efSGarrett D'Amore } 156*95c635efSGarrett D'Amore 157*95c635efSGarrett D'Amore void * 158*95c635efSGarrett D'Amore html_alloc(char *outopts) 159*95c635efSGarrett D'Amore { 160*95c635efSGarrett D'Amore 161*95c635efSGarrett D'Amore return(ml_alloc(outopts, HTML_HTML_4_01_STRICT)); 162*95c635efSGarrett D'Amore } 163*95c635efSGarrett D'Amore 164*95c635efSGarrett D'Amore 165*95c635efSGarrett D'Amore void * 166*95c635efSGarrett D'Amore xhtml_alloc(char *outopts) 167*95c635efSGarrett D'Amore { 168*95c635efSGarrett D'Amore 169*95c635efSGarrett D'Amore return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT)); 170*95c635efSGarrett D'Amore } 171*95c635efSGarrett D'Amore 172*95c635efSGarrett D'Amore 173*95c635efSGarrett D'Amore void 174*95c635efSGarrett D'Amore html_free(void *p) 175*95c635efSGarrett D'Amore { 176*95c635efSGarrett D'Amore struct tag *tag; 177*95c635efSGarrett D'Amore struct html *h; 178*95c635efSGarrett D'Amore 179*95c635efSGarrett D'Amore h = (struct html *)p; 180*95c635efSGarrett D'Amore 181*95c635efSGarrett D'Amore while ((tag = h->tags.head) != NULL) { 182*95c635efSGarrett D'Amore h->tags.head = tag->next; 183*95c635efSGarrett D'Amore free(tag); 184*95c635efSGarrett D'Amore } 185*95c635efSGarrett D'Amore 186*95c635efSGarrett D'Amore if (h->symtab) 187*95c635efSGarrett D'Amore mchars_free(h->symtab); 188*95c635efSGarrett D'Amore 189*95c635efSGarrett D'Amore free(h); 190*95c635efSGarrett D'Amore } 191*95c635efSGarrett D'Amore 192*95c635efSGarrett D'Amore 193*95c635efSGarrett D'Amore void 194*95c635efSGarrett D'Amore print_gen_head(struct html *h) 195*95c635efSGarrett D'Amore { 196*95c635efSGarrett D'Amore struct htmlpair tag[4]; 197*95c635efSGarrett D'Amore 198*95c635efSGarrett D'Amore tag[0].key = ATTR_HTTPEQUIV; 199*95c635efSGarrett D'Amore tag[0].val = "Content-Type"; 200*95c635efSGarrett D'Amore tag[1].key = ATTR_CONTENT; 201*95c635efSGarrett D'Amore tag[1].val = "text/html; charset=utf-8"; 202*95c635efSGarrett D'Amore print_otag(h, TAG_META, 2, tag); 203*95c635efSGarrett D'Amore 204*95c635efSGarrett D'Amore tag[0].key = ATTR_NAME; 205*95c635efSGarrett D'Amore tag[0].val = "resource-type"; 206*95c635efSGarrett D'Amore tag[1].key = ATTR_CONTENT; 207*95c635efSGarrett D'Amore tag[1].val = "document"; 208*95c635efSGarrett D'Amore print_otag(h, TAG_META, 2, tag); 209*95c635efSGarrett D'Amore 210*95c635efSGarrett D'Amore if (h->style) { 211*95c635efSGarrett D'Amore tag[0].key = ATTR_REL; 212*95c635efSGarrett D'Amore tag[0].val = "stylesheet"; 213*95c635efSGarrett D'Amore tag[1].key = ATTR_HREF; 214*95c635efSGarrett D'Amore tag[1].val = h->style; 215*95c635efSGarrett D'Amore tag[2].key = ATTR_TYPE; 216*95c635efSGarrett D'Amore tag[2].val = "text/css"; 217*95c635efSGarrett D'Amore tag[3].key = ATTR_MEDIA; 218*95c635efSGarrett D'Amore tag[3].val = "all"; 219*95c635efSGarrett D'Amore print_otag(h, TAG_LINK, 4, tag); 220*95c635efSGarrett D'Amore } 221*95c635efSGarrett D'Amore } 222*95c635efSGarrett D'Amore 223*95c635efSGarrett D'Amore static void 224*95c635efSGarrett D'Amore print_metaf(struct html *h, enum mandoc_esc deco) 225*95c635efSGarrett D'Amore { 226*95c635efSGarrett D'Amore enum htmlfont font; 227*95c635efSGarrett D'Amore 228*95c635efSGarrett D'Amore switch (deco) { 229*95c635efSGarrett D'Amore case (ESCAPE_FONTPREV): 230*95c635efSGarrett D'Amore font = h->metal; 231*95c635efSGarrett D'Amore break; 232*95c635efSGarrett D'Amore case (ESCAPE_FONTITALIC): 233*95c635efSGarrett D'Amore font = HTMLFONT_ITALIC; 234*95c635efSGarrett D'Amore break; 235*95c635efSGarrett D'Amore case (ESCAPE_FONTBOLD): 236*95c635efSGarrett D'Amore font = HTMLFONT_BOLD; 237*95c635efSGarrett D'Amore break; 238*95c635efSGarrett D'Amore case (ESCAPE_FONT): 239*95c635efSGarrett D'Amore /* FALLTHROUGH */ 240*95c635efSGarrett D'Amore case (ESCAPE_FONTROMAN): 241*95c635efSGarrett D'Amore font = HTMLFONT_NONE; 242*95c635efSGarrett D'Amore break; 243*95c635efSGarrett D'Amore default: 244*95c635efSGarrett D'Amore abort(); 245*95c635efSGarrett D'Amore /* NOTREACHED */ 246*95c635efSGarrett D'Amore } 247*95c635efSGarrett D'Amore 248*95c635efSGarrett D'Amore if (h->metaf) { 249*95c635efSGarrett D'Amore print_tagq(h, h->metaf); 250*95c635efSGarrett D'Amore h->metaf = NULL; 251*95c635efSGarrett D'Amore } 252*95c635efSGarrett D'Amore 253*95c635efSGarrett D'Amore h->metal = h->metac; 254*95c635efSGarrett D'Amore h->metac = font; 255*95c635efSGarrett D'Amore 256*95c635efSGarrett D'Amore if (HTMLFONT_NONE != font) 257*95c635efSGarrett D'Amore h->metaf = HTMLFONT_BOLD == font ? 258*95c635efSGarrett D'Amore print_otag(h, TAG_B, 0, NULL) : 259*95c635efSGarrett D'Amore print_otag(h, TAG_I, 0, NULL); 260*95c635efSGarrett D'Amore } 261*95c635efSGarrett D'Amore 262*95c635efSGarrett D'Amore int 263*95c635efSGarrett D'Amore html_strlen(const char *cp) 264*95c635efSGarrett D'Amore { 265*95c635efSGarrett D'Amore int ssz, sz; 266*95c635efSGarrett D'Amore const char *seq, *p; 267*95c635efSGarrett D'Amore 268*95c635efSGarrett D'Amore /* 269*95c635efSGarrett D'Amore * Account for escaped sequences within string length 270*95c635efSGarrett D'Amore * calculations. This follows the logic in term_strlen() as we 271*95c635efSGarrett D'Amore * must calculate the width of produced strings. 272*95c635efSGarrett D'Amore * Assume that characters are always width of "1". This is 273*95c635efSGarrett D'Amore * hacky, but it gets the job done for approximation of widths. 274*95c635efSGarrett D'Amore */ 275*95c635efSGarrett D'Amore 276*95c635efSGarrett D'Amore sz = 0; 277*95c635efSGarrett D'Amore while (NULL != (p = strchr(cp, '\\'))) { 278*95c635efSGarrett D'Amore sz += (int)(p - cp); 279*95c635efSGarrett D'Amore ++cp; 280*95c635efSGarrett D'Amore switch (mandoc_escape(&cp, &seq, &ssz)) { 281*95c635efSGarrett D'Amore case (ESCAPE_ERROR): 282*95c635efSGarrett D'Amore return(sz); 283*95c635efSGarrett D'Amore case (ESCAPE_UNICODE): 284*95c635efSGarrett D'Amore /* FALLTHROUGH */ 285*95c635efSGarrett D'Amore case (ESCAPE_NUMBERED): 286*95c635efSGarrett D'Amore /* FALLTHROUGH */ 287*95c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 288*95c635efSGarrett D'Amore sz++; 289*95c635efSGarrett D'Amore break; 290*95c635efSGarrett D'Amore default: 291*95c635efSGarrett D'Amore break; 292*95c635efSGarrett D'Amore } 293*95c635efSGarrett D'Amore } 294*95c635efSGarrett D'Amore 295*95c635efSGarrett D'Amore assert(sz >= 0); 296*95c635efSGarrett D'Amore return(sz + strlen(cp)); 297*95c635efSGarrett D'Amore } 298*95c635efSGarrett D'Amore 299*95c635efSGarrett D'Amore static int 300*95c635efSGarrett D'Amore print_encode(struct html *h, const char *p, int norecurse) 301*95c635efSGarrett D'Amore { 302*95c635efSGarrett D'Amore size_t sz; 303*95c635efSGarrett D'Amore int c, len, nospace; 304*95c635efSGarrett D'Amore const char *seq; 305*95c635efSGarrett D'Amore enum mandoc_esc esc; 306*95c635efSGarrett D'Amore static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' }; 307*95c635efSGarrett D'Amore 308*95c635efSGarrett D'Amore nospace = 0; 309*95c635efSGarrett D'Amore 310*95c635efSGarrett D'Amore while ('\0' != *p) { 311*95c635efSGarrett D'Amore sz = strcspn(p, rejs); 312*95c635efSGarrett D'Amore 313*95c635efSGarrett D'Amore fwrite(p, 1, sz, stdout); 314*95c635efSGarrett D'Amore p += (int)sz; 315*95c635efSGarrett D'Amore 316*95c635efSGarrett D'Amore if ('\0' == *p) 317*95c635efSGarrett D'Amore break; 318*95c635efSGarrett D'Amore 319*95c635efSGarrett D'Amore switch (*p++) { 320*95c635efSGarrett D'Amore case ('<'): 321*95c635efSGarrett D'Amore printf("<"); 322*95c635efSGarrett D'Amore continue; 323*95c635efSGarrett D'Amore case ('>'): 324*95c635efSGarrett D'Amore printf(">"); 325*95c635efSGarrett D'Amore continue; 326*95c635efSGarrett D'Amore case ('&'): 327*95c635efSGarrett D'Amore printf("&"); 328*95c635efSGarrett D'Amore continue; 329*95c635efSGarrett D'Amore case (ASCII_HYPH): 330*95c635efSGarrett D'Amore putchar('-'); 331*95c635efSGarrett D'Amore continue; 332*95c635efSGarrett D'Amore default: 333*95c635efSGarrett D'Amore break; 334*95c635efSGarrett D'Amore } 335*95c635efSGarrett D'Amore 336*95c635efSGarrett D'Amore esc = mandoc_escape(&p, &seq, &len); 337*95c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 338*95c635efSGarrett D'Amore break; 339*95c635efSGarrett D'Amore 340*95c635efSGarrett D'Amore switch (esc) { 341*95c635efSGarrett D'Amore case (ESCAPE_UNICODE): 342*95c635efSGarrett D'Amore /* Skip passed "u" header. */ 343*95c635efSGarrett D'Amore c = mchars_num2uc(seq + 1, len - 1); 344*95c635efSGarrett D'Amore if ('\0' != c) 345*95c635efSGarrett D'Amore printf("&#x%x;", c); 346*95c635efSGarrett D'Amore break; 347*95c635efSGarrett D'Amore case (ESCAPE_NUMBERED): 348*95c635efSGarrett D'Amore c = mchars_num2char(seq, len); 349*95c635efSGarrett D'Amore if ('\0' != c) 350*95c635efSGarrett D'Amore putchar(c); 351*95c635efSGarrett D'Amore break; 352*95c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 353*95c635efSGarrett D'Amore c = mchars_spec2cp(h->symtab, seq, len); 354*95c635efSGarrett D'Amore if (c > 0) 355*95c635efSGarrett D'Amore printf("&#%d;", c); 356*95c635efSGarrett D'Amore else if (-1 == c && 1 == len) 357*95c635efSGarrett D'Amore putchar((int)*seq); 358*95c635efSGarrett D'Amore break; 359*95c635efSGarrett D'Amore case (ESCAPE_FONT): 360*95c635efSGarrett D'Amore /* FALLTHROUGH */ 361*95c635efSGarrett D'Amore case (ESCAPE_FONTPREV): 362*95c635efSGarrett D'Amore /* FALLTHROUGH */ 363*95c635efSGarrett D'Amore case (ESCAPE_FONTBOLD): 364*95c635efSGarrett D'Amore /* FALLTHROUGH */ 365*95c635efSGarrett D'Amore case (ESCAPE_FONTITALIC): 366*95c635efSGarrett D'Amore /* FALLTHROUGH */ 367*95c635efSGarrett D'Amore case (ESCAPE_FONTROMAN): 368*95c635efSGarrett D'Amore if (norecurse) 369*95c635efSGarrett D'Amore break; 370*95c635efSGarrett D'Amore print_metaf(h, esc); 371*95c635efSGarrett D'Amore break; 372*95c635efSGarrett D'Amore case (ESCAPE_NOSPACE): 373*95c635efSGarrett D'Amore if ('\0' == *p) 374*95c635efSGarrett D'Amore nospace = 1; 375*95c635efSGarrett D'Amore break; 376*95c635efSGarrett D'Amore default: 377*95c635efSGarrett D'Amore break; 378*95c635efSGarrett D'Amore } 379*95c635efSGarrett D'Amore } 380*95c635efSGarrett D'Amore 381*95c635efSGarrett D'Amore return(nospace); 382*95c635efSGarrett D'Amore } 383*95c635efSGarrett D'Amore 384*95c635efSGarrett D'Amore 385*95c635efSGarrett D'Amore static void 386*95c635efSGarrett D'Amore print_attr(struct html *h, const char *key, const char *val) 387*95c635efSGarrett D'Amore { 388*95c635efSGarrett D'Amore printf(" %s=\"", key); 389*95c635efSGarrett D'Amore (void)print_encode(h, val, 1); 390*95c635efSGarrett D'Amore putchar('\"'); 391*95c635efSGarrett D'Amore } 392*95c635efSGarrett D'Amore 393*95c635efSGarrett D'Amore 394*95c635efSGarrett D'Amore struct tag * 395*95c635efSGarrett D'Amore print_otag(struct html *h, enum htmltag tag, 396*95c635efSGarrett D'Amore int sz, const struct htmlpair *p) 397*95c635efSGarrett D'Amore { 398*95c635efSGarrett D'Amore int i; 399*95c635efSGarrett D'Amore struct tag *t; 400*95c635efSGarrett D'Amore 401*95c635efSGarrett D'Amore /* Push this tags onto the stack of open scopes. */ 402*95c635efSGarrett D'Amore 403*95c635efSGarrett D'Amore if ( ! (HTML_NOSTACK & htmltags[tag].flags)) { 404*95c635efSGarrett D'Amore t = mandoc_malloc(sizeof(struct tag)); 405*95c635efSGarrett D'Amore t->tag = tag; 406*95c635efSGarrett D'Amore t->next = h->tags.head; 407*95c635efSGarrett D'Amore h->tags.head = t; 408*95c635efSGarrett D'Amore } else 409*95c635efSGarrett D'Amore t = NULL; 410*95c635efSGarrett D'Amore 411*95c635efSGarrett D'Amore if ( ! (HTML_NOSPACE & h->flags)) 412*95c635efSGarrett D'Amore if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { 413*95c635efSGarrett D'Amore /* Manage keeps! */ 414*95c635efSGarrett D'Amore if ( ! (HTML_KEEP & h->flags)) { 415*95c635efSGarrett D'Amore if (HTML_PREKEEP & h->flags) 416*95c635efSGarrett D'Amore h->flags |= HTML_KEEP; 417*95c635efSGarrett D'Amore putchar(' '); 418*95c635efSGarrett D'Amore } else 419*95c635efSGarrett D'Amore printf(" "); 420*95c635efSGarrett D'Amore } 421*95c635efSGarrett D'Amore 422*95c635efSGarrett D'Amore if ( ! (h->flags & HTML_NONOSPACE)) 423*95c635efSGarrett D'Amore h->flags &= ~HTML_NOSPACE; 424*95c635efSGarrett D'Amore else 425*95c635efSGarrett D'Amore h->flags |= HTML_NOSPACE; 426*95c635efSGarrett D'Amore 427*95c635efSGarrett D'Amore /* Print out the tag name and attributes. */ 428*95c635efSGarrett D'Amore 429*95c635efSGarrett D'Amore printf("<%s", htmltags[tag].name); 430*95c635efSGarrett D'Amore for (i = 0; i < sz; i++) 431*95c635efSGarrett D'Amore print_attr(h, htmlattrs[p[i].key], p[i].val); 432*95c635efSGarrett D'Amore 433*95c635efSGarrett D'Amore /* Add non-overridable attributes. */ 434*95c635efSGarrett D'Amore 435*95c635efSGarrett D'Amore if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) { 436*95c635efSGarrett D'Amore print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml"); 437*95c635efSGarrett D'Amore print_attr(h, "xml:lang", "en"); 438*95c635efSGarrett D'Amore print_attr(h, "lang", "en"); 439*95c635efSGarrett D'Amore } 440*95c635efSGarrett D'Amore 441*95c635efSGarrett D'Amore /* Accommodate for XML "well-formed" singleton escaping. */ 442*95c635efSGarrett D'Amore 443*95c635efSGarrett D'Amore if (HTML_AUTOCLOSE & htmltags[tag].flags) 444*95c635efSGarrett D'Amore switch (h->type) { 445*95c635efSGarrett D'Amore case (HTML_XHTML_1_0_STRICT): 446*95c635efSGarrett D'Amore putchar('/'); 447*95c635efSGarrett D'Amore break; 448*95c635efSGarrett D'Amore default: 449*95c635efSGarrett D'Amore break; 450*95c635efSGarrett D'Amore } 451*95c635efSGarrett D'Amore 452*95c635efSGarrett D'Amore putchar('>'); 453*95c635efSGarrett D'Amore 454*95c635efSGarrett D'Amore h->flags |= HTML_NOSPACE; 455*95c635efSGarrett D'Amore 456*95c635efSGarrett D'Amore if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) 457*95c635efSGarrett D'Amore putchar('\n'); 458*95c635efSGarrett D'Amore 459*95c635efSGarrett D'Amore return(t); 460*95c635efSGarrett D'Amore } 461*95c635efSGarrett D'Amore 462*95c635efSGarrett D'Amore 463*95c635efSGarrett D'Amore static void 464*95c635efSGarrett D'Amore print_ctag(struct html *h, enum htmltag tag) 465*95c635efSGarrett D'Amore { 466*95c635efSGarrett D'Amore 467*95c635efSGarrett D'Amore printf("</%s>", htmltags[tag].name); 468*95c635efSGarrett D'Amore if (HTML_CLRLINE & htmltags[tag].flags) { 469*95c635efSGarrett D'Amore h->flags |= HTML_NOSPACE; 470*95c635efSGarrett D'Amore putchar('\n'); 471*95c635efSGarrett D'Amore } 472*95c635efSGarrett D'Amore } 473*95c635efSGarrett D'Amore 474*95c635efSGarrett D'Amore void 475*95c635efSGarrett D'Amore print_gen_decls(struct html *h) 476*95c635efSGarrett D'Amore { 477*95c635efSGarrett D'Amore const char *doctype; 478*95c635efSGarrett D'Amore const char *dtd; 479*95c635efSGarrett D'Amore const char *name; 480*95c635efSGarrett D'Amore 481*95c635efSGarrett D'Amore switch (h->type) { 482*95c635efSGarrett D'Amore case (HTML_HTML_4_01_STRICT): 483*95c635efSGarrett D'Amore name = "HTML"; 484*95c635efSGarrett D'Amore doctype = "-//W3C//DTD HTML 4.01//EN"; 485*95c635efSGarrett D'Amore dtd = "http://www.w3.org/TR/html4/strict.dtd"; 486*95c635efSGarrett D'Amore break; 487*95c635efSGarrett D'Amore default: 488*95c635efSGarrett D'Amore puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 489*95c635efSGarrett D'Amore name = "html"; 490*95c635efSGarrett D'Amore doctype = "-//W3C//DTD XHTML 1.0 Strict//EN"; 491*95c635efSGarrett D'Amore dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; 492*95c635efSGarrett D'Amore break; 493*95c635efSGarrett D'Amore } 494*95c635efSGarrett D'Amore 495*95c635efSGarrett D'Amore printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", 496*95c635efSGarrett D'Amore name, doctype, dtd); 497*95c635efSGarrett D'Amore } 498*95c635efSGarrett D'Amore 499*95c635efSGarrett D'Amore void 500*95c635efSGarrett D'Amore print_text(struct html *h, const char *word) 501*95c635efSGarrett D'Amore { 502*95c635efSGarrett D'Amore 503*95c635efSGarrett D'Amore if ( ! (HTML_NOSPACE & h->flags)) { 504*95c635efSGarrett D'Amore /* Manage keeps! */ 505*95c635efSGarrett D'Amore if ( ! (HTML_KEEP & h->flags)) { 506*95c635efSGarrett D'Amore if (HTML_PREKEEP & h->flags) 507*95c635efSGarrett D'Amore h->flags |= HTML_KEEP; 508*95c635efSGarrett D'Amore putchar(' '); 509*95c635efSGarrett D'Amore } else 510*95c635efSGarrett D'Amore printf(" "); 511*95c635efSGarrett D'Amore } 512*95c635efSGarrett D'Amore 513*95c635efSGarrett D'Amore assert(NULL == h->metaf); 514*95c635efSGarrett D'Amore if (HTMLFONT_NONE != h->metac) 515*95c635efSGarrett D'Amore h->metaf = HTMLFONT_BOLD == h->metac ? 516*95c635efSGarrett D'Amore print_otag(h, TAG_B, 0, NULL) : 517*95c635efSGarrett D'Amore print_otag(h, TAG_I, 0, NULL); 518*95c635efSGarrett D'Amore 519*95c635efSGarrett D'Amore assert(word); 520*95c635efSGarrett D'Amore if ( ! print_encode(h, word, 0)) { 521*95c635efSGarrett D'Amore if ( ! (h->flags & HTML_NONOSPACE)) 522*95c635efSGarrett D'Amore h->flags &= ~HTML_NOSPACE; 523*95c635efSGarrett D'Amore } else 524*95c635efSGarrett D'Amore h->flags |= HTML_NOSPACE; 525*95c635efSGarrett D'Amore 526*95c635efSGarrett D'Amore if (h->metaf) { 527*95c635efSGarrett D'Amore print_tagq(h, h->metaf); 528*95c635efSGarrett D'Amore h->metaf = NULL; 529*95c635efSGarrett D'Amore } 530*95c635efSGarrett D'Amore 531*95c635efSGarrett D'Amore h->flags &= ~HTML_IGNDELIM; 532*95c635efSGarrett D'Amore } 533*95c635efSGarrett D'Amore 534*95c635efSGarrett D'Amore 535*95c635efSGarrett D'Amore void 536*95c635efSGarrett D'Amore print_tagq(struct html *h, const struct tag *until) 537*95c635efSGarrett D'Amore { 538*95c635efSGarrett D'Amore struct tag *tag; 539*95c635efSGarrett D'Amore 540*95c635efSGarrett D'Amore while ((tag = h->tags.head) != NULL) { 541*95c635efSGarrett D'Amore /* 542*95c635efSGarrett D'Amore * Remember to close out and nullify the current 543*95c635efSGarrett D'Amore * meta-font and table, if applicable. 544*95c635efSGarrett D'Amore */ 545*95c635efSGarrett D'Amore if (tag == h->metaf) 546*95c635efSGarrett D'Amore h->metaf = NULL; 547*95c635efSGarrett D'Amore if (tag == h->tblt) 548*95c635efSGarrett D'Amore h->tblt = NULL; 549*95c635efSGarrett D'Amore print_ctag(h, tag->tag); 550*95c635efSGarrett D'Amore h->tags.head = tag->next; 551*95c635efSGarrett D'Amore free(tag); 552*95c635efSGarrett D'Amore if (until && tag == until) 553*95c635efSGarrett D'Amore return; 554*95c635efSGarrett D'Amore } 555*95c635efSGarrett D'Amore } 556*95c635efSGarrett D'Amore 557*95c635efSGarrett D'Amore 558*95c635efSGarrett D'Amore void 559*95c635efSGarrett D'Amore print_stagq(struct html *h, const struct tag *suntil) 560*95c635efSGarrett D'Amore { 561*95c635efSGarrett D'Amore struct tag *tag; 562*95c635efSGarrett D'Amore 563*95c635efSGarrett D'Amore while ((tag = h->tags.head) != NULL) { 564*95c635efSGarrett D'Amore if (suntil && tag == suntil) 565*95c635efSGarrett D'Amore return; 566*95c635efSGarrett D'Amore /* 567*95c635efSGarrett D'Amore * Remember to close out and nullify the current 568*95c635efSGarrett D'Amore * meta-font and table, if applicable. 569*95c635efSGarrett D'Amore */ 570*95c635efSGarrett D'Amore if (tag == h->metaf) 571*95c635efSGarrett D'Amore h->metaf = NULL; 572*95c635efSGarrett D'Amore if (tag == h->tblt) 573*95c635efSGarrett D'Amore h->tblt = NULL; 574*95c635efSGarrett D'Amore print_ctag(h, tag->tag); 575*95c635efSGarrett D'Amore h->tags.head = tag->next; 576*95c635efSGarrett D'Amore free(tag); 577*95c635efSGarrett D'Amore } 578*95c635efSGarrett D'Amore } 579*95c635efSGarrett D'Amore 580*95c635efSGarrett D'Amore void 581*95c635efSGarrett D'Amore bufinit(struct html *h) 582*95c635efSGarrett D'Amore { 583*95c635efSGarrett D'Amore 584*95c635efSGarrett D'Amore h->buf[0] = '\0'; 585*95c635efSGarrett D'Amore h->buflen = 0; 586*95c635efSGarrett D'Amore } 587*95c635efSGarrett D'Amore 588*95c635efSGarrett D'Amore void 589*95c635efSGarrett D'Amore bufcat_style(struct html *h, const char *key, const char *val) 590*95c635efSGarrett D'Amore { 591*95c635efSGarrett D'Amore 592*95c635efSGarrett D'Amore bufcat(h, key); 593*95c635efSGarrett D'Amore bufcat(h, ":"); 594*95c635efSGarrett D'Amore bufcat(h, val); 595*95c635efSGarrett D'Amore bufcat(h, ";"); 596*95c635efSGarrett D'Amore } 597*95c635efSGarrett D'Amore 598*95c635efSGarrett D'Amore void 599*95c635efSGarrett D'Amore bufcat(struct html *h, const char *p) 600*95c635efSGarrett D'Amore { 601*95c635efSGarrett D'Amore 602*95c635efSGarrett D'Amore h->buflen = strlcat(h->buf, p, BUFSIZ); 603*95c635efSGarrett D'Amore assert(h->buflen < BUFSIZ); 604*95c635efSGarrett D'Amore } 605*95c635efSGarrett D'Amore 606*95c635efSGarrett D'Amore void 607*95c635efSGarrett D'Amore bufcat_fmt(struct html *h, const char *fmt, ...) 608*95c635efSGarrett D'Amore { 609*95c635efSGarrett D'Amore va_list ap; 610*95c635efSGarrett D'Amore 611*95c635efSGarrett D'Amore va_start(ap, fmt); 612*95c635efSGarrett D'Amore (void)vsnprintf(h->buf + (int)h->buflen, 613*95c635efSGarrett D'Amore BUFSIZ - h->buflen - 1, fmt, ap); 614*95c635efSGarrett D'Amore va_end(ap); 615*95c635efSGarrett D'Amore h->buflen = strlen(h->buf); 616*95c635efSGarrett D'Amore } 617*95c635efSGarrett D'Amore 618*95c635efSGarrett D'Amore static void 619*95c635efSGarrett D'Amore bufncat(struct html *h, const char *p, size_t sz) 620*95c635efSGarrett D'Amore { 621*95c635efSGarrett D'Amore 622*95c635efSGarrett D'Amore assert(h->buflen + sz + 1 < BUFSIZ); 623*95c635efSGarrett D'Amore strncat(h->buf, p, sz); 624*95c635efSGarrett D'Amore h->buflen += sz; 625*95c635efSGarrett D'Amore } 626*95c635efSGarrett D'Amore 627*95c635efSGarrett D'Amore void 628*95c635efSGarrett D'Amore buffmt_includes(struct html *h, const char *name) 629*95c635efSGarrett D'Amore { 630*95c635efSGarrett D'Amore const char *p, *pp; 631*95c635efSGarrett D'Amore 632*95c635efSGarrett D'Amore pp = h->base_includes; 633*95c635efSGarrett D'Amore 634*95c635efSGarrett D'Amore bufinit(h); 635*95c635efSGarrett D'Amore while (NULL != (p = strchr(pp, '%'))) { 636*95c635efSGarrett D'Amore bufncat(h, pp, (size_t)(p - pp)); 637*95c635efSGarrett D'Amore switch (*(p + 1)) { 638*95c635efSGarrett D'Amore case('I'): 639*95c635efSGarrett D'Amore bufcat(h, name); 640*95c635efSGarrett D'Amore break; 641*95c635efSGarrett D'Amore default: 642*95c635efSGarrett D'Amore bufncat(h, p, 2); 643*95c635efSGarrett D'Amore break; 644*95c635efSGarrett D'Amore } 645*95c635efSGarrett D'Amore pp = p + 2; 646*95c635efSGarrett D'Amore } 647*95c635efSGarrett D'Amore if (pp) 648*95c635efSGarrett D'Amore bufcat(h, pp); 649*95c635efSGarrett D'Amore } 650*95c635efSGarrett D'Amore 651*95c635efSGarrett D'Amore void 652*95c635efSGarrett D'Amore buffmt_man(struct html *h, 653*95c635efSGarrett D'Amore const char *name, const char *sec) 654*95c635efSGarrett D'Amore { 655*95c635efSGarrett D'Amore const char *p, *pp; 656*95c635efSGarrett D'Amore 657*95c635efSGarrett D'Amore pp = h->base_man; 658*95c635efSGarrett D'Amore 659*95c635efSGarrett D'Amore bufinit(h); 660*95c635efSGarrett D'Amore while (NULL != (p = strchr(pp, '%'))) { 661*95c635efSGarrett D'Amore bufncat(h, pp, (size_t)(p - pp)); 662*95c635efSGarrett D'Amore switch (*(p + 1)) { 663*95c635efSGarrett D'Amore case('S'): 664*95c635efSGarrett D'Amore bufcat(h, sec ? sec : "1"); 665*95c635efSGarrett D'Amore break; 666*95c635efSGarrett D'Amore case('N'): 667*95c635efSGarrett D'Amore bufcat_fmt(h, name); 668*95c635efSGarrett D'Amore break; 669*95c635efSGarrett D'Amore default: 670*95c635efSGarrett D'Amore bufncat(h, p, 2); 671*95c635efSGarrett D'Amore break; 672*95c635efSGarrett D'Amore } 673*95c635efSGarrett D'Amore pp = p + 2; 674*95c635efSGarrett D'Amore } 675*95c635efSGarrett D'Amore if (pp) 676*95c635efSGarrett D'Amore bufcat(h, pp); 677*95c635efSGarrett D'Amore } 678*95c635efSGarrett D'Amore 679*95c635efSGarrett D'Amore void 680*95c635efSGarrett D'Amore bufcat_su(struct html *h, const char *p, const struct roffsu *su) 681*95c635efSGarrett D'Amore { 682*95c635efSGarrett D'Amore double v; 683*95c635efSGarrett D'Amore 684*95c635efSGarrett D'Amore v = su->scale; 685*95c635efSGarrett D'Amore if (SCALE_MM == su->unit && 0.0 == (v /= 100.0)) 686*95c635efSGarrett D'Amore v = 1.0; 687*95c635efSGarrett D'Amore 688*95c635efSGarrett D'Amore bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]); 689*95c635efSGarrett D'Amore } 690*95c635efSGarrett D'Amore 691*95c635efSGarrett D'Amore void 692*95c635efSGarrett D'Amore bufcat_id(struct html *h, const char *src) 693*95c635efSGarrett D'Amore { 694*95c635efSGarrett D'Amore 695*95c635efSGarrett D'Amore /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */ 696*95c635efSGarrett D'Amore 697*95c635efSGarrett D'Amore while ('\0' != *src) 698*95c635efSGarrett D'Amore bufcat_fmt(h, "%.2x", *src++); 699*95c635efSGarrett D'Amore } 700