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