xref: /illumos-gate/usr/src/cmd/mandoc/man_html.c (revision 4d131170)
1*4d131170SRobert Mustacchi /* $Id: man_html.c,v 1.179 2020/10/16 17:22:43 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*4d131170SRobert Mustacchi  * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
4260e9a87SYuri Pankov  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
10371584c2SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12371584c2SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*4d131170SRobert Mustacchi  *
18*4d131170SRobert Mustacchi  * HTML formatter for man(7) used by mandoc(1).
1995c635efSGarrett D'Amore  */
2095c635efSGarrett D'Amore #include "config.h"
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <sys/types.h>
2395c635efSGarrett D'Amore 
2495c635efSGarrett D'Amore #include <assert.h>
2595c635efSGarrett D'Amore #include <ctype.h>
2695c635efSGarrett D'Amore #include <stdio.h>
2795c635efSGarrett D'Amore #include <stdlib.h>
2895c635efSGarrett D'Amore #include <string.h>
2995c635efSGarrett D'Amore 
30260e9a87SYuri Pankov #include "mandoc_aux.h"
31c66b8046SYuri Pankov #include "mandoc.h"
32371584c2SYuri Pankov #include "roff.h"
33260e9a87SYuri Pankov #include "man.h"
3495c635efSGarrett D'Amore #include "out.h"
3595c635efSGarrett D'Amore #include "html.h"
3695c635efSGarrett D'Amore #include "main.h"
3795c635efSGarrett D'Amore 
38371584c2SYuri Pankov #define	MAN_ARGS	  const struct roff_meta *man, \
39*4d131170SRobert Mustacchi 			  struct roff_node *n, \
4095c635efSGarrett D'Amore 			  struct html *h
4195c635efSGarrett D'Amore 
42cec8643bSMichal Nowak struct	man_html_act {
4395c635efSGarrett D'Amore 	int		(*pre)(MAN_ARGS);
4495c635efSGarrett D'Amore 	int		(*post)(MAN_ARGS);
4595c635efSGarrett D'Amore };
4695c635efSGarrett D'Amore 
476640c13bSYuri Pankov static	void		  print_man_head(const struct roff_meta *,
486640c13bSYuri Pankov 				struct html *);
4995c635efSGarrett D'Amore static	void		  print_man_nodelist(MAN_ARGS);
5095c635efSGarrett D'Amore static	void		  print_man_node(MAN_ARGS);
51cec8643bSMichal Nowak static	char		  list_continues(const struct roff_node *,
52cec8643bSMichal Nowak 				const struct roff_node *);
5395c635efSGarrett D'Amore static	int		  man_B_pre(MAN_ARGS);
5495c635efSGarrett D'Amore static	int		  man_IP_pre(MAN_ARGS);
5595c635efSGarrett D'Amore static	int		  man_I_pre(MAN_ARGS);
5695c635efSGarrett D'Amore static	int		  man_OP_pre(MAN_ARGS);
5795c635efSGarrett D'Amore static	int		  man_PP_pre(MAN_ARGS);
5895c635efSGarrett D'Amore static	int		  man_RS_pre(MAN_ARGS);
5995c635efSGarrett D'Amore static	int		  man_SH_pre(MAN_ARGS);
6095c635efSGarrett D'Amore static	int		  man_SM_pre(MAN_ARGS);
61cec8643bSMichal Nowak static	int		  man_SY_pre(MAN_ARGS);
62698f87a4SGarrett D'Amore static	int		  man_UR_pre(MAN_ARGS);
63cec8643bSMichal Nowak static	int		  man_abort_pre(MAN_ARGS);
6495c635efSGarrett D'Amore static	int		  man_alt_pre(MAN_ARGS);
6595c635efSGarrett D'Amore static	int		  man_ign_pre(MAN_ARGS);
6695c635efSGarrett D'Amore static	int		  man_in_pre(MAN_ARGS);
676640c13bSYuri Pankov static	void		  man_root_post(const struct roff_meta *,
686640c13bSYuri Pankov 				struct html *);
696640c13bSYuri Pankov static	void		  man_root_pre(const struct roff_meta *,
706640c13bSYuri Pankov 				struct html *);
7195c635efSGarrett D'Amore 
72cec8643bSMichal Nowak static	const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
7395c635efSGarrett D'Amore 	{ NULL, NULL }, /* TH */
7495c635efSGarrett D'Amore 	{ man_SH_pre, NULL }, /* SH */
75cec8643bSMichal Nowak 	{ man_SH_pre, NULL }, /* SS */
7695c635efSGarrett D'Amore 	{ man_IP_pre, NULL }, /* TP */
77cec8643bSMichal Nowak 	{ man_IP_pre, NULL }, /* TQ */
78cec8643bSMichal Nowak 	{ man_abort_pre, NULL }, /* LP */
7995c635efSGarrett D'Amore 	{ man_PP_pre, NULL }, /* PP */
80cec8643bSMichal Nowak 	{ man_abort_pre, NULL }, /* P */
8195c635efSGarrett D'Amore 	{ man_IP_pre, NULL }, /* IP */
82cec8643bSMichal Nowak 	{ man_PP_pre, NULL }, /* HP */
8395c635efSGarrett D'Amore 	{ man_SM_pre, NULL }, /* SM */
8495c635efSGarrett D'Amore 	{ man_SM_pre, NULL }, /* SB */
8595c635efSGarrett D'Amore 	{ man_alt_pre, NULL }, /* BI */
8695c635efSGarrett D'Amore 	{ man_alt_pre, NULL }, /* IB */
8795c635efSGarrett D'Amore 	{ man_alt_pre, NULL }, /* BR */
8895c635efSGarrett D'Amore 	{ man_alt_pre, NULL }, /* RB */
8995c635efSGarrett D'Amore 	{ NULL, NULL }, /* R */
9095c635efSGarrett D'Amore 	{ man_B_pre, NULL }, /* B */
9195c635efSGarrett D'Amore 	{ man_I_pre, NULL }, /* I */
9295c635efSGarrett D'Amore 	{ man_alt_pre, NULL }, /* IR */
9395c635efSGarrett D'Amore 	{ man_alt_pre, NULL }, /* RI */
9495c635efSGarrett D'Amore 	{ NULL, NULL }, /* RE */
9595c635efSGarrett D'Amore 	{ man_RS_pre, NULL }, /* RS */
9695c635efSGarrett D'Amore 	{ man_ign_pre, NULL }, /* DT */
9795c635efSGarrett D'Amore 	{ man_ign_pre, NULL }, /* UC */
9895c635efSGarrett D'Amore 	{ man_ign_pre, NULL }, /* PD */
9995c635efSGarrett D'Amore 	{ man_ign_pre, NULL }, /* AT */
10095c635efSGarrett D'Amore 	{ man_in_pre, NULL }, /* in */
101cec8643bSMichal Nowak 	{ man_SY_pre, NULL }, /* SY */
102cec8643bSMichal Nowak 	{ NULL, NULL }, /* YS */
10395c635efSGarrett D'Amore 	{ man_OP_pre, NULL }, /* OP */
104a40ea1a7SYuri Pankov 	{ NULL, NULL }, /* EX */
105a40ea1a7SYuri Pankov 	{ NULL, NULL }, /* EE */
106698f87a4SGarrett D'Amore 	{ man_UR_pre, NULL }, /* UR */
107698f87a4SGarrett D'Amore 	{ NULL, NULL }, /* UE */
108c66b8046SYuri Pankov 	{ man_UR_pre, NULL }, /* MT */
109c66b8046SYuri Pankov 	{ NULL, NULL }, /* ME */
11095c635efSGarrett D'Amore };
11195c635efSGarrett D'Amore 
11295c635efSGarrett D'Amore 
11395c635efSGarrett D'Amore void
html_man(void * arg,const struct roff_meta * man)114cec8643bSMichal Nowak html_man(void *arg, const struct roff_meta *man)
11595c635efSGarrett D'Amore {
1166640c13bSYuri Pankov 	struct html		*h;
1176640c13bSYuri Pankov 	struct roff_node	*n;
1186640c13bSYuri Pankov 	struct tag		*t;
11995c635efSGarrett D'Amore 
120371584c2SYuri Pankov 	h = (struct html *)arg;
1216640c13bSYuri Pankov 	n = man->first->child;
12295c635efSGarrett D'Amore 
123a40ea1a7SYuri Pankov 	if ((h->oflags & HTML_FRAGMENT) == 0) {
12495c635efSGarrett D'Amore 		print_gen_decls(h);
125a40ea1a7SYuri Pankov 		print_otag(h, TAG_HTML, "");
126cec8643bSMichal Nowak 		if (n != NULL && n->type == ROFFT_COMMENT)
1276640c13bSYuri Pankov 			print_gen_comment(h, n);
128a40ea1a7SYuri Pankov 		t = print_otag(h, TAG_HEAD, "");
129cec8643bSMichal Nowak 		print_man_head(man, h);
130a40ea1a7SYuri Pankov 		print_tagq(h, t);
131a40ea1a7SYuri Pankov 		print_otag(h, TAG_BODY, "");
132a40ea1a7SYuri Pankov 	}
13395c635efSGarrett D'Amore 
134cec8643bSMichal Nowak 	man_root_pre(man, h);
135a40ea1a7SYuri Pankov 	t = print_otag(h, TAG_DIV, "c", "manual-text");
136cec8643bSMichal Nowak 	print_man_nodelist(man, n, h);
13795c635efSGarrett D'Amore 	print_tagq(h, t);
138cec8643bSMichal Nowak 	man_root_post(man, h);
139a40ea1a7SYuri Pankov 	print_tagq(h, NULL);
14095c635efSGarrett D'Amore }
14195c635efSGarrett D'Amore 
14295c635efSGarrett D'Amore static void
print_man_head(const struct roff_meta * man,struct html * h)1436640c13bSYuri Pankov print_man_head(const struct roff_meta *man, struct html *h)
14495c635efSGarrett D'Amore {
145a40ea1a7SYuri Pankov 	char	*cp;
14695c635efSGarrett D'Amore 
14795c635efSGarrett D'Amore 	print_gen_head(h);
148a40ea1a7SYuri Pankov 	mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
149a40ea1a7SYuri Pankov 	print_otag(h, TAG_TITLE, "");
150a40ea1a7SYuri Pankov 	print_text(h, cp);
151a40ea1a7SYuri Pankov 	free(cp);
15295c635efSGarrett D'Amore }
15395c635efSGarrett D'Amore 
15495c635efSGarrett D'Amore static void
print_man_nodelist(MAN_ARGS)15595c635efSGarrett D'Amore print_man_nodelist(MAN_ARGS)
15695c635efSGarrett D'Amore {
157260e9a87SYuri Pankov 	while (n != NULL) {
158a40ea1a7SYuri Pankov 		print_man_node(man, n, h);
159260e9a87SYuri Pankov 		n = n->next;
160260e9a87SYuri Pankov 	}
16195c635efSGarrett D'Amore }
16295c635efSGarrett D'Amore 
16395c635efSGarrett D'Amore static void
print_man_node(MAN_ARGS)16495c635efSGarrett D'Amore print_man_node(MAN_ARGS)
16595c635efSGarrett D'Amore {
16695c635efSGarrett D'Amore 	struct tag	*t;
167a40ea1a7SYuri Pankov 	int		 child;
16895c635efSGarrett D'Amore 
169cec8643bSMichal Nowak 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
170a40ea1a7SYuri Pankov 		return;
171a40ea1a7SYuri Pankov 
172*4d131170SRobert Mustacchi 	if ((n->flags & NODE_NOFILL) == 0)
173*4d131170SRobert Mustacchi 		html_fillmode(h, ROFF_fi);
174*4d131170SRobert Mustacchi 	else if (html_fillmode(h, ROFF_nf) == ROFF_nf &&
175*4d131170SRobert Mustacchi 	    n->tok != ROFF_fi && n->flags & NODE_LINE &&
176*4d131170SRobert Mustacchi 	    (n->prev == NULL || n->prev->tok != MAN_YS))
177*4d131170SRobert Mustacchi 		print_endline(h);
178a40ea1a7SYuri Pankov 
179a40ea1a7SYuri Pankov 	child = 1;
180a40ea1a7SYuri Pankov 	switch (n->type) {
181a40ea1a7SYuri Pankov 	case ROFFT_TEXT:
182cec8643bSMichal Nowak 		if (*n->string == '\0') {
183cec8643bSMichal Nowak 			print_endline(h);
184cec8643bSMichal Nowak 			return;
185cec8643bSMichal Nowak 		}
186cec8643bSMichal Nowak 		if (*n->string == ' ' && n->flags & NODE_LINE &&
187cec8643bSMichal Nowak 		    (h->flags & HTML_NONEWLINE) == 0)
188*4d131170SRobert Mustacchi 			print_otag(h, TAG_BR, "");
189cec8643bSMichal Nowak 		else if (n->flags & NODE_DELIMC)
190cec8643bSMichal Nowak 			h->flags |= HTML_NOSPACE;
191a40ea1a7SYuri Pankov 		t = h->tag;
192cec8643bSMichal Nowak 		t->refcnt++;
193a40ea1a7SYuri Pankov 		print_text(h, n->string);
194a40ea1a7SYuri Pankov 		break;
195371584c2SYuri Pankov 	case ROFFT_EQN:
196a40ea1a7SYuri Pankov 		t = h->tag;
197cec8643bSMichal Nowak 		t->refcnt++;
19895c635efSGarrett D'Amore 		print_eqn(h, n->eqn);
19995c635efSGarrett D'Amore 		break;
200371584c2SYuri Pankov 	case ROFFT_TBL:
20195c635efSGarrett D'Amore 		/*
20295c635efSGarrett D'Amore 		 * This will take care of initialising all of the table
20395c635efSGarrett D'Amore 		 * state data for the first table, then tearing it down
20495c635efSGarrett D'Amore 		 * for the last one.
20595c635efSGarrett D'Amore 		 */
20695c635efSGarrett D'Amore 		print_tbl(h, n->span);
20795c635efSGarrett D'Amore 		return;
20895c635efSGarrett D'Amore 	default:
209260e9a87SYuri Pankov 		/*
21095c635efSGarrett D'Amore 		 * Close out scope of font prior to opening a macro
21195c635efSGarrett D'Amore 		 * scope.
21295c635efSGarrett D'Amore 		 */
213*4d131170SRobert Mustacchi 		if (h->metac != ESCAPE_FONTROMAN) {
21495c635efSGarrett D'Amore 			h->metal = h->metac;
215*4d131170SRobert Mustacchi 			h->metac = ESCAPE_FONTROMAN;
21695c635efSGarrett D'Amore 		}
21795c635efSGarrett D'Amore 
21895c635efSGarrett D'Amore 		/*
21995c635efSGarrett D'Amore 		 * Close out the current table, if it's open, and unset
22095c635efSGarrett D'Amore 		 * the "meta" table state.  This will be reopened on the
22195c635efSGarrett D'Amore 		 * next table element.
22295c635efSGarrett D'Amore 		 */
223cec8643bSMichal Nowak 		if (h->tblt != NULL)
22495c635efSGarrett D'Amore 			print_tblclose(h);
225a40ea1a7SYuri Pankov 		t = h->tag;
226cec8643bSMichal Nowak 		t->refcnt++;
227c66b8046SYuri Pankov 		if (n->tok < ROFF_MAX) {
228c66b8046SYuri Pankov 			roff_html_pre(h, n);
229cec8643bSMichal Nowak 			t->refcnt--;
230cec8643bSMichal Nowak 			print_stagq(h, t);
231cec8643bSMichal Nowak 			return;
232c66b8046SYuri Pankov 		}
233c66b8046SYuri Pankov 		assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
234cec8643bSMichal Nowak 		if (man_html_acts[n->tok - MAN_TH].pre != NULL)
235cec8643bSMichal Nowak 			child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
236cec8643bSMichal Nowak 			    n, h);
23795c635efSGarrett D'Amore 		break;
23895c635efSGarrett D'Amore 	}
23995c635efSGarrett D'Amore 
240cec8643bSMichal Nowak 	if (child && n->child != NULL)
241a40ea1a7SYuri Pankov 		print_man_nodelist(man, n->child, h);
24295c635efSGarrett D'Amore 
24395c635efSGarrett D'Amore 	/* This will automatically close out any font scope. */
244cec8643bSMichal Nowak 	t->refcnt--;
245cec8643bSMichal Nowak 	if (n->type == ROFFT_BLOCK &&
246cec8643bSMichal Nowak 	    (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
247cec8643bSMichal Nowak 		t = h->tag;
248cec8643bSMichal Nowak 		while (t->tag != TAG_DL && t->tag != TAG_UL)
249cec8643bSMichal Nowak 			t = t->next;
250cec8643bSMichal Nowak 		/*
251cec8643bSMichal Nowak 		 * Close the list if no further item of the same type
252cec8643bSMichal Nowak 		 * follows; otherwise, close the item only.
253cec8643bSMichal Nowak 		 */
254*4d131170SRobert Mustacchi 		if (list_continues(n, roff_node_next(n)) == '\0') {
255cec8643bSMichal Nowak 			print_tagq(h, t);
256cec8643bSMichal Nowak 			t = NULL;
257cec8643bSMichal Nowak 		}
258cec8643bSMichal Nowak 	}
259cec8643bSMichal Nowak 	if (t != NULL)
260cec8643bSMichal Nowak 		print_stagq(h, t);
26195c635efSGarrett D'Amore }
26295c635efSGarrett D'Amore 
26395c635efSGarrett D'Amore static void
man_root_pre(const struct roff_meta * man,struct html * h)2646640c13bSYuri Pankov man_root_pre(const struct roff_meta *man, struct html *h)
26595c635efSGarrett D'Amore {
26695c635efSGarrett D'Amore 	struct tag	*t, *tt;
267260e9a87SYuri Pankov 	char		*title;
26895c635efSGarrett D'Amore 
269698f87a4SGarrett D'Amore 	assert(man->title);
270698f87a4SGarrett D'Amore 	assert(man->msec);
271260e9a87SYuri Pankov 	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
27295c635efSGarrett D'Amore 
273a40ea1a7SYuri Pankov 	t = print_otag(h, TAG_TABLE, "c", "head");
274a40ea1a7SYuri Pankov 	tt = print_otag(h, TAG_TR, "");
27595c635efSGarrett D'Amore 
276a40ea1a7SYuri Pankov 	print_otag(h, TAG_TD, "c", "head-ltitle");
27795c635efSGarrett D'Amore 	print_text(h, title);
27895c635efSGarrett D'Amore 	print_stagq(h, tt);
27995c635efSGarrett D'Amore 
280a40ea1a7SYuri Pankov 	print_otag(h, TAG_TD, "c", "head-vol");
281cec8643bSMichal Nowak 	if (man->vol != NULL)
282260e9a87SYuri Pankov 		print_text(h, man->vol);
28395c635efSGarrett D'Amore 	print_stagq(h, tt);
28495c635efSGarrett D'Amore 
285a40ea1a7SYuri Pankov 	print_otag(h, TAG_TD, "c", "head-rtitle");
28695c635efSGarrett D'Amore 	print_text(h, title);
28795c635efSGarrett D'Amore 	print_tagq(h, t);
288260e9a87SYuri Pankov 	free(title);
28995c635efSGarrett D'Amore }
29095c635efSGarrett D'Amore 
29195c635efSGarrett D'Amore static void
man_root_post(const struct roff_meta * man,struct html * h)2926640c13bSYuri Pankov man_root_post(const struct roff_meta *man, struct html *h)
29395c635efSGarrett D'Amore {
29495c635efSGarrett D'Amore 	struct tag	*t, *tt;
29595c635efSGarrett D'Amore 
296a40ea1a7SYuri Pankov 	t = print_otag(h, TAG_TABLE, "c", "foot");
297a40ea1a7SYuri Pankov 	tt = print_otag(h, TAG_TR, "");
29895c635efSGarrett D'Amore 
299a40ea1a7SYuri Pankov 	print_otag(h, TAG_TD, "c", "foot-date");
300698f87a4SGarrett D'Amore 	print_text(h, man->date);
30195c635efSGarrett D'Amore 	print_stagq(h, tt);
30295c635efSGarrett D'Amore 
303a40ea1a7SYuri Pankov 	print_otag(h, TAG_TD, "c", "foot-os");
304cec8643bSMichal Nowak 	if (man->os != NULL)
305371584c2SYuri Pankov 		print_text(h, man->os);
30695c635efSGarrett D'Amore 	print_tagq(h, t);
30795c635efSGarrett D'Amore }
30895c635efSGarrett D'Amore 
30995c635efSGarrett D'Amore static int
man_SH_pre(MAN_ARGS)31095c635efSGarrett D'Amore man_SH_pre(MAN_ARGS)
31195c635efSGarrett D'Amore {
312cec8643bSMichal Nowak 	const char	*class;
313cec8643bSMichal Nowak 	enum htmltag	 tag;
314cec8643bSMichal Nowak 
315cec8643bSMichal Nowak 	if (n->tok == MAN_SH) {
316cec8643bSMichal Nowak 		tag = TAG_H1;
317cec8643bSMichal Nowak 		class = "Sh";
318cec8643bSMichal Nowak 	} else {
319cec8643bSMichal Nowak 		tag = TAG_H2;
320cec8643bSMichal Nowak 		class = "Ss";
321cec8643bSMichal Nowak 	}
322cec8643bSMichal Nowak 	switch (n->type) {
323cec8643bSMichal Nowak 	case ROFFT_BLOCK:
324cec8643bSMichal Nowak 		html_close_paragraph(h);
325cec8643bSMichal Nowak 		print_otag(h, TAG_SECTION, "c", class);
326cec8643bSMichal Nowak 		break;
327cec8643bSMichal Nowak 	case ROFFT_HEAD:
328*4d131170SRobert Mustacchi 		print_otag_id(h, tag, class, n);
329cec8643bSMichal Nowak 		break;
330cec8643bSMichal Nowak 	case ROFFT_BODY:
331cec8643bSMichal Nowak 		break;
332cec8643bSMichal Nowak 	default:
333cec8643bSMichal Nowak 		abort();
334c66b8046SYuri Pankov 	}
335371584c2SYuri Pankov 	return 1;
33695c635efSGarrett D'Amore }
33795c635efSGarrett D'Amore 
33895c635efSGarrett D'Amore static int
man_alt_pre(MAN_ARGS)33995c635efSGarrett D'Amore man_alt_pre(MAN_ARGS)
34095c635efSGarrett D'Amore {
341371584c2SYuri Pankov 	const struct roff_node	*nn;
342cec8643bSMichal Nowak 	struct tag	*t;
343a40ea1a7SYuri Pankov 	int		 i;
34495c635efSGarrett D'Amore 	enum htmltag	 fp;
34595c635efSGarrett D'Amore 
346cec8643bSMichal Nowak 	for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
34795c635efSGarrett D'Amore 		switch (n->tok) {
348260e9a87SYuri Pankov 		case MAN_BI:
34995c635efSGarrett D'Amore 			fp = i % 2 ? TAG_I : TAG_B;
35095c635efSGarrett D'Amore 			break;
351260e9a87SYuri Pankov 		case MAN_IB:
35295c635efSGarrett D'Amore 			fp = i % 2 ? TAG_B : TAG_I;
35395c635efSGarrett D'Amore 			break;
354260e9a87SYuri Pankov 		case MAN_RI:
35595c635efSGarrett D'Amore 			fp = i % 2 ? TAG_I : TAG_MAX;
35695c635efSGarrett D'Amore 			break;
357260e9a87SYuri Pankov 		case MAN_IR:
35895c635efSGarrett D'Amore 			fp = i % 2 ? TAG_MAX : TAG_I;
35995c635efSGarrett D'Amore 			break;
360260e9a87SYuri Pankov 		case MAN_BR:
36195c635efSGarrett D'Amore 			fp = i % 2 ? TAG_MAX : TAG_B;
36295c635efSGarrett D'Amore 			break;
363260e9a87SYuri Pankov 		case MAN_RB:
36495c635efSGarrett D'Amore 			fp = i % 2 ? TAG_B : TAG_MAX;
36595c635efSGarrett D'Amore 			break;
36695c635efSGarrett D'Amore 		default:
36795c635efSGarrett D'Amore 			abort();
36895c635efSGarrett D'Amore 		}
36995c635efSGarrett D'Amore 
37095c635efSGarrett D'Amore 		if (i)
37195c635efSGarrett D'Amore 			h->flags |= HTML_NOSPACE;
37295c635efSGarrett D'Amore 
373a40ea1a7SYuri Pankov 		if (fp != TAG_MAX)
374a40ea1a7SYuri Pankov 			t = print_otag(h, fp, "");
37595c635efSGarrett D'Amore 
376a40ea1a7SYuri Pankov 		print_text(h, nn->string);
37795c635efSGarrett D'Amore 
378a40ea1a7SYuri Pankov 		if (fp != TAG_MAX)
37995c635efSGarrett D'Amore 			print_tagq(h, t);
38095c635efSGarrett D'Amore 	}
381371584c2SYuri Pankov 	return 0;
38295c635efSGarrett D'Amore }
38395c635efSGarrett D'Amore 
38495c635efSGarrett D'Amore static int
man_SM_pre(MAN_ARGS)38595c635efSGarrett D'Amore man_SM_pre(MAN_ARGS)
38695c635efSGarrett D'Amore {
387a40ea1a7SYuri Pankov 	print_otag(h, TAG_SMALL, "");
388cec8643bSMichal Nowak 	if (n->tok == MAN_SB)
389a40ea1a7SYuri Pankov 		print_otag(h, TAG_B, "");
390371584c2SYuri Pankov 	return 1;
39195c635efSGarrett D'Amore }
39295c635efSGarrett D'Amore 
39395c635efSGarrett D'Amore static int
man_PP_pre(MAN_ARGS)394cec8643bSMichal Nowak man_PP_pre(MAN_ARGS)
39595c635efSGarrett D'Amore {
396cec8643bSMichal Nowak 	switch (n->type) {
397cec8643bSMichal Nowak 	case ROFFT_BLOCK:
398cec8643bSMichal Nowak 		html_close_paragraph(h);
399cec8643bSMichal Nowak 		break;
400cec8643bSMichal Nowak 	case ROFFT_HEAD:
401cec8643bSMichal Nowak 		return 0;
402cec8643bSMichal Nowak 	case ROFFT_BODY:
403cec8643bSMichal Nowak 		if (n->child != NULL &&
404cec8643bSMichal Nowak 		    (n->child->flags & NODE_NOFILL) == 0)
405cec8643bSMichal Nowak 			print_otag(h, TAG_P, "c",
406cec8643bSMichal Nowak 			    n->tok == MAN_PP ? "Pp" : "Pp HP");
407cec8643bSMichal Nowak 		break;
408cec8643bSMichal Nowak 	default:
409cec8643bSMichal Nowak 		abort();
410c66b8046SYuri Pankov 	}
411371584c2SYuri Pankov 	return 1;
41295c635efSGarrett D'Amore }
41395c635efSGarrett D'Amore 
414cec8643bSMichal Nowak static char
list_continues(const struct roff_node * n1,const struct roff_node * n2)415cec8643bSMichal Nowak list_continues(const struct roff_node *n1, const struct roff_node *n2)
41695c635efSGarrett D'Amore {
417cec8643bSMichal Nowak 	const char *s1, *s2;
418cec8643bSMichal Nowak 	char c1, c2;
419cec8643bSMichal Nowak 
420cec8643bSMichal Nowak 	if (n1 == NULL || n1->type != ROFFT_BLOCK ||
421cec8643bSMichal Nowak 	    n2 == NULL || n2->type != ROFFT_BLOCK)
422cec8643bSMichal Nowak 		return '\0';
423cec8643bSMichal Nowak 	if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
424cec8643bSMichal Nowak 	    (n2->tok == MAN_TP || n2->tok == MAN_TQ))
425cec8643bSMichal Nowak 		return ' ';
426cec8643bSMichal Nowak 	if (n1->tok != MAN_IP || n2->tok != MAN_IP)
427cec8643bSMichal Nowak 		return '\0';
428cec8643bSMichal Nowak 	n1 = n1->head->child;
429cec8643bSMichal Nowak 	n2 = n2->head->child;
430cec8643bSMichal Nowak 	s1 = n1 == NULL ? "" : n1->string;
431cec8643bSMichal Nowak 	s2 = n2 == NULL ? "" : n2->string;
432cec8643bSMichal Nowak 	c1 = strcmp(s1, "*") == 0 ? '*' :
433cec8643bSMichal Nowak 	     strcmp(s1, "\\-") == 0 ? '-' :
434cec8643bSMichal Nowak 	     strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
435cec8643bSMichal Nowak 	c2 = strcmp(s2, "*") == 0 ? '*' :
436cec8643bSMichal Nowak 	     strcmp(s2, "\\-") == 0 ? '-' :
437cec8643bSMichal Nowak 	     strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
438cec8643bSMichal Nowak 	return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
43995c635efSGarrett D'Amore }
44095c635efSGarrett D'Amore 
44195c635efSGarrett D'Amore static int
man_IP_pre(MAN_ARGS)44295c635efSGarrett D'Amore man_IP_pre(MAN_ARGS)
44395c635efSGarrett D'Amore {
444*4d131170SRobert Mustacchi 	struct roff_node	*nn;
445cec8643bSMichal Nowak 	const char		*list_class;
446cec8643bSMichal Nowak 	enum htmltag		 list_elem, body_elem;
447cec8643bSMichal Nowak 	char			 list_type;
448cec8643bSMichal Nowak 
449cec8643bSMichal Nowak 	nn = n->type == ROFFT_BLOCK ? n : n->parent;
450*4d131170SRobert Mustacchi 	list_type = list_continues(roff_node_prev(nn), nn);
451*4d131170SRobert Mustacchi 	if (list_type == '\0') {
452cec8643bSMichal Nowak 		/* Start a new list. */
453*4d131170SRobert Mustacchi 		list_type = list_continues(nn, roff_node_next(nn));
454*4d131170SRobert Mustacchi 		if (list_type == '\0')
455cec8643bSMichal Nowak 			list_type = ' ';
456cec8643bSMichal Nowak 		switch (list_type) {
457cec8643bSMichal Nowak 		case ' ':
458cec8643bSMichal Nowak 			list_class = "Bl-tag";
459cec8643bSMichal Nowak 			list_elem = TAG_DL;
460cec8643bSMichal Nowak 			break;
461cec8643bSMichal Nowak 		case '*':
462cec8643bSMichal Nowak 			list_class = "Bl-bullet";
463cec8643bSMichal Nowak 			list_elem = TAG_UL;
464cec8643bSMichal Nowak 			break;
465cec8643bSMichal Nowak 		case '-':
466cec8643bSMichal Nowak 			list_class = "Bl-dash";
467cec8643bSMichal Nowak 			list_elem = TAG_UL;
468cec8643bSMichal Nowak 			break;
469cec8643bSMichal Nowak 		default:
470cec8643bSMichal Nowak 			abort();
471cec8643bSMichal Nowak 		}
472cec8643bSMichal Nowak 	} else {
473cec8643bSMichal Nowak 		/* Continue a list that was started earlier. */
474cec8643bSMichal Nowak 		list_class = NULL;
475cec8643bSMichal Nowak 		list_elem = TAG_MAX;
476cec8643bSMichal Nowak 	}
477cec8643bSMichal Nowak 	body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
47895c635efSGarrett D'Amore 
479cec8643bSMichal Nowak 	switch (n->type) {
480cec8643bSMichal Nowak 	case ROFFT_BLOCK:
481cec8643bSMichal Nowak 		html_close_paragraph(h);
482cec8643bSMichal Nowak 		if (list_elem != TAG_MAX)
483cec8643bSMichal Nowak 			print_otag(h, list_elem, "c", list_class);
484371584c2SYuri Pankov 		return 1;
485cec8643bSMichal Nowak 	case ROFFT_HEAD:
486cec8643bSMichal Nowak 		if (body_elem == TAG_LI)
487cec8643bSMichal Nowak 			return 0;
488*4d131170SRobert Mustacchi 		print_otag_id(h, TAG_DT, NULL, n);
489cec8643bSMichal Nowak 		break;
490cec8643bSMichal Nowak 	case ROFFT_BODY:
491cec8643bSMichal Nowak 		print_otag(h, body_elem, "");
492371584c2SYuri Pankov 		return 1;
493cec8643bSMichal Nowak 	default:
494cec8643bSMichal Nowak 		abort();
49595c635efSGarrett D'Amore 	}
496cec8643bSMichal Nowak 	switch(n->tok) {
497cec8643bSMichal Nowak 	case MAN_IP:  /* Only print the first header element. */
498cec8643bSMichal Nowak 		if (n->child != NULL)
499cec8643bSMichal Nowak 			print_man_node(man, n->child, h);
500cec8643bSMichal Nowak 		break;
501cec8643bSMichal Nowak 	case MAN_TP:  /* Only print next-line header elements. */
502cec8643bSMichal Nowak 	case MAN_TQ:
503260e9a87SYuri Pankov 		nn = n->child;
504cec8643bSMichal Nowak 		while (nn != NULL && (NODE_LINE & nn->flags) == 0)
505260e9a87SYuri Pankov 			nn = nn->next;
506cec8643bSMichal Nowak 		while (nn != NULL) {
507a40ea1a7SYuri Pankov 			print_man_node(man, nn, h);
508260e9a87SYuri Pankov 			nn = nn->next;
509260e9a87SYuri Pankov 		}
510cec8643bSMichal Nowak 		break;
511cec8643bSMichal Nowak 	default:
512cec8643bSMichal Nowak 		abort();
513260e9a87SYuri Pankov 	}
514371584c2SYuri Pankov 	return 0;
51595c635efSGarrett D'Amore }
51695c635efSGarrett D'Amore 
51795c635efSGarrett D'Amore static int
man_OP_pre(MAN_ARGS)51895c635efSGarrett D'Amore man_OP_pre(MAN_ARGS)
51995c635efSGarrett D'Amore {
52095c635efSGarrett D'Amore 	struct tag	*tt;
52195c635efSGarrett D'Amore 
52295c635efSGarrett D'Amore 	print_text(h, "[");
52395c635efSGarrett D'Amore 	h->flags |= HTML_NOSPACE;
524a40ea1a7SYuri Pankov 	tt = print_otag(h, TAG_SPAN, "c", "Op");
52595c635efSGarrett D'Amore 
526cec8643bSMichal Nowak 	if ((n = n->child) != NULL) {
527a40ea1a7SYuri Pankov 		print_otag(h, TAG_B, "");
52895c635efSGarrett D'Amore 		print_text(h, n->string);
52995c635efSGarrett D'Amore 	}
53095c635efSGarrett D'Amore 
53195c635efSGarrett D'Amore 	print_stagq(h, tt);
53295c635efSGarrett D'Amore 
533cec8643bSMichal Nowak 	if (n != NULL && n->next != NULL) {
534a40ea1a7SYuri Pankov 		print_otag(h, TAG_I, "");
53595c635efSGarrett D'Amore 		print_text(h, n->next->string);
53695c635efSGarrett D'Amore 	}
53795c635efSGarrett D'Amore 
53895c635efSGarrett D'Amore 	print_stagq(h, tt);
53995c635efSGarrett D'Amore 	h->flags |= HTML_NOSPACE;
54095c635efSGarrett D'Amore 	print_text(h, "]");
541371584c2SYuri Pankov 	return 0;
54295c635efSGarrett D'Amore }
54395c635efSGarrett D'Amore 
54495c635efSGarrett D'Amore static int
man_B_pre(MAN_ARGS)54595c635efSGarrett D'Amore man_B_pre(MAN_ARGS)
54695c635efSGarrett D'Amore {
547a40ea1a7SYuri Pankov 	print_otag(h, TAG_B, "");
548371584c2SYuri Pankov 	return 1;
54995c635efSGarrett D'Amore }
55095c635efSGarrett D'Amore 
55195c635efSGarrett D'Amore static int
man_I_pre(MAN_ARGS)55295c635efSGarrett D'Amore man_I_pre(MAN_ARGS)
55395c635efSGarrett D'Amore {
554a40ea1a7SYuri Pankov 	print_otag(h, TAG_I, "");
555371584c2SYuri Pankov 	return 1;
55695c635efSGarrett D'Amore }
55795c635efSGarrett D'Amore 
55895c635efSGarrett D'Amore static int
man_in_pre(MAN_ARGS)55995c635efSGarrett D'Amore man_in_pre(MAN_ARGS)
56095c635efSGarrett D'Amore {
561a40ea1a7SYuri Pankov 	print_otag(h, TAG_BR, "");
562371584c2SYuri Pankov 	return 0;
56395c635efSGarrett D'Amore }
56495c635efSGarrett D'Amore 
56595c635efSGarrett D'Amore static int
man_ign_pre(MAN_ARGS)56695c635efSGarrett D'Amore man_ign_pre(MAN_ARGS)
56795c635efSGarrett D'Amore {
568371584c2SYuri Pankov 	return 0;
56995c635efSGarrett D'Amore }
57095c635efSGarrett D'Amore 
57195c635efSGarrett D'Amore static int
man_RS_pre(MAN_ARGS)57295c635efSGarrett D'Amore man_RS_pre(MAN_ARGS)
57395c635efSGarrett D'Amore {
574cec8643bSMichal Nowak 	switch (n->type) {
575cec8643bSMichal Nowak 	case ROFFT_BLOCK:
576cec8643bSMichal Nowak 		html_close_paragraph(h);
577cec8643bSMichal Nowak 		break;
578cec8643bSMichal Nowak 	case ROFFT_HEAD:
579371584c2SYuri Pankov 		return 0;
580cec8643bSMichal Nowak 	case ROFFT_BODY:
5816640c13bSYuri Pankov 		print_otag(h, TAG_DIV, "c", "Bd-indent");
582cec8643bSMichal Nowak 		break;
583cec8643bSMichal Nowak 	default:
584cec8643bSMichal Nowak 		abort();
585cec8643bSMichal Nowak 	}
586cec8643bSMichal Nowak 	return 1;
587cec8643bSMichal Nowak }
588cec8643bSMichal Nowak 
589cec8643bSMichal Nowak static int
man_SY_pre(MAN_ARGS)590cec8643bSMichal Nowak man_SY_pre(MAN_ARGS)
591cec8643bSMichal Nowak {
592cec8643bSMichal Nowak 	switch (n->type) {
593cec8643bSMichal Nowak 	case ROFFT_BLOCK:
594cec8643bSMichal Nowak 		html_close_paragraph(h);
595cec8643bSMichal Nowak 		print_otag(h, TAG_TABLE, "c", "Nm");
596cec8643bSMichal Nowak 		print_otag(h, TAG_TR, "");
597cec8643bSMichal Nowak 		break;
598cec8643bSMichal Nowak 	case ROFFT_HEAD:
599cec8643bSMichal Nowak 		print_otag(h, TAG_TD, "");
600cec8643bSMichal Nowak 		print_otag(h, TAG_CODE, "c", "Nm");
601cec8643bSMichal Nowak 		break;
602cec8643bSMichal Nowak 	case ROFFT_BODY:
603cec8643bSMichal Nowak 		print_otag(h, TAG_TD, "");
604cec8643bSMichal Nowak 		break;
605cec8643bSMichal Nowak 	default:
606cec8643bSMichal Nowak 		abort();
607cec8643bSMichal Nowak 	}
608371584c2SYuri Pankov 	return 1;
60995c635efSGarrett D'Amore }
610698f87a4SGarrett D'Amore 
611698f87a4SGarrett D'Amore static int
man_UR_pre(MAN_ARGS)612698f87a4SGarrett D'Amore man_UR_pre(MAN_ARGS)
613698f87a4SGarrett D'Amore {
614c66b8046SYuri Pankov 	char *cp;
615cec8643bSMichal Nowak 
616698f87a4SGarrett D'Amore 	n = n->child;
617371584c2SYuri Pankov 	assert(n->type == ROFFT_HEAD);
618371584c2SYuri Pankov 	if (n->child != NULL) {
619371584c2SYuri Pankov 		assert(n->child->type == ROFFT_TEXT);
620c66b8046SYuri Pankov 		if (n->tok == MAN_MT) {
621c66b8046SYuri Pankov 			mandoc_asprintf(&cp, "mailto:%s", n->child->string);
622cec8643bSMichal Nowak 			print_otag(h, TAG_A, "ch", "Mt", cp);
623c66b8046SYuri Pankov 			free(cp);
624c66b8046SYuri Pankov 		} else
625cec8643bSMichal Nowak 			print_otag(h, TAG_A, "ch", "Lk", n->child->string);
626698f87a4SGarrett D'Amore 	}
627698f87a4SGarrett D'Amore 
628371584c2SYuri Pankov 	assert(n->next->type == ROFFT_BODY);
629371584c2SYuri Pankov 	if (n->next->child != NULL)
630698f87a4SGarrett D'Amore 		n = n->next;
631698f87a4SGarrett D'Amore 
632a40ea1a7SYuri Pankov 	print_man_nodelist(man, n->child, h);
633371584c2SYuri Pankov 	return 0;
634698f87a4SGarrett D'Amore }
635cec8643bSMichal Nowak 
636cec8643bSMichal Nowak static int
man_abort_pre(MAN_ARGS)637cec8643bSMichal Nowak man_abort_pre(MAN_ARGS)
638cec8643bSMichal Nowak {
639cec8643bSMichal Nowak 	abort();
640cec8643bSMichal Nowak }
641