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