1322249Sbapt/*	$Id: mdoc_html.c,v 1.294 2017/07/15 17:57:51 schwarze Exp $ */
2241675Suqs/*
3275432Sbapt * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4316420Sbapt * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10294113Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12294113Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#include "config.h"
19241675Suqs
20241675Suqs#include <sys/types.h>
21241675Suqs
22241675Suqs#include <assert.h>
23241675Suqs#include <ctype.h>
24241675Suqs#include <stdio.h>
25241675Suqs#include <stdlib.h>
26241675Suqs#include <string.h>
27241675Suqs#include <unistd.h>
28241675Suqs
29274880Sbapt#include "mandoc_aux.h"
30322249Sbapt#include "mandoc.h"
31294113Sbapt#include "roff.h"
32276219Sbapt#include "mdoc.h"
33241675Suqs#include "out.h"
34241675Suqs#include "html.h"
35241675Suqs#include "main.h"
36241675Suqs
37241675Suqs#define	INDENT		 5
38241675Suqs
39294113Sbapt#define	MDOC_ARGS	  const struct roff_meta *meta, \
40294113Sbapt			  struct roff_node *n, \
41241675Suqs			  struct html *h
42241675Suqs
43241675Suqs#ifndef MIN
44241675Suqs#define	MIN(a,b)	((/*CONSTCOND*/(a)<(b))?(a):(b))
45241675Suqs#endif
46241675Suqs
47241675Suqsstruct	htmlmdoc {
48241675Suqs	int		(*pre)(MDOC_ARGS);
49241675Suqs	void		(*post)(MDOC_ARGS);
50241675Suqs};
51241675Suqs
52322249Sbaptstatic	char		 *cond_id(const struct roff_node *);
53241675Suqsstatic	void		  print_mdoc_head(MDOC_ARGS);
54241675Suqsstatic	void		  print_mdoc_node(MDOC_ARGS);
55241675Suqsstatic	void		  print_mdoc_nodelist(MDOC_ARGS);
56274880Sbaptstatic	void		  synopsis_pre(struct html *,
57294113Sbapt				const struct roff_node *);
58241675Suqs
59241675Suqsstatic	void		  mdoc_root_post(MDOC_ARGS);
60241675Suqsstatic	int		  mdoc_root_pre(MDOC_ARGS);
61241675Suqs
62241675Suqsstatic	void		  mdoc__x_post(MDOC_ARGS);
63241675Suqsstatic	int		  mdoc__x_pre(MDOC_ARGS);
64241675Suqsstatic	int		  mdoc_ad_pre(MDOC_ARGS);
65241675Suqsstatic	int		  mdoc_an_pre(MDOC_ARGS);
66241675Suqsstatic	int		  mdoc_ap_pre(MDOC_ARGS);
67241675Suqsstatic	int		  mdoc_ar_pre(MDOC_ARGS);
68241675Suqsstatic	int		  mdoc_bd_pre(MDOC_ARGS);
69241675Suqsstatic	int		  mdoc_bf_pre(MDOC_ARGS);
70241675Suqsstatic	void		  mdoc_bk_post(MDOC_ARGS);
71241675Suqsstatic	int		  mdoc_bk_pre(MDOC_ARGS);
72241675Suqsstatic	int		  mdoc_bl_pre(MDOC_ARGS);
73241675Suqsstatic	int		  mdoc_cd_pre(MDOC_ARGS);
74316420Sbaptstatic	int		  mdoc_cm_pre(MDOC_ARGS);
75241675Suqsstatic	int		  mdoc_d1_pre(MDOC_ARGS);
76241675Suqsstatic	int		  mdoc_dv_pre(MDOC_ARGS);
77241675Suqsstatic	int		  mdoc_fa_pre(MDOC_ARGS);
78241675Suqsstatic	int		  mdoc_fd_pre(MDOC_ARGS);
79241675Suqsstatic	int		  mdoc_fl_pre(MDOC_ARGS);
80241675Suqsstatic	int		  mdoc_fn_pre(MDOC_ARGS);
81241675Suqsstatic	int		  mdoc_ft_pre(MDOC_ARGS);
82241675Suqsstatic	int		  mdoc_em_pre(MDOC_ARGS);
83279527Sbaptstatic	void		  mdoc_eo_post(MDOC_ARGS);
84279527Sbaptstatic	int		  mdoc_eo_pre(MDOC_ARGS);
85241675Suqsstatic	int		  mdoc_er_pre(MDOC_ARGS);
86241675Suqsstatic	int		  mdoc_ev_pre(MDOC_ARGS);
87241675Suqsstatic	int		  mdoc_ex_pre(MDOC_ARGS);
88241675Suqsstatic	void		  mdoc_fo_post(MDOC_ARGS);
89241675Suqsstatic	int		  mdoc_fo_pre(MDOC_ARGS);
90241675Suqsstatic	int		  mdoc_ic_pre(MDOC_ARGS);
91241675Suqsstatic	int		  mdoc_igndelim_pre(MDOC_ARGS);
92241675Suqsstatic	int		  mdoc_in_pre(MDOC_ARGS);
93241675Suqsstatic	int		  mdoc_it_pre(MDOC_ARGS);
94241675Suqsstatic	int		  mdoc_lb_pre(MDOC_ARGS);
95241675Suqsstatic	int		  mdoc_li_pre(MDOC_ARGS);
96241675Suqsstatic	int		  mdoc_lk_pre(MDOC_ARGS);
97241675Suqsstatic	int		  mdoc_mt_pre(MDOC_ARGS);
98241675Suqsstatic	int		  mdoc_ms_pre(MDOC_ARGS);
99241675Suqsstatic	int		  mdoc_nd_pre(MDOC_ARGS);
100241675Suqsstatic	int		  mdoc_nm_pre(MDOC_ARGS);
101275432Sbaptstatic	int		  mdoc_no_pre(MDOC_ARGS);
102241675Suqsstatic	int		  mdoc_ns_pre(MDOC_ARGS);
103241675Suqsstatic	int		  mdoc_pa_pre(MDOC_ARGS);
104241675Suqsstatic	void		  mdoc_pf_post(MDOC_ARGS);
105241675Suqsstatic	int		  mdoc_pp_pre(MDOC_ARGS);
106241675Suqsstatic	void		  mdoc_quote_post(MDOC_ARGS);
107241675Suqsstatic	int		  mdoc_quote_pre(MDOC_ARGS);
108241675Suqsstatic	int		  mdoc_rs_pre(MDOC_ARGS);
109241675Suqsstatic	int		  mdoc_sh_pre(MDOC_ARGS);
110274880Sbaptstatic	int		  mdoc_skip_pre(MDOC_ARGS);
111241675Suqsstatic	int		  mdoc_sm_pre(MDOC_ARGS);
112241675Suqsstatic	int		  mdoc_ss_pre(MDOC_ARGS);
113322249Sbaptstatic	int		  mdoc_st_pre(MDOC_ARGS);
114241675Suqsstatic	int		  mdoc_sx_pre(MDOC_ARGS);
115241675Suqsstatic	int		  mdoc_sy_pre(MDOC_ARGS);
116241675Suqsstatic	int		  mdoc_va_pre(MDOC_ARGS);
117241675Suqsstatic	int		  mdoc_vt_pre(MDOC_ARGS);
118241675Suqsstatic	int		  mdoc_xr_pre(MDOC_ARGS);
119241675Suqsstatic	int		  mdoc_xx_pre(MDOC_ARGS);
120241675Suqs
121322249Sbaptstatic	const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = {
122241675Suqs	{NULL, NULL}, /* Dd */
123241675Suqs	{NULL, NULL}, /* Dt */
124241675Suqs	{NULL, NULL}, /* Os */
125241675Suqs	{mdoc_sh_pre, NULL }, /* Sh */
126274880Sbapt	{mdoc_ss_pre, NULL }, /* Ss */
127274880Sbapt	{mdoc_pp_pre, NULL}, /* Pp */
128241675Suqs	{mdoc_d1_pre, NULL}, /* D1 */
129241675Suqs	{mdoc_d1_pre, NULL}, /* Dl */
130241675Suqs	{mdoc_bd_pre, NULL}, /* Bd */
131241675Suqs	{NULL, NULL}, /* Ed */
132241675Suqs	{mdoc_bl_pre, NULL}, /* Bl */
133241675Suqs	{NULL, NULL}, /* El */
134241675Suqs	{mdoc_it_pre, NULL}, /* It */
135274880Sbapt	{mdoc_ad_pre, NULL}, /* Ad */
136241675Suqs	{mdoc_an_pre, NULL}, /* An */
137322249Sbapt	{mdoc_ap_pre, NULL}, /* Ap */
138241675Suqs	{mdoc_ar_pre, NULL}, /* Ar */
139241675Suqs	{mdoc_cd_pre, NULL}, /* Cd */
140316420Sbapt	{mdoc_cm_pre, NULL}, /* Cm */
141274880Sbapt	{mdoc_dv_pre, NULL}, /* Dv */
142274880Sbapt	{mdoc_er_pre, NULL}, /* Er */
143274880Sbapt	{mdoc_ev_pre, NULL}, /* Ev */
144241675Suqs	{mdoc_ex_pre, NULL}, /* Ex */
145274880Sbapt	{mdoc_fa_pre, NULL}, /* Fa */
146274880Sbapt	{mdoc_fd_pre, NULL}, /* Fd */
147241675Suqs	{mdoc_fl_pre, NULL}, /* Fl */
148274880Sbapt	{mdoc_fn_pre, NULL}, /* Fn */
149274880Sbapt	{mdoc_ft_pre, NULL}, /* Ft */
150274880Sbapt	{mdoc_ic_pre, NULL}, /* Ic */
151274880Sbapt	{mdoc_in_pre, NULL}, /* In */
152241675Suqs	{mdoc_li_pre, NULL}, /* Li */
153274880Sbapt	{mdoc_nd_pre, NULL}, /* Nd */
154274880Sbapt	{mdoc_nm_pre, NULL}, /* Nm */
155241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Op */
156274880Sbapt	{mdoc_ft_pre, NULL}, /* Ot */
157241675Suqs	{mdoc_pa_pre, NULL}, /* Pa */
158316420Sbapt	{mdoc_ex_pre, NULL}, /* Rv */
159322249Sbapt	{mdoc_st_pre, NULL}, /* St */
160241675Suqs	{mdoc_va_pre, NULL}, /* Va */
161274880Sbapt	{mdoc_vt_pre, NULL}, /* Vt */
162241675Suqs	{mdoc_xr_pre, NULL}, /* Xr */
163241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %A */
164241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %B */
165241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %D */
166241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %I */
167241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %J */
168241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %N */
169241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %O */
170241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %P */
171241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %R */
172241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %T */
173241675Suqs	{mdoc__x_pre, mdoc__x_post}, /* %V */
174241675Suqs	{NULL, NULL}, /* Ac */
175241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Ao */
176241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Aq */
177322249Sbapt	{mdoc_xx_pre, NULL}, /* At */
178241675Suqs	{NULL, NULL}, /* Bc */
179274880Sbapt	{mdoc_bf_pre, NULL}, /* Bf */
180241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Bo */
181241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Bq */
182241675Suqs	{mdoc_xx_pre, NULL}, /* Bsx */
183316420Sbapt	{mdoc_xx_pre, NULL}, /* Bx */
184275432Sbapt	{mdoc_skip_pre, NULL}, /* Db */
185241675Suqs	{NULL, NULL}, /* Dc */
186241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Do */
187241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Dq */
188241675Suqs	{NULL, NULL}, /* Ec */ /* FIXME: no space */
189241675Suqs	{NULL, NULL}, /* Ef */
190274880Sbapt	{mdoc_em_pre, NULL}, /* Em */
191279527Sbapt	{mdoc_eo_pre, mdoc_eo_post}, /* Eo */
192241675Suqs	{mdoc_xx_pre, NULL}, /* Fx */
193241675Suqs	{mdoc_ms_pre, NULL}, /* Ms */
194275432Sbapt	{mdoc_no_pre, NULL}, /* No */
195241675Suqs	{mdoc_ns_pre, NULL}, /* Ns */
196241675Suqs	{mdoc_xx_pre, NULL}, /* Nx */
197241675Suqs	{mdoc_xx_pre, NULL}, /* Ox */
198241675Suqs	{NULL, NULL}, /* Pc */
199241675Suqs	{mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */
200241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Po */
201241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Pq */
202241675Suqs	{NULL, NULL}, /* Qc */
203241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Ql */
204241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Qo */
205241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Qq */
206241675Suqs	{NULL, NULL}, /* Re */
207241675Suqs	{mdoc_rs_pre, NULL}, /* Rs */
208241675Suqs	{NULL, NULL}, /* Sc */
209241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* So */
210241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Sq */
211274880Sbapt	{mdoc_sm_pre, NULL}, /* Sm */
212241675Suqs	{mdoc_sx_pre, NULL}, /* Sx */
213241675Suqs	{mdoc_sy_pre, NULL}, /* Sy */
214241675Suqs	{NULL, NULL}, /* Tn */
215241675Suqs	{mdoc_xx_pre, NULL}, /* Ux */
216241675Suqs	{NULL, NULL}, /* Xc */
217241675Suqs	{NULL, NULL}, /* Xo */
218274880Sbapt	{mdoc_fo_pre, mdoc_fo_post}, /* Fo */
219274880Sbapt	{NULL, NULL}, /* Fc */
220241675Suqs	{mdoc_quote_pre, mdoc_quote_post}, /* Oo */
221241675Suqs	{NULL, NULL}, /* Oc */
222241675Suqs	{mdoc_bk_pre, mdoc_bk_post}, /* Bk */
223241675Suqs	{NULL, NULL}, /* Ek */
224316420Sbapt	{NULL, NULL}, /* Bt */
225241675Suqs	{NULL, NULL}, /* Hf */
226274880Sbapt	{mdoc_em_pre, NULL}, /* Fr */
227316420Sbapt	{NULL, NULL}, /* Ud */
228241675Suqs	{mdoc_lb_pre, NULL}, /* Lb */
229274880Sbapt	{mdoc_pp_pre, NULL}, /* Lp */
230274880Sbapt	{mdoc_lk_pre, NULL}, /* Lk */
231274880Sbapt	{mdoc_mt_pre, NULL}, /* Mt */
232274880Sbapt	{mdoc_quote_pre, mdoc_quote_post}, /* Brq */
233274880Sbapt	{mdoc_quote_pre, mdoc_quote_post}, /* Bro */
234274880Sbapt	{NULL, NULL}, /* Brc */
235274880Sbapt	{mdoc__x_pre, mdoc__x_post}, /* %C */
236274880Sbapt	{mdoc_skip_pre, NULL}, /* Es */
237274880Sbapt	{mdoc_quote_pre, mdoc_quote_post}, /* En */
238274880Sbapt	{mdoc_xx_pre, NULL}, /* Dx */
239274880Sbapt	{mdoc__x_pre, mdoc__x_post}, /* %Q */
240274880Sbapt	{mdoc__x_pre, mdoc__x_post}, /* %U */
241274880Sbapt	{NULL, NULL}, /* Ta */
242241675Suqs};
243322249Sbaptstatic	const struct htmlmdoc *const mdocs = __mdocs - MDOC_Dd;
244241675Suqs
245241675Suqs
246241675Suqs/*
247241675Suqs * See the same function in mdoc_term.c for documentation.
248241675Suqs */
249241675Suqsstatic void
250294113Sbaptsynopsis_pre(struct html *h, const struct roff_node *n)
251241675Suqs{
252241675Suqs
253316420Sbapt	if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
254241675Suqs		return;
255241675Suqs
256274880Sbapt	if (n->prev->tok == n->tok &&
257274880Sbapt	    MDOC_Fo != n->tok &&
258274880Sbapt	    MDOC_Ft != n->tok &&
259274880Sbapt	    MDOC_Fn != n->tok) {
260316420Sbapt		print_otag(h, TAG_BR, "");
261241675Suqs		return;
262241675Suqs	}
263241675Suqs
264241675Suqs	switch (n->prev->tok) {
265274880Sbapt	case MDOC_Fd:
266274880Sbapt	case MDOC_Fn:
267274880Sbapt	case MDOC_Fo:
268274880Sbapt	case MDOC_In:
269274880Sbapt	case MDOC_Vt:
270275432Sbapt		print_paragraph(h);
271241675Suqs		break;
272274880Sbapt	case MDOC_Ft:
273241675Suqs		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
274275432Sbapt			print_paragraph(h);
275241675Suqs			break;
276241675Suqs		}
277241675Suqs		/* FALLTHROUGH */
278241675Suqs	default:
279316420Sbapt		print_otag(h, TAG_BR, "");
280241675Suqs		break;
281241675Suqs	}
282241675Suqs}
283241675Suqs
284294113Sbaptvoid
285294113Sbapthtml_mdoc(void *arg, const struct roff_man *mdoc)
286241675Suqs{
287294113Sbapt	struct html	*h;
288316420Sbapt	struct tag	*t;
289241675Suqs
290294113Sbapt	h = (struct html *)arg;
291241675Suqs
292316420Sbapt	if ((h->oflags & HTML_FRAGMENT) == 0) {
293241675Suqs		print_gen_decls(h);
294316420Sbapt		print_otag(h, TAG_HTML, "");
295316420Sbapt		t = print_otag(h, TAG_HEAD, "");
296294113Sbapt		print_mdoc_head(&mdoc->meta, mdoc->first->child, h);
297316420Sbapt		print_tagq(h, t);
298316420Sbapt		print_otag(h, TAG_BODY, "");
299316420Sbapt	}
300241675Suqs
301294113Sbapt	mdoc_root_pre(&mdoc->meta, mdoc->first->child, h);
302316420Sbapt	t = print_otag(h, TAG_DIV, "c", "manual-text");
303294113Sbapt	print_mdoc_nodelist(&mdoc->meta, mdoc->first->child, h);
304316420Sbapt	print_tagq(h, t);
305294113Sbapt	mdoc_root_post(&mdoc->meta, mdoc->first->child, h);
306316420Sbapt	print_tagq(h, NULL);
307241675Suqs}
308241675Suqs
309241675Suqsstatic void
310241675Suqsprint_mdoc_head(MDOC_ARGS)
311241675Suqs{
312316420Sbapt	char	*cp;
313241675Suqs
314241675Suqs	print_gen_head(h);
315241675Suqs
316316420Sbapt	if (meta->arch != NULL && meta->msec != NULL)
317316420Sbapt		mandoc_asprintf(&cp, "%s(%s) (%s)", meta->title,
318316420Sbapt		    meta->msec, meta->arch);
319316420Sbapt	else if (meta->msec != NULL)
320316420Sbapt		mandoc_asprintf(&cp, "%s(%s)", meta->title, meta->msec);
321316420Sbapt	else if (meta->arch != NULL)
322316420Sbapt		mandoc_asprintf(&cp, "%s (%s)", meta->title, meta->arch);
323316420Sbapt	else
324316420Sbapt		cp = mandoc_strdup(meta->title);
325316420Sbapt
326316420Sbapt	print_otag(h, TAG_TITLE, "");
327316420Sbapt	print_text(h, cp);
328316420Sbapt	free(cp);
329241675Suqs}
330241675Suqs
331241675Suqsstatic void
332241675Suqsprint_mdoc_nodelist(MDOC_ARGS)
333241675Suqs{
334241675Suqs
335279527Sbapt	while (n != NULL) {
336279527Sbapt		print_mdoc_node(meta, n, h);
337279527Sbapt		n = n->next;
338279527Sbapt	}
339241675Suqs}
340241675Suqs
341241675Suqsstatic void
342241675Suqsprint_mdoc_node(MDOC_ARGS)
343241675Suqs{
344241675Suqs	int		 child;
345241675Suqs	struct tag	*t;
346241675Suqs
347316420Sbapt	if (n->flags & NODE_NOPRT)
348316420Sbapt		return;
349316420Sbapt
350241675Suqs	child = 1;
351322249Sbapt	t = h->tag;
352316420Sbapt	n->flags &= ~NODE_ENDED;
353241675Suqs
354241675Suqs	switch (n->type) {
355294113Sbapt	case ROFFT_TEXT:
356241675Suqs		/* No tables in this mode... */
357241675Suqs		assert(NULL == h->tblt);
358241675Suqs
359241675Suqs		/*
360241675Suqs		 * Make sure that if we're in a literal mode already
361241675Suqs		 * (i.e., within a <PRE>) don't print the newline.
362241675Suqs		 */
363322249Sbapt		if (*n->string == ' ' && n->flags & NODE_LINE &&
364322249Sbapt		    (h->flags & (HTML_LITERAL | HTML_NONEWLINE)) == 0)
365322249Sbapt			print_otag(h, TAG_BR, "");
366316420Sbapt		if (NODE_DELIMC & n->flags)
367241675Suqs			h->flags |= HTML_NOSPACE;
368241675Suqs		print_text(h, n->string);
369316420Sbapt		if (NODE_DELIMO & n->flags)
370241675Suqs			h->flags |= HTML_NOSPACE;
371241675Suqs		return;
372294113Sbapt	case ROFFT_EQN:
373241675Suqs		print_eqn(h, n->eqn);
374241675Suqs		break;
375294113Sbapt	case ROFFT_TBL:
376241675Suqs		/*
377241675Suqs		 * This will take care of initialising all of the table
378241675Suqs		 * state data for the first table, then tearing it down
379241675Suqs		 * for the last one.
380241675Suqs		 */
381241675Suqs		print_tbl(h, n->span);
382241675Suqs		return;
383241675Suqs	default:
384241675Suqs		/*
385241675Suqs		 * Close out the current table, if it's open, and unset
386241675Suqs		 * the "meta" table state.  This will be reopened on the
387241675Suqs		 * next table element.
388241675Suqs		 */
389275432Sbapt		if (h->tblt != NULL) {
390241675Suqs			print_tblclose(h);
391322249Sbapt			t = h->tag;
392241675Suqs		}
393275432Sbapt		assert(h->tblt == NULL);
394322249Sbapt		if (n->tok < ROFF_MAX) {
395322249Sbapt			roff_html_pre(h, n);
396322249Sbapt			child = 0;
397322249Sbapt			break;
398322249Sbapt		}
399322249Sbapt		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
400322249Sbapt		if (mdocs[n->tok].pre != NULL &&
401322249Sbapt		    (n->end == ENDBODY_NOT || n->child != NULL))
402261344Suqs			child = (*mdocs[n->tok].pre)(meta, n, h);
403241675Suqs		break;
404241675Suqs	}
405241675Suqs
406316420Sbapt	if (h->flags & HTML_KEEP && n->flags & NODE_LINE) {
407279527Sbapt		h->flags &= ~HTML_KEEP;
408279527Sbapt		h->flags |= HTML_PREKEEP;
409241675Suqs	}
410241675Suqs
411241675Suqs	if (child && n->child)
412261344Suqs		print_mdoc_nodelist(meta, n->child, h);
413241675Suqs
414241675Suqs	print_stagq(h, t);
415241675Suqs
416241675Suqs	switch (n->type) {
417294113Sbapt	case ROFFT_EQN:
418241675Suqs		break;
419241675Suqs	default:
420322249Sbapt		if (n->tok < ROFF_MAX ||
421322249Sbapt		    mdocs[n->tok].post == NULL ||
422322249Sbapt		    n->flags & NODE_ENDED)
423275432Sbapt			break;
424275432Sbapt		(*mdocs[n->tok].post)(meta, n, h);
425275432Sbapt		if (n->end != ENDBODY_NOT)
426316420Sbapt			n->body->flags |= NODE_ENDED;
427241675Suqs		break;
428241675Suqs	}
429241675Suqs}
430241675Suqs
431241675Suqsstatic void
432241675Suqsmdoc_root_post(MDOC_ARGS)
433241675Suqs{
434241675Suqs	struct tag	*t, *tt;
435241675Suqs
436316420Sbapt	t = print_otag(h, TAG_TABLE, "c", "foot");
437316420Sbapt	tt = print_otag(h, TAG_TR, "");
438241675Suqs
439316420Sbapt	print_otag(h, TAG_TD, "c", "foot-date");
440261344Suqs	print_text(h, meta->date);
441241675Suqs	print_stagq(h, tt);
442241675Suqs
443316420Sbapt	print_otag(h, TAG_TD, "c", "foot-os");
444261344Suqs	print_text(h, meta->os);
445241675Suqs	print_tagq(h, t);
446241675Suqs}
447241675Suqs
448241675Suqsstatic int
449241675Suqsmdoc_root_pre(MDOC_ARGS)
450241675Suqs{
451241675Suqs	struct tag	*t, *tt;
452274880Sbapt	char		*volume, *title;
453241675Suqs
454274880Sbapt	if (NULL == meta->arch)
455274880Sbapt		volume = mandoc_strdup(meta->vol);
456274880Sbapt	else
457274880Sbapt		mandoc_asprintf(&volume, "%s (%s)",
458274880Sbapt		    meta->vol, meta->arch);
459241675Suqs
460274880Sbapt	if (NULL == meta->msec)
461274880Sbapt		title = mandoc_strdup(meta->title);
462274880Sbapt	else
463274880Sbapt		mandoc_asprintf(&title, "%s(%s)",
464274880Sbapt		    meta->title, meta->msec);
465241675Suqs
466316420Sbapt	t = print_otag(h, TAG_TABLE, "c", "head");
467316420Sbapt	tt = print_otag(h, TAG_TR, "");
468241675Suqs
469316420Sbapt	print_otag(h, TAG_TD, "c", "head-ltitle");
470241675Suqs	print_text(h, title);
471241675Suqs	print_stagq(h, tt);
472241675Suqs
473316420Sbapt	print_otag(h, TAG_TD, "c", "head-vol");
474274880Sbapt	print_text(h, volume);
475241675Suqs	print_stagq(h, tt);
476241675Suqs
477316420Sbapt	print_otag(h, TAG_TD, "c", "head-rtitle");
478241675Suqs	print_text(h, title);
479241675Suqs	print_tagq(h, t);
480274880Sbapt
481274880Sbapt	free(title);
482274880Sbapt	free(volume);
483294113Sbapt	return 1;
484241675Suqs}
485241675Suqs
486316420Sbaptstatic char *
487322249Sbaptcond_id(const struct roff_node *n)
488316420Sbapt{
489322249Sbapt	if (n->child != NULL &&
490322249Sbapt	    n->child->type == ROFFT_TEXT &&
491322249Sbapt	    (n->prev == NULL ||
492322249Sbapt	     (n->prev->type == ROFFT_TEXT &&
493322249Sbapt	      strcmp(n->prev->string, "|") == 0)) &&
494322249Sbapt	    (n->parent->tok == MDOC_It ||
495322249Sbapt	     (n->parent->tok == MDOC_Xo &&
496322249Sbapt	      n->parent->parent->prev == NULL &&
497322249Sbapt	      n->parent->parent->parent->tok == MDOC_It)))
498322249Sbapt		return html_make_id(n);
499322249Sbapt	return NULL;
500316420Sbapt}
501316420Sbapt
502241675Suqsstatic int
503241675Suqsmdoc_sh_pre(MDOC_ARGS)
504241675Suqs{
505316420Sbapt	char	*id;
506241675Suqs
507275432Sbapt	switch (n->type) {
508322249Sbapt	case ROFFT_HEAD:
509322249Sbapt		id = html_make_id(n);
510322249Sbapt		print_otag(h, TAG_H1, "cTi", "Sh", id);
511322249Sbapt		if (id != NULL)
512322249Sbapt			print_otag(h, TAG_A, "chR", "selflink", id);
513322249Sbapt		free(id);
514322249Sbapt		break;
515294113Sbapt	case ROFFT_BODY:
516275432Sbapt		if (n->sec == SEC_AUTHORS)
517275432Sbapt			h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT);
518322249Sbapt		break;
519275432Sbapt	default:
520275432Sbapt		break;
521275432Sbapt	}
522294113Sbapt	return 1;
523241675Suqs}
524241675Suqs
525241675Suqsstatic int
526241675Suqsmdoc_ss_pre(MDOC_ARGS)
527241675Suqs{
528316420Sbapt	char	*id;
529241675Suqs
530316420Sbapt	if (n->type != ROFFT_HEAD)
531294113Sbapt		return 1;
532241675Suqs
533322249Sbapt	id = html_make_id(n);
534322249Sbapt	print_otag(h, TAG_H2, "cTi", "Ss", id);
535322249Sbapt	if (id != NULL)
536322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
537322249Sbapt	free(id);
538294113Sbapt	return 1;
539241675Suqs}
540241675Suqs
541241675Suqsstatic int
542241675Suqsmdoc_fl_pre(MDOC_ARGS)
543241675Suqs{
544322249Sbapt	char	*id;
545322249Sbapt
546322249Sbapt	if ((id = cond_id(n)) != NULL)
547322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
548322249Sbapt	print_otag(h, TAG_B, "cTi", "Fl", id);
549322249Sbapt	free(id);
550322249Sbapt
551241675Suqs	print_text(h, "\\-");
552294113Sbapt	if (!(n->child == NULL &&
553275432Sbapt	    (n->next == NULL ||
554294113Sbapt	     n->next->type == ROFFT_TEXT ||
555316420Sbapt	     n->next->flags & NODE_LINE)))
556241675Suqs		h->flags |= HTML_NOSPACE;
557241675Suqs
558294113Sbapt	return 1;
559241675Suqs}
560241675Suqs
561241675Suqsstatic int
562316420Sbaptmdoc_cm_pre(MDOC_ARGS)
563316420Sbapt{
564322249Sbapt	char	*id;
565322249Sbapt
566322249Sbapt	if ((id = cond_id(n)) != NULL)
567322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
568322249Sbapt	print_otag(h, TAG_B, "cTi", "Cm", id);
569322249Sbapt	free(id);
570316420Sbapt	return 1;
571316420Sbapt}
572316420Sbapt
573316420Sbaptstatic int
574241675Suqsmdoc_nd_pre(MDOC_ARGS)
575241675Suqs{
576294113Sbapt	if (n->type != ROFFT_BODY)
577294113Sbapt		return 1;
578241675Suqs
579241675Suqs	/* XXX: this tag in theory can contain block elements. */
580241675Suqs
581241675Suqs	print_text(h, "\\(em");
582322249Sbapt	print_otag(h, TAG_SPAN, "cT", "Nd");
583294113Sbapt	return 1;
584241675Suqs}
585241675Suqs
586241675Suqsstatic int
587241675Suqsmdoc_nm_pre(MDOC_ARGS)
588241675Suqs{
589241675Suqs	switch (n->type) {
590294113Sbapt	case ROFFT_HEAD:
591316420Sbapt		print_otag(h, TAG_TD, "");
592294113Sbapt		/* FALLTHROUGH */
593294113Sbapt	case ROFFT_ELEM:
594322249Sbapt		print_otag(h, TAG_B, "cT", "Nm");
595294113Sbapt		return 1;
596294113Sbapt	case ROFFT_BODY:
597316420Sbapt		print_otag(h, TAG_TD, "");
598294113Sbapt		return 1;
599241675Suqs	default:
600241675Suqs		break;
601241675Suqs	}
602241675Suqs	synopsis_pre(h, n);
603316420Sbapt	print_otag(h, TAG_TABLE, "c", "Nm");
604316420Sbapt	print_otag(h, TAG_TR, "");
605294113Sbapt	return 1;
606241675Suqs}
607241675Suqs
608241675Suqsstatic int
609241675Suqsmdoc_xr_pre(MDOC_ARGS)
610241675Suqs{
611241675Suqs	if (NULL == n->child)
612294113Sbapt		return 0;
613241675Suqs
614316420Sbapt	if (h->base_man)
615322249Sbapt		print_otag(h, TAG_A, "cThM", "Xr",
616316420Sbapt		    n->child->string, n->child->next == NULL ?
617316420Sbapt		    NULL : n->child->next->string);
618316420Sbapt	else
619322249Sbapt		print_otag(h, TAG_A, "cT", "Xr");
620241675Suqs
621241675Suqs	n = n->child;
622241675Suqs	print_text(h, n->string);
623241675Suqs
624241675Suqs	if (NULL == (n = n->next))
625294113Sbapt		return 0;
626241675Suqs
627241675Suqs	h->flags |= HTML_NOSPACE;
628241675Suqs	print_text(h, "(");
629241675Suqs	h->flags |= HTML_NOSPACE;
630241675Suqs	print_text(h, n->string);
631241675Suqs	h->flags |= HTML_NOSPACE;
632241675Suqs	print_text(h, ")");
633294113Sbapt	return 0;
634241675Suqs}
635241675Suqs
636241675Suqsstatic int
637241675Suqsmdoc_ns_pre(MDOC_ARGS)
638241675Suqs{
639241675Suqs
640316420Sbapt	if ( ! (NODE_LINE & n->flags))
641241675Suqs		h->flags |= HTML_NOSPACE;
642294113Sbapt	return 1;
643241675Suqs}
644241675Suqs
645241675Suqsstatic int
646241675Suqsmdoc_ar_pre(MDOC_ARGS)
647241675Suqs{
648322249Sbapt	print_otag(h, TAG_VAR, "cT", "Ar");
649294113Sbapt	return 1;
650241675Suqs}
651241675Suqs
652241675Suqsstatic int
653241675Suqsmdoc_xx_pre(MDOC_ARGS)
654241675Suqs{
655316420Sbapt	print_otag(h, TAG_SPAN, "c", "Ux");
656316420Sbapt	return 1;
657241675Suqs}
658241675Suqs
659241675Suqsstatic int
660241675Suqsmdoc_it_pre(MDOC_ARGS)
661241675Suqs{
662316420Sbapt	const struct roff_node	*bl;
663322249Sbapt	struct tag		*t;
664316420Sbapt	const char		*cattr;
665316420Sbapt	enum mdoc_list		 type;
666241675Suqs
667241675Suqs	bl = n->parent;
668322249Sbapt	while (bl->tok != MDOC_Bl)
669241675Suqs		bl = bl->parent;
670241675Suqs	type = bl->norm->Bl.type;
671241675Suqs
672316420Sbapt	switch (type) {
673316420Sbapt	case LIST_bullet:
674316420Sbapt		cattr = "It-bullet";
675316420Sbapt		break;
676316420Sbapt	case LIST_dash:
677316420Sbapt	case LIST_hyphen:
678316420Sbapt		cattr = "It-dash";
679316420Sbapt		break;
680316420Sbapt	case LIST_item:
681316420Sbapt		cattr = "It-item";
682316420Sbapt		break;
683316420Sbapt	case LIST_enum:
684316420Sbapt		cattr = "It-enum";
685316420Sbapt		break;
686316420Sbapt	case LIST_diag:
687316420Sbapt		cattr = "It-diag";
688316420Sbapt		break;
689316420Sbapt	case LIST_hang:
690316420Sbapt		cattr = "It-hang";
691316420Sbapt		break;
692316420Sbapt	case LIST_inset:
693316420Sbapt		cattr = "It-inset";
694316420Sbapt		break;
695316420Sbapt	case LIST_ohang:
696316420Sbapt		cattr = "It-ohang";
697316420Sbapt		break;
698316420Sbapt	case LIST_tag:
699316420Sbapt		cattr = "It-tag";
700316420Sbapt		break;
701316420Sbapt	case LIST_column:
702316420Sbapt		cattr = "It-column";
703316420Sbapt		break;
704316420Sbapt	default:
705316420Sbapt		break;
706316420Sbapt	}
707241675Suqs
708316420Sbapt	switch (type) {
709316420Sbapt	case LIST_bullet:
710316420Sbapt	case LIST_dash:
711316420Sbapt	case LIST_hyphen:
712316420Sbapt	case LIST_item:
713316420Sbapt	case LIST_enum:
714316420Sbapt		switch (n->type) {
715316420Sbapt		case ROFFT_HEAD:
716294113Sbapt			return 0;
717316420Sbapt		case ROFFT_BODY:
718322249Sbapt			print_otag(h, TAG_LI, "c", cattr);
719241675Suqs			break;
720241675Suqs		default:
721241675Suqs			break;
722241675Suqs		}
723316420Sbapt		break;
724316420Sbapt	case LIST_diag:
725316420Sbapt	case LIST_hang:
726316420Sbapt	case LIST_inset:
727316420Sbapt	case LIST_ohang:
728316420Sbapt		switch (n->type) {
729316420Sbapt		case ROFFT_HEAD:
730322249Sbapt			print_otag(h, TAG_DT, "c", cattr);
731316420Sbapt			if (type == LIST_diag)
732316420Sbapt				print_otag(h, TAG_B, "c", cattr);
733241675Suqs			break;
734316420Sbapt		case ROFFT_BODY:
735322249Sbapt			print_otag(h, TAG_DD, "csw*+l", cattr,
736322249Sbapt			    bl->norm->Bl.width);
737322249Sbapt			break;
738322249Sbapt		default:
739322249Sbapt			break;
740322249Sbapt		}
741322249Sbapt		break;
742322249Sbapt	case LIST_tag:
743322249Sbapt		switch (n->type) {
744322249Sbapt		case ROFFT_HEAD:
745322249Sbapt			if (h->style != NULL && !bl->norm->Bl.comp &&
746322249Sbapt			    (n->parent->prev == NULL ||
747322249Sbapt			     n->parent->prev->body == NULL ||
748322249Sbapt			     n->parent->prev->body->child != NULL)) {
749322249Sbapt				t = print_otag(h, TAG_DT, "csw*+-l",
750322249Sbapt				    cattr, bl->norm->Bl.width);
751322249Sbapt				print_text(h, "\\ ");
752322249Sbapt				print_tagq(h, t);
753322249Sbapt				t = print_otag(h, TAG_DD, "c", cattr);
754322249Sbapt				print_text(h, "\\ ");
755322249Sbapt				print_tagq(h, t);
756322249Sbapt			}
757322249Sbapt			print_otag(h, TAG_DT, "csw*+-l", cattr,
758322249Sbapt			    bl->norm->Bl.width);
759322249Sbapt			break;
760322249Sbapt		case ROFFT_BODY:
761322249Sbapt			if (n->child == NULL) {
762322249Sbapt				print_otag(h, TAG_DD, "css?", cattr,
763322249Sbapt				    "width", "auto");
764322249Sbapt				print_text(h, "\\ ");
765322249Sbapt			} else
766316420Sbapt				print_otag(h, TAG_DD, "c", cattr);
767241675Suqs			break;
768241675Suqs		default:
769241675Suqs			break;
770241675Suqs		}
771316420Sbapt		break;
772316420Sbapt	case LIST_column:
773316420Sbapt		switch (n->type) {
774316420Sbapt		case ROFFT_HEAD:
775241675Suqs			break;
776316420Sbapt		case ROFFT_BODY:
777322249Sbapt			print_otag(h, TAG_TD, "c", cattr);
778316420Sbapt			break;
779241675Suqs		default:
780316420Sbapt			print_otag(h, TAG_TR, "c", cattr);
781241675Suqs		}
782316420Sbapt	default:
783316420Sbapt		break;
784241675Suqs	}
785241675Suqs
786294113Sbapt	return 1;
787241675Suqs}
788241675Suqs
789241675Suqsstatic int
790241675Suqsmdoc_bl_pre(MDOC_ARGS)
791241675Suqs{
792322249Sbapt	char		 cattr[21];
793322249Sbapt	struct tag	*t;
794322249Sbapt	struct mdoc_bl	*bl;
795322249Sbapt	size_t		 i;
796316420Sbapt	enum htmltag	 elemtype;
797241675Suqs
798322249Sbapt	bl = &n->norm->Bl;
799322249Sbapt
800322249Sbapt	switch (n->type) {
801322249Sbapt	case ROFFT_BODY:
802294113Sbapt		return 1;
803241675Suqs
804322249Sbapt	case ROFFT_HEAD:
805322249Sbapt		if (bl->type != LIST_column || bl->ncols == 0)
806294113Sbapt			return 0;
807241675Suqs
808241675Suqs		/*
809241675Suqs		 * For each column, print out the <COL> tag with our
810241675Suqs		 * suggested width.  The last column gets min-width, as
811241675Suqs		 * in terminal mode it auto-sizes to the width of the
812241675Suqs		 * screen and we want to preserve that behaviour.
813241675Suqs		 */
814241675Suqs
815322249Sbapt		t = print_otag(h, TAG_COLGROUP, "");
816322249Sbapt		for (i = 0; i < bl->ncols - 1; i++)
817322249Sbapt			print_otag(h, TAG_COL, "sw+w", bl->cols[i]);
818322249Sbapt		print_otag(h, TAG_COL, "swW", bl->cols[i]);
819322249Sbapt		print_tagq(h, t);
820322249Sbapt		return 0;
821241675Suqs
822322249Sbapt	default:
823322249Sbapt		break;
824241675Suqs	}
825241675Suqs
826322249Sbapt	switch (bl->type) {
827274880Sbapt	case LIST_bullet:
828316420Sbapt		elemtype = TAG_UL;
829322249Sbapt		(void)strlcpy(cattr, "Bl-bullet", sizeof(cattr));
830316420Sbapt		break;
831274880Sbapt	case LIST_dash:
832274880Sbapt	case LIST_hyphen:
833316420Sbapt		elemtype = TAG_UL;
834322249Sbapt		(void)strlcpy(cattr, "Bl-dash", sizeof(cattr));
835316420Sbapt		break;
836274880Sbapt	case LIST_item:
837316420Sbapt		elemtype = TAG_UL;
838322249Sbapt		(void)strlcpy(cattr, "Bl-item", sizeof(cattr));
839241675Suqs		break;
840274880Sbapt	case LIST_enum:
841316420Sbapt		elemtype = TAG_OL;
842322249Sbapt		(void)strlcpy(cattr, "Bl-enum", sizeof(cattr));
843241675Suqs		break;
844274880Sbapt	case LIST_diag:
845316420Sbapt		elemtype = TAG_DL;
846322249Sbapt		(void)strlcpy(cattr, "Bl-diag", sizeof(cattr));
847316420Sbapt		break;
848274880Sbapt	case LIST_hang:
849316420Sbapt		elemtype = TAG_DL;
850322249Sbapt		(void)strlcpy(cattr, "Bl-hang", sizeof(cattr));
851316420Sbapt		break;
852274880Sbapt	case LIST_inset:
853316420Sbapt		elemtype = TAG_DL;
854322249Sbapt		(void)strlcpy(cattr, "Bl-inset", sizeof(cattr));
855316420Sbapt		break;
856274880Sbapt	case LIST_ohang:
857316420Sbapt		elemtype = TAG_DL;
858322249Sbapt		(void)strlcpy(cattr, "Bl-ohang", sizeof(cattr));
859316420Sbapt		break;
860274880Sbapt	case LIST_tag:
861322249Sbapt		if (bl->offs)
862322249Sbapt			print_otag(h, TAG_DIV, "cswl", "Bl-tag", bl->offs);
863322249Sbapt		print_otag(h, TAG_DL, "csw*+l", bl->comp ?
864322249Sbapt		    "Bl-tag Bl-compact" : "Bl-tag", bl->width);
865322249Sbapt		return 1;
866274880Sbapt	case LIST_column:
867316420Sbapt		elemtype = TAG_TABLE;
868322249Sbapt		(void)strlcpy(cattr, "Bl-column", sizeof(cattr));
869241675Suqs		break;
870241675Suqs	default:
871241675Suqs		abort();
872241675Suqs	}
873322249Sbapt	if (bl->comp)
874322249Sbapt		(void)strlcat(cattr, " Bl-compact", sizeof(cattr));
875322249Sbapt	print_otag(h, elemtype, "cswl", cattr, bl->offs);
876294113Sbapt	return 1;
877241675Suqs}
878241675Suqs
879241675Suqsstatic int
880241675Suqsmdoc_ex_pre(MDOC_ARGS)
881241675Suqs{
882241675Suqs	if (n->prev)
883316420Sbapt		print_otag(h, TAG_BR, "");
884316420Sbapt	return 1;
885241675Suqs}
886241675Suqs
887241675Suqsstatic int
888322249Sbaptmdoc_st_pre(MDOC_ARGS)
889322249Sbapt{
890322249Sbapt	print_otag(h, TAG_SPAN, "cT", "St");
891322249Sbapt	return 1;
892322249Sbapt}
893322249Sbapt
894322249Sbaptstatic int
895241675Suqsmdoc_em_pre(MDOC_ARGS)
896241675Suqs{
897322249Sbapt	print_otag(h, TAG_I, "cT", "Em");
898294113Sbapt	return 1;
899241675Suqs}
900241675Suqs
901241675Suqsstatic int
902241675Suqsmdoc_d1_pre(MDOC_ARGS)
903241675Suqs{
904294113Sbapt	if (n->type != ROFFT_BLOCK)
905294113Sbapt		return 1;
906241675Suqs
907316420Sbapt	print_otag(h, TAG_DIV, "c", "D1");
908241675Suqs
909316420Sbapt	if (n->tok == MDOC_Dl)
910316420Sbapt		print_otag(h, TAG_CODE, "c", "Li");
911241675Suqs
912294113Sbapt	return 1;
913241675Suqs}
914241675Suqs
915241675Suqsstatic int
916241675Suqsmdoc_sx_pre(MDOC_ARGS)
917241675Suqs{
918316420Sbapt	char	*id;
919241675Suqs
920322249Sbapt	id = html_make_id(n);
921322249Sbapt	print_otag(h, TAG_A, "cThR", "Sx", id);
922322249Sbapt	free(id);
923294113Sbapt	return 1;
924241675Suqs}
925241675Suqs
926241675Suqsstatic int
927241675Suqsmdoc_bd_pre(MDOC_ARGS)
928241675Suqs{
929316420Sbapt	int			 comp, offs, sv;
930294113Sbapt	struct roff_node	*nn;
931241675Suqs
932294113Sbapt	if (n->type == ROFFT_HEAD)
933294113Sbapt		return 0;
934241675Suqs
935294113Sbapt	if (n->type == ROFFT_BLOCK) {
936241675Suqs		comp = n->norm->Bd.comp;
937241675Suqs		for (nn = n; nn && ! comp; nn = nn->parent) {
938294113Sbapt			if (nn->type != ROFFT_BLOCK)
939241675Suqs				continue;
940241675Suqs			if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
941241675Suqs				comp = 1;
942241675Suqs			if (nn->prev)
943241675Suqs				break;
944241675Suqs		}
945241675Suqs		if ( ! comp)
946275432Sbapt			print_paragraph(h);
947294113Sbapt		return 1;
948241675Suqs	}
949241675Suqs
950275432Sbapt	/* Handle the -offset argument. */
951274880Sbapt
952275432Sbapt	if (n->norm->Bd.offs == NULL ||
953275432Sbapt	    ! strcmp(n->norm->Bd.offs, "left"))
954316420Sbapt		offs = 0;
955275432Sbapt	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
956316420Sbapt		offs = INDENT;
957275432Sbapt	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
958316420Sbapt		offs = INDENT * 2;
959275432Sbapt	else
960316420Sbapt		offs = -1;
961275432Sbapt
962316420Sbapt	if (offs == -1)
963316420Sbapt		print_otag(h, TAG_DIV, "cswl", "Bd", n->norm->Bd.offs);
964316420Sbapt	else
965316420Sbapt		print_otag(h, TAG_DIV, "cshl", "Bd", offs);
966241675Suqs
967316420Sbapt	if (n->norm->Bd.type != DISP_unfilled &&
968316420Sbapt	    n->norm->Bd.type != DISP_literal)
969294113Sbapt		return 1;
970241675Suqs
971316420Sbapt	print_otag(h, TAG_PRE, "c", "Li");
972241675Suqs
973241675Suqs	/* This can be recursive: save & set our literal state. */
974241675Suqs
975241675Suqs	sv = h->flags & HTML_LITERAL;
976241675Suqs	h->flags |= HTML_LITERAL;
977241675Suqs
978241675Suqs	for (nn = n->child; nn; nn = nn->next) {
979261344Suqs		print_mdoc_node(meta, nn, h);
980241675Suqs		/*
981241675Suqs		 * If the printed node flushes its own line, then we
982241675Suqs		 * needn't do it here as well.  This is hacky, but the
983241675Suqs		 * notion of selective eoln whitespace is pretty dumb
984241675Suqs		 * anyway, so don't sweat it.
985241675Suqs		 */
986241675Suqs		switch (nn->tok) {
987322249Sbapt		case ROFF_br:
988322249Sbapt		case ROFF_sp:
989274880Sbapt		case MDOC_Sm:
990274880Sbapt		case MDOC_Bl:
991274880Sbapt		case MDOC_D1:
992274880Sbapt		case MDOC_Dl:
993274880Sbapt		case MDOC_Lp:
994274880Sbapt		case MDOC_Pp:
995241675Suqs			continue;
996241675Suqs		default:
997241675Suqs			break;
998241675Suqs		}
999276219Sbapt		if (h->flags & HTML_NONEWLINE ||
1000316420Sbapt		    (nn->next && ! (nn->next->flags & NODE_LINE)))
1001241675Suqs			continue;
1002241675Suqs		else if (nn->next)
1003241675Suqs			print_text(h, "\n");
1004241675Suqs
1005241675Suqs		h->flags |= HTML_NOSPACE;
1006241675Suqs	}
1007241675Suqs
1008241675Suqs	if (0 == sv)
1009241675Suqs		h->flags &= ~HTML_LITERAL;
1010241675Suqs
1011294113Sbapt	return 0;
1012241675Suqs}
1013241675Suqs
1014241675Suqsstatic int
1015241675Suqsmdoc_pa_pre(MDOC_ARGS)
1016241675Suqs{
1017322249Sbapt	print_otag(h, TAG_I, "cT", "Pa");
1018294113Sbapt	return 1;
1019241675Suqs}
1020241675Suqs
1021241675Suqsstatic int
1022241675Suqsmdoc_ad_pre(MDOC_ARGS)
1023241675Suqs{
1024316420Sbapt	print_otag(h, TAG_I, "c", "Ad");
1025294113Sbapt	return 1;
1026241675Suqs}
1027241675Suqs
1028241675Suqsstatic int
1029241675Suqsmdoc_an_pre(MDOC_ARGS)
1030241675Suqs{
1031275432Sbapt	if (n->norm->An.auth == AUTH_split) {
1032275432Sbapt		h->flags &= ~HTML_NOSPLIT;
1033275432Sbapt		h->flags |= HTML_SPLIT;
1034294113Sbapt		return 0;
1035275432Sbapt	}
1036275432Sbapt	if (n->norm->An.auth == AUTH_nosplit) {
1037275432Sbapt		h->flags &= ~HTML_SPLIT;
1038275432Sbapt		h->flags |= HTML_NOSPLIT;
1039294113Sbapt		return 0;
1040275432Sbapt	}
1041241675Suqs
1042275432Sbapt	if (h->flags & HTML_SPLIT)
1043316420Sbapt		print_otag(h, TAG_BR, "");
1044275432Sbapt
1045275432Sbapt	if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
1046275432Sbapt		h->flags |= HTML_SPLIT;
1047275432Sbapt
1048322249Sbapt	print_otag(h, TAG_SPAN, "cT", "An");
1049294113Sbapt	return 1;
1050241675Suqs}
1051241675Suqs
1052241675Suqsstatic int
1053241675Suqsmdoc_cd_pre(MDOC_ARGS)
1054241675Suqs{
1055241675Suqs	synopsis_pre(h, n);
1056322249Sbapt	print_otag(h, TAG_B, "cT", "Cd");
1057294113Sbapt	return 1;
1058241675Suqs}
1059241675Suqs
1060241675Suqsstatic int
1061241675Suqsmdoc_dv_pre(MDOC_ARGS)
1062241675Suqs{
1063322249Sbapt	char	*id;
1064322249Sbapt
1065322249Sbapt	if ((id = cond_id(n)) != NULL)
1066322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1067322249Sbapt	print_otag(h, TAG_CODE, "cTi", "Dv", id);
1068322249Sbapt	free(id);
1069294113Sbapt	return 1;
1070241675Suqs}
1071241675Suqs
1072241675Suqsstatic int
1073241675Suqsmdoc_ev_pre(MDOC_ARGS)
1074241675Suqs{
1075322249Sbapt	char	*id;
1076322249Sbapt
1077322249Sbapt	if ((id = cond_id(n)) != NULL)
1078322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1079322249Sbapt	print_otag(h, TAG_CODE, "cTi", "Ev", id);
1080322249Sbapt	free(id);
1081294113Sbapt	return 1;
1082241675Suqs}
1083241675Suqs
1084241675Suqsstatic int
1085241675Suqsmdoc_er_pre(MDOC_ARGS)
1086241675Suqs{
1087322249Sbapt	char	*id;
1088322249Sbapt
1089322249Sbapt	id = n->sec == SEC_ERRORS &&
1090322249Sbapt	    (n->parent->tok == MDOC_It ||
1091322249Sbapt	     (n->parent->tok == MDOC_Bq &&
1092322249Sbapt	      n->parent->parent->parent->tok == MDOC_It)) ?
1093322249Sbapt	    html_make_id(n) : NULL;
1094322249Sbapt
1095322249Sbapt	if (id != NULL)
1096322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1097322249Sbapt	print_otag(h, TAG_CODE, "cTi", "Er", id);
1098322249Sbapt	free(id);
1099294113Sbapt	return 1;
1100241675Suqs}
1101241675Suqs
1102241675Suqsstatic int
1103241675Suqsmdoc_fa_pre(MDOC_ARGS)
1104241675Suqs{
1105294113Sbapt	const struct roff_node	*nn;
1106241675Suqs	struct tag		*t;
1107241675Suqs
1108241675Suqs	if (n->parent->tok != MDOC_Fo) {
1109322249Sbapt		print_otag(h, TAG_VAR, "cT", "Fa");
1110294113Sbapt		return 1;
1111241675Suqs	}
1112241675Suqs
1113241675Suqs	for (nn = n->child; nn; nn = nn->next) {
1114322249Sbapt		t = print_otag(h, TAG_VAR, "cT", "Fa");
1115241675Suqs		print_text(h, nn->string);
1116241675Suqs		print_tagq(h, t);
1117241675Suqs		if (nn->next) {
1118241675Suqs			h->flags |= HTML_NOSPACE;
1119241675Suqs			print_text(h, ",");
1120241675Suqs		}
1121241675Suqs	}
1122241675Suqs
1123241675Suqs	if (n->child && n->next && n->next->tok == MDOC_Fa) {
1124241675Suqs		h->flags |= HTML_NOSPACE;
1125241675Suqs		print_text(h, ",");
1126241675Suqs	}
1127241675Suqs
1128294113Sbapt	return 0;
1129241675Suqs}
1130241675Suqs
1131241675Suqsstatic int
1132241675Suqsmdoc_fd_pre(MDOC_ARGS)
1133241675Suqs{
1134241675Suqs	struct tag	*t;
1135316420Sbapt	char		*buf, *cp;
1136241675Suqs
1137241675Suqs	synopsis_pre(h, n);
1138241675Suqs
1139241675Suqs	if (NULL == (n = n->child))
1140294113Sbapt		return 0;
1141241675Suqs
1142294113Sbapt	assert(n->type == ROFFT_TEXT);
1143241675Suqs
1144241675Suqs	if (strcmp(n->string, "#include")) {
1145322249Sbapt		print_otag(h, TAG_B, "cT", "Fd");
1146294113Sbapt		return 1;
1147241675Suqs	}
1148241675Suqs
1149322249Sbapt	print_otag(h, TAG_B, "cT", "In");
1150241675Suqs	print_text(h, n->string);
1151241675Suqs
1152241675Suqs	if (NULL != (n = n->next)) {
1153294113Sbapt		assert(n->type == ROFFT_TEXT);
1154241675Suqs
1155241675Suqs		if (h->base_includes) {
1156316420Sbapt			cp = n->string;
1157316420Sbapt			if (*cp == '<' || *cp == '"')
1158316420Sbapt				cp++;
1159316420Sbapt			buf = mandoc_strdup(cp);
1160316420Sbapt			cp = strchr(buf, '\0') - 1;
1161316420Sbapt			if (cp >= buf && (*cp == '>' || *cp == '"'))
1162316420Sbapt				*cp = '\0';
1163322249Sbapt			t = print_otag(h, TAG_A, "cThI", "In", buf);
1164316420Sbapt			free(buf);
1165316420Sbapt		} else
1166322249Sbapt			t = print_otag(h, TAG_A, "cT", "In");
1167241675Suqs
1168241675Suqs		print_text(h, n->string);
1169241675Suqs		print_tagq(h, t);
1170241675Suqs
1171241675Suqs		n = n->next;
1172241675Suqs	}
1173241675Suqs
1174241675Suqs	for ( ; n; n = n->next) {
1175294113Sbapt		assert(n->type == ROFFT_TEXT);
1176241675Suqs		print_text(h, n->string);
1177241675Suqs	}
1178241675Suqs
1179294113Sbapt	return 0;
1180241675Suqs}
1181241675Suqs
1182241675Suqsstatic int
1183241675Suqsmdoc_vt_pre(MDOC_ARGS)
1184241675Suqs{
1185294113Sbapt	if (n->type == ROFFT_BLOCK) {
1186241675Suqs		synopsis_pre(h, n);
1187294113Sbapt		return 1;
1188294113Sbapt	} else if (n->type == ROFFT_ELEM) {
1189241675Suqs		synopsis_pre(h, n);
1190294113Sbapt	} else if (n->type == ROFFT_HEAD)
1191294113Sbapt		return 0;
1192241675Suqs
1193322249Sbapt	print_otag(h, TAG_VAR, "cT", "Vt");
1194294113Sbapt	return 1;
1195241675Suqs}
1196241675Suqs
1197241675Suqsstatic int
1198241675Suqsmdoc_ft_pre(MDOC_ARGS)
1199241675Suqs{
1200241675Suqs	synopsis_pre(h, n);
1201322249Sbapt	print_otag(h, TAG_VAR, "cT", "Ft");
1202294113Sbapt	return 1;
1203241675Suqs}
1204241675Suqs
1205241675Suqsstatic int
1206241675Suqsmdoc_fn_pre(MDOC_ARGS)
1207241675Suqs{
1208241675Suqs	struct tag	*t;
1209241675Suqs	char		 nbuf[BUFSIZ];
1210241675Suqs	const char	*sp, *ep;
1211316420Sbapt	int		 sz, pretty;
1212241675Suqs
1213316420Sbapt	pretty = NODE_SYNPRETTY & n->flags;
1214241675Suqs	synopsis_pre(h, n);
1215241675Suqs
1216241675Suqs	/* Split apart into type and name. */
1217241675Suqs	assert(n->child->string);
1218241675Suqs	sp = n->child->string;
1219241675Suqs
1220241675Suqs	ep = strchr(sp, ' ');
1221241675Suqs	if (NULL != ep) {
1222322249Sbapt		t = print_otag(h, TAG_VAR, "cT", "Ft");
1223274880Sbapt
1224241675Suqs		while (ep) {
1225241675Suqs			sz = MIN((int)(ep - sp), BUFSIZ - 1);
1226241675Suqs			(void)memcpy(nbuf, sp, (size_t)sz);
1227241675Suqs			nbuf[sz] = '\0';
1228241675Suqs			print_text(h, nbuf);
1229241675Suqs			sp = ++ep;
1230241675Suqs			ep = strchr(sp, ' ');
1231241675Suqs		}
1232241675Suqs		print_tagq(h, t);
1233241675Suqs	}
1234241675Suqs
1235322249Sbapt	t = print_otag(h, TAG_B, "cT", "Fn");
1236241675Suqs
1237274880Sbapt	if (sp)
1238274880Sbapt		print_text(h, sp);
1239241675Suqs
1240241675Suqs	print_tagq(h, t);
1241241675Suqs
1242241675Suqs	h->flags |= HTML_NOSPACE;
1243241675Suqs	print_text(h, "(");
1244241675Suqs	h->flags |= HTML_NOSPACE;
1245241675Suqs
1246241675Suqs	for (n = n->child->next; n; n = n->next) {
1247316420Sbapt		if (NODE_SYNPRETTY & n->flags)
1248322249Sbapt			t = print_otag(h, TAG_VAR, "cTss?", "Fa",
1249316420Sbapt			    "white-space", "nowrap");
1250316420Sbapt		else
1251322249Sbapt			t = print_otag(h, TAG_VAR, "cT", "Fa");
1252241675Suqs		print_text(h, n->string);
1253241675Suqs		print_tagq(h, t);
1254241675Suqs		if (n->next) {
1255241675Suqs			h->flags |= HTML_NOSPACE;
1256241675Suqs			print_text(h, ",");
1257241675Suqs		}
1258241675Suqs	}
1259241675Suqs
1260241675Suqs	h->flags |= HTML_NOSPACE;
1261241675Suqs	print_text(h, ")");
1262241675Suqs
1263241675Suqs	if (pretty) {
1264241675Suqs		h->flags |= HTML_NOSPACE;
1265241675Suqs		print_text(h, ";");
1266241675Suqs	}
1267241675Suqs
1268294113Sbapt	return 0;
1269241675Suqs}
1270241675Suqs
1271241675Suqsstatic int
1272241675Suqsmdoc_sm_pre(MDOC_ARGS)
1273241675Suqs{
1274241675Suqs
1275274880Sbapt	if (NULL == n->child)
1276274880Sbapt		h->flags ^= HTML_NONOSPACE;
1277274880Sbapt	else if (0 == strcmp("on", n->child->string))
1278241675Suqs		h->flags &= ~HTML_NONOSPACE;
1279274880Sbapt	else
1280241675Suqs		h->flags |= HTML_NONOSPACE;
1281241675Suqs
1282274880Sbapt	if ( ! (HTML_NONOSPACE & h->flags))
1283274880Sbapt		h->flags &= ~HTML_NOSPACE;
1284274880Sbapt
1285294113Sbapt	return 0;
1286241675Suqs}
1287241675Suqs
1288241675Suqsstatic int
1289274880Sbaptmdoc_skip_pre(MDOC_ARGS)
1290274880Sbapt{
1291274880Sbapt
1292294113Sbapt	return 0;
1293274880Sbapt}
1294274880Sbapt
1295274880Sbaptstatic int
1296241675Suqsmdoc_pp_pre(MDOC_ARGS)
1297241675Suqs{
1298241675Suqs
1299275432Sbapt	print_paragraph(h);
1300294113Sbapt	return 0;
1301241675Suqs}
1302241675Suqs
1303241675Suqsstatic int
1304322249Sbaptmdoc_lk_pre(MDOC_ARGS)
1305241675Suqs{
1306322249Sbapt	const struct roff_node *link, *descr, *punct;
1307322249Sbapt	struct tag	*t;
1308241675Suqs
1309322249Sbapt	if ((link = n->child) == NULL)
1310294113Sbapt		return 0;
1311241675Suqs
1312322249Sbapt	/* Find beginning of trailing punctuation. */
1313322249Sbapt	punct = n->last;
1314322249Sbapt	while (punct != link && punct->flags & NODE_DELIMC)
1315322249Sbapt		punct = punct->prev;
1316322249Sbapt	punct = punct->next;
1317241675Suqs
1318322249Sbapt	/* Link target and link text. */
1319322249Sbapt	descr = link->next;
1320322249Sbapt	if (descr == punct)
1321322249Sbapt		descr = link;  /* no text */
1322322249Sbapt	t = print_otag(h, TAG_A, "cTh", "Lk", link->string);
1323322249Sbapt	do {
1324322249Sbapt		if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
1325322249Sbapt			h->flags |= HTML_NOSPACE;
1326322249Sbapt		print_text(h, descr->string);
1327322249Sbapt		descr = descr->next;
1328322249Sbapt	} while (descr != punct);
1329322249Sbapt	print_tagq(h, t);
1330241675Suqs
1331322249Sbapt	/* Trailing punctuation. */
1332322249Sbapt	while (punct != NULL) {
1333322249Sbapt		h->flags |= HTML_NOSPACE;
1334322249Sbapt		print_text(h, punct->string);
1335322249Sbapt		punct = punct->next;
1336322249Sbapt	}
1337294113Sbapt	return 0;
1338241675Suqs}
1339241675Suqs
1340241675Suqsstatic int
1341241675Suqsmdoc_mt_pre(MDOC_ARGS)
1342241675Suqs{
1343241675Suqs	struct tag	*t;
1344316420Sbapt	char		*cp;
1345241675Suqs
1346241675Suqs	for (n = n->child; n; n = n->next) {
1347294113Sbapt		assert(n->type == ROFFT_TEXT);
1348241675Suqs
1349316420Sbapt		mandoc_asprintf(&cp, "mailto:%s", n->string);
1350322249Sbapt		t = print_otag(h, TAG_A, "cTh", "Mt", cp);
1351241675Suqs		print_text(h, n->string);
1352241675Suqs		print_tagq(h, t);
1353316420Sbapt		free(cp);
1354241675Suqs	}
1355274880Sbapt
1356294113Sbapt	return 0;
1357241675Suqs}
1358241675Suqs
1359241675Suqsstatic int
1360241675Suqsmdoc_fo_pre(MDOC_ARGS)
1361241675Suqs{
1362241675Suqs	struct tag	*t;
1363241675Suqs
1364294113Sbapt	if (n->type == ROFFT_BODY) {
1365241675Suqs		h->flags |= HTML_NOSPACE;
1366241675Suqs		print_text(h, "(");
1367241675Suqs		h->flags |= HTML_NOSPACE;
1368294113Sbapt		return 1;
1369294113Sbapt	} else if (n->type == ROFFT_BLOCK) {
1370241675Suqs		synopsis_pre(h, n);
1371294113Sbapt		return 1;
1372241675Suqs	}
1373241675Suqs
1374294113Sbapt	if (n->child == NULL)
1375294113Sbapt		return 0;
1376241675Suqs
1377241675Suqs	assert(n->child->string);
1378322249Sbapt	t = print_otag(h, TAG_B, "cT", "Fn");
1379241675Suqs	print_text(h, n->child->string);
1380241675Suqs	print_tagq(h, t);
1381294113Sbapt	return 0;
1382241675Suqs}
1383241675Suqs
1384241675Suqsstatic void
1385241675Suqsmdoc_fo_post(MDOC_ARGS)
1386241675Suqs{
1387241675Suqs
1388294113Sbapt	if (n->type != ROFFT_BODY)
1389241675Suqs		return;
1390241675Suqs	h->flags |= HTML_NOSPACE;
1391241675Suqs	print_text(h, ")");
1392241675Suqs	h->flags |= HTML_NOSPACE;
1393241675Suqs	print_text(h, ";");
1394241675Suqs}
1395241675Suqs
1396241675Suqsstatic int
1397241675Suqsmdoc_in_pre(MDOC_ARGS)
1398241675Suqs{
1399241675Suqs	struct tag	*t;
1400241675Suqs
1401241675Suqs	synopsis_pre(h, n);
1402322249Sbapt	print_otag(h, TAG_B, "cT", "In");
1403241675Suqs
1404241675Suqs	/*
1405241675Suqs	 * The first argument of the `In' gets special treatment as
1406241675Suqs	 * being a linked value.  Subsequent values are printed
1407241675Suqs	 * afterward.  groff does similarly.  This also handles the case
1408241675Suqs	 * of no children.
1409241675Suqs	 */
1410241675Suqs
1411316420Sbapt	if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags)
1412241675Suqs		print_text(h, "#include");
1413241675Suqs
1414241675Suqs	print_text(h, "<");
1415241675Suqs	h->flags |= HTML_NOSPACE;
1416241675Suqs
1417241675Suqs	if (NULL != (n = n->child)) {
1418294113Sbapt		assert(n->type == ROFFT_TEXT);
1419241675Suqs
1420316420Sbapt		if (h->base_includes)
1421322249Sbapt			t = print_otag(h, TAG_A, "cThI", "In", n->string);
1422316420Sbapt		else
1423322249Sbapt			t = print_otag(h, TAG_A, "cT", "In");
1424241675Suqs		print_text(h, n->string);
1425241675Suqs		print_tagq(h, t);
1426241675Suqs
1427241675Suqs		n = n->next;
1428241675Suqs	}
1429241675Suqs
1430241675Suqs	h->flags |= HTML_NOSPACE;
1431241675Suqs	print_text(h, ">");
1432241675Suqs
1433241675Suqs	for ( ; n; n = n->next) {
1434294113Sbapt		assert(n->type == ROFFT_TEXT);
1435241675Suqs		print_text(h, n->string);
1436241675Suqs	}
1437241675Suqs
1438294113Sbapt	return 0;
1439241675Suqs}
1440241675Suqs
1441241675Suqsstatic int
1442241675Suqsmdoc_ic_pre(MDOC_ARGS)
1443241675Suqs{
1444322249Sbapt	char	*id;
1445322249Sbapt
1446322249Sbapt	if ((id = cond_id(n)) != NULL)
1447322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1448322249Sbapt	print_otag(h, TAG_B, "cTi", "Ic", id);
1449322249Sbapt	free(id);
1450294113Sbapt	return 1;
1451241675Suqs}
1452241675Suqs
1453241675Suqsstatic int
1454241675Suqsmdoc_va_pre(MDOC_ARGS)
1455241675Suqs{
1456322249Sbapt	print_otag(h, TAG_VAR, "cT", "Va");
1457294113Sbapt	return 1;
1458241675Suqs}
1459241675Suqs
1460241675Suqsstatic int
1461241675Suqsmdoc_ap_pre(MDOC_ARGS)
1462241675Suqs{
1463274880Sbapt
1464241675Suqs	h->flags |= HTML_NOSPACE;
1465241675Suqs	print_text(h, "\\(aq");
1466241675Suqs	h->flags |= HTML_NOSPACE;
1467294113Sbapt	return 1;
1468241675Suqs}
1469241675Suqs
1470241675Suqsstatic int
1471241675Suqsmdoc_bf_pre(MDOC_ARGS)
1472241675Suqs{
1473316420Sbapt	const char	*cattr;
1474241675Suqs
1475294113Sbapt	if (n->type == ROFFT_HEAD)
1476294113Sbapt		return 0;
1477294113Sbapt	else if (n->type != ROFFT_BODY)
1478294113Sbapt		return 1;
1479241675Suqs
1480274880Sbapt	if (FONT_Em == n->norm->Bf.font)
1481316420Sbapt		cattr = "Em";
1482274880Sbapt	else if (FONT_Sy == n->norm->Bf.font)
1483316420Sbapt		cattr = "Sy";
1484274880Sbapt	else if (FONT_Li == n->norm->Bf.font)
1485316420Sbapt		cattr = "Li";
1486241675Suqs	else
1487322249Sbapt		cattr = "No";
1488241675Suqs
1489274880Sbapt	/*
1490241675Suqs	 * We want this to be inline-formatted, but needs to be div to
1491274880Sbapt	 * accept block children.
1492241675Suqs	 */
1493316420Sbapt
1494316420Sbapt	print_otag(h, TAG_DIV, "css?hl", cattr, "display", "inline", 1);
1495294113Sbapt	return 1;
1496241675Suqs}
1497241675Suqs
1498241675Suqsstatic int
1499241675Suqsmdoc_ms_pre(MDOC_ARGS)
1500241675Suqs{
1501322249Sbapt	char *id;
1502322249Sbapt
1503322249Sbapt	if ((id = cond_id(n)) != NULL)
1504322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1505322249Sbapt	print_otag(h, TAG_B, "cTi", "Ms", id);
1506322249Sbapt	free(id);
1507294113Sbapt	return 1;
1508241675Suqs}
1509241675Suqs
1510241675Suqsstatic int
1511241675Suqsmdoc_igndelim_pre(MDOC_ARGS)
1512241675Suqs{
1513241675Suqs
1514241675Suqs	h->flags |= HTML_IGNDELIM;
1515294113Sbapt	return 1;
1516241675Suqs}
1517241675Suqs
1518241675Suqsstatic void
1519241675Suqsmdoc_pf_post(MDOC_ARGS)
1520241675Suqs{
1521241675Suqs
1522316420Sbapt	if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1523276219Sbapt		h->flags |= HTML_NOSPACE;
1524241675Suqs}
1525241675Suqs
1526241675Suqsstatic int
1527241675Suqsmdoc_rs_pre(MDOC_ARGS)
1528241675Suqs{
1529294113Sbapt	if (n->type != ROFFT_BLOCK)
1530294113Sbapt		return 1;
1531241675Suqs
1532241675Suqs	if (n->prev && SEC_SEE_ALSO == n->sec)
1533275432Sbapt		print_paragraph(h);
1534241675Suqs
1535322249Sbapt	print_otag(h, TAG_CITE, "cT", "Rs");
1536294113Sbapt	return 1;
1537241675Suqs}
1538241675Suqs
1539241675Suqsstatic int
1540275432Sbaptmdoc_no_pre(MDOC_ARGS)
1541275432Sbapt{
1542322249Sbapt	char *id;
1543322249Sbapt
1544322249Sbapt	if ((id = cond_id(n)) != NULL)
1545322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1546322249Sbapt	print_otag(h, TAG_SPAN, "ci", "No", id);
1547322249Sbapt	free(id);
1548294113Sbapt	return 1;
1549275432Sbapt}
1550275432Sbapt
1551275432Sbaptstatic int
1552241675Suqsmdoc_li_pre(MDOC_ARGS)
1553241675Suqs{
1554322249Sbapt	char	*id;
1555322249Sbapt
1556322249Sbapt	if ((id = cond_id(n)) != NULL)
1557322249Sbapt		print_otag(h, TAG_A, "chR", "selflink", id);
1558322249Sbapt	print_otag(h, TAG_CODE, "ci", "Li", id);
1559322249Sbapt	free(id);
1560294113Sbapt	return 1;
1561241675Suqs}
1562241675Suqs
1563241675Suqsstatic int
1564241675Suqsmdoc_sy_pre(MDOC_ARGS)
1565241675Suqs{
1566322249Sbapt	print_otag(h, TAG_B, "cT", "Sy");
1567294113Sbapt	return 1;
1568241675Suqs}
1569241675Suqs
1570241675Suqsstatic int
1571241675Suqsmdoc_lb_pre(MDOC_ARGS)
1572241675Suqs{
1573316420Sbapt	if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev)
1574316420Sbapt		print_otag(h, TAG_BR, "");
1575241675Suqs
1576322249Sbapt	print_otag(h, TAG_SPAN, "cT", "Lb");
1577294113Sbapt	return 1;
1578241675Suqs}
1579241675Suqs
1580241675Suqsstatic int
1581241675Suqsmdoc__x_pre(MDOC_ARGS)
1582241675Suqs{
1583316420Sbapt	const char	*cattr;
1584316420Sbapt	enum htmltag	 t;
1585241675Suqs
1586241675Suqs	t = TAG_SPAN;
1587241675Suqs
1588241675Suqs	switch (n->tok) {
1589274880Sbapt	case MDOC__A:
1590316420Sbapt		cattr = "RsA";
1591241675Suqs		if (n->prev && MDOC__A == n->prev->tok)
1592241675Suqs			if (NULL == n->next || MDOC__A != n->next->tok)
1593241675Suqs				print_text(h, "and");
1594241675Suqs		break;
1595274880Sbapt	case MDOC__B:
1596241675Suqs		t = TAG_I;
1597316420Sbapt		cattr = "RsB";
1598241675Suqs		break;
1599274880Sbapt	case MDOC__C:
1600316420Sbapt		cattr = "RsC";
1601241675Suqs		break;
1602274880Sbapt	case MDOC__D:
1603316420Sbapt		cattr = "RsD";
1604241675Suqs		break;
1605274880Sbapt	case MDOC__I:
1606241675Suqs		t = TAG_I;
1607316420Sbapt		cattr = "RsI";
1608241675Suqs		break;
1609274880Sbapt	case MDOC__J:
1610241675Suqs		t = TAG_I;
1611316420Sbapt		cattr = "RsJ";
1612241675Suqs		break;
1613274880Sbapt	case MDOC__N:
1614316420Sbapt		cattr = "RsN";
1615241675Suqs		break;
1616274880Sbapt	case MDOC__O:
1617316420Sbapt		cattr = "RsO";
1618241675Suqs		break;
1619274880Sbapt	case MDOC__P:
1620316420Sbapt		cattr = "RsP";
1621241675Suqs		break;
1622274880Sbapt	case MDOC__Q:
1623316420Sbapt		cattr = "RsQ";
1624241675Suqs		break;
1625274880Sbapt	case MDOC__R:
1626316420Sbapt		cattr = "RsR";
1627241675Suqs		break;
1628274880Sbapt	case MDOC__T:
1629316420Sbapt		cattr = "RsT";
1630241675Suqs		break;
1631274880Sbapt	case MDOC__U:
1632316420Sbapt		print_otag(h, TAG_A, "ch", "RsU", n->child->string);
1633316420Sbapt		return 1;
1634274880Sbapt	case MDOC__V:
1635316420Sbapt		cattr = "RsV";
1636241675Suqs		break;
1637241675Suqs	default:
1638241675Suqs		abort();
1639241675Suqs	}
1640241675Suqs
1641316420Sbapt	print_otag(h, t, "c", cattr);
1642294113Sbapt	return 1;
1643241675Suqs}
1644241675Suqs
1645241675Suqsstatic void
1646241675Suqsmdoc__x_post(MDOC_ARGS)
1647241675Suqs{
1648241675Suqs
1649241675Suqs	if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
1650241675Suqs		if (NULL == n->next->next || MDOC__A != n->next->next->tok)
1651241675Suqs			if (NULL == n->prev || MDOC__A != n->prev->tok)
1652241675Suqs				return;
1653241675Suqs
1654241675Suqs	/* TODO: %U */
1655241675Suqs
1656241675Suqs	if (NULL == n->parent || MDOC_Rs != n->parent->tok)
1657241675Suqs		return;
1658241675Suqs
1659241675Suqs	h->flags |= HTML_NOSPACE;
1660241675Suqs	print_text(h, n->next ? "," : ".");
1661241675Suqs}
1662241675Suqs
1663241675Suqsstatic int
1664241675Suqsmdoc_bk_pre(MDOC_ARGS)
1665241675Suqs{
1666241675Suqs
1667241675Suqs	switch (n->type) {
1668294113Sbapt	case ROFFT_BLOCK:
1669241675Suqs		break;
1670294113Sbapt	case ROFFT_HEAD:
1671294113Sbapt		return 0;
1672294113Sbapt	case ROFFT_BODY:
1673294113Sbapt		if (n->parent->args != NULL || n->prev->child == NULL)
1674241675Suqs			h->flags |= HTML_PREKEEP;
1675241675Suqs		break;
1676241675Suqs	default:
1677241675Suqs		abort();
1678241675Suqs	}
1679241675Suqs
1680294113Sbapt	return 1;
1681241675Suqs}
1682241675Suqs
1683241675Suqsstatic void
1684241675Suqsmdoc_bk_post(MDOC_ARGS)
1685241675Suqs{
1686241675Suqs
1687294113Sbapt	if (n->type == ROFFT_BODY)
1688241675Suqs		h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
1689241675Suqs}
1690241675Suqs
1691241675Suqsstatic int
1692241675Suqsmdoc_quote_pre(MDOC_ARGS)
1693241675Suqs{
1694294113Sbapt	if (n->type != ROFFT_BODY)
1695294113Sbapt		return 1;
1696241675Suqs
1697241675Suqs	switch (n->tok) {
1698274880Sbapt	case MDOC_Ao:
1699274880Sbapt	case MDOC_Aq:
1700294113Sbapt		print_text(h, n->child != NULL && n->child->next == NULL &&
1701279527Sbapt		    n->child->tok == MDOC_Mt ?  "<" : "\\(la");
1702241675Suqs		break;
1703274880Sbapt	case MDOC_Bro:
1704274880Sbapt	case MDOC_Brq:
1705241675Suqs		print_text(h, "\\(lC");
1706241675Suqs		break;
1707274880Sbapt	case MDOC_Bo:
1708274880Sbapt	case MDOC_Bq:
1709241675Suqs		print_text(h, "\\(lB");
1710241675Suqs		break;
1711274880Sbapt	case MDOC_Oo:
1712274880Sbapt	case MDOC_Op:
1713241675Suqs		print_text(h, "\\(lB");
1714241675Suqs		h->flags |= HTML_NOSPACE;
1715316420Sbapt		print_otag(h, TAG_SPAN, "c", "Op");
1716241675Suqs		break;
1717274880Sbapt	case MDOC_En:
1718274880Sbapt		if (NULL == n->norm->Es ||
1719274880Sbapt		    NULL == n->norm->Es->child)
1720294113Sbapt			return 1;
1721274880Sbapt		print_text(h, n->norm->Es->child->string);
1722241675Suqs		break;
1723274880Sbapt	case MDOC_Do:
1724274880Sbapt	case MDOC_Dq:
1725274880Sbapt	case MDOC_Qo:
1726274880Sbapt	case MDOC_Qq:
1727241675Suqs		print_text(h, "\\(lq");
1728241675Suqs		break;
1729274880Sbapt	case MDOC_Po:
1730274880Sbapt	case MDOC_Pq:
1731241675Suqs		print_text(h, "(");
1732241675Suqs		break;
1733274880Sbapt	case MDOC_Ql:
1734241675Suqs		print_text(h, "\\(oq");
1735241675Suqs		h->flags |= HTML_NOSPACE;
1736316420Sbapt		print_otag(h, TAG_CODE, "c", "Li");
1737241675Suqs		break;
1738274880Sbapt	case MDOC_So:
1739274880Sbapt	case MDOC_Sq:
1740241675Suqs		print_text(h, "\\(oq");
1741241675Suqs		break;
1742241675Suqs	default:
1743241675Suqs		abort();
1744241675Suqs	}
1745241675Suqs
1746241675Suqs	h->flags |= HTML_NOSPACE;
1747294113Sbapt	return 1;
1748241675Suqs}
1749241675Suqs
1750241675Suqsstatic void
1751241675Suqsmdoc_quote_post(MDOC_ARGS)
1752241675Suqs{
1753241675Suqs
1754294113Sbapt	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1755241675Suqs		return;
1756241675Suqs
1757279527Sbapt	h->flags |= HTML_NOSPACE;
1758241675Suqs
1759241675Suqs	switch (n->tok) {
1760274880Sbapt	case MDOC_Ao:
1761274880Sbapt	case MDOC_Aq:
1762294113Sbapt		print_text(h, n->child != NULL && n->child->next == NULL &&
1763279527Sbapt		    n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
1764241675Suqs		break;
1765274880Sbapt	case MDOC_Bro:
1766274880Sbapt	case MDOC_Brq:
1767241675Suqs		print_text(h, "\\(rC");
1768241675Suqs		break;
1769274880Sbapt	case MDOC_Oo:
1770274880Sbapt	case MDOC_Op:
1771274880Sbapt	case MDOC_Bo:
1772274880Sbapt	case MDOC_Bq:
1773241675Suqs		print_text(h, "\\(rB");
1774241675Suqs		break;
1775274880Sbapt	case MDOC_En:
1776279527Sbapt		if (n->norm->Es == NULL ||
1777279527Sbapt		    n->norm->Es->child == NULL ||
1778279527Sbapt		    n->norm->Es->child->next == NULL)
1779279527Sbapt			h->flags &= ~HTML_NOSPACE;
1780279527Sbapt		else
1781274880Sbapt			print_text(h, n->norm->Es->child->next->string);
1782241675Suqs		break;
1783274880Sbapt	case MDOC_Qo:
1784274880Sbapt	case MDOC_Qq:
1785274880Sbapt	case MDOC_Do:
1786274880Sbapt	case MDOC_Dq:
1787241675Suqs		print_text(h, "\\(rq");
1788241675Suqs		break;
1789274880Sbapt	case MDOC_Po:
1790274880Sbapt	case MDOC_Pq:
1791241675Suqs		print_text(h, ")");
1792241675Suqs		break;
1793274880Sbapt	case MDOC_Ql:
1794274880Sbapt	case MDOC_So:
1795274880Sbapt	case MDOC_Sq:
1796261344Suqs		print_text(h, "\\(cq");
1797241675Suqs		break;
1798241675Suqs	default:
1799241675Suqs		abort();
1800241675Suqs	}
1801241675Suqs}
1802279527Sbapt
1803279527Sbaptstatic int
1804279527Sbaptmdoc_eo_pre(MDOC_ARGS)
1805279527Sbapt{
1806279527Sbapt
1807294113Sbapt	if (n->type != ROFFT_BODY)
1808294113Sbapt		return 1;
1809279527Sbapt
1810279527Sbapt	if (n->end == ENDBODY_NOT &&
1811279527Sbapt	    n->parent->head->child == NULL &&
1812279527Sbapt	    n->child != NULL &&
1813279527Sbapt	    n->child->end != ENDBODY_NOT)
1814279527Sbapt		print_text(h, "\\&");
1815279527Sbapt	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1816279527Sbapt	    n->parent->head->child != NULL && (n->child != NULL ||
1817279527Sbapt	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1818279527Sbapt		h->flags |= HTML_NOSPACE;
1819294113Sbapt	return 1;
1820279527Sbapt}
1821279527Sbapt
1822279527Sbaptstatic void
1823279527Sbaptmdoc_eo_post(MDOC_ARGS)
1824279527Sbapt{
1825279527Sbapt	int	 body, tail;
1826279527Sbapt
1827294113Sbapt	if (n->type != ROFFT_BODY)
1828279527Sbapt		return;
1829279527Sbapt
1830279527Sbapt	if (n->end != ENDBODY_NOT) {
1831279527Sbapt		h->flags &= ~HTML_NOSPACE;
1832279527Sbapt		return;
1833279527Sbapt	}
1834279527Sbapt
1835279527Sbapt	body = n->child != NULL || n->parent->head->child != NULL;
1836279527Sbapt	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1837279527Sbapt
1838279527Sbapt	if (body && tail)
1839279527Sbapt		h->flags |= HTML_NOSPACE;
1840279527Sbapt	else if ( ! tail)
1841279527Sbapt		h->flags &= ~HTML_NOSPACE;
1842279527Sbapt}
1843