xref: /titanic_52/usr/src/cmd/mandoc/html.c (revision 95c635efb7c3b86efc493e0447eaec7aecca3f0f)
1*95c635efSGarrett D'Amore /*	$Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */
2*95c635efSGarrett D'Amore /*
3*95c635efSGarrett D'Amore  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*95c635efSGarrett D'Amore  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
5*95c635efSGarrett D'Amore  *
6*95c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
7*95c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
8*95c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
9*95c635efSGarrett D'Amore  *
10*95c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*95c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*95c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*95c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*95c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*95c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*95c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*95c635efSGarrett D'Amore  */
18*95c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H
19*95c635efSGarrett D'Amore #include "config.h"
20*95c635efSGarrett D'Amore #endif
21*95c635efSGarrett D'Amore 
22*95c635efSGarrett D'Amore #include <sys/types.h>
23*95c635efSGarrett D'Amore 
24*95c635efSGarrett D'Amore #include <assert.h>
25*95c635efSGarrett D'Amore #include <ctype.h>
26*95c635efSGarrett D'Amore #include <stdarg.h>
27*95c635efSGarrett D'Amore #include <stdio.h>
28*95c635efSGarrett D'Amore #include <stdint.h>
29*95c635efSGarrett D'Amore #include <stdlib.h>
30*95c635efSGarrett D'Amore #include <string.h>
31*95c635efSGarrett D'Amore #include <unistd.h>
32*95c635efSGarrett D'Amore 
33*95c635efSGarrett D'Amore #include "mandoc.h"
34*95c635efSGarrett D'Amore #include "libmandoc.h"
35*95c635efSGarrett D'Amore #include "out.h"
36*95c635efSGarrett D'Amore #include "html.h"
37*95c635efSGarrett D'Amore #include "main.h"
38*95c635efSGarrett D'Amore 
39*95c635efSGarrett D'Amore struct	htmldata {
40*95c635efSGarrett D'Amore 	const char	 *name;
41*95c635efSGarrett D'Amore 	int		  flags;
42*95c635efSGarrett D'Amore #define	HTML_CLRLINE	 (1 << 0)
43*95c635efSGarrett D'Amore #define	HTML_NOSTACK	 (1 << 1)
44*95c635efSGarrett D'Amore #define	HTML_AUTOCLOSE	 (1 << 2) /* Tag has auto-closure. */
45*95c635efSGarrett D'Amore };
46*95c635efSGarrett D'Amore 
47*95c635efSGarrett D'Amore static	const struct htmldata htmltags[TAG_MAX] = {
48*95c635efSGarrett D'Amore 	{"html",	HTML_CLRLINE}, /* TAG_HTML */
49*95c635efSGarrett D'Amore 	{"head",	HTML_CLRLINE}, /* TAG_HEAD */
50*95c635efSGarrett D'Amore 	{"body",	HTML_CLRLINE}, /* TAG_BODY */
51*95c635efSGarrett D'Amore 	{"meta",	HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
52*95c635efSGarrett D'Amore 	{"title",	HTML_CLRLINE}, /* TAG_TITLE */
53*95c635efSGarrett D'Amore 	{"div",		HTML_CLRLINE}, /* TAG_DIV */
54*95c635efSGarrett D'Amore 	{"h1",		0}, /* TAG_H1 */
55*95c635efSGarrett D'Amore 	{"h2",		0}, /* TAG_H2 */
56*95c635efSGarrett D'Amore 	{"span",	0}, /* TAG_SPAN */
57*95c635efSGarrett D'Amore 	{"link",	HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
58*95c635efSGarrett D'Amore 	{"br",		HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
59*95c635efSGarrett D'Amore 	{"a",		0}, /* TAG_A */
60*95c635efSGarrett D'Amore 	{"table",	HTML_CLRLINE}, /* TAG_TABLE */
61*95c635efSGarrett D'Amore 	{"tbody",	HTML_CLRLINE}, /* TAG_TBODY */
62*95c635efSGarrett D'Amore 	{"col",		HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
63*95c635efSGarrett D'Amore 	{"tr",		HTML_CLRLINE}, /* TAG_TR */
64*95c635efSGarrett D'Amore 	{"td",		HTML_CLRLINE}, /* TAG_TD */
65*95c635efSGarrett D'Amore 	{"li",		HTML_CLRLINE}, /* TAG_LI */
66*95c635efSGarrett D'Amore 	{"ul",		HTML_CLRLINE}, /* TAG_UL */
67*95c635efSGarrett D'Amore 	{"ol",		HTML_CLRLINE}, /* TAG_OL */
68*95c635efSGarrett D'Amore 	{"dl",		HTML_CLRLINE}, /* TAG_DL */
69*95c635efSGarrett D'Amore 	{"dt",		HTML_CLRLINE}, /* TAG_DT */
70*95c635efSGarrett D'Amore 	{"dd",		HTML_CLRLINE}, /* TAG_DD */
71*95c635efSGarrett D'Amore 	{"blockquote",	HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
72*95c635efSGarrett D'Amore 	{"p",		HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */
73*95c635efSGarrett D'Amore 	{"pre",		HTML_CLRLINE }, /* TAG_PRE */
74*95c635efSGarrett D'Amore 	{"b",		0 }, /* TAG_B */
75*95c635efSGarrett D'Amore 	{"i",		0 }, /* TAG_I */
76*95c635efSGarrett D'Amore 	{"code",	0 }, /* TAG_CODE */
77*95c635efSGarrett D'Amore 	{"small",	0 }, /* TAG_SMALL */
78*95c635efSGarrett D'Amore };
79*95c635efSGarrett D'Amore 
80*95c635efSGarrett D'Amore static	const char	*const htmlattrs[ATTR_MAX] = {
81*95c635efSGarrett D'Amore 	"http-equiv", /* ATTR_HTTPEQUIV */
82*95c635efSGarrett D'Amore 	"content", /* ATTR_CONTENT */
83*95c635efSGarrett D'Amore 	"name", /* ATTR_NAME */
84*95c635efSGarrett D'Amore 	"rel", /* ATTR_REL */
85*95c635efSGarrett D'Amore 	"href", /* ATTR_HREF */
86*95c635efSGarrett D'Amore 	"type", /* ATTR_TYPE */
87*95c635efSGarrett D'Amore 	"media", /* ATTR_MEDIA */
88*95c635efSGarrett D'Amore 	"class", /* ATTR_CLASS */
89*95c635efSGarrett D'Amore 	"style", /* ATTR_STYLE */
90*95c635efSGarrett D'Amore 	"width", /* ATTR_WIDTH */
91*95c635efSGarrett D'Amore 	"id", /* ATTR_ID */
92*95c635efSGarrett D'Amore 	"summary", /* ATTR_SUMMARY */
93*95c635efSGarrett D'Amore 	"align", /* ATTR_ALIGN */
94*95c635efSGarrett D'Amore 	"colspan", /* ATTR_COLSPAN */
95*95c635efSGarrett D'Amore };
96*95c635efSGarrett D'Amore 
97*95c635efSGarrett D'Amore static	const char	*const roffscales[SCALE_MAX] = {
98*95c635efSGarrett D'Amore 	"cm", /* SCALE_CM */
99*95c635efSGarrett D'Amore 	"in", /* SCALE_IN */
100*95c635efSGarrett D'Amore 	"pc", /* SCALE_PC */
101*95c635efSGarrett D'Amore 	"pt", /* SCALE_PT */
102*95c635efSGarrett D'Amore 	"em", /* SCALE_EM */
103*95c635efSGarrett D'Amore 	"em", /* SCALE_MM */
104*95c635efSGarrett D'Amore 	"ex", /* SCALE_EN */
105*95c635efSGarrett D'Amore 	"ex", /* SCALE_BU */
106*95c635efSGarrett D'Amore 	"em", /* SCALE_VS */
107*95c635efSGarrett D'Amore 	"ex", /* SCALE_FS */
108*95c635efSGarrett D'Amore };
109*95c635efSGarrett D'Amore 
110*95c635efSGarrett D'Amore static	void	 bufncat(struct html *, const char *, size_t);
111*95c635efSGarrett D'Amore static	void	 print_ctag(struct html *, enum htmltag);
112*95c635efSGarrett D'Amore static	int	 print_encode(struct html *, const char *, int);
113*95c635efSGarrett D'Amore static	void	 print_metaf(struct html *, enum mandoc_esc);
114*95c635efSGarrett D'Amore static	void	 print_attr(struct html *, const char *, const char *);
115*95c635efSGarrett D'Amore static	void	 *ml_alloc(char *, enum htmltype);
116*95c635efSGarrett D'Amore 
117*95c635efSGarrett D'Amore static void *
118*95c635efSGarrett D'Amore ml_alloc(char *outopts, enum htmltype type)
119*95c635efSGarrett D'Amore {
120*95c635efSGarrett D'Amore 	struct html	*h;
121*95c635efSGarrett D'Amore 	const char	*toks[5];
122*95c635efSGarrett D'Amore 	char		*v;
123*95c635efSGarrett D'Amore 
124*95c635efSGarrett D'Amore 	toks[0] = "style";
125*95c635efSGarrett D'Amore 	toks[1] = "man";
126*95c635efSGarrett D'Amore 	toks[2] = "includes";
127*95c635efSGarrett D'Amore 	toks[3] = "fragment";
128*95c635efSGarrett D'Amore 	toks[4] = NULL;
129*95c635efSGarrett D'Amore 
130*95c635efSGarrett D'Amore 	h = mandoc_calloc(1, sizeof(struct html));
131*95c635efSGarrett D'Amore 
132*95c635efSGarrett D'Amore 	h->type = type;
133*95c635efSGarrett D'Amore 	h->tags.head = NULL;
134*95c635efSGarrett D'Amore 	h->symtab = mchars_alloc();
135*95c635efSGarrett D'Amore 
136*95c635efSGarrett D'Amore 	while (outopts && *outopts)
137*95c635efSGarrett D'Amore 		switch (getsubopt(&outopts, UNCONST(toks), &v)) {
138*95c635efSGarrett D'Amore 		case (0):
139*95c635efSGarrett D'Amore 			h->style = v;
140*95c635efSGarrett D'Amore 			break;
141*95c635efSGarrett D'Amore 		case (1):
142*95c635efSGarrett D'Amore 			h->base_man = v;
143*95c635efSGarrett D'Amore 			break;
144*95c635efSGarrett D'Amore 		case (2):
145*95c635efSGarrett D'Amore 			h->base_includes = v;
146*95c635efSGarrett D'Amore 			break;
147*95c635efSGarrett D'Amore 		case (3):
148*95c635efSGarrett D'Amore 			h->oflags |= HTML_FRAGMENT;
149*95c635efSGarrett D'Amore 			break;
150*95c635efSGarrett D'Amore 		default:
151*95c635efSGarrett D'Amore 			break;
152*95c635efSGarrett D'Amore 		}
153*95c635efSGarrett D'Amore 
154*95c635efSGarrett D'Amore 	return(h);
155*95c635efSGarrett D'Amore }
156*95c635efSGarrett D'Amore 
157*95c635efSGarrett D'Amore void *
158*95c635efSGarrett D'Amore html_alloc(char *outopts)
159*95c635efSGarrett D'Amore {
160*95c635efSGarrett D'Amore 
161*95c635efSGarrett D'Amore 	return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
162*95c635efSGarrett D'Amore }
163*95c635efSGarrett D'Amore 
164*95c635efSGarrett D'Amore 
165*95c635efSGarrett D'Amore void *
166*95c635efSGarrett D'Amore xhtml_alloc(char *outopts)
167*95c635efSGarrett D'Amore {
168*95c635efSGarrett D'Amore 
169*95c635efSGarrett D'Amore 	return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
170*95c635efSGarrett D'Amore }
171*95c635efSGarrett D'Amore 
172*95c635efSGarrett D'Amore 
173*95c635efSGarrett D'Amore void
174*95c635efSGarrett D'Amore html_free(void *p)
175*95c635efSGarrett D'Amore {
176*95c635efSGarrett D'Amore 	struct tag	*tag;
177*95c635efSGarrett D'Amore 	struct html	*h;
178*95c635efSGarrett D'Amore 
179*95c635efSGarrett D'Amore 	h = (struct html *)p;
180*95c635efSGarrett D'Amore 
181*95c635efSGarrett D'Amore 	while ((tag = h->tags.head) != NULL) {
182*95c635efSGarrett D'Amore 		h->tags.head = tag->next;
183*95c635efSGarrett D'Amore 		free(tag);
184*95c635efSGarrett D'Amore 	}
185*95c635efSGarrett D'Amore 
186*95c635efSGarrett D'Amore 	if (h->symtab)
187*95c635efSGarrett D'Amore 		mchars_free(h->symtab);
188*95c635efSGarrett D'Amore 
189*95c635efSGarrett D'Amore 	free(h);
190*95c635efSGarrett D'Amore }
191*95c635efSGarrett D'Amore 
192*95c635efSGarrett D'Amore 
193*95c635efSGarrett D'Amore void
194*95c635efSGarrett D'Amore print_gen_head(struct html *h)
195*95c635efSGarrett D'Amore {
196*95c635efSGarrett D'Amore 	struct htmlpair	 tag[4];
197*95c635efSGarrett D'Amore 
198*95c635efSGarrett D'Amore 	tag[0].key = ATTR_HTTPEQUIV;
199*95c635efSGarrett D'Amore 	tag[0].val = "Content-Type";
200*95c635efSGarrett D'Amore 	tag[1].key = ATTR_CONTENT;
201*95c635efSGarrett D'Amore 	tag[1].val = "text/html; charset=utf-8";
202*95c635efSGarrett D'Amore 	print_otag(h, TAG_META, 2, tag);
203*95c635efSGarrett D'Amore 
204*95c635efSGarrett D'Amore 	tag[0].key = ATTR_NAME;
205*95c635efSGarrett D'Amore 	tag[0].val = "resource-type";
206*95c635efSGarrett D'Amore 	tag[1].key = ATTR_CONTENT;
207*95c635efSGarrett D'Amore 	tag[1].val = "document";
208*95c635efSGarrett D'Amore 	print_otag(h, TAG_META, 2, tag);
209*95c635efSGarrett D'Amore 
210*95c635efSGarrett D'Amore 	if (h->style) {
211*95c635efSGarrett D'Amore 		tag[0].key = ATTR_REL;
212*95c635efSGarrett D'Amore 		tag[0].val = "stylesheet";
213*95c635efSGarrett D'Amore 		tag[1].key = ATTR_HREF;
214*95c635efSGarrett D'Amore 		tag[1].val = h->style;
215*95c635efSGarrett D'Amore 		tag[2].key = ATTR_TYPE;
216*95c635efSGarrett D'Amore 		tag[2].val = "text/css";
217*95c635efSGarrett D'Amore 		tag[3].key = ATTR_MEDIA;
218*95c635efSGarrett D'Amore 		tag[3].val = "all";
219*95c635efSGarrett D'Amore 		print_otag(h, TAG_LINK, 4, tag);
220*95c635efSGarrett D'Amore 	}
221*95c635efSGarrett D'Amore }
222*95c635efSGarrett D'Amore 
223*95c635efSGarrett D'Amore static void
224*95c635efSGarrett D'Amore print_metaf(struct html *h, enum mandoc_esc deco)
225*95c635efSGarrett D'Amore {
226*95c635efSGarrett D'Amore 	enum htmlfont	 font;
227*95c635efSGarrett D'Amore 
228*95c635efSGarrett D'Amore 	switch (deco) {
229*95c635efSGarrett D'Amore 	case (ESCAPE_FONTPREV):
230*95c635efSGarrett D'Amore 		font = h->metal;
231*95c635efSGarrett D'Amore 		break;
232*95c635efSGarrett D'Amore 	case (ESCAPE_FONTITALIC):
233*95c635efSGarrett D'Amore 		font = HTMLFONT_ITALIC;
234*95c635efSGarrett D'Amore 		break;
235*95c635efSGarrett D'Amore 	case (ESCAPE_FONTBOLD):
236*95c635efSGarrett D'Amore 		font = HTMLFONT_BOLD;
237*95c635efSGarrett D'Amore 		break;
238*95c635efSGarrett D'Amore 	case (ESCAPE_FONT):
239*95c635efSGarrett D'Amore 		/* FALLTHROUGH */
240*95c635efSGarrett D'Amore 	case (ESCAPE_FONTROMAN):
241*95c635efSGarrett D'Amore 		font = HTMLFONT_NONE;
242*95c635efSGarrett D'Amore 		break;
243*95c635efSGarrett D'Amore 	default:
244*95c635efSGarrett D'Amore 		abort();
245*95c635efSGarrett D'Amore 		/* NOTREACHED */
246*95c635efSGarrett D'Amore 	}
247*95c635efSGarrett D'Amore 
248*95c635efSGarrett D'Amore 	if (h->metaf) {
249*95c635efSGarrett D'Amore 		print_tagq(h, h->metaf);
250*95c635efSGarrett D'Amore 		h->metaf = NULL;
251*95c635efSGarrett D'Amore 	}
252*95c635efSGarrett D'Amore 
253*95c635efSGarrett D'Amore 	h->metal = h->metac;
254*95c635efSGarrett D'Amore 	h->metac = font;
255*95c635efSGarrett D'Amore 
256*95c635efSGarrett D'Amore 	if (HTMLFONT_NONE != font)
257*95c635efSGarrett D'Amore 		h->metaf = HTMLFONT_BOLD == font ?
258*95c635efSGarrett D'Amore 			print_otag(h, TAG_B, 0, NULL) :
259*95c635efSGarrett D'Amore 			print_otag(h, TAG_I, 0, NULL);
260*95c635efSGarrett D'Amore }
261*95c635efSGarrett D'Amore 
262*95c635efSGarrett D'Amore int
263*95c635efSGarrett D'Amore html_strlen(const char *cp)
264*95c635efSGarrett D'Amore {
265*95c635efSGarrett D'Amore 	int		 ssz, sz;
266*95c635efSGarrett D'Amore 	const char	*seq, *p;
267*95c635efSGarrett D'Amore 
268*95c635efSGarrett D'Amore 	/*
269*95c635efSGarrett D'Amore 	 * Account for escaped sequences within string length
270*95c635efSGarrett D'Amore 	 * calculations.  This follows the logic in term_strlen() as we
271*95c635efSGarrett D'Amore 	 * must calculate the width of produced strings.
272*95c635efSGarrett D'Amore 	 * Assume that characters are always width of "1".  This is
273*95c635efSGarrett D'Amore 	 * hacky, but it gets the job done for approximation of widths.
274*95c635efSGarrett D'Amore 	 */
275*95c635efSGarrett D'Amore 
276*95c635efSGarrett D'Amore 	sz = 0;
277*95c635efSGarrett D'Amore 	while (NULL != (p = strchr(cp, '\\'))) {
278*95c635efSGarrett D'Amore 		sz += (int)(p - cp);
279*95c635efSGarrett D'Amore 		++cp;
280*95c635efSGarrett D'Amore 		switch (mandoc_escape(&cp, &seq, &ssz)) {
281*95c635efSGarrett D'Amore 		case (ESCAPE_ERROR):
282*95c635efSGarrett D'Amore 			return(sz);
283*95c635efSGarrett D'Amore 		case (ESCAPE_UNICODE):
284*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
285*95c635efSGarrett D'Amore 		case (ESCAPE_NUMBERED):
286*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
287*95c635efSGarrett D'Amore 		case (ESCAPE_SPECIAL):
288*95c635efSGarrett D'Amore 			sz++;
289*95c635efSGarrett D'Amore 			break;
290*95c635efSGarrett D'Amore 		default:
291*95c635efSGarrett D'Amore 			break;
292*95c635efSGarrett D'Amore 		}
293*95c635efSGarrett D'Amore 	}
294*95c635efSGarrett D'Amore 
295*95c635efSGarrett D'Amore 	assert(sz >= 0);
296*95c635efSGarrett D'Amore 	return(sz + strlen(cp));
297*95c635efSGarrett D'Amore }
298*95c635efSGarrett D'Amore 
299*95c635efSGarrett D'Amore static int
300*95c635efSGarrett D'Amore print_encode(struct html *h, const char *p, int norecurse)
301*95c635efSGarrett D'Amore {
302*95c635efSGarrett D'Amore 	size_t		 sz;
303*95c635efSGarrett D'Amore 	int		 c, len, nospace;
304*95c635efSGarrett D'Amore 	const char	*seq;
305*95c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
306*95c635efSGarrett D'Amore 	static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
307*95c635efSGarrett D'Amore 
308*95c635efSGarrett D'Amore 	nospace = 0;
309*95c635efSGarrett D'Amore 
310*95c635efSGarrett D'Amore 	while ('\0' != *p) {
311*95c635efSGarrett D'Amore 		sz = strcspn(p, rejs);
312*95c635efSGarrett D'Amore 
313*95c635efSGarrett D'Amore 		fwrite(p, 1, sz, stdout);
314*95c635efSGarrett D'Amore 		p += (int)sz;
315*95c635efSGarrett D'Amore 
316*95c635efSGarrett D'Amore 		if ('\0' == *p)
317*95c635efSGarrett D'Amore 			break;
318*95c635efSGarrett D'Amore 
319*95c635efSGarrett D'Amore 		switch (*p++) {
320*95c635efSGarrett D'Amore 		case ('<'):
321*95c635efSGarrett D'Amore 			printf("&lt;");
322*95c635efSGarrett D'Amore 			continue;
323*95c635efSGarrett D'Amore 		case ('>'):
324*95c635efSGarrett D'Amore 			printf("&gt;");
325*95c635efSGarrett D'Amore 			continue;
326*95c635efSGarrett D'Amore 		case ('&'):
327*95c635efSGarrett D'Amore 			printf("&amp;");
328*95c635efSGarrett D'Amore 			continue;
329*95c635efSGarrett D'Amore 		case (ASCII_HYPH):
330*95c635efSGarrett D'Amore 			putchar('-');
331*95c635efSGarrett D'Amore 			continue;
332*95c635efSGarrett D'Amore 		default:
333*95c635efSGarrett D'Amore 			break;
334*95c635efSGarrett D'Amore 		}
335*95c635efSGarrett D'Amore 
336*95c635efSGarrett D'Amore 		esc = mandoc_escape(&p, &seq, &len);
337*95c635efSGarrett D'Amore 		if (ESCAPE_ERROR == esc)
338*95c635efSGarrett D'Amore 			break;
339*95c635efSGarrett D'Amore 
340*95c635efSGarrett D'Amore 		switch (esc) {
341*95c635efSGarrett D'Amore 		case (ESCAPE_UNICODE):
342*95c635efSGarrett D'Amore 			/* Skip passed "u" header. */
343*95c635efSGarrett D'Amore 			c = mchars_num2uc(seq + 1, len - 1);
344*95c635efSGarrett D'Amore 			if ('\0' != c)
345*95c635efSGarrett D'Amore 				printf("&#x%x;", c);
346*95c635efSGarrett D'Amore 			break;
347*95c635efSGarrett D'Amore 		case (ESCAPE_NUMBERED):
348*95c635efSGarrett D'Amore 			c = mchars_num2char(seq, len);
349*95c635efSGarrett D'Amore 			if ('\0' != c)
350*95c635efSGarrett D'Amore 				putchar(c);
351*95c635efSGarrett D'Amore 			break;
352*95c635efSGarrett D'Amore 		case (ESCAPE_SPECIAL):
353*95c635efSGarrett D'Amore 			c = mchars_spec2cp(h->symtab, seq, len);
354*95c635efSGarrett D'Amore 			if (c > 0)
355*95c635efSGarrett D'Amore 				printf("&#%d;", c);
356*95c635efSGarrett D'Amore 			else if (-1 == c && 1 == len)
357*95c635efSGarrett D'Amore 				putchar((int)*seq);
358*95c635efSGarrett D'Amore 			break;
359*95c635efSGarrett D'Amore 		case (ESCAPE_FONT):
360*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
361*95c635efSGarrett D'Amore 		case (ESCAPE_FONTPREV):
362*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
363*95c635efSGarrett D'Amore 		case (ESCAPE_FONTBOLD):
364*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
365*95c635efSGarrett D'Amore 		case (ESCAPE_FONTITALIC):
366*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
367*95c635efSGarrett D'Amore 		case (ESCAPE_FONTROMAN):
368*95c635efSGarrett D'Amore 			if (norecurse)
369*95c635efSGarrett D'Amore 				break;
370*95c635efSGarrett D'Amore 			print_metaf(h, esc);
371*95c635efSGarrett D'Amore 			break;
372*95c635efSGarrett D'Amore 		case (ESCAPE_NOSPACE):
373*95c635efSGarrett D'Amore 			if ('\0' == *p)
374*95c635efSGarrett D'Amore 				nospace = 1;
375*95c635efSGarrett D'Amore 			break;
376*95c635efSGarrett D'Amore 		default:
377*95c635efSGarrett D'Amore 			break;
378*95c635efSGarrett D'Amore 		}
379*95c635efSGarrett D'Amore 	}
380*95c635efSGarrett D'Amore 
381*95c635efSGarrett D'Amore 	return(nospace);
382*95c635efSGarrett D'Amore }
383*95c635efSGarrett D'Amore 
384*95c635efSGarrett D'Amore 
385*95c635efSGarrett D'Amore static void
386*95c635efSGarrett D'Amore print_attr(struct html *h, const char *key, const char *val)
387*95c635efSGarrett D'Amore {
388*95c635efSGarrett D'Amore 	printf(" %s=\"", key);
389*95c635efSGarrett D'Amore 	(void)print_encode(h, val, 1);
390*95c635efSGarrett D'Amore 	putchar('\"');
391*95c635efSGarrett D'Amore }
392*95c635efSGarrett D'Amore 
393*95c635efSGarrett D'Amore 
394*95c635efSGarrett D'Amore struct tag *
395*95c635efSGarrett D'Amore print_otag(struct html *h, enum htmltag tag,
396*95c635efSGarrett D'Amore 		int sz, const struct htmlpair *p)
397*95c635efSGarrett D'Amore {
398*95c635efSGarrett D'Amore 	int		 i;
399*95c635efSGarrett D'Amore 	struct tag	*t;
400*95c635efSGarrett D'Amore 
401*95c635efSGarrett D'Amore 	/* Push this tags onto the stack of open scopes. */
402*95c635efSGarrett D'Amore 
403*95c635efSGarrett D'Amore 	if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
404*95c635efSGarrett D'Amore 		t = mandoc_malloc(sizeof(struct tag));
405*95c635efSGarrett D'Amore 		t->tag = tag;
406*95c635efSGarrett D'Amore 		t->next = h->tags.head;
407*95c635efSGarrett D'Amore 		h->tags.head = t;
408*95c635efSGarrett D'Amore 	} else
409*95c635efSGarrett D'Amore 		t = NULL;
410*95c635efSGarrett D'Amore 
411*95c635efSGarrett D'Amore 	if ( ! (HTML_NOSPACE & h->flags))
412*95c635efSGarrett D'Amore 		if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
413*95c635efSGarrett D'Amore 			/* Manage keeps! */
414*95c635efSGarrett D'Amore 			if ( ! (HTML_KEEP & h->flags)) {
415*95c635efSGarrett D'Amore 				if (HTML_PREKEEP & h->flags)
416*95c635efSGarrett D'Amore 					h->flags |= HTML_KEEP;
417*95c635efSGarrett D'Amore 				putchar(' ');
418*95c635efSGarrett D'Amore 			} else
419*95c635efSGarrett D'Amore 				printf("&#160;");
420*95c635efSGarrett D'Amore 		}
421*95c635efSGarrett D'Amore 
422*95c635efSGarrett D'Amore 	if ( ! (h->flags & HTML_NONOSPACE))
423*95c635efSGarrett D'Amore 		h->flags &= ~HTML_NOSPACE;
424*95c635efSGarrett D'Amore 	else
425*95c635efSGarrett D'Amore 		h->flags |= HTML_NOSPACE;
426*95c635efSGarrett D'Amore 
427*95c635efSGarrett D'Amore 	/* Print out the tag name and attributes. */
428*95c635efSGarrett D'Amore 
429*95c635efSGarrett D'Amore 	printf("<%s", htmltags[tag].name);
430*95c635efSGarrett D'Amore 	for (i = 0; i < sz; i++)
431*95c635efSGarrett D'Amore 		print_attr(h, htmlattrs[p[i].key], p[i].val);
432*95c635efSGarrett D'Amore 
433*95c635efSGarrett D'Amore 	/* Add non-overridable attributes. */
434*95c635efSGarrett D'Amore 
435*95c635efSGarrett D'Amore 	if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
436*95c635efSGarrett D'Amore 		print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
437*95c635efSGarrett D'Amore 		print_attr(h, "xml:lang", "en");
438*95c635efSGarrett D'Amore 		print_attr(h, "lang", "en");
439*95c635efSGarrett D'Amore 	}
440*95c635efSGarrett D'Amore 
441*95c635efSGarrett D'Amore 	/* Accommodate for XML "well-formed" singleton escaping. */
442*95c635efSGarrett D'Amore 
443*95c635efSGarrett D'Amore 	if (HTML_AUTOCLOSE & htmltags[tag].flags)
444*95c635efSGarrett D'Amore 		switch (h->type) {
445*95c635efSGarrett D'Amore 		case (HTML_XHTML_1_0_STRICT):
446*95c635efSGarrett D'Amore 			putchar('/');
447*95c635efSGarrett D'Amore 			break;
448*95c635efSGarrett D'Amore 		default:
449*95c635efSGarrett D'Amore 			break;
450*95c635efSGarrett D'Amore 		}
451*95c635efSGarrett D'Amore 
452*95c635efSGarrett D'Amore 	putchar('>');
453*95c635efSGarrett D'Amore 
454*95c635efSGarrett D'Amore 	h->flags |= HTML_NOSPACE;
455*95c635efSGarrett D'Amore 
456*95c635efSGarrett D'Amore 	if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
457*95c635efSGarrett D'Amore 		putchar('\n');
458*95c635efSGarrett D'Amore 
459*95c635efSGarrett D'Amore 	return(t);
460*95c635efSGarrett D'Amore }
461*95c635efSGarrett D'Amore 
462*95c635efSGarrett D'Amore 
463*95c635efSGarrett D'Amore static void
464*95c635efSGarrett D'Amore print_ctag(struct html *h, enum htmltag tag)
465*95c635efSGarrett D'Amore {
466*95c635efSGarrett D'Amore 
467*95c635efSGarrett D'Amore 	printf("</%s>", htmltags[tag].name);
468*95c635efSGarrett D'Amore 	if (HTML_CLRLINE & htmltags[tag].flags) {
469*95c635efSGarrett D'Amore 		h->flags |= HTML_NOSPACE;
470*95c635efSGarrett D'Amore 		putchar('\n');
471*95c635efSGarrett D'Amore 	}
472*95c635efSGarrett D'Amore }
473*95c635efSGarrett D'Amore 
474*95c635efSGarrett D'Amore void
475*95c635efSGarrett D'Amore print_gen_decls(struct html *h)
476*95c635efSGarrett D'Amore {
477*95c635efSGarrett D'Amore 	const char	*doctype;
478*95c635efSGarrett D'Amore 	const char	*dtd;
479*95c635efSGarrett D'Amore 	const char	*name;
480*95c635efSGarrett D'Amore 
481*95c635efSGarrett D'Amore 	switch (h->type) {
482*95c635efSGarrett D'Amore 	case (HTML_HTML_4_01_STRICT):
483*95c635efSGarrett D'Amore 		name = "HTML";
484*95c635efSGarrett D'Amore 		doctype = "-//W3C//DTD HTML 4.01//EN";
485*95c635efSGarrett D'Amore 		dtd = "http://www.w3.org/TR/html4/strict.dtd";
486*95c635efSGarrett D'Amore 		break;
487*95c635efSGarrett D'Amore 	default:
488*95c635efSGarrett D'Amore 		puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
489*95c635efSGarrett D'Amore 		name = "html";
490*95c635efSGarrett D'Amore 		doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
491*95c635efSGarrett D'Amore 		dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
492*95c635efSGarrett D'Amore 		break;
493*95c635efSGarrett D'Amore 	}
494*95c635efSGarrett D'Amore 
495*95c635efSGarrett D'Amore 	printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
496*95c635efSGarrett D'Amore 			name, doctype, dtd);
497*95c635efSGarrett D'Amore }
498*95c635efSGarrett D'Amore 
499*95c635efSGarrett D'Amore void
500*95c635efSGarrett D'Amore print_text(struct html *h, const char *word)
501*95c635efSGarrett D'Amore {
502*95c635efSGarrett D'Amore 
503*95c635efSGarrett D'Amore 	if ( ! (HTML_NOSPACE & h->flags)) {
504*95c635efSGarrett D'Amore 		/* Manage keeps! */
505*95c635efSGarrett D'Amore 		if ( ! (HTML_KEEP & h->flags)) {
506*95c635efSGarrett D'Amore 			if (HTML_PREKEEP & h->flags)
507*95c635efSGarrett D'Amore 				h->flags |= HTML_KEEP;
508*95c635efSGarrett D'Amore 			putchar(' ');
509*95c635efSGarrett D'Amore 		} else
510*95c635efSGarrett D'Amore 			printf("&#160;");
511*95c635efSGarrett D'Amore 	}
512*95c635efSGarrett D'Amore 
513*95c635efSGarrett D'Amore 	assert(NULL == h->metaf);
514*95c635efSGarrett D'Amore 	if (HTMLFONT_NONE != h->metac)
515*95c635efSGarrett D'Amore 		h->metaf = HTMLFONT_BOLD == h->metac ?
516*95c635efSGarrett D'Amore 			print_otag(h, TAG_B, 0, NULL) :
517*95c635efSGarrett D'Amore 			print_otag(h, TAG_I, 0, NULL);
518*95c635efSGarrett D'Amore 
519*95c635efSGarrett D'Amore 	assert(word);
520*95c635efSGarrett D'Amore 	if ( ! print_encode(h, word, 0)) {
521*95c635efSGarrett D'Amore 		if ( ! (h->flags & HTML_NONOSPACE))
522*95c635efSGarrett D'Amore 			h->flags &= ~HTML_NOSPACE;
523*95c635efSGarrett D'Amore 	} else
524*95c635efSGarrett D'Amore 		h->flags |= HTML_NOSPACE;
525*95c635efSGarrett D'Amore 
526*95c635efSGarrett D'Amore 	if (h->metaf) {
527*95c635efSGarrett D'Amore 		print_tagq(h, h->metaf);
528*95c635efSGarrett D'Amore 		h->metaf = NULL;
529*95c635efSGarrett D'Amore 	}
530*95c635efSGarrett D'Amore 
531*95c635efSGarrett D'Amore 	h->flags &= ~HTML_IGNDELIM;
532*95c635efSGarrett D'Amore }
533*95c635efSGarrett D'Amore 
534*95c635efSGarrett D'Amore 
535*95c635efSGarrett D'Amore void
536*95c635efSGarrett D'Amore print_tagq(struct html *h, const struct tag *until)
537*95c635efSGarrett D'Amore {
538*95c635efSGarrett D'Amore 	struct tag	*tag;
539*95c635efSGarrett D'Amore 
540*95c635efSGarrett D'Amore 	while ((tag = h->tags.head) != NULL) {
541*95c635efSGarrett D'Amore 		/*
542*95c635efSGarrett D'Amore 		 * Remember to close out and nullify the current
543*95c635efSGarrett D'Amore 		 * meta-font and table, if applicable.
544*95c635efSGarrett D'Amore 		 */
545*95c635efSGarrett D'Amore 		if (tag == h->metaf)
546*95c635efSGarrett D'Amore 			h->metaf = NULL;
547*95c635efSGarrett D'Amore 		if (tag == h->tblt)
548*95c635efSGarrett D'Amore 			h->tblt = NULL;
549*95c635efSGarrett D'Amore 		print_ctag(h, tag->tag);
550*95c635efSGarrett D'Amore 		h->tags.head = tag->next;
551*95c635efSGarrett D'Amore 		free(tag);
552*95c635efSGarrett D'Amore 		if (until && tag == until)
553*95c635efSGarrett D'Amore 			return;
554*95c635efSGarrett D'Amore 	}
555*95c635efSGarrett D'Amore }
556*95c635efSGarrett D'Amore 
557*95c635efSGarrett D'Amore 
558*95c635efSGarrett D'Amore void
559*95c635efSGarrett D'Amore print_stagq(struct html *h, const struct tag *suntil)
560*95c635efSGarrett D'Amore {
561*95c635efSGarrett D'Amore 	struct tag	*tag;
562*95c635efSGarrett D'Amore 
563*95c635efSGarrett D'Amore 	while ((tag = h->tags.head) != NULL) {
564*95c635efSGarrett D'Amore 		if (suntil && tag == suntil)
565*95c635efSGarrett D'Amore 			return;
566*95c635efSGarrett D'Amore 		/*
567*95c635efSGarrett D'Amore 		 * Remember to close out and nullify the current
568*95c635efSGarrett D'Amore 		 * meta-font and table, if applicable.
569*95c635efSGarrett D'Amore 		 */
570*95c635efSGarrett D'Amore 		if (tag == h->metaf)
571*95c635efSGarrett D'Amore 			h->metaf = NULL;
572*95c635efSGarrett D'Amore 		if (tag == h->tblt)
573*95c635efSGarrett D'Amore 			h->tblt = NULL;
574*95c635efSGarrett D'Amore 		print_ctag(h, tag->tag);
575*95c635efSGarrett D'Amore 		h->tags.head = tag->next;
576*95c635efSGarrett D'Amore 		free(tag);
577*95c635efSGarrett D'Amore 	}
578*95c635efSGarrett D'Amore }
579*95c635efSGarrett D'Amore 
580*95c635efSGarrett D'Amore void
581*95c635efSGarrett D'Amore bufinit(struct html *h)
582*95c635efSGarrett D'Amore {
583*95c635efSGarrett D'Amore 
584*95c635efSGarrett D'Amore 	h->buf[0] = '\0';
585*95c635efSGarrett D'Amore 	h->buflen = 0;
586*95c635efSGarrett D'Amore }
587*95c635efSGarrett D'Amore 
588*95c635efSGarrett D'Amore void
589*95c635efSGarrett D'Amore bufcat_style(struct html *h, const char *key, const char *val)
590*95c635efSGarrett D'Amore {
591*95c635efSGarrett D'Amore 
592*95c635efSGarrett D'Amore 	bufcat(h, key);
593*95c635efSGarrett D'Amore 	bufcat(h, ":");
594*95c635efSGarrett D'Amore 	bufcat(h, val);
595*95c635efSGarrett D'Amore 	bufcat(h, ";");
596*95c635efSGarrett D'Amore }
597*95c635efSGarrett D'Amore 
598*95c635efSGarrett D'Amore void
599*95c635efSGarrett D'Amore bufcat(struct html *h, const char *p)
600*95c635efSGarrett D'Amore {
601*95c635efSGarrett D'Amore 
602*95c635efSGarrett D'Amore 	h->buflen = strlcat(h->buf, p, BUFSIZ);
603*95c635efSGarrett D'Amore 	assert(h->buflen < BUFSIZ);
604*95c635efSGarrett D'Amore }
605*95c635efSGarrett D'Amore 
606*95c635efSGarrett D'Amore void
607*95c635efSGarrett D'Amore bufcat_fmt(struct html *h, const char *fmt, ...)
608*95c635efSGarrett D'Amore {
609*95c635efSGarrett D'Amore 	va_list		 ap;
610*95c635efSGarrett D'Amore 
611*95c635efSGarrett D'Amore 	va_start(ap, fmt);
612*95c635efSGarrett D'Amore 	(void)vsnprintf(h->buf + (int)h->buflen,
613*95c635efSGarrett D'Amore 			BUFSIZ - h->buflen - 1, fmt, ap);
614*95c635efSGarrett D'Amore 	va_end(ap);
615*95c635efSGarrett D'Amore 	h->buflen = strlen(h->buf);
616*95c635efSGarrett D'Amore }
617*95c635efSGarrett D'Amore 
618*95c635efSGarrett D'Amore static void
619*95c635efSGarrett D'Amore bufncat(struct html *h, const char *p, size_t sz)
620*95c635efSGarrett D'Amore {
621*95c635efSGarrett D'Amore 
622*95c635efSGarrett D'Amore 	assert(h->buflen + sz + 1 < BUFSIZ);
623*95c635efSGarrett D'Amore 	strncat(h->buf, p, sz);
624*95c635efSGarrett D'Amore 	h->buflen += sz;
625*95c635efSGarrett D'Amore }
626*95c635efSGarrett D'Amore 
627*95c635efSGarrett D'Amore void
628*95c635efSGarrett D'Amore buffmt_includes(struct html *h, const char *name)
629*95c635efSGarrett D'Amore {
630*95c635efSGarrett D'Amore 	const char	*p, *pp;
631*95c635efSGarrett D'Amore 
632*95c635efSGarrett D'Amore 	pp = h->base_includes;
633*95c635efSGarrett D'Amore 
634*95c635efSGarrett D'Amore 	bufinit(h);
635*95c635efSGarrett D'Amore 	while (NULL != (p = strchr(pp, '%'))) {
636*95c635efSGarrett D'Amore 		bufncat(h, pp, (size_t)(p - pp));
637*95c635efSGarrett D'Amore 		switch (*(p + 1)) {
638*95c635efSGarrett D'Amore 		case('I'):
639*95c635efSGarrett D'Amore 			bufcat(h, name);
640*95c635efSGarrett D'Amore 			break;
641*95c635efSGarrett D'Amore 		default:
642*95c635efSGarrett D'Amore 			bufncat(h, p, 2);
643*95c635efSGarrett D'Amore 			break;
644*95c635efSGarrett D'Amore 		}
645*95c635efSGarrett D'Amore 		pp = p + 2;
646*95c635efSGarrett D'Amore 	}
647*95c635efSGarrett D'Amore 	if (pp)
648*95c635efSGarrett D'Amore 		bufcat(h, pp);
649*95c635efSGarrett D'Amore }
650*95c635efSGarrett D'Amore 
651*95c635efSGarrett D'Amore void
652*95c635efSGarrett D'Amore buffmt_man(struct html *h,
653*95c635efSGarrett D'Amore 		const char *name, const char *sec)
654*95c635efSGarrett D'Amore {
655*95c635efSGarrett D'Amore 	const char	*p, *pp;
656*95c635efSGarrett D'Amore 
657*95c635efSGarrett D'Amore 	pp = h->base_man;
658*95c635efSGarrett D'Amore 
659*95c635efSGarrett D'Amore 	bufinit(h);
660*95c635efSGarrett D'Amore 	while (NULL != (p = strchr(pp, '%'))) {
661*95c635efSGarrett D'Amore 		bufncat(h, pp, (size_t)(p - pp));
662*95c635efSGarrett D'Amore 		switch (*(p + 1)) {
663*95c635efSGarrett D'Amore 		case('S'):
664*95c635efSGarrett D'Amore 			bufcat(h, sec ? sec : "1");
665*95c635efSGarrett D'Amore 			break;
666*95c635efSGarrett D'Amore 		case('N'):
667*95c635efSGarrett D'Amore 			bufcat_fmt(h, name);
668*95c635efSGarrett D'Amore 			break;
669*95c635efSGarrett D'Amore 		default:
670*95c635efSGarrett D'Amore 			bufncat(h, p, 2);
671*95c635efSGarrett D'Amore 			break;
672*95c635efSGarrett D'Amore 		}
673*95c635efSGarrett D'Amore 		pp = p + 2;
674*95c635efSGarrett D'Amore 	}
675*95c635efSGarrett D'Amore 	if (pp)
676*95c635efSGarrett D'Amore 		bufcat(h, pp);
677*95c635efSGarrett D'Amore }
678*95c635efSGarrett D'Amore 
679*95c635efSGarrett D'Amore void
680*95c635efSGarrett D'Amore bufcat_su(struct html *h, const char *p, const struct roffsu *su)
681*95c635efSGarrett D'Amore {
682*95c635efSGarrett D'Amore 	double		 v;
683*95c635efSGarrett D'Amore 
684*95c635efSGarrett D'Amore 	v = su->scale;
685*95c635efSGarrett D'Amore 	if (SCALE_MM == su->unit && 0.0 == (v /= 100.0))
686*95c635efSGarrett D'Amore 		v = 1.0;
687*95c635efSGarrett D'Amore 
688*95c635efSGarrett D'Amore 	bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]);
689*95c635efSGarrett D'Amore }
690*95c635efSGarrett D'Amore 
691*95c635efSGarrett D'Amore void
692*95c635efSGarrett D'Amore bufcat_id(struct html *h, const char *src)
693*95c635efSGarrett D'Amore {
694*95c635efSGarrett D'Amore 
695*95c635efSGarrett D'Amore 	/* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
696*95c635efSGarrett D'Amore 
697*95c635efSGarrett D'Amore 	while ('\0' != *src)
698*95c635efSGarrett D'Amore 		bufcat_fmt(h, "%.2x", *src++);
699*95c635efSGarrett D'Amore }
700