xref: /netbsd-src/external/bsd/mdocml/dist/man_html.c (revision 4e6df137e8e14049b5a701d249962c480449c141)
1 /*	$Vendor-Id: man_html.c,v 1.26 2010/01/29 14:39:38 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 
29 #include "out.h"
30 #include "html.h"
31 #include "man.h"
32 #include "main.h"
33 
34 /* TODO: preserve ident widths. */
35 /* FIXME: have PD set the default vspace width. */
36 
37 #define	INDENT		  5
38 #define	HALFINDENT	  3
39 
40 #define	MAN_ARGS	  const struct man_meta *m, \
41 			  const struct man_node *n, \
42 			  struct html *h
43 
44 struct	htmlman {
45 	int		(*pre)(MAN_ARGS);
46 	int		(*post)(MAN_ARGS);
47 };
48 
49 static	void		  print_man(MAN_ARGS);
50 static	void		  print_man_head(MAN_ARGS);
51 static	void		  print_man_nodelist(MAN_ARGS);
52 static	void		  print_man_node(MAN_ARGS);
53 
54 static	int		  a2width(const struct man_node *,
55 				struct roffsu *);
56 
57 static	int		  man_alt_pre(MAN_ARGS);
58 static	int		  man_br_pre(MAN_ARGS);
59 static	int		  man_ign_pre(MAN_ARGS);
60 static	void		  man_root_post(MAN_ARGS);
61 static	int		  man_root_pre(MAN_ARGS);
62 static	int		  man_B_pre(MAN_ARGS);
63 static	int		  man_HP_pre(MAN_ARGS);
64 static	int		  man_I_pre(MAN_ARGS);
65 static	int		  man_IP_pre(MAN_ARGS);
66 static	int		  man_PP_pre(MAN_ARGS);
67 static	int		  man_RS_pre(MAN_ARGS);
68 static	int		  man_SB_pre(MAN_ARGS);
69 static	int		  man_SH_pre(MAN_ARGS);
70 static	int		  man_SM_pre(MAN_ARGS);
71 static	int		  man_SS_pre(MAN_ARGS);
72 
73 static	const struct htmlman mans[MAN_MAX] = {
74 	{ man_br_pre, NULL }, /* br */
75 	{ NULL, NULL }, /* TH */
76 	{ man_SH_pre, NULL }, /* SH */
77 	{ man_SS_pre, NULL }, /* SS */
78 	{ man_IP_pre, NULL }, /* TP */
79 	{ man_PP_pre, NULL }, /* LP */
80 	{ man_PP_pre, NULL }, /* PP */
81 	{ man_PP_pre, NULL }, /* P */
82 	{ man_IP_pre, NULL }, /* IP */
83 	{ man_HP_pre, NULL }, /* HP */
84 	{ man_SM_pre, NULL }, /* SM */
85 	{ man_SB_pre, NULL }, /* SB */
86 	{ man_alt_pre, NULL }, /* BI */
87 	{ man_alt_pre, NULL }, /* IB */
88 	{ man_alt_pre, NULL }, /* BR */
89 	{ man_alt_pre, NULL }, /* RB */
90 	{ NULL, NULL }, /* R */
91 	{ man_B_pre, NULL }, /* B */
92 	{ man_I_pre, NULL }, /* I */
93 	{ man_alt_pre, NULL }, /* IR */
94 	{ man_alt_pre, NULL }, /* RI */
95 	{ NULL, NULL }, /* na */
96 	{ NULL, NULL }, /* i */
97 	{ man_br_pre, NULL }, /* sp */
98 	{ NULL, NULL }, /* nf */
99 	{ NULL, NULL }, /* fi */
100 	{ NULL, NULL }, /* r */
101 	{ NULL, NULL }, /* RE */
102 	{ man_RS_pre, NULL }, /* RS */
103 	{ man_ign_pre, NULL }, /* DT */
104 	{ man_ign_pre, NULL }, /* UC */
105 	{ man_ign_pre, NULL }, /* PD */
106 };
107 
108 
109 void
110 html_man(void *arg, const struct man *m)
111 {
112 	struct html	*h;
113 	struct tag	*t;
114 
115 	h = (struct html *)arg;
116 
117 	print_gen_decls(h);
118 
119 	t = print_otag(h, TAG_HTML, 0, NULL);
120 	print_man(man_meta(m), man_node(m), h);
121 	print_tagq(h, t);
122 
123 	printf("\n");
124 }
125 
126 
127 static void
128 print_man(MAN_ARGS)
129 {
130 	struct tag	*t;
131 	struct htmlpair	 tag;
132 
133 	t = print_otag(h, TAG_HEAD, 0, NULL);
134 
135 	print_man_head(m, n, h);
136 	print_tagq(h, t);
137 	t = print_otag(h, TAG_BODY, 0, NULL);
138 
139 	tag.key = ATTR_CLASS;
140 	tag.val = "body";
141 	print_otag(h, TAG_DIV, 1, &tag);
142 
143 	print_man_nodelist(m, n, h);
144 
145 	print_tagq(h, t);
146 }
147 
148 
149 /* ARGSUSED */
150 static void
151 print_man_head(MAN_ARGS)
152 {
153 
154 	print_gen_head(h);
155 	bufinit(h);
156 	buffmt(h, "%s(%d)", m->title, m->msec);
157 
158 	print_otag(h, TAG_TITLE, 0, NULL);
159 	print_text(h, h->buf);
160 }
161 
162 
163 static void
164 print_man_nodelist(MAN_ARGS)
165 {
166 
167 	print_man_node(m, n, h);
168 	if (n->next)
169 		print_man_nodelist(m, n->next, h);
170 }
171 
172 
173 static void
174 print_man_node(MAN_ARGS)
175 {
176 	int		 child;
177 	struct tag	*t;
178 
179 	child = 1;
180 	t = h->tags.head;
181 
182 	bufinit(h);
183 
184 	switch (n->type) {
185 	case (MAN_ROOT):
186 		child = man_root_pre(m, n, h);
187 		break;
188 	case (MAN_TEXT):
189 		print_text(h, n->string);
190 		return;
191 	default:
192 		/*
193 		 * Close out scope of font prior to opening a macro
194 		 * scope.  Assert that the metafont is on the top of the
195 		 * stack (it's never nested).
196 		 */
197 		if (h->metaf) {
198 			assert(h->metaf == t);
199 			print_tagq(h, h->metaf);
200 			assert(NULL == h->metaf);
201 			t = h->tags.head;
202 		}
203 		if (mans[n->tok].pre)
204 			child = (*mans[n->tok].pre)(m, n, h);
205 		break;
206 	}
207 
208 	if (child && n->child)
209 		print_man_nodelist(m, n->child, h);
210 
211 	/* This will automatically close out any font scope. */
212 	print_stagq(h, t);
213 
214 	bufinit(h);
215 
216 	switch (n->type) {
217 	case (MAN_ROOT):
218 		man_root_post(m, n, h);
219 		break;
220 	case (MAN_TEXT):
221 		break;
222 	default:
223 		if (mans[n->tok].post)
224 			(*mans[n->tok].post)(m, n, h);
225 		break;
226 	}
227 }
228 
229 
230 static int
231 a2width(const struct man_node *n, struct roffsu *su)
232 {
233 
234 	if (MAN_TEXT != n->type)
235 		return(0);
236 	if (a2roffsu(n->string, su, SCALE_BU))
237 		return(1);
238 
239 	return(0);
240 }
241 
242 
243 /* ARGSUSED */
244 static int
245 man_root_pre(MAN_ARGS)
246 {
247 	struct htmlpair	 tag[3];
248 	struct tag	*t, *tt;
249 	char		 b[BUFSIZ], title[BUFSIZ];
250 
251 	b[0] = 0;
252 	if (m->vol)
253 		(void)strlcat(b, m->vol, BUFSIZ);
254 
255 	snprintf(title, BUFSIZ - 1, "%s(%d)", m->title, m->msec);
256 
257 	PAIR_CLASS_INIT(&tag[0], "header");
258 	bufcat_style(h, "width", "100%");
259 	PAIR_STYLE_INIT(&tag[1], h);
260 	PAIR_SUMMARY_INIT(&tag[2], "header");
261 
262 	t = print_otag(h, TAG_TABLE, 3, tag);
263 	tt = print_otag(h, TAG_TR, 0, NULL);
264 
265 	bufinit(h);
266 	bufcat_style(h, "width", "10%");
267 	PAIR_STYLE_INIT(&tag[0], h);
268 	print_otag(h, TAG_TD, 1, tag);
269 	print_text(h, title);
270 	print_stagq(h, tt);
271 
272 	bufinit(h);
273 	bufcat_style(h, "width", "80%");
274 	bufcat_style(h, "white-space", "nowrap");
275 	bufcat_style(h, "text-align", "center");
276 	PAIR_STYLE_INIT(&tag[0], h);
277 	print_otag(h, TAG_TD, 1, tag);
278 	print_text(h, b);
279 	print_stagq(h, tt);
280 
281 	bufinit(h);
282 	bufcat_style(h, "width", "10%");
283 	bufcat_style(h, "text-align", "right");
284 	PAIR_STYLE_INIT(&tag[0], h);
285 	print_otag(h, TAG_TD, 1, tag);
286 	print_text(h, title);
287 	print_tagq(h, t);
288 	return(1);
289 }
290 
291 
292 /* ARGSUSED */
293 static void
294 man_root_post(MAN_ARGS)
295 {
296 	struct htmlpair	 tag[3];
297 	struct tag	*t, *tt;
298 	char		 b[DATESIZ];
299 
300 	time2a(m->date, b, DATESIZ);
301 
302 	PAIR_CLASS_INIT(&tag[0], "footer");
303 	bufcat_style(h, "width", "100%");
304 	PAIR_STYLE_INIT(&tag[1], h);
305 	PAIR_SUMMARY_INIT(&tag[2], "footer");
306 
307 	t = print_otag(h, TAG_TABLE, 3, tag);
308 	tt = print_otag(h, TAG_TR, 0, NULL);
309 
310 	bufinit(h);
311 	bufcat_style(h, "width", "50%");
312 	PAIR_STYLE_INIT(&tag[0], h);
313 	print_otag(h, TAG_TD, 1, tag);
314 	print_text(h, b);
315 	print_stagq(h, tt);
316 
317 	bufinit(h);
318 	bufcat_style(h, "width", "50%");
319 	bufcat_style(h, "text-align", "right");
320 	PAIR_STYLE_INIT(&tag[0], h);
321 	print_otag(h, TAG_TD, 1, tag);
322 	if (m->source)
323 		print_text(h, m->source);
324 	print_tagq(h, t);
325 }
326 
327 
328 
329 /* ARGSUSED */
330 static int
331 man_br_pre(MAN_ARGS)
332 {
333 	struct roffsu	 su;
334 	struct htmlpair	 tag;
335 
336 	SCALE_VS_INIT(&su, 1);
337 
338 	if (MAN_sp == n->tok && n->child)
339 		a2roffsu(n->child->string, &su, SCALE_VS);
340 	else if (MAN_br == n->tok)
341 		su.scale = 0;
342 
343 	bufcat_su(h, "height", &su);
344 	PAIR_STYLE_INIT(&tag, h);
345 	print_otag(h, TAG_DIV, 1, &tag);
346 
347 	/* So the div isn't empty: */
348 	print_text(h, "\\~");
349 
350 	return(0);
351 }
352 
353 
354 /* ARGSUSED */
355 static int
356 man_SH_pre(MAN_ARGS)
357 {
358 	struct htmlpair	 tag[2];
359 	struct roffsu	 su;
360 
361 	if (MAN_BODY == n->type) {
362 		SCALE_HS_INIT(&su, INDENT);
363 		bufcat_su(h, "margin-left", &su);
364 		PAIR_CLASS_INIT(&tag[0], "sec-body");
365 		PAIR_STYLE_INIT(&tag[1], h);
366 		print_otag(h, TAG_DIV, 2, tag);
367 		return(1);
368 	} else if (MAN_BLOCK == n->type) {
369 		PAIR_CLASS_INIT(&tag[0], "sec-block");
370 		if (n->prev && MAN_SH == n->prev->tok)
371 			if (NULL == n->prev->body->child) {
372 				print_otag(h, TAG_DIV, 1, tag);
373 				return(1);
374 			}
375 
376 		SCALE_VS_INIT(&su, 1);
377 		bufcat_su(h, "margin-top", &su);
378 		if (NULL == n->next)
379 			bufcat_su(h, "margin-bottom", &su);
380 		PAIR_STYLE_INIT(&tag[1], h);
381 		print_otag(h, TAG_DIV, 2, tag);
382 		return(1);
383 	}
384 
385 	PAIR_CLASS_INIT(&tag[0], "sec-head");
386 	print_otag(h, TAG_DIV, 1, tag);
387 	return(1);
388 }
389 
390 
391 /* ARGSUSED */
392 static int
393 man_alt_pre(MAN_ARGS)
394 {
395 	const struct man_node	*nn;
396 	struct tag		*t;
397 	int			 i;
398 	enum htmlfont		 fp;
399 
400 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
401 		switch (n->tok) {
402 		case (MAN_BI):
403 			fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
404 			break;
405 		case (MAN_IB):
406 			fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
407 			break;
408 		case (MAN_RI):
409 			fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
410 			break;
411 		case (MAN_IR):
412 			fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
413 			break;
414 		case (MAN_BR):
415 			fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
416 			break;
417 		case (MAN_RB):
418 			fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
419 			break;
420 		default:
421 			abort();
422 			/* NOTREACHED */
423 		}
424 
425 		if (i)
426 			h->flags |= HTML_NOSPACE;
427 
428 		/*
429 		 * Open and close the scope with each argument, so that
430 		 * internal \f escapes, which are common, are also
431 		 * closed out with the scope.
432 		 */
433 		t = print_ofont(h, fp);
434 		print_man_node(m, nn, h);
435 		print_tagq(h, t);
436 	}
437 
438 	return(0);
439 }
440 
441 
442 /* ARGSUSED */
443 static int
444 man_SB_pre(MAN_ARGS)
445 {
446 	struct htmlpair	 tag;
447 
448 	/* FIXME: print_ofont(). */
449 	PAIR_CLASS_INIT(&tag, "small bold");
450 	print_otag(h, TAG_SPAN, 1, &tag);
451 	return(1);
452 }
453 
454 
455 /* ARGSUSED */
456 static int
457 man_SM_pre(MAN_ARGS)
458 {
459 	struct htmlpair	 tag;
460 
461 	PAIR_CLASS_INIT(&tag, "small");
462 	print_otag(h, TAG_SPAN, 1, &tag);
463 	return(1);
464 }
465 
466 
467 /* ARGSUSED */
468 static int
469 man_SS_pre(MAN_ARGS)
470 {
471 	struct htmlpair	 tag[3];
472 	struct roffsu	 su;
473 
474 	SCALE_VS_INIT(&su, 1);
475 
476 	if (MAN_BODY == n->type) {
477 		PAIR_CLASS_INIT(&tag[0], "ssec-body");
478 		if (n->parent->next && n->child) {
479 			bufcat_su(h, "margin-bottom", &su);
480 			PAIR_STYLE_INIT(&tag[1], h);
481 			print_otag(h, TAG_DIV, 2, tag);
482 			return(1);
483 		}
484 
485 		print_otag(h, TAG_DIV, 1, tag);
486 		return(1);
487 	} else if (MAN_BLOCK == n->type) {
488 		PAIR_CLASS_INIT(&tag[0], "ssec-block");
489 		if (n->prev && MAN_SS == n->prev->tok)
490 			if (n->prev->body->child) {
491 				bufcat_su(h, "margin-top", &su);
492 				PAIR_STYLE_INIT(&tag[1], h);
493 				print_otag(h, TAG_DIV, 2, tag);
494 				return(1);
495 			}
496 
497 		print_otag(h, TAG_DIV, 1, tag);
498 		return(1);
499 	}
500 
501 	SCALE_HS_INIT(&su, INDENT - HALFINDENT);
502 	bufcat_su(h, "margin-left", &su);
503 	PAIR_CLASS_INIT(&tag[0], "ssec-head");
504 	PAIR_STYLE_INIT(&tag[1], h);
505 	print_otag(h, TAG_DIV, 2, tag);
506 	return(1);
507 }
508 
509 
510 /* ARGSUSED */
511 static int
512 man_PP_pre(MAN_ARGS)
513 {
514 	struct htmlpair	 tag;
515 	struct roffsu	 su;
516 	int		 i;
517 
518 	if (MAN_BLOCK != n->type)
519 		return(1);
520 
521 	i = 0;
522 
523 	if (MAN_ROOT == n->parent->type) {
524 		SCALE_HS_INIT(&su, INDENT);
525 		bufcat_su(h, "margin-left", &su);
526 		i = 1;
527 	}
528 	if (n->prev) {
529 		SCALE_VS_INIT(&su, 1);
530 		bufcat_su(h, "margin-top", &su);
531 		i = 1;
532 	}
533 
534 	PAIR_STYLE_INIT(&tag, h);
535 	print_otag(h, TAG_DIV, i, &tag);
536 	return(1);
537 }
538 
539 
540 /* ARGSUSED */
541 static int
542 man_IP_pre(MAN_ARGS)
543 {
544 	struct roffsu		 su;
545 	struct htmlpair	 	 tag;
546 	const struct man_node	*nn;
547 	int			 width;
548 
549 	/*
550 	 * This scattering of 1-BU margins and pads is to make sure that
551 	 * when text overruns its box, the subsequent text isn't flush
552 	 * up against it.  However, the rest of the right-hand box must
553 	 * also be adjusted in consideration of this 1-BU space.
554 	 */
555 
556 	if (MAN_BODY == n->type) {
557 		SCALE_HS_INIT(&su, INDENT);
558 		bufcat_su(h, "margin-left", &su);
559 		PAIR_STYLE_INIT(&tag, h);
560 		print_otag(h, TAG_DIV, 1, &tag);
561 		return(1);
562 	}
563 
564 	nn = MAN_BLOCK == n->type ?
565 		n->head->child : n->parent->head->child;
566 
567 	SCALE_HS_INIT(&su, INDENT);
568 	width = 0;
569 
570 	if (MAN_IP == n->tok && NULL != nn)
571 		if (NULL != (nn = nn->next)) {
572 			for ( ; nn->next; nn = nn->next)
573 				/* Do nothing. */ ;
574 			width = a2width(nn, &su);
575 		}
576 
577 	if (MAN_TP == n->tok && NULL != nn)
578 		width = a2width(nn, &su);
579 
580 	if (MAN_BLOCK == n->type) {
581 		bufcat_su(h, "margin-left", &su);
582 		SCALE_VS_INIT(&su, 1);
583 		bufcat_su(h, "margin-top", &su);
584 		bufcat_style(h, "clear", "both");
585 		PAIR_STYLE_INIT(&tag, h);
586 		print_otag(h, TAG_DIV, 1, &tag);
587 		return(1);
588 	}
589 
590 	bufcat_su(h, "min-width", &su);
591 	SCALE_INVERT(&su);
592 	bufcat_su(h, "margin-left", &su);
593 	SCALE_HS_INIT(&su, 1);
594 	bufcat_su(h, "margin-right", &su);
595 	bufcat_style(h, "clear", "left");
596 
597 	if (n->next && n->next->child)
598 		bufcat_style(h, "float", "left");
599 
600 	PAIR_STYLE_INIT(&tag, h);
601 	print_otag(h, TAG_DIV, 1, &tag);
602 
603 	/* With a length string, manually omit the last child. */
604 
605 	if ( ! width)
606 		return(1);
607 
608 	if (MAN_IP == n->tok)
609 		for (nn = n->child; nn->next; nn = nn->next)
610 			print_man_node(m, nn, h);
611 	if (MAN_TP == n->tok)
612 		for (nn = n->child->next; nn; nn = nn->next)
613 			print_man_node(m, nn, h);
614 
615 	return(0);
616 }
617 
618 
619 /* ARGSUSED */
620 static int
621 man_HP_pre(MAN_ARGS)
622 {
623 	const struct man_node	*nn;
624 	struct htmlpair	 	 tag;
625 	struct roffsu		 su;
626 
627 	if (MAN_HEAD == n->type)
628 		return(0);
629 
630 	nn = MAN_BLOCK == n->type ?
631 		n->head->child : n->parent->head->child;
632 
633 	SCALE_HS_INIT(&su, INDENT);
634 
635 	if (NULL != nn)
636 		(void)a2width(nn, &su);
637 
638 	if (MAN_BLOCK == n->type) {
639 		bufcat_su(h, "margin-left", &su);
640 		SCALE_VS_INIT(&su, 1);
641 		bufcat_su(h, "margin-top", &su);
642 		bufcat_style(h, "clear", "both");
643 		PAIR_STYLE_INIT(&tag, h);
644 		print_otag(h, TAG_DIV, 1, &tag);
645 		return(1);
646 	}
647 
648 	bufcat_su(h, "margin-left", &su);
649 	SCALE_INVERT(&su);
650 	bufcat_su(h, "text-indent", &su);
651 
652 	PAIR_STYLE_INIT(&tag, h);
653 	print_otag(h, TAG_DIV, 1, &tag);
654 	return(1);
655 }
656 
657 
658 /* ARGSUSED */
659 static int
660 man_B_pre(MAN_ARGS)
661 {
662 
663 	print_ofont(h, HTMLFONT_BOLD);
664 	return(1);
665 }
666 
667 
668 /* ARGSUSED */
669 static int
670 man_I_pre(MAN_ARGS)
671 {
672 
673 	print_ofont(h, HTMLFONT_ITALIC);
674 	return(1);
675 }
676 
677 
678 /* ARGSUSED */
679 static int
680 man_ign_pre(MAN_ARGS)
681 {
682 
683 	return(0);
684 }
685 
686 
687 /* ARGSUSED */
688 static int
689 man_RS_pre(MAN_ARGS)
690 {
691 	struct htmlpair	 tag;
692 	struct roffsu	 su;
693 
694 	if (MAN_HEAD == n->type)
695 		return(0);
696 	else if (MAN_BODY == n->type)
697 		return(1);
698 
699 	SCALE_HS_INIT(&su, INDENT);
700 	bufcat_su(h, "margin-left", &su);
701 
702 	if (n->head->child) {
703 		SCALE_VS_INIT(&su, 1);
704 		a2width(n->head->child, &su);
705 		bufcat_su(h, "margin-top", &su);
706 	}
707 
708 	PAIR_STYLE_INIT(&tag, h);
709 	print_otag(h, TAG_DIV, 1, &tag);
710 	return(1);
711 }
712