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 *
html_alloc(const struct manoutput * outopts)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
html_reset_internal(struct html * h)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
html_reset(void * p)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
html_free(void * p)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
print_gen_head(struct html * h)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
html_setfont(struct html * h,enum mandoc_esc font)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
print_metaf(struct html * h)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
html_close_paragraph(struct html * h)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
html_fillmode(struct html * h,enum roff_tok want)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 *
html_make_id(const struct roff_node * n,int unique)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
print_escape(struct html * h,char c)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
print_encode(struct html * h,const char * p,const char * pend,int norecurse)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
print_href(struct html * h,const char * name,const char * sec,int man)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 *
print_otag(struct html * h,enum htmltag tag,const char * fmt,...)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 *
print_otag_id(struct html * h,enum htmltag elemtype,const char * cattr,struct roff_node * n)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
print_ctag(struct html * h,struct tag * tag)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
print_gen_decls(struct html * h)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
print_gen_comment(struct html * h,struct roff_node * n)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
print_text(struct html * h,const char * word)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
print_tagged_text(struct html * h,const char * word,struct roff_node * n)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
print_tagq(struct html * h,const struct tag * until)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
print_stagq(struct html * h,const struct tag * suntil)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
print_byte(struct html * h,char c)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
print_endline(struct html * h)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
print_endword(struct html * h)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
print_indent(struct html * h)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
print_word(struct html * h,const char * cp)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