1*c1c95addSBrooks Davis /* $Id: html.c,v 1.279 2022/08/09 11:23:11 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 46d38604fSBaptiste Daroussin * Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> 5*c1c95addSBrooks Davis * Copyright (c) 2022 Anna Vyalkova <cyber@sysrq.in> 661d06d6bSBaptiste Daroussin * 761d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 861d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 961d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 1061d06d6bSBaptiste Daroussin * 1161d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1261d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1361d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1461d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1561d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1661d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1761d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 186d38604fSBaptiste Daroussin * 196d38604fSBaptiste Daroussin * Common functions for mandoc(1) HTML formatters. 206d38604fSBaptiste Daroussin * For use by individual formatters and by the main program. 2161d06d6bSBaptiste Daroussin */ 2261d06d6bSBaptiste Daroussin #include "config.h" 2361d06d6bSBaptiste Daroussin 2461d06d6bSBaptiste Daroussin #include <sys/types.h> 257295610fSBaptiste Daroussin #include <sys/stat.h> 2661d06d6bSBaptiste Daroussin 2761d06d6bSBaptiste Daroussin #include <assert.h> 2861d06d6bSBaptiste Daroussin #include <ctype.h> 2961d06d6bSBaptiste Daroussin #include <stdarg.h> 3061d06d6bSBaptiste Daroussin #include <stddef.h> 3161d06d6bSBaptiste Daroussin #include <stdio.h> 3261d06d6bSBaptiste Daroussin #include <stdint.h> 3361d06d6bSBaptiste Daroussin #include <stdlib.h> 3461d06d6bSBaptiste Daroussin #include <string.h> 3561d06d6bSBaptiste Daroussin #include <unistd.h> 3661d06d6bSBaptiste Daroussin 3761d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 3861d06d6bSBaptiste Daroussin #include "mandoc_ohash.h" 3961d06d6bSBaptiste Daroussin #include "mandoc.h" 4061d06d6bSBaptiste Daroussin #include "roff.h" 4161d06d6bSBaptiste Daroussin #include "out.h" 4261d06d6bSBaptiste Daroussin #include "html.h" 4361d06d6bSBaptiste Daroussin #include "manconf.h" 4461d06d6bSBaptiste Daroussin #include "main.h" 4561d06d6bSBaptiste Daroussin 4661d06d6bSBaptiste Daroussin struct htmldata { 4761d06d6bSBaptiste Daroussin const char *name; 4861d06d6bSBaptiste Daroussin int flags; 496d38604fSBaptiste Daroussin #define HTML_INPHRASE (1 << 0) /* Can appear in phrasing context. */ 506d38604fSBaptiste Daroussin #define HTML_TOPHRASE (1 << 1) /* Establishes phrasing context. */ 516d38604fSBaptiste Daroussin #define HTML_NOSTACK (1 << 2) /* Does not have an end tag. */ 526d38604fSBaptiste Daroussin #define HTML_NLBEFORE (1 << 3) /* Output line break before opening. */ 536d38604fSBaptiste Daroussin #define HTML_NLBEGIN (1 << 4) /* Output line break after opening. */ 546d38604fSBaptiste Daroussin #define HTML_NLEND (1 << 5) /* Output line break before closing. */ 556d38604fSBaptiste Daroussin #define HTML_NLAFTER (1 << 6) /* Output line break after closing. */ 5661d06d6bSBaptiste Daroussin #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) 5761d06d6bSBaptiste Daroussin #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) 5861d06d6bSBaptiste Daroussin #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) 596d38604fSBaptiste Daroussin #define HTML_INDENT (1 << 7) /* Indent content by two spaces. */ 606d38604fSBaptiste Daroussin #define HTML_NOINDENT (1 << 8) /* Exception: never indent content. */ 6161d06d6bSBaptiste Daroussin }; 6261d06d6bSBaptiste Daroussin 6361d06d6bSBaptiste Daroussin static const struct htmldata htmltags[TAG_MAX] = { 6461d06d6bSBaptiste Daroussin {"html", HTML_NLALL}, 6561d06d6bSBaptiste Daroussin {"head", HTML_NLALL | HTML_INDENT}, 666d38604fSBaptiste Daroussin {"meta", HTML_NOSTACK | HTML_NLALL}, 676d38604fSBaptiste Daroussin {"link", HTML_NOSTACK | HTML_NLALL}, 686d38604fSBaptiste Daroussin {"style", HTML_NLALL | HTML_INDENT}, 6961d06d6bSBaptiste Daroussin {"title", HTML_NLAROUND}, 706d38604fSBaptiste Daroussin {"body", HTML_NLALL}, 71*c1c95addSBrooks Davis {"main", HTML_NLALL}, 7261d06d6bSBaptiste Daroussin {"div", HTML_NLAROUND}, 737295610fSBaptiste Daroussin {"section", HTML_NLALL}, 74*c1c95addSBrooks Davis {"nav", HTML_NLALL}, 7561d06d6bSBaptiste Daroussin {"table", HTML_NLALL | HTML_INDENT}, 7661d06d6bSBaptiste Daroussin {"tr", HTML_NLALL | HTML_INDENT}, 7761d06d6bSBaptiste Daroussin {"td", HTML_NLAROUND}, 7861d06d6bSBaptiste Daroussin {"li", HTML_NLAROUND | HTML_INDENT}, 7961d06d6bSBaptiste Daroussin {"ul", HTML_NLALL | HTML_INDENT}, 8061d06d6bSBaptiste Daroussin {"ol", HTML_NLALL | HTML_INDENT}, 8161d06d6bSBaptiste Daroussin {"dl", HTML_NLALL | HTML_INDENT}, 8261d06d6bSBaptiste Daroussin {"dt", HTML_NLAROUND}, 8361d06d6bSBaptiste Daroussin {"dd", HTML_NLAROUND | HTML_INDENT}, 846d38604fSBaptiste Daroussin {"h2", HTML_TOPHRASE | HTML_NLAROUND}, 85*c1c95addSBrooks Davis {"h3", HTML_TOPHRASE | HTML_NLAROUND}, 866d38604fSBaptiste Daroussin {"p", HTML_TOPHRASE | HTML_NLAROUND | HTML_INDENT}, 876d38604fSBaptiste Daroussin {"pre", HTML_TOPHRASE | HTML_NLAROUND | HTML_NOINDENT}, 886d38604fSBaptiste Daroussin {"a", HTML_INPHRASE | HTML_TOPHRASE}, 896d38604fSBaptiste Daroussin {"b", HTML_INPHRASE | HTML_TOPHRASE}, 906d38604fSBaptiste Daroussin {"cite", HTML_INPHRASE | HTML_TOPHRASE}, 916d38604fSBaptiste Daroussin {"code", HTML_INPHRASE | HTML_TOPHRASE}, 926d38604fSBaptiste Daroussin {"i", HTML_INPHRASE | HTML_TOPHRASE}, 936d38604fSBaptiste Daroussin {"small", HTML_INPHRASE | HTML_TOPHRASE}, 946d38604fSBaptiste Daroussin {"span", HTML_INPHRASE | HTML_TOPHRASE}, 956d38604fSBaptiste Daroussin {"var", HTML_INPHRASE | HTML_TOPHRASE}, 966d38604fSBaptiste Daroussin {"br", HTML_INPHRASE | HTML_NOSTACK | HTML_NLALL}, 976d38604fSBaptiste Daroussin {"hr", HTML_INPHRASE | HTML_NOSTACK}, 986d38604fSBaptiste Daroussin {"mark", HTML_INPHRASE }, 996d38604fSBaptiste Daroussin {"math", HTML_INPHRASE | HTML_NLALL | HTML_INDENT}, 10061d06d6bSBaptiste Daroussin {"mrow", 0}, 10161d06d6bSBaptiste Daroussin {"mi", 0}, 10261d06d6bSBaptiste Daroussin {"mn", 0}, 10361d06d6bSBaptiste Daroussin {"mo", 0}, 10461d06d6bSBaptiste Daroussin {"msup", 0}, 10561d06d6bSBaptiste Daroussin {"msub", 0}, 10661d06d6bSBaptiste Daroussin {"msubsup", 0}, 10761d06d6bSBaptiste Daroussin {"mfrac", 0}, 10861d06d6bSBaptiste Daroussin {"msqrt", 0}, 10961d06d6bSBaptiste Daroussin {"mfenced", 0}, 11061d06d6bSBaptiste Daroussin {"mtable", 0}, 11161d06d6bSBaptiste Daroussin {"mtr", 0}, 11261d06d6bSBaptiste Daroussin {"mtd", 0}, 11361d06d6bSBaptiste Daroussin {"munderover", 0}, 11461d06d6bSBaptiste Daroussin {"munder", 0}, 11561d06d6bSBaptiste Daroussin {"mover", 0}, 11661d06d6bSBaptiste Daroussin }; 11761d06d6bSBaptiste Daroussin 11861d06d6bSBaptiste Daroussin /* Avoid duplicate HTML id= attributes. */ 1196d38604fSBaptiste Daroussin 1206d38604fSBaptiste Daroussin struct id_entry { 1216d38604fSBaptiste Daroussin int ord; /* Ordinal number of the latest occurrence. */ 1226d38604fSBaptiste Daroussin char id[]; /* The id= attribute without any ordinal suffix. */ 1236d38604fSBaptiste Daroussin }; 12461d06d6bSBaptiste Daroussin static struct ohash id_unique; 12561d06d6bSBaptiste Daroussin 1267295610fSBaptiste Daroussin static void html_reset_internal(struct html *); 12761d06d6bSBaptiste Daroussin static void print_byte(struct html *, char); 12861d06d6bSBaptiste Daroussin static void print_endword(struct html *); 12961d06d6bSBaptiste Daroussin static void print_indent(struct html *); 13061d06d6bSBaptiste Daroussin static void print_word(struct html *, const char *); 13161d06d6bSBaptiste Daroussin 13261d06d6bSBaptiste Daroussin static void print_ctag(struct html *, struct tag *); 13361d06d6bSBaptiste Daroussin static int print_escape(struct html *, char); 13461d06d6bSBaptiste Daroussin static int print_encode(struct html *, const char *, const char *, int); 13561d06d6bSBaptiste Daroussin static void print_href(struct html *, const char *, const char *, int); 13645a5aec3SBaptiste Daroussin static void print_metaf(struct html *); 13761d06d6bSBaptiste Daroussin 13861d06d6bSBaptiste Daroussin 13961d06d6bSBaptiste Daroussin void * 14061d06d6bSBaptiste Daroussin html_alloc(const struct manoutput *outopts) 14161d06d6bSBaptiste Daroussin { 14261d06d6bSBaptiste Daroussin struct html *h; 14361d06d6bSBaptiste Daroussin 14461d06d6bSBaptiste Daroussin h = mandoc_calloc(1, sizeof(struct html)); 14561d06d6bSBaptiste Daroussin 14661d06d6bSBaptiste Daroussin h->tag = NULL; 1476d38604fSBaptiste Daroussin h->metac = h->metal = ESCAPE_FONTROMAN; 14861d06d6bSBaptiste Daroussin h->style = outopts->style; 1497295610fSBaptiste Daroussin if ((h->base_man1 = outopts->man) == NULL) 1507295610fSBaptiste Daroussin h->base_man2 = NULL; 1517295610fSBaptiste Daroussin else if ((h->base_man2 = strchr(h->base_man1, ';')) != NULL) 1527295610fSBaptiste Daroussin *h->base_man2++ = '\0'; 15361d06d6bSBaptiste Daroussin h->base_includes = outopts->includes; 15461d06d6bSBaptiste Daroussin if (outopts->fragment) 15561d06d6bSBaptiste Daroussin h->oflags |= HTML_FRAGMENT; 1567295610fSBaptiste Daroussin if (outopts->toc) 1577295610fSBaptiste Daroussin h->oflags |= HTML_TOC; 15861d06d6bSBaptiste Daroussin 1596d38604fSBaptiste Daroussin mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); 16061d06d6bSBaptiste Daroussin 16161d06d6bSBaptiste Daroussin return h; 16261d06d6bSBaptiste Daroussin } 16361d06d6bSBaptiste Daroussin 1647295610fSBaptiste Daroussin static void 1657295610fSBaptiste Daroussin html_reset_internal(struct html *h) 16661d06d6bSBaptiste Daroussin { 16761d06d6bSBaptiste Daroussin struct tag *tag; 1686d38604fSBaptiste Daroussin struct id_entry *entry; 16961d06d6bSBaptiste Daroussin unsigned int slot; 17061d06d6bSBaptiste Daroussin 17161d06d6bSBaptiste Daroussin while ((tag = h->tag) != NULL) { 17261d06d6bSBaptiste Daroussin h->tag = tag->next; 17361d06d6bSBaptiste Daroussin free(tag); 17461d06d6bSBaptiste Daroussin } 1756d38604fSBaptiste Daroussin entry = ohash_first(&id_unique, &slot); 1766d38604fSBaptiste Daroussin while (entry != NULL) { 1776d38604fSBaptiste Daroussin free(entry); 1786d38604fSBaptiste Daroussin entry = ohash_next(&id_unique, &slot); 17961d06d6bSBaptiste Daroussin } 18061d06d6bSBaptiste Daroussin ohash_delete(&id_unique); 18161d06d6bSBaptiste Daroussin } 18261d06d6bSBaptiste Daroussin 18361d06d6bSBaptiste Daroussin void 1847295610fSBaptiste Daroussin html_reset(void *p) 1857295610fSBaptiste Daroussin { 1867295610fSBaptiste Daroussin html_reset_internal(p); 1876d38604fSBaptiste Daroussin mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); 1887295610fSBaptiste Daroussin } 1897295610fSBaptiste Daroussin 1907295610fSBaptiste Daroussin void 1917295610fSBaptiste Daroussin html_free(void *p) 1927295610fSBaptiste Daroussin { 1937295610fSBaptiste Daroussin html_reset_internal(p); 1947295610fSBaptiste Daroussin free(p); 1957295610fSBaptiste Daroussin } 1967295610fSBaptiste Daroussin 1977295610fSBaptiste Daroussin void 19861d06d6bSBaptiste Daroussin print_gen_head(struct html *h) 19961d06d6bSBaptiste Daroussin { 20061d06d6bSBaptiste Daroussin struct tag *t; 20161d06d6bSBaptiste Daroussin 20261d06d6bSBaptiste Daroussin print_otag(h, TAG_META, "?", "charset", "utf-8"); 2036d38604fSBaptiste Daroussin print_otag(h, TAG_META, "??", "name", "viewport", 2046d38604fSBaptiste Daroussin "content", "width=device-width, initial-scale=1.0"); 20561d06d6bSBaptiste Daroussin if (h->style != NULL) { 20661d06d6bSBaptiste Daroussin print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", 20761d06d6bSBaptiste Daroussin h->style, "type", "text/css", "media", "all"); 20861d06d6bSBaptiste Daroussin return; 20961d06d6bSBaptiste Daroussin } 21061d06d6bSBaptiste Daroussin 21161d06d6bSBaptiste Daroussin /* 21261d06d6bSBaptiste Daroussin * Print a minimal embedded style sheet. 21361d06d6bSBaptiste Daroussin */ 21461d06d6bSBaptiste Daroussin 21561d06d6bSBaptiste Daroussin t = print_otag(h, TAG_STYLE, ""); 21661d06d6bSBaptiste Daroussin print_text(h, "table.head, table.foot { width: 100%; }"); 21761d06d6bSBaptiste Daroussin print_endline(h); 21861d06d6bSBaptiste Daroussin print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }"); 21961d06d6bSBaptiste Daroussin print_endline(h); 22061d06d6bSBaptiste Daroussin print_text(h, "td.head-vol { text-align: center; }"); 22161d06d6bSBaptiste Daroussin print_endline(h); 2226d38604fSBaptiste Daroussin print_text(h, ".Nd, .Bf, .Op { display: inline; }"); 22361d06d6bSBaptiste Daroussin print_endline(h); 2246d38604fSBaptiste Daroussin print_text(h, ".Pa, .Ad { font-style: italic; }"); 22561d06d6bSBaptiste Daroussin print_endline(h); 2266d38604fSBaptiste Daroussin print_text(h, ".Ms { font-weight: bold; }"); 22761d06d6bSBaptiste Daroussin print_endline(h); 2286d38604fSBaptiste Daroussin print_text(h, ".Bl-diag "); 22961d06d6bSBaptiste Daroussin print_byte(h, '>'); 23061d06d6bSBaptiste Daroussin print_text(h, " dt { font-weight: bold; }"); 23161d06d6bSBaptiste Daroussin print_endline(h); 2326d38604fSBaptiste Daroussin print_text(h, "code.Nm, .Fl, .Cm, .Ic, code.In, .Fd, .Fn, .Cd " 2336d38604fSBaptiste Daroussin "{ font-weight: bold; font-family: inherit; }"); 23461d06d6bSBaptiste Daroussin print_tagq(h, t); 23561d06d6bSBaptiste Daroussin } 23661d06d6bSBaptiste Daroussin 23745a5aec3SBaptiste Daroussin int 23845a5aec3SBaptiste Daroussin html_setfont(struct html *h, enum mandoc_esc font) 23961d06d6bSBaptiste Daroussin { 24045a5aec3SBaptiste Daroussin switch (font) { 24161d06d6bSBaptiste Daroussin case ESCAPE_FONTPREV: 24261d06d6bSBaptiste Daroussin font = h->metal; 24361d06d6bSBaptiste Daroussin break; 24461d06d6bSBaptiste Daroussin case ESCAPE_FONTITALIC: 24561d06d6bSBaptiste Daroussin case ESCAPE_FONTBOLD: 24661d06d6bSBaptiste Daroussin case ESCAPE_FONTBI: 24745a5aec3SBaptiste Daroussin case ESCAPE_FONTROMAN: 2486d38604fSBaptiste Daroussin case ESCAPE_FONTCR: 2496d38604fSBaptiste Daroussin case ESCAPE_FONTCB: 2506d38604fSBaptiste Daroussin case ESCAPE_FONTCI: 2517295610fSBaptiste Daroussin break; 25261d06d6bSBaptiste Daroussin case ESCAPE_FONT: 25345a5aec3SBaptiste Daroussin font = ESCAPE_FONTROMAN; 25461d06d6bSBaptiste Daroussin break; 25561d06d6bSBaptiste Daroussin default: 25645a5aec3SBaptiste Daroussin return 0; 25745a5aec3SBaptiste Daroussin } 25845a5aec3SBaptiste Daroussin h->metal = h->metac; 25945a5aec3SBaptiste Daroussin h->metac = font; 26045a5aec3SBaptiste Daroussin return 1; 26161d06d6bSBaptiste Daroussin } 26261d06d6bSBaptiste Daroussin 26345a5aec3SBaptiste Daroussin static void 26445a5aec3SBaptiste Daroussin print_metaf(struct html *h) 26545a5aec3SBaptiste Daroussin { 26661d06d6bSBaptiste Daroussin if (h->metaf) { 26761d06d6bSBaptiste Daroussin print_tagq(h, h->metaf); 26861d06d6bSBaptiste Daroussin h->metaf = NULL; 26961d06d6bSBaptiste Daroussin } 27045a5aec3SBaptiste Daroussin switch (h->metac) { 27145a5aec3SBaptiste Daroussin case ESCAPE_FONTITALIC: 27261d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_I, ""); 27361d06d6bSBaptiste Daroussin break; 27445a5aec3SBaptiste Daroussin case ESCAPE_FONTBOLD: 27561d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_B, ""); 27661d06d6bSBaptiste Daroussin break; 27745a5aec3SBaptiste Daroussin case ESCAPE_FONTBI: 27861d06d6bSBaptiste Daroussin h->metaf = print_otag(h, TAG_B, ""); 27961d06d6bSBaptiste Daroussin print_otag(h, TAG_I, ""); 28061d06d6bSBaptiste Daroussin break; 2816d38604fSBaptiste Daroussin case ESCAPE_FONTCR: 2827295610fSBaptiste Daroussin h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); 2837295610fSBaptiste Daroussin break; 2846d38604fSBaptiste Daroussin case ESCAPE_FONTCB: 2856d38604fSBaptiste Daroussin h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); 2866d38604fSBaptiste Daroussin print_otag(h, TAG_B, ""); 2876d38604fSBaptiste Daroussin break; 2886d38604fSBaptiste Daroussin case ESCAPE_FONTCI: 2896d38604fSBaptiste Daroussin h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); 2906d38604fSBaptiste Daroussin print_otag(h, TAG_I, ""); 2916d38604fSBaptiste Daroussin break; 29261d06d6bSBaptiste Daroussin default: 29361d06d6bSBaptiste Daroussin break; 29461d06d6bSBaptiste Daroussin } 29561d06d6bSBaptiste Daroussin } 29661d06d6bSBaptiste Daroussin 2977295610fSBaptiste Daroussin void 2987295610fSBaptiste Daroussin html_close_paragraph(struct html *h) 2997295610fSBaptiste Daroussin { 3006d38604fSBaptiste Daroussin struct tag *this, *next; 3016d38604fSBaptiste Daroussin int flags; 3027295610fSBaptiste Daroussin 3036d38604fSBaptiste Daroussin this = h->tag; 3046d38604fSBaptiste Daroussin for (;;) { 3056d38604fSBaptiste Daroussin next = this->next; 3066d38604fSBaptiste Daroussin flags = htmltags[this->tag].flags; 3076d38604fSBaptiste Daroussin if (flags & (HTML_INPHRASE | HTML_TOPHRASE)) 3086d38604fSBaptiste Daroussin print_ctag(h, this); 3096d38604fSBaptiste Daroussin if ((flags & HTML_INPHRASE) == 0) 3107295610fSBaptiste Daroussin break; 3116d38604fSBaptiste Daroussin this = next; 3127295610fSBaptiste Daroussin } 3137295610fSBaptiste Daroussin } 3147295610fSBaptiste Daroussin 3157295610fSBaptiste Daroussin /* 3167295610fSBaptiste Daroussin * ROFF_nf switches to no-fill mode, ROFF_fi to fill mode. 3177295610fSBaptiste Daroussin * TOKEN_NONE does not switch. The old mode is returned. 3187295610fSBaptiste Daroussin */ 3197295610fSBaptiste Daroussin enum roff_tok 3207295610fSBaptiste Daroussin html_fillmode(struct html *h, enum roff_tok want) 3217295610fSBaptiste Daroussin { 3227295610fSBaptiste Daroussin struct tag *t; 3237295610fSBaptiste Daroussin enum roff_tok had; 3247295610fSBaptiste Daroussin 3257295610fSBaptiste Daroussin for (t = h->tag; t != NULL; t = t->next) 3267295610fSBaptiste Daroussin if (t->tag == TAG_PRE) 3277295610fSBaptiste Daroussin break; 3287295610fSBaptiste Daroussin 3297295610fSBaptiste Daroussin had = t == NULL ? ROFF_fi : ROFF_nf; 3307295610fSBaptiste Daroussin 3317295610fSBaptiste Daroussin if (want != had) { 3327295610fSBaptiste Daroussin switch (want) { 3337295610fSBaptiste Daroussin case ROFF_fi: 3347295610fSBaptiste Daroussin print_tagq(h, t); 3357295610fSBaptiste Daroussin break; 3367295610fSBaptiste Daroussin case ROFF_nf: 3377295610fSBaptiste Daroussin html_close_paragraph(h); 3387295610fSBaptiste Daroussin print_otag(h, TAG_PRE, ""); 3397295610fSBaptiste Daroussin break; 3407295610fSBaptiste Daroussin case TOKEN_NONE: 3417295610fSBaptiste Daroussin break; 3427295610fSBaptiste Daroussin default: 3437295610fSBaptiste Daroussin abort(); 3447295610fSBaptiste Daroussin } 3457295610fSBaptiste Daroussin } 3467295610fSBaptiste Daroussin return had; 3477295610fSBaptiste Daroussin } 3487295610fSBaptiste Daroussin 3496d38604fSBaptiste Daroussin /* 3506d38604fSBaptiste Daroussin * Allocate a string to be used for the "id=" attribute of an HTML 3516d38604fSBaptiste Daroussin * element and/or as a segment identifier for a URI in an <a> element. 3526d38604fSBaptiste Daroussin * The function may fail and return NULL if the node lacks text data 3536d38604fSBaptiste Daroussin * to create the attribute from. 3546d38604fSBaptiste Daroussin * The caller is responsible for free(3)ing the returned string. 3556d38604fSBaptiste Daroussin * 3566d38604fSBaptiste Daroussin * If the "unique" argument is non-zero, the "id_unique" ohash table 3576d38604fSBaptiste Daroussin * is used for de-duplication. If the "unique" argument is 1, 3586d38604fSBaptiste Daroussin * it is the first time the function is called for this tag and 3596d38604fSBaptiste Daroussin * location, so if an ordinal suffix is needed, it is incremented. 3606d38604fSBaptiste Daroussin * If the "unique" argument is 2, it is the second time the function 3616d38604fSBaptiste Daroussin * is called for this tag and location, so the ordinal suffix 3626d38604fSBaptiste Daroussin * remains unchanged. 3636d38604fSBaptiste Daroussin */ 36461d06d6bSBaptiste Daroussin char * 36561d06d6bSBaptiste Daroussin html_make_id(const struct roff_node *n, int unique) 36661d06d6bSBaptiste Daroussin { 36761d06d6bSBaptiste Daroussin const struct roff_node *nch; 3686d38604fSBaptiste Daroussin struct id_entry *entry; 3696d38604fSBaptiste Daroussin char *buf, *cp; 3706d38604fSBaptiste Daroussin size_t len; 37161d06d6bSBaptiste Daroussin unsigned int slot; 37261d06d6bSBaptiste Daroussin 3736d38604fSBaptiste Daroussin if (n->tag != NULL) 3746d38604fSBaptiste Daroussin buf = mandoc_strdup(n->tag); 3756d38604fSBaptiste Daroussin else { 3766d38604fSBaptiste Daroussin switch (n->tok) { 3776d38604fSBaptiste Daroussin case MDOC_Sh: 3786d38604fSBaptiste Daroussin case MDOC_Ss: 3796d38604fSBaptiste Daroussin case MDOC_Sx: 3806d38604fSBaptiste Daroussin case MAN_SH: 3816d38604fSBaptiste Daroussin case MAN_SS: 38261d06d6bSBaptiste Daroussin for (nch = n->child; nch != NULL; nch = nch->next) 38361d06d6bSBaptiste Daroussin if (nch->type != ROFFT_TEXT) 38461d06d6bSBaptiste Daroussin return NULL; 38561d06d6bSBaptiste Daroussin buf = NULL; 38661d06d6bSBaptiste Daroussin deroff(&buf, n); 38761d06d6bSBaptiste Daroussin if (buf == NULL) 38861d06d6bSBaptiste Daroussin return NULL; 3896d38604fSBaptiste Daroussin break; 3906d38604fSBaptiste Daroussin default: 3916d38604fSBaptiste Daroussin if (n->child == NULL || n->child->type != ROFFT_TEXT) 3926d38604fSBaptiste Daroussin return NULL; 3936d38604fSBaptiste Daroussin buf = mandoc_strdup(n->child->string); 3946d38604fSBaptiste Daroussin break; 3956d38604fSBaptiste Daroussin } 3966d38604fSBaptiste Daroussin } 39761d06d6bSBaptiste Daroussin 39861d06d6bSBaptiste Daroussin /* 39961d06d6bSBaptiste Daroussin * In ID attributes, only use ASCII characters that are 40061d06d6bSBaptiste Daroussin * permitted in URL-fragment strings according to the 40161d06d6bSBaptiste Daroussin * explicit list at: 40261d06d6bSBaptiste Daroussin * https://url.spec.whatwg.org/#url-fragment-string 4036d38604fSBaptiste Daroussin * In addition, reserve '~' for ordinal suffixes. 40461d06d6bSBaptiste Daroussin */ 40561d06d6bSBaptiste Daroussin 406*c1c95addSBrooks Davis for (cp = buf; *cp != '\0'; cp++) { 407*c1c95addSBrooks Davis if (*cp == ASCII_HYPH) 408*c1c95addSBrooks Davis *cp = '-'; 409*c1c95addSBrooks Davis else if (isalnum((unsigned char)*cp) == 0 && 4106d38604fSBaptiste Daroussin strchr("!$&'()*+,-./:;=?@_", *cp) == NULL) 41161d06d6bSBaptiste Daroussin *cp = '_'; 412*c1c95addSBrooks Davis } 41361d06d6bSBaptiste Daroussin 41461d06d6bSBaptiste Daroussin if (unique == 0) 41561d06d6bSBaptiste Daroussin return buf; 41661d06d6bSBaptiste Daroussin 41761d06d6bSBaptiste Daroussin /* Avoid duplicate HTML id= attributes. */ 41861d06d6bSBaptiste Daroussin 41961d06d6bSBaptiste Daroussin slot = ohash_qlookup(&id_unique, buf); 4206d38604fSBaptiste Daroussin if ((entry = ohash_find(&id_unique, slot)) == NULL) { 4216d38604fSBaptiste Daroussin len = strlen(buf) + 1; 4226d38604fSBaptiste Daroussin entry = mandoc_malloc(sizeof(*entry) + len); 4236d38604fSBaptiste Daroussin entry->ord = 1; 4246d38604fSBaptiste Daroussin memcpy(entry->id, buf, len); 4256d38604fSBaptiste Daroussin ohash_insert(&id_unique, slot, entry); 4266d38604fSBaptiste Daroussin } else if (unique == 1) 4276d38604fSBaptiste Daroussin entry->ord++; 4286d38604fSBaptiste Daroussin 4296d38604fSBaptiste Daroussin if (entry->ord > 1) { 4306d38604fSBaptiste Daroussin cp = buf; 4316d38604fSBaptiste Daroussin mandoc_asprintf(&buf, "%s~%d", cp, entry->ord); 4326d38604fSBaptiste Daroussin free(cp); 43361d06d6bSBaptiste Daroussin } 43461d06d6bSBaptiste Daroussin return buf; 43561d06d6bSBaptiste Daroussin } 43661d06d6bSBaptiste Daroussin 43761d06d6bSBaptiste Daroussin static int 43861d06d6bSBaptiste Daroussin print_escape(struct html *h, char c) 43961d06d6bSBaptiste Daroussin { 44061d06d6bSBaptiste Daroussin 44161d06d6bSBaptiste Daroussin switch (c) { 44261d06d6bSBaptiste Daroussin case '<': 44361d06d6bSBaptiste Daroussin print_word(h, "<"); 44461d06d6bSBaptiste Daroussin break; 44561d06d6bSBaptiste Daroussin case '>': 44661d06d6bSBaptiste Daroussin print_word(h, ">"); 44761d06d6bSBaptiste Daroussin break; 44861d06d6bSBaptiste Daroussin case '&': 44961d06d6bSBaptiste Daroussin print_word(h, "&"); 45061d06d6bSBaptiste Daroussin break; 45161d06d6bSBaptiste Daroussin case '"': 45261d06d6bSBaptiste Daroussin print_word(h, """); 45361d06d6bSBaptiste Daroussin break; 45461d06d6bSBaptiste Daroussin case ASCII_NBRSP: 45561d06d6bSBaptiste Daroussin print_word(h, " "); 45661d06d6bSBaptiste Daroussin break; 45761d06d6bSBaptiste Daroussin case ASCII_HYPH: 45861d06d6bSBaptiste Daroussin print_byte(h, '-'); 45961d06d6bSBaptiste Daroussin break; 46061d06d6bSBaptiste Daroussin case ASCII_BREAK: 46161d06d6bSBaptiste Daroussin break; 46261d06d6bSBaptiste Daroussin default: 46361d06d6bSBaptiste Daroussin return 0; 46461d06d6bSBaptiste Daroussin } 46561d06d6bSBaptiste Daroussin return 1; 46661d06d6bSBaptiste Daroussin } 46761d06d6bSBaptiste Daroussin 46861d06d6bSBaptiste Daroussin static int 46961d06d6bSBaptiste Daroussin print_encode(struct html *h, const char *p, const char *pend, int norecurse) 47061d06d6bSBaptiste Daroussin { 47161d06d6bSBaptiste Daroussin char numbuf[16]; 47261d06d6bSBaptiste Daroussin const char *seq; 47361d06d6bSBaptiste Daroussin size_t sz; 47461d06d6bSBaptiste Daroussin int c, len, breakline, nospace; 47561d06d6bSBaptiste Daroussin enum mandoc_esc esc; 47661d06d6bSBaptiste Daroussin static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"', 47761d06d6bSBaptiste Daroussin ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; 47861d06d6bSBaptiste Daroussin 47961d06d6bSBaptiste Daroussin if (pend == NULL) 48061d06d6bSBaptiste Daroussin pend = strchr(p, '\0'); 48161d06d6bSBaptiste Daroussin 48261d06d6bSBaptiste Daroussin breakline = 0; 48361d06d6bSBaptiste Daroussin nospace = 0; 48461d06d6bSBaptiste Daroussin 48561d06d6bSBaptiste Daroussin while (p < pend) { 48661d06d6bSBaptiste Daroussin if (HTML_SKIPCHAR & h->flags && '\\' != *p) { 48761d06d6bSBaptiste Daroussin h->flags &= ~HTML_SKIPCHAR; 48861d06d6bSBaptiste Daroussin p++; 48961d06d6bSBaptiste Daroussin continue; 49061d06d6bSBaptiste Daroussin } 49161d06d6bSBaptiste Daroussin 49261d06d6bSBaptiste Daroussin for (sz = strcspn(p, rejs); sz-- && p < pend; p++) 49361d06d6bSBaptiste Daroussin print_byte(h, *p); 49461d06d6bSBaptiste Daroussin 49561d06d6bSBaptiste Daroussin if (breakline && 49661d06d6bSBaptiste Daroussin (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) { 4977295610fSBaptiste Daroussin print_otag(h, TAG_BR, ""); 49861d06d6bSBaptiste Daroussin breakline = 0; 49961d06d6bSBaptiste Daroussin while (p < pend && (*p == ' ' || *p == ASCII_NBRSP)) 50061d06d6bSBaptiste Daroussin p++; 50161d06d6bSBaptiste Daroussin continue; 50261d06d6bSBaptiste Daroussin } 50361d06d6bSBaptiste Daroussin 50461d06d6bSBaptiste Daroussin if (p >= pend) 50561d06d6bSBaptiste Daroussin break; 50661d06d6bSBaptiste Daroussin 50761d06d6bSBaptiste Daroussin if (*p == ' ') { 50861d06d6bSBaptiste Daroussin print_endword(h); 50961d06d6bSBaptiste Daroussin p++; 51061d06d6bSBaptiste Daroussin continue; 51161d06d6bSBaptiste Daroussin } 51261d06d6bSBaptiste Daroussin 51361d06d6bSBaptiste Daroussin if (print_escape(h, *p++)) 51461d06d6bSBaptiste Daroussin continue; 51561d06d6bSBaptiste Daroussin 51661d06d6bSBaptiste Daroussin esc = mandoc_escape(&p, &seq, &len); 51761d06d6bSBaptiste Daroussin switch (esc) { 51861d06d6bSBaptiste Daroussin case ESCAPE_FONT: 51961d06d6bSBaptiste Daroussin case ESCAPE_FONTPREV: 52061d06d6bSBaptiste Daroussin case ESCAPE_FONTBOLD: 52161d06d6bSBaptiste Daroussin case ESCAPE_FONTITALIC: 52261d06d6bSBaptiste Daroussin case ESCAPE_FONTBI: 52361d06d6bSBaptiste Daroussin case ESCAPE_FONTROMAN: 5246d38604fSBaptiste Daroussin case ESCAPE_FONTCR: 5256d38604fSBaptiste Daroussin case ESCAPE_FONTCB: 5266d38604fSBaptiste Daroussin case ESCAPE_FONTCI: 5277295610fSBaptiste Daroussin if (0 == norecurse) { 5287295610fSBaptiste Daroussin h->flags |= HTML_NOSPACE; 52945a5aec3SBaptiste Daroussin if (html_setfont(h, esc)) 53045a5aec3SBaptiste Daroussin print_metaf(h); 5317295610fSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 5327295610fSBaptiste Daroussin } 53361d06d6bSBaptiste Daroussin continue; 53461d06d6bSBaptiste Daroussin case ESCAPE_SKIPCHAR: 53561d06d6bSBaptiste Daroussin h->flags |= HTML_SKIPCHAR; 53661d06d6bSBaptiste Daroussin continue; 5377295610fSBaptiste Daroussin case ESCAPE_ERROR: 5387295610fSBaptiste Daroussin continue; 53961d06d6bSBaptiste Daroussin default: 54061d06d6bSBaptiste Daroussin break; 54161d06d6bSBaptiste Daroussin } 54261d06d6bSBaptiste Daroussin 54361d06d6bSBaptiste Daroussin if (h->flags & HTML_SKIPCHAR) { 54461d06d6bSBaptiste Daroussin h->flags &= ~HTML_SKIPCHAR; 54561d06d6bSBaptiste Daroussin continue; 54661d06d6bSBaptiste Daroussin } 54761d06d6bSBaptiste Daroussin 54861d06d6bSBaptiste Daroussin switch (esc) { 54961d06d6bSBaptiste Daroussin case ESCAPE_UNICODE: 55061d06d6bSBaptiste Daroussin /* Skip past "u" header. */ 55161d06d6bSBaptiste Daroussin c = mchars_num2uc(seq + 1, len - 1); 55261d06d6bSBaptiste Daroussin break; 55361d06d6bSBaptiste Daroussin case ESCAPE_NUMBERED: 55461d06d6bSBaptiste Daroussin c = mchars_num2char(seq, len); 55561d06d6bSBaptiste Daroussin if (c < 0) 55661d06d6bSBaptiste Daroussin continue; 55761d06d6bSBaptiste Daroussin break; 55861d06d6bSBaptiste Daroussin case ESCAPE_SPECIAL: 55961d06d6bSBaptiste Daroussin c = mchars_spec2cp(seq, len); 56061d06d6bSBaptiste Daroussin if (c <= 0) 56161d06d6bSBaptiste Daroussin continue; 56261d06d6bSBaptiste Daroussin break; 5637295610fSBaptiste Daroussin case ESCAPE_UNDEF: 5647295610fSBaptiste Daroussin c = *seq; 5657295610fSBaptiste Daroussin break; 5667295610fSBaptiste Daroussin case ESCAPE_DEVICE: 5677295610fSBaptiste Daroussin print_word(h, "html"); 5687295610fSBaptiste Daroussin continue; 56961d06d6bSBaptiste Daroussin case ESCAPE_BREAK: 57061d06d6bSBaptiste Daroussin breakline = 1; 57161d06d6bSBaptiste Daroussin continue; 57261d06d6bSBaptiste Daroussin case ESCAPE_NOSPACE: 57361d06d6bSBaptiste Daroussin if ('\0' == *p) 57461d06d6bSBaptiste Daroussin nospace = 1; 57561d06d6bSBaptiste Daroussin continue; 57661d06d6bSBaptiste Daroussin case ESCAPE_OVERSTRIKE: 57761d06d6bSBaptiste Daroussin if (len == 0) 57861d06d6bSBaptiste Daroussin continue; 57961d06d6bSBaptiste Daroussin c = seq[len - 1]; 58061d06d6bSBaptiste Daroussin break; 58161d06d6bSBaptiste Daroussin default: 58261d06d6bSBaptiste Daroussin continue; 58361d06d6bSBaptiste Daroussin } 58461d06d6bSBaptiste Daroussin if ((c < 0x20 && c != 0x09) || 58561d06d6bSBaptiste Daroussin (c > 0x7E && c < 0xA0)) 58661d06d6bSBaptiste Daroussin c = 0xFFFD; 58761d06d6bSBaptiste Daroussin if (c > 0x7E) { 58861d06d6bSBaptiste Daroussin (void)snprintf(numbuf, sizeof(numbuf), "&#x%.4X;", c); 58961d06d6bSBaptiste Daroussin print_word(h, numbuf); 59061d06d6bSBaptiste Daroussin } else if (print_escape(h, c) == 0) 59161d06d6bSBaptiste Daroussin print_byte(h, c); 59261d06d6bSBaptiste Daroussin } 59361d06d6bSBaptiste Daroussin 59461d06d6bSBaptiste Daroussin return nospace; 59561d06d6bSBaptiste Daroussin } 59661d06d6bSBaptiste Daroussin 59761d06d6bSBaptiste Daroussin static void 59861d06d6bSBaptiste Daroussin print_href(struct html *h, const char *name, const char *sec, int man) 59961d06d6bSBaptiste Daroussin { 6007295610fSBaptiste Daroussin struct stat sb; 60161d06d6bSBaptiste Daroussin const char *p, *pp; 6027295610fSBaptiste Daroussin char *filename; 60361d06d6bSBaptiste Daroussin 6047295610fSBaptiste Daroussin if (man) { 6057295610fSBaptiste Daroussin pp = h->base_man1; 6067295610fSBaptiste Daroussin if (h->base_man2 != NULL) { 6077295610fSBaptiste Daroussin mandoc_asprintf(&filename, "%s.%s", name, sec); 6087295610fSBaptiste Daroussin if (stat(filename, &sb) == -1) 6097295610fSBaptiste Daroussin pp = h->base_man2; 6107295610fSBaptiste Daroussin free(filename); 6117295610fSBaptiste Daroussin } 6127295610fSBaptiste Daroussin } else 6137295610fSBaptiste Daroussin pp = h->base_includes; 6147295610fSBaptiste Daroussin 61561d06d6bSBaptiste Daroussin while ((p = strchr(pp, '%')) != NULL) { 61661d06d6bSBaptiste Daroussin print_encode(h, pp, p, 1); 61761d06d6bSBaptiste Daroussin if (man && p[1] == 'S') { 61861d06d6bSBaptiste Daroussin if (sec == NULL) 61961d06d6bSBaptiste Daroussin print_byte(h, '1'); 62061d06d6bSBaptiste Daroussin else 62161d06d6bSBaptiste Daroussin print_encode(h, sec, NULL, 1); 62261d06d6bSBaptiste Daroussin } else if ((man && p[1] == 'N') || 62361d06d6bSBaptiste Daroussin (man == 0 && p[1] == 'I')) 62461d06d6bSBaptiste Daroussin print_encode(h, name, NULL, 1); 62561d06d6bSBaptiste Daroussin else 62661d06d6bSBaptiste Daroussin print_encode(h, p, p + 2, 1); 62761d06d6bSBaptiste Daroussin pp = p + 2; 62861d06d6bSBaptiste Daroussin } 62961d06d6bSBaptiste Daroussin if (*pp != '\0') 63061d06d6bSBaptiste Daroussin print_encode(h, pp, NULL, 1); 63161d06d6bSBaptiste Daroussin } 63261d06d6bSBaptiste Daroussin 63361d06d6bSBaptiste Daroussin struct tag * 63461d06d6bSBaptiste Daroussin print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) 63561d06d6bSBaptiste Daroussin { 63661d06d6bSBaptiste Daroussin va_list ap; 63761d06d6bSBaptiste Daroussin struct tag *t; 63861d06d6bSBaptiste Daroussin const char *attr; 63961d06d6bSBaptiste Daroussin char *arg1, *arg2; 6407295610fSBaptiste Daroussin int style_written, tflags; 64161d06d6bSBaptiste Daroussin 64261d06d6bSBaptiste Daroussin tflags = htmltags[tag].flags; 64361d06d6bSBaptiste Daroussin 6446d38604fSBaptiste Daroussin /* Flow content is not allowed in phrasing context. */ 6456d38604fSBaptiste Daroussin 6466d38604fSBaptiste Daroussin if ((tflags & HTML_INPHRASE) == 0) { 6476d38604fSBaptiste Daroussin for (t = h->tag; t != NULL; t = t->next) { 6486d38604fSBaptiste Daroussin if (t->closed) 6496d38604fSBaptiste Daroussin continue; 6506d38604fSBaptiste Daroussin assert((htmltags[t->tag].flags & HTML_TOPHRASE) == 0); 6516d38604fSBaptiste Daroussin break; 6526d38604fSBaptiste Daroussin } 6536d38604fSBaptiste Daroussin 6546d38604fSBaptiste Daroussin /* 6556d38604fSBaptiste Daroussin * Always wrap phrasing elements in a paragraph 6566d38604fSBaptiste Daroussin * unless already contained in some flow container; 6576d38604fSBaptiste Daroussin * never put them directly into a section. 6586d38604fSBaptiste Daroussin */ 6596d38604fSBaptiste Daroussin 6606d38604fSBaptiste Daroussin } else if (tflags & HTML_TOPHRASE && h->tag->tag == TAG_SECTION) 6616d38604fSBaptiste Daroussin print_otag(h, TAG_P, "c", "Pp"); 6626d38604fSBaptiste Daroussin 66361d06d6bSBaptiste Daroussin /* Push this tag onto the stack of open scopes. */ 66461d06d6bSBaptiste Daroussin 66561d06d6bSBaptiste Daroussin if ((tflags & HTML_NOSTACK) == 0) { 66661d06d6bSBaptiste Daroussin t = mandoc_malloc(sizeof(struct tag)); 66761d06d6bSBaptiste Daroussin t->tag = tag; 66861d06d6bSBaptiste Daroussin t->next = h->tag; 6697295610fSBaptiste Daroussin t->refcnt = 0; 6707295610fSBaptiste Daroussin t->closed = 0; 67161d06d6bSBaptiste Daroussin h->tag = t; 67261d06d6bSBaptiste Daroussin } else 67361d06d6bSBaptiste Daroussin t = NULL; 67461d06d6bSBaptiste Daroussin 67561d06d6bSBaptiste Daroussin if (tflags & HTML_NLBEFORE) 67661d06d6bSBaptiste Daroussin print_endline(h); 67761d06d6bSBaptiste Daroussin if (h->col == 0) 67861d06d6bSBaptiste Daroussin print_indent(h); 67961d06d6bSBaptiste Daroussin else if ((h->flags & HTML_NOSPACE) == 0) { 68061d06d6bSBaptiste Daroussin if (h->flags & HTML_KEEP) 68161d06d6bSBaptiste Daroussin print_word(h, " "); 68261d06d6bSBaptiste Daroussin else { 68361d06d6bSBaptiste Daroussin if (h->flags & HTML_PREKEEP) 68461d06d6bSBaptiste Daroussin h->flags |= HTML_KEEP; 68561d06d6bSBaptiste Daroussin print_endword(h); 68661d06d6bSBaptiste Daroussin } 68761d06d6bSBaptiste Daroussin } 68861d06d6bSBaptiste Daroussin 68961d06d6bSBaptiste Daroussin if ( ! (h->flags & HTML_NONOSPACE)) 69061d06d6bSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 69161d06d6bSBaptiste Daroussin else 69261d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE; 69361d06d6bSBaptiste Daroussin 69461d06d6bSBaptiste Daroussin /* Print out the tag name and attributes. */ 69561d06d6bSBaptiste Daroussin 69661d06d6bSBaptiste Daroussin print_byte(h, '<'); 69761d06d6bSBaptiste Daroussin print_word(h, htmltags[tag].name); 69861d06d6bSBaptiste Daroussin 69961d06d6bSBaptiste Daroussin va_start(ap, fmt); 70061d06d6bSBaptiste Daroussin 7017295610fSBaptiste Daroussin while (*fmt != '\0' && *fmt != 's') { 70261d06d6bSBaptiste Daroussin 70361d06d6bSBaptiste Daroussin /* Parse attributes and arguments. */ 70461d06d6bSBaptiste Daroussin 70561d06d6bSBaptiste Daroussin arg1 = va_arg(ap, char *); 70661d06d6bSBaptiste Daroussin arg2 = NULL; 70761d06d6bSBaptiste Daroussin switch (*fmt++) { 70861d06d6bSBaptiste Daroussin case 'c': 70961d06d6bSBaptiste Daroussin attr = "class"; 71061d06d6bSBaptiste Daroussin break; 71161d06d6bSBaptiste Daroussin case 'h': 71261d06d6bSBaptiste Daroussin attr = "href"; 71361d06d6bSBaptiste Daroussin break; 71461d06d6bSBaptiste Daroussin case 'i': 71561d06d6bSBaptiste Daroussin attr = "id"; 71661d06d6bSBaptiste Daroussin break; 717*c1c95addSBrooks Davis case 'r': 718*c1c95addSBrooks Davis attr = "role"; 719*c1c95addSBrooks Davis break; 72061d06d6bSBaptiste Daroussin case '?': 72161d06d6bSBaptiste Daroussin attr = arg1; 72261d06d6bSBaptiste Daroussin arg1 = va_arg(ap, char *); 72361d06d6bSBaptiste Daroussin break; 72461d06d6bSBaptiste Daroussin default: 72561d06d6bSBaptiste Daroussin abort(); 72661d06d6bSBaptiste Daroussin } 72761d06d6bSBaptiste Daroussin if (*fmt == 'M') 72861d06d6bSBaptiste Daroussin arg2 = va_arg(ap, char *); 72961d06d6bSBaptiste Daroussin if (arg1 == NULL) 73061d06d6bSBaptiste Daroussin continue; 73161d06d6bSBaptiste Daroussin 73261d06d6bSBaptiste Daroussin /* Print the attributes. */ 73361d06d6bSBaptiste Daroussin 73461d06d6bSBaptiste Daroussin print_byte(h, ' '); 73561d06d6bSBaptiste Daroussin print_word(h, attr); 73661d06d6bSBaptiste Daroussin print_byte(h, '='); 73761d06d6bSBaptiste Daroussin print_byte(h, '"'); 73861d06d6bSBaptiste Daroussin switch (*fmt) { 73961d06d6bSBaptiste Daroussin case 'I': 74061d06d6bSBaptiste Daroussin print_href(h, arg1, NULL, 0); 74161d06d6bSBaptiste Daroussin fmt++; 74261d06d6bSBaptiste Daroussin break; 74361d06d6bSBaptiste Daroussin case 'M': 74461d06d6bSBaptiste Daroussin print_href(h, arg1, arg2, 1); 74561d06d6bSBaptiste Daroussin fmt++; 74661d06d6bSBaptiste Daroussin break; 74761d06d6bSBaptiste Daroussin case 'R': 74861d06d6bSBaptiste Daroussin print_byte(h, '#'); 74961d06d6bSBaptiste Daroussin print_encode(h, arg1, NULL, 1); 75061d06d6bSBaptiste Daroussin fmt++; 75161d06d6bSBaptiste Daroussin break; 75261d06d6bSBaptiste Daroussin default: 75361d06d6bSBaptiste Daroussin print_encode(h, arg1, NULL, 1); 7547295610fSBaptiste Daroussin break; 7557295610fSBaptiste Daroussin } 7567295610fSBaptiste Daroussin print_byte(h, '"'); 7577295610fSBaptiste Daroussin } 7587295610fSBaptiste Daroussin 7597295610fSBaptiste Daroussin style_written = 0; 7607295610fSBaptiste Daroussin while (*fmt++ == 's') { 7617295610fSBaptiste Daroussin arg1 = va_arg(ap, char *); 7627295610fSBaptiste Daroussin arg2 = va_arg(ap, char *); 7637295610fSBaptiste Daroussin if (arg2 == NULL) 7647295610fSBaptiste Daroussin continue; 7657295610fSBaptiste Daroussin print_byte(h, ' '); 7667295610fSBaptiste Daroussin if (style_written == 0) { 7677295610fSBaptiste Daroussin print_word(h, "style=\""); 7687295610fSBaptiste Daroussin style_written = 1; 7697295610fSBaptiste Daroussin } 77061d06d6bSBaptiste Daroussin print_word(h, arg1); 77161d06d6bSBaptiste Daroussin print_byte(h, ':'); 77261d06d6bSBaptiste Daroussin print_byte(h, ' '); 77361d06d6bSBaptiste Daroussin print_word(h, arg2); 77461d06d6bSBaptiste Daroussin print_byte(h, ';'); 77561d06d6bSBaptiste Daroussin } 7767295610fSBaptiste Daroussin if (style_written) 77761d06d6bSBaptiste Daroussin print_byte(h, '"'); 7787295610fSBaptiste Daroussin 77961d06d6bSBaptiste Daroussin va_end(ap); 78061d06d6bSBaptiste Daroussin 78161d06d6bSBaptiste Daroussin /* Accommodate for "well-formed" singleton escaping. */ 78261d06d6bSBaptiste Daroussin 7836d38604fSBaptiste Daroussin if (htmltags[tag].flags & HTML_NOSTACK) 78461d06d6bSBaptiste Daroussin print_byte(h, '/'); 78561d06d6bSBaptiste Daroussin 78661d06d6bSBaptiste Daroussin print_byte(h, '>'); 78761d06d6bSBaptiste Daroussin 78861d06d6bSBaptiste Daroussin if (tflags & HTML_NLBEGIN) 78961d06d6bSBaptiste Daroussin print_endline(h); 79061d06d6bSBaptiste Daroussin else 79161d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE; 79261d06d6bSBaptiste Daroussin 79361d06d6bSBaptiste Daroussin if (tflags & HTML_INDENT) 79461d06d6bSBaptiste Daroussin h->indent++; 79561d06d6bSBaptiste Daroussin if (tflags & HTML_NOINDENT) 79661d06d6bSBaptiste Daroussin h->noindent++; 79761d06d6bSBaptiste Daroussin 79861d06d6bSBaptiste Daroussin return t; 79961d06d6bSBaptiste Daroussin } 80061d06d6bSBaptiste Daroussin 8016d38604fSBaptiste Daroussin /* 8026d38604fSBaptiste Daroussin * Print an element with an optional "id=" attribute. 8036d38604fSBaptiste Daroussin * If the element has phrasing content and an "id=" attribute, 8046d38604fSBaptiste Daroussin * also add a permalink: outside if it can be in phrasing context, 8056d38604fSBaptiste Daroussin * inside otherwise. 8066d38604fSBaptiste Daroussin */ 8076d38604fSBaptiste Daroussin struct tag * 8086d38604fSBaptiste Daroussin print_otag_id(struct html *h, enum htmltag elemtype, const char *cattr, 8096d38604fSBaptiste Daroussin struct roff_node *n) 8106d38604fSBaptiste Daroussin { 8116d38604fSBaptiste Daroussin struct roff_node *nch; 8126d38604fSBaptiste Daroussin struct tag *ret, *t; 8136d38604fSBaptiste Daroussin char *id, *href; 8146d38604fSBaptiste Daroussin 8156d38604fSBaptiste Daroussin ret = NULL; 8166d38604fSBaptiste Daroussin id = href = NULL; 8176d38604fSBaptiste Daroussin if (n->flags & NODE_ID) 8186d38604fSBaptiste Daroussin id = html_make_id(n, 1); 8196d38604fSBaptiste Daroussin if (n->flags & NODE_HREF) 8206d38604fSBaptiste Daroussin href = id == NULL ? html_make_id(n, 2) : id; 8216d38604fSBaptiste Daroussin if (href != NULL && htmltags[elemtype].flags & HTML_INPHRASE) 8226d38604fSBaptiste Daroussin ret = print_otag(h, TAG_A, "chR", "permalink", href); 8236d38604fSBaptiste Daroussin t = print_otag(h, elemtype, "ci", cattr, id); 8246d38604fSBaptiste Daroussin if (ret == NULL) { 8256d38604fSBaptiste Daroussin ret = t; 8266d38604fSBaptiste Daroussin if (href != NULL && (nch = n->child) != NULL) { 8276d38604fSBaptiste Daroussin /* man(7) is safe, it tags phrasing content only. */ 8286d38604fSBaptiste Daroussin if (n->tok > MDOC_MAX || 8296d38604fSBaptiste Daroussin htmltags[elemtype].flags & HTML_TOPHRASE) 8306d38604fSBaptiste Daroussin nch = NULL; 8316d38604fSBaptiste Daroussin else /* For mdoc(7), beware of nested blocks. */ 8326d38604fSBaptiste Daroussin while (nch != NULL && nch->type == ROFFT_TEXT) 8336d38604fSBaptiste Daroussin nch = nch->next; 8346d38604fSBaptiste Daroussin if (nch == NULL) 8356d38604fSBaptiste Daroussin print_otag(h, TAG_A, "chR", "permalink", href); 8366d38604fSBaptiste Daroussin } 8376d38604fSBaptiste Daroussin } 8386d38604fSBaptiste Daroussin free(id); 8396d38604fSBaptiste Daroussin if (id == NULL) 8406d38604fSBaptiste Daroussin free(href); 8416d38604fSBaptiste Daroussin return ret; 8426d38604fSBaptiste Daroussin } 8436d38604fSBaptiste Daroussin 84461d06d6bSBaptiste Daroussin static void 84561d06d6bSBaptiste Daroussin print_ctag(struct html *h, struct tag *tag) 84661d06d6bSBaptiste Daroussin { 84761d06d6bSBaptiste Daroussin int tflags; 84861d06d6bSBaptiste Daroussin 8497295610fSBaptiste Daroussin if (tag->closed == 0) { 8507295610fSBaptiste Daroussin tag->closed = 1; 85161d06d6bSBaptiste Daroussin if (tag == h->metaf) 85261d06d6bSBaptiste Daroussin h->metaf = NULL; 85361d06d6bSBaptiste Daroussin if (tag == h->tblt) 85461d06d6bSBaptiste Daroussin h->tblt = NULL; 85561d06d6bSBaptiste Daroussin 85661d06d6bSBaptiste Daroussin tflags = htmltags[tag->tag].flags; 85761d06d6bSBaptiste Daroussin if (tflags & HTML_INDENT) 85861d06d6bSBaptiste Daroussin h->indent--; 85961d06d6bSBaptiste Daroussin if (tflags & HTML_NOINDENT) 86061d06d6bSBaptiste Daroussin h->noindent--; 86161d06d6bSBaptiste Daroussin if (tflags & HTML_NLEND) 86261d06d6bSBaptiste Daroussin print_endline(h); 86361d06d6bSBaptiste Daroussin print_indent(h); 86461d06d6bSBaptiste Daroussin print_byte(h, '<'); 86561d06d6bSBaptiste Daroussin print_byte(h, '/'); 86661d06d6bSBaptiste Daroussin print_word(h, htmltags[tag->tag].name); 86761d06d6bSBaptiste Daroussin print_byte(h, '>'); 86861d06d6bSBaptiste Daroussin if (tflags & HTML_NLAFTER) 86961d06d6bSBaptiste Daroussin print_endline(h); 8707295610fSBaptiste Daroussin } 8717295610fSBaptiste Daroussin if (tag->refcnt == 0) { 87261d06d6bSBaptiste Daroussin h->tag = tag->next; 87361d06d6bSBaptiste Daroussin free(tag); 87461d06d6bSBaptiste Daroussin } 8757295610fSBaptiste Daroussin } 87661d06d6bSBaptiste Daroussin 87761d06d6bSBaptiste Daroussin void 87861d06d6bSBaptiste Daroussin print_gen_decls(struct html *h) 87961d06d6bSBaptiste Daroussin { 88061d06d6bSBaptiste Daroussin print_word(h, "<!DOCTYPE html>"); 88161d06d6bSBaptiste Daroussin print_endline(h); 88261d06d6bSBaptiste Daroussin } 88361d06d6bSBaptiste Daroussin 88461d06d6bSBaptiste Daroussin void 88561d06d6bSBaptiste Daroussin print_gen_comment(struct html *h, struct roff_node *n) 88661d06d6bSBaptiste Daroussin { 88761d06d6bSBaptiste Daroussin int wantblank; 88861d06d6bSBaptiste Daroussin 88961d06d6bSBaptiste Daroussin print_word(h, "<!-- This is an automatically generated file." 89061d06d6bSBaptiste Daroussin " Do not edit."); 89161d06d6bSBaptiste Daroussin h->indent = 1; 89261d06d6bSBaptiste Daroussin wantblank = 0; 89361d06d6bSBaptiste Daroussin while (n != NULL && n->type == ROFFT_COMMENT) { 89461d06d6bSBaptiste Daroussin if (strstr(n->string, "-->") == NULL && 89561d06d6bSBaptiste Daroussin (wantblank || *n->string != '\0')) { 89661d06d6bSBaptiste Daroussin print_endline(h); 89761d06d6bSBaptiste Daroussin print_indent(h); 89861d06d6bSBaptiste Daroussin print_word(h, n->string); 89961d06d6bSBaptiste Daroussin wantblank = *n->string != '\0'; 90061d06d6bSBaptiste Daroussin } 90161d06d6bSBaptiste Daroussin n = n->next; 90261d06d6bSBaptiste Daroussin } 90361d06d6bSBaptiste Daroussin if (wantblank) 90461d06d6bSBaptiste Daroussin print_endline(h); 90561d06d6bSBaptiste Daroussin print_word(h, " -->"); 90661d06d6bSBaptiste Daroussin print_endline(h); 90761d06d6bSBaptiste Daroussin h->indent = 0; 90861d06d6bSBaptiste Daroussin } 90961d06d6bSBaptiste Daroussin 91061d06d6bSBaptiste Daroussin void 91161d06d6bSBaptiste Daroussin print_text(struct html *h, const char *word) 91261d06d6bSBaptiste Daroussin { 9136d38604fSBaptiste Daroussin print_tagged_text(h, word, NULL); 9146d38604fSBaptiste Daroussin } 9156d38604fSBaptiste Daroussin 9166d38604fSBaptiste Daroussin void 9176d38604fSBaptiste Daroussin print_tagged_text(struct html *h, const char *word, struct roff_node *n) 9186d38604fSBaptiste Daroussin { 9196d38604fSBaptiste Daroussin struct tag *t; 9206d38604fSBaptiste Daroussin char *href; 9216d38604fSBaptiste Daroussin 9226d38604fSBaptiste Daroussin /* 9236d38604fSBaptiste Daroussin * Always wrap text in a paragraph unless already contained in 9246d38604fSBaptiste Daroussin * some flow container; never put it directly into a section. 9256d38604fSBaptiste Daroussin */ 9266d38604fSBaptiste Daroussin 9276d38604fSBaptiste Daroussin if (h->tag->tag == TAG_SECTION) 9286d38604fSBaptiste Daroussin print_otag(h, TAG_P, "c", "Pp"); 9296d38604fSBaptiste Daroussin 9306d38604fSBaptiste Daroussin /* Output whitespace before this text? */ 9316d38604fSBaptiste Daroussin 93261d06d6bSBaptiste Daroussin if (h->col && (h->flags & HTML_NOSPACE) == 0) { 93361d06d6bSBaptiste Daroussin if ( ! (HTML_KEEP & h->flags)) { 93461d06d6bSBaptiste Daroussin if (HTML_PREKEEP & h->flags) 93561d06d6bSBaptiste Daroussin h->flags |= HTML_KEEP; 93661d06d6bSBaptiste Daroussin print_endword(h); 93761d06d6bSBaptiste Daroussin } else 93861d06d6bSBaptiste Daroussin print_word(h, " "); 93961d06d6bSBaptiste Daroussin } 94061d06d6bSBaptiste Daroussin 9416d38604fSBaptiste Daroussin /* 9426d38604fSBaptiste Daroussin * Optionally switch fonts, optionally write a permalink, then 9436d38604fSBaptiste Daroussin * print the text, optionally surrounded by HTML whitespace. 9446d38604fSBaptiste Daroussin */ 9456d38604fSBaptiste Daroussin 94645a5aec3SBaptiste Daroussin assert(h->metaf == NULL); 94745a5aec3SBaptiste Daroussin print_metaf(h); 94861d06d6bSBaptiste Daroussin print_indent(h); 9496d38604fSBaptiste Daroussin 9506d38604fSBaptiste Daroussin if (n != NULL && (href = html_make_id(n, 2)) != NULL) { 9516d38604fSBaptiste Daroussin t = print_otag(h, TAG_A, "chR", "permalink", href); 9526d38604fSBaptiste Daroussin free(href); 9536d38604fSBaptiste Daroussin } else 9546d38604fSBaptiste Daroussin t = NULL; 9556d38604fSBaptiste Daroussin 95661d06d6bSBaptiste Daroussin if ( ! print_encode(h, word, NULL, 0)) { 95761d06d6bSBaptiste Daroussin if ( ! (h->flags & HTML_NONOSPACE)) 95861d06d6bSBaptiste Daroussin h->flags &= ~HTML_NOSPACE; 95961d06d6bSBaptiste Daroussin h->flags &= ~HTML_NONEWLINE; 96061d06d6bSBaptiste Daroussin } else 96161d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE | HTML_NONEWLINE; 96261d06d6bSBaptiste Daroussin 96345a5aec3SBaptiste Daroussin if (h->metaf != NULL) { 96461d06d6bSBaptiste Daroussin print_tagq(h, h->metaf); 96561d06d6bSBaptiste Daroussin h->metaf = NULL; 9666d38604fSBaptiste Daroussin } else if (t != NULL) 9676d38604fSBaptiste Daroussin print_tagq(h, t); 96861d06d6bSBaptiste Daroussin 96961d06d6bSBaptiste Daroussin h->flags &= ~HTML_IGNDELIM; 97061d06d6bSBaptiste Daroussin } 97161d06d6bSBaptiste Daroussin 97261d06d6bSBaptiste Daroussin void 97361d06d6bSBaptiste Daroussin print_tagq(struct html *h, const struct tag *until) 97461d06d6bSBaptiste Daroussin { 9757295610fSBaptiste Daroussin struct tag *this, *next; 97661d06d6bSBaptiste Daroussin 9777295610fSBaptiste Daroussin for (this = h->tag; this != NULL; this = next) { 9787295610fSBaptiste Daroussin next = this == until ? NULL : this->next; 9797295610fSBaptiste Daroussin print_ctag(h, this); 98061d06d6bSBaptiste Daroussin } 98161d06d6bSBaptiste Daroussin } 98261d06d6bSBaptiste Daroussin 9837295610fSBaptiste Daroussin /* 9847295610fSBaptiste Daroussin * Close out all open elements up to but excluding suntil. 9857295610fSBaptiste Daroussin * Note that a paragraph just inside stays open together with it 9867295610fSBaptiste Daroussin * because paragraphs include subsequent phrasing content. 9877295610fSBaptiste Daroussin */ 98861d06d6bSBaptiste Daroussin void 98961d06d6bSBaptiste Daroussin print_stagq(struct html *h, const struct tag *suntil) 99061d06d6bSBaptiste Daroussin { 9917295610fSBaptiste Daroussin struct tag *this, *next; 99261d06d6bSBaptiste Daroussin 9937295610fSBaptiste Daroussin for (this = h->tag; this != NULL; this = next) { 9947295610fSBaptiste Daroussin next = this->next; 9957295610fSBaptiste Daroussin if (this == suntil || (next == suntil && 9967295610fSBaptiste Daroussin (this->tag == TAG_P || this->tag == TAG_PRE))) 9977295610fSBaptiste Daroussin break; 9987295610fSBaptiste Daroussin print_ctag(h, this); 99961d06d6bSBaptiste Daroussin } 100061d06d6bSBaptiste Daroussin } 100161d06d6bSBaptiste Daroussin 100261d06d6bSBaptiste Daroussin 100361d06d6bSBaptiste Daroussin /*********************************************************************** 100461d06d6bSBaptiste Daroussin * Low level output functions. 100561d06d6bSBaptiste Daroussin * They implement line breaking using a short static buffer. 100661d06d6bSBaptiste Daroussin ***********************************************************************/ 100761d06d6bSBaptiste Daroussin 100861d06d6bSBaptiste Daroussin /* 100961d06d6bSBaptiste Daroussin * Buffer one HTML output byte. 101061d06d6bSBaptiste Daroussin * If the buffer is full, flush and deactivate it and start a new line. 101161d06d6bSBaptiste Daroussin * If the buffer is inactive, print directly. 101261d06d6bSBaptiste Daroussin */ 101361d06d6bSBaptiste Daroussin static void 101461d06d6bSBaptiste Daroussin print_byte(struct html *h, char c) 101561d06d6bSBaptiste Daroussin { 101661d06d6bSBaptiste Daroussin if ((h->flags & HTML_BUFFER) == 0) { 101761d06d6bSBaptiste Daroussin putchar(c); 101861d06d6bSBaptiste Daroussin h->col++; 101961d06d6bSBaptiste Daroussin return; 102061d06d6bSBaptiste Daroussin } 102161d06d6bSBaptiste Daroussin 102261d06d6bSBaptiste Daroussin if (h->col + h->bufcol < sizeof(h->buf)) { 102361d06d6bSBaptiste Daroussin h->buf[h->bufcol++] = c; 102461d06d6bSBaptiste Daroussin return; 102561d06d6bSBaptiste Daroussin } 102661d06d6bSBaptiste Daroussin 102761d06d6bSBaptiste Daroussin putchar('\n'); 102861d06d6bSBaptiste Daroussin h->col = 0; 102961d06d6bSBaptiste Daroussin print_indent(h); 103061d06d6bSBaptiste Daroussin putchar(' '); 103161d06d6bSBaptiste Daroussin putchar(' '); 103261d06d6bSBaptiste Daroussin fwrite(h->buf, h->bufcol, 1, stdout); 103361d06d6bSBaptiste Daroussin putchar(c); 103461d06d6bSBaptiste Daroussin h->col = (h->indent + 1) * 2 + h->bufcol + 1; 103561d06d6bSBaptiste Daroussin h->bufcol = 0; 103661d06d6bSBaptiste Daroussin h->flags &= ~HTML_BUFFER; 103761d06d6bSBaptiste Daroussin } 103861d06d6bSBaptiste Daroussin 103961d06d6bSBaptiste Daroussin /* 104061d06d6bSBaptiste Daroussin * If something was printed on the current output line, end it. 104161d06d6bSBaptiste Daroussin * Not to be called right after print_indent(). 104261d06d6bSBaptiste Daroussin */ 104361d06d6bSBaptiste Daroussin void 104461d06d6bSBaptiste Daroussin print_endline(struct html *h) 104561d06d6bSBaptiste Daroussin { 104661d06d6bSBaptiste Daroussin if (h->col == 0) 104761d06d6bSBaptiste Daroussin return; 104861d06d6bSBaptiste Daroussin 104961d06d6bSBaptiste Daroussin if (h->bufcol) { 105061d06d6bSBaptiste Daroussin putchar(' '); 105161d06d6bSBaptiste Daroussin fwrite(h->buf, h->bufcol, 1, stdout); 105261d06d6bSBaptiste Daroussin h->bufcol = 0; 105361d06d6bSBaptiste Daroussin } 105461d06d6bSBaptiste Daroussin putchar('\n'); 105561d06d6bSBaptiste Daroussin h->col = 0; 105661d06d6bSBaptiste Daroussin h->flags |= HTML_NOSPACE; 105761d06d6bSBaptiste Daroussin h->flags &= ~HTML_BUFFER; 105861d06d6bSBaptiste Daroussin } 105961d06d6bSBaptiste Daroussin 106061d06d6bSBaptiste Daroussin /* 106161d06d6bSBaptiste Daroussin * Flush the HTML output buffer. 106261d06d6bSBaptiste Daroussin * If it is inactive, activate it. 106361d06d6bSBaptiste Daroussin */ 106461d06d6bSBaptiste Daroussin static void 106561d06d6bSBaptiste Daroussin print_endword(struct html *h) 106661d06d6bSBaptiste Daroussin { 106761d06d6bSBaptiste Daroussin if (h->noindent) { 106861d06d6bSBaptiste Daroussin print_byte(h, ' '); 106961d06d6bSBaptiste Daroussin return; 107061d06d6bSBaptiste Daroussin } 107161d06d6bSBaptiste Daroussin 107261d06d6bSBaptiste Daroussin if ((h->flags & HTML_BUFFER) == 0) { 107361d06d6bSBaptiste Daroussin h->col++; 107461d06d6bSBaptiste Daroussin h->flags |= HTML_BUFFER; 107561d06d6bSBaptiste Daroussin } else if (h->bufcol) { 107661d06d6bSBaptiste Daroussin putchar(' '); 107761d06d6bSBaptiste Daroussin fwrite(h->buf, h->bufcol, 1, stdout); 107861d06d6bSBaptiste Daroussin h->col += h->bufcol + 1; 107961d06d6bSBaptiste Daroussin } 108061d06d6bSBaptiste Daroussin h->bufcol = 0; 108161d06d6bSBaptiste Daroussin } 108261d06d6bSBaptiste Daroussin 108361d06d6bSBaptiste Daroussin /* 108461d06d6bSBaptiste Daroussin * If at the beginning of a new output line, 108561d06d6bSBaptiste Daroussin * perform indentation and mark the line as containing output. 108661d06d6bSBaptiste Daroussin * Make sure to really produce some output right afterwards, 108761d06d6bSBaptiste Daroussin * but do not use print_otag() for producing it. 108861d06d6bSBaptiste Daroussin */ 108961d06d6bSBaptiste Daroussin static void 109061d06d6bSBaptiste Daroussin print_indent(struct html *h) 109161d06d6bSBaptiste Daroussin { 109261d06d6bSBaptiste Daroussin size_t i; 109361d06d6bSBaptiste Daroussin 10946d38604fSBaptiste Daroussin if (h->col || h->noindent) 109561d06d6bSBaptiste Daroussin return; 109661d06d6bSBaptiste Daroussin 109761d06d6bSBaptiste Daroussin h->col = h->indent * 2; 109861d06d6bSBaptiste Daroussin for (i = 0; i < h->col; i++) 109961d06d6bSBaptiste Daroussin putchar(' '); 110061d06d6bSBaptiste Daroussin } 110161d06d6bSBaptiste Daroussin 110261d06d6bSBaptiste Daroussin /* 110361d06d6bSBaptiste Daroussin * Print or buffer some characters 110461d06d6bSBaptiste Daroussin * depending on the current HTML output buffer state. 110561d06d6bSBaptiste Daroussin */ 110661d06d6bSBaptiste Daroussin static void 110761d06d6bSBaptiste Daroussin print_word(struct html *h, const char *cp) 110861d06d6bSBaptiste Daroussin { 110961d06d6bSBaptiste Daroussin while (*cp != '\0') 111061d06d6bSBaptiste Daroussin print_byte(h, *cp++); 111161d06d6bSBaptiste Daroussin } 1112