xref: /netbsd-src/external/bsd/mdocml/dist/man_html.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$Vendor-Id: man_html.c,v 1.45 2010/07/23 12:27:28 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include <sys/types.h>
22 
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "mandoc.h"
30 #include "out.h"
31 #include "html.h"
32 #include "man.h"
33 #include "main.h"
34 
35 /* TODO: preserve ident widths. */
36 /* FIXME: have PD set the default vspace width. */
37 
38 #define	INDENT		  5
39 #define	HALFINDENT	  3
40 
41 #define	MAN_ARGS	  const struct man_meta *m, \
42 			  const struct man_node *n, \
43 			  struct mhtml *mh, \
44 			  struct html *h
45 
46 struct	mhtml {
47 	int		  fl;
48 #define	MANH_LITERAL	 (1 << 0) /* literal context */
49 };
50 
51 struct	htmlman {
52 	int		(*pre)(MAN_ARGS);
53 	int		(*post)(MAN_ARGS);
54 };
55 
56 static	void		  print_man(MAN_ARGS);
57 static	void		  print_man_head(MAN_ARGS);
58 static	void		  print_man_nodelist(MAN_ARGS);
59 static	void		  print_man_node(MAN_ARGS);
60 
61 static	int		  a2width(const struct man_node *,
62 				struct roffsu *);
63 
64 static	int		  man_alt_pre(MAN_ARGS);
65 static	int		  man_br_pre(MAN_ARGS);
66 static	int		  man_ign_pre(MAN_ARGS);
67 static	int		  man_in_pre(MAN_ARGS);
68 static	int		  man_literal_pre(MAN_ARGS);
69 static	void		  man_root_post(MAN_ARGS);
70 static	int		  man_root_pre(MAN_ARGS);
71 static	int		  man_B_pre(MAN_ARGS);
72 static	int		  man_HP_pre(MAN_ARGS);
73 static	int		  man_I_pre(MAN_ARGS);
74 static	int		  man_IP_pre(MAN_ARGS);
75 static	int		  man_PP_pre(MAN_ARGS);
76 static	int		  man_RS_pre(MAN_ARGS);
77 static	int		  man_SB_pre(MAN_ARGS);
78 static	int		  man_SH_pre(MAN_ARGS);
79 static	int		  man_SM_pre(MAN_ARGS);
80 static	int		  man_SS_pre(MAN_ARGS);
81 
82 static	const struct htmlman mans[MAN_MAX] = {
83 	{ man_br_pre, NULL }, /* br */
84 	{ NULL, NULL }, /* TH */
85 	{ man_SH_pre, NULL }, /* SH */
86 	{ man_SS_pre, NULL }, /* SS */
87 	{ man_IP_pre, NULL }, /* TP */
88 	{ man_PP_pre, NULL }, /* LP */
89 	{ man_PP_pre, NULL }, /* PP */
90 	{ man_PP_pre, NULL }, /* P */
91 	{ man_IP_pre, NULL }, /* IP */
92 	{ man_HP_pre, NULL }, /* HP */
93 	{ man_SM_pre, NULL }, /* SM */
94 	{ man_SB_pre, NULL }, /* SB */
95 	{ man_alt_pre, NULL }, /* BI */
96 	{ man_alt_pre, NULL }, /* IB */
97 	{ man_alt_pre, NULL }, /* BR */
98 	{ man_alt_pre, NULL }, /* RB */
99 	{ NULL, NULL }, /* R */
100 	{ man_B_pre, NULL }, /* B */
101 	{ man_I_pre, NULL }, /* I */
102 	{ man_alt_pre, NULL }, /* IR */
103 	{ man_alt_pre, NULL }, /* RI */
104 	{ NULL, NULL }, /* na */
105 	{ NULL, NULL }, /* i */
106 	{ man_br_pre, NULL }, /* sp */
107 	{ man_literal_pre, NULL }, /* nf */
108 	{ man_literal_pre, NULL }, /* fi */
109 	{ NULL, NULL }, /* r */
110 	{ NULL, NULL }, /* RE */
111 	{ man_RS_pre, NULL }, /* RS */
112 	{ man_ign_pre, NULL }, /* DT */
113 	{ man_ign_pre, NULL }, /* UC */
114 	{ man_ign_pre, NULL }, /* PD */
115 	{ man_br_pre, NULL }, /* Sp */
116 	{ man_literal_pre, NULL }, /* Vb */
117 	{ man_literal_pre, NULL }, /* Ve */
118 	{ man_ign_pre, NULL }, /* AT */
119 	{ man_in_pre, NULL }, /* in */
120 };
121 
122 
123 void
124 html_man(void *arg, const struct man *m)
125 {
126 	struct html	*h;
127 	struct tag	*t;
128 	struct mhtml	 mh;
129 
130 	h = (struct html *)arg;
131 
132 	print_gen_decls(h);
133 
134 	memset(&mh, 0, sizeof(struct mhtml));
135 
136 	t = print_otag(h, TAG_HTML, 0, NULL);
137 	print_man(man_meta(m), man_node(m), &mh, h);
138 	print_tagq(h, t);
139 
140 	printf("\n");
141 }
142 
143 
144 static void
145 print_man(MAN_ARGS)
146 {
147 	struct tag	*t;
148 	struct htmlpair	 tag;
149 
150 	t = print_otag(h, TAG_HEAD, 0, NULL);
151 
152 	print_man_head(m, n, mh, h);
153 	print_tagq(h, t);
154 	t = print_otag(h, TAG_BODY, 0, NULL);
155 
156 	tag.key = ATTR_CLASS;
157 	tag.val = "body";
158 	print_otag(h, TAG_DIV, 1, &tag);
159 
160 	print_man_nodelist(m, n, mh, h);
161 
162 	print_tagq(h, t);
163 }
164 
165 
166 /* ARGSUSED */
167 static void
168 print_man_head(MAN_ARGS)
169 {
170 
171 	print_gen_head(h);
172 	bufinit(h);
173 	buffmt(h, "%s(%s)", m->title, m->msec);
174 
175 	print_otag(h, TAG_TITLE, 0, NULL);
176 	print_text(h, h->buf);
177 }
178 
179 
180 static void
181 print_man_nodelist(MAN_ARGS)
182 {
183 
184 	print_man_node(m, n, mh, h);
185 	if (n->next)
186 		print_man_nodelist(m, n->next, mh, h);
187 }
188 
189 
190 static void
191 print_man_node(MAN_ARGS)
192 {
193 	int		 child;
194 	struct tag	*t;
195 
196 	child = 1;
197 	t = h->tags.head;
198 
199 	bufinit(h);
200 
201 	/*
202 	 * FIXME: embedded elements within next-line scopes (e.g., `br'
203 	 * within an empty `B') will cause formatting to be forgotten
204 	 * due to scope closing out.
205 	 */
206 
207 	switch (n->type) {
208 	case (MAN_ROOT):
209 		child = man_root_pre(m, n, mh, h);
210 		break;
211 	case (MAN_TEXT):
212 		print_text(h, n->string);
213 
214 		if (MANH_LITERAL & mh->fl)
215 			print_otag(h, TAG_BR, 0, NULL);
216 
217 		return;
218 	default:
219 		/*
220 		 * Close out scope of font prior to opening a macro
221 		 * scope.  Assert that the metafont is on the top of the
222 		 * stack (it's never nested).
223 		 */
224 		if (h->metaf) {
225 			assert(h->metaf == t);
226 			print_tagq(h, h->metaf);
227 			assert(NULL == h->metaf);
228 			t = h->tags.head;
229 		}
230 		if (mans[n->tok].pre)
231 			child = (*mans[n->tok].pre)(m, n, mh, h);
232 		break;
233 	}
234 
235 	if (child && n->child)
236 		print_man_nodelist(m, n->child, mh, h);
237 
238 	/* This will automatically close out any font scope. */
239 	print_stagq(h, t);
240 
241 	bufinit(h);
242 
243 	switch (n->type) {
244 	case (MAN_ROOT):
245 		man_root_post(m, n, mh, h);
246 		break;
247 	case (MAN_TEXT):
248 		break;
249 	default:
250 		if (mans[n->tok].post)
251 			(*mans[n->tok].post)(m, n, mh, h);
252 		break;
253 	}
254 }
255 
256 
257 static int
258 a2width(const struct man_node *n, struct roffsu *su)
259 {
260 
261 	if (MAN_TEXT != n->type)
262 		return(0);
263 	if (a2roffsu(n->string, su, SCALE_BU))
264 		return(1);
265 
266 	return(0);
267 }
268 
269 
270 /* ARGSUSED */
271 static int
272 man_root_pre(MAN_ARGS)
273 {
274 	struct htmlpair	 tag[3];
275 	struct tag	*t, *tt;
276 	char		 b[BUFSIZ], title[BUFSIZ];
277 
278 	b[0] = 0;
279 	if (m->vol)
280 		(void)strlcat(b, m->vol, BUFSIZ);
281 
282 	snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
283 
284 	PAIR_CLASS_INIT(&tag[0], "header");
285 	bufcat_style(h, "width", "100%");
286 	PAIR_STYLE_INIT(&tag[1], h);
287 	PAIR_SUMMARY_INIT(&tag[2], "header");
288 
289 	t = print_otag(h, TAG_TABLE, 3, tag);
290 	tt = print_otag(h, TAG_TR, 0, NULL);
291 
292 	bufinit(h);
293 	bufcat_style(h, "width", "10%");
294 	PAIR_STYLE_INIT(&tag[0], h);
295 	print_otag(h, TAG_TD, 1, tag);
296 	print_text(h, title);
297 	print_stagq(h, tt);
298 
299 	bufinit(h);
300 	bufcat_style(h, "width", "80%");
301 	bufcat_style(h, "white-space", "nowrap");
302 	bufcat_style(h, "text-align", "center");
303 	PAIR_STYLE_INIT(&tag[0], h);
304 	print_otag(h, TAG_TD, 1, tag);
305 	print_text(h, b);
306 	print_stagq(h, tt);
307 
308 	bufinit(h);
309 	bufcat_style(h, "width", "10%");
310 	bufcat_style(h, "text-align", "right");
311 	PAIR_STYLE_INIT(&tag[0], h);
312 	print_otag(h, TAG_TD, 1, tag);
313 	print_text(h, title);
314 	print_tagq(h, t);
315 	return(1);
316 }
317 
318 
319 /* ARGSUSED */
320 static void
321 man_root_post(MAN_ARGS)
322 {
323 	struct htmlpair	 tag[3];
324 	struct tag	*t, *tt;
325 	char		 b[DATESIZ];
326 
327 	if (m->rawdate)
328 		strlcpy(b, m->rawdate, DATESIZ);
329 	else
330 		time2a(m->date, b, DATESIZ);
331 
332 	PAIR_CLASS_INIT(&tag[0], "footer");
333 	bufcat_style(h, "width", "100%");
334 	PAIR_STYLE_INIT(&tag[1], h);
335 	PAIR_SUMMARY_INIT(&tag[2], "footer");
336 
337 	t = print_otag(h, TAG_TABLE, 3, tag);
338 	tt = print_otag(h, TAG_TR, 0, NULL);
339 
340 	bufinit(h);
341 	bufcat_style(h, "width", "50%");
342 	PAIR_STYLE_INIT(&tag[0], h);
343 	print_otag(h, TAG_TD, 1, tag);
344 	print_text(h, b);
345 	print_stagq(h, tt);
346 
347 	bufinit(h);
348 	bufcat_style(h, "width", "50%");
349 	bufcat_style(h, "text-align", "right");
350 	PAIR_STYLE_INIT(&tag[0], h);
351 	print_otag(h, TAG_TD, 1, tag);
352 	if (m->source)
353 		print_text(h, m->source);
354 	print_tagq(h, t);
355 }
356 
357 
358 
359 /* ARGSUSED */
360 static int
361 man_br_pre(MAN_ARGS)
362 {
363 	struct roffsu	 su;
364 	struct htmlpair	 tag;
365 
366 	SCALE_VS_INIT(&su, 1);
367 
368 	switch (n->tok) {
369 	case (MAN_Sp):
370 		SCALE_VS_INIT(&su, 0.5);
371 		break;
372 	case (MAN_sp):
373 		if (n->child)
374 			a2roffsu(n->child->string, &su, SCALE_VS);
375 		break;
376 	default:
377 		su.scale = 0;
378 		break;
379 	}
380 
381 	bufcat_su(h, "height", &su);
382 	PAIR_STYLE_INIT(&tag, h);
383 	print_otag(h, TAG_DIV, 1, &tag);
384 
385 	/* So the div isn't empty: */
386 	print_text(h, "\\~");
387 
388 	return(0);
389 }
390 
391 
392 /* ARGSUSED */
393 static int
394 man_SH_pre(MAN_ARGS)
395 {
396 	struct htmlpair	 tag[2];
397 	struct roffsu	 su;
398 
399 	if (MAN_BODY == n->type) {
400 		SCALE_HS_INIT(&su, INDENT);
401 		bufcat_su(h, "margin-left", &su);
402 		PAIR_CLASS_INIT(&tag[0], "sec-body");
403 		PAIR_STYLE_INIT(&tag[1], h);
404 		print_otag(h, TAG_DIV, 2, tag);
405 		return(1);
406 	} else if (MAN_BLOCK == n->type) {
407 		PAIR_CLASS_INIT(&tag[0], "sec-block");
408 		if (n->prev && MAN_SH == n->prev->tok)
409 			if (NULL == n->prev->body->child) {
410 				print_otag(h, TAG_DIV, 1, tag);
411 				return(1);
412 			}
413 
414 		SCALE_VS_INIT(&su, 1);
415 		bufcat_su(h, "margin-top", &su);
416 		if (NULL == n->next)
417 			bufcat_su(h, "margin-bottom", &su);
418 		PAIR_STYLE_INIT(&tag[1], h);
419 		print_otag(h, TAG_DIV, 2, tag);
420 		return(1);
421 	}
422 
423 	PAIR_CLASS_INIT(&tag[0], "sec-head");
424 	print_otag(h, TAG_DIV, 1, tag);
425 	return(1);
426 }
427 
428 
429 /* ARGSUSED */
430 static int
431 man_alt_pre(MAN_ARGS)
432 {
433 	const struct man_node	*nn;
434 	struct tag		*t;
435 	int			 i;
436 	enum htmlfont		 fp;
437 
438 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
439 		switch (n->tok) {
440 		case (MAN_BI):
441 			fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
442 			break;
443 		case (MAN_IB):
444 			fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
445 			break;
446 		case (MAN_RI):
447 			fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
448 			break;
449 		case (MAN_IR):
450 			fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
451 			break;
452 		case (MAN_BR):
453 			fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
454 			break;
455 		case (MAN_RB):
456 			fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
457 			break;
458 		default:
459 			abort();
460 			/* NOTREACHED */
461 		}
462 
463 		if (i)
464 			h->flags |= HTML_NOSPACE;
465 
466 		/*
467 		 * Open and close the scope with each argument, so that
468 		 * internal \f escapes, which are common, are also
469 		 * closed out with the scope.
470 		 */
471 		t = print_ofont(h, fp);
472 		print_man_node(m, nn, mh, h);
473 		print_tagq(h, t);
474 	}
475 
476 	return(0);
477 }
478 
479 
480 /* ARGSUSED */
481 static int
482 man_SB_pre(MAN_ARGS)
483 {
484 	struct htmlpair	 tag;
485 
486 	/* FIXME: print_ofont(). */
487 	PAIR_CLASS_INIT(&tag, "small bold");
488 	print_otag(h, TAG_SPAN, 1, &tag);
489 	return(1);
490 }
491 
492 
493 /* ARGSUSED */
494 static int
495 man_SM_pre(MAN_ARGS)
496 {
497 	struct htmlpair	 tag;
498 
499 	PAIR_CLASS_INIT(&tag, "small");
500 	print_otag(h, TAG_SPAN, 1, &tag);
501 	return(1);
502 }
503 
504 
505 /* ARGSUSED */
506 static int
507 man_SS_pre(MAN_ARGS)
508 {
509 	struct htmlpair	 tag[3];
510 	struct roffsu	 su;
511 
512 	SCALE_VS_INIT(&su, 1);
513 
514 	if (MAN_BODY == n->type) {
515 		PAIR_CLASS_INIT(&tag[0], "ssec-body");
516 		if (n->parent->next && n->child) {
517 			bufcat_su(h, "margin-bottom", &su);
518 			PAIR_STYLE_INIT(&tag[1], h);
519 			print_otag(h, TAG_DIV, 2, tag);
520 			return(1);
521 		}
522 
523 		print_otag(h, TAG_DIV, 1, tag);
524 		return(1);
525 	} else if (MAN_BLOCK == n->type) {
526 		PAIR_CLASS_INIT(&tag[0], "ssec-block");
527 		if (n->prev && MAN_SS == n->prev->tok)
528 			if (n->prev->body->child) {
529 				bufcat_su(h, "margin-top", &su);
530 				PAIR_STYLE_INIT(&tag[1], h);
531 				print_otag(h, TAG_DIV, 2, tag);
532 				return(1);
533 			}
534 
535 		print_otag(h, TAG_DIV, 1, tag);
536 		return(1);
537 	}
538 
539 	SCALE_HS_INIT(&su, INDENT - HALFINDENT);
540 	bufcat_su(h, "margin-left", &su);
541 	PAIR_CLASS_INIT(&tag[0], "ssec-head");
542 	PAIR_STYLE_INIT(&tag[1], h);
543 	print_otag(h, TAG_DIV, 2, tag);
544 	return(1);
545 }
546 
547 
548 /* ARGSUSED */
549 static int
550 man_PP_pre(MAN_ARGS)
551 {
552 	struct htmlpair	 tag;
553 	struct roffsu	 su;
554 	int		 i;
555 
556 	if (MAN_BLOCK != n->type)
557 		return(1);
558 
559 	i = 0;
560 
561 	if (MAN_ROOT == n->parent->type) {
562 		SCALE_HS_INIT(&su, INDENT);
563 		bufcat_su(h, "margin-left", &su);
564 		i = 1;
565 	}
566 	if (n->prev) {
567 		SCALE_VS_INIT(&su, 1);
568 		bufcat_su(h, "margin-top", &su);
569 		i = 1;
570 	}
571 
572 	PAIR_STYLE_INIT(&tag, h);
573 	print_otag(h, TAG_DIV, i, &tag);
574 	return(1);
575 }
576 
577 
578 /* ARGSUSED */
579 static int
580 man_IP_pre(MAN_ARGS)
581 {
582 	struct roffsu		 su;
583 	struct htmlpair	 	 tag;
584 	const struct man_node	*nn;
585 	int			 width;
586 
587 	/*
588 	 * This scattering of 1-BU margins and pads is to make sure that
589 	 * when text overruns its box, the subsequent text isn't flush
590 	 * up against it.  However, the rest of the right-hand box must
591 	 * also be adjusted in consideration of this 1-BU space.
592 	 */
593 
594 	if (MAN_BODY == n->type) {
595 		SCALE_HS_INIT(&su, INDENT);
596 		bufcat_su(h, "margin-left", &su);
597 		PAIR_STYLE_INIT(&tag, h);
598 		print_otag(h, TAG_DIV, 1, &tag);
599 		return(1);
600 	}
601 
602 	nn = MAN_BLOCK == n->type ?
603 		n->head->child : n->parent->head->child;
604 
605 	SCALE_HS_INIT(&su, INDENT);
606 	width = 0;
607 
608 	/* Width is the last token. */
609 
610 	if (MAN_IP == n->tok && NULL != nn)
611 		if (NULL != (nn = nn->next)) {
612 			for ( ; nn->next; nn = nn->next)
613 				/* Do nothing. */ ;
614 			width = a2width(nn, &su);
615 		}
616 
617 	/* Width is the first token. */
618 
619 	if (MAN_TP == n->tok && NULL != nn) {
620 		/* Skip past non-text children. */
621 		while (nn && MAN_TEXT != nn->type)
622 			nn = nn->next;
623 		if (nn)
624 			width = a2width(nn, &su);
625 	}
626 
627 	if (MAN_BLOCK == n->type) {
628 		bufcat_su(h, "margin-left", &su);
629 		SCALE_VS_INIT(&su, 1);
630 		bufcat_su(h, "margin-top", &su);
631 		bufcat_style(h, "clear", "both");
632 		PAIR_STYLE_INIT(&tag, h);
633 		print_otag(h, TAG_DIV, 1, &tag);
634 		return(1);
635 	}
636 
637 	bufcat_su(h, "min-width", &su);
638 	SCALE_INVERT(&su);
639 	bufcat_su(h, "margin-left", &su);
640 	SCALE_HS_INIT(&su, 1);
641 	bufcat_su(h, "margin-right", &su);
642 	bufcat_style(h, "clear", "left");
643 
644 	if (n->next && n->next->child)
645 		bufcat_style(h, "float", "left");
646 
647 	PAIR_STYLE_INIT(&tag, h);
648 	print_otag(h, TAG_DIV, 1, &tag);
649 
650 	/*
651 	 * Without a length string, we can print all of our children.
652 	 */
653 
654 	if ( ! width)
655 		return(1);
656 
657 	/*
658 	 * When a length has been specified, we need to carefully print
659 	 * our child context:  IP gets all children printed but the last
660 	 * (the width), while TP gets all children printed but the first
661 	 * (the width).
662 	 */
663 
664 	if (MAN_IP == n->tok)
665 		for (nn = n->child; nn->next; nn = nn->next)
666 			print_man_node(m, nn, mh, h);
667 	if (MAN_TP == n->tok)
668 		for (nn = n->child->next; nn; nn = nn->next)
669 			print_man_node(m, nn, mh, h);
670 
671 	return(0);
672 }
673 
674 
675 /* ARGSUSED */
676 static int
677 man_HP_pre(MAN_ARGS)
678 {
679 	const struct man_node	*nn;
680 	struct htmlpair	 	 tag;
681 	struct roffsu		 su;
682 
683 	if (MAN_HEAD == n->type)
684 		return(0);
685 
686 	nn = MAN_BLOCK == n->type ?
687 		n->head->child : n->parent->head->child;
688 
689 	SCALE_HS_INIT(&su, INDENT);
690 
691 	if (NULL != nn)
692 		(void)a2width(nn, &su);
693 
694 	if (MAN_BLOCK == n->type) {
695 		bufcat_su(h, "margin-left", &su);
696 		SCALE_VS_INIT(&su, 1);
697 		bufcat_su(h, "margin-top", &su);
698 		bufcat_style(h, "clear", "both");
699 		PAIR_STYLE_INIT(&tag, h);
700 		print_otag(h, TAG_DIV, 1, &tag);
701 		return(1);
702 	}
703 
704 	bufcat_su(h, "margin-left", &su);
705 	SCALE_INVERT(&su);
706 	bufcat_su(h, "text-indent", &su);
707 
708 	PAIR_STYLE_INIT(&tag, h);
709 	print_otag(h, TAG_DIV, 1, &tag);
710 	return(1);
711 }
712 
713 
714 /* ARGSUSED */
715 static int
716 man_B_pre(MAN_ARGS)
717 {
718 
719 	print_ofont(h, HTMLFONT_BOLD);
720 	return(1);
721 }
722 
723 
724 /* ARGSUSED */
725 static int
726 man_I_pre(MAN_ARGS)
727 {
728 
729 	print_ofont(h, HTMLFONT_ITALIC);
730 	return(1);
731 }
732 
733 
734 /* ARGSUSED */
735 static int
736 man_literal_pre(MAN_ARGS)
737 {
738 
739 	switch (n->tok) {
740 	case (MAN_nf):
741 		/* FALLTHROUGH */
742 	case (MAN_Vb):
743 		print_otag(h, TAG_BR, 0, NULL);
744 		mh->fl |= MANH_LITERAL;
745 		return(MAN_Vb != n->tok);
746 	default:
747 		mh->fl &= ~MANH_LITERAL;
748 		break;
749 	}
750 
751 	return(1);
752 }
753 
754 
755 /* ARGSUSED */
756 static int
757 man_in_pre(MAN_ARGS)
758 {
759 
760 	print_otag(h, TAG_BR, 0, NULL);
761 	return(0);
762 }
763 
764 
765 /* ARGSUSED */
766 static int
767 man_ign_pre(MAN_ARGS)
768 {
769 
770 	return(0);
771 }
772 
773 
774 /* ARGSUSED */
775 static int
776 man_RS_pre(MAN_ARGS)
777 {
778 	struct htmlpair	 tag;
779 	struct roffsu	 su;
780 
781 	if (MAN_HEAD == n->type)
782 		return(0);
783 	else if (MAN_BODY == n->type)
784 		return(1);
785 
786 	SCALE_HS_INIT(&su, INDENT);
787 	bufcat_su(h, "margin-left", &su);
788 
789 	if (n->head->child) {
790 		SCALE_VS_INIT(&su, 1);
791 		a2width(n->head->child, &su);
792 		bufcat_su(h, "margin-top", &su);
793 	}
794 
795 	PAIR_STYLE_INIT(&tag, h);
796 	print_otag(h, TAG_DIV, 1, &tag);
797 	return(1);
798 }
799