xref: /dragonfly/contrib/mdocml/man_html.c (revision 99db7d0e)
1*99db7d0eSSascha Wildner /* $Id: man_html.c,v 1.179 2020/10/16 17:22:43 schwarze Exp $ */
280387638SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
454ba9607SSascha Wildner  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
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  *
1054ba9607SSascha 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
1254ba9607SSascha 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.
17*99db7d0eSSascha Wildner  *
18*99db7d0eSSascha Wildner  * HTML formatter for man(7) used by mandoc(1).
1980387638SSascha Wildner  */
2080387638SSascha Wildner #include "config.h"
2180387638SSascha Wildner 
2280387638SSascha Wildner #include <sys/types.h>
2380387638SSascha Wildner 
2480387638SSascha Wildner #include <assert.h>
2580387638SSascha Wildner #include <ctype.h>
2680387638SSascha Wildner #include <stdio.h>
2780387638SSascha Wildner #include <stdlib.h>
2880387638SSascha Wildner #include <string.h>
2980387638SSascha Wildner 
30070c62a6SFranco Fichtner #include "mandoc_aux.h"
3154ba9607SSascha Wildner #include "mandoc.h"
3254ba9607SSascha Wildner #include "roff.h"
3354ba9607SSascha Wildner #include "man.h"
3480387638SSascha Wildner #include "out.h"
3580387638SSascha Wildner #include "html.h"
3680387638SSascha Wildner #include "main.h"
3780387638SSascha Wildner 
3854ba9607SSascha Wildner #define	MAN_ARGS	  const struct roff_meta *man, \
39*99db7d0eSSascha Wildner 			  struct roff_node *n, \
4080387638SSascha Wildner 			  struct html *h
4180387638SSascha Wildner 
4254ba9607SSascha Wildner struct	man_html_act {
4380387638SSascha Wildner 	int		(*pre)(MAN_ARGS);
4480387638SSascha Wildner 	int		(*post)(MAN_ARGS);
4580387638SSascha Wildner };
4680387638SSascha Wildner 
4754ba9607SSascha Wildner static	void		  print_man_head(const struct roff_meta *,
4854ba9607SSascha Wildner 				struct html *);
4980387638SSascha Wildner static	void		  print_man_nodelist(MAN_ARGS);
5080387638SSascha Wildner static	void		  print_man_node(MAN_ARGS);
5154ba9607SSascha Wildner static	char		  list_continues(const struct roff_node *,
5254ba9607SSascha Wildner 				const struct roff_node *);
5336342e81SSascha Wildner static	int		  man_B_pre(MAN_ARGS);
5436342e81SSascha Wildner static	int		  man_IP_pre(MAN_ARGS);
5536342e81SSascha Wildner static	int		  man_I_pre(MAN_ARGS);
5636342e81SSascha Wildner static	int		  man_OP_pre(MAN_ARGS);
5736342e81SSascha Wildner static	int		  man_PP_pre(MAN_ARGS);
5836342e81SSascha Wildner static	int		  man_RS_pre(MAN_ARGS);
5936342e81SSascha Wildner static	int		  man_SH_pre(MAN_ARGS);
6036342e81SSascha Wildner static	int		  man_SM_pre(MAN_ARGS);
6154ba9607SSascha Wildner static	int		  man_SY_pre(MAN_ARGS);
627888c61dSFranco Fichtner static	int		  man_UR_pre(MAN_ARGS);
6354ba9607SSascha Wildner static	int		  man_abort_pre(MAN_ARGS);
6480387638SSascha Wildner static	int		  man_alt_pre(MAN_ARGS);
6580387638SSascha Wildner static	int		  man_ign_pre(MAN_ARGS);
6680387638SSascha Wildner static	int		  man_in_pre(MAN_ARGS);
6754ba9607SSascha Wildner static	void		  man_root_post(const struct roff_meta *,
6854ba9607SSascha Wildner 				struct html *);
6954ba9607SSascha Wildner static	void		  man_root_pre(const struct roff_meta *,
7054ba9607SSascha Wildner 				struct html *);
7180387638SSascha Wildner 
7254ba9607SSascha Wildner static	const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
7380387638SSascha Wildner 	{ NULL, NULL }, /* TH */
7480387638SSascha Wildner 	{ man_SH_pre, NULL }, /* SH */
7554ba9607SSascha Wildner 	{ man_SH_pre, NULL }, /* SS */
7680387638SSascha Wildner 	{ man_IP_pre, NULL }, /* TP */
7754ba9607SSascha Wildner 	{ man_IP_pre, NULL }, /* TQ */
7854ba9607SSascha Wildner 	{ man_abort_pre, NULL }, /* LP */
7980387638SSascha Wildner 	{ man_PP_pre, NULL }, /* PP */
8054ba9607SSascha Wildner 	{ man_abort_pre, NULL }, /* P */
8180387638SSascha Wildner 	{ man_IP_pre, NULL }, /* IP */
8254ba9607SSascha Wildner 	{ man_PP_pre, NULL }, /* HP */
8380387638SSascha Wildner 	{ man_SM_pre, NULL }, /* SM */
8480387638SSascha Wildner 	{ man_SM_pre, NULL }, /* SB */
8580387638SSascha Wildner 	{ man_alt_pre, NULL }, /* BI */
8680387638SSascha Wildner 	{ man_alt_pre, NULL }, /* IB */
8780387638SSascha Wildner 	{ man_alt_pre, NULL }, /* BR */
8880387638SSascha Wildner 	{ man_alt_pre, NULL }, /* RB */
8980387638SSascha Wildner 	{ NULL, NULL }, /* R */
9080387638SSascha Wildner 	{ man_B_pre, NULL }, /* B */
9180387638SSascha Wildner 	{ man_I_pre, NULL }, /* I */
9280387638SSascha Wildner 	{ man_alt_pre, NULL }, /* IR */
9380387638SSascha Wildner 	{ man_alt_pre, NULL }, /* RI */
9480387638SSascha Wildner 	{ NULL, NULL }, /* RE */
9580387638SSascha Wildner 	{ man_RS_pre, NULL }, /* RS */
9680387638SSascha Wildner 	{ man_ign_pre, NULL }, /* DT */
9780387638SSascha Wildner 	{ man_ign_pre, NULL }, /* UC */
9880387638SSascha Wildner 	{ man_ign_pre, NULL }, /* PD */
9980387638SSascha Wildner 	{ man_ign_pre, NULL }, /* AT */
10080387638SSascha Wildner 	{ man_in_pre, NULL }, /* in */
10154ba9607SSascha Wildner 	{ man_SY_pre, NULL }, /* SY */
10254ba9607SSascha Wildner 	{ NULL, NULL }, /* YS */
10336342e81SSascha Wildner 	{ man_OP_pre, NULL }, /* OP */
10454ba9607SSascha Wildner 	{ NULL, NULL }, /* EX */
10554ba9607SSascha Wildner 	{ NULL, NULL }, /* EE */
1067888c61dSFranco Fichtner 	{ man_UR_pre, NULL }, /* UR */
1077888c61dSFranco Fichtner 	{ NULL, NULL }, /* UE */
10854ba9607SSascha Wildner 	{ man_UR_pre, NULL }, /* MT */
10954ba9607SSascha Wildner 	{ NULL, NULL }, /* ME */
11080387638SSascha Wildner };
11180387638SSascha Wildner 
112070c62a6SFranco Fichtner 
11380387638SSascha Wildner void
html_man(void * arg,const struct roff_meta * man)11454ba9607SSascha Wildner html_man(void *arg, const struct roff_meta *man)
11580387638SSascha Wildner {
11654ba9607SSascha Wildner 	struct html		*h;
11754ba9607SSascha Wildner 	struct roff_node	*n;
11854ba9607SSascha Wildner 	struct tag		*t;
11980387638SSascha Wildner 
12054ba9607SSascha Wildner 	h = (struct html *)arg;
12154ba9607SSascha Wildner 	n = man->first->child;
12280387638SSascha Wildner 
12354ba9607SSascha Wildner 	if ((h->oflags & HTML_FRAGMENT) == 0) {
12436342e81SSascha Wildner 		print_gen_decls(h);
12554ba9607SSascha Wildner 		print_otag(h, TAG_HTML, "");
12654ba9607SSascha Wildner 		if (n != NULL && n->type == ROFFT_COMMENT)
12754ba9607SSascha Wildner 			print_gen_comment(h, n);
12854ba9607SSascha Wildner 		t = print_otag(h, TAG_HEAD, "");
12954ba9607SSascha Wildner 		print_man_head(man, h);
13080387638SSascha Wildner 		print_tagq(h, t);
13154ba9607SSascha Wildner 		print_otag(h, TAG_BODY, "");
13254ba9607SSascha Wildner 	}
13354ba9607SSascha Wildner 
13454ba9607SSascha Wildner 	man_root_pre(man, h);
13554ba9607SSascha Wildner 	t = print_otag(h, TAG_DIV, "c", "manual-text");
13654ba9607SSascha Wildner 	print_man_nodelist(man, n, h);
13754ba9607SSascha Wildner 	print_tagq(h, t);
13854ba9607SSascha Wildner 	man_root_post(man, h);
13954ba9607SSascha Wildner 	print_tagq(h, NULL);
14080387638SSascha Wildner }
14180387638SSascha Wildner 
14280387638SSascha Wildner static void
print_man_head(const struct roff_meta * man,struct html * h)14354ba9607SSascha Wildner print_man_head(const struct roff_meta *man, struct html *h)
14480387638SSascha Wildner {
14554ba9607SSascha Wildner 	char	*cp;
14680387638SSascha Wildner 
14780387638SSascha Wildner 	print_gen_head(h);
14854ba9607SSascha Wildner 	mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
14954ba9607SSascha Wildner 	print_otag(h, TAG_TITLE, "");
15054ba9607SSascha Wildner 	print_text(h, cp);
15154ba9607SSascha Wildner 	free(cp);
15280387638SSascha Wildner }
15380387638SSascha Wildner 
15480387638SSascha Wildner static void
print_man_nodelist(MAN_ARGS)15580387638SSascha Wildner print_man_nodelist(MAN_ARGS)
15680387638SSascha Wildner {
15754ba9607SSascha Wildner 	while (n != NULL) {
15854ba9607SSascha Wildner 		print_man_node(man, n, h);
15954ba9607SSascha Wildner 		n = n->next;
16054ba9607SSascha Wildner 	}
16180387638SSascha Wildner }
16280387638SSascha Wildner 
16380387638SSascha Wildner static void
print_man_node(MAN_ARGS)16480387638SSascha Wildner print_man_node(MAN_ARGS)
16580387638SSascha Wildner {
16680387638SSascha Wildner 	struct tag	*t;
16754ba9607SSascha Wildner 	int		 child;
16854ba9607SSascha Wildner 
16954ba9607SSascha Wildner 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
17054ba9607SSascha Wildner 		return;
17154ba9607SSascha Wildner 
172*99db7d0eSSascha Wildner 	if ((n->flags & NODE_NOFILL) == 0)
173*99db7d0eSSascha Wildner 		html_fillmode(h, ROFF_fi);
174*99db7d0eSSascha Wildner 	else if (html_fillmode(h, ROFF_nf) == ROFF_nf &&
175*99db7d0eSSascha Wildner 	    n->tok != ROFF_fi && n->flags & NODE_LINE &&
176*99db7d0eSSascha Wildner 	    (n->prev == NULL || n->prev->tok != MAN_YS))
177*99db7d0eSSascha Wildner 		print_endline(h);
17880387638SSascha Wildner 
17980387638SSascha Wildner 	child = 1;
18080387638SSascha Wildner 	switch (n->type) {
18154ba9607SSascha Wildner 	case ROFFT_TEXT:
18254ba9607SSascha Wildner 		if (*n->string == '\0') {
18354ba9607SSascha Wildner 			print_endline(h);
18460e1e752SSascha Wildner 			return;
18536342e81SSascha Wildner 		}
18654ba9607SSascha Wildner 		if (*n->string == ' ' && n->flags & NODE_LINE &&
18754ba9607SSascha Wildner 		    (h->flags & HTML_NONEWLINE) == 0)
188*99db7d0eSSascha Wildner 			print_otag(h, TAG_BR, "");
18954ba9607SSascha Wildner 		else if (n->flags & NODE_DELIMC)
19054ba9607SSascha Wildner 			h->flags |= HTML_NOSPACE;
19154ba9607SSascha Wildner 		t = h->tag;
19254ba9607SSascha Wildner 		t->refcnt++;
19380387638SSascha Wildner 		print_text(h, n->string);
19454ba9607SSascha Wildner 		break;
19554ba9607SSascha Wildner 	case ROFFT_EQN:
19654ba9607SSascha Wildner 		t = h->tag;
19754ba9607SSascha Wildner 		t->refcnt++;
19836342e81SSascha Wildner 		print_eqn(h, n->eqn);
19980387638SSascha Wildner 		break;
20054ba9607SSascha Wildner 	case ROFFT_TBL:
20160e1e752SSascha Wildner 		/*
20260e1e752SSascha Wildner 		 * This will take care of initialising all of the table
20360e1e752SSascha Wildner 		 * state data for the first table, then tearing it down
20460e1e752SSascha Wildner 		 * for the last one.
20560e1e752SSascha Wildner 		 */
20660e1e752SSascha Wildner 		print_tbl(h, n->span);
20760e1e752SSascha Wildner 		return;
20880387638SSascha Wildner 	default:
20980387638SSascha Wildner 		/*
21080387638SSascha Wildner 		 * Close out scope of font prior to opening a macro
21160e1e752SSascha Wildner 		 * scope.
21280387638SSascha Wildner 		 */
213*99db7d0eSSascha Wildner 		if (h->metac != ESCAPE_FONTROMAN) {
21480387638SSascha Wildner 			h->metal = h->metac;
215*99db7d0eSSascha Wildner 			h->metac = ESCAPE_FONTROMAN;
21680387638SSascha Wildner 		}
21760e1e752SSascha Wildner 
21860e1e752SSascha Wildner 		/*
21960e1e752SSascha Wildner 		 * Close out the current table, if it's open, and unset
22060e1e752SSascha Wildner 		 * the "meta" table state.  This will be reopened on the
22160e1e752SSascha Wildner 		 * next table element.
22260e1e752SSascha Wildner 		 */
22354ba9607SSascha Wildner 		if (h->tblt != NULL)
22460e1e752SSascha Wildner 			print_tblclose(h);
22554ba9607SSascha Wildner 		t = h->tag;
22654ba9607SSascha Wildner 		t->refcnt++;
22754ba9607SSascha Wildner 		if (n->tok < ROFF_MAX) {
22854ba9607SSascha Wildner 			roff_html_pre(h, n);
22954ba9607SSascha Wildner 			t->refcnt--;
23054ba9607SSascha Wildner 			print_stagq(h, t);
23154ba9607SSascha Wildner 			return;
23260e1e752SSascha Wildner 		}
23354ba9607SSascha Wildner 		assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
23454ba9607SSascha Wildner 		if (man_html_acts[n->tok - MAN_TH].pre != NULL)
23554ba9607SSascha Wildner 			child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
23654ba9607SSascha Wildner 			    n, h);
23780387638SSascha Wildner 		break;
23880387638SSascha Wildner 	}
23980387638SSascha Wildner 
24054ba9607SSascha Wildner 	if (child && n->child != NULL)
24154ba9607SSascha Wildner 		print_man_nodelist(man, n->child, h);
24280387638SSascha Wildner 
24380387638SSascha Wildner 	/* This will automatically close out any font scope. */
24454ba9607SSascha Wildner 	t->refcnt--;
24554ba9607SSascha Wildner 	if (n->type == ROFFT_BLOCK &&
24654ba9607SSascha Wildner 	    (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
24754ba9607SSascha Wildner 		t = h->tag;
24854ba9607SSascha Wildner 		while (t->tag != TAG_DL && t->tag != TAG_UL)
24954ba9607SSascha Wildner 			t = t->next;
25054ba9607SSascha Wildner 		/*
25154ba9607SSascha Wildner 		 * Close the list if no further item of the same type
25254ba9607SSascha Wildner 		 * follows; otherwise, close the item only.
25354ba9607SSascha Wildner 		 */
254*99db7d0eSSascha Wildner 		if (list_continues(n, roff_node_next(n)) == '\0') {
25554ba9607SSascha Wildner 			print_tagq(h, t);
25654ba9607SSascha Wildner 			t = NULL;
25754ba9607SSascha Wildner 		}
25854ba9607SSascha Wildner 	}
25954ba9607SSascha Wildner 	if (t != NULL)
26080387638SSascha Wildner 		print_stagq(h, t);
26180387638SSascha Wildner }
26280387638SSascha Wildner 
26360e1e752SSascha Wildner static void
man_root_pre(const struct roff_meta * man,struct html * h)26454ba9607SSascha 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 
27354ba9607SSascha Wildner 	t = print_otag(h, TAG_TABLE, "c", "head");
27454ba9607SSascha Wildner 	tt = print_otag(h, TAG_TR, "");
27580387638SSascha Wildner 
27654ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "head-ltitle");
27780387638SSascha Wildner 	print_text(h, title);
27880387638SSascha Wildner 	print_stagq(h, tt);
27980387638SSascha Wildner 
28054ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "head-vol");
28154ba9607SSascha Wildner 	if (man->vol != NULL)
282070c62a6SFranco Fichtner 		print_text(h, man->vol);
28380387638SSascha Wildner 	print_stagq(h, tt);
28480387638SSascha Wildner 
28554ba9607SSascha 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
man_root_post(const struct roff_meta * man,struct html * h)29254ba9607SSascha Wildner man_root_post(const struct roff_meta *man, struct html *h)
29380387638SSascha Wildner {
29480387638SSascha Wildner 	struct tag	*t, *tt;
29580387638SSascha Wildner 
29654ba9607SSascha Wildner 	t = print_otag(h, TAG_TABLE, "c", "foot");
29754ba9607SSascha Wildner 	tt = print_otag(h, TAG_TR, "");
29880387638SSascha Wildner 
29954ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "foot-date");
300f88b6c16SFranco Fichtner 	print_text(h, man->date);
30180387638SSascha Wildner 	print_stagq(h, tt);
30280387638SSascha Wildner 
30354ba9607SSascha Wildner 	print_otag(h, TAG_TD, "c", "foot-os");
30454ba9607SSascha Wildner 	if (man->os != NULL)
30554ba9607SSascha Wildner 		print_text(h, man->os);
30680387638SSascha Wildner 	print_tagq(h, t);
30780387638SSascha Wildner }
30880387638SSascha Wildner 
30980387638SSascha Wildner static int
man_SH_pre(MAN_ARGS)31080387638SSascha Wildner man_SH_pre(MAN_ARGS)
31180387638SSascha Wildner {
31254ba9607SSascha Wildner 	const char	*class;
31354ba9607SSascha Wildner 	enum htmltag	 tag;
31480387638SSascha Wildner 
31554ba9607SSascha Wildner 	if (n->tok == MAN_SH) {
31654ba9607SSascha Wildner 		tag = TAG_H1;
31754ba9607SSascha Wildner 		class = "Sh";
31854ba9607SSascha Wildner 	} else {
31954ba9607SSascha Wildner 		tag = TAG_H2;
32054ba9607SSascha Wildner 		class = "Ss";
32154ba9607SSascha Wildner 	}
32254ba9607SSascha Wildner 	switch (n->type) {
32354ba9607SSascha Wildner 	case ROFFT_BLOCK:
32454ba9607SSascha Wildner 		html_close_paragraph(h);
32554ba9607SSascha Wildner 		print_otag(h, TAG_SECTION, "c", class);
32654ba9607SSascha Wildner 		break;
32754ba9607SSascha Wildner 	case ROFFT_HEAD:
328*99db7d0eSSascha Wildner 		print_otag_id(h, tag, class, n);
32954ba9607SSascha Wildner 		break;
33054ba9607SSascha Wildner 	case ROFFT_BODY:
33154ba9607SSascha Wildner 		break;
33254ba9607SSascha Wildner 	default:
33354ba9607SSascha Wildner 		abort();
33454ba9607SSascha Wildner 	}
33554ba9607SSascha Wildner 	return 1;
33680387638SSascha Wildner }
33780387638SSascha Wildner 
33880387638SSascha Wildner static int
man_alt_pre(MAN_ARGS)33980387638SSascha Wildner man_alt_pre(MAN_ARGS)
34080387638SSascha Wildner {
34154ba9607SSascha Wildner 	const struct roff_node	*nn;
34280387638SSascha Wildner 	struct tag	*t;
34354ba9607SSascha Wildner 	int		 i;
34454ba9607SSascha Wildner 	enum htmltag	 fp;
34580387638SSascha Wildner 
34654ba9607SSascha Wildner 	for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
34780387638SSascha Wildner 		switch (n->tok) {
348070c62a6SFranco Fichtner 		case MAN_BI:
34980387638SSascha Wildner 			fp = i % 2 ? TAG_I : TAG_B;
35080387638SSascha Wildner 			break;
351070c62a6SFranco Fichtner 		case MAN_IB:
35280387638SSascha Wildner 			fp = i % 2 ? TAG_B : TAG_I;
35380387638SSascha Wildner 			break;
354070c62a6SFranco Fichtner 		case MAN_RI:
35580387638SSascha Wildner 			fp = i % 2 ? TAG_I : TAG_MAX;
35680387638SSascha Wildner 			break;
357070c62a6SFranco Fichtner 		case MAN_IR:
35880387638SSascha Wildner 			fp = i % 2 ? TAG_MAX : TAG_I;
35980387638SSascha Wildner 			break;
360070c62a6SFranco Fichtner 		case MAN_BR:
36180387638SSascha Wildner 			fp = i % 2 ? TAG_MAX : TAG_B;
36280387638SSascha Wildner 			break;
363070c62a6SFranco Fichtner 		case MAN_RB:
36480387638SSascha Wildner 			fp = i % 2 ? TAG_B : TAG_MAX;
36580387638SSascha Wildner 			break;
36680387638SSascha Wildner 		default:
36780387638SSascha Wildner 			abort();
36880387638SSascha Wildner 		}
36980387638SSascha Wildner 
37080387638SSascha Wildner 		if (i)
37180387638SSascha Wildner 			h->flags |= HTML_NOSPACE;
37280387638SSascha Wildner 
37354ba9607SSascha Wildner 		if (fp != TAG_MAX)
37454ba9607SSascha Wildner 			t = print_otag(h, fp, "");
37580387638SSascha Wildner 
37654ba9607SSascha Wildner 		print_text(h, nn->string);
37780387638SSascha Wildner 
37854ba9607SSascha Wildner 		if (fp != TAG_MAX)
37980387638SSascha Wildner 			print_tagq(h, t);
38080387638SSascha Wildner 	}
38154ba9607SSascha Wildner 	return 0;
38280387638SSascha Wildner }
38380387638SSascha Wildner 
38480387638SSascha Wildner static int
man_SM_pre(MAN_ARGS)38580387638SSascha Wildner man_SM_pre(MAN_ARGS)
38680387638SSascha Wildner {
38754ba9607SSascha Wildner 	print_otag(h, TAG_SMALL, "");
38854ba9607SSascha Wildner 	if (n->tok == MAN_SB)
38954ba9607SSascha Wildner 		print_otag(h, TAG_B, "");
39054ba9607SSascha Wildner 	return 1;
39180387638SSascha Wildner }
39280387638SSascha Wildner 
39380387638SSascha Wildner static int
man_PP_pre(MAN_ARGS)39480387638SSascha Wildner man_PP_pre(MAN_ARGS)
39580387638SSascha Wildner {
39654ba9607SSascha Wildner 	switch (n->type) {
39754ba9607SSascha Wildner 	case ROFFT_BLOCK:
39854ba9607SSascha Wildner 		html_close_paragraph(h);
39954ba9607SSascha Wildner 		break;
40054ba9607SSascha Wildner 	case ROFFT_HEAD:
40154ba9607SSascha Wildner 		return 0;
40254ba9607SSascha Wildner 	case ROFFT_BODY:
40354ba9607SSascha Wildner 		if (n->child != NULL &&
40454ba9607SSascha Wildner 		    (n->child->flags & NODE_NOFILL) == 0)
40554ba9607SSascha Wildner 			print_otag(h, TAG_P, "c",
40654ba9607SSascha Wildner 			    n->tok == MAN_PP ? "Pp" : "Pp HP");
40754ba9607SSascha Wildner 		break;
40854ba9607SSascha Wildner 	default:
40954ba9607SSascha Wildner 		abort();
41054ba9607SSascha Wildner 	}
41154ba9607SSascha Wildner 	return 1;
41254ba9607SSascha Wildner }
41380387638SSascha Wildner 
41454ba9607SSascha Wildner static char
list_continues(const struct roff_node * n1,const struct roff_node * n2)41554ba9607SSascha Wildner list_continues(const struct roff_node *n1, const struct roff_node *n2)
41654ba9607SSascha Wildner {
41754ba9607SSascha Wildner 	const char *s1, *s2;
41854ba9607SSascha Wildner 	char c1, c2;
41980387638SSascha Wildner 
42054ba9607SSascha Wildner 	if (n1 == NULL || n1->type != ROFFT_BLOCK ||
42154ba9607SSascha Wildner 	    n2 == NULL || n2->type != ROFFT_BLOCK)
42254ba9607SSascha Wildner 		return '\0';
42354ba9607SSascha Wildner 	if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
42454ba9607SSascha Wildner 	    (n2->tok == MAN_TP || n2->tok == MAN_TQ))
42554ba9607SSascha Wildner 		return ' ';
42654ba9607SSascha Wildner 	if (n1->tok != MAN_IP || n2->tok != MAN_IP)
42754ba9607SSascha Wildner 		return '\0';
42854ba9607SSascha Wildner 	n1 = n1->head->child;
42954ba9607SSascha Wildner 	n2 = n2->head->child;
43054ba9607SSascha Wildner 	s1 = n1 == NULL ? "" : n1->string;
43154ba9607SSascha Wildner 	s2 = n2 == NULL ? "" : n2->string;
43254ba9607SSascha Wildner 	c1 = strcmp(s1, "*") == 0 ? '*' :
43354ba9607SSascha Wildner 	     strcmp(s1, "\\-") == 0 ? '-' :
43454ba9607SSascha Wildner 	     strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
43554ba9607SSascha Wildner 	c2 = strcmp(s2, "*") == 0 ? '*' :
43654ba9607SSascha Wildner 	     strcmp(s2, "\\-") == 0 ? '-' :
43754ba9607SSascha Wildner 	     strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
43854ba9607SSascha Wildner 	return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
43980387638SSascha Wildner }
44080387638SSascha Wildner 
44180387638SSascha Wildner static int
man_IP_pre(MAN_ARGS)44280387638SSascha Wildner man_IP_pre(MAN_ARGS)
44380387638SSascha Wildner {
444*99db7d0eSSascha Wildner 	struct roff_node	*nn;
44554ba9607SSascha Wildner 	const char		*list_class;
44654ba9607SSascha Wildner 	enum htmltag		 list_elem, body_elem;
44754ba9607SSascha Wildner 	char			 list_type;
44880387638SSascha Wildner 
44954ba9607SSascha Wildner 	nn = n->type == ROFFT_BLOCK ? n : n->parent;
450*99db7d0eSSascha Wildner 	list_type = list_continues(roff_node_prev(nn), nn);
451*99db7d0eSSascha Wildner 	if (list_type == '\0') {
45254ba9607SSascha Wildner 		/* Start a new list. */
453*99db7d0eSSascha Wildner 		list_type = list_continues(nn, roff_node_next(nn));
454*99db7d0eSSascha Wildner 		if (list_type == '\0')
45554ba9607SSascha Wildner 			list_type = ' ';
45654ba9607SSascha Wildner 		switch (list_type) {
45754ba9607SSascha Wildner 		case ' ':
45854ba9607SSascha Wildner 			list_class = "Bl-tag";
45954ba9607SSascha Wildner 			list_elem = TAG_DL;
46054ba9607SSascha Wildner 			break;
46154ba9607SSascha Wildner 		case '*':
46254ba9607SSascha Wildner 			list_class = "Bl-bullet";
46354ba9607SSascha Wildner 			list_elem = TAG_UL;
46454ba9607SSascha Wildner 			break;
46554ba9607SSascha Wildner 		case '-':
46654ba9607SSascha Wildner 			list_class = "Bl-dash";
46754ba9607SSascha Wildner 			list_elem = TAG_UL;
46854ba9607SSascha Wildner 			break;
46954ba9607SSascha Wildner 		default:
47054ba9607SSascha Wildner 			abort();
47154ba9607SSascha Wildner 		}
47254ba9607SSascha Wildner 	} else {
47354ba9607SSascha Wildner 		/* Continue a list that was started earlier. */
47454ba9607SSascha Wildner 		list_class = NULL;
47554ba9607SSascha Wildner 		list_elem = TAG_MAX;
47654ba9607SSascha Wildner 	}
47754ba9607SSascha Wildner 	body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
47854ba9607SSascha Wildner 
47954ba9607SSascha Wildner 	switch (n->type) {
48054ba9607SSascha Wildner 	case ROFFT_BLOCK:
48154ba9607SSascha Wildner 		html_close_paragraph(h);
48254ba9607SSascha Wildner 		if (list_elem != TAG_MAX)
48354ba9607SSascha Wildner 			print_otag(h, list_elem, "c", list_class);
48454ba9607SSascha Wildner 		return 1;
48554ba9607SSascha Wildner 	case ROFFT_HEAD:
48654ba9607SSascha Wildner 		if (body_elem == TAG_LI)
48754ba9607SSascha Wildner 			return 0;
488*99db7d0eSSascha Wildner 		print_otag_id(h, TAG_DT, NULL, n);
48954ba9607SSascha Wildner 		break;
49054ba9607SSascha Wildner 	case ROFFT_BODY:
49154ba9607SSascha Wildner 		print_otag(h, body_elem, "");
49254ba9607SSascha Wildner 		return 1;
49354ba9607SSascha Wildner 	default:
49454ba9607SSascha Wildner 		abort();
49580387638SSascha Wildner 	}
49654ba9607SSascha Wildner 	switch(n->tok) {
49754ba9607SSascha Wildner 	case MAN_IP:  /* Only print the first header element. */
49854ba9607SSascha Wildner 		if (n->child != NULL)
49954ba9607SSascha Wildner 			print_man_node(man, n->child, h);
50054ba9607SSascha Wildner 		break;
50154ba9607SSascha Wildner 	case MAN_TP:  /* Only print next-line header elements. */
50254ba9607SSascha Wildner 	case MAN_TQ:
503070c62a6SFranco Fichtner 		nn = n->child;
50454ba9607SSascha Wildner 		while (nn != NULL && (NODE_LINE & nn->flags) == 0)
505070c62a6SFranco Fichtner 			nn = nn->next;
50654ba9607SSascha Wildner 		while (nn != NULL) {
50754ba9607SSascha Wildner 			print_man_node(man, nn, h);
508070c62a6SFranco Fichtner 			nn = nn->next;
509070c62a6SFranco Fichtner 		}
51054ba9607SSascha Wildner 		break;
51154ba9607SSascha Wildner 	default:
51254ba9607SSascha Wildner 		abort();
513070c62a6SFranco Fichtner 	}
51454ba9607SSascha Wildner 	return 0;
51580387638SSascha Wildner }
51680387638SSascha Wildner 
51736342e81SSascha Wildner static int
man_OP_pre(MAN_ARGS)51836342e81SSascha Wildner man_OP_pre(MAN_ARGS)
51936342e81SSascha Wildner {
52036342e81SSascha Wildner 	struct tag	*tt;
52136342e81SSascha Wildner 
52236342e81SSascha Wildner 	print_text(h, "[");
52336342e81SSascha Wildner 	h->flags |= HTML_NOSPACE;
52454ba9607SSascha Wildner 	tt = print_otag(h, TAG_SPAN, "c", "Op");
52536342e81SSascha Wildner 
52654ba9607SSascha Wildner 	if ((n = n->child) != NULL) {
52754ba9607SSascha Wildner 		print_otag(h, TAG_B, "");
52836342e81SSascha Wildner 		print_text(h, n->string);
52936342e81SSascha Wildner 	}
53036342e81SSascha Wildner 
53136342e81SSascha Wildner 	print_stagq(h, tt);
53236342e81SSascha Wildner 
53354ba9607SSascha Wildner 	if (n != NULL && n->next != NULL) {
53454ba9607SSascha Wildner 		print_otag(h, TAG_I, "");
53536342e81SSascha Wildner 		print_text(h, n->next->string);
53636342e81SSascha Wildner 	}
53736342e81SSascha Wildner 
53836342e81SSascha Wildner 	print_stagq(h, tt);
53936342e81SSascha Wildner 	h->flags |= HTML_NOSPACE;
54036342e81SSascha Wildner 	print_text(h, "]");
54154ba9607SSascha Wildner 	return 0;
54236342e81SSascha Wildner }
54336342e81SSascha Wildner 
54480387638SSascha Wildner static int
man_B_pre(MAN_ARGS)54580387638SSascha Wildner man_B_pre(MAN_ARGS)
54680387638SSascha Wildner {
54754ba9607SSascha Wildner 	print_otag(h, TAG_B, "");
54854ba9607SSascha Wildner 	return 1;
54980387638SSascha Wildner }
55080387638SSascha Wildner 
55180387638SSascha Wildner static int
man_I_pre(MAN_ARGS)55280387638SSascha Wildner man_I_pre(MAN_ARGS)
55380387638SSascha Wildner {
55454ba9607SSascha Wildner 	print_otag(h, TAG_I, "");
55554ba9607SSascha Wildner 	return 1;
55680387638SSascha Wildner }
55780387638SSascha Wildner 
55880387638SSascha Wildner static int
man_in_pre(MAN_ARGS)55980387638SSascha Wildner man_in_pre(MAN_ARGS)
56080387638SSascha Wildner {
56154ba9607SSascha Wildner 	print_otag(h, TAG_BR, "");
56254ba9607SSascha Wildner 	return 0;
56380387638SSascha Wildner }
56480387638SSascha Wildner 
56580387638SSascha Wildner static int
man_ign_pre(MAN_ARGS)56680387638SSascha Wildner man_ign_pre(MAN_ARGS)
56780387638SSascha Wildner {
56854ba9607SSascha Wildner 	return 0;
56980387638SSascha Wildner }
57080387638SSascha Wildner 
57180387638SSascha Wildner static int
man_RS_pre(MAN_ARGS)57280387638SSascha Wildner man_RS_pre(MAN_ARGS)
57380387638SSascha Wildner {
57454ba9607SSascha Wildner 	switch (n->type) {
57554ba9607SSascha Wildner 	case ROFFT_BLOCK:
57654ba9607SSascha Wildner 		html_close_paragraph(h);
57754ba9607SSascha Wildner 		break;
57854ba9607SSascha Wildner 	case ROFFT_HEAD:
57954ba9607SSascha Wildner 		return 0;
58054ba9607SSascha Wildner 	case ROFFT_BODY:
58154ba9607SSascha Wildner 		print_otag(h, TAG_DIV, "c", "Bd-indent");
58254ba9607SSascha Wildner 		break;
58354ba9607SSascha Wildner 	default:
58454ba9607SSascha Wildner 		abort();
58554ba9607SSascha Wildner 	}
58654ba9607SSascha Wildner 	return 1;
58754ba9607SSascha Wildner }
58880387638SSascha Wildner 
58954ba9607SSascha Wildner static int
man_SY_pre(MAN_ARGS)59054ba9607SSascha Wildner man_SY_pre(MAN_ARGS)
59154ba9607SSascha Wildner {
59254ba9607SSascha Wildner 	switch (n->type) {
59354ba9607SSascha Wildner 	case ROFFT_BLOCK:
59454ba9607SSascha Wildner 		html_close_paragraph(h);
59554ba9607SSascha Wildner 		print_otag(h, TAG_TABLE, "c", "Nm");
59654ba9607SSascha Wildner 		print_otag(h, TAG_TR, "");
59754ba9607SSascha Wildner 		break;
59854ba9607SSascha Wildner 	case ROFFT_HEAD:
59954ba9607SSascha Wildner 		print_otag(h, TAG_TD, "");
60054ba9607SSascha Wildner 		print_otag(h, TAG_CODE, "c", "Nm");
60154ba9607SSascha Wildner 		break;
60254ba9607SSascha Wildner 	case ROFFT_BODY:
60354ba9607SSascha Wildner 		print_otag(h, TAG_TD, "");
60454ba9607SSascha Wildner 		break;
60554ba9607SSascha Wildner 	default:
60654ba9607SSascha Wildner 		abort();
60754ba9607SSascha Wildner 	}
60854ba9607SSascha Wildner 	return 1;
60980387638SSascha Wildner }
6107888c61dSFranco Fichtner 
6117888c61dSFranco Fichtner static int
man_UR_pre(MAN_ARGS)6127888c61dSFranco Fichtner man_UR_pre(MAN_ARGS)
6137888c61dSFranco Fichtner {
61454ba9607SSascha Wildner 	char *cp;
6157888c61dSFranco Fichtner 
6167888c61dSFranco Fichtner 	n = n->child;
61754ba9607SSascha Wildner 	assert(n->type == ROFFT_HEAD);
61854ba9607SSascha Wildner 	if (n->child != NULL) {
61954ba9607SSascha Wildner 		assert(n->child->type == ROFFT_TEXT);
62054ba9607SSascha Wildner 		if (n->tok == MAN_MT) {
62154ba9607SSascha Wildner 			mandoc_asprintf(&cp, "mailto:%s", n->child->string);
62254ba9607SSascha Wildner 			print_otag(h, TAG_A, "ch", "Mt", cp);
62354ba9607SSascha Wildner 			free(cp);
62454ba9607SSascha Wildner 		} else
62554ba9607SSascha Wildner 			print_otag(h, TAG_A, "ch", "Lk", n->child->string);
6267888c61dSFranco Fichtner 	}
6277888c61dSFranco Fichtner 
62854ba9607SSascha Wildner 	assert(n->next->type == ROFFT_BODY);
62954ba9607SSascha Wildner 	if (n->next->child != NULL)
6307888c61dSFranco Fichtner 		n = n->next;
6317888c61dSFranco Fichtner 
63254ba9607SSascha Wildner 	print_man_nodelist(man, n->child, h);
63354ba9607SSascha Wildner 	return 0;
63454ba9607SSascha Wildner }
6357888c61dSFranco Fichtner 
63654ba9607SSascha Wildner static int
man_abort_pre(MAN_ARGS)63754ba9607SSascha Wildner man_abort_pre(MAN_ARGS)
63854ba9607SSascha Wildner {
63954ba9607SSascha Wildner 	abort();
6407888c61dSFranco Fichtner }
641