xref: /netbsd-src/external/bsd/mdocml/dist/man_html.c (revision 544c191c349c1704c9d5e679d12ec15cff579663)
1*544c191cSchristos /*	Id: man_html.c,v 1.173 2019/03/02 16:30:53 schwarze Exp  */
24154958bSjoerg /*
3fec65c98Schristos  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*544c191cSchristos  * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
54154958bSjoerg  *
64154958bSjoerg  * Permission to use, copy, modify, and distribute this software for any
74154958bSjoerg  * purpose with or without fee is hereby granted, provided that the above
84154958bSjoerg  * copyright notice and this permission notice appear in all copies.
94154958bSjoerg  *
109ff1f2acSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
114154958bSjoerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129ff1f2acSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
134154958bSjoerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144154958bSjoerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154154958bSjoerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164154958bSjoerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174154958bSjoerg  */
18d5e63c8dSjoerg #include "config.h"
19d5e63c8dSjoerg 
204154958bSjoerg #include <sys/types.h>
214154958bSjoerg 
224154958bSjoerg #include <assert.h>
234154958bSjoerg #include <ctype.h>
244154958bSjoerg #include <stdio.h>
254154958bSjoerg #include <stdlib.h>
264154958bSjoerg #include <string.h>
274154958bSjoerg 
28fec65c98Schristos #include "mandoc_aux.h"
29c9bcef03Schristos #include "mandoc.h"
309ff1f2acSchristos #include "roff.h"
31fec65c98Schristos #include "man.h"
324154958bSjoerg #include "out.h"
334154958bSjoerg #include "html.h"
344154958bSjoerg #include "main.h"
354154958bSjoerg 
369ff1f2acSchristos #define	MAN_ARGS	  const struct roff_meta *man, \
379ff1f2acSchristos 			  const struct roff_node *n, \
384154958bSjoerg 			  struct html *h
394154958bSjoerg 
40*544c191cSchristos struct	man_html_act {
414154958bSjoerg 	int		(*pre)(MAN_ARGS);
424154958bSjoerg 	int		(*post)(MAN_ARGS);
434154958bSjoerg };
444154958bSjoerg 
45c9bcef03Schristos static	void		  print_man_head(const struct roff_meta *,
46c9bcef03Schristos 				struct html *);
474154958bSjoerg static	void		  print_man_nodelist(MAN_ARGS);
484154958bSjoerg static	void		  print_man_node(MAN_ARGS);
49*544c191cSchristos static	char		  list_continues(const struct roff_node *,
50*544c191cSchristos 				const struct roff_node *);
51cf816816Sjoerg static	int		  man_B_pre(MAN_ARGS);
52cf816816Sjoerg static	int		  man_IP_pre(MAN_ARGS);
53cf816816Sjoerg static	int		  man_I_pre(MAN_ARGS);
54cf816816Sjoerg static	int		  man_OP_pre(MAN_ARGS);
55cf816816Sjoerg static	int		  man_PP_pre(MAN_ARGS);
56cf816816Sjoerg static	int		  man_RS_pre(MAN_ARGS);
57cf816816Sjoerg static	int		  man_SH_pre(MAN_ARGS);
58cf816816Sjoerg static	int		  man_SM_pre(MAN_ARGS);
59*544c191cSchristos static	int		  man_SY_pre(MAN_ARGS);
6070f041f9Sjoerg static	int		  man_UR_pre(MAN_ARGS);
61*544c191cSchristos static	int		  man_abort_pre(MAN_ARGS);
624154958bSjoerg static	int		  man_alt_pre(MAN_ARGS);
634154958bSjoerg static	int		  man_ign_pre(MAN_ARGS);
647da9b934Sjoerg static	int		  man_in_pre(MAN_ARGS);
65c9bcef03Schristos static	void		  man_root_post(const struct roff_meta *,
66c9bcef03Schristos 				struct html *);
67c9bcef03Schristos static	void		  man_root_pre(const struct roff_meta *,
68c9bcef03Schristos 				struct html *);
694154958bSjoerg 
70*544c191cSchristos static	const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
714154958bSjoerg 	{ NULL, NULL }, /* TH */
724154958bSjoerg 	{ man_SH_pre, NULL }, /* SH */
73*544c191cSchristos 	{ man_SH_pre, NULL }, /* SS */
744154958bSjoerg 	{ man_IP_pre, NULL }, /* TP */
75*544c191cSchristos 	{ man_IP_pre, NULL }, /* TQ */
76*544c191cSchristos 	{ man_abort_pre, NULL }, /* LP */
774154958bSjoerg 	{ man_PP_pre, NULL }, /* PP */
78*544c191cSchristos 	{ man_abort_pre, NULL }, /* P */
794154958bSjoerg 	{ man_IP_pre, NULL }, /* IP */
80*544c191cSchristos 	{ man_PP_pre, NULL }, /* HP */
814154958bSjoerg 	{ man_SM_pre, NULL }, /* SM */
82c0d9444aSjoerg 	{ man_SM_pre, NULL }, /* SB */
834154958bSjoerg 	{ man_alt_pre, NULL }, /* BI */
844154958bSjoerg 	{ man_alt_pre, NULL }, /* IB */
854154958bSjoerg 	{ man_alt_pre, NULL }, /* BR */
864154958bSjoerg 	{ man_alt_pre, NULL }, /* RB */
874154958bSjoerg 	{ NULL, NULL }, /* R */
884154958bSjoerg 	{ man_B_pre, NULL }, /* B */
894154958bSjoerg 	{ man_I_pre, NULL }, /* I */
904154958bSjoerg 	{ man_alt_pre, NULL }, /* IR */
914154958bSjoerg 	{ man_alt_pre, NULL }, /* RI */
924154958bSjoerg 	{ NULL, NULL }, /* RE */
934154958bSjoerg 	{ man_RS_pre, NULL }, /* RS */
944154958bSjoerg 	{ man_ign_pre, NULL }, /* DT */
954154958bSjoerg 	{ man_ign_pre, NULL }, /* UC */
9622af4063Sjoerg 	{ man_ign_pre, NULL }, /* PD */
970a84adc5Sjoerg 	{ man_ign_pre, NULL }, /* AT */
987da9b934Sjoerg 	{ man_in_pre, NULL }, /* in */
99*544c191cSchristos 	{ man_SY_pre, NULL }, /* SY */
100*544c191cSchristos 	{ NULL, NULL }, /* YS */
101cf816816Sjoerg 	{ man_OP_pre, NULL }, /* OP */
1029508192eSchristos 	{ NULL, NULL }, /* EX */
1039508192eSchristos 	{ NULL, NULL }, /* EE */
10470f041f9Sjoerg 	{ man_UR_pre, NULL }, /* UR */
10570f041f9Sjoerg 	{ NULL, NULL }, /* UE */
106c9bcef03Schristos 	{ man_UR_pre, NULL }, /* MT */
107c9bcef03Schristos 	{ NULL, NULL }, /* ME */
1084154958bSjoerg };
1094154958bSjoerg 
110fec65c98Schristos 
1114154958bSjoerg void
html_man(void * arg,const struct roff_meta * man)112*544c191cSchristos html_man(void *arg, const struct roff_meta *man)
1134154958bSjoerg {
1149ff1f2acSchristos 	struct html		*h;
115c9bcef03Schristos 	struct roff_node	*n;
1169508192eSchristos 	struct tag		*t;
1174154958bSjoerg 
1189ff1f2acSchristos 	h = (struct html *)arg;
119c9bcef03Schristos 	n = man->first->child;
120c5f73b34Sjoerg 
1219508192eSchristos 	if ((h->oflags & HTML_FRAGMENT) == 0) {
122c5f73b34Sjoerg 		print_gen_decls(h);
1239508192eSchristos 		print_otag(h, TAG_HTML, "");
124*544c191cSchristos 		if (n != NULL && n->type == ROFFT_COMMENT)
125c9bcef03Schristos 			print_gen_comment(h, n);
1269508192eSchristos 		t = print_otag(h, TAG_HEAD, "");
127*544c191cSchristos 		print_man_head(man, h);
1284154958bSjoerg 		print_tagq(h, t);
1299508192eSchristos 		print_otag(h, TAG_BODY, "");
1309508192eSchristos 	}
1319508192eSchristos 
132*544c191cSchristos 	man_root_pre(man, h);
1339508192eSchristos 	t = print_otag(h, TAG_DIV, "c", "manual-text");
134*544c191cSchristos 	print_man_nodelist(man, n, h);
1359508192eSchristos 	print_tagq(h, t);
136*544c191cSchristos 	man_root_post(man, h);
1379508192eSchristos 	print_tagq(h, NULL);
1384154958bSjoerg }
1394154958bSjoerg 
1404154958bSjoerg static void
print_man_head(const struct roff_meta * man,struct html * h)141c9bcef03Schristos print_man_head(const struct roff_meta *man, struct html *h)
1424154958bSjoerg {
1439508192eSchristos 	char	*cp;
1444154958bSjoerg 
1454154958bSjoerg 	print_gen_head(h);
1469508192eSchristos 	mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
1479508192eSchristos 	print_otag(h, TAG_TITLE, "");
1489508192eSchristos 	print_text(h, cp);
1499508192eSchristos 	free(cp);
1504154958bSjoerg }
1514154958bSjoerg 
1524154958bSjoerg static void
print_man_nodelist(MAN_ARGS)1534154958bSjoerg print_man_nodelist(MAN_ARGS)
1544154958bSjoerg {
155fec65c98Schristos 	while (n != NULL) {
1569508192eSchristos 		print_man_node(man, n, h);
157fec65c98Schristos 		n = n->next;
1584154958bSjoerg 	}
159fec65c98Schristos }
1604154958bSjoerg 
1614154958bSjoerg static void
print_man_node(MAN_ARGS)1624154958bSjoerg print_man_node(MAN_ARGS)
1634154958bSjoerg {
1649508192eSchristos 	struct tag	*t;
1659508192eSchristos 	int		 child;
1669508192eSchristos 
167*544c191cSchristos 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1689508192eSchristos 		return;
1699508192eSchristos 
170*544c191cSchristos 	html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
1719508192eSchristos 
1729508192eSchristos 	child = 1;
1739508192eSchristos 	switch (n->type) {
1749508192eSchristos 	case ROFFT_TEXT:
175*544c191cSchristos 		if (*n->string == '\0') {
176*544c191cSchristos 			print_endline(h);
177*544c191cSchristos 			return;
178*544c191cSchristos 		}
179*544c191cSchristos 		if (*n->string == ' ' && n->flags & NODE_LINE &&
180*544c191cSchristos 		    (h->flags & HTML_NONEWLINE) == 0)
181*544c191cSchristos 			print_endline(h);
182*544c191cSchristos 		else if (n->flags & NODE_DELIMC)
183*544c191cSchristos 			h->flags |= HTML_NOSPACE;
1849508192eSchristos 		t = h->tag;
185*544c191cSchristos 		t->refcnt++;
1864154958bSjoerg 		print_text(h, n->string);
1879508192eSchristos 		break;
1889ff1f2acSchristos 	case ROFFT_EQN:
1899508192eSchristos 		t = h->tag;
190*544c191cSchristos 		t->refcnt++;
191c5f73b34Sjoerg 		print_eqn(h, n->eqn);
192c0d9444aSjoerg 		break;
1939ff1f2acSchristos 	case ROFFT_TBL:
19448741257Sjoerg 		/*
19548741257Sjoerg 		 * This will take care of initialising all of the table
19648741257Sjoerg 		 * state data for the first table, then tearing it down
19748741257Sjoerg 		 * for the last one.
19848741257Sjoerg 		 */
19948741257Sjoerg 		print_tbl(h, n->span);
20048741257Sjoerg 		return;
2014154958bSjoerg 	default:
2027d71a621Sjoerg 		/*
2037d71a621Sjoerg 		 * Close out scope of font prior to opening a macro
20448741257Sjoerg 		 * scope.
2057d71a621Sjoerg 		 */
206c0d9444aSjoerg 		if (HTMLFONT_NONE != h->metac) {
207c0d9444aSjoerg 			h->metal = h->metac;
208c0d9444aSjoerg 			h->metac = HTMLFONT_NONE;
2097d71a621Sjoerg 		}
21048741257Sjoerg 
21148741257Sjoerg 		/*
21248741257Sjoerg 		 * Close out the current table, if it's open, and unset
21348741257Sjoerg 		 * the "meta" table state.  This will be reopened on the
21448741257Sjoerg 		 * next table element.
21548741257Sjoerg 		 */
216*544c191cSchristos 		if (h->tblt != NULL)
21748741257Sjoerg 			print_tblclose(h);
2189508192eSchristos 		t = h->tag;
219*544c191cSchristos 		t->refcnt++;
220c9bcef03Schristos 		if (n->tok < ROFF_MAX) {
221c9bcef03Schristos 			roff_html_pre(h, n);
222*544c191cSchristos 			t->refcnt--;
223*544c191cSchristos 			print_stagq(h, t);
224*544c191cSchristos 			return;
225c9bcef03Schristos 		}
226c9bcef03Schristos 		assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
227*544c191cSchristos 		if (man_html_acts[n->tok - MAN_TH].pre != NULL)
228*544c191cSchristos 			child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
229*544c191cSchristos 			    n, h);
2304154958bSjoerg 		break;
2314154958bSjoerg 	}
2324154958bSjoerg 
233*544c191cSchristos 	if (child && n->child != NULL)
2349508192eSchristos 		print_man_nodelist(man, n->child, h);
2354154958bSjoerg 
2367d71a621Sjoerg 	/* This will automatically close out any font scope. */
237*544c191cSchristos 	t->refcnt--;
238*544c191cSchristos 	if (n->type == ROFFT_BLOCK &&
239*544c191cSchristos 	    (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
240*544c191cSchristos 		t = h->tag;
241*544c191cSchristos 		while (t->tag != TAG_DL && t->tag != TAG_UL)
242*544c191cSchristos 			t = t->next;
243*544c191cSchristos 		/*
244*544c191cSchristos 		 * Close the list if no further item of the same type
245*544c191cSchristos 		 * follows; otherwise, close the item only.
246*544c191cSchristos 		 */
247*544c191cSchristos 		if (list_continues(n, n->next) == '\0') {
248*544c191cSchristos 			print_tagq(h, t);
249*544c191cSchristos 			t = NULL;
250*544c191cSchristos 		}
251*544c191cSchristos 	}
252*544c191cSchristos 	if (t != NULL)
2534154958bSjoerg 		print_stagq(h, t);
2544154958bSjoerg 
255*544c191cSchristos 	if (n->flags & NODE_NOFILL && n->tok != MAN_YS &&
256*544c191cSchristos 	    (n->next != NULL && n->next->flags & NODE_LINE)) {
257*544c191cSchristos 		/* In .nf = <pre>, print even empty lines. */
258*544c191cSchristos 		h->col++;
2599508192eSchristos 		print_endline(h);
2604154958bSjoerg 	}
2614154958bSjoerg }
2624154958bSjoerg 
26348741257Sjoerg static void
man_root_pre(const struct roff_meta * man,struct html * h)264c9bcef03Schristos man_root_pre(const struct roff_meta *man, struct html *h)
2654154958bSjoerg {
2664154958bSjoerg 	struct tag	*t, *tt;
267fec65c98Schristos 	char		*title;
2684154958bSjoerg 
26970f041f9Sjoerg 	assert(man->title);
27070f041f9Sjoerg 	assert(man->msec);
271fec65c98Schristos 	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
2724154958bSjoerg 
2739508192eSchristos 	t = print_otag(h, TAG_TABLE, "c", "head");
2749508192eSchristos 	tt = print_otag(h, TAG_TR, "");
275c0d9444aSjoerg 
2769508192eSchristos 	print_otag(h, TAG_TD, "c", "head-ltitle");
2774154958bSjoerg 	print_text(h, title);
2784154958bSjoerg 	print_stagq(h, tt);
2794154958bSjoerg 
2809508192eSchristos 	print_otag(h, TAG_TD, "c", "head-vol");
281*544c191cSchristos 	if (man->vol != NULL)
282fec65c98Schristos 		print_text(h, man->vol);
2834154958bSjoerg 	print_stagq(h, tt);
2844154958bSjoerg 
2859508192eSchristos 	print_otag(h, TAG_TD, "c", "head-rtitle");
2864154958bSjoerg 	print_text(h, title);
2874154958bSjoerg 	print_tagq(h, t);
288fec65c98Schristos 	free(title);
2894154958bSjoerg }
2904154958bSjoerg 
2914154958bSjoerg static void
man_root_post(const struct roff_meta * man,struct html * h)292c9bcef03Schristos man_root_post(const struct roff_meta *man, struct html *h)
2934154958bSjoerg {
2944154958bSjoerg 	struct tag	*t, *tt;
2954154958bSjoerg 
2969508192eSchristos 	t = print_otag(h, TAG_TABLE, "c", "foot");
2979508192eSchristos 	tt = print_otag(h, TAG_TR, "");
298c0d9444aSjoerg 
2999508192eSchristos 	print_otag(h, TAG_TD, "c", "foot-date");
30070f041f9Sjoerg 	print_text(h, man->date);
3014154958bSjoerg 	print_stagq(h, tt);
3024154958bSjoerg 
3039508192eSchristos 	print_otag(h, TAG_TD, "c", "foot-os");
304*544c191cSchristos 	if (man->os != NULL)
3059ff1f2acSchristos 		print_text(h, man->os);
3064154958bSjoerg 	print_tagq(h, t);
3074154958bSjoerg }
3084154958bSjoerg 
3094154958bSjoerg static int
man_SH_pre(MAN_ARGS)3104154958bSjoerg man_SH_pre(MAN_ARGS)
3114154958bSjoerg {
312*544c191cSchristos 	const char	*class;
313c9bcef03Schristos 	char		*id;
314*544c191cSchristos 	enum htmltag	 tag;
315c9bcef03Schristos 
316*544c191cSchristos 	if (n->tok == MAN_SH) {
317*544c191cSchristos 		tag = TAG_H1;
318*544c191cSchristos 		class = "Sh";
319*544c191cSchristos 	} else {
320*544c191cSchristos 		tag = TAG_H2;
321*544c191cSchristos 		class = "Ss";
322*544c191cSchristos 	}
323*544c191cSchristos 	switch (n->type) {
324*544c191cSchristos 	case ROFFT_BLOCK:
325*544c191cSchristos 		html_close_paragraph(h);
326*544c191cSchristos 		print_otag(h, TAG_SECTION, "c", class);
327*544c191cSchristos 		break;
328*544c191cSchristos 	case ROFFT_HEAD:
329c9bcef03Schristos 		id = html_make_id(n, 1);
330*544c191cSchristos 		print_otag(h, tag, "ci", class, id);
331c9bcef03Schristos 		if (id != NULL)
332c9bcef03Schristos 			print_otag(h, TAG_A, "chR", "permalink", id);
333*544c191cSchristos 		break;
334*544c191cSchristos 	case ROFFT_BODY:
335*544c191cSchristos 		break;
336*544c191cSchristos 	default:
337*544c191cSchristos 		abort();
338c9bcef03Schristos 	}
3399ff1f2acSchristos 	return 1;
3404154958bSjoerg }
3414154958bSjoerg 
3424154958bSjoerg static int
man_alt_pre(MAN_ARGS)3434154958bSjoerg man_alt_pre(MAN_ARGS)
3444154958bSjoerg {
3459ff1f2acSchristos 	const struct roff_node	*nn;
346*544c191cSchristos 	struct tag	*t;
3479508192eSchristos 	int		 i;
348c0d9444aSjoerg 	enum htmltag	 fp;
3494154958bSjoerg 
350*544c191cSchristos 	for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
3514154958bSjoerg 		switch (n->tok) {
352fec65c98Schristos 		case MAN_BI:
353c0d9444aSjoerg 			fp = i % 2 ? TAG_I : TAG_B;
3544154958bSjoerg 			break;
355fec65c98Schristos 		case MAN_IB:
356c0d9444aSjoerg 			fp = i % 2 ? TAG_B : TAG_I;
3574154958bSjoerg 			break;
358fec65c98Schristos 		case MAN_RI:
359c0d9444aSjoerg 			fp = i % 2 ? TAG_I : TAG_MAX;
3604154958bSjoerg 			break;
361fec65c98Schristos 		case MAN_IR:
362c0d9444aSjoerg 			fp = i % 2 ? TAG_MAX : TAG_I;
3634154958bSjoerg 			break;
364fec65c98Schristos 		case MAN_BR:
365c0d9444aSjoerg 			fp = i % 2 ? TAG_MAX : TAG_B;
3664154958bSjoerg 			break;
367fec65c98Schristos 		case MAN_RB:
368c0d9444aSjoerg 			fp = i % 2 ? TAG_B : TAG_MAX;
3694154958bSjoerg 			break;
3704154958bSjoerg 		default:
3714154958bSjoerg 			abort();
3724154958bSjoerg 		}
3734154958bSjoerg 
3744154958bSjoerg 		if (i)
3754154958bSjoerg 			h->flags |= HTML_NOSPACE;
3764154958bSjoerg 
3779508192eSchristos 		if (fp != TAG_MAX)
3789508192eSchristos 			t = print_otag(h, fp, "");
379c0d9444aSjoerg 
3809508192eSchristos 		print_text(h, nn->string);
381c0d9444aSjoerg 
3829508192eSchristos 		if (fp != TAG_MAX)
3834154958bSjoerg 			print_tagq(h, t);
3844154958bSjoerg 	}
3859ff1f2acSchristos 	return 0;
3864154958bSjoerg }
3874154958bSjoerg 
3884154958bSjoerg static int
man_SM_pre(MAN_ARGS)3894154958bSjoerg man_SM_pre(MAN_ARGS)
3904154958bSjoerg {
3919508192eSchristos 	print_otag(h, TAG_SMALL, "");
392*544c191cSchristos 	if (n->tok == MAN_SB)
3939508192eSchristos 		print_otag(h, TAG_B, "");
3949ff1f2acSchristos 	return 1;
3954154958bSjoerg }
3964154958bSjoerg 
3974154958bSjoerg static int
man_PP_pre(MAN_ARGS)3984154958bSjoerg man_PP_pre(MAN_ARGS)
3994154958bSjoerg {
400*544c191cSchristos 	switch (n->type) {
401*544c191cSchristos 	case ROFFT_BLOCK:
402*544c191cSchristos 		html_close_paragraph(h);
403*544c191cSchristos 		break;
404*544c191cSchristos 	case ROFFT_HEAD:
4059ff1f2acSchristos 		return 0;
406*544c191cSchristos 	case ROFFT_BODY:
407*544c191cSchristos 		if (n->child != NULL &&
408*544c191cSchristos 		    (n->child->flags & NODE_NOFILL) == 0)
409*544c191cSchristos 			print_otag(h, TAG_P, "c",
410*544c191cSchristos 			    n->tok == MAN_PP ? "Pp" : "Pp HP");
411*544c191cSchristos 		break;
412*544c191cSchristos 	default:
413*544c191cSchristos 		abort();
414*544c191cSchristos 	}
4159ff1f2acSchristos 	return 1;
4164154958bSjoerg }
4174154958bSjoerg 
418*544c191cSchristos static char
list_continues(const struct roff_node * n1,const struct roff_node * n2)419*544c191cSchristos list_continues(const struct roff_node *n1, const struct roff_node *n2)
420*544c191cSchristos {
421*544c191cSchristos 	const char *s1, *s2;
422*544c191cSchristos 	char c1, c2;
423*544c191cSchristos 
424*544c191cSchristos 	if (n1 == NULL || n1->type != ROFFT_BLOCK ||
425*544c191cSchristos 	    n2 == NULL || n2->type != ROFFT_BLOCK)
426*544c191cSchristos 		return '\0';
427*544c191cSchristos 	if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
428*544c191cSchristos 	    (n2->tok == MAN_TP || n2->tok == MAN_TQ))
429*544c191cSchristos 		return ' ';
430*544c191cSchristos 	if (n1->tok != MAN_IP || n2->tok != MAN_IP)
431*544c191cSchristos 		return '\0';
432*544c191cSchristos 	n1 = n1->head->child;
433*544c191cSchristos 	n2 = n2->head->child;
434*544c191cSchristos 	s1 = n1 == NULL ? "" : n1->string;
435*544c191cSchristos 	s2 = n2 == NULL ? "" : n2->string;
436*544c191cSchristos 	c1 = strcmp(s1, "*") == 0 ? '*' :
437*544c191cSchristos 	     strcmp(s1, "\\-") == 0 ? '-' :
438*544c191cSchristos 	     strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
439*544c191cSchristos 	c2 = strcmp(s2, "*") == 0 ? '*' :
440*544c191cSchristos 	     strcmp(s2, "\\-") == 0 ? '-' :
441*544c191cSchristos 	     strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
442*544c191cSchristos 	return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
443*544c191cSchristos }
444*544c191cSchristos 
4454154958bSjoerg static int
man_IP_pre(MAN_ARGS)4464154958bSjoerg man_IP_pre(MAN_ARGS)
4474154958bSjoerg {
4489ff1f2acSchristos 	const struct roff_node	*nn;
449*544c191cSchristos 	const char		*list_class;
450*544c191cSchristos 	enum htmltag		 list_elem, body_elem;
451*544c191cSchristos 	char			 list_type;
4524154958bSjoerg 
453*544c191cSchristos 	nn = n->type == ROFFT_BLOCK ? n : n->parent;
454*544c191cSchristos 	if ((list_type = list_continues(nn->prev, nn)) == '\0') {
455*544c191cSchristos 		/* Start a new list. */
456*544c191cSchristos 		if ((list_type = list_continues(nn, nn->next)) == '\0')
457*544c191cSchristos 			list_type = ' ';
458*544c191cSchristos 		switch (list_type) {
459*544c191cSchristos 		case ' ':
460*544c191cSchristos 			list_class = "Bl-tag";
461*544c191cSchristos 			list_elem = TAG_DL;
462*544c191cSchristos 			break;
463*544c191cSchristos 		case '*':
464*544c191cSchristos 			list_class = "Bl-bullet";
465*544c191cSchristos 			list_elem = TAG_UL;
466*544c191cSchristos 			break;
467*544c191cSchristos 		case '-':
468*544c191cSchristos 			list_class = "Bl-dash";
469*544c191cSchristos 			list_elem = TAG_UL;
470*544c191cSchristos 			break;
471*544c191cSchristos 		default:
472*544c191cSchristos 			abort();
473*544c191cSchristos 		}
474*544c191cSchristos 	} else {
475*544c191cSchristos 		/* Continue a list that was started earlier. */
476*544c191cSchristos 		list_class = NULL;
477*544c191cSchristos 		list_elem = TAG_MAX;
478*544c191cSchristos 	}
479*544c191cSchristos 	body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
480*544c191cSchristos 
481*544c191cSchristos 	switch (n->type) {
482*544c191cSchristos 	case ROFFT_BLOCK:
483*544c191cSchristos 		html_close_paragraph(h);
484*544c191cSchristos 		if (list_elem != TAG_MAX)
485*544c191cSchristos 			print_otag(h, list_elem, "c", list_class);
4869ff1f2acSchristos 		return 1;
487*544c191cSchristos 	case ROFFT_HEAD:
488*544c191cSchristos 		if (body_elem == TAG_LI)
489*544c191cSchristos 			return 0;
490*544c191cSchristos 		print_otag(h, TAG_DT, "");
491*544c191cSchristos 		break;
492*544c191cSchristos 	case ROFFT_BODY:
493*544c191cSchristos 		print_otag(h, body_elem, "");
4949ff1f2acSchristos 		return 1;
495*544c191cSchristos 	default:
496*544c191cSchristos 		abort();
4974154958bSjoerg 	}
4984154958bSjoerg 
499*544c191cSchristos 	switch(n->tok) {
500*544c191cSchristos 	case MAN_IP:  /* Only print the first header element. */
501*544c191cSchristos 		if (n->child != NULL)
5029508192eSchristos 			print_man_node(man, n->child, h);
503*544c191cSchristos 		break;
504*544c191cSchristos 	case MAN_TP:  /* Only print next-line header elements. */
505*544c191cSchristos 	case MAN_TQ:
506fec65c98Schristos 		nn = n->child;
507*544c191cSchristos 		while (nn != NULL && (NODE_LINE & nn->flags) == 0)
508fec65c98Schristos 			nn = nn->next;
509*544c191cSchristos 		while (nn != NULL) {
5109508192eSchristos 			print_man_node(man, nn, h);
511fec65c98Schristos 			nn = nn->next;
512fec65c98Schristos 		}
513*544c191cSchristos 		break;
514*544c191cSchristos 	default:
515*544c191cSchristos 		abort();
516fec65c98Schristos 	}
5179ff1f2acSchristos 	return 0;
5184154958bSjoerg }
5194154958bSjoerg 
5204154958bSjoerg static int
man_OP_pre(MAN_ARGS)521cf816816Sjoerg man_OP_pre(MAN_ARGS)
522cf816816Sjoerg {
523cf816816Sjoerg 	struct tag	*tt;
524cf816816Sjoerg 
525cf816816Sjoerg 	print_text(h, "[");
526cf816816Sjoerg 	h->flags |= HTML_NOSPACE;
5279508192eSchristos 	tt = print_otag(h, TAG_SPAN, "c", "Op");
528cf816816Sjoerg 
529*544c191cSchristos 	if ((n = n->child) != NULL) {
5309508192eSchristos 		print_otag(h, TAG_B, "");
531cf816816Sjoerg 		print_text(h, n->string);
532cf816816Sjoerg 	}
533cf816816Sjoerg 
534cf816816Sjoerg 	print_stagq(h, tt);
535cf816816Sjoerg 
536*544c191cSchristos 	if (n != NULL && n->next != NULL) {
5379508192eSchristos 		print_otag(h, TAG_I, "");
538cf816816Sjoerg 		print_text(h, n->next->string);
539cf816816Sjoerg 	}
540cf816816Sjoerg 
541cf816816Sjoerg 	print_stagq(h, tt);
542cf816816Sjoerg 	h->flags |= HTML_NOSPACE;
543cf816816Sjoerg 	print_text(h, "]");
5449ff1f2acSchristos 	return 0;
545cf816816Sjoerg }
546cf816816Sjoerg 
547cf816816Sjoerg static int
man_B_pre(MAN_ARGS)5484154958bSjoerg man_B_pre(MAN_ARGS)
5494154958bSjoerg {
5509508192eSchristos 	print_otag(h, TAG_B, "");
5519ff1f2acSchristos 	return 1;
5524154958bSjoerg }
5534154958bSjoerg 
5544154958bSjoerg static int
man_I_pre(MAN_ARGS)5554154958bSjoerg man_I_pre(MAN_ARGS)
5564154958bSjoerg {
5579508192eSchristos 	print_otag(h, TAG_I, "");
5589ff1f2acSchristos 	return 1;
5594154958bSjoerg }
5604154958bSjoerg 
5614154958bSjoerg static int
man_in_pre(MAN_ARGS)5627da9b934Sjoerg man_in_pre(MAN_ARGS)
5637da9b934Sjoerg {
5649508192eSchristos 	print_otag(h, TAG_BR, "");
5659ff1f2acSchristos 	return 0;
5667da9b934Sjoerg }
5677da9b934Sjoerg 
5687da9b934Sjoerg static int
man_ign_pre(MAN_ARGS)5694154958bSjoerg man_ign_pre(MAN_ARGS)
5704154958bSjoerg {
5719ff1f2acSchristos 	return 0;
5724154958bSjoerg }
5734154958bSjoerg 
5744154958bSjoerg static int
man_RS_pre(MAN_ARGS)5754154958bSjoerg man_RS_pre(MAN_ARGS)
5764154958bSjoerg {
577*544c191cSchristos 	switch (n->type) {
578*544c191cSchristos 	case ROFFT_BLOCK:
579*544c191cSchristos 		html_close_paragraph(h);
580*544c191cSchristos 		break;
581*544c191cSchristos 	case ROFFT_HEAD:
5829ff1f2acSchristos 		return 0;
583*544c191cSchristos 	case ROFFT_BODY:
584c9bcef03Schristos 		print_otag(h, TAG_DIV, "c", "Bd-indent");
585*544c191cSchristos 		break;
586*544c191cSchristos 	default:
587*544c191cSchristos 		abort();
588*544c191cSchristos 	}
589*544c191cSchristos 	return 1;
590*544c191cSchristos }
591*544c191cSchristos 
592*544c191cSchristos static int
man_SY_pre(MAN_ARGS)593*544c191cSchristos man_SY_pre(MAN_ARGS)
594*544c191cSchristos {
595*544c191cSchristos 	switch (n->type) {
596*544c191cSchristos 	case ROFFT_BLOCK:
597*544c191cSchristos 		html_close_paragraph(h);
598*544c191cSchristos 		print_otag(h, TAG_TABLE, "c", "Nm");
599*544c191cSchristos 		print_otag(h, TAG_TR, "");
600*544c191cSchristos 		break;
601*544c191cSchristos 	case ROFFT_HEAD:
602*544c191cSchristos 		print_otag(h, TAG_TD, "");
603*544c191cSchristos 		print_otag(h, TAG_CODE, "c", "Nm");
604*544c191cSchristos 		break;
605*544c191cSchristos 	case ROFFT_BODY:
606*544c191cSchristos 		print_otag(h, TAG_TD, "");
607*544c191cSchristos 		break;
608*544c191cSchristos 	default:
609*544c191cSchristos 		abort();
610*544c191cSchristos 	}
6119ff1f2acSchristos 	return 1;
6124154958bSjoerg }
61370f041f9Sjoerg 
61470f041f9Sjoerg static int
man_UR_pre(MAN_ARGS)61570f041f9Sjoerg man_UR_pre(MAN_ARGS)
61670f041f9Sjoerg {
617c9bcef03Schristos 	char *cp;
618*544c191cSchristos 
61970f041f9Sjoerg 	n = n->child;
6209ff1f2acSchristos 	assert(n->type == ROFFT_HEAD);
6219ff1f2acSchristos 	if (n->child != NULL) {
6229ff1f2acSchristos 		assert(n->child->type == ROFFT_TEXT);
623c9bcef03Schristos 		if (n->tok == MAN_MT) {
624c9bcef03Schristos 			mandoc_asprintf(&cp, "mailto:%s", n->child->string);
625*544c191cSchristos 			print_otag(h, TAG_A, "ch", "Mt", cp);
626c9bcef03Schristos 			free(cp);
627c9bcef03Schristos 		} else
628*544c191cSchristos 			print_otag(h, TAG_A, "ch", "Lk", n->child->string);
62970f041f9Sjoerg 	}
63070f041f9Sjoerg 
6319ff1f2acSchristos 	assert(n->next->type == ROFFT_BODY);
6329ff1f2acSchristos 	if (n->next->child != NULL)
63370f041f9Sjoerg 		n = n->next;
63470f041f9Sjoerg 
6359508192eSchristos 	print_man_nodelist(man, n->child, h);
6369ff1f2acSchristos 	return 0;
63770f041f9Sjoerg }
638*544c191cSchristos 
639*544c191cSchristos static int
man_abort_pre(MAN_ARGS)640*544c191cSchristos man_abort_pre(MAN_ARGS)
641*544c191cSchristos {
642*544c191cSchristos 	abort();
643*544c191cSchristos }
644