1*0a6a1f1dSLionel Sambuc /* Id: html.c,v 1.153 2014/01/05 19:10:56 joerg Exp */
2d65f6f70SBen Gras /*
392395e9cSLionel Sambuc * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*0a6a1f1dSLionel Sambuc * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
5d65f6f70SBen Gras *
6d65f6f70SBen Gras * Permission to use, copy, modify, and distribute this software for any
7d65f6f70SBen Gras * purpose with or without fee is hereby granted, provided that the above
8d65f6f70SBen Gras * copyright notice and this permission notice appear in all copies.
9d65f6f70SBen Gras *
10d65f6f70SBen Gras * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d65f6f70SBen Gras * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d65f6f70SBen Gras * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d65f6f70SBen Gras * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d65f6f70SBen Gras * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d65f6f70SBen Gras * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d65f6f70SBen Gras * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d65f6f70SBen Gras */
18d65f6f70SBen Gras #ifdef HAVE_CONFIG_H
19d65f6f70SBen Gras #include "config.h"
20d65f6f70SBen Gras #endif
21d65f6f70SBen Gras
22d65f6f70SBen Gras #include <sys/types.h>
23d65f6f70SBen Gras
24d65f6f70SBen Gras #include <assert.h>
25d65f6f70SBen Gras #include <ctype.h>
26d65f6f70SBen Gras #include <stdarg.h>
27d65f6f70SBen Gras #include <stdio.h>
28d65f6f70SBen Gras #include <stdint.h>
29d65f6f70SBen Gras #include <stdlib.h>
30d65f6f70SBen Gras #include <string.h>
31d65f6f70SBen Gras #include <unistd.h>
32d65f6f70SBen Gras
33d65f6f70SBen Gras #include "mandoc.h"
3492395e9cSLionel Sambuc #include "libmandoc.h"
35d65f6f70SBen Gras #include "out.h"
36d65f6f70SBen Gras #include "html.h"
37d65f6f70SBen Gras #include "main.h"
38d65f6f70SBen Gras
39d65f6f70SBen Gras struct htmldata {
40d65f6f70SBen Gras const char *name;
41d65f6f70SBen Gras int flags;
42d65f6f70SBen Gras #define HTML_CLRLINE (1 << 0)
43d65f6f70SBen Gras #define HTML_NOSTACK (1 << 1)
44d65f6f70SBen Gras #define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */
45d65f6f70SBen Gras };
46d65f6f70SBen Gras
47d65f6f70SBen Gras static const struct htmldata htmltags[TAG_MAX] = {
48d65f6f70SBen Gras {"html", HTML_CLRLINE}, /* TAG_HTML */
49d65f6f70SBen Gras {"head", HTML_CLRLINE}, /* TAG_HEAD */
50d65f6f70SBen Gras {"body", HTML_CLRLINE}, /* TAG_BODY */
51d65f6f70SBen Gras {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
52d65f6f70SBen Gras {"title", HTML_CLRLINE}, /* TAG_TITLE */
53d65f6f70SBen Gras {"div", HTML_CLRLINE}, /* TAG_DIV */
54d65f6f70SBen Gras {"h1", 0}, /* TAG_H1 */
55d65f6f70SBen Gras {"h2", 0}, /* TAG_H2 */
56d65f6f70SBen Gras {"span", 0}, /* TAG_SPAN */
57d65f6f70SBen Gras {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
58d65f6f70SBen Gras {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
59d65f6f70SBen Gras {"a", 0}, /* TAG_A */
60d65f6f70SBen Gras {"table", HTML_CLRLINE}, /* TAG_TABLE */
61d65f6f70SBen Gras {"tbody", HTML_CLRLINE}, /* TAG_TBODY */
62d65f6f70SBen Gras {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
63d65f6f70SBen Gras {"tr", HTML_CLRLINE}, /* TAG_TR */
64d65f6f70SBen Gras {"td", HTML_CLRLINE}, /* TAG_TD */
65d65f6f70SBen Gras {"li", HTML_CLRLINE}, /* TAG_LI */
66d65f6f70SBen Gras {"ul", HTML_CLRLINE}, /* TAG_UL */
67d65f6f70SBen Gras {"ol", HTML_CLRLINE}, /* TAG_OL */
68d65f6f70SBen Gras {"dl", HTML_CLRLINE}, /* TAG_DL */
69d65f6f70SBen Gras {"dt", HTML_CLRLINE}, /* TAG_DT */
70d65f6f70SBen Gras {"dd", HTML_CLRLINE}, /* TAG_DD */
71d65f6f70SBen Gras {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
72d65f6f70SBen Gras {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */
73d65f6f70SBen Gras {"pre", HTML_CLRLINE }, /* TAG_PRE */
74d65f6f70SBen Gras {"b", 0 }, /* TAG_B */
75d65f6f70SBen Gras {"i", 0 }, /* TAG_I */
76d65f6f70SBen Gras {"code", 0 }, /* TAG_CODE */
77d65f6f70SBen Gras {"small", 0 }, /* TAG_SMALL */
78d65f6f70SBen Gras };
79d65f6f70SBen Gras
80d65f6f70SBen Gras static const char *const htmlattrs[ATTR_MAX] = {
81d65f6f70SBen Gras "http-equiv", /* ATTR_HTTPEQUIV */
82d65f6f70SBen Gras "content", /* ATTR_CONTENT */
83d65f6f70SBen Gras "name", /* ATTR_NAME */
84d65f6f70SBen Gras "rel", /* ATTR_REL */
85d65f6f70SBen Gras "href", /* ATTR_HREF */
86d65f6f70SBen Gras "type", /* ATTR_TYPE */
87d65f6f70SBen Gras "media", /* ATTR_MEDIA */
88d65f6f70SBen Gras "class", /* ATTR_CLASS */
89d65f6f70SBen Gras "style", /* ATTR_STYLE */
90d65f6f70SBen Gras "width", /* ATTR_WIDTH */
91d65f6f70SBen Gras "id", /* ATTR_ID */
92d65f6f70SBen Gras "summary", /* ATTR_SUMMARY */
93d65f6f70SBen Gras "align", /* ATTR_ALIGN */
9492395e9cSLionel Sambuc "colspan", /* ATTR_COLSPAN */
95d65f6f70SBen Gras };
96d65f6f70SBen Gras
9792395e9cSLionel Sambuc static const char *const roffscales[SCALE_MAX] = {
9892395e9cSLionel Sambuc "cm", /* SCALE_CM */
9992395e9cSLionel Sambuc "in", /* SCALE_IN */
10092395e9cSLionel Sambuc "pc", /* SCALE_PC */
10192395e9cSLionel Sambuc "pt", /* SCALE_PT */
10292395e9cSLionel Sambuc "em", /* SCALE_EM */
10392395e9cSLionel Sambuc "em", /* SCALE_MM */
10492395e9cSLionel Sambuc "ex", /* SCALE_EN */
10592395e9cSLionel Sambuc "ex", /* SCALE_BU */
10692395e9cSLionel Sambuc "em", /* SCALE_VS */
10792395e9cSLionel Sambuc "ex", /* SCALE_FS */
10892395e9cSLionel Sambuc };
109d65f6f70SBen Gras
11092395e9cSLionel Sambuc static void bufncat(struct html *, const char *, size_t);
11192395e9cSLionel Sambuc static void print_ctag(struct html *, enum htmltag);
11292395e9cSLionel Sambuc static int print_encode(struct html *, const char *, int);
11392395e9cSLionel Sambuc static void print_metaf(struct html *, enum mandoc_esc);
11492395e9cSLionel Sambuc static void print_attr(struct html *, const char *, const char *);
11592395e9cSLionel Sambuc static void *ml_alloc(char *, enum htmltype);
116d65f6f70SBen Gras
117d65f6f70SBen Gras static void *
ml_alloc(char * outopts,enum htmltype type)118d65f6f70SBen Gras ml_alloc(char *outopts, enum htmltype type)
119d65f6f70SBen Gras {
120d65f6f70SBen Gras struct html *h;
12192395e9cSLionel Sambuc const char *toks[5];
122d65f6f70SBen Gras char *v;
123d65f6f70SBen Gras
124d65f6f70SBen Gras toks[0] = "style";
125d65f6f70SBen Gras toks[1] = "man";
126d65f6f70SBen Gras toks[2] = "includes";
12792395e9cSLionel Sambuc toks[3] = "fragment";
12892395e9cSLionel Sambuc toks[4] = NULL;
129d65f6f70SBen Gras
13092395e9cSLionel Sambuc h = mandoc_calloc(1, sizeof(struct html));
131d65f6f70SBen Gras
132d65f6f70SBen Gras h->type = type;
133d65f6f70SBen Gras h->tags.head = NULL;
13492395e9cSLionel Sambuc h->symtab = mchars_alloc();
135d65f6f70SBen Gras
136d65f6f70SBen Gras while (outopts && *outopts)
137d65f6f70SBen Gras switch (getsubopt(&outopts, UNCONST(toks), &v)) {
138d65f6f70SBen Gras case (0):
139d65f6f70SBen Gras h->style = v;
140d65f6f70SBen Gras break;
141d65f6f70SBen Gras case (1):
142d65f6f70SBen Gras h->base_man = v;
143d65f6f70SBen Gras break;
144d65f6f70SBen Gras case (2):
145d65f6f70SBen Gras h->base_includes = v;
146d65f6f70SBen Gras break;
14792395e9cSLionel Sambuc case (3):
14892395e9cSLionel Sambuc h->oflags |= HTML_FRAGMENT;
14992395e9cSLionel Sambuc break;
150d65f6f70SBen Gras default:
151d65f6f70SBen Gras break;
152d65f6f70SBen Gras }
153d65f6f70SBen Gras
154d65f6f70SBen Gras return(h);
155d65f6f70SBen Gras }
156d65f6f70SBen Gras
157d65f6f70SBen Gras void *
html_alloc(char * outopts)158d65f6f70SBen Gras html_alloc(char *outopts)
159d65f6f70SBen Gras {
160d65f6f70SBen Gras
161d65f6f70SBen Gras return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
162d65f6f70SBen Gras }
163d65f6f70SBen Gras
164d65f6f70SBen Gras
165d65f6f70SBen Gras void *
xhtml_alloc(char * outopts)166d65f6f70SBen Gras xhtml_alloc(char *outopts)
167d65f6f70SBen Gras {
168d65f6f70SBen Gras
169d65f6f70SBen Gras return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
170d65f6f70SBen Gras }
171d65f6f70SBen Gras
172d65f6f70SBen Gras
173d65f6f70SBen Gras void
html_free(void * p)174d65f6f70SBen Gras html_free(void *p)
175d65f6f70SBen Gras {
176d65f6f70SBen Gras struct tag *tag;
177d65f6f70SBen Gras struct html *h;
178d65f6f70SBen Gras
179d65f6f70SBen Gras h = (struct html *)p;
180d65f6f70SBen Gras
181d65f6f70SBen Gras while ((tag = h->tags.head) != NULL) {
182d65f6f70SBen Gras h->tags.head = tag->next;
183d65f6f70SBen Gras free(tag);
184d65f6f70SBen Gras }
185d65f6f70SBen Gras
186d65f6f70SBen Gras if (h->symtab)
18792395e9cSLionel Sambuc mchars_free(h->symtab);
188d65f6f70SBen Gras
189d65f6f70SBen Gras free(h);
190d65f6f70SBen Gras }
191d65f6f70SBen Gras
192d65f6f70SBen Gras
193d65f6f70SBen Gras void
print_gen_head(struct html * h)194d65f6f70SBen Gras print_gen_head(struct html *h)
195d65f6f70SBen Gras {
196d65f6f70SBen Gras struct htmlpair tag[4];
197d65f6f70SBen Gras
198d65f6f70SBen Gras tag[0].key = ATTR_HTTPEQUIV;
199d65f6f70SBen Gras tag[0].val = "Content-Type";
200d65f6f70SBen Gras tag[1].key = ATTR_CONTENT;
201d65f6f70SBen Gras tag[1].val = "text/html; charset=utf-8";
202d65f6f70SBen Gras print_otag(h, TAG_META, 2, tag);
203d65f6f70SBen Gras
204d65f6f70SBen Gras tag[0].key = ATTR_NAME;
205d65f6f70SBen Gras tag[0].val = "resource-type";
206d65f6f70SBen Gras tag[1].key = ATTR_CONTENT;
207d65f6f70SBen Gras tag[1].val = "document";
208d65f6f70SBen Gras print_otag(h, TAG_META, 2, tag);
209d65f6f70SBen Gras
210d65f6f70SBen Gras if (h->style) {
211d65f6f70SBen Gras tag[0].key = ATTR_REL;
212d65f6f70SBen Gras tag[0].val = "stylesheet";
213d65f6f70SBen Gras tag[1].key = ATTR_HREF;
214d65f6f70SBen Gras tag[1].val = h->style;
215d65f6f70SBen Gras tag[2].key = ATTR_TYPE;
216d65f6f70SBen Gras tag[2].val = "text/css";
217d65f6f70SBen Gras tag[3].key = ATTR_MEDIA;
218d65f6f70SBen Gras tag[3].val = "all";
219d65f6f70SBen Gras print_otag(h, TAG_LINK, 4, tag);
220d65f6f70SBen Gras }
221d65f6f70SBen Gras }
222d65f6f70SBen Gras
223d65f6f70SBen Gras static void
print_metaf(struct html * h,enum mandoc_esc deco)22492395e9cSLionel Sambuc print_metaf(struct html *h, enum mandoc_esc deco)
225d65f6f70SBen Gras {
226d65f6f70SBen Gras enum htmlfont font;
227d65f6f70SBen Gras
228d65f6f70SBen Gras switch (deco) {
22992395e9cSLionel Sambuc case (ESCAPE_FONTPREV):
230d65f6f70SBen Gras font = h->metal;
231d65f6f70SBen Gras break;
23292395e9cSLionel Sambuc case (ESCAPE_FONTITALIC):
233d65f6f70SBen Gras font = HTMLFONT_ITALIC;
234d65f6f70SBen Gras break;
23592395e9cSLionel Sambuc case (ESCAPE_FONTBOLD):
236d65f6f70SBen Gras font = HTMLFONT_BOLD;
237d65f6f70SBen Gras break;
238*0a6a1f1dSLionel Sambuc case (ESCAPE_FONTBI):
239*0a6a1f1dSLionel Sambuc font = HTMLFONT_BI;
240*0a6a1f1dSLionel Sambuc break;
24192395e9cSLionel Sambuc case (ESCAPE_FONT):
24292395e9cSLionel Sambuc /* FALLTHROUGH */
24392395e9cSLionel Sambuc case (ESCAPE_FONTROMAN):
244d65f6f70SBen Gras font = HTMLFONT_NONE;
245d65f6f70SBen Gras break;
246d65f6f70SBen Gras default:
247d65f6f70SBen Gras abort();
248d65f6f70SBen Gras /* NOTREACHED */
249d65f6f70SBen Gras }
250d65f6f70SBen Gras
251d65f6f70SBen Gras if (h->metaf) {
252d65f6f70SBen Gras print_tagq(h, h->metaf);
253d65f6f70SBen Gras h->metaf = NULL;
254d65f6f70SBen Gras }
255d65f6f70SBen Gras
256d65f6f70SBen Gras h->metal = h->metac;
257d65f6f70SBen Gras h->metac = font;
258d65f6f70SBen Gras
259*0a6a1f1dSLionel Sambuc switch (font) {
260*0a6a1f1dSLionel Sambuc case (HTMLFONT_ITALIC):
261*0a6a1f1dSLionel Sambuc h->metaf = print_otag(h, TAG_I, 0, NULL);
262*0a6a1f1dSLionel Sambuc break;
263*0a6a1f1dSLionel Sambuc case (HTMLFONT_BOLD):
264*0a6a1f1dSLionel Sambuc h->metaf = print_otag(h, TAG_B, 0, NULL);
265*0a6a1f1dSLionel Sambuc break;
266*0a6a1f1dSLionel Sambuc case (HTMLFONT_BI):
267*0a6a1f1dSLionel Sambuc h->metaf = print_otag(h, TAG_B, 0, NULL);
268d65f6f70SBen Gras print_otag(h, TAG_I, 0, NULL);
269*0a6a1f1dSLionel Sambuc break;
270*0a6a1f1dSLionel Sambuc default:
271*0a6a1f1dSLionel Sambuc break;
272*0a6a1f1dSLionel Sambuc }
273d65f6f70SBen Gras }
274d65f6f70SBen Gras
27592395e9cSLionel Sambuc int
html_strlen(const char * cp)27692395e9cSLionel Sambuc html_strlen(const char *cp)
27792395e9cSLionel Sambuc {
278*0a6a1f1dSLionel Sambuc size_t rsz;
279*0a6a1f1dSLionel Sambuc int skip, sz;
28092395e9cSLionel Sambuc
28192395e9cSLionel Sambuc /*
28292395e9cSLionel Sambuc * Account for escaped sequences within string length
28392395e9cSLionel Sambuc * calculations. This follows the logic in term_strlen() as we
28492395e9cSLionel Sambuc * must calculate the width of produced strings.
28592395e9cSLionel Sambuc * Assume that characters are always width of "1". This is
28692395e9cSLionel Sambuc * hacky, but it gets the job done for approximation of widths.
28792395e9cSLionel Sambuc */
28892395e9cSLionel Sambuc
28992395e9cSLionel Sambuc sz = 0;
290*0a6a1f1dSLionel Sambuc skip = 0;
291*0a6a1f1dSLionel Sambuc while (1) {
292*0a6a1f1dSLionel Sambuc rsz = strcspn(cp, "\\");
293*0a6a1f1dSLionel Sambuc if (rsz) {
294*0a6a1f1dSLionel Sambuc cp += rsz;
295*0a6a1f1dSLionel Sambuc if (skip) {
296*0a6a1f1dSLionel Sambuc skip = 0;
297*0a6a1f1dSLionel Sambuc rsz--;
298*0a6a1f1dSLionel Sambuc }
299*0a6a1f1dSLionel Sambuc sz += rsz;
300*0a6a1f1dSLionel Sambuc }
301*0a6a1f1dSLionel Sambuc if ('\0' == *cp)
302*0a6a1f1dSLionel Sambuc break;
303*0a6a1f1dSLionel Sambuc cp++;
304*0a6a1f1dSLionel Sambuc switch (mandoc_escape(&cp, NULL, NULL)) {
30592395e9cSLionel Sambuc case (ESCAPE_ERROR):
30692395e9cSLionel Sambuc return(sz);
30792395e9cSLionel Sambuc case (ESCAPE_UNICODE):
30892395e9cSLionel Sambuc /* FALLTHROUGH */
30992395e9cSLionel Sambuc case (ESCAPE_NUMBERED):
31092395e9cSLionel Sambuc /* FALLTHROUGH */
31192395e9cSLionel Sambuc case (ESCAPE_SPECIAL):
312*0a6a1f1dSLionel Sambuc if (skip)
313*0a6a1f1dSLionel Sambuc skip = 0;
314*0a6a1f1dSLionel Sambuc else
31592395e9cSLionel Sambuc sz++;
31692395e9cSLionel Sambuc break;
317*0a6a1f1dSLionel Sambuc case (ESCAPE_SKIPCHAR):
318*0a6a1f1dSLionel Sambuc skip = 1;
319*0a6a1f1dSLionel Sambuc break;
32092395e9cSLionel Sambuc default:
32192395e9cSLionel Sambuc break;
32292395e9cSLionel Sambuc }
32392395e9cSLionel Sambuc }
324*0a6a1f1dSLionel Sambuc return(sz);
32592395e9cSLionel Sambuc }
326d65f6f70SBen Gras
327d65f6f70SBen Gras static int
print_encode(struct html * h,const char * p,int norecurse)328d65f6f70SBen Gras print_encode(struct html *h, const char *p, int norecurse)
329d65f6f70SBen Gras {
330d65f6f70SBen Gras size_t sz;
33192395e9cSLionel Sambuc int c, len, nospace;
332d65f6f70SBen Gras const char *seq;
33392395e9cSLionel Sambuc enum mandoc_esc esc;
334d65f6f70SBen Gras static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
335d65f6f70SBen Gras
336d65f6f70SBen Gras nospace = 0;
337d65f6f70SBen Gras
33892395e9cSLionel Sambuc while ('\0' != *p) {
339*0a6a1f1dSLionel Sambuc if (HTML_SKIPCHAR & h->flags && '\\' != *p) {
340*0a6a1f1dSLionel Sambuc h->flags &= ~HTML_SKIPCHAR;
341*0a6a1f1dSLionel Sambuc p++;
342*0a6a1f1dSLionel Sambuc continue;
343*0a6a1f1dSLionel Sambuc }
344*0a6a1f1dSLionel Sambuc
345d65f6f70SBen Gras sz = strcspn(p, rejs);
346d65f6f70SBen Gras
347d65f6f70SBen Gras fwrite(p, 1, sz, stdout);
34892395e9cSLionel Sambuc p += (int)sz;
349d65f6f70SBen Gras
35092395e9cSLionel Sambuc if ('\0' == *p)
35192395e9cSLionel Sambuc break;
35292395e9cSLionel Sambuc
35392395e9cSLionel Sambuc switch (*p++) {
35492395e9cSLionel Sambuc case ('<'):
355d65f6f70SBen Gras printf("<");
356d65f6f70SBen Gras continue;
35792395e9cSLionel Sambuc case ('>'):
358d65f6f70SBen Gras printf(">");
359d65f6f70SBen Gras continue;
36092395e9cSLionel Sambuc case ('&'):
361d65f6f70SBen Gras printf("&");
362d65f6f70SBen Gras continue;
36392395e9cSLionel Sambuc case (ASCII_HYPH):
364d65f6f70SBen Gras putchar('-');
365d65f6f70SBen Gras continue;
366d65f6f70SBen Gras default:
367d65f6f70SBen Gras break;
368d65f6f70SBen Gras }
369d65f6f70SBen Gras
37092395e9cSLionel Sambuc esc = mandoc_escape(&p, &seq, &len);
37192395e9cSLionel Sambuc if (ESCAPE_ERROR == esc)
37292395e9cSLionel Sambuc break;
373d65f6f70SBen Gras
37492395e9cSLionel Sambuc switch (esc) {
375*0a6a1f1dSLionel Sambuc case (ESCAPE_FONT):
376*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
377*0a6a1f1dSLionel Sambuc case (ESCAPE_FONTPREV):
378*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
379*0a6a1f1dSLionel Sambuc case (ESCAPE_FONTBOLD):
380*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
381*0a6a1f1dSLionel Sambuc case (ESCAPE_FONTITALIC):
382*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
383*0a6a1f1dSLionel Sambuc case (ESCAPE_FONTBI):
384*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
385*0a6a1f1dSLionel Sambuc case (ESCAPE_FONTROMAN):
386*0a6a1f1dSLionel Sambuc if (0 == norecurse)
387*0a6a1f1dSLionel Sambuc print_metaf(h, esc);
388*0a6a1f1dSLionel Sambuc continue;
389*0a6a1f1dSLionel Sambuc case (ESCAPE_SKIPCHAR):
390*0a6a1f1dSLionel Sambuc h->flags |= HTML_SKIPCHAR;
391*0a6a1f1dSLionel Sambuc continue;
392*0a6a1f1dSLionel Sambuc default:
393*0a6a1f1dSLionel Sambuc break;
394*0a6a1f1dSLionel Sambuc }
395*0a6a1f1dSLionel Sambuc
396*0a6a1f1dSLionel Sambuc if (h->flags & HTML_SKIPCHAR) {
397*0a6a1f1dSLionel Sambuc h->flags &= ~HTML_SKIPCHAR;
398*0a6a1f1dSLionel Sambuc continue;
399*0a6a1f1dSLionel Sambuc }
400*0a6a1f1dSLionel Sambuc
401*0a6a1f1dSLionel Sambuc switch (esc) {
40292395e9cSLionel Sambuc case (ESCAPE_UNICODE):
40392395e9cSLionel Sambuc /* Skip passed "u" header. */
40492395e9cSLionel Sambuc c = mchars_num2uc(seq + 1, len - 1);
40592395e9cSLionel Sambuc if ('\0' != c)
40692395e9cSLionel Sambuc printf("&#x%x;", c);
40792395e9cSLionel Sambuc break;
40892395e9cSLionel Sambuc case (ESCAPE_NUMBERED):
40992395e9cSLionel Sambuc c = mchars_num2char(seq, len);
41092395e9cSLionel Sambuc if ('\0' != c)
41192395e9cSLionel Sambuc putchar(c);
41292395e9cSLionel Sambuc break;
41392395e9cSLionel Sambuc case (ESCAPE_SPECIAL):
41492395e9cSLionel Sambuc c = mchars_spec2cp(h->symtab, seq, len);
41592395e9cSLionel Sambuc if (c > 0)
41692395e9cSLionel Sambuc printf("&#%d;", c);
41792395e9cSLionel Sambuc else if (-1 == c && 1 == len)
41892395e9cSLionel Sambuc putchar((int)*seq);
41992395e9cSLionel Sambuc break;
42092395e9cSLionel Sambuc case (ESCAPE_NOSPACE):
42192395e9cSLionel Sambuc if ('\0' == *p)
422d65f6f70SBen Gras nospace = 1;
42392395e9cSLionel Sambuc break;
42492395e9cSLionel Sambuc default:
42592395e9cSLionel Sambuc break;
42692395e9cSLionel Sambuc }
427d65f6f70SBen Gras }
428d65f6f70SBen Gras
429d65f6f70SBen Gras return(nospace);
430d65f6f70SBen Gras }
431d65f6f70SBen Gras
432d65f6f70SBen Gras
433d65f6f70SBen Gras static void
print_attr(struct html * h,const char * key,const char * val)434d65f6f70SBen Gras print_attr(struct html *h, const char *key, const char *val)
435d65f6f70SBen Gras {
436d65f6f70SBen Gras printf(" %s=\"", key);
437d65f6f70SBen Gras (void)print_encode(h, val, 1);
438d65f6f70SBen Gras putchar('\"');
439d65f6f70SBen Gras }
440d65f6f70SBen Gras
441d65f6f70SBen Gras
442d65f6f70SBen Gras struct tag *
print_otag(struct html * h,enum htmltag tag,int sz,const struct htmlpair * p)443d65f6f70SBen Gras print_otag(struct html *h, enum htmltag tag,
444d65f6f70SBen Gras int sz, const struct htmlpair *p)
445d65f6f70SBen Gras {
446d65f6f70SBen Gras int i;
447d65f6f70SBen Gras struct tag *t;
448d65f6f70SBen Gras
449d65f6f70SBen Gras /* Push this tags onto the stack of open scopes. */
450d65f6f70SBen Gras
451d65f6f70SBen Gras if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
45292395e9cSLionel Sambuc t = mandoc_malloc(sizeof(struct tag));
453d65f6f70SBen Gras t->tag = tag;
454d65f6f70SBen Gras t->next = h->tags.head;
455d65f6f70SBen Gras h->tags.head = t;
456d65f6f70SBen Gras } else
457d65f6f70SBen Gras t = NULL;
458d65f6f70SBen Gras
459d65f6f70SBen Gras if ( ! (HTML_NOSPACE & h->flags))
460d65f6f70SBen Gras if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
461d65f6f70SBen Gras /* Manage keeps! */
462d65f6f70SBen Gras if ( ! (HTML_KEEP & h->flags)) {
463d65f6f70SBen Gras if (HTML_PREKEEP & h->flags)
464d65f6f70SBen Gras h->flags |= HTML_KEEP;
465d65f6f70SBen Gras putchar(' ');
466d65f6f70SBen Gras } else
467d65f6f70SBen Gras printf(" ");
468d65f6f70SBen Gras }
469d65f6f70SBen Gras
470d65f6f70SBen Gras if ( ! (h->flags & HTML_NONOSPACE))
471d65f6f70SBen Gras h->flags &= ~HTML_NOSPACE;
472d65f6f70SBen Gras else
473d65f6f70SBen Gras h->flags |= HTML_NOSPACE;
474d65f6f70SBen Gras
475d65f6f70SBen Gras /* Print out the tag name and attributes. */
476d65f6f70SBen Gras
477d65f6f70SBen Gras printf("<%s", htmltags[tag].name);
478d65f6f70SBen Gras for (i = 0; i < sz; i++)
479d65f6f70SBen Gras print_attr(h, htmlattrs[p[i].key], p[i].val);
480d65f6f70SBen Gras
481d65f6f70SBen Gras /* Add non-overridable attributes. */
482d65f6f70SBen Gras
483d65f6f70SBen Gras if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
484d65f6f70SBen Gras print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
485d65f6f70SBen Gras print_attr(h, "xml:lang", "en");
486d65f6f70SBen Gras print_attr(h, "lang", "en");
487d65f6f70SBen Gras }
488d65f6f70SBen Gras
48992395e9cSLionel Sambuc /* Accommodate for XML "well-formed" singleton escaping. */
490d65f6f70SBen Gras
491d65f6f70SBen Gras if (HTML_AUTOCLOSE & htmltags[tag].flags)
492d65f6f70SBen Gras switch (h->type) {
493d65f6f70SBen Gras case (HTML_XHTML_1_0_STRICT):
494d65f6f70SBen Gras putchar('/');
495d65f6f70SBen Gras break;
496d65f6f70SBen Gras default:
497d65f6f70SBen Gras break;
498d65f6f70SBen Gras }
499d65f6f70SBen Gras
500d65f6f70SBen Gras putchar('>');
501d65f6f70SBen Gras
502d65f6f70SBen Gras h->flags |= HTML_NOSPACE;
503d65f6f70SBen Gras
504d65f6f70SBen Gras if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
505d65f6f70SBen Gras putchar('\n');
506d65f6f70SBen Gras
507d65f6f70SBen Gras return(t);
508d65f6f70SBen Gras }
509d65f6f70SBen Gras
510d65f6f70SBen Gras
511d65f6f70SBen Gras static void
print_ctag(struct html * h,enum htmltag tag)512d65f6f70SBen Gras print_ctag(struct html *h, enum htmltag tag)
513d65f6f70SBen Gras {
514d65f6f70SBen Gras
515d65f6f70SBen Gras printf("</%s>", htmltags[tag].name);
516d65f6f70SBen Gras if (HTML_CLRLINE & htmltags[tag].flags) {
517d65f6f70SBen Gras h->flags |= HTML_NOSPACE;
518d65f6f70SBen Gras putchar('\n');
519d65f6f70SBen Gras }
520d65f6f70SBen Gras }
521d65f6f70SBen Gras
522d65f6f70SBen Gras void
print_gen_decls(struct html * h)523d65f6f70SBen Gras print_gen_decls(struct html *h)
524d65f6f70SBen Gras {
525d65f6f70SBen Gras const char *doctype;
526d65f6f70SBen Gras const char *dtd;
527d65f6f70SBen Gras const char *name;
528d65f6f70SBen Gras
529d65f6f70SBen Gras switch (h->type) {
530d65f6f70SBen Gras case (HTML_HTML_4_01_STRICT):
531d65f6f70SBen Gras name = "HTML";
532d65f6f70SBen Gras doctype = "-//W3C//DTD HTML 4.01//EN";
533d65f6f70SBen Gras dtd = "http://www.w3.org/TR/html4/strict.dtd";
534d65f6f70SBen Gras break;
535d65f6f70SBen Gras default:
53692395e9cSLionel Sambuc puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
537d65f6f70SBen Gras name = "html";
538d65f6f70SBen Gras doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
539d65f6f70SBen Gras dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
540d65f6f70SBen Gras break;
541d65f6f70SBen Gras }
542d65f6f70SBen Gras
543d65f6f70SBen Gras printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
544d65f6f70SBen Gras name, doctype, dtd);
545d65f6f70SBen Gras }
546d65f6f70SBen Gras
547d65f6f70SBen Gras void
print_text(struct html * h,const char * word)548d65f6f70SBen Gras print_text(struct html *h, const char *word)
549d65f6f70SBen Gras {
550d65f6f70SBen Gras
551d65f6f70SBen Gras if ( ! (HTML_NOSPACE & h->flags)) {
552d65f6f70SBen Gras /* Manage keeps! */
553d65f6f70SBen Gras if ( ! (HTML_KEEP & h->flags)) {
554d65f6f70SBen Gras if (HTML_PREKEEP & h->flags)
555d65f6f70SBen Gras h->flags |= HTML_KEEP;
556d65f6f70SBen Gras putchar(' ');
557d65f6f70SBen Gras } else
558d65f6f70SBen Gras printf(" ");
559d65f6f70SBen Gras }
560d65f6f70SBen Gras
561d65f6f70SBen Gras assert(NULL == h->metaf);
562*0a6a1f1dSLionel Sambuc switch (h->metac) {
563*0a6a1f1dSLionel Sambuc case (HTMLFONT_ITALIC):
564*0a6a1f1dSLionel Sambuc h->metaf = print_otag(h, TAG_I, 0, NULL);
565*0a6a1f1dSLionel Sambuc break;
566*0a6a1f1dSLionel Sambuc case (HTMLFONT_BOLD):
567*0a6a1f1dSLionel Sambuc h->metaf = print_otag(h, TAG_B, 0, NULL);
568*0a6a1f1dSLionel Sambuc break;
569*0a6a1f1dSLionel Sambuc case (HTMLFONT_BI):
570*0a6a1f1dSLionel Sambuc h->metaf = print_otag(h, TAG_B, 0, NULL);
571d65f6f70SBen Gras print_otag(h, TAG_I, 0, NULL);
572*0a6a1f1dSLionel Sambuc break;
573*0a6a1f1dSLionel Sambuc default:
574*0a6a1f1dSLionel Sambuc break;
575*0a6a1f1dSLionel Sambuc }
576d65f6f70SBen Gras
577d65f6f70SBen Gras assert(word);
57892395e9cSLionel Sambuc if ( ! print_encode(h, word, 0)) {
579d65f6f70SBen Gras if ( ! (h->flags & HTML_NONOSPACE))
580d65f6f70SBen Gras h->flags &= ~HTML_NOSPACE;
58192395e9cSLionel Sambuc } else
58292395e9cSLionel Sambuc h->flags |= HTML_NOSPACE;
583d65f6f70SBen Gras
584d65f6f70SBen Gras if (h->metaf) {
585d65f6f70SBen Gras print_tagq(h, h->metaf);
586d65f6f70SBen Gras h->metaf = NULL;
587d65f6f70SBen Gras }
588d65f6f70SBen Gras
589d65f6f70SBen Gras h->flags &= ~HTML_IGNDELIM;
590d65f6f70SBen Gras }
591d65f6f70SBen Gras
592d65f6f70SBen Gras
593d65f6f70SBen Gras void
print_tagq(struct html * h,const struct tag * until)594d65f6f70SBen Gras print_tagq(struct html *h, const struct tag *until)
595d65f6f70SBen Gras {
596d65f6f70SBen Gras struct tag *tag;
597d65f6f70SBen Gras
598d65f6f70SBen Gras while ((tag = h->tags.head) != NULL) {
59992395e9cSLionel Sambuc /*
60092395e9cSLionel Sambuc * Remember to close out and nullify the current
60192395e9cSLionel Sambuc * meta-font and table, if applicable.
60292395e9cSLionel Sambuc */
603d65f6f70SBen Gras if (tag == h->metaf)
604d65f6f70SBen Gras h->metaf = NULL;
60592395e9cSLionel Sambuc if (tag == h->tblt)
60692395e9cSLionel Sambuc h->tblt = NULL;
607d65f6f70SBen Gras print_ctag(h, tag->tag);
608d65f6f70SBen Gras h->tags.head = tag->next;
609d65f6f70SBen Gras free(tag);
610d65f6f70SBen Gras if (until && tag == until)
611d65f6f70SBen Gras return;
612d65f6f70SBen Gras }
613d65f6f70SBen Gras }
614d65f6f70SBen Gras
615d65f6f70SBen Gras
616d65f6f70SBen Gras void
print_stagq(struct html * h,const struct tag * suntil)617d65f6f70SBen Gras print_stagq(struct html *h, const struct tag *suntil)
618d65f6f70SBen Gras {
619d65f6f70SBen Gras struct tag *tag;
620d65f6f70SBen Gras
621d65f6f70SBen Gras while ((tag = h->tags.head) != NULL) {
622d65f6f70SBen Gras if (suntil && tag == suntil)
623d65f6f70SBen Gras return;
62492395e9cSLionel Sambuc /*
62592395e9cSLionel Sambuc * Remember to close out and nullify the current
62692395e9cSLionel Sambuc * meta-font and table, if applicable.
62792395e9cSLionel Sambuc */
628d65f6f70SBen Gras if (tag == h->metaf)
629d65f6f70SBen Gras h->metaf = NULL;
63092395e9cSLionel Sambuc if (tag == h->tblt)
63192395e9cSLionel Sambuc h->tblt = NULL;
632d65f6f70SBen Gras print_ctag(h, tag->tag);
633d65f6f70SBen Gras h->tags.head = tag->next;
634d65f6f70SBen Gras free(tag);
635d65f6f70SBen Gras }
636d65f6f70SBen Gras }
637d65f6f70SBen Gras
638d65f6f70SBen Gras void
bufinit(struct html * h)639d65f6f70SBen Gras bufinit(struct html *h)
640d65f6f70SBen Gras {
641d65f6f70SBen Gras
642d65f6f70SBen Gras h->buf[0] = '\0';
643d65f6f70SBen Gras h->buflen = 0;
644d65f6f70SBen Gras }
645d65f6f70SBen Gras
646d65f6f70SBen Gras void
bufcat_style(struct html * h,const char * key,const char * val)647d65f6f70SBen Gras bufcat_style(struct html *h, const char *key, const char *val)
648d65f6f70SBen Gras {
649d65f6f70SBen Gras
650d65f6f70SBen Gras bufcat(h, key);
65192395e9cSLionel Sambuc bufcat(h, ":");
652d65f6f70SBen Gras bufcat(h, val);
65392395e9cSLionel Sambuc bufcat(h, ";");
654d65f6f70SBen Gras }
655d65f6f70SBen Gras
656d65f6f70SBen Gras void
bufcat(struct html * h,const char * p)657d65f6f70SBen Gras bufcat(struct html *h, const char *p)
658d65f6f70SBen Gras {
659d65f6f70SBen Gras
66092395e9cSLionel Sambuc h->buflen = strlcat(h->buf, p, BUFSIZ);
66192395e9cSLionel Sambuc assert(h->buflen < BUFSIZ);
662d65f6f70SBen Gras }
663d65f6f70SBen Gras
664d65f6f70SBen Gras void
bufcat_fmt(struct html * h,const char * fmt,...)66592395e9cSLionel Sambuc bufcat_fmt(struct html *h, const char *fmt, ...)
666d65f6f70SBen Gras {
667d65f6f70SBen Gras va_list ap;
668d65f6f70SBen Gras
669d65f6f70SBen Gras va_start(ap, fmt);
670d65f6f70SBen Gras (void)vsnprintf(h->buf + (int)h->buflen,
671d65f6f70SBen Gras BUFSIZ - h->buflen - 1, fmt, ap);
672d65f6f70SBen Gras va_end(ap);
673d65f6f70SBen Gras h->buflen = strlen(h->buf);
674d65f6f70SBen Gras }
675d65f6f70SBen Gras
67692395e9cSLionel Sambuc static void
bufncat(struct html * h,const char * p,size_t sz)677d65f6f70SBen Gras bufncat(struct html *h, const char *p, size_t sz)
678d65f6f70SBen Gras {
679d65f6f70SBen Gras
68092395e9cSLionel Sambuc assert(h->buflen + sz + 1 < BUFSIZ);
68192395e9cSLionel Sambuc strncat(h->buf, p, sz);
682d65f6f70SBen Gras h->buflen += sz;
683d65f6f70SBen Gras }
684d65f6f70SBen Gras
685d65f6f70SBen Gras void
buffmt_includes(struct html * h,const char * name)686d65f6f70SBen Gras buffmt_includes(struct html *h, const char *name)
687d65f6f70SBen Gras {
688d65f6f70SBen Gras const char *p, *pp;
689d65f6f70SBen Gras
690d65f6f70SBen Gras pp = h->base_includes;
691d65f6f70SBen Gras
69292395e9cSLionel Sambuc bufinit(h);
693d65f6f70SBen Gras while (NULL != (p = strchr(pp, '%'))) {
694d65f6f70SBen Gras bufncat(h, pp, (size_t)(p - pp));
695d65f6f70SBen Gras switch (*(p + 1)) {
696d65f6f70SBen Gras case('I'):
697d65f6f70SBen Gras bufcat(h, name);
698d65f6f70SBen Gras break;
699d65f6f70SBen Gras default:
700d65f6f70SBen Gras bufncat(h, p, 2);
701d65f6f70SBen Gras break;
702d65f6f70SBen Gras }
703d65f6f70SBen Gras pp = p + 2;
704d65f6f70SBen Gras }
705d65f6f70SBen Gras if (pp)
706d65f6f70SBen Gras bufcat(h, pp);
707d65f6f70SBen Gras }
708d65f6f70SBen Gras
709d65f6f70SBen Gras void
buffmt_man(struct html * h,const char * name,const char * sec)710d65f6f70SBen Gras buffmt_man(struct html *h,
711d65f6f70SBen Gras const char *name, const char *sec)
712d65f6f70SBen Gras {
713d65f6f70SBen Gras const char *p, *pp;
714d65f6f70SBen Gras
715d65f6f70SBen Gras pp = h->base_man;
716d65f6f70SBen Gras
71792395e9cSLionel Sambuc bufinit(h);
718d65f6f70SBen Gras while (NULL != (p = strchr(pp, '%'))) {
719d65f6f70SBen Gras bufncat(h, pp, (size_t)(p - pp));
720d65f6f70SBen Gras switch (*(p + 1)) {
721d65f6f70SBen Gras case('S'):
722d65f6f70SBen Gras bufcat(h, sec ? sec : "1");
723d65f6f70SBen Gras break;
724d65f6f70SBen Gras case('N'):
725*0a6a1f1dSLionel Sambuc bufcat_fmt(h, "%s", name);
726d65f6f70SBen Gras break;
727d65f6f70SBen Gras default:
728d65f6f70SBen Gras bufncat(h, p, 2);
729d65f6f70SBen Gras break;
730d65f6f70SBen Gras }
731d65f6f70SBen Gras pp = p + 2;
732d65f6f70SBen Gras }
733d65f6f70SBen Gras if (pp)
734d65f6f70SBen Gras bufcat(h, pp);
735d65f6f70SBen Gras }
736d65f6f70SBen Gras
737d65f6f70SBen Gras void
bufcat_su(struct html * h,const char * p,const struct roffsu * su)738d65f6f70SBen Gras bufcat_su(struct html *h, const char *p, const struct roffsu *su)
739d65f6f70SBen Gras {
740d65f6f70SBen Gras double v;
741d65f6f70SBen Gras
742d65f6f70SBen Gras v = su->scale;
74392395e9cSLionel Sambuc if (SCALE_MM == su->unit && 0.0 == (v /= 100.0))
74492395e9cSLionel Sambuc v = 1.0;
745d65f6f70SBen Gras
74692395e9cSLionel Sambuc bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]);
747d65f6f70SBen Gras }
748d65f6f70SBen Gras
749d65f6f70SBen Gras void
bufcat_id(struct html * h,const char * src)75092395e9cSLionel Sambuc bufcat_id(struct html *h, const char *src)
751d65f6f70SBen Gras {
752d65f6f70SBen Gras
753d65f6f70SBen Gras /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
754d65f6f70SBen Gras
75592395e9cSLionel Sambuc while ('\0' != *src)
75692395e9cSLionel Sambuc bufcat_fmt(h, "%.2x", *src++);
757d65f6f70SBen Gras }
758