xref: /dragonfly/contrib/mdocml/man_html.c (revision 54ba9607)
1*54ba9607SSascha Wildner /*	$Id: man_html.c,v 1.173 2019/03/02 16:30:53 schwarze Exp $ */
280387638SSascha Wildner /*
3*54ba9607SSascha Wildner  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*54ba9607SSascha Wildner  * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
580387638SSascha Wildner  *
680387638SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
780387638SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
880387638SSascha Wildner  * copyright notice and this permission notice appear in all copies.
980387638SSascha Wildner  *
10*54ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1180387638SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*54ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1380387638SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1480387638SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1580387638SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1680387638SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1780387638SSascha Wildner  */
1880387638SSascha Wildner #include "config.h"
1980387638SSascha Wildner 
2080387638SSascha Wildner #include <sys/types.h>
2180387638SSascha Wildner 
2280387638SSascha Wildner #include <assert.h>
2380387638SSascha Wildner #include <ctype.h>
2480387638SSascha Wildner #include <stdio.h>
2580387638SSascha Wildner #include <stdlib.h>
2680387638SSascha Wildner #include <string.h>
2780387638SSascha Wildner 
28070c62a6SFranco Fichtner #include "mandoc_aux.h"
29*54ba9607SSascha Wildner #include "mandoc.h"
30*54ba9607SSascha Wildner #include "roff.h"
31*54ba9607SSascha Wildner #include "man.h"
3280387638SSascha Wildner #include "out.h"
3380387638SSascha Wildner #include "html.h"
3480387638SSascha Wildner #include "main.h"
3580387638SSascha Wildner 
36*54ba9607SSascha Wildner #define	MAN_ARGS	  const struct roff_meta *man, \
37*54ba9607SSascha Wildner 			  const struct roff_node *n, \
3880387638SSascha Wildner 			  struct html *h
3980387638SSascha Wildner 
40*54ba9607SSascha Wildner struct	man_html_act {
4180387638SSascha Wildner 	int		(*pre)(MAN_ARGS);
4280387638SSascha Wildner 	int		(*post)(MAN_ARGS);
4380387638SSascha Wildner };
4480387638SSascha Wildner 
45*54ba9607SSascha Wildner static	void		  print_man_head(const struct roff_meta *,
46*54ba9607SSascha Wildner 				struct html *);
4780387638SSascha Wildner static	void		  print_man_nodelist(MAN_ARGS);
4880387638SSascha Wildner static	void		  print_man_node(MAN_ARGS);
49*54ba9607SSascha Wildner static	char		  list_continues(const struct roff_node *,
50*54ba9607SSascha Wildner 				const struct roff_node *);
5136342e81SSascha Wildner static	int		  man_B_pre(MAN_ARGS);
5236342e81SSascha Wildner static	int		  man_IP_pre(MAN_ARGS);
5336342e81SSascha Wildner static	int		  man_I_pre(MAN_ARGS);
5436342e81SSascha Wildner static	int		  man_OP_pre(MAN_ARGS);
5536342e81SSascha Wildner static	int		  man_PP_pre(MAN_ARGS);
5636342e81SSascha Wildner static	int		  man_RS_pre(MAN_ARGS);
5736342e81SSascha Wildner static	int		  man_SH_pre(MAN_ARGS);
5836342e81SSascha Wildner static	int		  man_SM_pre(MAN_ARGS);
59*54ba9607SSascha Wildner static	int		  man_SY_pre(MAN_ARGS);
607888c61dSFranco Fichtner static	int		  man_UR_pre(MAN_ARGS);
61*54ba9607SSascha Wildner static	int		  man_abort_pre(MAN_ARGS);
6280387638SSascha Wildner static	int		  man_alt_pre(MAN_ARGS);
6380387638SSascha Wildner static	int		  man_ign_pre(MAN_ARGS);
6480387638SSascha Wildner static	int		  man_in_pre(MAN_ARGS);
65*54ba9607SSascha Wildner static	void		  man_root_post(const struct roff_meta *,
66*54ba9607SSascha Wildner 				struct html *);
67*54ba9607SSascha Wildner static	void		  man_root_pre(const struct roff_meta *,
68*54ba9607SSascha Wildner 				struct html *);
6980387638SSascha Wildner 
70*54ba9607SSascha Wildner static	const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
7180387638SSascha Wildner 	{ NULL, NULL }, /* TH */
7280387638SSascha Wildner 	{ man_SH_pre, NULL }, /* SH */
73*54ba9607SSascha Wildner 	{ man_SH_pre, NULL }, /* SS */
7480387638SSascha Wildner 	{ man_IP_pre, NULL }, /* TP */
75*54ba9607SSascha Wildner 	{ man_IP_pre, NULL }, /* TQ */
76*54ba9607SSascha Wildner 	{ man_abort_pre, NULL }, /* LP */
7780387638SSascha Wildner 	{ man_PP_pre, NULL }, /* PP */
78*54ba9607SSascha Wildner 	{ man_abort_pre, NULL }, /* P */
7980387638SSascha Wildner 	{ man_IP_pre, NULL }, /* IP */
80*54ba9607SSascha Wildner 	{ man_PP_pre, NULL }, /* HP */
8180387638SSascha Wildner 	{ man_SM_pre, NULL }, /* SM */
8280387638SSascha Wildner 	{ man_SM_pre, NULL }, /* SB */
8380387638SSascha Wildner 	{ man_alt_pre, NULL }, /* BI */
8480387638SSascha Wildner 	{ man_alt_pre, NULL }, /* IB */
8580387638SSascha Wildner 	{ man_alt_pre, NULL }, /* BR */
8680387638SSascha Wildner 	{ man_alt_pre, NULL }, /* RB */
8780387638SSascha Wildner 	{ NULL, NULL }, /* R */
8880387638SSascha Wildner 	{ man_B_pre, NULL }, /* B */
8980387638SSascha Wildner 	{ man_I_pre, NULL }, /* I */
9080387638SSascha Wildner 	{ man_alt_pre, NULL }, /* IR */
9180387638SSascha Wildner 	{ man_alt_pre, NULL }, /* RI */
9280387638SSascha Wildner 	{ NULL, NULL }, /* RE */
9380387638SSascha Wildner 	{ man_RS_pre, NULL }, /* RS */
9480387638SSascha Wildner 	{ man_ign_pre, NULL }, /* DT */
9580387638SSascha Wildner 	{ man_ign_pre, NULL }, /* UC */
9680387638SSascha Wildner 	{ man_ign_pre, NULL }, /* PD */
9780387638SSascha Wildner 	{ man_ign_pre, NULL }, /* AT */
9880387638SSascha Wildner 	{ man_in_pre, NULL }, /* in */
99*54ba9607SSascha Wildner 	{ man_SY_pre, NULL }, /* SY */
100*54ba9607SSascha Wildner 	{ NULL, NULL }, /* YS */
10136342e81SSascha Wildner 	{ man_OP_pre, NULL }, /* OP */
102*54ba9607SSascha Wildner 	{ NULL, NULL }, /* EX */
103*54ba9607SSascha Wildner 	{ NULL, NULL }, /* EE */
1047888c61dSFranco Fichtner 	{ man_UR_pre, NULL }, /* UR */
1057888c61dSFranco Fichtner 	{ NULL, NULL }, /* UE */
106*54ba9607SSascha Wildner 	{ man_UR_pre, NULL }, /* MT */
107*54ba9607SSascha Wildner 	{ NULL, NULL }, /* ME */
10880387638SSascha Wildner };
10980387638SSascha Wildner 
110070c62a6SFranco Fichtner 
11180387638SSascha Wildner void
112*54ba9607SSascha Wildner html_man(void *arg, const struct roff_meta *man)
11380387638SSascha Wildner {
114*54ba9607SSascha Wildner 	struct html		*h;
115*54ba9607SSascha Wildner 	struct roff_node	*n;
116*54ba9607SSascha Wildner 	struct tag		*t;
11780387638SSascha Wildner 
118*54ba9607SSascha Wildner 	h = (struct html *)arg;
119*54ba9607SSascha Wildner 	n = man->first->child;
12080387638SSascha Wildner 
121*54ba9607SSascha Wildner 	if ((h->oflags & HTML_FRAGMENT) == 0) {
12236342e81SSascha Wildner 		print_gen_decls(h);
123*54ba9607SSascha Wildner 		print_otag(h, TAG_HTML, "");
124*54ba9607SSascha Wildner 		if (n != NULL && n->type == ROFFT_COMMENT)
125*54ba9607SSascha Wildner 			print_gen_comment(h, n);
126*54ba9607SSascha Wildner 		t = print_otag(h, TAG_HEAD, "");
127*54ba9607SSascha Wildner 		print_man_head(man, h);
12880387638SSascha Wildner 		print_tagq(h, t);
129*54ba9607SSascha Wildner 		print_otag(h, TAG_BODY, "");
130*54ba9607SSascha Wildner 	}
131*54ba9607SSascha Wildner 
132*54ba9607SSascha Wildner 	man_root_pre(man, h);
133*54ba9607SSascha Wildner 	t = print_otag(h, TAG_DIV, "c", "manual-text");
134*54ba9607SSascha Wildner 	print_man_nodelist(man, n, h);
135*54ba9607SSascha Wildner 	print_tagq(h, t);
136*54ba9607SSascha Wildner 	man_root_post(man, h);
137*54ba9607SSascha Wildner 	print_tagq(h, NULL);
13880387638SSascha Wildner }
13980387638SSascha Wildner 
14080387638SSascha Wildner static void
141*54ba9607SSascha Wildner print_man_head(const struct roff_meta *man, struct html *h)
14280387638SSascha Wildner {
143*54ba9607SSascha Wildner 	char	*cp;
14480387638SSascha Wildner 
14580387638SSascha Wildner 	print_gen_head(h);
146*54ba9607SSascha Wildner 	mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
147*54ba9607SSascha Wildner 	print_otag(h, TAG_TITLE, "");
148*54ba9607SSascha Wildner 	print_text(h, cp);
149*54ba9607SSascha Wildner 	free(cp);
15080387638SSascha Wildner }
15180387638SSascha Wildner 
15280387638SSascha Wildner static void
15380387638SSascha Wildner print_man_nodelist(MAN_ARGS)
15480387638SSascha Wildner {
155*54ba9607SSascha Wildner 	while (n != NULL) {
156*54ba9607SSascha Wildner 		print_man_node(man, n, h);
157*54ba9607SSascha Wildner 		n = n->next;
158*54ba9607SSascha Wildner 	}
15980387638SSascha Wildner }
16080387638SSascha Wildner 
16180387638SSascha Wildner static void
16280387638SSascha Wildner print_man_node(MAN_ARGS)
16380387638SSascha Wildner {
16480387638SSascha Wildner 	struct tag	*t;
165*54ba9607SSascha Wildner 	int		 child;
166*54ba9607SSascha Wildner 
167*54ba9607SSascha Wildner 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
168*54ba9607SSascha Wildner 		return;
169*54ba9607SSascha Wildner 
170*54ba9607SSascha Wildner 	html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
17180387638SSascha Wildner 
17280387638SSascha Wildner 	child = 1;
17380387638SSascha Wildner 	switch (n->type) {
174*54ba9607SSascha Wildner 	case ROFFT_TEXT:
175*54ba9607SSascha Wildner 		if (*n->string == '\0') {
176*54ba9607SSascha Wildner 			print_endline(h);
17760e1e752SSascha Wildner 			return;
17836342e81SSascha Wildner 		}
179*54ba9607SSascha Wildner 		if (*n->string == ' ' && n->flags & NODE_LINE &&
180*54ba9607SSascha Wildner 		    (h->flags & HTML_NONEWLINE) == 0)
181*54ba9607SSascha Wildner 			print_endline(h);
182*54ba9607SSascha Wildner 		else if (n->flags & NODE_DELIMC)
183*54ba9607SSascha Wildner 			h->flags |= HTML_NOSPACE;
184*54ba9607SSascha Wildner 		t = h->tag;
185*54ba9607SSascha Wildner 		t->refcnt++;
18680387638SSascha Wildner 		print_text(h, n->string);
187*54ba9607SSascha Wildner 		break;
188*54ba9607SSascha Wildner 	case ROFFT_EQN:
189*54ba9607SSascha Wildner 		t = h->tag;
190*54ba9607SSascha Wildner 		t->refcnt++;
19136342e81SSascha Wildner 		print_eqn(h, n->eqn);
19280387638SSascha Wildner 		break;
193*54ba9607SSascha Wildner 	case ROFFT_TBL:
19460e1e752SSascha Wildner 		/*
19560e1e752SSascha Wildner 		 * This will take care of initialising all of the table
19660e1e752SSascha Wildner 		 * state data for the first table, then tearing it down
19760e1e752SSascha Wildner 		 * for the last one.
19860e1e752SSascha Wildner 		 */
19960e1e752SSascha Wildner 		print_tbl(h, n->span);
20060e1e752SSascha Wildner 		return;
20180387638SSascha Wildner 	default:
20280387638SSascha Wildner 		/*
20380387638SSascha Wildner 		 * Close out scope of font prior to opening a macro
20460e1e752SSascha Wildner 		 * scope.
20580387638SSascha Wildner 		 */
20680387638SSascha Wildner 		if (HTMLFONT_NONE != h->metac) {
20780387638SSascha Wildner 			h->metal = h->metac;
20880387638SSascha Wildner 			h->metac = HTMLFONT_NONE;
20980387638SSascha Wildner 		}
21060e1e752SSascha Wildner 
21160e1e752SSascha Wildner 		/*
21260e1e752SSascha Wildner 		 * Close out the current table, if it's open, and unset
21360e1e752SSascha Wildner 		 * the "meta" table state.  This will be reopened on the
21460e1e752SSascha Wildner 		 * next table element.
21560e1e752SSascha Wildner 		 */
216*54ba9607SSascha Wildner 		if (h->tblt != NULL)
21760e1e752SSascha Wildner 			print_tblclose(h);
218*54ba9607SSascha Wildner 		t = h->tag;
219*54ba9607SSascha Wildner 		t->refcnt++;
220*54ba9607SSascha Wildner 		if (n->tok < ROFF_MAX) {
221*54ba9607SSascha Wildner 			roff_html_pre(h, n);
222*54ba9607SSascha Wildner 			t->refcnt--;
223*54ba9607SSascha Wildner 			print_stagq(h, t);
224*54ba9607SSascha Wildner 			return;
22560e1e752SSascha Wildner 		}
226*54ba9607SSascha Wildner 		assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
227*54ba9607SSascha Wildner 		if (man_html_acts[n->tok - MAN_TH].pre != NULL)
228*54ba9607SSascha Wildner 			child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
229*54ba9607SSascha Wildner 			    n, h);
23080387638SSascha Wildner 		break;
23180387638SSascha Wildner 	}
23280387638SSascha Wildner 
233*54ba9607SSascha Wildner 	if (child && n->child != NULL)
234*54ba9607SSascha Wildner 		print_man_nodelist(man, n->child, h);
23580387638SSascha Wildner 
23680387638SSascha Wildner 	/* This will automatically close out any font scope. */
237*54ba9607SSascha Wildner 	t->refcnt--;
238*54ba9607SSascha Wildner 	if (n->type == ROFFT_BLOCK &&
239*54ba9607SSascha Wildner 	    (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
240*54ba9607SSascha Wildner 		t = h->tag;
241*54ba9607SSascha Wildner 		while (t->tag != TAG_DL && t->tag != TAG_UL)
242*54ba9607SSascha Wildner 			t = t->next;
243*54ba9607SSascha Wildner 		/*
244*54ba9607SSascha Wildner 		 * Close the list if no further item of the same type
245*54ba9607SSascha Wildner 		 * follows; otherwise, close the item only.
246*54ba9607SSascha Wildner 		 */
247*54ba9607SSascha Wildner 		if (list_continues(n, n->next) == '\0') {
248*54ba9607SSascha Wildner 			print_tagq(h, t);
249*54ba9607SSascha Wildner 			t = NULL;
250*54ba9607SSascha Wildner 		}
251*54ba9607SSascha Wildner 	}
252*54ba9607SSascha Wildner 	if (t != NULL)
25380387638SSascha Wildner 		print_stagq(h, t);
25480387638SSascha Wildner 
255*54ba9607SSascha Wildner 	if (n->flags & NODE_NOFILL && n->tok != MAN_YS &&
256*54ba9607SSascha Wildner 	    (n->next != NULL && n->next->flags & NODE_LINE)) {
257*54ba9607SSascha Wildner 		/* In .nf = <pre>, print even empty lines. */
258*54ba9607SSascha Wildner 		h->col++;
259*54ba9607SSascha Wildner 		print_endline(h);
26080387638SSascha Wildner 	}
26180387638SSascha Wildner }
26280387638SSascha Wildner 
26360e1e752SSascha Wildner static void
264*54ba9607SSascha Wildner man_root_pre(const struct roff_meta *man, struct html *h)
26580387638SSascha Wildner {
26680387638SSascha Wildner 	struct tag	*t, *tt;
267070c62a6SFranco Fichtner 	char		*title;
26880387638SSascha Wildner 
269f88b6c16SFranco Fichtner 	assert(man->title);
270f88b6c16SFranco Fichtner 	assert(man->msec);
271070c62a6SFranco Fichtner 	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
27280387638SSascha Wildner 
273*54ba9607SSascha Wildner 	t = print_otag(h, TAG_TABLE, "c", "head");
274*54ba9607SSascha Wildner 	tt = print_otag(h, TAG_TR, "");
27580387638SSascha Wildner 
276*54ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "head-ltitle");
27780387638SSascha Wildner 	print_text(h, title);
27880387638SSascha Wildner 	print_stagq(h, tt);
27980387638SSascha Wildner 
280*54ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "head-vol");
281*54ba9607SSascha Wildner 	if (man->vol != NULL)
282070c62a6SFranco Fichtner 		print_text(h, man->vol);
28380387638SSascha Wildner 	print_stagq(h, tt);
28480387638SSascha Wildner 
285*54ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "head-rtitle");
28680387638SSascha Wildner 	print_text(h, title);
28780387638SSascha Wildner 	print_tagq(h, t);
288070c62a6SFranco Fichtner 	free(title);
28980387638SSascha Wildner }
29080387638SSascha Wildner 
29180387638SSascha Wildner static void
292*54ba9607SSascha Wildner man_root_post(const struct roff_meta *man, struct html *h)
29380387638SSascha Wildner {
29480387638SSascha Wildner 	struct tag	*t, *tt;
29580387638SSascha Wildner 
296*54ba9607SSascha Wildner 	t = print_otag(h, TAG_TABLE, "c", "foot");
297*54ba9607SSascha Wildner 	tt = print_otag(h, TAG_TR, "");
29880387638SSascha Wildner 
299*54ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "foot-date");
300f88b6c16SFranco Fichtner 	print_text(h, man->date);
30180387638SSascha Wildner 	print_stagq(h, tt);
30280387638SSascha Wildner 
303*54ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "foot-os");
304*54ba9607SSascha Wildner 	if (man->os != NULL)
305*54ba9607SSascha Wildner 		print_text(h, man->os);
30680387638SSascha Wildner 	print_tagq(h, t);
30780387638SSascha Wildner }
30880387638SSascha Wildner 
30980387638SSascha Wildner static int
31080387638SSascha Wildner man_SH_pre(MAN_ARGS)
31180387638SSascha Wildner {
312*54ba9607SSascha Wildner 	const char	*class;
313*54ba9607SSascha Wildner 	char		*id;
314*54ba9607SSascha Wildner 	enum htmltag	 tag;
31580387638SSascha Wildner 
316*54ba9607SSascha Wildner 	if (n->tok == MAN_SH) {
317*54ba9607SSascha Wildner 		tag = TAG_H1;
318*54ba9607SSascha Wildner 		class = "Sh";
319*54ba9607SSascha Wildner 	} else {
320*54ba9607SSascha Wildner 		tag = TAG_H2;
321*54ba9607SSascha Wildner 		class = "Ss";
322*54ba9607SSascha Wildner 	}
323*54ba9607SSascha Wildner 	switch (n->type) {
324*54ba9607SSascha Wildner 	case ROFFT_BLOCK:
325*54ba9607SSascha Wildner 		html_close_paragraph(h);
326*54ba9607SSascha Wildner 		print_otag(h, TAG_SECTION, "c", class);
327*54ba9607SSascha Wildner 		break;
328*54ba9607SSascha Wildner 	case ROFFT_HEAD:
329*54ba9607SSascha Wildner 		id = html_make_id(n, 1);
330*54ba9607SSascha Wildner 		print_otag(h, tag, "ci", class, id);
331*54ba9607SSascha Wildner 		if (id != NULL)
332*54ba9607SSascha Wildner 			print_otag(h, TAG_A, "chR", "permalink", id);
333*54ba9607SSascha Wildner 		break;
334*54ba9607SSascha Wildner 	case ROFFT_BODY:
335*54ba9607SSascha Wildner 		break;
336*54ba9607SSascha Wildner 	default:
337*54ba9607SSascha Wildner 		abort();
338*54ba9607SSascha Wildner 	}
339*54ba9607SSascha Wildner 	return 1;
34080387638SSascha Wildner }
34180387638SSascha Wildner 
34280387638SSascha Wildner static int
34380387638SSascha Wildner man_alt_pre(MAN_ARGS)
34480387638SSascha Wildner {
345*54ba9607SSascha Wildner 	const struct roff_node	*nn;
34680387638SSascha Wildner 	struct tag	*t;
347*54ba9607SSascha Wildner 	int		 i;
348*54ba9607SSascha Wildner 	enum htmltag	 fp;
34980387638SSascha Wildner 
350*54ba9607SSascha Wildner 	for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
35180387638SSascha Wildner 		switch (n->tok) {
352070c62a6SFranco Fichtner 		case MAN_BI:
35380387638SSascha Wildner 			fp = i % 2 ? TAG_I : TAG_B;
35480387638SSascha Wildner 			break;
355070c62a6SFranco Fichtner 		case MAN_IB:
35680387638SSascha Wildner 			fp = i % 2 ? TAG_B : TAG_I;
35780387638SSascha Wildner 			break;
358070c62a6SFranco Fichtner 		case MAN_RI:
35980387638SSascha Wildner 			fp = i % 2 ? TAG_I : TAG_MAX;
36080387638SSascha Wildner 			break;
361070c62a6SFranco Fichtner 		case MAN_IR:
36280387638SSascha Wildner 			fp = i % 2 ? TAG_MAX : TAG_I;
36380387638SSascha Wildner 			break;
364070c62a6SFranco Fichtner 		case MAN_BR:
36580387638SSascha Wildner 			fp = i % 2 ? TAG_MAX : TAG_B;
36680387638SSascha Wildner 			break;
367070c62a6SFranco Fichtner 		case MAN_RB:
36880387638SSascha Wildner 			fp = i % 2 ? TAG_B : TAG_MAX;
36980387638SSascha Wildner 			break;
37080387638SSascha Wildner 		default:
37180387638SSascha Wildner 			abort();
37280387638SSascha Wildner 		}
37380387638SSascha Wildner 
37480387638SSascha Wildner 		if (i)
37580387638SSascha Wildner 			h->flags |= HTML_NOSPACE;
37680387638SSascha Wildner 
377*54ba9607SSascha Wildner 		if (fp != TAG_MAX)
378*54ba9607SSascha Wildner 			t = print_otag(h, fp, "");
37980387638SSascha Wildner 
380*54ba9607SSascha Wildner 		print_text(h, nn->string);
38180387638SSascha Wildner 
382*54ba9607SSascha Wildner 		if (fp != TAG_MAX)
38380387638SSascha Wildner 			print_tagq(h, t);
38480387638SSascha Wildner 	}
385*54ba9607SSascha Wildner 	return 0;
38680387638SSascha Wildner }
38780387638SSascha Wildner 
38880387638SSascha Wildner static int
38980387638SSascha Wildner man_SM_pre(MAN_ARGS)
39080387638SSascha Wildner {
391*54ba9607SSascha Wildner 	print_otag(h, TAG_SMALL, "");
392*54ba9607SSascha Wildner 	if (n->tok == MAN_SB)
393*54ba9607SSascha Wildner 		print_otag(h, TAG_B, "");
394*54ba9607SSascha Wildner 	return 1;
39580387638SSascha Wildner }
39680387638SSascha Wildner 
39780387638SSascha Wildner static int
39880387638SSascha Wildner man_PP_pre(MAN_ARGS)
39980387638SSascha Wildner {
400*54ba9607SSascha Wildner 	switch (n->type) {
401*54ba9607SSascha Wildner 	case ROFFT_BLOCK:
402*54ba9607SSascha Wildner 		html_close_paragraph(h);
403*54ba9607SSascha Wildner 		break;
404*54ba9607SSascha Wildner 	case ROFFT_HEAD:
405*54ba9607SSascha Wildner 		return 0;
406*54ba9607SSascha Wildner 	case ROFFT_BODY:
407*54ba9607SSascha Wildner 		if (n->child != NULL &&
408*54ba9607SSascha Wildner 		    (n->child->flags & NODE_NOFILL) == 0)
409*54ba9607SSascha Wildner 			print_otag(h, TAG_P, "c",
410*54ba9607SSascha Wildner 			    n->tok == MAN_PP ? "Pp" : "Pp HP");
411*54ba9607SSascha Wildner 		break;
412*54ba9607SSascha Wildner 	default:
413*54ba9607SSascha Wildner 		abort();
414*54ba9607SSascha Wildner 	}
415*54ba9607SSascha Wildner 	return 1;
416*54ba9607SSascha Wildner }
41780387638SSascha Wildner 
418*54ba9607SSascha Wildner static char
419*54ba9607SSascha Wildner list_continues(const struct roff_node *n1, const struct roff_node *n2)
420*54ba9607SSascha Wildner {
421*54ba9607SSascha Wildner 	const char *s1, *s2;
422*54ba9607SSascha Wildner 	char c1, c2;
42380387638SSascha Wildner 
424*54ba9607SSascha Wildner 	if (n1 == NULL || n1->type != ROFFT_BLOCK ||
425*54ba9607SSascha Wildner 	    n2 == NULL || n2->type != ROFFT_BLOCK)
426*54ba9607SSascha Wildner 		return '\0';
427*54ba9607SSascha Wildner 	if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
428*54ba9607SSascha Wildner 	    (n2->tok == MAN_TP || n2->tok == MAN_TQ))
429*54ba9607SSascha Wildner 		return ' ';
430*54ba9607SSascha Wildner 	if (n1->tok != MAN_IP || n2->tok != MAN_IP)
431*54ba9607SSascha Wildner 		return '\0';
432*54ba9607SSascha Wildner 	n1 = n1->head->child;
433*54ba9607SSascha Wildner 	n2 = n2->head->child;
434*54ba9607SSascha Wildner 	s1 = n1 == NULL ? "" : n1->string;
435*54ba9607SSascha Wildner 	s2 = n2 == NULL ? "" : n2->string;
436*54ba9607SSascha Wildner 	c1 = strcmp(s1, "*") == 0 ? '*' :
437*54ba9607SSascha Wildner 	     strcmp(s1, "\\-") == 0 ? '-' :
438*54ba9607SSascha Wildner 	     strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
439*54ba9607SSascha Wildner 	c2 = strcmp(s2, "*") == 0 ? '*' :
440*54ba9607SSascha Wildner 	     strcmp(s2, "\\-") == 0 ? '-' :
441*54ba9607SSascha Wildner 	     strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
442*54ba9607SSascha Wildner 	return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
44380387638SSascha Wildner }
44480387638SSascha Wildner 
44580387638SSascha Wildner static int
44680387638SSascha Wildner man_IP_pre(MAN_ARGS)
44780387638SSascha Wildner {
448*54ba9607SSascha Wildner 	const struct roff_node	*nn;
449*54ba9607SSascha Wildner 	const char		*list_class;
450*54ba9607SSascha Wildner 	enum htmltag		 list_elem, body_elem;
451*54ba9607SSascha Wildner 	char			 list_type;
45280387638SSascha Wildner 
453*54ba9607SSascha Wildner 	nn = n->type == ROFFT_BLOCK ? n : n->parent;
454*54ba9607SSascha Wildner 	if ((list_type = list_continues(nn->prev, nn)) == '\0') {
455*54ba9607SSascha Wildner 		/* Start a new list. */
456*54ba9607SSascha Wildner 		if ((list_type = list_continues(nn, nn->next)) == '\0')
457*54ba9607SSascha Wildner 			list_type = ' ';
458*54ba9607SSascha Wildner 		switch (list_type) {
459*54ba9607SSascha Wildner 		case ' ':
460*54ba9607SSascha Wildner 			list_class = "Bl-tag";
461*54ba9607SSascha Wildner 			list_elem = TAG_DL;
462*54ba9607SSascha Wildner 			break;
463*54ba9607SSascha Wildner 		case '*':
464*54ba9607SSascha Wildner 			list_class = "Bl-bullet";
465*54ba9607SSascha Wildner 			list_elem = TAG_UL;
466*54ba9607SSascha Wildner 			break;
467*54ba9607SSascha Wildner 		case '-':
468*54ba9607SSascha Wildner 			list_class = "Bl-dash";
469*54ba9607SSascha Wildner 			list_elem = TAG_UL;
470*54ba9607SSascha Wildner 			break;
471*54ba9607SSascha Wildner 		default:
472*54ba9607SSascha Wildner 			abort();
473*54ba9607SSascha Wildner 		}
474*54ba9607SSascha Wildner 	} else {
475*54ba9607SSascha Wildner 		/* Continue a list that was started earlier. */
476*54ba9607SSascha Wildner 		list_class = NULL;
477*54ba9607SSascha Wildner 		list_elem = TAG_MAX;
478*54ba9607SSascha Wildner 	}
479*54ba9607SSascha Wildner 	body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
480*54ba9607SSascha Wildner 
481*54ba9607SSascha Wildner 	switch (n->type) {
482*54ba9607SSascha Wildner 	case ROFFT_BLOCK:
483*54ba9607SSascha Wildner 		html_close_paragraph(h);
484*54ba9607SSascha Wildner 		if (list_elem != TAG_MAX)
485*54ba9607SSascha Wildner 			print_otag(h, list_elem, "c", list_class);
486*54ba9607SSascha Wildner 		return 1;
487*54ba9607SSascha Wildner 	case ROFFT_HEAD:
488*54ba9607SSascha Wildner 		if (body_elem == TAG_LI)
489*54ba9607SSascha Wildner 			return 0;
490*54ba9607SSascha Wildner 		print_otag(h, TAG_DT, "");
491*54ba9607SSascha Wildner 		break;
492*54ba9607SSascha Wildner 	case ROFFT_BODY:
493*54ba9607SSascha Wildner 		print_otag(h, body_elem, "");
494*54ba9607SSascha Wildner 		return 1;
495*54ba9607SSascha Wildner 	default:
496*54ba9607SSascha Wildner 		abort();
49780387638SSascha Wildner 	}
49880387638SSascha Wildner 
499*54ba9607SSascha Wildner 	switch(n->tok) {
500*54ba9607SSascha Wildner 	case MAN_IP:  /* Only print the first header element. */
501*54ba9607SSascha Wildner 		if (n->child != NULL)
502*54ba9607SSascha Wildner 			print_man_node(man, n->child, h);
503*54ba9607SSascha Wildner 		break;
504*54ba9607SSascha Wildner 	case MAN_TP:  /* Only print next-line header elements. */
505*54ba9607SSascha Wildner 	case MAN_TQ:
506070c62a6SFranco Fichtner 		nn = n->child;
507*54ba9607SSascha Wildner 		while (nn != NULL && (NODE_LINE & nn->flags) == 0)
508070c62a6SFranco Fichtner 			nn = nn->next;
509*54ba9607SSascha Wildner 		while (nn != NULL) {
510*54ba9607SSascha Wildner 			print_man_node(man, nn, h);
511070c62a6SFranco Fichtner 			nn = nn->next;
512070c62a6SFranco Fichtner 		}
513*54ba9607SSascha Wildner 		break;
514*54ba9607SSascha Wildner 	default:
515*54ba9607SSascha Wildner 		abort();
516070c62a6SFranco Fichtner 	}
517*54ba9607SSascha Wildner 	return 0;
51880387638SSascha Wildner }
51980387638SSascha Wildner 
52036342e81SSascha Wildner static int
52136342e81SSascha Wildner man_OP_pre(MAN_ARGS)
52236342e81SSascha Wildner {
52336342e81SSascha Wildner 	struct tag	*tt;
52436342e81SSascha Wildner 
52536342e81SSascha Wildner 	print_text(h, "[");
52636342e81SSascha Wildner 	h->flags |= HTML_NOSPACE;
527*54ba9607SSascha Wildner 	tt = print_otag(h, TAG_SPAN, "c", "Op");
52836342e81SSascha Wildner 
529*54ba9607SSascha Wildner 	if ((n = n->child) != NULL) {
530*54ba9607SSascha Wildner 		print_otag(h, TAG_B, "");
53136342e81SSascha Wildner 		print_text(h, n->string);
53236342e81SSascha Wildner 	}
53336342e81SSascha Wildner 
53436342e81SSascha Wildner 	print_stagq(h, tt);
53536342e81SSascha Wildner 
536*54ba9607SSascha Wildner 	if (n != NULL && n->next != NULL) {
537*54ba9607SSascha Wildner 		print_otag(h, TAG_I, "");
53836342e81SSascha Wildner 		print_text(h, n->next->string);
53936342e81SSascha Wildner 	}
54036342e81SSascha Wildner 
54136342e81SSascha Wildner 	print_stagq(h, tt);
54236342e81SSascha Wildner 	h->flags |= HTML_NOSPACE;
54336342e81SSascha Wildner 	print_text(h, "]");
544*54ba9607SSascha Wildner 	return 0;
54536342e81SSascha Wildner }
54636342e81SSascha Wildner 
54780387638SSascha Wildner static int
54880387638SSascha Wildner man_B_pre(MAN_ARGS)
54980387638SSascha Wildner {
550*54ba9607SSascha Wildner 	print_otag(h, TAG_B, "");
551*54ba9607SSascha Wildner 	return 1;
55280387638SSascha Wildner }
55380387638SSascha Wildner 
55480387638SSascha Wildner static int
55580387638SSascha Wildner man_I_pre(MAN_ARGS)
55680387638SSascha Wildner {
557*54ba9607SSascha Wildner 	print_otag(h, TAG_I, "");
558*54ba9607SSascha Wildner 	return 1;
55980387638SSascha Wildner }
56080387638SSascha Wildner 
56180387638SSascha Wildner static int
56280387638SSascha Wildner man_in_pre(MAN_ARGS)
56380387638SSascha Wildner {
564*54ba9607SSascha Wildner 	print_otag(h, TAG_BR, "");
565*54ba9607SSascha Wildner 	return 0;
56680387638SSascha Wildner }
56780387638SSascha Wildner 
56880387638SSascha Wildner static int
56980387638SSascha Wildner man_ign_pre(MAN_ARGS)
57080387638SSascha Wildner {
571*54ba9607SSascha Wildner 	return 0;
57280387638SSascha Wildner }
57380387638SSascha Wildner 
57480387638SSascha Wildner static int
57580387638SSascha Wildner man_RS_pre(MAN_ARGS)
57680387638SSascha Wildner {
577*54ba9607SSascha Wildner 	switch (n->type) {
578*54ba9607SSascha Wildner 	case ROFFT_BLOCK:
579*54ba9607SSascha Wildner 		html_close_paragraph(h);
580*54ba9607SSascha Wildner 		break;
581*54ba9607SSascha Wildner 	case ROFFT_HEAD:
582*54ba9607SSascha Wildner 		return 0;
583*54ba9607SSascha Wildner 	case ROFFT_BODY:
584*54ba9607SSascha Wildner 		print_otag(h, TAG_DIV, "c", "Bd-indent");
585*54ba9607SSascha Wildner 		break;
586*54ba9607SSascha Wildner 	default:
587*54ba9607SSascha Wildner 		abort();
588*54ba9607SSascha Wildner 	}
589*54ba9607SSascha Wildner 	return 1;
590*54ba9607SSascha Wildner }
59180387638SSascha Wildner 
592*54ba9607SSascha Wildner static int
593*54ba9607SSascha Wildner man_SY_pre(MAN_ARGS)
594*54ba9607SSascha Wildner {
595*54ba9607SSascha Wildner 	switch (n->type) {
596*54ba9607SSascha Wildner 	case ROFFT_BLOCK:
597*54ba9607SSascha Wildner 		html_close_paragraph(h);
598*54ba9607SSascha Wildner 		print_otag(h, TAG_TABLE, "c", "Nm");
599*54ba9607SSascha Wildner 		print_otag(h, TAG_TR, "");
600*54ba9607SSascha Wildner 		break;
601*54ba9607SSascha Wildner 	case ROFFT_HEAD:
602*54ba9607SSascha Wildner 		print_otag(h, TAG_TD, "");
603*54ba9607SSascha Wildner 		print_otag(h, TAG_CODE, "c", "Nm");
604*54ba9607SSascha Wildner 		break;
605*54ba9607SSascha Wildner 	case ROFFT_BODY:
606*54ba9607SSascha Wildner 		print_otag(h, TAG_TD, "");
607*54ba9607SSascha Wildner 		break;
608*54ba9607SSascha Wildner 	default:
609*54ba9607SSascha Wildner 		abort();
610*54ba9607SSascha Wildner 	}
611*54ba9607SSascha Wildner 	return 1;
61280387638SSascha Wildner }
6137888c61dSFranco Fichtner 
6147888c61dSFranco Fichtner static int
6157888c61dSFranco Fichtner man_UR_pre(MAN_ARGS)
6167888c61dSFranco Fichtner {
617*54ba9607SSascha Wildner 	char *cp;
6187888c61dSFranco Fichtner 
6197888c61dSFranco Fichtner 	n = n->child;
620*54ba9607SSascha Wildner 	assert(n->type == ROFFT_HEAD);
621*54ba9607SSascha Wildner 	if (n->child != NULL) {
622*54ba9607SSascha Wildner 		assert(n->child->type == ROFFT_TEXT);
623*54ba9607SSascha Wildner 		if (n->tok == MAN_MT) {
624*54ba9607SSascha Wildner 			mandoc_asprintf(&cp, "mailto:%s", n->child->string);
625*54ba9607SSascha Wildner 			print_otag(h, TAG_A, "ch", "Mt", cp);
626*54ba9607SSascha Wildner 			free(cp);
627*54ba9607SSascha Wildner 		} else
628*54ba9607SSascha Wildner 			print_otag(h, TAG_A, "ch", "Lk", n->child->string);
6297888c61dSFranco Fichtner 	}
6307888c61dSFranco Fichtner 
631*54ba9607SSascha Wildner 	assert(n->next->type == ROFFT_BODY);
632*54ba9607SSascha Wildner 	if (n->next->child != NULL)
6337888c61dSFranco Fichtner 		n = n->next;
6347888c61dSFranco Fichtner 
635*54ba9607SSascha Wildner 	print_man_nodelist(man, n->child, h);
636*54ba9607SSascha Wildner 	return 0;
637*54ba9607SSascha Wildner }
6387888c61dSFranco Fichtner 
639*54ba9607SSascha Wildner static int
640*54ba9607SSascha Wildner man_abort_pre(MAN_ARGS)
641*54ba9607SSascha Wildner {
642*54ba9607SSascha Wildner 	abort();
6437888c61dSFranco Fichtner }
644