xref: /freebsd/contrib/mandoc/man_html.c (revision 45a5aec3f156d8a01fe1ea4ec87c1b1d489f13ac)
1*45a5aec3SBaptiste Daroussin /*	$Id: man_html.c,v 1.174 2019/04/30 15:53:00 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
47295610fSBaptiste Daroussin  * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin  *
661d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin  *
1061d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1161d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1361d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin  */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin 
2061d06d6bSBaptiste Daroussin #include <sys/types.h>
2161d06d6bSBaptiste Daroussin 
2261d06d6bSBaptiste Daroussin #include <assert.h>
2361d06d6bSBaptiste Daroussin #include <ctype.h>
2461d06d6bSBaptiste Daroussin #include <stdio.h>
2561d06d6bSBaptiste Daroussin #include <stdlib.h>
2661d06d6bSBaptiste Daroussin #include <string.h>
2761d06d6bSBaptiste Daroussin 
2861d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
2961d06d6bSBaptiste Daroussin #include "mandoc.h"
3061d06d6bSBaptiste Daroussin #include "roff.h"
3161d06d6bSBaptiste Daroussin #include "man.h"
3261d06d6bSBaptiste Daroussin #include "out.h"
3361d06d6bSBaptiste Daroussin #include "html.h"
3461d06d6bSBaptiste Daroussin #include "main.h"
3561d06d6bSBaptiste Daroussin 
3661d06d6bSBaptiste Daroussin #define	MAN_ARGS	  const struct roff_meta *man, \
3761d06d6bSBaptiste Daroussin 			  const struct roff_node *n, \
3861d06d6bSBaptiste Daroussin 			  struct html *h
3961d06d6bSBaptiste Daroussin 
407295610fSBaptiste Daroussin struct	man_html_act {
4161d06d6bSBaptiste Daroussin 	int		(*pre)(MAN_ARGS);
4261d06d6bSBaptiste Daroussin 	int		(*post)(MAN_ARGS);
4361d06d6bSBaptiste Daroussin };
4461d06d6bSBaptiste Daroussin 
4561d06d6bSBaptiste Daroussin static	void		  print_man_head(const struct roff_meta *,
4661d06d6bSBaptiste Daroussin 				struct html *);
4761d06d6bSBaptiste Daroussin static	void		  print_man_nodelist(MAN_ARGS);
4861d06d6bSBaptiste Daroussin static	void		  print_man_node(MAN_ARGS);
497295610fSBaptiste Daroussin static	char		  list_continues(const struct roff_node *,
507295610fSBaptiste Daroussin 				const struct roff_node *);
5161d06d6bSBaptiste Daroussin static	int		  man_B_pre(MAN_ARGS);
5261d06d6bSBaptiste Daroussin static	int		  man_IP_pre(MAN_ARGS);
5361d06d6bSBaptiste Daroussin static	int		  man_I_pre(MAN_ARGS);
5461d06d6bSBaptiste Daroussin static	int		  man_OP_pre(MAN_ARGS);
5561d06d6bSBaptiste Daroussin static	int		  man_PP_pre(MAN_ARGS);
5661d06d6bSBaptiste Daroussin static	int		  man_RS_pre(MAN_ARGS);
5761d06d6bSBaptiste Daroussin static	int		  man_SH_pre(MAN_ARGS);
5861d06d6bSBaptiste Daroussin static	int		  man_SM_pre(MAN_ARGS);
597295610fSBaptiste Daroussin static	int		  man_SY_pre(MAN_ARGS);
6061d06d6bSBaptiste Daroussin static	int		  man_UR_pre(MAN_ARGS);
617295610fSBaptiste Daroussin static	int		  man_abort_pre(MAN_ARGS);
6261d06d6bSBaptiste Daroussin static	int		  man_alt_pre(MAN_ARGS);
6361d06d6bSBaptiste Daroussin static	int		  man_ign_pre(MAN_ARGS);
6461d06d6bSBaptiste Daroussin static	int		  man_in_pre(MAN_ARGS);
6561d06d6bSBaptiste Daroussin static	void		  man_root_post(const struct roff_meta *,
6661d06d6bSBaptiste Daroussin 				struct html *);
6761d06d6bSBaptiste Daroussin static	void		  man_root_pre(const struct roff_meta *,
6861d06d6bSBaptiste Daroussin 				struct html *);
6961d06d6bSBaptiste Daroussin 
707295610fSBaptiste Daroussin static	const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
7161d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* TH */
7261d06d6bSBaptiste Daroussin 	{ man_SH_pre, NULL }, /* SH */
737295610fSBaptiste Daroussin 	{ man_SH_pre, NULL }, /* SS */
7461d06d6bSBaptiste Daroussin 	{ man_IP_pre, NULL }, /* TP */
757295610fSBaptiste Daroussin 	{ man_IP_pre, NULL }, /* TQ */
767295610fSBaptiste Daroussin 	{ man_abort_pre, NULL }, /* LP */
7761d06d6bSBaptiste Daroussin 	{ man_PP_pre, NULL }, /* PP */
787295610fSBaptiste Daroussin 	{ man_abort_pre, NULL }, /* P */
7961d06d6bSBaptiste Daroussin 	{ man_IP_pre, NULL }, /* IP */
807295610fSBaptiste Daroussin 	{ man_PP_pre, NULL }, /* HP */
8161d06d6bSBaptiste Daroussin 	{ man_SM_pre, NULL }, /* SM */
8261d06d6bSBaptiste Daroussin 	{ man_SM_pre, NULL }, /* SB */
8361d06d6bSBaptiste Daroussin 	{ man_alt_pre, NULL }, /* BI */
8461d06d6bSBaptiste Daroussin 	{ man_alt_pre, NULL }, /* IB */
8561d06d6bSBaptiste Daroussin 	{ man_alt_pre, NULL }, /* BR */
8661d06d6bSBaptiste Daroussin 	{ man_alt_pre, NULL }, /* RB */
8761d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* R */
8861d06d6bSBaptiste Daroussin 	{ man_B_pre, NULL }, /* B */
8961d06d6bSBaptiste Daroussin 	{ man_I_pre, NULL }, /* I */
9061d06d6bSBaptiste Daroussin 	{ man_alt_pre, NULL }, /* IR */
9161d06d6bSBaptiste Daroussin 	{ man_alt_pre, NULL }, /* RI */
9261d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* RE */
9361d06d6bSBaptiste Daroussin 	{ man_RS_pre, NULL }, /* RS */
9461d06d6bSBaptiste Daroussin 	{ man_ign_pre, NULL }, /* DT */
9561d06d6bSBaptiste Daroussin 	{ man_ign_pre, NULL }, /* UC */
9661d06d6bSBaptiste Daroussin 	{ man_ign_pre, NULL }, /* PD */
9761d06d6bSBaptiste Daroussin 	{ man_ign_pre, NULL }, /* AT */
9861d06d6bSBaptiste Daroussin 	{ man_in_pre, NULL }, /* in */
997295610fSBaptiste Daroussin 	{ man_SY_pre, NULL }, /* SY */
1007295610fSBaptiste Daroussin 	{ NULL, NULL }, /* YS */
10161d06d6bSBaptiste Daroussin 	{ man_OP_pre, NULL }, /* OP */
10261d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* EX */
10361d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* EE */
10461d06d6bSBaptiste Daroussin 	{ man_UR_pre, NULL }, /* UR */
10561d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* UE */
10661d06d6bSBaptiste Daroussin 	{ man_UR_pre, NULL }, /* MT */
10761d06d6bSBaptiste Daroussin 	{ NULL, NULL }, /* ME */
10861d06d6bSBaptiste Daroussin };
10961d06d6bSBaptiste Daroussin 
11061d06d6bSBaptiste Daroussin 
11161d06d6bSBaptiste Daroussin void
1127295610fSBaptiste Daroussin html_man(void *arg, const struct roff_meta *man)
11361d06d6bSBaptiste Daroussin {
11461d06d6bSBaptiste Daroussin 	struct html		*h;
11561d06d6bSBaptiste Daroussin 	struct roff_node	*n;
11661d06d6bSBaptiste Daroussin 	struct tag		*t;
11761d06d6bSBaptiste Daroussin 
11861d06d6bSBaptiste Daroussin 	h = (struct html *)arg;
11961d06d6bSBaptiste Daroussin 	n = man->first->child;
12061d06d6bSBaptiste Daroussin 
12161d06d6bSBaptiste Daroussin 	if ((h->oflags & HTML_FRAGMENT) == 0) {
12261d06d6bSBaptiste Daroussin 		print_gen_decls(h);
12361d06d6bSBaptiste Daroussin 		print_otag(h, TAG_HTML, "");
1247295610fSBaptiste Daroussin 		if (n != NULL && n->type == ROFFT_COMMENT)
12561d06d6bSBaptiste Daroussin 			print_gen_comment(h, n);
12661d06d6bSBaptiste Daroussin 		t = print_otag(h, TAG_HEAD, "");
1277295610fSBaptiste Daroussin 		print_man_head(man, h);
12861d06d6bSBaptiste Daroussin 		print_tagq(h, t);
12961d06d6bSBaptiste Daroussin 		print_otag(h, TAG_BODY, "");
13061d06d6bSBaptiste Daroussin 	}
13161d06d6bSBaptiste Daroussin 
1327295610fSBaptiste Daroussin 	man_root_pre(man, h);
13361d06d6bSBaptiste Daroussin 	t = print_otag(h, TAG_DIV, "c", "manual-text");
1347295610fSBaptiste Daroussin 	print_man_nodelist(man, n, h);
13561d06d6bSBaptiste Daroussin 	print_tagq(h, t);
1367295610fSBaptiste Daroussin 	man_root_post(man, h);
13761d06d6bSBaptiste Daroussin 	print_tagq(h, NULL);
13861d06d6bSBaptiste Daroussin }
13961d06d6bSBaptiste Daroussin 
14061d06d6bSBaptiste Daroussin static void
14161d06d6bSBaptiste Daroussin print_man_head(const struct roff_meta *man, struct html *h)
14261d06d6bSBaptiste Daroussin {
14361d06d6bSBaptiste Daroussin 	char	*cp;
14461d06d6bSBaptiste Daroussin 
14561d06d6bSBaptiste Daroussin 	print_gen_head(h);
14661d06d6bSBaptiste Daroussin 	mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
14761d06d6bSBaptiste Daroussin 	print_otag(h, TAG_TITLE, "");
14861d06d6bSBaptiste Daroussin 	print_text(h, cp);
14961d06d6bSBaptiste Daroussin 	free(cp);
15061d06d6bSBaptiste Daroussin }
15161d06d6bSBaptiste Daroussin 
15261d06d6bSBaptiste Daroussin static void
15361d06d6bSBaptiste Daroussin print_man_nodelist(MAN_ARGS)
15461d06d6bSBaptiste Daroussin {
15561d06d6bSBaptiste Daroussin 	while (n != NULL) {
15661d06d6bSBaptiste Daroussin 		print_man_node(man, n, h);
15761d06d6bSBaptiste Daroussin 		n = n->next;
15861d06d6bSBaptiste Daroussin 	}
15961d06d6bSBaptiste Daroussin }
16061d06d6bSBaptiste Daroussin 
16161d06d6bSBaptiste Daroussin static void
16261d06d6bSBaptiste Daroussin print_man_node(MAN_ARGS)
16361d06d6bSBaptiste Daroussin {
16461d06d6bSBaptiste Daroussin 	struct tag	*t;
16561d06d6bSBaptiste Daroussin 	int		 child;
16661d06d6bSBaptiste Daroussin 
1677295610fSBaptiste Daroussin 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
16861d06d6bSBaptiste Daroussin 		return;
16961d06d6bSBaptiste Daroussin 
1707295610fSBaptiste Daroussin 	html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
17161d06d6bSBaptiste Daroussin 
17261d06d6bSBaptiste Daroussin 	child = 1;
17361d06d6bSBaptiste Daroussin 	switch (n->type) {
17461d06d6bSBaptiste Daroussin 	case ROFFT_TEXT:
1757295610fSBaptiste Daroussin 		if (*n->string == '\0') {
1767295610fSBaptiste Daroussin 			print_endline(h);
1777295610fSBaptiste Daroussin 			return;
1787295610fSBaptiste Daroussin 		}
1797295610fSBaptiste Daroussin 		if (*n->string == ' ' && n->flags & NODE_LINE &&
1807295610fSBaptiste Daroussin 		    (h->flags & HTML_NONEWLINE) == 0)
1817295610fSBaptiste Daroussin 			print_endline(h);
1827295610fSBaptiste Daroussin 		else if (n->flags & NODE_DELIMC)
1837295610fSBaptiste Daroussin 			h->flags |= HTML_NOSPACE;
18461d06d6bSBaptiste Daroussin 		t = h->tag;
1857295610fSBaptiste Daroussin 		t->refcnt++;
18661d06d6bSBaptiste Daroussin 		print_text(h, n->string);
18761d06d6bSBaptiste Daroussin 		break;
18861d06d6bSBaptiste Daroussin 	case ROFFT_EQN:
18961d06d6bSBaptiste Daroussin 		t = h->tag;
1907295610fSBaptiste Daroussin 		t->refcnt++;
19161d06d6bSBaptiste Daroussin 		print_eqn(h, n->eqn);
19261d06d6bSBaptiste Daroussin 		break;
19361d06d6bSBaptiste Daroussin 	case ROFFT_TBL:
19461d06d6bSBaptiste Daroussin 		/*
19561d06d6bSBaptiste Daroussin 		 * This will take care of initialising all of the table
19661d06d6bSBaptiste Daroussin 		 * state data for the first table, then tearing it down
19761d06d6bSBaptiste Daroussin 		 * for the last one.
19861d06d6bSBaptiste Daroussin 		 */
19961d06d6bSBaptiste Daroussin 		print_tbl(h, n->span);
20061d06d6bSBaptiste Daroussin 		return;
20161d06d6bSBaptiste Daroussin 	default:
20261d06d6bSBaptiste Daroussin 		/*
20361d06d6bSBaptiste Daroussin 		 * Close out scope of font prior to opening a macro
20461d06d6bSBaptiste Daroussin 		 * scope.
20561d06d6bSBaptiste Daroussin 		 */
206*45a5aec3SBaptiste Daroussin 		if (h->metac != ESCAPE_FONTROMAN) {
20761d06d6bSBaptiste Daroussin 			h->metal = h->metac;
208*45a5aec3SBaptiste Daroussin 			h->metac = ESCAPE_FONTROMAN;
20961d06d6bSBaptiste Daroussin 		}
21061d06d6bSBaptiste Daroussin 
21161d06d6bSBaptiste Daroussin 		/*
21261d06d6bSBaptiste Daroussin 		 * Close out the current table, if it's open, and unset
21361d06d6bSBaptiste Daroussin 		 * the "meta" table state.  This will be reopened on the
21461d06d6bSBaptiste Daroussin 		 * next table element.
21561d06d6bSBaptiste Daroussin 		 */
2167295610fSBaptiste Daroussin 		if (h->tblt != NULL)
21761d06d6bSBaptiste Daroussin 			print_tblclose(h);
21861d06d6bSBaptiste Daroussin 		t = h->tag;
2197295610fSBaptiste Daroussin 		t->refcnt++;
22061d06d6bSBaptiste Daroussin 		if (n->tok < ROFF_MAX) {
22161d06d6bSBaptiste Daroussin 			roff_html_pre(h, n);
2227295610fSBaptiste Daroussin 			t->refcnt--;
2237295610fSBaptiste Daroussin 			print_stagq(h, t);
2247295610fSBaptiste Daroussin 			return;
22561d06d6bSBaptiste Daroussin 		}
22661d06d6bSBaptiste Daroussin 		assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
2277295610fSBaptiste Daroussin 		if (man_html_acts[n->tok - MAN_TH].pre != NULL)
2287295610fSBaptiste Daroussin 			child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
2297295610fSBaptiste Daroussin 			    n, h);
23061d06d6bSBaptiste Daroussin 		break;
23161d06d6bSBaptiste Daroussin 	}
23261d06d6bSBaptiste Daroussin 
2337295610fSBaptiste Daroussin 	if (child && n->child != NULL)
23461d06d6bSBaptiste Daroussin 		print_man_nodelist(man, n->child, h);
23561d06d6bSBaptiste Daroussin 
23661d06d6bSBaptiste Daroussin 	/* This will automatically close out any font scope. */
2377295610fSBaptiste Daroussin 	t->refcnt--;
2387295610fSBaptiste Daroussin 	if (n->type == ROFFT_BLOCK &&
2397295610fSBaptiste Daroussin 	    (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
2407295610fSBaptiste Daroussin 		t = h->tag;
2417295610fSBaptiste Daroussin 		while (t->tag != TAG_DL && t->tag != TAG_UL)
2427295610fSBaptiste Daroussin 			t = t->next;
2437295610fSBaptiste Daroussin 		/*
2447295610fSBaptiste Daroussin 		 * Close the list if no further item of the same type
2457295610fSBaptiste Daroussin 		 * follows; otherwise, close the item only.
2467295610fSBaptiste Daroussin 		 */
2477295610fSBaptiste Daroussin 		if (list_continues(n, n->next) == '\0') {
2487295610fSBaptiste Daroussin 			print_tagq(h, t);
2497295610fSBaptiste Daroussin 			t = NULL;
2507295610fSBaptiste Daroussin 		}
2517295610fSBaptiste Daroussin 	}
2527295610fSBaptiste Daroussin 	if (t != NULL)
25361d06d6bSBaptiste Daroussin 		print_stagq(h, t);
25461d06d6bSBaptiste Daroussin 
2557295610fSBaptiste Daroussin 	if (n->flags & NODE_NOFILL && n->tok != MAN_YS &&
2567295610fSBaptiste Daroussin 	    (n->next != NULL && n->next->flags & NODE_LINE)) {
2577295610fSBaptiste Daroussin 		/* In .nf = <pre>, print even empty lines. */
2587295610fSBaptiste Daroussin 		h->col++;
25961d06d6bSBaptiste Daroussin 		print_endline(h);
26061d06d6bSBaptiste Daroussin 	}
26161d06d6bSBaptiste Daroussin }
26261d06d6bSBaptiste Daroussin 
26361d06d6bSBaptiste Daroussin static void
26461d06d6bSBaptiste Daroussin man_root_pre(const struct roff_meta *man, struct html *h)
26561d06d6bSBaptiste Daroussin {
26661d06d6bSBaptiste Daroussin 	struct tag	*t, *tt;
26761d06d6bSBaptiste Daroussin 	char		*title;
26861d06d6bSBaptiste Daroussin 
26961d06d6bSBaptiste Daroussin 	assert(man->title);
27061d06d6bSBaptiste Daroussin 	assert(man->msec);
27161d06d6bSBaptiste Daroussin 	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
27261d06d6bSBaptiste Daroussin 
27361d06d6bSBaptiste Daroussin 	t = print_otag(h, TAG_TABLE, "c", "head");
27461d06d6bSBaptiste Daroussin 	tt = print_otag(h, TAG_TR, "");
27561d06d6bSBaptiste Daroussin 
27661d06d6bSBaptiste Daroussin 	print_otag(h, TAG_TD, "c", "head-ltitle");
27761d06d6bSBaptiste Daroussin 	print_text(h, title);
27861d06d6bSBaptiste Daroussin 	print_stagq(h, tt);
27961d06d6bSBaptiste Daroussin 
28061d06d6bSBaptiste Daroussin 	print_otag(h, TAG_TD, "c", "head-vol");
2817295610fSBaptiste Daroussin 	if (man->vol != NULL)
28261d06d6bSBaptiste Daroussin 		print_text(h, man->vol);
28361d06d6bSBaptiste Daroussin 	print_stagq(h, tt);
28461d06d6bSBaptiste Daroussin 
28561d06d6bSBaptiste Daroussin 	print_otag(h, TAG_TD, "c", "head-rtitle");
28661d06d6bSBaptiste Daroussin 	print_text(h, title);
28761d06d6bSBaptiste Daroussin 	print_tagq(h, t);
28861d06d6bSBaptiste Daroussin 	free(title);
28961d06d6bSBaptiste Daroussin }
29061d06d6bSBaptiste Daroussin 
29161d06d6bSBaptiste Daroussin static void
29261d06d6bSBaptiste Daroussin man_root_post(const struct roff_meta *man, struct html *h)
29361d06d6bSBaptiste Daroussin {
29461d06d6bSBaptiste Daroussin 	struct tag	*t, *tt;
29561d06d6bSBaptiste Daroussin 
29661d06d6bSBaptiste Daroussin 	t = print_otag(h, TAG_TABLE, "c", "foot");
29761d06d6bSBaptiste Daroussin 	tt = print_otag(h, TAG_TR, "");
29861d06d6bSBaptiste Daroussin 
29961d06d6bSBaptiste Daroussin 	print_otag(h, TAG_TD, "c", "foot-date");
30061d06d6bSBaptiste Daroussin 	print_text(h, man->date);
30161d06d6bSBaptiste Daroussin 	print_stagq(h, tt);
30261d06d6bSBaptiste Daroussin 
30361d06d6bSBaptiste Daroussin 	print_otag(h, TAG_TD, "c", "foot-os");
3047295610fSBaptiste Daroussin 	if (man->os != NULL)
30561d06d6bSBaptiste Daroussin 		print_text(h, man->os);
30661d06d6bSBaptiste Daroussin 	print_tagq(h, t);
30761d06d6bSBaptiste Daroussin }
30861d06d6bSBaptiste Daroussin 
30961d06d6bSBaptiste Daroussin static int
31061d06d6bSBaptiste Daroussin man_SH_pre(MAN_ARGS)
31161d06d6bSBaptiste Daroussin {
3127295610fSBaptiste Daroussin 	const char	*class;
31361d06d6bSBaptiste Daroussin 	char		*id;
3147295610fSBaptiste Daroussin 	enum htmltag	 tag;
31561d06d6bSBaptiste Daroussin 
3167295610fSBaptiste Daroussin 	if (n->tok == MAN_SH) {
3177295610fSBaptiste Daroussin 		tag = TAG_H1;
3187295610fSBaptiste Daroussin 		class = "Sh";
3197295610fSBaptiste Daroussin 	} else {
3207295610fSBaptiste Daroussin 		tag = TAG_H2;
3217295610fSBaptiste Daroussin 		class = "Ss";
3227295610fSBaptiste Daroussin 	}
3237295610fSBaptiste Daroussin 	switch (n->type) {
3247295610fSBaptiste Daroussin 	case ROFFT_BLOCK:
3257295610fSBaptiste Daroussin 		html_close_paragraph(h);
3267295610fSBaptiste Daroussin 		print_otag(h, TAG_SECTION, "c", class);
3277295610fSBaptiste Daroussin 		break;
3287295610fSBaptiste Daroussin 	case ROFFT_HEAD:
32961d06d6bSBaptiste Daroussin 		id = html_make_id(n, 1);
3307295610fSBaptiste Daroussin 		print_otag(h, tag, "ci", class, id);
33161d06d6bSBaptiste Daroussin 		if (id != NULL)
33261d06d6bSBaptiste Daroussin 			print_otag(h, TAG_A, "chR", "permalink", id);
3337295610fSBaptiste Daroussin 		break;
3347295610fSBaptiste Daroussin 	case ROFFT_BODY:
3357295610fSBaptiste Daroussin 		break;
3367295610fSBaptiste Daroussin 	default:
3377295610fSBaptiste Daroussin 		abort();
33861d06d6bSBaptiste Daroussin 	}
33961d06d6bSBaptiste Daroussin 	return 1;
34061d06d6bSBaptiste Daroussin }
34161d06d6bSBaptiste Daroussin 
34261d06d6bSBaptiste Daroussin static int
34361d06d6bSBaptiste Daroussin man_alt_pre(MAN_ARGS)
34461d06d6bSBaptiste Daroussin {
34561d06d6bSBaptiste Daroussin 	const struct roff_node	*nn;
3467295610fSBaptiste Daroussin 	struct tag	*t;
34761d06d6bSBaptiste Daroussin 	int		 i;
34861d06d6bSBaptiste Daroussin 	enum htmltag	 fp;
34961d06d6bSBaptiste Daroussin 
3507295610fSBaptiste Daroussin 	for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
35161d06d6bSBaptiste Daroussin 		switch (n->tok) {
35261d06d6bSBaptiste Daroussin 		case MAN_BI:
35361d06d6bSBaptiste Daroussin 			fp = i % 2 ? TAG_I : TAG_B;
35461d06d6bSBaptiste Daroussin 			break;
35561d06d6bSBaptiste Daroussin 		case MAN_IB:
35661d06d6bSBaptiste Daroussin 			fp = i % 2 ? TAG_B : TAG_I;
35761d06d6bSBaptiste Daroussin 			break;
35861d06d6bSBaptiste Daroussin 		case MAN_RI:
35961d06d6bSBaptiste Daroussin 			fp = i % 2 ? TAG_I : TAG_MAX;
36061d06d6bSBaptiste Daroussin 			break;
36161d06d6bSBaptiste Daroussin 		case MAN_IR:
36261d06d6bSBaptiste Daroussin 			fp = i % 2 ? TAG_MAX : TAG_I;
36361d06d6bSBaptiste Daroussin 			break;
36461d06d6bSBaptiste Daroussin 		case MAN_BR:
36561d06d6bSBaptiste Daroussin 			fp = i % 2 ? TAG_MAX : TAG_B;
36661d06d6bSBaptiste Daroussin 			break;
36761d06d6bSBaptiste Daroussin 		case MAN_RB:
36861d06d6bSBaptiste Daroussin 			fp = i % 2 ? TAG_B : TAG_MAX;
36961d06d6bSBaptiste Daroussin 			break;
37061d06d6bSBaptiste Daroussin 		default:
37161d06d6bSBaptiste Daroussin 			abort();
37261d06d6bSBaptiste Daroussin 		}
37361d06d6bSBaptiste Daroussin 
37461d06d6bSBaptiste Daroussin 		if (i)
37561d06d6bSBaptiste Daroussin 			h->flags |= HTML_NOSPACE;
37661d06d6bSBaptiste Daroussin 
37761d06d6bSBaptiste Daroussin 		if (fp != TAG_MAX)
37861d06d6bSBaptiste Daroussin 			t = print_otag(h, fp, "");
37961d06d6bSBaptiste Daroussin 
38061d06d6bSBaptiste Daroussin 		print_text(h, nn->string);
38161d06d6bSBaptiste Daroussin 
38261d06d6bSBaptiste Daroussin 		if (fp != TAG_MAX)
38361d06d6bSBaptiste Daroussin 			print_tagq(h, t);
38461d06d6bSBaptiste Daroussin 	}
38561d06d6bSBaptiste Daroussin 	return 0;
38661d06d6bSBaptiste Daroussin }
38761d06d6bSBaptiste Daroussin 
38861d06d6bSBaptiste Daroussin static int
38961d06d6bSBaptiste Daroussin man_SM_pre(MAN_ARGS)
39061d06d6bSBaptiste Daroussin {
39161d06d6bSBaptiste Daroussin 	print_otag(h, TAG_SMALL, "");
3927295610fSBaptiste Daroussin 	if (n->tok == MAN_SB)
39361d06d6bSBaptiste Daroussin 		print_otag(h, TAG_B, "");
39461d06d6bSBaptiste Daroussin 	return 1;
39561d06d6bSBaptiste Daroussin }
39661d06d6bSBaptiste Daroussin 
39761d06d6bSBaptiste Daroussin static int
39861d06d6bSBaptiste Daroussin man_PP_pre(MAN_ARGS)
39961d06d6bSBaptiste Daroussin {
4007295610fSBaptiste Daroussin 	switch (n->type) {
4017295610fSBaptiste Daroussin 	case ROFFT_BLOCK:
4027295610fSBaptiste Daroussin 		html_close_paragraph(h);
4037295610fSBaptiste Daroussin 		break;
4047295610fSBaptiste Daroussin 	case ROFFT_HEAD:
40561d06d6bSBaptiste Daroussin 		return 0;
4067295610fSBaptiste Daroussin 	case ROFFT_BODY:
4077295610fSBaptiste Daroussin 		if (n->child != NULL &&
4087295610fSBaptiste Daroussin 		    (n->child->flags & NODE_NOFILL) == 0)
4097295610fSBaptiste Daroussin 			print_otag(h, TAG_P, "c",
4107295610fSBaptiste Daroussin 			    n->tok == MAN_PP ? "Pp" : "Pp HP");
4117295610fSBaptiste Daroussin 		break;
4127295610fSBaptiste Daroussin 	default:
4137295610fSBaptiste Daroussin 		abort();
4147295610fSBaptiste Daroussin 	}
41561d06d6bSBaptiste Daroussin 	return 1;
41661d06d6bSBaptiste Daroussin }
41761d06d6bSBaptiste Daroussin 
4187295610fSBaptiste Daroussin static char
4197295610fSBaptiste Daroussin list_continues(const struct roff_node *n1, const struct roff_node *n2)
4207295610fSBaptiste Daroussin {
4217295610fSBaptiste Daroussin 	const char *s1, *s2;
4227295610fSBaptiste Daroussin 	char c1, c2;
4237295610fSBaptiste Daroussin 
4247295610fSBaptiste Daroussin 	if (n1 == NULL || n1->type != ROFFT_BLOCK ||
4257295610fSBaptiste Daroussin 	    n2 == NULL || n2->type != ROFFT_BLOCK)
4267295610fSBaptiste Daroussin 		return '\0';
4277295610fSBaptiste Daroussin 	if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
4287295610fSBaptiste Daroussin 	    (n2->tok == MAN_TP || n2->tok == MAN_TQ))
4297295610fSBaptiste Daroussin 		return ' ';
4307295610fSBaptiste Daroussin 	if (n1->tok != MAN_IP || n2->tok != MAN_IP)
4317295610fSBaptiste Daroussin 		return '\0';
4327295610fSBaptiste Daroussin 	n1 = n1->head->child;
4337295610fSBaptiste Daroussin 	n2 = n2->head->child;
4347295610fSBaptiste Daroussin 	s1 = n1 == NULL ? "" : n1->string;
4357295610fSBaptiste Daroussin 	s2 = n2 == NULL ? "" : n2->string;
4367295610fSBaptiste Daroussin 	c1 = strcmp(s1, "*") == 0 ? '*' :
4377295610fSBaptiste Daroussin 	     strcmp(s1, "\\-") == 0 ? '-' :
4387295610fSBaptiste Daroussin 	     strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
4397295610fSBaptiste Daroussin 	c2 = strcmp(s2, "*") == 0 ? '*' :
4407295610fSBaptiste Daroussin 	     strcmp(s2, "\\-") == 0 ? '-' :
4417295610fSBaptiste Daroussin 	     strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
4427295610fSBaptiste Daroussin 	return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
4437295610fSBaptiste Daroussin }
4447295610fSBaptiste Daroussin 
44561d06d6bSBaptiste Daroussin static int
44661d06d6bSBaptiste Daroussin man_IP_pre(MAN_ARGS)
44761d06d6bSBaptiste Daroussin {
44861d06d6bSBaptiste Daroussin 	const struct roff_node	*nn;
4497295610fSBaptiste Daroussin 	const char		*list_class;
4507295610fSBaptiste Daroussin 	enum htmltag		 list_elem, body_elem;
4517295610fSBaptiste Daroussin 	char			 list_type;
45261d06d6bSBaptiste Daroussin 
4537295610fSBaptiste Daroussin 	nn = n->type == ROFFT_BLOCK ? n : n->parent;
4547295610fSBaptiste Daroussin 	if ((list_type = list_continues(nn->prev, nn)) == '\0') {
4557295610fSBaptiste Daroussin 		/* Start a new list. */
4567295610fSBaptiste Daroussin 		if ((list_type = list_continues(nn, nn->next)) == '\0')
4577295610fSBaptiste Daroussin 			list_type = ' ';
4587295610fSBaptiste Daroussin 		switch (list_type) {
4597295610fSBaptiste Daroussin 		case ' ':
4607295610fSBaptiste Daroussin 			list_class = "Bl-tag";
4617295610fSBaptiste Daroussin 			list_elem = TAG_DL;
4627295610fSBaptiste Daroussin 			break;
4637295610fSBaptiste Daroussin 		case '*':
4647295610fSBaptiste Daroussin 			list_class = "Bl-bullet";
4657295610fSBaptiste Daroussin 			list_elem = TAG_UL;
4667295610fSBaptiste Daroussin 			break;
4677295610fSBaptiste Daroussin 		case '-':
4687295610fSBaptiste Daroussin 			list_class = "Bl-dash";
4697295610fSBaptiste Daroussin 			list_elem = TAG_UL;
4707295610fSBaptiste Daroussin 			break;
4717295610fSBaptiste Daroussin 		default:
4727295610fSBaptiste Daroussin 			abort();
4737295610fSBaptiste Daroussin 		}
4747295610fSBaptiste Daroussin 	} else {
4757295610fSBaptiste Daroussin 		/* Continue a list that was started earlier. */
4767295610fSBaptiste Daroussin 		list_class = NULL;
4777295610fSBaptiste Daroussin 		list_elem = TAG_MAX;
4787295610fSBaptiste Daroussin 	}
4797295610fSBaptiste Daroussin 	body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
4807295610fSBaptiste Daroussin 
4817295610fSBaptiste Daroussin 	switch (n->type) {
4827295610fSBaptiste Daroussin 	case ROFFT_BLOCK:
4837295610fSBaptiste Daroussin 		html_close_paragraph(h);
4847295610fSBaptiste Daroussin 		if (list_elem != TAG_MAX)
4857295610fSBaptiste Daroussin 			print_otag(h, list_elem, "c", list_class);
48661d06d6bSBaptiste Daroussin 		return 1;
4877295610fSBaptiste Daroussin 	case ROFFT_HEAD:
4887295610fSBaptiste Daroussin 		if (body_elem == TAG_LI)
4897295610fSBaptiste Daroussin 			return 0;
4907295610fSBaptiste Daroussin 		print_otag(h, TAG_DT, "");
4917295610fSBaptiste Daroussin 		break;
4927295610fSBaptiste Daroussin 	case ROFFT_BODY:
4937295610fSBaptiste Daroussin 		print_otag(h, body_elem, "");
49461d06d6bSBaptiste Daroussin 		return 1;
4957295610fSBaptiste Daroussin 	default:
4967295610fSBaptiste Daroussin 		abort();
49761d06d6bSBaptiste Daroussin 	}
49861d06d6bSBaptiste Daroussin 
4997295610fSBaptiste Daroussin 	switch(n->tok) {
5007295610fSBaptiste Daroussin 	case MAN_IP:  /* Only print the first header element. */
5017295610fSBaptiste Daroussin 		if (n->child != NULL)
50261d06d6bSBaptiste Daroussin 			print_man_node(man, n->child, h);
5037295610fSBaptiste Daroussin 		break;
5047295610fSBaptiste Daroussin 	case MAN_TP:  /* Only print next-line header elements. */
5057295610fSBaptiste Daroussin 	case MAN_TQ:
50661d06d6bSBaptiste Daroussin 		nn = n->child;
5077295610fSBaptiste Daroussin 		while (nn != NULL && (NODE_LINE & nn->flags) == 0)
50861d06d6bSBaptiste Daroussin 			nn = nn->next;
5097295610fSBaptiste Daroussin 		while (nn != NULL) {
51061d06d6bSBaptiste Daroussin 			print_man_node(man, nn, h);
51161d06d6bSBaptiste Daroussin 			nn = nn->next;
51261d06d6bSBaptiste Daroussin 		}
5137295610fSBaptiste Daroussin 		break;
5147295610fSBaptiste Daroussin 	default:
5157295610fSBaptiste Daroussin 		abort();
51661d06d6bSBaptiste Daroussin 	}
51761d06d6bSBaptiste Daroussin 	return 0;
51861d06d6bSBaptiste Daroussin }
51961d06d6bSBaptiste Daroussin 
52061d06d6bSBaptiste Daroussin static int
52161d06d6bSBaptiste Daroussin man_OP_pre(MAN_ARGS)
52261d06d6bSBaptiste Daroussin {
52361d06d6bSBaptiste Daroussin 	struct tag	*tt;
52461d06d6bSBaptiste Daroussin 
52561d06d6bSBaptiste Daroussin 	print_text(h, "[");
52661d06d6bSBaptiste Daroussin 	h->flags |= HTML_NOSPACE;
52761d06d6bSBaptiste Daroussin 	tt = print_otag(h, TAG_SPAN, "c", "Op");
52861d06d6bSBaptiste Daroussin 
5297295610fSBaptiste Daroussin 	if ((n = n->child) != NULL) {
53061d06d6bSBaptiste Daroussin 		print_otag(h, TAG_B, "");
53161d06d6bSBaptiste Daroussin 		print_text(h, n->string);
53261d06d6bSBaptiste Daroussin 	}
53361d06d6bSBaptiste Daroussin 
53461d06d6bSBaptiste Daroussin 	print_stagq(h, tt);
53561d06d6bSBaptiste Daroussin 
5367295610fSBaptiste Daroussin 	if (n != NULL && n->next != NULL) {
53761d06d6bSBaptiste Daroussin 		print_otag(h, TAG_I, "");
53861d06d6bSBaptiste Daroussin 		print_text(h, n->next->string);
53961d06d6bSBaptiste Daroussin 	}
54061d06d6bSBaptiste Daroussin 
54161d06d6bSBaptiste Daroussin 	print_stagq(h, tt);
54261d06d6bSBaptiste Daroussin 	h->flags |= HTML_NOSPACE;
54361d06d6bSBaptiste Daroussin 	print_text(h, "]");
54461d06d6bSBaptiste Daroussin 	return 0;
54561d06d6bSBaptiste Daroussin }
54661d06d6bSBaptiste Daroussin 
54761d06d6bSBaptiste Daroussin static int
54861d06d6bSBaptiste Daroussin man_B_pre(MAN_ARGS)
54961d06d6bSBaptiste Daroussin {
55061d06d6bSBaptiste Daroussin 	print_otag(h, TAG_B, "");
55161d06d6bSBaptiste Daroussin 	return 1;
55261d06d6bSBaptiste Daroussin }
55361d06d6bSBaptiste Daroussin 
55461d06d6bSBaptiste Daroussin static int
55561d06d6bSBaptiste Daroussin man_I_pre(MAN_ARGS)
55661d06d6bSBaptiste Daroussin {
55761d06d6bSBaptiste Daroussin 	print_otag(h, TAG_I, "");
55861d06d6bSBaptiste Daroussin 	return 1;
55961d06d6bSBaptiste Daroussin }
56061d06d6bSBaptiste Daroussin 
56161d06d6bSBaptiste Daroussin static int
56261d06d6bSBaptiste Daroussin man_in_pre(MAN_ARGS)
56361d06d6bSBaptiste Daroussin {
56461d06d6bSBaptiste Daroussin 	print_otag(h, TAG_BR, "");
56561d06d6bSBaptiste Daroussin 	return 0;
56661d06d6bSBaptiste Daroussin }
56761d06d6bSBaptiste Daroussin 
56861d06d6bSBaptiste Daroussin static int
56961d06d6bSBaptiste Daroussin man_ign_pre(MAN_ARGS)
57061d06d6bSBaptiste Daroussin {
57161d06d6bSBaptiste Daroussin 	return 0;
57261d06d6bSBaptiste Daroussin }
57361d06d6bSBaptiste Daroussin 
57461d06d6bSBaptiste Daroussin static int
57561d06d6bSBaptiste Daroussin man_RS_pre(MAN_ARGS)
57661d06d6bSBaptiste Daroussin {
5777295610fSBaptiste Daroussin 	switch (n->type) {
5787295610fSBaptiste Daroussin 	case ROFFT_BLOCK:
5797295610fSBaptiste Daroussin 		html_close_paragraph(h);
5807295610fSBaptiste Daroussin 		break;
5817295610fSBaptiste Daroussin 	case ROFFT_HEAD:
58261d06d6bSBaptiste Daroussin 		return 0;
5837295610fSBaptiste Daroussin 	case ROFFT_BODY:
58461d06d6bSBaptiste Daroussin 		print_otag(h, TAG_DIV, "c", "Bd-indent");
5857295610fSBaptiste Daroussin 		break;
5867295610fSBaptiste Daroussin 	default:
5877295610fSBaptiste Daroussin 		abort();
5887295610fSBaptiste Daroussin 	}
5897295610fSBaptiste Daroussin 	return 1;
5907295610fSBaptiste Daroussin }
5917295610fSBaptiste Daroussin 
5927295610fSBaptiste Daroussin static int
5937295610fSBaptiste Daroussin man_SY_pre(MAN_ARGS)
5947295610fSBaptiste Daroussin {
5957295610fSBaptiste Daroussin 	switch (n->type) {
5967295610fSBaptiste Daroussin 	case ROFFT_BLOCK:
5977295610fSBaptiste Daroussin 		html_close_paragraph(h);
5987295610fSBaptiste Daroussin 		print_otag(h, TAG_TABLE, "c", "Nm");
5997295610fSBaptiste Daroussin 		print_otag(h, TAG_TR, "");
6007295610fSBaptiste Daroussin 		break;
6017295610fSBaptiste Daroussin 	case ROFFT_HEAD:
6027295610fSBaptiste Daroussin 		print_otag(h, TAG_TD, "");
6037295610fSBaptiste Daroussin 		print_otag(h, TAG_CODE, "c", "Nm");
6047295610fSBaptiste Daroussin 		break;
6057295610fSBaptiste Daroussin 	case ROFFT_BODY:
6067295610fSBaptiste Daroussin 		print_otag(h, TAG_TD, "");
6077295610fSBaptiste Daroussin 		break;
6087295610fSBaptiste Daroussin 	default:
6097295610fSBaptiste Daroussin 		abort();
6107295610fSBaptiste Daroussin 	}
61161d06d6bSBaptiste Daroussin 	return 1;
61261d06d6bSBaptiste Daroussin }
61361d06d6bSBaptiste Daroussin 
61461d06d6bSBaptiste Daroussin static int
61561d06d6bSBaptiste Daroussin man_UR_pre(MAN_ARGS)
61661d06d6bSBaptiste Daroussin {
61761d06d6bSBaptiste Daroussin 	char *cp;
6187295610fSBaptiste Daroussin 
61961d06d6bSBaptiste Daroussin 	n = n->child;
62061d06d6bSBaptiste Daroussin 	assert(n->type == ROFFT_HEAD);
62161d06d6bSBaptiste Daroussin 	if (n->child != NULL) {
62261d06d6bSBaptiste Daroussin 		assert(n->child->type == ROFFT_TEXT);
62361d06d6bSBaptiste Daroussin 		if (n->tok == MAN_MT) {
62461d06d6bSBaptiste Daroussin 			mandoc_asprintf(&cp, "mailto:%s", n->child->string);
6257295610fSBaptiste Daroussin 			print_otag(h, TAG_A, "ch", "Mt", cp);
62661d06d6bSBaptiste Daroussin 			free(cp);
62761d06d6bSBaptiste Daroussin 		} else
6287295610fSBaptiste Daroussin 			print_otag(h, TAG_A, "ch", "Lk", n->child->string);
62961d06d6bSBaptiste Daroussin 	}
63061d06d6bSBaptiste Daroussin 
63161d06d6bSBaptiste Daroussin 	assert(n->next->type == ROFFT_BODY);
63261d06d6bSBaptiste Daroussin 	if (n->next->child != NULL)
63361d06d6bSBaptiste Daroussin 		n = n->next;
63461d06d6bSBaptiste Daroussin 
63561d06d6bSBaptiste Daroussin 	print_man_nodelist(man, n->child, h);
63661d06d6bSBaptiste Daroussin 	return 0;
63761d06d6bSBaptiste Daroussin }
6387295610fSBaptiste Daroussin 
6397295610fSBaptiste Daroussin static int
6407295610fSBaptiste Daroussin man_abort_pre(MAN_ARGS)
6417295610fSBaptiste Daroussin {
6427295610fSBaptiste Daroussin 	abort();
6437295610fSBaptiste Daroussin }
644