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