1/*	$Id: mdoc_html.c,v 1.182 2011/11/03 20:37:00 schwarze Exp $ */
2/*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#ifdef HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <sys/types.h>
22
23#include <assert.h>
24#include <ctype.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29
30#include "mandoc.h"
31#include "out.h"
32#include "html.h"
33#include "mdoc.h"
34#include "main.h"
35
36#define	INDENT		 5
37
38#define	MDOC_ARGS	  const struct mdoc_meta *m, \
39			  const struct mdoc_node *n, \
40			  struct html *h
41
42#ifndef MIN
43#define	MIN(a,b)	((/*CONSTCOND*/(a)<(b))?(a):(b))
44#endif
45
46struct	htmlmdoc {
47	int		(*pre)(MDOC_ARGS);
48	void		(*post)(MDOC_ARGS);
49};
50
51static	void		  print_mdoc(MDOC_ARGS);
52static	void		  print_mdoc_head(MDOC_ARGS);
53static	void		  print_mdoc_node(MDOC_ARGS);
54static	void		  print_mdoc_nodelist(MDOC_ARGS);
55static	void	  	  synopsis_pre(struct html *,
56				const struct mdoc_node *);
57
58static	void		  a2width(const char *, struct roffsu *);
59static	void		  a2offs(const char *, struct roffsu *);
60
61static	void		  mdoc_root_post(MDOC_ARGS);
62static	int		  mdoc_root_pre(MDOC_ARGS);
63
64static	void		  mdoc__x_post(MDOC_ARGS);
65static	int		  mdoc__x_pre(MDOC_ARGS);
66static	int		  mdoc_ad_pre(MDOC_ARGS);
67static	int		  mdoc_an_pre(MDOC_ARGS);
68static	int		  mdoc_ap_pre(MDOC_ARGS);
69static	int		  mdoc_ar_pre(MDOC_ARGS);
70static	int		  mdoc_bd_pre(MDOC_ARGS);
71static	int		  mdoc_bf_pre(MDOC_ARGS);
72static	void		  mdoc_bk_post(MDOC_ARGS);
73static	int		  mdoc_bk_pre(MDOC_ARGS);
74static	int		  mdoc_bl_pre(MDOC_ARGS);
75static	int		  mdoc_bt_pre(MDOC_ARGS);
76static	int		  mdoc_bx_pre(MDOC_ARGS);
77static	int		  mdoc_cd_pre(MDOC_ARGS);
78static	int		  mdoc_d1_pre(MDOC_ARGS);
79static	int		  mdoc_dv_pre(MDOC_ARGS);
80static	int		  mdoc_fa_pre(MDOC_ARGS);
81static	int		  mdoc_fd_pre(MDOC_ARGS);
82static	int		  mdoc_fl_pre(MDOC_ARGS);
83static	int		  mdoc_fn_pre(MDOC_ARGS);
84static	int		  mdoc_ft_pre(MDOC_ARGS);
85static	int		  mdoc_em_pre(MDOC_ARGS);
86static	int		  mdoc_er_pre(MDOC_ARGS);
87static	int		  mdoc_ev_pre(MDOC_ARGS);
88static	int		  mdoc_ex_pre(MDOC_ARGS);
89static	void		  mdoc_fo_post(MDOC_ARGS);
90static	int		  mdoc_fo_pre(MDOC_ARGS);
91static	int		  mdoc_ic_pre(MDOC_ARGS);
92static	int		  mdoc_igndelim_pre(MDOC_ARGS);
93static	int		  mdoc_in_pre(MDOC_ARGS);
94static	int		  mdoc_it_pre(MDOC_ARGS);
95static	int		  mdoc_lb_pre(MDOC_ARGS);
96static	int		  mdoc_li_pre(MDOC_ARGS);
97static	int		  mdoc_lk_pre(MDOC_ARGS);
98static	int		  mdoc_mt_pre(MDOC_ARGS);
99static	int		  mdoc_ms_pre(MDOC_ARGS);
100static	int		  mdoc_nd_pre(MDOC_ARGS);
101static	int		  mdoc_nm_pre(MDOC_ARGS);
102static	int		  mdoc_ns_pre(MDOC_ARGS);
103static	int		  mdoc_pa_pre(MDOC_ARGS);
104static	void		  mdoc_pf_post(MDOC_ARGS);
105static	int		  mdoc_pp_pre(MDOC_ARGS);
106static	void		  mdoc_quote_post(MDOC_ARGS);
107static	int		  mdoc_quote_pre(MDOC_ARGS);
108static	int		  mdoc_rs_pre(MDOC_ARGS);
109static	int		  mdoc_rv_pre(MDOC_ARGS);
110static	int		  mdoc_sh_pre(MDOC_ARGS);
111static	int		  mdoc_sm_pre(MDOC_ARGS);
112static	int		  mdoc_sp_pre(MDOC_ARGS);
113static	int		  mdoc_ss_pre(MDOC_ARGS);
114static	int		  mdoc_sx_pre(MDOC_ARGS);
115static	int		  mdoc_sy_pre(MDOC_ARGS);
116static	int		  mdoc_ud_pre(MDOC_ARGS);
117static	int		  mdoc_va_pre(MDOC_ARGS);
118static	int		  mdoc_vt_pre(MDOC_ARGS);
119static	int		  mdoc_xr_pre(MDOC_ARGS);
120static	int		  mdoc_xx_pre(MDOC_ARGS);
121
122static	const struct htmlmdoc mdocs[MDOC_MAX] = {
123	{mdoc_ap_pre, NULL}, /* Ap */
124	{NULL, NULL}, /* Dd */
125	{NULL, NULL}, /* Dt */
126	{NULL, NULL}, /* Os */
127	{mdoc_sh_pre, NULL }, /* Sh */
128	{mdoc_ss_pre, NULL }, /* Ss */
129	{mdoc_pp_pre, NULL}, /* Pp */
130	{mdoc_d1_pre, NULL}, /* D1 */
131	{mdoc_d1_pre, NULL}, /* Dl */
132	{mdoc_bd_pre, NULL}, /* Bd */
133	{NULL, NULL}, /* Ed */
134	{mdoc_bl_pre, NULL}, /* Bl */
135	{NULL, NULL}, /* El */
136	{mdoc_it_pre, NULL}, /* It */
137	{mdoc_ad_pre, NULL}, /* Ad */
138	{mdoc_an_pre, NULL}, /* An */
139	{mdoc_ar_pre, NULL}, /* Ar */
140	{mdoc_cd_pre, NULL}, /* Cd */
141	{mdoc_fl_pre, NULL}, /* Cm */
142	{mdoc_dv_pre, NULL}, /* Dv */
143	{mdoc_er_pre, NULL}, /* Er */
144	{mdoc_ev_pre, NULL}, /* Ev */
145	{mdoc_ex_pre, NULL}, /* Ex */
146	{mdoc_fa_pre, NULL}, /* Fa */
147	{mdoc_fd_pre, NULL}, /* Fd */
148	{mdoc_fl_pre, NULL}, /* Fl */
149	{mdoc_fn_pre, NULL}, /* Fn */
150	{mdoc_ft_pre, NULL}, /* Ft */
151	{mdoc_ic_pre, NULL}, /* Ic */
152	{mdoc_in_pre, NULL}, /* In */
153	{mdoc_li_pre, NULL}, /* Li */
154	{mdoc_nd_pre, NULL}, /* Nd */
155	{mdoc_nm_pre, NULL}, /* Nm */
156	{mdoc_quote_pre, mdoc_quote_post}, /* Op */
157	{NULL, NULL}, /* Ot */
158	{mdoc_pa_pre, NULL}, /* Pa */
159	{mdoc_rv_pre, NULL}, /* Rv */
160	{NULL, NULL}, /* St */
161	{mdoc_va_pre, NULL}, /* Va */
162	{mdoc_vt_pre, NULL}, /* Vt */
163	{mdoc_xr_pre, NULL}, /* Xr */
164	{mdoc__x_pre, mdoc__x_post}, /* %A */
165	{mdoc__x_pre, mdoc__x_post}, /* %B */
166	{mdoc__x_pre, mdoc__x_post}, /* %D */
167	{mdoc__x_pre, mdoc__x_post}, /* %I */
168	{mdoc__x_pre, mdoc__x_post}, /* %J */
169	{mdoc__x_pre, mdoc__x_post}, /* %N */
170	{mdoc__x_pre, mdoc__x_post}, /* %O */
171	{mdoc__x_pre, mdoc__x_post}, /* %P */
172	{mdoc__x_pre, mdoc__x_post}, /* %R */
173	{mdoc__x_pre, mdoc__x_post}, /* %T */
174	{mdoc__x_pre, mdoc__x_post}, /* %V */
175	{NULL, NULL}, /* Ac */
176	{mdoc_quote_pre, mdoc_quote_post}, /* Ao */
177	{mdoc_quote_pre, mdoc_quote_post}, /* Aq */
178	{NULL, NULL}, /* At */
179	{NULL, NULL}, /* Bc */
180	{mdoc_bf_pre, NULL}, /* Bf */
181	{mdoc_quote_pre, mdoc_quote_post}, /* Bo */
182	{mdoc_quote_pre, mdoc_quote_post}, /* Bq */
183	{mdoc_xx_pre, NULL}, /* Bsx */
184	{mdoc_bx_pre, NULL}, /* Bx */
185	{NULL, NULL}, /* Db */
186	{NULL, NULL}, /* Dc */
187	{mdoc_quote_pre, mdoc_quote_post}, /* Do */
188	{mdoc_quote_pre, mdoc_quote_post}, /* Dq */
189	{NULL, NULL}, /* Ec */ /* FIXME: no space */
190	{NULL, NULL}, /* Ef */
191	{mdoc_em_pre, NULL}, /* Em */
192	{mdoc_quote_pre, mdoc_quote_post}, /* Eo */
193	{mdoc_xx_pre, NULL}, /* Fx */
194	{mdoc_ms_pre, NULL}, /* Ms */
195	{mdoc_igndelim_pre, NULL}, /* No */
196	{mdoc_ns_pre, NULL}, /* Ns */
197	{mdoc_xx_pre, NULL}, /* Nx */
198	{mdoc_xx_pre, NULL}, /* Ox */
199	{NULL, NULL}, /* Pc */
200	{mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */
201	{mdoc_quote_pre, mdoc_quote_post}, /* Po */
202	{mdoc_quote_pre, mdoc_quote_post}, /* Pq */
203	{NULL, NULL}, /* Qc */
204	{mdoc_quote_pre, mdoc_quote_post}, /* Ql */
205	{mdoc_quote_pre, mdoc_quote_post}, /* Qo */
206	{mdoc_quote_pre, mdoc_quote_post}, /* Qq */
207	{NULL, NULL}, /* Re */
208	{mdoc_rs_pre, NULL}, /* Rs */
209	{NULL, NULL}, /* Sc */
210	{mdoc_quote_pre, mdoc_quote_post}, /* So */
211	{mdoc_quote_pre, mdoc_quote_post}, /* Sq */
212	{mdoc_sm_pre, NULL}, /* Sm */
213	{mdoc_sx_pre, NULL}, /* Sx */
214	{mdoc_sy_pre, NULL}, /* Sy */
215	{NULL, NULL}, /* Tn */
216	{mdoc_xx_pre, NULL}, /* Ux */
217	{NULL, NULL}, /* Xc */
218	{NULL, NULL}, /* Xo */
219	{mdoc_fo_pre, mdoc_fo_post}, /* Fo */
220	{NULL, NULL}, /* Fc */
221	{mdoc_quote_pre, mdoc_quote_post}, /* Oo */
222	{NULL, NULL}, /* Oc */
223	{mdoc_bk_pre, mdoc_bk_post}, /* Bk */
224	{NULL, NULL}, /* Ek */
225	{mdoc_bt_pre, NULL}, /* Bt */
226	{NULL, NULL}, /* Hf */
227	{NULL, NULL}, /* Fr */
228	{mdoc_ud_pre, NULL}, /* Ud */
229	{mdoc_lb_pre, NULL}, /* Lb */
230	{mdoc_pp_pre, NULL}, /* Lp */
231	{mdoc_lk_pre, NULL}, /* Lk */
232	{mdoc_mt_pre, NULL}, /* Mt */
233	{mdoc_quote_pre, mdoc_quote_post}, /* Brq */
234	{mdoc_quote_pre, mdoc_quote_post}, /* Bro */
235	{NULL, NULL}, /* Brc */
236	{mdoc__x_pre, mdoc__x_post}, /* %C */
237	{NULL, NULL}, /* Es */  /* TODO */
238	{NULL, NULL}, /* En */  /* TODO */
239	{mdoc_xx_pre, NULL}, /* Dx */
240	{mdoc__x_pre, mdoc__x_post}, /* %Q */
241	{mdoc_sp_pre, NULL}, /* br */
242	{mdoc_sp_pre, NULL}, /* sp */
243	{mdoc__x_pre, mdoc__x_post}, /* %U */
244	{NULL, NULL}, /* Ta */
245};
246
247static	const char * const lists[LIST_MAX] = {
248	NULL,
249	"list-bul",
250	"list-col",
251	"list-dash",
252	"list-diag",
253	"list-enum",
254	"list-hang",
255	"list-hyph",
256	"list-inset",
257	"list-item",
258	"list-ohang",
259	"list-tag"
260};
261
262void
263html_mdoc(void *arg, const struct mdoc *m)
264{
265
266	print_mdoc(mdoc_meta(m), mdoc_node(m), (struct html *)arg);
267	putchar('\n');
268}
269
270
271/*
272 * Calculate the scaling unit passed in a `-width' argument.  This uses
273 * either a native scaling unit (e.g., 1i, 2m) or the string length of
274 * the value.
275 */
276static void
277a2width(const char *p, struct roffsu *su)
278{
279
280	if ( ! a2roffsu(p, su, SCALE_MAX)) {
281		su->unit = SCALE_BU;
282		su->scale = html_strlen(p);
283	}
284}
285
286
287/*
288 * See the same function in mdoc_term.c for documentation.
289 */
290static void
291synopsis_pre(struct html *h, const struct mdoc_node *n)
292{
293
294	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
295		return;
296
297	if (n->prev->tok == n->tok &&
298			MDOC_Fo != n->tok &&
299			MDOC_Ft != n->tok &&
300			MDOC_Fn != n->tok) {
301		print_otag(h, TAG_BR, 0, NULL);
302		return;
303	}
304
305	switch (n->prev->tok) {
306	case (MDOC_Fd):
307		/* FALLTHROUGH */
308	case (MDOC_Fn):
309		/* FALLTHROUGH */
310	case (MDOC_Fo):
311		/* FALLTHROUGH */
312	case (MDOC_In):
313		/* FALLTHROUGH */
314	case (MDOC_Vt):
315		print_otag(h, TAG_P, 0, NULL);
316		break;
317	case (MDOC_Ft):
318		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
319			print_otag(h, TAG_P, 0, NULL);
320			break;
321		}
322		/* FALLTHROUGH */
323	default:
324		print_otag(h, TAG_BR, 0, NULL);
325		break;
326	}
327}
328
329
330/*
331 * Calculate the scaling unit passed in an `-offset' argument.  This
332 * uses either a native scaling unit (e.g., 1i, 2m), one of a set of
333 * predefined strings (indent, etc.), or the string length of the value.
334 */
335static void
336a2offs(const char *p, struct roffsu *su)
337{
338
339	/* FIXME: "right"? */
340
341	if (0 == strcmp(p, "left"))
342		SCALE_HS_INIT(su, 0);
343	else if (0 == strcmp(p, "indent"))
344		SCALE_HS_INIT(su, INDENT);
345	else if (0 == strcmp(p, "indent-two"))
346		SCALE_HS_INIT(su, INDENT * 2);
347	else if ( ! a2roffsu(p, su, SCALE_MAX))
348		SCALE_HS_INIT(su, html_strlen(p));
349}
350
351
352static void
353print_mdoc(MDOC_ARGS)
354{
355	struct tag	*t, *tt;
356	struct htmlpair	 tag;
357
358	PAIR_CLASS_INIT(&tag, "mandoc");
359
360	if ( ! (HTML_FRAGMENT & h->oflags)) {
361		print_gen_decls(h);
362		t = print_otag(h, TAG_HTML, 0, NULL);
363		tt = print_otag(h, TAG_HEAD, 0, NULL);
364		print_mdoc_head(m, n, h);
365		print_tagq(h, tt);
366		print_otag(h, TAG_BODY, 0, NULL);
367		print_otag(h, TAG_DIV, 1, &tag);
368	} else
369		t = print_otag(h, TAG_DIV, 1, &tag);
370
371	print_mdoc_nodelist(m, n, h);
372	print_tagq(h, t);
373}
374
375
376/* ARGSUSED */
377static void
378print_mdoc_head(MDOC_ARGS)
379{
380
381	print_gen_head(h);
382	bufinit(h);
383	bufcat_fmt(h, "%s(%s)", m->title, m->msec);
384
385	if (m->arch)
386		bufcat_fmt(h, " (%s)", m->arch);
387
388	print_otag(h, TAG_TITLE, 0, NULL);
389	print_text(h, h->buf);
390}
391
392
393static void
394print_mdoc_nodelist(MDOC_ARGS)
395{
396
397	print_mdoc_node(m, n, h);
398	if (n->next)
399		print_mdoc_nodelist(m, n->next, h);
400}
401
402
403static void
404print_mdoc_node(MDOC_ARGS)
405{
406	int		 child;
407	struct tag	*t;
408
409	child = 1;
410	t = h->tags.head;
411
412	switch (n->type) {
413	case (MDOC_ROOT):
414		child = mdoc_root_pre(m, n, h);
415		break;
416	case (MDOC_TEXT):
417		/* No tables in this mode... */
418		assert(NULL == h->tblt);
419
420		/*
421		 * Make sure that if we're in a literal mode already
422		 * (i.e., within a <PRE>) don't print the newline.
423		 */
424		if (' ' == *n->string && MDOC_LINE & n->flags)
425			if ( ! (HTML_LITERAL & h->flags))
426				print_otag(h, TAG_BR, 0, NULL);
427		if (MDOC_DELIMC & n->flags)
428			h->flags |= HTML_NOSPACE;
429		print_text(h, n->string);
430		if (MDOC_DELIMO & n->flags)
431			h->flags |= HTML_NOSPACE;
432		return;
433	case (MDOC_EQN):
434		print_eqn(h, n->eqn);
435		break;
436	case (MDOC_TBL):
437		/*
438		 * This will take care of initialising all of the table
439		 * state data for the first table, then tearing it down
440		 * for the last one.
441		 */
442		print_tbl(h, n->span);
443		return;
444	default:
445		/*
446		 * Close out the current table, if it's open, and unset
447		 * the "meta" table state.  This will be reopened on the
448		 * next table element.
449		 */
450		if (h->tblt) {
451			print_tblclose(h);
452			t = h->tags.head;
453		}
454
455		assert(NULL == h->tblt);
456		if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
457			child = (*mdocs[n->tok].pre)(m, n, h);
458		break;
459	}
460
461	if (HTML_KEEP & h->flags) {
462		if (n->prev && n->prev->line != n->line) {
463			h->flags &= ~HTML_KEEP;
464			h->flags |= HTML_PREKEEP;
465		} else if (NULL == n->prev) {
466			if (n->parent && n->parent->line != n->line) {
467				h->flags &= ~HTML_KEEP;
468				h->flags |= HTML_PREKEEP;
469			}
470		}
471	}
472
473	if (child && n->child)
474		print_mdoc_nodelist(m, n->child, h);
475
476	print_stagq(h, t);
477
478	switch (n->type) {
479	case (MDOC_ROOT):
480		mdoc_root_post(m, n, h);
481		break;
482	case (MDOC_EQN):
483		break;
484	default:
485		if (mdocs[n->tok].post && ENDBODY_NOT == n->end)
486			(*mdocs[n->tok].post)(m, n, h);
487		break;
488	}
489}
490
491/* ARGSUSED */
492static void
493mdoc_root_post(MDOC_ARGS)
494{
495	struct htmlpair	 tag[3];
496	struct tag	*t, *tt;
497
498	PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
499	PAIR_CLASS_INIT(&tag[1], "foot");
500	PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
501	t = print_otag(h, TAG_TABLE, 3, tag);
502	PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
503	print_otag(h, TAG_COL, 1, tag);
504	print_otag(h, TAG_COL, 1, tag);
505
506	print_otag(h, TAG_TBODY, 0, NULL);
507
508	tt = print_otag(h, TAG_TR, 0, NULL);
509
510	PAIR_CLASS_INIT(&tag[0], "foot-date");
511	print_otag(h, TAG_TD, 1, tag);
512	print_text(h, m->date);
513	print_stagq(h, tt);
514
515	PAIR_CLASS_INIT(&tag[0], "foot-os");
516	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
517	print_otag(h, TAG_TD, 2, tag);
518	print_text(h, m->os);
519	print_tagq(h, t);
520}
521
522
523/* ARGSUSED */
524static int
525mdoc_root_pre(MDOC_ARGS)
526{
527	struct htmlpair	 tag[3];
528	struct tag	*t, *tt;
529	char		 b[BUFSIZ], title[BUFSIZ];
530
531	strlcpy(b, m->vol, BUFSIZ);
532
533	if (m->arch) {
534		strlcat(b, " (", BUFSIZ);
535		strlcat(b, m->arch, BUFSIZ);
536		strlcat(b, ")", BUFSIZ);
537	}
538
539	snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
540
541	PAIR_SUMMARY_INIT(&tag[0], "Document Header");
542	PAIR_CLASS_INIT(&tag[1], "head");
543	PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
544	t = print_otag(h, TAG_TABLE, 3, tag);
545	PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
546	print_otag(h, TAG_COL, 1, tag);
547	print_otag(h, TAG_COL, 1, tag);
548	print_otag(h, TAG_COL, 1, tag);
549
550	print_otag(h, TAG_TBODY, 0, NULL);
551
552	tt = print_otag(h, TAG_TR, 0, NULL);
553
554	PAIR_CLASS_INIT(&tag[0], "head-ltitle");
555	print_otag(h, TAG_TD, 1, tag);
556	print_text(h, title);
557	print_stagq(h, tt);
558
559	PAIR_CLASS_INIT(&tag[0], "head-vol");
560	PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
561	print_otag(h, TAG_TD, 2, tag);
562	print_text(h, b);
563	print_stagq(h, tt);
564
565	PAIR_CLASS_INIT(&tag[0], "head-rtitle");
566	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
567	print_otag(h, TAG_TD, 2, tag);
568	print_text(h, title);
569	print_tagq(h, t);
570	return(1);
571}
572
573
574/* ARGSUSED */
575static int
576mdoc_sh_pre(MDOC_ARGS)
577{
578	struct htmlpair	 tag;
579
580	if (MDOC_BLOCK == n->type) {
581		PAIR_CLASS_INIT(&tag, "section");
582		print_otag(h, TAG_DIV, 1, &tag);
583		return(1);
584	} else if (MDOC_BODY == n->type)
585		return(1);
586
587	bufinit(h);
588	bufcat(h, "x");
589
590	for (n = n->child; n && MDOC_TEXT == n->type; ) {
591		bufcat_id(h, n->string);
592		if (NULL != (n = n->next))
593			bufcat_id(h, " ");
594	}
595
596	if (NULL == n) {
597		PAIR_ID_INIT(&tag, h->buf);
598		print_otag(h, TAG_H1, 1, &tag);
599	} else
600		print_otag(h, TAG_H1, 0, NULL);
601
602	return(1);
603}
604
605/* ARGSUSED */
606static int
607mdoc_ss_pre(MDOC_ARGS)
608{
609	struct htmlpair	 tag;
610
611	if (MDOC_BLOCK == n->type) {
612		PAIR_CLASS_INIT(&tag, "subsection");
613		print_otag(h, TAG_DIV, 1, &tag);
614		return(1);
615	} else if (MDOC_BODY == n->type)
616		return(1);
617
618	bufinit(h);
619	bufcat(h, "x");
620
621	for (n = n->child; n && MDOC_TEXT == n->type; ) {
622		bufcat_id(h, n->string);
623		if (NULL != (n = n->next))
624			bufcat_id(h, " ");
625	}
626
627	if (NULL == n) {
628		PAIR_ID_INIT(&tag, h->buf);
629		print_otag(h, TAG_H2, 1, &tag);
630	} else
631		print_otag(h, TAG_H2, 0, NULL);
632
633	return(1);
634}
635
636
637/* ARGSUSED */
638static int
639mdoc_fl_pre(MDOC_ARGS)
640{
641	struct htmlpair	 tag;
642
643	PAIR_CLASS_INIT(&tag, "flag");
644	print_otag(h, TAG_B, 1, &tag);
645
646	/* `Cm' has no leading hyphen. */
647
648	if (MDOC_Cm == n->tok)
649		return(1);
650
651	print_text(h, "\\-");
652
653	if (n->child)
654		h->flags |= HTML_NOSPACE;
655	else if (n->next && n->next->line == n->line)
656		h->flags |= HTML_NOSPACE;
657
658	return(1);
659}
660
661
662/* ARGSUSED */
663static int
664mdoc_nd_pre(MDOC_ARGS)
665{
666	struct htmlpair	 tag;
667
668	if (MDOC_BODY != n->type)
669		return(1);
670
671	/* XXX: this tag in theory can contain block elements. */
672
673	print_text(h, "\\(em");
674	PAIR_CLASS_INIT(&tag, "desc");
675	print_otag(h, TAG_SPAN, 1, &tag);
676	return(1);
677}
678
679
680static int
681mdoc_nm_pre(MDOC_ARGS)
682{
683	struct htmlpair	 tag;
684	struct roffsu	 su;
685	int		 len;
686
687	switch (n->type) {
688	case (MDOC_ELEM):
689		synopsis_pre(h, n);
690		PAIR_CLASS_INIT(&tag, "name");
691		print_otag(h, TAG_B, 1, &tag);
692		if (NULL == n->child && m->name)
693			print_text(h, m->name);
694		return(1);
695	case (MDOC_HEAD):
696		print_otag(h, TAG_TD, 0, NULL);
697		if (NULL == n->child && m->name)
698			print_text(h, m->name);
699		return(1);
700	case (MDOC_BODY):
701		print_otag(h, TAG_TD, 0, NULL);
702		return(1);
703	default:
704		break;
705	}
706
707	synopsis_pre(h, n);
708	PAIR_CLASS_INIT(&tag, "synopsis");
709	print_otag(h, TAG_TABLE, 1, &tag);
710
711	for (len = 0, n = n->child; n; n = n->next)
712		if (MDOC_TEXT == n->type)
713			len += html_strlen(n->string);
714
715	if (0 == len && m->name)
716		len = html_strlen(m->name);
717
718	SCALE_HS_INIT(&su, (double)len);
719	bufinit(h);
720	bufcat_su(h, "width", &su);
721	PAIR_STYLE_INIT(&tag, h);
722	print_otag(h, TAG_COL, 1, &tag);
723	print_otag(h, TAG_COL, 0, NULL);
724	print_otag(h, TAG_TBODY, 0, NULL);
725	print_otag(h, TAG_TR, 0, NULL);
726	return(1);
727}
728
729
730/* ARGSUSED */
731static int
732mdoc_xr_pre(MDOC_ARGS)
733{
734	struct htmlpair	 tag[2];
735
736	if (NULL == n->child)
737		return(0);
738
739	PAIR_CLASS_INIT(&tag[0], "link-man");
740
741	if (h->base_man) {
742		buffmt_man(h, n->child->string,
743				n->child->next ?
744				n->child->next->string : NULL);
745		PAIR_HREF_INIT(&tag[1], h->buf);
746		print_otag(h, TAG_A, 2, tag);
747	} else
748		print_otag(h, TAG_A, 1, tag);
749
750	n = n->child;
751	print_text(h, n->string);
752
753	if (NULL == (n = n->next))
754		return(0);
755
756	h->flags |= HTML_NOSPACE;
757	print_text(h, "(");
758	h->flags |= HTML_NOSPACE;
759	print_text(h, n->string);
760	h->flags |= HTML_NOSPACE;
761	print_text(h, ")");
762	return(0);
763}
764
765
766/* ARGSUSED */
767static int
768mdoc_ns_pre(MDOC_ARGS)
769{
770
771	if ( ! (MDOC_LINE & n->flags))
772		h->flags |= HTML_NOSPACE;
773	return(1);
774}
775
776
777/* ARGSUSED */
778static int
779mdoc_ar_pre(MDOC_ARGS)
780{
781	struct htmlpair tag;
782
783	PAIR_CLASS_INIT(&tag, "arg");
784	print_otag(h, TAG_I, 1, &tag);
785	return(1);
786}
787
788
789/* ARGSUSED */
790static int
791mdoc_xx_pre(MDOC_ARGS)
792{
793	const char	*pp;
794	struct htmlpair	 tag;
795	int		 flags;
796
797	switch (n->tok) {
798	case (MDOC_Bsx):
799		pp = "BSD/OS";
800		break;
801	case (MDOC_Dx):
802		pp = "DragonFly";
803		break;
804	case (MDOC_Fx):
805		pp = "FreeBSD";
806		break;
807	case (MDOC_Nx):
808		pp = "NetBSD";
809		break;
810	case (MDOC_Ox):
811		pp = "OpenBSD";
812		break;
813	case (MDOC_Ux):
814		pp = "UNIX";
815		break;
816	default:
817		return(1);
818	}
819
820	PAIR_CLASS_INIT(&tag, "unix");
821	print_otag(h, TAG_SPAN, 1, &tag);
822
823	print_text(h, pp);
824	if (n->child) {
825		flags = h->flags;
826		h->flags |= HTML_KEEP;
827		print_text(h, n->child->string);
828		h->flags = flags;
829	}
830	return(0);
831}
832
833
834/* ARGSUSED */
835static int
836mdoc_bx_pre(MDOC_ARGS)
837{
838	struct htmlpair	 tag;
839
840	PAIR_CLASS_INIT(&tag, "unix");
841	print_otag(h, TAG_SPAN, 1, &tag);
842
843	if (NULL != (n = n->child)) {
844		print_text(h, n->string);
845		h->flags |= HTML_NOSPACE;
846		print_text(h, "BSD");
847	} else {
848		print_text(h, "BSD");
849		return(0);
850	}
851
852	if (NULL != (n = n->next)) {
853		h->flags |= HTML_NOSPACE;
854		print_text(h, "-");
855		h->flags |= HTML_NOSPACE;
856		print_text(h, n->string);
857	}
858
859	return(0);
860}
861
862/* ARGSUSED */
863static int
864mdoc_it_pre(MDOC_ARGS)
865{
866	struct roffsu	 su;
867	enum mdoc_list	 type;
868	struct htmlpair	 tag[2];
869	const struct mdoc_node *bl;
870
871	bl = n->parent;
872	while (bl && MDOC_Bl != bl->tok)
873		bl = bl->parent;
874
875	assert(bl);
876
877	type = bl->norm->Bl.type;
878
879	assert(lists[type]);
880	PAIR_CLASS_INIT(&tag[0], lists[type]);
881
882	bufinit(h);
883
884	if (MDOC_HEAD == n->type) {
885		switch (type) {
886		case(LIST_bullet):
887			/* FALLTHROUGH */
888		case(LIST_dash):
889			/* FALLTHROUGH */
890		case(LIST_item):
891			/* FALLTHROUGH */
892		case(LIST_hyphen):
893			/* FALLTHROUGH */
894		case(LIST_enum):
895			return(0);
896		case(LIST_diag):
897			/* FALLTHROUGH */
898		case(LIST_hang):
899			/* FALLTHROUGH */
900		case(LIST_inset):
901			/* FALLTHROUGH */
902		case(LIST_ohang):
903			/* FALLTHROUGH */
904		case(LIST_tag):
905			SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
906			bufcat_su(h, "margin-top", &su);
907			PAIR_STYLE_INIT(&tag[1], h);
908			print_otag(h, TAG_DT, 2, tag);
909			if (LIST_diag != type)
910				break;
911			PAIR_CLASS_INIT(&tag[0], "diag");
912			print_otag(h, TAG_B, 1, tag);
913			break;
914		case(LIST_column):
915			break;
916		default:
917			break;
918		}
919	} else if (MDOC_BODY == n->type) {
920		switch (type) {
921		case(LIST_bullet):
922			/* FALLTHROUGH */
923		case(LIST_hyphen):
924			/* FALLTHROUGH */
925		case(LIST_dash):
926			/* FALLTHROUGH */
927		case(LIST_enum):
928			/* FALLTHROUGH */
929		case(LIST_item):
930			SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
931			bufcat_su(h, "margin-top", &su);
932			PAIR_STYLE_INIT(&tag[1], h);
933			print_otag(h, TAG_LI, 2, tag);
934			break;
935		case(LIST_diag):
936			/* FALLTHROUGH */
937		case(LIST_hang):
938			/* FALLTHROUGH */
939		case(LIST_inset):
940			/* FALLTHROUGH */
941		case(LIST_ohang):
942			/* FALLTHROUGH */
943		case(LIST_tag):
944			if (NULL == bl->norm->Bl.width) {
945				print_otag(h, TAG_DD, 1, tag);
946				break;
947			}
948			a2width(bl->norm->Bl.width, &su);
949			bufcat_su(h, "margin-left", &su);
950			PAIR_STYLE_INIT(&tag[1], h);
951			print_otag(h, TAG_DD, 2, tag);
952			break;
953		case(LIST_column):
954			SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
955			bufcat_su(h, "margin-top", &su);
956			PAIR_STYLE_INIT(&tag[1], h);
957			print_otag(h, TAG_TD, 2, tag);
958			break;
959		default:
960			break;
961		}
962	} else {
963		switch (type) {
964		case (LIST_column):
965			print_otag(h, TAG_TR, 1, tag);
966			break;
967		default:
968			break;
969		}
970	}
971
972	return(1);
973}
974
975/* ARGSUSED */
976static int
977mdoc_bl_pre(MDOC_ARGS)
978{
979	int		 i;
980	struct htmlpair	 tag[3];
981	struct roffsu	 su;
982	char		 buf[BUFSIZ];
983
984	bufinit(h);
985
986	if (MDOC_BODY == n->type) {
987		if (LIST_column == n->norm->Bl.type)
988			print_otag(h, TAG_TBODY, 0, NULL);
989		return(1);
990	}
991
992	if (MDOC_HEAD == n->type) {
993		if (LIST_column != n->norm->Bl.type)
994			return(0);
995
996		/*
997		 * For each column, print out the <COL> tag with our
998		 * suggested width.  The last column gets min-width, as
999		 * in terminal mode it auto-sizes to the width of the
1000		 * screen and we want to preserve that behaviour.
1001		 */
1002
1003		for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
1004			a2width(n->norm->Bl.cols[i], &su);
1005			if (i < (int)n->norm->Bl.ncols - 1)
1006				bufcat_su(h, "width", &su);
1007			else
1008				bufcat_su(h, "min-width", &su);
1009			PAIR_STYLE_INIT(&tag[0], h);
1010			print_otag(h, TAG_COL, 1, tag);
1011		}
1012
1013		return(0);
1014	}
1015
1016	SCALE_VS_INIT(&su, 0);
1017	bufcat_su(h, "margin-top", &su);
1018	bufcat_su(h, "margin-bottom", &su);
1019	PAIR_STYLE_INIT(&tag[0], h);
1020
1021	assert(lists[n->norm->Bl.type]);
1022	strlcpy(buf, "list ", BUFSIZ);
1023	strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
1024	PAIR_INIT(&tag[1], ATTR_CLASS, buf);
1025
1026	/* Set the block's left-hand margin. */
1027
1028	if (n->norm->Bl.offs) {
1029		a2offs(n->norm->Bl.offs, &su);
1030		bufcat_su(h, "margin-left", &su);
1031	}
1032
1033	switch (n->norm->Bl.type) {
1034	case(LIST_bullet):
1035		/* FALLTHROUGH */
1036	case(LIST_dash):
1037		/* FALLTHROUGH */
1038	case(LIST_hyphen):
1039		/* FALLTHROUGH */
1040	case(LIST_item):
1041		print_otag(h, TAG_UL, 2, tag);
1042		break;
1043	case(LIST_enum):
1044		print_otag(h, TAG_OL, 2, tag);
1045		break;
1046	case(LIST_diag):
1047		/* FALLTHROUGH */
1048	case(LIST_hang):
1049		/* FALLTHROUGH */
1050	case(LIST_inset):
1051		/* FALLTHROUGH */
1052	case(LIST_ohang):
1053		/* FALLTHROUGH */
1054	case(LIST_tag):
1055		print_otag(h, TAG_DL, 2, tag);
1056		break;
1057	case(LIST_column):
1058		print_otag(h, TAG_TABLE, 2, tag);
1059		break;
1060	default:
1061		abort();
1062		/* NOTREACHED */
1063	}
1064
1065	return(1);
1066}
1067
1068/* ARGSUSED */
1069static int
1070mdoc_ex_pre(MDOC_ARGS)
1071{
1072	struct tag	*t;
1073	struct htmlpair	 tag;
1074	int		 nchild;
1075
1076	if (n->prev)
1077		print_otag(h, TAG_BR, 0, NULL);
1078
1079	PAIR_CLASS_INIT(&tag, "utility");
1080
1081	print_text(h, "The");
1082
1083	nchild = n->nchild;
1084	for (n = n->child; n; n = n->next) {
1085		assert(MDOC_TEXT == n->type);
1086
1087		t = print_otag(h, TAG_B, 1, &tag);
1088		print_text(h, n->string);
1089		print_tagq(h, t);
1090
1091		if (nchild > 2 && n->next) {
1092			h->flags |= HTML_NOSPACE;
1093			print_text(h, ",");
1094		}
1095
1096		if (n->next && NULL == n->next->next)
1097			print_text(h, "and");
1098	}
1099
1100	if (nchild > 1)
1101		print_text(h, "utilities exit");
1102	else
1103		print_text(h, "utility exits");
1104
1105       	print_text(h, "0 on success, and >0 if an error occurs.");
1106	return(0);
1107}
1108
1109
1110/* ARGSUSED */
1111static int
1112mdoc_em_pre(MDOC_ARGS)
1113{
1114	struct htmlpair	tag;
1115
1116	PAIR_CLASS_INIT(&tag, "emph");
1117	print_otag(h, TAG_SPAN, 1, &tag);
1118	return(1);
1119}
1120
1121
1122/* ARGSUSED */
1123static int
1124mdoc_d1_pre(MDOC_ARGS)
1125{
1126	struct htmlpair	 tag[2];
1127	struct roffsu	 su;
1128
1129	if (MDOC_BLOCK != n->type)
1130		return(1);
1131
1132	SCALE_VS_INIT(&su, 0);
1133	bufinit(h);
1134	bufcat_su(h, "margin-top", &su);
1135	bufcat_su(h, "margin-bottom", &su);
1136	PAIR_STYLE_INIT(&tag[0], h);
1137	print_otag(h, TAG_BLOCKQUOTE, 1, tag);
1138
1139	/* BLOCKQUOTE needs a block body. */
1140
1141	PAIR_CLASS_INIT(&tag[0], "display");
1142	print_otag(h, TAG_DIV, 1, tag);
1143
1144	if (MDOC_Dl == n->tok) {
1145		PAIR_CLASS_INIT(&tag[0], "lit");
1146		print_otag(h, TAG_CODE, 1, tag);
1147	}
1148
1149	return(1);
1150}
1151
1152
1153/* ARGSUSED */
1154static int
1155mdoc_sx_pre(MDOC_ARGS)
1156{
1157	struct htmlpair	 tag[2];
1158
1159	bufinit(h);
1160	bufcat(h, "#x");
1161
1162	for (n = n->child; n; ) {
1163		bufcat_id(h, n->string);
1164		if (NULL != (n = n->next))
1165			bufcat_id(h, " ");
1166	}
1167
1168	PAIR_CLASS_INIT(&tag[0], "link-sec");
1169	PAIR_HREF_INIT(&tag[1], h->buf);
1170
1171	print_otag(h, TAG_I, 1, tag);
1172	print_otag(h, TAG_A, 2, tag);
1173	return(1);
1174}
1175
1176
1177/* ARGSUSED */
1178static int
1179mdoc_bd_pre(MDOC_ARGS)
1180{
1181	struct htmlpair	 	 tag[2];
1182	int		 	 comp, sv;
1183	const struct mdoc_node	*nn;
1184	struct roffsu		 su;
1185
1186	if (MDOC_HEAD == n->type)
1187		return(0);
1188
1189	if (MDOC_BLOCK == n->type) {
1190		comp = n->norm->Bd.comp;
1191		for (nn = n; nn && ! comp; nn = nn->parent) {
1192			if (MDOC_BLOCK != nn->type)
1193				continue;
1194			if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
1195				comp = 1;
1196			if (nn->prev)
1197				break;
1198		}
1199		if ( ! comp)
1200			print_otag(h, TAG_P, 0, NULL);
1201		return(1);
1202	}
1203
1204	SCALE_HS_INIT(&su, 0);
1205	if (n->norm->Bd.offs)
1206		a2offs(n->norm->Bd.offs, &su);
1207
1208	bufinit(h);
1209	bufcat_su(h, "margin-left", &su);
1210	PAIR_STYLE_INIT(&tag[0], h);
1211
1212	if (DISP_unfilled != n->norm->Bd.type &&
1213			DISP_literal != n->norm->Bd.type) {
1214		PAIR_CLASS_INIT(&tag[1], "display");
1215		print_otag(h, TAG_DIV, 2, tag);
1216		return(1);
1217	}
1218
1219	PAIR_CLASS_INIT(&tag[1], "lit display");
1220	print_otag(h, TAG_PRE, 2, tag);
1221
1222	/* This can be recursive: save & set our literal state. */
1223
1224	sv = h->flags & HTML_LITERAL;
1225	h->flags |= HTML_LITERAL;
1226
1227	for (nn = n->child; nn; nn = nn->next) {
1228		print_mdoc_node(m, nn, h);
1229		/*
1230		 * If the printed node flushes its own line, then we
1231		 * needn't do it here as well.  This is hacky, but the
1232		 * notion of selective eoln whitespace is pretty dumb
1233		 * anyway, so don't sweat it.
1234		 */
1235		switch (nn->tok) {
1236		case (MDOC_Sm):
1237			/* FALLTHROUGH */
1238		case (MDOC_br):
1239			/* FALLTHROUGH */
1240		case (MDOC_sp):
1241			/* FALLTHROUGH */
1242		case (MDOC_Bl):
1243			/* FALLTHROUGH */
1244		case (MDOC_D1):
1245			/* FALLTHROUGH */
1246		case (MDOC_Dl):
1247			/* FALLTHROUGH */
1248		case (MDOC_Lp):
1249			/* FALLTHROUGH */
1250		case (MDOC_Pp):
1251			continue;
1252		default:
1253			break;
1254		}
1255		if (nn->next && nn->next->line == nn->line)
1256			continue;
1257		else if (nn->next)
1258			print_text(h, "\n");
1259
1260		h->flags |= HTML_NOSPACE;
1261	}
1262
1263	if (0 == sv)
1264		h->flags &= ~HTML_LITERAL;
1265
1266	return(0);
1267}
1268
1269
1270/* ARGSUSED */
1271static int
1272mdoc_pa_pre(MDOC_ARGS)
1273{
1274	struct htmlpair	tag;
1275
1276	PAIR_CLASS_INIT(&tag, "file");
1277	print_otag(h, TAG_I, 1, &tag);
1278	return(1);
1279}
1280
1281
1282/* ARGSUSED */
1283static int
1284mdoc_ad_pre(MDOC_ARGS)
1285{
1286	struct htmlpair	tag;
1287
1288	PAIR_CLASS_INIT(&tag, "addr");
1289	print_otag(h, TAG_I, 1, &tag);
1290	return(1);
1291}
1292
1293
1294/* ARGSUSED */
1295static int
1296mdoc_an_pre(MDOC_ARGS)
1297{
1298	struct htmlpair	tag;
1299
1300	/* TODO: -split and -nosplit (see termp_an_pre()). */
1301
1302	PAIR_CLASS_INIT(&tag, "author");
1303	print_otag(h, TAG_SPAN, 1, &tag);
1304	return(1);
1305}
1306
1307
1308/* ARGSUSED */
1309static int
1310mdoc_cd_pre(MDOC_ARGS)
1311{
1312	struct htmlpair	tag;
1313
1314	synopsis_pre(h, n);
1315	PAIR_CLASS_INIT(&tag, "config");
1316	print_otag(h, TAG_B, 1, &tag);
1317	return(1);
1318}
1319
1320
1321/* ARGSUSED */
1322static int
1323mdoc_dv_pre(MDOC_ARGS)
1324{
1325	struct htmlpair	tag;
1326
1327	PAIR_CLASS_INIT(&tag, "define");
1328	print_otag(h, TAG_SPAN, 1, &tag);
1329	return(1);
1330}
1331
1332
1333/* ARGSUSED */
1334static int
1335mdoc_ev_pre(MDOC_ARGS)
1336{
1337	struct htmlpair	tag;
1338
1339	PAIR_CLASS_INIT(&tag, "env");
1340	print_otag(h, TAG_SPAN, 1, &tag);
1341	return(1);
1342}
1343
1344
1345/* ARGSUSED */
1346static int
1347mdoc_er_pre(MDOC_ARGS)
1348{
1349	struct htmlpair	tag;
1350
1351	PAIR_CLASS_INIT(&tag, "errno");
1352	print_otag(h, TAG_SPAN, 1, &tag);
1353	return(1);
1354}
1355
1356
1357/* ARGSUSED */
1358static int
1359mdoc_fa_pre(MDOC_ARGS)
1360{
1361	const struct mdoc_node	*nn;
1362	struct htmlpair		 tag;
1363	struct tag		*t;
1364
1365	PAIR_CLASS_INIT(&tag, "farg");
1366	if (n->parent->tok != MDOC_Fo) {
1367		print_otag(h, TAG_I, 1, &tag);
1368		return(1);
1369	}
1370
1371	for (nn = n->child; nn; nn = nn->next) {
1372		t = print_otag(h, TAG_I, 1, &tag);
1373		print_text(h, nn->string);
1374		print_tagq(h, t);
1375		if (nn->next) {
1376			h->flags |= HTML_NOSPACE;
1377			print_text(h, ",");
1378		}
1379	}
1380
1381	if (n->child && n->next && n->next->tok == MDOC_Fa) {
1382		h->flags |= HTML_NOSPACE;
1383		print_text(h, ",");
1384	}
1385
1386	return(0);
1387}
1388
1389
1390/* ARGSUSED */
1391static int
1392mdoc_fd_pre(MDOC_ARGS)
1393{
1394	struct htmlpair	 tag[2];
1395	char		 buf[BUFSIZ];
1396	size_t		 sz;
1397	int		 i;
1398	struct tag	*t;
1399
1400	synopsis_pre(h, n);
1401
1402	if (NULL == (n = n->child))
1403		return(0);
1404
1405	assert(MDOC_TEXT == n->type);
1406
1407	if (strcmp(n->string, "#include")) {
1408		PAIR_CLASS_INIT(&tag[0], "macro");
1409		print_otag(h, TAG_B, 1, tag);
1410		return(1);
1411	}
1412
1413	PAIR_CLASS_INIT(&tag[0], "includes");
1414	print_otag(h, TAG_B, 1, tag);
1415	print_text(h, n->string);
1416
1417	if (NULL != (n = n->next)) {
1418		assert(MDOC_TEXT == n->type);
1419		strlcpy(buf, '<' == *n->string || '"' == *n->string ?
1420				n->string + 1 : n->string, BUFSIZ);
1421
1422		sz = strlen(buf);
1423		if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1]))
1424			buf[sz - 1] = '\0';
1425
1426		PAIR_CLASS_INIT(&tag[0], "link-includes");
1427
1428		i = 1;
1429		if (h->base_includes) {
1430			buffmt_includes(h, buf);
1431			PAIR_HREF_INIT(&tag[i], h->buf);
1432			i++;
1433		}
1434
1435		t = print_otag(h, TAG_A, i, tag);
1436		print_text(h, n->string);
1437		print_tagq(h, t);
1438
1439		n = n->next;
1440	}
1441
1442	for ( ; n; n = n->next) {
1443		assert(MDOC_TEXT == n->type);
1444		print_text(h, n->string);
1445	}
1446
1447	return(0);
1448}
1449
1450
1451/* ARGSUSED */
1452static int
1453mdoc_vt_pre(MDOC_ARGS)
1454{
1455	struct htmlpair	 tag;
1456
1457	if (MDOC_BLOCK == n->type) {
1458		synopsis_pre(h, n);
1459		return(1);
1460	} else if (MDOC_ELEM == n->type) {
1461		synopsis_pre(h, n);
1462	} else if (MDOC_HEAD == n->type)
1463		return(0);
1464
1465	PAIR_CLASS_INIT(&tag, "type");
1466	print_otag(h, TAG_SPAN, 1, &tag);
1467	return(1);
1468}
1469
1470
1471/* ARGSUSED */
1472static int
1473mdoc_ft_pre(MDOC_ARGS)
1474{
1475	struct htmlpair	 tag;
1476
1477	synopsis_pre(h, n);
1478	PAIR_CLASS_INIT(&tag, "ftype");
1479	print_otag(h, TAG_I, 1, &tag);
1480	return(1);
1481}
1482
1483
1484/* ARGSUSED */
1485static int
1486mdoc_fn_pre(MDOC_ARGS)
1487{
1488	struct tag	*t;
1489	struct htmlpair	 tag[2];
1490	char		 nbuf[BUFSIZ];
1491	const char	*sp, *ep;
1492	int		 sz, i, pretty;
1493
1494	pretty = MDOC_SYNPRETTY & n->flags;
1495	synopsis_pre(h, n);
1496
1497	/* Split apart into type and name. */
1498	assert(n->child->string);
1499	sp = n->child->string;
1500
1501	ep = strchr(sp, ' ');
1502	if (NULL != ep) {
1503		PAIR_CLASS_INIT(&tag[0], "ftype");
1504		t = print_otag(h, TAG_I, 1, tag);
1505
1506		while (ep) {
1507			sz = MIN((int)(ep - sp), BUFSIZ - 1);
1508			(void)memcpy(nbuf, sp, (size_t)sz);
1509			nbuf[sz] = '\0';
1510			print_text(h, nbuf);
1511			sp = ++ep;
1512			ep = strchr(sp, ' ');
1513		}
1514		print_tagq(h, t);
1515	}
1516
1517	PAIR_CLASS_INIT(&tag[0], "fname");
1518
1519	/*
1520	 * FIXME: only refer to IDs that we know exist.
1521	 */
1522
1523#if 0
1524	if (MDOC_SYNPRETTY & n->flags) {
1525		nbuf[0] = '\0';
1526		html_idcat(nbuf, sp, BUFSIZ);
1527		PAIR_ID_INIT(&tag[1], nbuf);
1528	} else {
1529		strlcpy(nbuf, "#", BUFSIZ);
1530		html_idcat(nbuf, sp, BUFSIZ);
1531		PAIR_HREF_INIT(&tag[1], nbuf);
1532	}
1533#endif
1534
1535	t = print_otag(h, TAG_B, 1, tag);
1536
1537	if (sp) {
1538		strlcpy(nbuf, sp, BUFSIZ);
1539		print_text(h, nbuf);
1540	}
1541
1542	print_tagq(h, t);
1543
1544	h->flags |= HTML_NOSPACE;
1545	print_text(h, "(");
1546	h->flags |= HTML_NOSPACE;
1547
1548	PAIR_CLASS_INIT(&tag[0], "farg");
1549	bufinit(h);
1550	bufcat_style(h, "white-space", "nowrap");
1551	PAIR_STYLE_INIT(&tag[1], h);
1552
1553	for (n = n->child->next; n; n = n->next) {
1554		i = 1;
1555		if (MDOC_SYNPRETTY & n->flags)
1556			i = 2;
1557		t = print_otag(h, TAG_I, i, tag);
1558		print_text(h, n->string);
1559		print_tagq(h, t);
1560		if (n->next) {
1561			h->flags |= HTML_NOSPACE;
1562			print_text(h, ",");
1563		}
1564	}
1565
1566	h->flags |= HTML_NOSPACE;
1567	print_text(h, ")");
1568
1569	if (pretty) {
1570		h->flags |= HTML_NOSPACE;
1571		print_text(h, ";");
1572	}
1573
1574	return(0);
1575}
1576
1577
1578/* ARGSUSED */
1579static int
1580mdoc_sm_pre(MDOC_ARGS)
1581{
1582
1583	assert(n->child && MDOC_TEXT == n->child->type);
1584	if (0 == strcmp("on", n->child->string)) {
1585		/*
1586		 * FIXME: no p->col to check.  Thus, if we have
1587		 *  .Bd -literal
1588		 *  .Sm off
1589		 *  1 2
1590		 *  .Sm on
1591		 *  3
1592		 *  .Ed
1593		 * the "3" is preceded by a space.
1594		 */
1595		h->flags &= ~HTML_NOSPACE;
1596		h->flags &= ~HTML_NONOSPACE;
1597	} else
1598		h->flags |= HTML_NONOSPACE;
1599
1600	return(0);
1601}
1602
1603/* ARGSUSED */
1604static int
1605mdoc_pp_pre(MDOC_ARGS)
1606{
1607
1608	print_otag(h, TAG_P, 0, NULL);
1609	return(0);
1610
1611}
1612
1613/* ARGSUSED */
1614static int
1615mdoc_sp_pre(MDOC_ARGS)
1616{
1617	struct roffsu	 su;
1618	struct htmlpair	 tag;
1619
1620	SCALE_VS_INIT(&su, 1);
1621
1622	if (MDOC_sp == n->tok) {
1623		if (NULL != (n = n->child))
1624			if ( ! a2roffsu(n->string, &su, SCALE_VS))
1625				SCALE_VS_INIT(&su, atoi(n->string));
1626	} else
1627		su.scale = 0;
1628
1629	bufinit(h);
1630	bufcat_su(h, "height", &su);
1631	PAIR_STYLE_INIT(&tag, h);
1632	print_otag(h, TAG_DIV, 1, &tag);
1633
1634	/* So the div isn't empty: */
1635	print_text(h, "\\~");
1636
1637	return(0);
1638
1639}
1640
1641/* ARGSUSED */
1642static int
1643mdoc_lk_pre(MDOC_ARGS)
1644{
1645	struct htmlpair	 tag[2];
1646
1647	if (NULL == (n = n->child))
1648		return(0);
1649
1650	assert(MDOC_TEXT == n->type);
1651
1652	PAIR_CLASS_INIT(&tag[0], "link-ext");
1653	PAIR_HREF_INIT(&tag[1], n->string);
1654
1655	print_otag(h, TAG_A, 2, tag);
1656
1657	if (NULL == n->next)
1658		print_text(h, n->string);
1659
1660	for (n = n->next; n; n = n->next)
1661		print_text(h, n->string);
1662
1663	return(0);
1664}
1665
1666
1667/* ARGSUSED */
1668static int
1669mdoc_mt_pre(MDOC_ARGS)
1670{
1671	struct htmlpair	 tag[2];
1672	struct tag	*t;
1673
1674	PAIR_CLASS_INIT(&tag[0], "link-mail");
1675
1676	for (n = n->child; n; n = n->next) {
1677		assert(MDOC_TEXT == n->type);
1678
1679		bufinit(h);
1680		bufcat(h, "mailto:");
1681		bufcat(h, n->string);
1682
1683		PAIR_HREF_INIT(&tag[1], h->buf);
1684		t = print_otag(h, TAG_A, 2, tag);
1685		print_text(h, n->string);
1686		print_tagq(h, t);
1687	}
1688
1689	return(0);
1690}
1691
1692
1693/* ARGSUSED */
1694static int
1695mdoc_fo_pre(MDOC_ARGS)
1696{
1697	struct htmlpair	 tag;
1698	struct tag	*t;
1699
1700	if (MDOC_BODY == n->type) {
1701		h->flags |= HTML_NOSPACE;
1702		print_text(h, "(");
1703		h->flags |= HTML_NOSPACE;
1704		return(1);
1705	} else if (MDOC_BLOCK == n->type) {
1706		synopsis_pre(h, n);
1707		return(1);
1708	}
1709
1710	/* XXX: we drop non-initial arguments as per groff. */
1711
1712	assert(n->child);
1713	assert(n->child->string);
1714
1715	PAIR_CLASS_INIT(&tag, "fname");
1716	t = print_otag(h, TAG_B, 1, &tag);
1717	print_text(h, n->child->string);
1718	print_tagq(h, t);
1719	return(0);
1720}
1721
1722
1723/* ARGSUSED */
1724static void
1725mdoc_fo_post(MDOC_ARGS)
1726{
1727
1728	if (MDOC_BODY != n->type)
1729		return;
1730	h->flags |= HTML_NOSPACE;
1731	print_text(h, ")");
1732	h->flags |= HTML_NOSPACE;
1733	print_text(h, ";");
1734}
1735
1736
1737/* ARGSUSED */
1738static int
1739mdoc_in_pre(MDOC_ARGS)
1740{
1741	struct tag	*t;
1742	struct htmlpair	 tag[2];
1743	int		 i;
1744
1745	synopsis_pre(h, n);
1746
1747	PAIR_CLASS_INIT(&tag[0], "includes");
1748	print_otag(h, TAG_B, 1, tag);
1749
1750	/*
1751	 * The first argument of the `In' gets special treatment as
1752	 * being a linked value.  Subsequent values are printed
1753	 * afterward.  groff does similarly.  This also handles the case
1754	 * of no children.
1755	 */
1756
1757	if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
1758		print_text(h, "#include");
1759
1760	print_text(h, "<");
1761	h->flags |= HTML_NOSPACE;
1762
1763	if (NULL != (n = n->child)) {
1764		assert(MDOC_TEXT == n->type);
1765
1766		PAIR_CLASS_INIT(&tag[0], "link-includes");
1767
1768		i = 1;
1769		if (h->base_includes) {
1770			buffmt_includes(h, n->string);
1771			PAIR_HREF_INIT(&tag[i], h->buf);
1772			i++;
1773		}
1774
1775		t = print_otag(h, TAG_A, i, tag);
1776		print_text(h, n->string);
1777		print_tagq(h, t);
1778
1779		n = n->next;
1780	}
1781
1782	h->flags |= HTML_NOSPACE;
1783	print_text(h, ">");
1784
1785	for ( ; n; n = n->next) {
1786		assert(MDOC_TEXT == n->type);
1787		print_text(h, n->string);
1788	}
1789
1790	return(0);
1791}
1792
1793
1794/* ARGSUSED */
1795static int
1796mdoc_ic_pre(MDOC_ARGS)
1797{
1798	struct htmlpair	tag;
1799
1800	PAIR_CLASS_INIT(&tag, "cmd");
1801	print_otag(h, TAG_B, 1, &tag);
1802	return(1);
1803}
1804
1805
1806/* ARGSUSED */
1807static int
1808mdoc_rv_pre(MDOC_ARGS)
1809{
1810	struct htmlpair	 tag;
1811	struct tag	*t;
1812	int		 nchild;
1813
1814	if (n->prev)
1815		print_otag(h, TAG_BR, 0, NULL);
1816
1817	PAIR_CLASS_INIT(&tag, "fname");
1818
1819	print_text(h, "The");
1820
1821	nchild = n->nchild;
1822	for (n = n->child; n; n = n->next) {
1823		assert(MDOC_TEXT == n->type);
1824
1825		t = print_otag(h, TAG_B, 1, &tag);
1826		print_text(h, n->string);
1827		print_tagq(h, t);
1828
1829		h->flags |= HTML_NOSPACE;
1830		print_text(h, "()");
1831
1832		if (nchild > 2 && n->next) {
1833			h->flags |= HTML_NOSPACE;
1834			print_text(h, ",");
1835		}
1836
1837		if (n->next && NULL == n->next->next)
1838			print_text(h, "and");
1839	}
1840
1841	if (nchild > 1)
1842		print_text(h, "functions return");
1843	else
1844		print_text(h, "function returns");
1845
1846       	print_text(h, "the value 0 if successful; otherwise the value "
1847			"-1 is returned and the global variable");
1848
1849	PAIR_CLASS_INIT(&tag, "var");
1850	t = print_otag(h, TAG_B, 1, &tag);
1851	print_text(h, "errno");
1852	print_tagq(h, t);
1853       	print_text(h, "is set to indicate the error.");
1854	return(0);
1855}
1856
1857
1858/* ARGSUSED */
1859static int
1860mdoc_va_pre(MDOC_ARGS)
1861{
1862	struct htmlpair	tag;
1863
1864	PAIR_CLASS_INIT(&tag, "var");
1865	print_otag(h, TAG_B, 1, &tag);
1866	return(1);
1867}
1868
1869
1870/* ARGSUSED */
1871static int
1872mdoc_ap_pre(MDOC_ARGS)
1873{
1874
1875	h->flags |= HTML_NOSPACE;
1876	print_text(h, "\\(aq");
1877	h->flags |= HTML_NOSPACE;
1878	return(1);
1879}
1880
1881
1882/* ARGSUSED */
1883static int
1884mdoc_bf_pre(MDOC_ARGS)
1885{
1886	struct htmlpair	 tag[2];
1887	struct roffsu	 su;
1888
1889	if (MDOC_HEAD == n->type)
1890		return(0);
1891	else if (MDOC_BODY != n->type)
1892		return(1);
1893
1894	if (FONT_Em == n->norm->Bf.font)
1895		PAIR_CLASS_INIT(&tag[0], "emph");
1896	else if (FONT_Sy == n->norm->Bf.font)
1897		PAIR_CLASS_INIT(&tag[0], "symb");
1898	else if (FONT_Li == n->norm->Bf.font)
1899		PAIR_CLASS_INIT(&tag[0], "lit");
1900	else
1901		PAIR_CLASS_INIT(&tag[0], "none");
1902
1903	/*
1904	 * We want this to be inline-formatted, but needs to be div to
1905	 * accept block children.
1906	 */
1907	bufinit(h);
1908	bufcat_style(h, "display", "inline");
1909	SCALE_HS_INIT(&su, 1);
1910	/* Needs a left-margin for spacing. */
1911	bufcat_su(h, "margin-left", &su);
1912	PAIR_STYLE_INIT(&tag[1], h);
1913	print_otag(h, TAG_DIV, 2, tag);
1914	return(1);
1915}
1916
1917
1918/* ARGSUSED */
1919static int
1920mdoc_ms_pre(MDOC_ARGS)
1921{
1922	struct htmlpair	tag;
1923
1924	PAIR_CLASS_INIT(&tag, "symb");
1925	print_otag(h, TAG_SPAN, 1, &tag);
1926	return(1);
1927}
1928
1929
1930/* ARGSUSED */
1931static int
1932mdoc_igndelim_pre(MDOC_ARGS)
1933{
1934
1935	h->flags |= HTML_IGNDELIM;
1936	return(1);
1937}
1938
1939
1940/* ARGSUSED */
1941static void
1942mdoc_pf_post(MDOC_ARGS)
1943{
1944
1945	h->flags |= HTML_NOSPACE;
1946}
1947
1948
1949/* ARGSUSED */
1950static int
1951mdoc_rs_pre(MDOC_ARGS)
1952{
1953	struct htmlpair	 tag;
1954
1955	if (MDOC_BLOCK != n->type)
1956		return(1);
1957
1958	if (n->prev && SEC_SEE_ALSO == n->sec)
1959		print_otag(h, TAG_P, 0, NULL);
1960
1961	PAIR_CLASS_INIT(&tag, "ref");
1962	print_otag(h, TAG_SPAN, 1, &tag);
1963	return(1);
1964}
1965
1966
1967
1968/* ARGSUSED */
1969static int
1970mdoc_li_pre(MDOC_ARGS)
1971{
1972	struct htmlpair	tag;
1973
1974	PAIR_CLASS_INIT(&tag, "lit");
1975	print_otag(h, TAG_CODE, 1, &tag);
1976	return(1);
1977}
1978
1979
1980/* ARGSUSED */
1981static int
1982mdoc_sy_pre(MDOC_ARGS)
1983{
1984	struct htmlpair	tag;
1985
1986	PAIR_CLASS_INIT(&tag, "symb");
1987	print_otag(h, TAG_SPAN, 1, &tag);
1988	return(1);
1989}
1990
1991
1992/* ARGSUSED */
1993static int
1994mdoc_bt_pre(MDOC_ARGS)
1995{
1996
1997	print_text(h, "is currently in beta test.");
1998	return(0);
1999}
2000
2001
2002/* ARGSUSED */
2003static int
2004mdoc_ud_pre(MDOC_ARGS)
2005{
2006
2007	print_text(h, "currently under development.");
2008	return(0);
2009}
2010
2011
2012/* ARGSUSED */
2013static int
2014mdoc_lb_pre(MDOC_ARGS)
2015{
2016	struct htmlpair	tag;
2017
2018	if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev)
2019		print_otag(h, TAG_BR, 0, NULL);
2020
2021	PAIR_CLASS_INIT(&tag, "lib");
2022	print_otag(h, TAG_SPAN, 1, &tag);
2023	return(1);
2024}
2025
2026
2027/* ARGSUSED */
2028static int
2029mdoc__x_pre(MDOC_ARGS)
2030{
2031	struct htmlpair	tag[2];
2032	enum htmltag	t;
2033
2034	t = TAG_SPAN;
2035
2036	switch (n->tok) {
2037	case(MDOC__A):
2038		PAIR_CLASS_INIT(&tag[0], "ref-auth");
2039		if (n->prev && MDOC__A == n->prev->tok)
2040			if (NULL == n->next || MDOC__A != n->next->tok)
2041				print_text(h, "and");
2042		break;
2043	case(MDOC__B):
2044		PAIR_CLASS_INIT(&tag[0], "ref-book");
2045		t = TAG_I;
2046		break;
2047	case(MDOC__C):
2048		PAIR_CLASS_INIT(&tag[0], "ref-city");
2049		break;
2050	case(MDOC__D):
2051		PAIR_CLASS_INIT(&tag[0], "ref-date");
2052		break;
2053	case(MDOC__I):
2054		PAIR_CLASS_INIT(&tag[0], "ref-issue");
2055		t = TAG_I;
2056		break;
2057	case(MDOC__J):
2058		PAIR_CLASS_INIT(&tag[0], "ref-jrnl");
2059		t = TAG_I;
2060		break;
2061	case(MDOC__N):
2062		PAIR_CLASS_INIT(&tag[0], "ref-num");
2063		break;
2064	case(MDOC__O):
2065		PAIR_CLASS_INIT(&tag[0], "ref-opt");
2066		break;
2067	case(MDOC__P):
2068		PAIR_CLASS_INIT(&tag[0], "ref-page");
2069		break;
2070	case(MDOC__Q):
2071		PAIR_CLASS_INIT(&tag[0], "ref-corp");
2072		break;
2073	case(MDOC__R):
2074		PAIR_CLASS_INIT(&tag[0], "ref-rep");
2075		break;
2076	case(MDOC__T):
2077		PAIR_CLASS_INIT(&tag[0], "ref-title");
2078		break;
2079	case(MDOC__U):
2080		PAIR_CLASS_INIT(&tag[0], "link-ref");
2081		break;
2082	case(MDOC__V):
2083		PAIR_CLASS_INIT(&tag[0], "ref-vol");
2084		break;
2085	default:
2086		abort();
2087		/* NOTREACHED */
2088	}
2089
2090	if (MDOC__U != n->tok) {
2091		print_otag(h, t, 1, tag);
2092		return(1);
2093	}
2094
2095	PAIR_HREF_INIT(&tag[1], n->child->string);
2096	print_otag(h, TAG_A, 2, tag);
2097
2098	return(1);
2099}
2100
2101
2102/* ARGSUSED */
2103static void
2104mdoc__x_post(MDOC_ARGS)
2105{
2106
2107	if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
2108		if (NULL == n->next->next || MDOC__A != n->next->next->tok)
2109			if (NULL == n->prev || MDOC__A != n->prev->tok)
2110				return;
2111
2112	/* TODO: %U */
2113
2114	if (NULL == n->parent || MDOC_Rs != n->parent->tok)
2115		return;
2116
2117	h->flags |= HTML_NOSPACE;
2118	print_text(h, n->next ? "," : ".");
2119}
2120
2121
2122/* ARGSUSED */
2123static int
2124mdoc_bk_pre(MDOC_ARGS)
2125{
2126
2127	switch (n->type) {
2128	case (MDOC_BLOCK):
2129		break;
2130	case (MDOC_HEAD):
2131		return(0);
2132	case (MDOC_BODY):
2133		if (n->parent->args || 0 == n->prev->nchild)
2134			h->flags |= HTML_PREKEEP;
2135		break;
2136	default:
2137		abort();
2138		/* NOTREACHED */
2139	}
2140
2141	return(1);
2142}
2143
2144
2145/* ARGSUSED */
2146static void
2147mdoc_bk_post(MDOC_ARGS)
2148{
2149
2150	if (MDOC_BODY == n->type)
2151		h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
2152}
2153
2154
2155/* ARGSUSED */
2156static int
2157mdoc_quote_pre(MDOC_ARGS)
2158{
2159	struct htmlpair	tag;
2160
2161	if (MDOC_BODY != n->type)
2162		return(1);
2163
2164	switch (n->tok) {
2165	case (MDOC_Ao):
2166		/* FALLTHROUGH */
2167	case (MDOC_Aq):
2168		print_text(h, "\\(la");
2169		break;
2170	case (MDOC_Bro):
2171		/* FALLTHROUGH */
2172	case (MDOC_Brq):
2173		print_text(h, "\\(lC");
2174		break;
2175	case (MDOC_Bo):
2176		/* FALLTHROUGH */
2177	case (MDOC_Bq):
2178		print_text(h, "\\(lB");
2179		break;
2180	case (MDOC_Oo):
2181		/* FALLTHROUGH */
2182	case (MDOC_Op):
2183		print_text(h, "\\(lB");
2184		h->flags |= HTML_NOSPACE;
2185		PAIR_CLASS_INIT(&tag, "opt");
2186		print_otag(h, TAG_SPAN, 1, &tag);
2187		break;
2188	case (MDOC_Eo):
2189		break;
2190	case (MDOC_Do):
2191		/* FALLTHROUGH */
2192	case (MDOC_Dq):
2193		/* FALLTHROUGH */
2194	case (MDOC_Qo):
2195		/* FALLTHROUGH */
2196	case (MDOC_Qq):
2197		print_text(h, "\\(lq");
2198		break;
2199	case (MDOC_Po):
2200		/* FALLTHROUGH */
2201	case (MDOC_Pq):
2202		print_text(h, "(");
2203		break;
2204	case (MDOC_Ql):
2205		print_text(h, "\\(oq");
2206		h->flags |= HTML_NOSPACE;
2207		PAIR_CLASS_INIT(&tag, "lit");
2208		print_otag(h, TAG_CODE, 1, &tag);
2209		break;
2210	case (MDOC_So):
2211		/* FALLTHROUGH */
2212	case (MDOC_Sq):
2213		print_text(h, "\\(oq");
2214		break;
2215	default:
2216		abort();
2217		/* NOTREACHED */
2218	}
2219
2220	h->flags |= HTML_NOSPACE;
2221	return(1);
2222}
2223
2224
2225/* ARGSUSED */
2226static void
2227mdoc_quote_post(MDOC_ARGS)
2228{
2229
2230	if (MDOC_BODY != n->type)
2231		return;
2232
2233	h->flags |= HTML_NOSPACE;
2234
2235	switch (n->tok) {
2236	case (MDOC_Ao):
2237		/* FALLTHROUGH */
2238	case (MDOC_Aq):
2239		print_text(h, "\\(ra");
2240		break;
2241	case (MDOC_Bro):
2242		/* FALLTHROUGH */
2243	case (MDOC_Brq):
2244		print_text(h, "\\(rC");
2245		break;
2246	case (MDOC_Oo):
2247		/* FALLTHROUGH */
2248	case (MDOC_Op):
2249		/* FALLTHROUGH */
2250	case (MDOC_Bo):
2251		/* FALLTHROUGH */
2252	case (MDOC_Bq):
2253		print_text(h, "\\(rB");
2254		break;
2255	case (MDOC_Eo):
2256		break;
2257	case (MDOC_Qo):
2258		/* FALLTHROUGH */
2259	case (MDOC_Qq):
2260		/* FALLTHROUGH */
2261	case (MDOC_Do):
2262		/* FALLTHROUGH */
2263	case (MDOC_Dq):
2264		print_text(h, "\\(rq");
2265		break;
2266	case (MDOC_Po):
2267		/* FALLTHROUGH */
2268	case (MDOC_Pq):
2269		print_text(h, ")");
2270		break;
2271	case (MDOC_Ql):
2272		/* FALLTHROUGH */
2273	case (MDOC_So):
2274		/* FALLTHROUGH */
2275	case (MDOC_Sq):
2276		print_text(h, "\\(aq");
2277		break;
2278	default:
2279		abort();
2280		/* NOTREACHED */
2281	}
2282}
2283
2284
2285