1*7295610fSBaptiste Daroussin /* $Id: html.c,v 1.254 2019/03/03 13:02:11 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4*7295610fSBaptiste Daroussin * Copyright (c) 2011-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 2061d06d6bSBaptiste Daroussin #include <sys/types.h> 21*7295610fSBaptiste Daroussin #include <sys/stat.h> 2261d06d6bSBaptiste Daroussin 2361d06d6bSBaptiste Daroussin #include <assert.h> 2461d06d6bSBaptiste Daroussin #include <ctype.h> 2561d06d6bSBaptiste Daroussin #include <stdarg.h> 2661d06d6bSBaptiste Daroussin #include <stddef.h> 2761d06d6bSBaptiste Daroussin #include <stdio.h> 2861d06d6bSBaptiste Daroussin #include <stdint.h> 2961d06d6bSBaptiste Daroussin #include <stdlib.h> 3061d06d6bSBaptiste Daroussin #include <string.h> 3161d06d6bSBaptiste Daroussin #include <unistd.h> 3261d06d6bSBaptiste Daroussin 3361d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 3461d06d6bSBaptiste Daroussin #include "mandoc_ohash.h" 3561d06d6bSBaptiste Daroussin #include "mandoc.h" 3661d06d6bSBaptiste Daroussin #include "roff.h" 3761d06d6bSBaptiste Daroussin #include "out.h" 3861d06d6bSBaptiste Daroussin #include "html.h" 3961d06d6bSBaptiste Daroussin #include "manconf.h" 4061d06d6bSBaptiste Daroussin #include "main.h" 4161d06d6bSBaptiste Daroussin 4261d06d6bSBaptiste Daroussin struct htmldata { 4361d06d6bSBaptiste Daroussin const char *name; 4461d06d6bSBaptiste Daroussin int flags; 4561d06d6bSBaptiste Daroussin #define HTML_NOSTACK (1 << 0) 4661d06d6bSBaptiste Daroussin #define HTML_AUTOCLOSE (1 << 1) 4761d06d6bSBaptiste Daroussin #define HTML_NLBEFORE (1 << 2) 4861d06d6bSBaptiste Daroussin #define HTML_NLBEGIN (1 << 3) 4961d06d6bSBaptiste Daroussin #define HTML_NLEND (1 << 4) 5061d06d6bSBaptiste Daroussin #define HTML_NLAFTER (1 << 5) 5161d06d6bSBaptiste Daroussin #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) 5261d06d6bSBaptiste Daroussin #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) 5361d06d6bSBaptiste Daroussin #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) 5461d06d6bSBaptiste Daroussin #define HTML_INDENT (1 << 6) 5561d06d6bSBaptiste Daroussin #define HTML_NOINDENT (1 << 7) 5661d06d6bSBaptiste Daroussin }; 5761d06d6bSBaptiste Daroussin 5861d06d6bSBaptiste Daroussin static const struct htmldata htmltags[TAG_MAX] = { 5961d06d6bSBaptiste Daroussin {"html", HTML_NLALL}, 6061d06d6bSBaptiste Daroussin {"head", HTML_NLALL | HTML_INDENT}, 6161d06d6bSBaptiste Daroussin {"body", HTML_NLALL}, 6261d06d6bSBaptiste Daroussin {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 6361d06d6bSBaptiste Daroussin {"title", HTML_NLAROUND}, 6461d06d6bSBaptiste Daroussin {"div", HTML_NLAROUND}, 6561d06d6bSBaptiste Daroussin {"div", 0}, 66*7295610fSBaptiste Daroussin {"section", HTML_NLALL}, 6761d06d6bSBaptiste Daroussin {"h1", HTML_NLAROUND}, 6861d06d6bSBaptiste Daroussin {"h2", HTML_NLAROUND}, 6961d06d6bSBaptiste Daroussin {"span", 0}, 7061d06d6bSBaptiste Daroussin {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 7161d06d6bSBaptiste Daroussin {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, 7261d06d6bSBaptiste Daroussin {"a", 0}, 7361d06d6bSBaptiste Daroussin {"table", HTML_NLALL | HTML_INDENT}, 7461d06d6bSBaptiste Daroussin {"tr", HTML_NLALL | HTML_INDENT}, 7561d06d6bSBaptiste Daroussin {"td", HTML_NLAROUND}, 7661d06d6bSBaptiste Daroussin {"li", HTML_NLAROUND | HTML_INDENT}, 7761d06d6bSBaptiste Daroussin {"ul", HTML_NLALL | HTML_INDENT}, 7861d06d6bSBaptiste Daroussin {"ol", HTML_NLALL | HTML_INDENT}, 7961d06d6bSBaptiste Daroussin {"dl", HTML_NLALL | HTML_INDENT}, 8061d06d6bSBaptiste Daroussin {"dt", HTML_NLAROUND}, 8161d06d6bSBaptiste Daroussin {"dd", HTML_NLAROUND | HTML_INDENT}, 82*7295610fSBaptiste Daroussin {"p", HTML_NLAROUND | HTML_INDENT}, 8361d06d6bSBaptiste Daroussin {"pre", HTML_NLALL | HTML_NOINDENT}, 8461d06d6bSBaptiste Daroussin {"var", 0}, 8561d06d6bSBaptiste Daroussin {"cite", 0}, 8661d06d6bSBaptiste Daroussin {"b", 0}, 8761d06d6bSBaptiste Daroussin {"i", 0}, 8861d06d6bSBaptiste Daroussin {"code", 0}, 8961d06d6bSBaptiste Daroussin {"small", 0}, 9061d06d6bSBaptiste Daroussin {"style", HTML_NLALL | HTML_INDENT}, 9161d06d6bSBaptiste Daroussin {"math", HTML_NLALL | HTML_INDENT}, 9261d06d6bSBaptiste Daroussin {"mrow", 0}, 9361d06d6bSBaptiste Daroussin {"mi", 0}, 9461d06d6bSBaptiste Daroussin {"mn", 0}, 9561d06d6bSBaptiste Daroussin {"mo", 0}, 9661d06d6bSBaptiste Daroussin {"msup", 0}, 9761d06d6bSBaptiste Daroussin {"msub", 0}, 9861d06d6bSBaptiste Daroussin {"msubsup", 0}, 9961d06d6bSBaptiste Daroussin {"mfrac", 0}, 10061d06d6bSBaptiste Daroussin {"msqrt", 0}, 10161d06d6bSBaptiste Daroussin {"mfenced", 0}, 10261d06d6bSBaptiste Daroussin {"mtable", 0}, 10361d06d6bSBaptiste Daroussin {"mtr", 0}, 10461d06d6bSBaptiste Daroussin {"mtd", 0}, 10561d06d6bSBaptiste Daroussin {"munderover", 0}, 10661d06d6bSBaptiste Daroussin {"munder", 0}, 10761d06d6bSBaptiste Daroussin {"mover", 0}, 10861d06d6bSBaptiste Daroussin }; 10961d06d6bSBaptiste Daroussin 11061d06d6bSBaptiste Daroussin /* Avoid duplicate HTML id= attributes. */ 11161d06d6bSBaptiste Daroussin static struct ohash id_unique; 11261d06d6bSBaptiste Daroussin 113*7295610fSBaptiste Daroussin static void html_reset_internal(struct html *); 11461d06d6bSBaptiste Daroussin static void print_byte(struct html *, char); 11561d06d6bSBaptiste Daroussin static void print_endword(struct html *); 11661d06d6bSBaptiste Daroussin static void print_indent(struct html *); 11761d06d6bSBaptiste Daroussin static void print_word(struct html *, const char *); 11861d06d6bSBaptiste Daroussin 11961d06d6bSBaptiste Daroussin static void print_ctag(struct html *, struct tag *); 12061d06d6bSBaptiste Daroussin static int print_escape(struct html *, char); 12161d06d6bSBaptiste Daroussin static int print_encode(struct html *, const char *, const char *, int); 12261d06d6bSBaptiste Daroussin static void print_href(struct html *, const char *, const char *, int); 12361d06d6bSBaptiste Daroussin 12461d06d6bSBaptiste Daroussin 12561d06d6bSBaptiste Daroussin void * 12661d06d6bSBaptiste Daroussin html_alloc(const struct manoutput *outopts) 12761d06d6bSBaptiste Daroussin { 12861d06d6bSBaptiste Daroussin struct html *h; 12961d06d6bSBaptiste Daroussin 13061d06d6bSBaptiste Daroussin h = mandoc_calloc(1, sizeof(struct html)); 13161d06d6bSBaptiste Daroussin 13261d06d6bSBaptiste Daroussin h->tag = NULL; 13361d06d6bSBaptiste Daroussin h->style = outopts->style; 134*7295610fSBaptiste Daroussin if ((h->base_man1 = outopts->man) == NULL) 135*7295610fSBaptiste Daroussin h->base_man2 = NULL; 136*7295610fSBaptiste Daroussin else if ((h->base_man2 = strchr(h->base_man1, ';')) != NULL) 137*7295610fSBaptiste Daroussin *h->base_man2++ = '\0'; 13861d06d6bSBaptiste Daroussin h->base_includes = outopts->includes; 13961d06d6bSBaptiste Daroussin if (outopts->fragment) 14061d06d6bSBaptiste Daroussin h->oflags |= HTML_FRAGMENT; 141*7295610fSBaptiste Daroussin if (outopts->toc) 142*7295610fSBaptiste Daroussin h->oflags |= HTML_TOC; 14361d06d6bSBaptiste Daroussin 14461d06d6bSBaptiste Daroussin mandoc_ohash_init(&id_unique, 4, 0); 14561d06d6bSBaptiste Daroussin 14661d06d6bSBaptiste Daroussin return h; 14761d06d6bSBaptiste Daroussin } 14861d06d6bSBaptiste Daroussin 149*7295610fSBaptiste Daroussin static void 150*7295610fSBaptiste Daroussin html_reset_internal(struct html *h) 15161d06d6bSBaptiste Daroussin { 15261d06d6bSBaptiste Daroussin struct tag *tag; 15361d06d6bSBaptiste Daroussin char *cp; 15461d06d6bSBaptiste Daroussin unsigned int slot; 15561d06d6bSBaptiste Daroussin 15661d06d6bSBaptiste Daroussin while ((tag = h->tag) != NULL) { 15761d06d6bSBaptiste Daroussin h->tag = tag->next; 15861d06d6bSBaptiste Daroussin free(tag); 15961d06d6bSBaptiste Daroussin } 16061d06d6bSBaptiste Daroussin cp = ohash_first(&id_unique, &slot); 16161d06d6bSBaptiste Daroussin while (cp != NULL) { 16261d06d6bSBaptiste Daroussin free(cp); 16361d06d6bSBaptiste Daroussin cp = ohash_next(&id_unique, &slot); 16461d06d6bSBaptiste Daroussin } 16561d06d6bSBaptiste Daroussin ohash_delete(&id_unique); 16661d06d6bSBaptiste Daroussin } 16761d06d6bSBaptiste Daroussin 16861d06d6bSBaptiste Daroussin void 169*7295610fSBaptiste Daroussin html_reset(void *p) 170*7295610fSBaptiste Daroussin { 171*7295610fSBaptiste Daroussin html_reset_internal(p); 172*7295610fSBaptiste Daroussin mandoc_ohash_init(&id_unique, 4, 0); 173*7295610fSBaptiste Daroussin } 174*7295610fSBaptiste Daroussin 175*7295610fSBaptiste Daroussin void 176*7295610fSBaptiste Daroussin html_free(void *p) 177*7295610fSBaptiste Daroussin { 178*7295610fSBaptiste Daroussin html_reset_internal(p); 179*7295610fSBaptiste Daroussin free(p); 180*7295610fSBaptiste Daroussin } 181*7295610fSBaptiste Daroussin 182*7295610fSBaptiste Daroussin void 18361d06d6bSBaptiste Daroussin print_gen_head(struct html *h) 18461d06d6bSBaptiste Daroussin { 18561d06d6bSBaptiste Daroussin struct tag *t; 18661d06d6bSBaptiste Daroussin 18761d06d6bSBaptiste Daroussin print_otag(h, TAG_META, "?", "charset", "utf-8"); 18861d06d6bSBaptiste Daroussin if (h->style != NULL) { 18961d06d6bSBaptiste Daroussin print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", 19061d06d6bSBaptiste Daroussin h->style, "type", "text/css", "media", "all"); 19161d06d6bSBaptiste Daroussin return; 19261d06d6bSBaptiste Daroussin } 19361d06d6bSBaptiste Daroussin 19461d06d6bSBaptiste Daroussin /* 19561d06d6bSBaptiste Daroussin * Print a minimal embedded style sheet. 19661d06d6bSBaptiste Daroussin */ 19761d06d6bSBaptiste Daroussin 19861d06d6bSBaptiste Daroussin t = print_otag(h, TAG_STYLE, ""); 19961d06d6bSBaptiste Daroussin print_text(h, "table.head, table.foot { width: 100%; }"); 20061d06d6bSBaptiste Daroussin print_endline(h); 20161d06d6bSBaptiste Daroussin print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }"); 20261d06d6bSBaptiste Daroussin print_endline(h); 20361d06d6bSBaptiste Daroussin print_text(h, "td.head-vol { text-align: center; }"); 20461d06d6bSBaptiste Daroussin print_endline(h); 20561d06d6bSBaptiste Daroussin print_text(h, "div.Pp { margin: 1ex 0ex; }"); 20661d06d6bSBaptiste Daroussin print_endline(h); 20761d06d6bSBaptiste Daroussin print_text(h, "div.Nd, div.Bf, div.Op { display: inline; }"); 20861d06d6bSBaptiste Daroussin print_endline(h); 20961d06d6bSBaptiste Daroussin print_text(h, "span.Pa, span.Ad { font-style: italic; }"); 21061d06d6bSBaptiste Daroussin print_endline(h); 21161d06d6bSBaptiste Daroussin print_text(h, "span.Ms { font-weight: bold; }"); 21261d06d6bSBaptiste Daroussin print_endline(h); 21361d06d6bSBaptiste Daroussin print_text(h, "dl.Bl-diag "); 21461d06d6bSBaptiste Daroussin print_byte(h, '>'); 21561d06d6bSBaptiste Daroussin print_text(h, " dt { font-weight: bold; }"); 21661d06d6bSBaptiste Daroussin print_endline(h); 21761d06d6bSBaptiste Daroussin print_text(h, "code.Nm, code.Fl, code.Cm, code.Ic, " 21861d06d6bSBaptiste Daroussin "code.In, code.Fd, code.Fn,"); 21961d06d6bSBaptiste Daroussin print_endline(h); 22061d06d6bSBaptiste Daroussin print_text(h, "code.Cd { font-weight: bold; " 22161d06d6bSBaptiste Daroussin "font-family: inherit; }"); 22261d06d6bSBaptiste Daroussin print_tagq(h, t); 22361d06d6bSBaptiste Daroussin } 22461d06d6bSBaptiste Daroussin 225*7295610fSBaptiste Daroussin void 22661d06d6bSBaptiste Daroussin print_metaf(struct html *h, enum mandoc_esc deco) 22761d06d6bSBaptiste Daroussin { 22861d06d6bSBaptiste Daroussin enum htmlfont font; 22961d06d6bSBaptiste Daroussin 23061d06d6bSBaptiste Daroussin switch (deco) { 23161d06d6bSBaptiste Daroussin case ESCAPE_FONTPREV: 23261d06d6bSBaptiste Daroussin font = h->metal; 23361d06d6bSBaptiste Daroussin break; 23461d06d6bSBaptiste Daroussin case ESCAPE_FONTITALIC: 23561d06d6bSBaptiste Daroussin font = HTMLFONT_ITALIC; 23661d06d6bSBaptiste Daroussin break; 23761d06d6bSBaptiste Daroussin case ESCAPE_FONTBOLD: 23861d06d6bSBaptiste Daroussin font = HTMLFONT_BOLD; 23961d06d6bSBaptiste Daroussin break; 24061d06d6bSBaptiste Daroussin case ESCAPE_FONTBI: 24161d06d6bSBaptiste Daroussin font = HTMLFONT_BI; 24261d06d6bSBaptiste Daroussin break; 243*7295610fSBaptiste Daroussin case ESCAPE_FONTCW: 244*7295610fSBaptiste Daroussin font = HTMLFONT_CW; 245*7295610fSBaptiste Daroussin break; 24661d06d6bSBaptiste Daroussin case ESCAPE_FONT: 24761d06d6bSBaptiste Daroussin case ESCAPE_FONTROMAN: 24861d06d6bSBaptiste Daroussin font = HTMLFONT_NONE; 24961d06d6bSBaptiste Daroussin break; 25061d06d6bSBaptiste Daroussin default: 251*7295610fSBaptiste Daroussin return; 25261d06d6bSBaptiste Daroussin } 25361d06d6bSBaptiste Daroussin 25461d06d6bSBaptiste Daroussin if (h->metaf) { 25561d06d6bSBaptiste Daroussin print_tagq(h, h->metaf); 25661d06d6bSBaptiste Daroussin h->metaf = NULL; 25761d06d6bSBaptiste Daroussin } 25861d06d6bSBaptiste Daroussin 25961d06d6bSBaptiste Daroussin h->metal = h->metac; 26061d06d6bSBaptiste Daroussin h->metac = font; 26161d06d6bSBaptiste Daroussin 26261d06d6bSBaptiste Daroussin switch (font) { 26361d06d6bSBaptiste Daroussin case HTMLFONT_ITALIC: 26461d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_I, ""); 26561d06d6bSBaptiste Daroussin break; 26661d06d6bSBaptiste Daroussin case HTMLFONT_BOLD: 26761d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_B, ""); 26861d06d6bSBaptiste Daroussin break; 26961d06d6bSBaptiste Daroussin case HTMLFONT_BI: 27061d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_B, ""); 27161d06d6bSBaptiste Daroussin print_otag(h, TAG_I, ""); 27261d06d6bSBaptiste Daroussin break; 273*7295610fSBaptiste Daroussin case HTMLFONT_CW: 274*7295610fSBaptiste Daroussin h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); 275*7295610fSBaptiste Daroussin break; 27661d06d6bSBaptiste Daroussin default: 27761d06d6bSBaptiste Daroussin break; 27861d06d6bSBaptiste Daroussin } 27961d06d6bSBaptiste Daroussin } 28061d06d6bSBaptiste Daroussin 281*7295610fSBaptiste Daroussin void 282*7295610fSBaptiste Daroussin html_close_paragraph(struct html *h) 283*7295610fSBaptiste Daroussin { 284*7295610fSBaptiste Daroussin struct tag *t; 285*7295610fSBaptiste Daroussin 286*7295610fSBaptiste Daroussin for (t = h->tag; t != NULL && t->closed == 0; t = t->next) { 287*7295610fSBaptiste Daroussin switch(t->tag) { 288*7295610fSBaptiste Daroussin case TAG_P: 289*7295610fSBaptiste Daroussin case TAG_PRE: 290*7295610fSBaptiste Daroussin print_tagq(h, t); 291*7295610fSBaptiste Daroussin break; 292*7295610fSBaptiste Daroussin case TAG_A: 293*7295610fSBaptiste Daroussin print_tagq(h, t); 294*7295610fSBaptiste Daroussin continue; 295*7295610fSBaptiste Daroussin default: 296*7295610fSBaptiste Daroussin continue; 297*7295610fSBaptiste Daroussin } 298*7295610fSBaptiste Daroussin break; 299*7295610fSBaptiste Daroussin } 300*7295610fSBaptiste Daroussin } 301*7295610fSBaptiste Daroussin 302*7295610fSBaptiste Daroussin /* 303*7295610fSBaptiste Daroussin * ROFF_nf switches to no-fill mode, ROFF_fi to fill mode. 304*7295610fSBaptiste Daroussin * TOKEN_NONE does not switch. The old mode is returned. 305*7295610fSBaptiste Daroussin */ 306*7295610fSBaptiste Daroussin enum roff_tok 307*7295610fSBaptiste Daroussin html_fillmode(struct html *h, enum roff_tok want) 308*7295610fSBaptiste Daroussin { 309*7295610fSBaptiste Daroussin struct tag *t; 310*7295610fSBaptiste Daroussin enum roff_tok had; 311*7295610fSBaptiste Daroussin 312*7295610fSBaptiste Daroussin for (t = h->tag; t != NULL; t = t->next) 313*7295610fSBaptiste Daroussin if (t->tag == TAG_PRE) 314*7295610fSBaptiste Daroussin break; 315*7295610fSBaptiste Daroussin 316*7295610fSBaptiste Daroussin had = t == NULL ? ROFF_fi : ROFF_nf; 317*7295610fSBaptiste Daroussin 318*7295610fSBaptiste Daroussin if (want != had) { 319*7295610fSBaptiste Daroussin switch (want) { 320*7295610fSBaptiste Daroussin case ROFF_fi: 321*7295610fSBaptiste Daroussin print_tagq(h, t); 322*7295610fSBaptiste Daroussin break; 323*7295610fSBaptiste Daroussin case ROFF_nf: 324*7295610fSBaptiste Daroussin html_close_paragraph(h); 325*7295610fSBaptiste Daroussin print_otag(h, TAG_PRE, ""); 326*7295610fSBaptiste Daroussin break; 327*7295610fSBaptiste Daroussin case TOKEN_NONE: 328*7295610fSBaptiste Daroussin break; 329*7295610fSBaptiste Daroussin default: 330*7295610fSBaptiste Daroussin abort(); 331*7295610fSBaptiste Daroussin } 332*7295610fSBaptiste Daroussin } 333*7295610fSBaptiste Daroussin return had; 334*7295610fSBaptiste Daroussin } 335*7295610fSBaptiste Daroussin 33661d06d6bSBaptiste Daroussin char * 33761d06d6bSBaptiste Daroussin html_make_id(const struct roff_node *n, int unique) 33861d06d6bSBaptiste Daroussin { 33961d06d6bSBaptiste Daroussin const struct roff_node *nch; 34061d06d6bSBaptiste Daroussin char *buf, *bufs, *cp; 34161d06d6bSBaptiste Daroussin unsigned int slot; 34261d06d6bSBaptiste Daroussin int suffix; 34361d06d6bSBaptiste Daroussin 34461d06d6bSBaptiste Daroussin for (nch = n->child; nch != NULL; nch = nch->next) 34561d06d6bSBaptiste Daroussin if (nch->type != ROFFT_TEXT) 34661d06d6bSBaptiste Daroussin return NULL; 34761d06d6bSBaptiste Daroussin 34861d06d6bSBaptiste Daroussin buf = NULL; 34961d06d6bSBaptiste Daroussin deroff(&buf, n); 35061d06d6bSBaptiste Daroussin if (buf == NULL) 35161d06d6bSBaptiste Daroussin return NULL; 35261d06d6bSBaptiste Daroussin 35361d06d6bSBaptiste Daroussin /* 35461d06d6bSBaptiste Daroussin * In ID attributes, only use ASCII characters that are 35561d06d6bSBaptiste Daroussin * permitted in URL-fragment strings according to the 35661d06d6bSBaptiste Daroussin * explicit list at: 35761d06d6bSBaptiste Daroussin * https://url.spec.whatwg.org/#url-fragment-string 35861d06d6bSBaptiste Daroussin */ 35961d06d6bSBaptiste Daroussin 36061d06d6bSBaptiste Daroussin for (cp = buf; *cp != '\0'; cp++) 36161d06d6bSBaptiste Daroussin if (isalnum((unsigned char)*cp) == 0 && 36261d06d6bSBaptiste Daroussin strchr("!$&'()*+,-./:;=?@_~", *cp) == NULL) 36361d06d6bSBaptiste Daroussin *cp = '_'; 36461d06d6bSBaptiste Daroussin 36561d06d6bSBaptiste Daroussin if (unique == 0) 36661d06d6bSBaptiste Daroussin return buf; 36761d06d6bSBaptiste Daroussin 36861d06d6bSBaptiste Daroussin /* Avoid duplicate HTML id= attributes. */ 36961d06d6bSBaptiste Daroussin 37061d06d6bSBaptiste Daroussin bufs = NULL; 37161d06d6bSBaptiste Daroussin suffix = 1; 37261d06d6bSBaptiste Daroussin slot = ohash_qlookup(&id_unique, buf); 37361d06d6bSBaptiste Daroussin cp = ohash_find(&id_unique, slot); 37461d06d6bSBaptiste Daroussin if (cp != NULL) { 37561d06d6bSBaptiste Daroussin while (cp != NULL) { 37661d06d6bSBaptiste Daroussin free(bufs); 37761d06d6bSBaptiste Daroussin if (++suffix > 127) { 37861d06d6bSBaptiste Daroussin free(buf); 37961d06d6bSBaptiste Daroussin return NULL; 38061d06d6bSBaptiste Daroussin } 38161d06d6bSBaptiste Daroussin mandoc_asprintf(&bufs, "%s_%d", buf, suffix); 38261d06d6bSBaptiste Daroussin slot = ohash_qlookup(&id_unique, bufs); 38361d06d6bSBaptiste Daroussin cp = ohash_find(&id_unique, slot); 38461d06d6bSBaptiste Daroussin } 38561d06d6bSBaptiste Daroussin free(buf); 38661d06d6bSBaptiste Daroussin buf = bufs; 38761d06d6bSBaptiste Daroussin } 38861d06d6bSBaptiste Daroussin ohash_insert(&id_unique, slot, buf); 38961d06d6bSBaptiste Daroussin return buf; 39061d06d6bSBaptiste Daroussin } 39161d06d6bSBaptiste Daroussin 39261d06d6bSBaptiste Daroussin static int 39361d06d6bSBaptiste Daroussin print_escape(struct html *h, char c) 39461d06d6bSBaptiste Daroussin { 39561d06d6bSBaptiste Daroussin 39661d06d6bSBaptiste Daroussin switch (c) { 39761d06d6bSBaptiste Daroussin case '<': 39861d06d6bSBaptiste Daroussin print_word(h, "<"); 39961d06d6bSBaptiste Daroussin break; 40061d06d6bSBaptiste Daroussin case '>': 40161d06d6bSBaptiste Daroussin print_word(h, ">"); 40261d06d6bSBaptiste Daroussin break; 40361d06d6bSBaptiste Daroussin case '&': 40461d06d6bSBaptiste Daroussin print_word(h, "&"); 40561d06d6bSBaptiste Daroussin break; 40661d06d6bSBaptiste Daroussin case '"': 40761d06d6bSBaptiste Daroussin print_word(h, """); 40861d06d6bSBaptiste Daroussin break; 40961d06d6bSBaptiste Daroussin case ASCII_NBRSP: 41061d06d6bSBaptiste Daroussin print_word(h, " "); 41161d06d6bSBaptiste Daroussin break; 41261d06d6bSBaptiste Daroussin case ASCII_HYPH: 41361d06d6bSBaptiste Daroussin print_byte(h, '-'); 41461d06d6bSBaptiste Daroussin break; 41561d06d6bSBaptiste Daroussin case ASCII_BREAK: 41661d06d6bSBaptiste Daroussin break; 41761d06d6bSBaptiste Daroussin default: 41861d06d6bSBaptiste Daroussin return 0; 41961d06d6bSBaptiste Daroussin } 42061d06d6bSBaptiste Daroussin return 1; 42161d06d6bSBaptiste Daroussin } 42261d06d6bSBaptiste Daroussin 42361d06d6bSBaptiste Daroussin static int 42461d06d6bSBaptiste Daroussin print_encode(struct html *h, const char *p, const char *pend, int norecurse) 42561d06d6bSBaptiste Daroussin { 42661d06d6bSBaptiste Daroussin char numbuf[16]; 42761d06d6bSBaptiste Daroussin const char *seq; 42861d06d6bSBaptiste Daroussin size_t sz; 42961d06d6bSBaptiste Daroussin int c, len, breakline, nospace; 43061d06d6bSBaptiste Daroussin enum mandoc_esc esc; 43161d06d6bSBaptiste Daroussin static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"', 43261d06d6bSBaptiste Daroussin ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; 43361d06d6bSBaptiste Daroussin 43461d06d6bSBaptiste Daroussin if (pend == NULL) 43561d06d6bSBaptiste Daroussin pend = strchr(p, '\0'); 43661d06d6bSBaptiste Daroussin 43761d06d6bSBaptiste Daroussin breakline = 0; 43861d06d6bSBaptiste Daroussin nospace = 0; 43961d06d6bSBaptiste Daroussin 44061d06d6bSBaptiste Daroussin while (p < pend) { 44161d06d6bSBaptiste Daroussin if (HTML_SKIPCHAR & h->flags && '\\' != *p) { 44261d06d6bSBaptiste Daroussin h->flags &= ~HTML_SKIPCHAR; 44361d06d6bSBaptiste Daroussin p++; 44461d06d6bSBaptiste Daroussin continue; 44561d06d6bSBaptiste Daroussin } 44661d06d6bSBaptiste Daroussin 44761d06d6bSBaptiste Daroussin for (sz = strcspn(p, rejs); sz-- && p < pend; p++) 44861d06d6bSBaptiste Daroussin print_byte(h, *p); 44961d06d6bSBaptiste Daroussin 45061d06d6bSBaptiste Daroussin if (breakline && 45161d06d6bSBaptiste Daroussin (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) { 452*7295610fSBaptiste Daroussin print_otag(h, TAG_BR, ""); 45361d06d6bSBaptiste Daroussin breakline = 0; 45461d06d6bSBaptiste Daroussin while (p < pend && (*p == ' ' || *p == ASCII_NBRSP)) 45561d06d6bSBaptiste Daroussin p++; 45661d06d6bSBaptiste Daroussin continue; 45761d06d6bSBaptiste Daroussin } 45861d06d6bSBaptiste Daroussin 45961d06d6bSBaptiste Daroussin if (p >= pend) 46061d06d6bSBaptiste Daroussin break; 46161d06d6bSBaptiste Daroussin 46261d06d6bSBaptiste Daroussin if (*p == ' ') { 46361d06d6bSBaptiste Daroussin print_endword(h); 46461d06d6bSBaptiste Daroussin p++; 46561d06d6bSBaptiste Daroussin continue; 46661d06d6bSBaptiste Daroussin } 46761d06d6bSBaptiste Daroussin 46861d06d6bSBaptiste Daroussin if (print_escape(h, *p++)) 46961d06d6bSBaptiste Daroussin continue; 47061d06d6bSBaptiste Daroussin 47161d06d6bSBaptiste Daroussin esc = mandoc_escape(&p, &seq, &len); 47261d06d6bSBaptiste Daroussin switch (esc) { 47361d06d6bSBaptiste Daroussin case ESCAPE_FONT: 47461d06d6bSBaptiste Daroussin case ESCAPE_FONTPREV: 47561d06d6bSBaptiste Daroussin case ESCAPE_FONTBOLD: 47661d06d6bSBaptiste Daroussin case ESCAPE_FONTITALIC: 47761d06d6bSBaptiste Daroussin case ESCAPE_FONTBI: 478*7295610fSBaptiste Daroussin case ESCAPE_FONTCW: 47961d06d6bSBaptiste Daroussin case ESCAPE_FONTROMAN: 480*7295610fSBaptiste Daroussin if (0 == norecurse) { 481*7295610fSBaptiste Daroussin h->flags |= HTML_NOSPACE; 48261d06d6bSBaptiste Daroussin print_metaf(h, esc); 483*7295610fSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 484*7295610fSBaptiste Daroussin } 48561d06d6bSBaptiste Daroussin continue; 48661d06d6bSBaptiste Daroussin case ESCAPE_SKIPCHAR: 48761d06d6bSBaptiste Daroussin h->flags |= HTML_SKIPCHAR; 48861d06d6bSBaptiste Daroussin continue; 489*7295610fSBaptiste Daroussin case ESCAPE_ERROR: 490*7295610fSBaptiste Daroussin continue; 49161d06d6bSBaptiste Daroussin default: 49261d06d6bSBaptiste Daroussin break; 49361d06d6bSBaptiste Daroussin } 49461d06d6bSBaptiste Daroussin 49561d06d6bSBaptiste Daroussin if (h->flags & HTML_SKIPCHAR) { 49661d06d6bSBaptiste Daroussin h->flags &= ~HTML_SKIPCHAR; 49761d06d6bSBaptiste Daroussin continue; 49861d06d6bSBaptiste Daroussin } 49961d06d6bSBaptiste Daroussin 50061d06d6bSBaptiste Daroussin switch (esc) { 50161d06d6bSBaptiste Daroussin case ESCAPE_UNICODE: 50261d06d6bSBaptiste Daroussin /* Skip past "u" header. */ 50361d06d6bSBaptiste Daroussin c = mchars_num2uc(seq + 1, len - 1); 50461d06d6bSBaptiste Daroussin break; 50561d06d6bSBaptiste Daroussin case ESCAPE_NUMBERED: 50661d06d6bSBaptiste Daroussin c = mchars_num2char(seq, len); 50761d06d6bSBaptiste Daroussin if (c < 0) 50861d06d6bSBaptiste Daroussin continue; 50961d06d6bSBaptiste Daroussin break; 51061d06d6bSBaptiste Daroussin case ESCAPE_SPECIAL: 51161d06d6bSBaptiste Daroussin c = mchars_spec2cp(seq, len); 51261d06d6bSBaptiste Daroussin if (c <= 0) 51361d06d6bSBaptiste Daroussin continue; 51461d06d6bSBaptiste Daroussin break; 515*7295610fSBaptiste Daroussin case ESCAPE_UNDEF: 516*7295610fSBaptiste Daroussin c = *seq; 517*7295610fSBaptiste Daroussin break; 518*7295610fSBaptiste Daroussin case ESCAPE_DEVICE: 519*7295610fSBaptiste Daroussin print_word(h, "html"); 520*7295610fSBaptiste Daroussin continue; 52161d06d6bSBaptiste Daroussin case ESCAPE_BREAK: 52261d06d6bSBaptiste Daroussin breakline = 1; 52361d06d6bSBaptiste Daroussin continue; 52461d06d6bSBaptiste Daroussin case ESCAPE_NOSPACE: 52561d06d6bSBaptiste Daroussin if ('\0' == *p) 52661d06d6bSBaptiste Daroussin nospace = 1; 52761d06d6bSBaptiste Daroussin continue; 52861d06d6bSBaptiste Daroussin case ESCAPE_OVERSTRIKE: 52961d06d6bSBaptiste Daroussin if (len == 0) 53061d06d6bSBaptiste Daroussin continue; 53161d06d6bSBaptiste Daroussin c = seq[len - 1]; 53261d06d6bSBaptiste Daroussin break; 53361d06d6bSBaptiste Daroussin default: 53461d06d6bSBaptiste Daroussin continue; 53561d06d6bSBaptiste Daroussin } 53661d06d6bSBaptiste Daroussin if ((c < 0x20 && c != 0x09) || 53761d06d6bSBaptiste Daroussin (c > 0x7E && c < 0xA0)) 53861d06d6bSBaptiste Daroussin c = 0xFFFD; 53961d06d6bSBaptiste Daroussin if (c > 0x7E) { 54061d06d6bSBaptiste Daroussin (void)snprintf(numbuf, sizeof(numbuf), "&#x%.4X;", c); 54161d06d6bSBaptiste Daroussin print_word(h, numbuf); 54261d06d6bSBaptiste Daroussin } else if (print_escape(h, c) == 0) 54361d06d6bSBaptiste Daroussin print_byte(h, c); 54461d06d6bSBaptiste Daroussin } 54561d06d6bSBaptiste Daroussin 54661d06d6bSBaptiste Daroussin return nospace; 54761d06d6bSBaptiste Daroussin } 54861d06d6bSBaptiste Daroussin 54961d06d6bSBaptiste Daroussin static void 55061d06d6bSBaptiste Daroussin print_href(struct html *h, const char *name, const char *sec, int man) 55161d06d6bSBaptiste Daroussin { 552*7295610fSBaptiste Daroussin struct stat sb; 55361d06d6bSBaptiste Daroussin const char *p, *pp; 554*7295610fSBaptiste Daroussin char *filename; 55561d06d6bSBaptiste Daroussin 556*7295610fSBaptiste Daroussin if (man) { 557*7295610fSBaptiste Daroussin pp = h->base_man1; 558*7295610fSBaptiste Daroussin if (h->base_man2 != NULL) { 559*7295610fSBaptiste Daroussin mandoc_asprintf(&filename, "%s.%s", name, sec); 560*7295610fSBaptiste Daroussin if (stat(filename, &sb) == -1) 561*7295610fSBaptiste Daroussin pp = h->base_man2; 562*7295610fSBaptiste Daroussin free(filename); 563*7295610fSBaptiste Daroussin } 564*7295610fSBaptiste Daroussin } else 565*7295610fSBaptiste Daroussin pp = h->base_includes; 566*7295610fSBaptiste Daroussin 56761d06d6bSBaptiste Daroussin while ((p = strchr(pp, '%')) != NULL) { 56861d06d6bSBaptiste Daroussin print_encode(h, pp, p, 1); 56961d06d6bSBaptiste Daroussin if (man && p[1] == 'S') { 57061d06d6bSBaptiste Daroussin if (sec == NULL) 57161d06d6bSBaptiste Daroussin print_byte(h, '1'); 57261d06d6bSBaptiste Daroussin else 57361d06d6bSBaptiste Daroussin print_encode(h, sec, NULL, 1); 57461d06d6bSBaptiste Daroussin } else if ((man && p[1] == 'N') || 57561d06d6bSBaptiste Daroussin (man == 0 && p[1] == 'I')) 57661d06d6bSBaptiste Daroussin print_encode(h, name, NULL, 1); 57761d06d6bSBaptiste Daroussin else 57861d06d6bSBaptiste Daroussin print_encode(h, p, p + 2, 1); 57961d06d6bSBaptiste Daroussin pp = p + 2; 58061d06d6bSBaptiste Daroussin } 58161d06d6bSBaptiste Daroussin if (*pp != '\0') 58261d06d6bSBaptiste Daroussin print_encode(h, pp, NULL, 1); 58361d06d6bSBaptiste Daroussin } 58461d06d6bSBaptiste Daroussin 58561d06d6bSBaptiste Daroussin struct tag * 58661d06d6bSBaptiste Daroussin print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) 58761d06d6bSBaptiste Daroussin { 58861d06d6bSBaptiste Daroussin va_list ap; 58961d06d6bSBaptiste Daroussin struct tag *t; 59061d06d6bSBaptiste Daroussin const char *attr; 59161d06d6bSBaptiste Daroussin char *arg1, *arg2; 592*7295610fSBaptiste Daroussin int style_written, tflags; 59361d06d6bSBaptiste Daroussin 59461d06d6bSBaptiste Daroussin tflags = htmltags[tag].flags; 59561d06d6bSBaptiste Daroussin 59661d06d6bSBaptiste Daroussin /* Push this tag onto the stack of open scopes. */ 59761d06d6bSBaptiste Daroussin 59861d06d6bSBaptiste Daroussin if ((tflags & HTML_NOSTACK) == 0) { 59961d06d6bSBaptiste Daroussin t = mandoc_malloc(sizeof(struct tag)); 60061d06d6bSBaptiste Daroussin t->tag = tag; 60161d06d6bSBaptiste Daroussin t->next = h->tag; 602*7295610fSBaptiste Daroussin t->refcnt = 0; 603*7295610fSBaptiste Daroussin t->closed = 0; 60461d06d6bSBaptiste Daroussin h->tag = t; 60561d06d6bSBaptiste Daroussin } else 60661d06d6bSBaptiste Daroussin t = NULL; 60761d06d6bSBaptiste Daroussin 60861d06d6bSBaptiste Daroussin if (tflags & HTML_NLBEFORE) 60961d06d6bSBaptiste Daroussin print_endline(h); 61061d06d6bSBaptiste Daroussin if (h->col == 0) 61161d06d6bSBaptiste Daroussin print_indent(h); 61261d06d6bSBaptiste Daroussin else if ((h->flags & HTML_NOSPACE) == 0) { 61361d06d6bSBaptiste Daroussin if (h->flags & HTML_KEEP) 61461d06d6bSBaptiste Daroussin print_word(h, " "); 61561d06d6bSBaptiste Daroussin else { 61661d06d6bSBaptiste Daroussin if (h->flags & HTML_PREKEEP) 61761d06d6bSBaptiste Daroussin h->flags |= HTML_KEEP; 61861d06d6bSBaptiste Daroussin print_endword(h); 61961d06d6bSBaptiste Daroussin } 62061d06d6bSBaptiste Daroussin } 62161d06d6bSBaptiste Daroussin 62261d06d6bSBaptiste Daroussin if ( ! (h->flags & HTML_NONOSPACE)) 62361d06d6bSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 62461d06d6bSBaptiste Daroussin else 62561d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE; 62661d06d6bSBaptiste Daroussin 62761d06d6bSBaptiste Daroussin /* Print out the tag name and attributes. */ 62861d06d6bSBaptiste Daroussin 62961d06d6bSBaptiste Daroussin print_byte(h, '<'); 63061d06d6bSBaptiste Daroussin print_word(h, htmltags[tag].name); 63161d06d6bSBaptiste Daroussin 63261d06d6bSBaptiste Daroussin va_start(ap, fmt); 63361d06d6bSBaptiste Daroussin 634*7295610fSBaptiste Daroussin while (*fmt != '\0' && *fmt != 's') { 63561d06d6bSBaptiste Daroussin 63661d06d6bSBaptiste Daroussin /* Parse attributes and arguments. */ 63761d06d6bSBaptiste Daroussin 63861d06d6bSBaptiste Daroussin arg1 = va_arg(ap, char *); 63961d06d6bSBaptiste Daroussin arg2 = NULL; 64061d06d6bSBaptiste Daroussin switch (*fmt++) { 64161d06d6bSBaptiste Daroussin case 'c': 64261d06d6bSBaptiste Daroussin attr = "class"; 64361d06d6bSBaptiste Daroussin break; 64461d06d6bSBaptiste Daroussin case 'h': 64561d06d6bSBaptiste Daroussin attr = "href"; 64661d06d6bSBaptiste Daroussin break; 64761d06d6bSBaptiste Daroussin case 'i': 64861d06d6bSBaptiste Daroussin attr = "id"; 64961d06d6bSBaptiste Daroussin break; 65061d06d6bSBaptiste Daroussin case '?': 65161d06d6bSBaptiste Daroussin attr = arg1; 65261d06d6bSBaptiste Daroussin arg1 = va_arg(ap, char *); 65361d06d6bSBaptiste Daroussin break; 65461d06d6bSBaptiste Daroussin default: 65561d06d6bSBaptiste Daroussin abort(); 65661d06d6bSBaptiste Daroussin } 65761d06d6bSBaptiste Daroussin if (*fmt == 'M') 65861d06d6bSBaptiste Daroussin arg2 = va_arg(ap, char *); 65961d06d6bSBaptiste Daroussin if (arg1 == NULL) 66061d06d6bSBaptiste Daroussin continue; 66161d06d6bSBaptiste Daroussin 66261d06d6bSBaptiste Daroussin /* Print the attributes. */ 66361d06d6bSBaptiste Daroussin 66461d06d6bSBaptiste Daroussin print_byte(h, ' '); 66561d06d6bSBaptiste Daroussin print_word(h, attr); 66661d06d6bSBaptiste Daroussin print_byte(h, '='); 66761d06d6bSBaptiste Daroussin print_byte(h, '"'); 66861d06d6bSBaptiste Daroussin switch (*fmt) { 66961d06d6bSBaptiste Daroussin case 'I': 67061d06d6bSBaptiste Daroussin print_href(h, arg1, NULL, 0); 67161d06d6bSBaptiste Daroussin fmt++; 67261d06d6bSBaptiste Daroussin break; 67361d06d6bSBaptiste Daroussin case 'M': 67461d06d6bSBaptiste Daroussin print_href(h, arg1, arg2, 1); 67561d06d6bSBaptiste Daroussin fmt++; 67661d06d6bSBaptiste Daroussin break; 67761d06d6bSBaptiste Daroussin case 'R': 67861d06d6bSBaptiste Daroussin print_byte(h, '#'); 67961d06d6bSBaptiste Daroussin print_encode(h, arg1, NULL, 1); 68061d06d6bSBaptiste Daroussin fmt++; 68161d06d6bSBaptiste Daroussin break; 68261d06d6bSBaptiste Daroussin default: 68361d06d6bSBaptiste Daroussin print_encode(h, arg1, NULL, 1); 684*7295610fSBaptiste Daroussin break; 685*7295610fSBaptiste Daroussin } 686*7295610fSBaptiste Daroussin print_byte(h, '"'); 687*7295610fSBaptiste Daroussin } 688*7295610fSBaptiste Daroussin 689*7295610fSBaptiste Daroussin style_written = 0; 690*7295610fSBaptiste Daroussin while (*fmt++ == 's') { 691*7295610fSBaptiste Daroussin arg1 = va_arg(ap, char *); 692*7295610fSBaptiste Daroussin arg2 = va_arg(ap, char *); 693*7295610fSBaptiste Daroussin if (arg2 == NULL) 694*7295610fSBaptiste Daroussin continue; 695*7295610fSBaptiste Daroussin print_byte(h, ' '); 696*7295610fSBaptiste Daroussin if (style_written == 0) { 697*7295610fSBaptiste Daroussin print_word(h, "style=\""); 698*7295610fSBaptiste Daroussin style_written = 1; 699*7295610fSBaptiste Daroussin } 70061d06d6bSBaptiste Daroussin print_word(h, arg1); 70161d06d6bSBaptiste Daroussin print_byte(h, ':'); 70261d06d6bSBaptiste Daroussin print_byte(h, ' '); 70361d06d6bSBaptiste Daroussin print_word(h, arg2); 70461d06d6bSBaptiste Daroussin print_byte(h, ';'); 70561d06d6bSBaptiste Daroussin } 706*7295610fSBaptiste Daroussin if (style_written) 70761d06d6bSBaptiste Daroussin print_byte(h, '"'); 708*7295610fSBaptiste Daroussin 70961d06d6bSBaptiste Daroussin va_end(ap); 71061d06d6bSBaptiste Daroussin 71161d06d6bSBaptiste Daroussin /* Accommodate for "well-formed" singleton escaping. */ 71261d06d6bSBaptiste Daroussin 71361d06d6bSBaptiste Daroussin if (HTML_AUTOCLOSE & htmltags[tag].flags) 71461d06d6bSBaptiste Daroussin print_byte(h, '/'); 71561d06d6bSBaptiste Daroussin 71661d06d6bSBaptiste Daroussin print_byte(h, '>'); 71761d06d6bSBaptiste Daroussin 71861d06d6bSBaptiste Daroussin if (tflags & HTML_NLBEGIN) 71961d06d6bSBaptiste Daroussin print_endline(h); 72061d06d6bSBaptiste Daroussin else 72161d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE; 72261d06d6bSBaptiste Daroussin 72361d06d6bSBaptiste Daroussin if (tflags & HTML_INDENT) 72461d06d6bSBaptiste Daroussin h->indent++; 72561d06d6bSBaptiste Daroussin if (tflags & HTML_NOINDENT) 72661d06d6bSBaptiste Daroussin h->noindent++; 72761d06d6bSBaptiste Daroussin 72861d06d6bSBaptiste Daroussin return t; 72961d06d6bSBaptiste Daroussin } 73061d06d6bSBaptiste Daroussin 73161d06d6bSBaptiste Daroussin static void 73261d06d6bSBaptiste Daroussin print_ctag(struct html *h, struct tag *tag) 73361d06d6bSBaptiste Daroussin { 73461d06d6bSBaptiste Daroussin int tflags; 73561d06d6bSBaptiste Daroussin 736*7295610fSBaptiste Daroussin if (tag->closed == 0) { 737*7295610fSBaptiste Daroussin tag->closed = 1; 73861d06d6bSBaptiste Daroussin if (tag == h->metaf) 73961d06d6bSBaptiste Daroussin h->metaf = NULL; 74061d06d6bSBaptiste Daroussin if (tag == h->tblt) 74161d06d6bSBaptiste Daroussin h->tblt = NULL; 74261d06d6bSBaptiste Daroussin 74361d06d6bSBaptiste Daroussin tflags = htmltags[tag->tag].flags; 74461d06d6bSBaptiste Daroussin if (tflags & HTML_INDENT) 74561d06d6bSBaptiste Daroussin h->indent--; 74661d06d6bSBaptiste Daroussin if (tflags & HTML_NOINDENT) 74761d06d6bSBaptiste Daroussin h->noindent--; 74861d06d6bSBaptiste Daroussin if (tflags & HTML_NLEND) 74961d06d6bSBaptiste Daroussin print_endline(h); 75061d06d6bSBaptiste Daroussin print_indent(h); 75161d06d6bSBaptiste Daroussin print_byte(h, '<'); 75261d06d6bSBaptiste Daroussin print_byte(h, '/'); 75361d06d6bSBaptiste Daroussin print_word(h, htmltags[tag->tag].name); 75461d06d6bSBaptiste Daroussin print_byte(h, '>'); 75561d06d6bSBaptiste Daroussin if (tflags & HTML_NLAFTER) 75661d06d6bSBaptiste Daroussin print_endline(h); 757*7295610fSBaptiste Daroussin } 758*7295610fSBaptiste Daroussin if (tag->refcnt == 0) { 75961d06d6bSBaptiste Daroussin h->tag = tag->next; 76061d06d6bSBaptiste Daroussin free(tag); 76161d06d6bSBaptiste Daroussin } 762*7295610fSBaptiste Daroussin } 76361d06d6bSBaptiste Daroussin 76461d06d6bSBaptiste Daroussin void 76561d06d6bSBaptiste Daroussin print_gen_decls(struct html *h) 76661d06d6bSBaptiste Daroussin { 76761d06d6bSBaptiste Daroussin print_word(h, "<!DOCTYPE html>"); 76861d06d6bSBaptiste Daroussin print_endline(h); 76961d06d6bSBaptiste Daroussin } 77061d06d6bSBaptiste Daroussin 77161d06d6bSBaptiste Daroussin void 77261d06d6bSBaptiste Daroussin print_gen_comment(struct html *h, struct roff_node *n) 77361d06d6bSBaptiste Daroussin { 77461d06d6bSBaptiste Daroussin int wantblank; 77561d06d6bSBaptiste Daroussin 77661d06d6bSBaptiste Daroussin print_word(h, "<!-- This is an automatically generated file." 77761d06d6bSBaptiste Daroussin " Do not edit."); 77861d06d6bSBaptiste Daroussin h->indent = 1; 77961d06d6bSBaptiste Daroussin wantblank = 0; 78061d06d6bSBaptiste Daroussin while (n != NULL && n->type == ROFFT_COMMENT) { 78161d06d6bSBaptiste Daroussin if (strstr(n->string, "-->") == NULL && 78261d06d6bSBaptiste Daroussin (wantblank || *n->string != '\0')) { 78361d06d6bSBaptiste Daroussin print_endline(h); 78461d06d6bSBaptiste Daroussin print_indent(h); 78561d06d6bSBaptiste Daroussin print_word(h, n->string); 78661d06d6bSBaptiste Daroussin wantblank = *n->string != '\0'; 78761d06d6bSBaptiste Daroussin } 78861d06d6bSBaptiste Daroussin n = n->next; 78961d06d6bSBaptiste Daroussin } 79061d06d6bSBaptiste Daroussin if (wantblank) 79161d06d6bSBaptiste Daroussin print_endline(h); 79261d06d6bSBaptiste Daroussin print_word(h, " -->"); 79361d06d6bSBaptiste Daroussin print_endline(h); 79461d06d6bSBaptiste Daroussin h->indent = 0; 79561d06d6bSBaptiste Daroussin } 79661d06d6bSBaptiste Daroussin 79761d06d6bSBaptiste Daroussin void 79861d06d6bSBaptiste Daroussin print_text(struct html *h, const char *word) 79961d06d6bSBaptiste Daroussin { 80061d06d6bSBaptiste Daroussin if (h->col && (h->flags & HTML_NOSPACE) == 0) { 80161d06d6bSBaptiste Daroussin if ( ! (HTML_KEEP & h->flags)) { 80261d06d6bSBaptiste Daroussin if (HTML_PREKEEP & h->flags) 80361d06d6bSBaptiste Daroussin h->flags |= HTML_KEEP; 80461d06d6bSBaptiste Daroussin print_endword(h); 80561d06d6bSBaptiste Daroussin } else 80661d06d6bSBaptiste Daroussin print_word(h, " "); 80761d06d6bSBaptiste Daroussin } 80861d06d6bSBaptiste Daroussin 80961d06d6bSBaptiste Daroussin assert(NULL == h->metaf); 81061d06d6bSBaptiste Daroussin switch (h->metac) { 81161d06d6bSBaptiste Daroussin case HTMLFONT_ITALIC: 81261d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_I, ""); 81361d06d6bSBaptiste Daroussin break; 81461d06d6bSBaptiste Daroussin case HTMLFONT_BOLD: 81561d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_B, ""); 81661d06d6bSBaptiste Daroussin break; 81761d06d6bSBaptiste Daroussin case HTMLFONT_BI: 81861d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_B, ""); 81961d06d6bSBaptiste Daroussin print_otag(h, TAG_I, ""); 82061d06d6bSBaptiste Daroussin break; 821*7295610fSBaptiste Daroussin case HTMLFONT_CW: 822*7295610fSBaptiste Daroussin h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); 823*7295610fSBaptiste Daroussin break; 82461d06d6bSBaptiste Daroussin default: 82561d06d6bSBaptiste Daroussin print_indent(h); 82661d06d6bSBaptiste Daroussin break; 82761d06d6bSBaptiste Daroussin } 82861d06d6bSBaptiste Daroussin 82961d06d6bSBaptiste Daroussin assert(word); 83061d06d6bSBaptiste Daroussin if ( ! print_encode(h, word, NULL, 0)) { 83161d06d6bSBaptiste Daroussin if ( ! (h->flags & HTML_NONOSPACE)) 83261d06d6bSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 83361d06d6bSBaptiste Daroussin h->flags &= ~HTML_NONEWLINE; 83461d06d6bSBaptiste Daroussin } else 83561d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE | HTML_NONEWLINE; 83661d06d6bSBaptiste Daroussin 83761d06d6bSBaptiste Daroussin if (h->metaf) { 83861d06d6bSBaptiste Daroussin print_tagq(h, h->metaf); 83961d06d6bSBaptiste Daroussin h->metaf = NULL; 84061d06d6bSBaptiste Daroussin } 84161d06d6bSBaptiste Daroussin 84261d06d6bSBaptiste Daroussin h->flags &= ~HTML_IGNDELIM; 84361d06d6bSBaptiste Daroussin } 84461d06d6bSBaptiste Daroussin 84561d06d6bSBaptiste Daroussin void 84661d06d6bSBaptiste Daroussin print_tagq(struct html *h, const struct tag *until) 84761d06d6bSBaptiste Daroussin { 848*7295610fSBaptiste Daroussin struct tag *this, *next; 84961d06d6bSBaptiste Daroussin 850*7295610fSBaptiste Daroussin for (this = h->tag; this != NULL; this = next) { 851*7295610fSBaptiste Daroussin next = this == until ? NULL : this->next; 852*7295610fSBaptiste Daroussin print_ctag(h, this); 85361d06d6bSBaptiste Daroussin } 85461d06d6bSBaptiste Daroussin } 85561d06d6bSBaptiste Daroussin 856*7295610fSBaptiste Daroussin /* 857*7295610fSBaptiste Daroussin * Close out all open elements up to but excluding suntil. 858*7295610fSBaptiste Daroussin * Note that a paragraph just inside stays open together with it 859*7295610fSBaptiste Daroussin * because paragraphs include subsequent phrasing content. 860*7295610fSBaptiste Daroussin */ 86161d06d6bSBaptiste Daroussin void 86261d06d6bSBaptiste Daroussin print_stagq(struct html *h, const struct tag *suntil) 86361d06d6bSBaptiste Daroussin { 864*7295610fSBaptiste Daroussin struct tag *this, *next; 86561d06d6bSBaptiste Daroussin 866*7295610fSBaptiste Daroussin for (this = h->tag; this != NULL; this = next) { 867*7295610fSBaptiste Daroussin next = this->next; 868*7295610fSBaptiste Daroussin if (this == suntil || (next == suntil && 869*7295610fSBaptiste Daroussin (this->tag == TAG_P || this->tag == TAG_PRE))) 870*7295610fSBaptiste Daroussin break; 871*7295610fSBaptiste Daroussin print_ctag(h, this); 87261d06d6bSBaptiste Daroussin } 87361d06d6bSBaptiste Daroussin } 87461d06d6bSBaptiste Daroussin 87561d06d6bSBaptiste Daroussin 87661d06d6bSBaptiste Daroussin /*********************************************************************** 87761d06d6bSBaptiste Daroussin * Low level output functions. 87861d06d6bSBaptiste Daroussin * They implement line breaking using a short static buffer. 87961d06d6bSBaptiste Daroussin ***********************************************************************/ 88061d06d6bSBaptiste Daroussin 88161d06d6bSBaptiste Daroussin /* 88261d06d6bSBaptiste Daroussin * Buffer one HTML output byte. 88361d06d6bSBaptiste Daroussin * If the buffer is full, flush and deactivate it and start a new line. 88461d06d6bSBaptiste Daroussin * If the buffer is inactive, print directly. 88561d06d6bSBaptiste Daroussin */ 88661d06d6bSBaptiste Daroussin static void 88761d06d6bSBaptiste Daroussin print_byte(struct html *h, char c) 88861d06d6bSBaptiste Daroussin { 88961d06d6bSBaptiste Daroussin if ((h->flags & HTML_BUFFER) == 0) { 89061d06d6bSBaptiste Daroussin putchar(c); 89161d06d6bSBaptiste Daroussin h->col++; 89261d06d6bSBaptiste Daroussin return; 89361d06d6bSBaptiste Daroussin } 89461d06d6bSBaptiste Daroussin 89561d06d6bSBaptiste Daroussin if (h->col + h->bufcol < sizeof(h->buf)) { 89661d06d6bSBaptiste Daroussin h->buf[h->bufcol++] = c; 89761d06d6bSBaptiste Daroussin return; 89861d06d6bSBaptiste Daroussin } 89961d06d6bSBaptiste Daroussin 90061d06d6bSBaptiste Daroussin putchar('\n'); 90161d06d6bSBaptiste Daroussin h->col = 0; 90261d06d6bSBaptiste Daroussin print_indent(h); 90361d06d6bSBaptiste Daroussin putchar(' '); 90461d06d6bSBaptiste Daroussin putchar(' '); 90561d06d6bSBaptiste Daroussin fwrite(h->buf, h->bufcol, 1, stdout); 90661d06d6bSBaptiste Daroussin putchar(c); 90761d06d6bSBaptiste Daroussin h->col = (h->indent + 1) * 2 + h->bufcol + 1; 90861d06d6bSBaptiste Daroussin h->bufcol = 0; 90961d06d6bSBaptiste Daroussin h->flags &= ~HTML_BUFFER; 91061d06d6bSBaptiste Daroussin } 91161d06d6bSBaptiste Daroussin 91261d06d6bSBaptiste Daroussin /* 91361d06d6bSBaptiste Daroussin * If something was printed on the current output line, end it. 91461d06d6bSBaptiste Daroussin * Not to be called right after print_indent(). 91561d06d6bSBaptiste Daroussin */ 91661d06d6bSBaptiste Daroussin void 91761d06d6bSBaptiste Daroussin print_endline(struct html *h) 91861d06d6bSBaptiste Daroussin { 91961d06d6bSBaptiste Daroussin if (h->col == 0) 92061d06d6bSBaptiste Daroussin return; 92161d06d6bSBaptiste Daroussin 92261d06d6bSBaptiste Daroussin if (h->bufcol) { 92361d06d6bSBaptiste Daroussin putchar(' '); 92461d06d6bSBaptiste Daroussin fwrite(h->buf, h->bufcol, 1, stdout); 92561d06d6bSBaptiste Daroussin h->bufcol = 0; 92661d06d6bSBaptiste Daroussin } 92761d06d6bSBaptiste Daroussin putchar('\n'); 92861d06d6bSBaptiste Daroussin h->col = 0; 92961d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE; 93061d06d6bSBaptiste Daroussin h->flags &= ~HTML_BUFFER; 93161d06d6bSBaptiste Daroussin } 93261d06d6bSBaptiste Daroussin 93361d06d6bSBaptiste Daroussin /* 93461d06d6bSBaptiste Daroussin * Flush the HTML output buffer. 93561d06d6bSBaptiste Daroussin * If it is inactive, activate it. 93661d06d6bSBaptiste Daroussin */ 93761d06d6bSBaptiste Daroussin static void 93861d06d6bSBaptiste Daroussin print_endword(struct html *h) 93961d06d6bSBaptiste Daroussin { 94061d06d6bSBaptiste Daroussin if (h->noindent) { 94161d06d6bSBaptiste Daroussin print_byte(h, ' '); 94261d06d6bSBaptiste Daroussin return; 94361d06d6bSBaptiste Daroussin } 94461d06d6bSBaptiste Daroussin 94561d06d6bSBaptiste Daroussin if ((h->flags & HTML_BUFFER) == 0) { 94661d06d6bSBaptiste Daroussin h->col++; 94761d06d6bSBaptiste Daroussin h->flags |= HTML_BUFFER; 94861d06d6bSBaptiste Daroussin } else if (h->bufcol) { 94961d06d6bSBaptiste Daroussin putchar(' '); 95061d06d6bSBaptiste Daroussin fwrite(h->buf, h->bufcol, 1, stdout); 95161d06d6bSBaptiste Daroussin h->col += h->bufcol + 1; 95261d06d6bSBaptiste Daroussin } 95361d06d6bSBaptiste Daroussin h->bufcol = 0; 95461d06d6bSBaptiste Daroussin } 95561d06d6bSBaptiste Daroussin 95661d06d6bSBaptiste Daroussin /* 95761d06d6bSBaptiste Daroussin * If at the beginning of a new output line, 95861d06d6bSBaptiste Daroussin * perform indentation and mark the line as containing output. 95961d06d6bSBaptiste Daroussin * Make sure to really produce some output right afterwards, 96061d06d6bSBaptiste Daroussin * but do not use print_otag() for producing it. 96161d06d6bSBaptiste Daroussin */ 96261d06d6bSBaptiste Daroussin static void 96361d06d6bSBaptiste Daroussin print_indent(struct html *h) 96461d06d6bSBaptiste Daroussin { 96561d06d6bSBaptiste Daroussin size_t i; 96661d06d6bSBaptiste Daroussin 96761d06d6bSBaptiste Daroussin if (h->col) 96861d06d6bSBaptiste Daroussin return; 96961d06d6bSBaptiste Daroussin 97061d06d6bSBaptiste Daroussin if (h->noindent == 0) { 97161d06d6bSBaptiste Daroussin h->col = h->indent * 2; 97261d06d6bSBaptiste Daroussin for (i = 0; i < h->col; i++) 97361d06d6bSBaptiste Daroussin putchar(' '); 97461d06d6bSBaptiste Daroussin } 97561d06d6bSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 97661d06d6bSBaptiste Daroussin } 97761d06d6bSBaptiste Daroussin 97861d06d6bSBaptiste Daroussin /* 97961d06d6bSBaptiste Daroussin * Print or buffer some characters 98061d06d6bSBaptiste Daroussin * depending on the current HTML output buffer state. 98161d06d6bSBaptiste Daroussin */ 98261d06d6bSBaptiste Daroussin static void 98361d06d6bSBaptiste Daroussin print_word(struct html *h, const char *cp) 98461d06d6bSBaptiste Daroussin { 98561d06d6bSBaptiste Daroussin while (*cp != '\0') 98661d06d6bSBaptiste Daroussin print_byte(h, *cp++); 98761d06d6bSBaptiste Daroussin } 988