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