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