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