]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
Initial step in fixing badness reported by Sascha Wildner (wip).
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.30 2010/03/24 20:10:53 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 { man_br_pre, NULL }, /* Sp */
107 { man_ign_pre, NULL }, /* Vb */
108 { NULL, NULL }, /* Ve */
109 { man_ign_pre, NULL }, /* de */
110 { man_ign_pre, NULL }, /* dei */
111 { man_ign_pre, NULL }, /* am */
112 { man_ign_pre, NULL }, /* ami */
113 { man_ign_pre, NULL }, /* ig */
114 { NULL, NULL }, /* . */
115 };
116
117
118 void
119 html_man(void *arg, const struct man *m)
120 {
121 struct html *h;
122 struct tag *t;
123
124 h = (struct html *)arg;
125
126 print_gen_decls(h);
127
128 t = print_otag(h, TAG_HTML, 0, NULL);
129 print_man(man_meta(m), man_node(m), h);
130 print_tagq(h, t);
131
132 printf("\n");
133 }
134
135
136 static void
137 print_man(MAN_ARGS)
138 {
139 struct tag *t;
140 struct htmlpair tag;
141
142 t = print_otag(h, TAG_HEAD, 0, NULL);
143
144 print_man_head(m, n, h);
145 print_tagq(h, t);
146 t = print_otag(h, TAG_BODY, 0, NULL);
147
148 tag.key = ATTR_CLASS;
149 tag.val = "body";
150 print_otag(h, TAG_DIV, 1, &tag);
151
152 print_man_nodelist(m, n, h);
153
154 print_tagq(h, t);
155 }
156
157
158 /* ARGSUSED */
159 static void
160 print_man_head(MAN_ARGS)
161 {
162
163 print_gen_head(h);
164 bufinit(h);
165 buffmt(h, "%s(%d)", m->title, m->msec);
166
167 print_otag(h, TAG_TITLE, 0, NULL);
168 print_text(h, h->buf);
169 }
170
171
172 static void
173 print_man_nodelist(MAN_ARGS)
174 {
175
176 print_man_node(m, n, h);
177 if (n->next)
178 print_man_nodelist(m, n->next, h);
179 }
180
181
182 static void
183 print_man_node(MAN_ARGS)
184 {
185 int child;
186 struct tag *t;
187
188 child = 1;
189 t = h->tags.head;
190
191 bufinit(h);
192
193 /*
194 * FIXME: embedded elements within next-line scopes (e.g., `br'
195 * within an empty `B') will cause formatting to be forgotten
196 * due to scope closing out.
197 */
198
199 switch (n->type) {
200 case (MAN_ROOT):
201 child = man_root_pre(m, n, h);
202 break;
203 case (MAN_TEXT):
204 print_text(h, n->string);
205 return;
206 default:
207 /*
208 * Close out scope of font prior to opening a macro
209 * scope. Assert that the metafont is on the top of the
210 * stack (it's never nested).
211 */
212 if (h->metaf) {
213 assert(h->metaf == t);
214 print_tagq(h, h->metaf);
215 assert(NULL == h->metaf);
216 t = h->tags.head;
217 }
218 if (mans[n->tok].pre)
219 child = (*mans[n->tok].pre)(m, n, h);
220 break;
221 }
222
223 if (child && n->child)
224 print_man_nodelist(m, n->child, h);
225
226 /* This will automatically close out any font scope. */
227 print_stagq(h, t);
228
229 bufinit(h);
230
231 switch (n->type) {
232 case (MAN_ROOT):
233 man_root_post(m, n, h);
234 break;
235 case (MAN_TEXT):
236 break;
237 default:
238 if (mans[n->tok].post)
239 (*mans[n->tok].post)(m, n, h);
240 break;
241 }
242 }
243
244
245 static int
246 a2width(const struct man_node *n, struct roffsu *su)
247 {
248
249 if (MAN_TEXT != n->type)
250 return(0);
251 if (a2roffsu(n->string, su, SCALE_BU))
252 return(1);
253
254 return(0);
255 }
256
257
258 /* ARGSUSED */
259 static int
260 man_root_pre(MAN_ARGS)
261 {
262 struct htmlpair tag[3];
263 struct tag *t, *tt;
264 char b[BUFSIZ], title[BUFSIZ];
265
266 b[0] = 0;
267 if (m->vol)
268 (void)strlcat(b, m->vol, BUFSIZ);
269
270 snprintf(title, BUFSIZ - 1, "%s(%d)", m->title, m->msec);
271
272 PAIR_CLASS_INIT(&tag[0], "header");
273 bufcat_style(h, "width", "100%");
274 PAIR_STYLE_INIT(&tag[1], h);
275 PAIR_SUMMARY_INIT(&tag[2], "header");
276
277 t = print_otag(h, TAG_TABLE, 3, tag);
278 tt = print_otag(h, TAG_TR, 0, NULL);
279
280 bufinit(h);
281 bufcat_style(h, "width", "10%");
282 PAIR_STYLE_INIT(&tag[0], h);
283 print_otag(h, TAG_TD, 1, tag);
284 print_text(h, title);
285 print_stagq(h, tt);
286
287 bufinit(h);
288 bufcat_style(h, "width", "80%");
289 bufcat_style(h, "white-space", "nowrap");
290 bufcat_style(h, "text-align", "center");
291 PAIR_STYLE_INIT(&tag[0], h);
292 print_otag(h, TAG_TD, 1, tag);
293 print_text(h, b);
294 print_stagq(h, tt);
295
296 bufinit(h);
297 bufcat_style(h, "width", "10%");
298 bufcat_style(h, "text-align", "right");
299 PAIR_STYLE_INIT(&tag[0], h);
300 print_otag(h, TAG_TD, 1, tag);
301 print_text(h, title);
302 print_tagq(h, t);
303 return(1);
304 }
305
306
307 /* ARGSUSED */
308 static void
309 man_root_post(MAN_ARGS)
310 {
311 struct htmlpair tag[3];
312 struct tag *t, *tt;
313 char b[DATESIZ];
314
315 time2a(m->date, b, DATESIZ);
316
317 PAIR_CLASS_INIT(&tag[0], "footer");
318 bufcat_style(h, "width", "100%");
319 PAIR_STYLE_INIT(&tag[1], h);
320 PAIR_SUMMARY_INIT(&tag[2], "footer");
321
322 t = print_otag(h, TAG_TABLE, 3, tag);
323 tt = print_otag(h, TAG_TR, 0, NULL);
324
325 bufinit(h);
326 bufcat_style(h, "width", "50%");
327 PAIR_STYLE_INIT(&tag[0], h);
328 print_otag(h, TAG_TD, 1, tag);
329 print_text(h, b);
330 print_stagq(h, tt);
331
332 bufinit(h);
333 bufcat_style(h, "width", "50%");
334 bufcat_style(h, "text-align", "right");
335 PAIR_STYLE_INIT(&tag[0], h);
336 print_otag(h, TAG_TD, 1, tag);
337 if (m->source)
338 print_text(h, m->source);
339 print_tagq(h, t);
340 }
341
342
343
344 /* ARGSUSED */
345 static int
346 man_br_pre(MAN_ARGS)
347 {
348 struct roffsu su;
349 struct htmlpair tag;
350
351 SCALE_VS_INIT(&su, 1);
352
353 switch (n->tok) {
354 case (MAN_Sp):
355 SCALE_VS_INIT(&su, 0.5);
356 break;
357 case (MAN_sp):
358 if (n->child)
359 a2roffsu(n->child->string, &su, SCALE_VS);
360 break;
361 default:
362 su.scale = 0;
363 break;
364 }
365
366 bufcat_su(h, "height", &su);
367 PAIR_STYLE_INIT(&tag, h);
368 print_otag(h, TAG_DIV, 1, &tag);
369
370 /* So the div isn't empty: */
371 print_text(h, "\\~");
372
373 return(0);
374 }
375
376
377 /* ARGSUSED */
378 static int
379 man_SH_pre(MAN_ARGS)
380 {
381 struct htmlpair tag[2];
382 struct roffsu su;
383
384 if (MAN_BODY == n->type) {
385 SCALE_HS_INIT(&su, INDENT);
386 bufcat_su(h, "margin-left", &su);
387 PAIR_CLASS_INIT(&tag[0], "sec-body");
388 PAIR_STYLE_INIT(&tag[1], h);
389 print_otag(h, TAG_DIV, 2, tag);
390 return(1);
391 } else if (MAN_BLOCK == n->type) {
392 PAIR_CLASS_INIT(&tag[0], "sec-block");
393 if (n->prev && MAN_SH == n->prev->tok)
394 if (NULL == n->prev->body->child) {
395 print_otag(h, TAG_DIV, 1, tag);
396 return(1);
397 }
398
399 SCALE_VS_INIT(&su, 1);
400 bufcat_su(h, "margin-top", &su);
401 if (NULL == n->next)
402 bufcat_su(h, "margin-bottom", &su);
403 PAIR_STYLE_INIT(&tag[1], h);
404 print_otag(h, TAG_DIV, 2, tag);
405 return(1);
406 }
407
408 PAIR_CLASS_INIT(&tag[0], "sec-head");
409 print_otag(h, TAG_DIV, 1, tag);
410 return(1);
411 }
412
413
414 /* ARGSUSED */
415 static int
416 man_alt_pre(MAN_ARGS)
417 {
418 const struct man_node *nn;
419 struct tag *t;
420 int i;
421 enum htmlfont fp;
422
423 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
424 switch (n->tok) {
425 case (MAN_BI):
426 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
427 break;
428 case (MAN_IB):
429 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
430 break;
431 case (MAN_RI):
432 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
433 break;
434 case (MAN_IR):
435 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
436 break;
437 case (MAN_BR):
438 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
439 break;
440 case (MAN_RB):
441 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
442 break;
443 default:
444 abort();
445 /* NOTREACHED */
446 }
447
448 if (i)
449 h->flags |= HTML_NOSPACE;
450
451 /*
452 * Open and close the scope with each argument, so that
453 * internal \f escapes, which are common, are also
454 * closed out with the scope.
455 */
456 t = print_ofont(h, fp);
457 print_man_node(m, nn, h);
458 print_tagq(h, t);
459 }
460
461 return(0);
462 }
463
464
465 /* ARGSUSED */
466 static int
467 man_SB_pre(MAN_ARGS)
468 {
469 struct htmlpair tag;
470
471 /* FIXME: print_ofont(). */
472 PAIR_CLASS_INIT(&tag, "small bold");
473 print_otag(h, TAG_SPAN, 1, &tag);
474 return(1);
475 }
476
477
478 /* ARGSUSED */
479 static int
480 man_SM_pre(MAN_ARGS)
481 {
482 struct htmlpair tag;
483
484 PAIR_CLASS_INIT(&tag, "small");
485 print_otag(h, TAG_SPAN, 1, &tag);
486 return(1);
487 }
488
489
490 /* ARGSUSED */
491 static int
492 man_SS_pre(MAN_ARGS)
493 {
494 struct htmlpair tag[3];
495 struct roffsu su;
496
497 SCALE_VS_INIT(&su, 1);
498
499 if (MAN_BODY == n->type) {
500 PAIR_CLASS_INIT(&tag[0], "ssec-body");
501 if (n->parent->next && n->child) {
502 bufcat_su(h, "margin-bottom", &su);
503 PAIR_STYLE_INIT(&tag[1], h);
504 print_otag(h, TAG_DIV, 2, tag);
505 return(1);
506 }
507
508 print_otag(h, TAG_DIV, 1, tag);
509 return(1);
510 } else if (MAN_BLOCK == n->type) {
511 PAIR_CLASS_INIT(&tag[0], "ssec-block");
512 if (n->prev && MAN_SS == n->prev->tok)
513 if (n->prev->body->child) {
514 bufcat_su(h, "margin-top", &su);
515 PAIR_STYLE_INIT(&tag[1], h);
516 print_otag(h, TAG_DIV, 2, tag);
517 return(1);
518 }
519
520 print_otag(h, TAG_DIV, 1, tag);
521 return(1);
522 }
523
524 SCALE_HS_INIT(&su, INDENT - HALFINDENT);
525 bufcat_su(h, "margin-left", &su);
526 PAIR_CLASS_INIT(&tag[0], "ssec-head");
527 PAIR_STYLE_INIT(&tag[1], h);
528 print_otag(h, TAG_DIV, 2, tag);
529 return(1);
530 }
531
532
533 /* ARGSUSED */
534 static int
535 man_PP_pre(MAN_ARGS)
536 {
537 struct htmlpair tag;
538 struct roffsu su;
539 int i;
540
541 if (MAN_BLOCK != n->type)
542 return(1);
543
544 i = 0;
545
546 if (MAN_ROOT == n->parent->type) {
547 SCALE_HS_INIT(&su, INDENT);
548 bufcat_su(h, "margin-left", &su);
549 i = 1;
550 }
551 if (n->prev) {
552 SCALE_VS_INIT(&su, 1);
553 bufcat_su(h, "margin-top", &su);
554 i = 1;
555 }
556
557 PAIR_STYLE_INIT(&tag, h);
558 print_otag(h, TAG_DIV, i, &tag);
559 return(1);
560 }
561
562
563 /* ARGSUSED */
564 static int
565 man_IP_pre(MAN_ARGS)
566 {
567 struct roffsu su;
568 struct htmlpair tag;
569 const struct man_node *nn;
570 int width;
571
572 /*
573 * This scattering of 1-BU margins and pads is to make sure that
574 * when text overruns its box, the subsequent text isn't flush
575 * up against it. However, the rest of the right-hand box must
576 * also be adjusted in consideration of this 1-BU space.
577 */
578
579 if (MAN_BODY == n->type) {
580 SCALE_HS_INIT(&su, INDENT);
581 bufcat_su(h, "margin-left", &su);
582 PAIR_STYLE_INIT(&tag, h);
583 print_otag(h, TAG_DIV, 1, &tag);
584 return(1);
585 }
586
587 nn = MAN_BLOCK == n->type ?
588 n->head->child : n->parent->head->child;
589
590 SCALE_HS_INIT(&su, INDENT);
591 width = 0;
592
593 /* Width is the last token. */
594
595 if (MAN_IP == n->tok && NULL != nn)
596 if (NULL != (nn = nn->next)) {
597 for ( ; nn->next; nn = nn->next)
598 /* Do nothing. */ ;
599 width = a2width(nn, &su);
600 }
601
602 /* Width is the first token. */
603
604 if (MAN_TP == n->tok && NULL != nn) {
605 /* Skip past non-text children. */
606 while (nn && MAN_TEXT != nn->type)
607 nn = nn->next;
608 if (nn)
609 width = a2width(nn, &su);
610 }
611
612 if (MAN_BLOCK == n->type) {
613 bufcat_su(h, "margin-left", &su);
614 SCALE_VS_INIT(&su, 1);
615 bufcat_su(h, "margin-top", &su);
616 bufcat_style(h, "clear", "both");
617 PAIR_STYLE_INIT(&tag, h);
618 print_otag(h, TAG_DIV, 1, &tag);
619 return(1);
620 }
621
622 bufcat_su(h, "min-width", &su);
623 SCALE_INVERT(&su);
624 bufcat_su(h, "margin-left", &su);
625 SCALE_HS_INIT(&su, 1);
626 bufcat_su(h, "margin-right", &su);
627 bufcat_style(h, "clear", "left");
628
629 if (n->next && n->next->child)
630 bufcat_style(h, "float", "left");
631
632 PAIR_STYLE_INIT(&tag, h);
633 print_otag(h, TAG_DIV, 1, &tag);
634
635 /*
636 * Without a length string, we can print all of our children.
637 */
638
639 if ( ! width)
640 return(1);
641
642 /*
643 * When a length has been specified, we need to carefully print
644 * our child context: IP gets all children printed but the last
645 * (the width), while TP gets all children printed but the first
646 * (the width).
647 */
648
649 if (MAN_IP == n->tok)
650 for (nn = n->child; nn->next; nn = nn->next)
651 print_man_node(m, nn, h);
652 if (MAN_TP == n->tok)
653 for (nn = n->child->next; nn; nn = nn->next)
654 print_man_node(m, nn, h);
655
656 return(0);
657 }
658
659
660 /* ARGSUSED */
661 static int
662 man_HP_pre(MAN_ARGS)
663 {
664 const struct man_node *nn;
665 struct htmlpair tag;
666 struct roffsu su;
667
668 if (MAN_HEAD == n->type)
669 return(0);
670
671 nn = MAN_BLOCK == n->type ?
672 n->head->child : n->parent->head->child;
673
674 SCALE_HS_INIT(&su, INDENT);
675
676 if (NULL != nn)
677 (void)a2width(nn, &su);
678
679 if (MAN_BLOCK == n->type) {
680 bufcat_su(h, "margin-left", &su);
681 SCALE_VS_INIT(&su, 1);
682 bufcat_su(h, "margin-top", &su);
683 bufcat_style(h, "clear", "both");
684 PAIR_STYLE_INIT(&tag, h);
685 print_otag(h, TAG_DIV, 1, &tag);
686 return(1);
687 }
688
689 bufcat_su(h, "margin-left", &su);
690 SCALE_INVERT(&su);
691 bufcat_su(h, "text-indent", &su);
692
693 PAIR_STYLE_INIT(&tag, h);
694 print_otag(h, TAG_DIV, 1, &tag);
695 return(1);
696 }
697
698
699 /* ARGSUSED */
700 static int
701 man_B_pre(MAN_ARGS)
702 {
703
704 print_ofont(h, HTMLFONT_BOLD);
705 return(1);
706 }
707
708
709 /* ARGSUSED */
710 static int
711 man_I_pre(MAN_ARGS)
712 {
713
714 print_ofont(h, HTMLFONT_ITALIC);
715 return(1);
716 }
717
718
719 /* ARGSUSED */
720 static int
721 man_ign_pre(MAN_ARGS)
722 {
723
724 return(0);
725 }
726
727
728 /* ARGSUSED */
729 static int
730 man_RS_pre(MAN_ARGS)
731 {
732 struct htmlpair tag;
733 struct roffsu su;
734
735 if (MAN_HEAD == n->type)
736 return(0);
737 else if (MAN_BODY == n->type)
738 return(1);
739
740 SCALE_HS_INIT(&su, INDENT);
741 bufcat_su(h, "margin-left", &su);
742
743 if (n->head->child) {
744 SCALE_VS_INIT(&su, 1);
745 a2width(n->head->child, &su);
746 bufcat_su(h, "margin-top", &su);
747 }
748
749 PAIR_STYLE_INIT(&tag, h);
750 print_otag(h, TAG_DIV, 1, &tag);
751 return(1);
752 }