]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
162f3045efd3b2facbaaba5d0645baa27329d841
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.40 2010/06/27 16:18:13 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 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 "regs.h"
33 #include "man.h"
34 #include "main.h"
35
36 /* TODO: preserve ident widths. */
37 /* FIXME: have PD set the default vspace width. */
38
39 #define INDENT 5
40 #define HALFINDENT 3
41
42 #define MAN_ARGS const struct man_meta *m, \
43 const struct man_node *n, \
44 struct html *h
45
46 struct htmlman {
47 int (*pre)(MAN_ARGS);
48 int (*post)(MAN_ARGS);
49 };
50
51 static void print_man(MAN_ARGS);
52 static void print_man_head(MAN_ARGS);
53 static void print_man_nodelist(MAN_ARGS);
54 static void print_man_node(MAN_ARGS);
55
56 static int a2width(const struct man_node *,
57 struct roffsu *);
58
59 static int man_alt_pre(MAN_ARGS);
60 static int man_br_pre(MAN_ARGS);
61 static int man_ign_pre(MAN_ARGS);
62 static void man_root_post(MAN_ARGS);
63 static int man_root_pre(MAN_ARGS);
64 static int man_B_pre(MAN_ARGS);
65 static int man_HP_pre(MAN_ARGS);
66 static int man_I_pre(MAN_ARGS);
67 static int man_IP_pre(MAN_ARGS);
68 static int man_PP_pre(MAN_ARGS);
69 static int man_RS_pre(MAN_ARGS);
70 static int man_SB_pre(MAN_ARGS);
71 static int man_SH_pre(MAN_ARGS);
72 static int man_SM_pre(MAN_ARGS);
73 static int man_SS_pre(MAN_ARGS);
74
75 static const struct htmlman mans[MAN_MAX] = {
76 { man_br_pre, NULL }, /* br */
77 { NULL, NULL }, /* TH */
78 { man_SH_pre, NULL }, /* SH */
79 { man_SS_pre, NULL }, /* SS */
80 { man_IP_pre, NULL }, /* TP */
81 { man_PP_pre, NULL }, /* LP */
82 { man_PP_pre, NULL }, /* PP */
83 { man_PP_pre, NULL }, /* P */
84 { man_IP_pre, NULL }, /* IP */
85 { man_HP_pre, NULL }, /* HP */
86 { man_SM_pre, NULL }, /* SM */
87 { man_SB_pre, NULL }, /* SB */
88 { man_alt_pre, NULL }, /* BI */
89 { man_alt_pre, NULL }, /* IB */
90 { man_alt_pre, NULL }, /* BR */
91 { man_alt_pre, NULL }, /* RB */
92 { NULL, NULL }, /* R */
93 { man_B_pre, NULL }, /* B */
94 { man_I_pre, NULL }, /* I */
95 { man_alt_pre, NULL }, /* IR */
96 { man_alt_pre, NULL }, /* RI */
97 { NULL, NULL }, /* na */
98 { NULL, NULL }, /* i */
99 { man_br_pre, NULL }, /* sp */
100 { NULL, NULL }, /* nf */
101 { NULL, NULL }, /* fi */
102 { NULL, NULL }, /* r */
103 { NULL, NULL }, /* RE */
104 { man_RS_pre, NULL }, /* RS */
105 { man_ign_pre, NULL }, /* DT */
106 { man_ign_pre, NULL }, /* UC */
107 { man_ign_pre, NULL }, /* PD */
108 { man_br_pre, NULL }, /* Sp */
109 { man_ign_pre, NULL }, /* Vb */
110 { NULL, NULL }, /* Ve */
111 { man_ign_pre, NULL }, /* AT */
112 };
113
114
115 void
116 html_man(void *arg, const struct man *m)
117 {
118 struct html *h;
119 struct tag *t;
120
121 h = (struct html *)arg;
122
123 print_gen_decls(h);
124
125 t = print_otag(h, TAG_HTML, 0, NULL);
126 print_man(man_meta(m), man_node(m), h);
127 print_tagq(h, t);
128
129 printf("\n");
130 }
131
132
133 static void
134 print_man(MAN_ARGS)
135 {
136 struct tag *t;
137 struct htmlpair tag;
138
139 t = print_otag(h, TAG_HEAD, 0, NULL);
140
141 print_man_head(m, n, h);
142 print_tagq(h, t);
143 t = print_otag(h, TAG_BODY, 0, NULL);
144
145 tag.key = ATTR_CLASS;
146 tag.val = "body";
147 print_otag(h, TAG_DIV, 1, &tag);
148
149 print_man_nodelist(m, n, h);
150
151 print_tagq(h, t);
152 }
153
154
155 /* ARGSUSED */
156 static void
157 print_man_head(MAN_ARGS)
158 {
159
160 print_gen_head(h);
161 bufinit(h);
162 buffmt(h, "%s(%s)", m->title, m->msec);
163
164 print_otag(h, TAG_TITLE, 0, NULL);
165 print_text(h, h->buf);
166 }
167
168
169 static void
170 print_man_nodelist(MAN_ARGS)
171 {
172
173 print_man_node(m, n, h);
174 if (n->next)
175 print_man_nodelist(m, n->next, h);
176 }
177
178
179 static void
180 print_man_node(MAN_ARGS)
181 {
182 int child;
183 struct tag *t;
184
185 child = 1;
186 t = h->tags.head;
187
188 bufinit(h);
189
190 /*
191 * FIXME: embedded elements within next-line scopes (e.g., `br'
192 * within an empty `B') will cause formatting to be forgotten
193 * due to scope closing out.
194 */
195
196 switch (n->type) {
197 case (MAN_ROOT):
198 child = man_root_pre(m, n, h);
199 break;
200 case (MAN_TEXT):
201 print_text(h, n->string);
202 return;
203 default:
204 /*
205 * Close out scope of font prior to opening a macro
206 * scope. Assert that the metafont is on the top of the
207 * stack (it's never nested).
208 */
209 if (h->metaf) {
210 assert(h->metaf == t);
211 print_tagq(h, h->metaf);
212 assert(NULL == h->metaf);
213 t = h->tags.head;
214 }
215 if (mans[n->tok].pre)
216 child = (*mans[n->tok].pre)(m, n, h);
217 break;
218 }
219
220 if (child && n->child)
221 print_man_nodelist(m, n->child, h);
222
223 /* This will automatically close out any font scope. */
224 print_stagq(h, t);
225
226 bufinit(h);
227
228 switch (n->type) {
229 case (MAN_ROOT):
230 man_root_post(m, n, h);
231 break;
232 case (MAN_TEXT):
233 break;
234 default:
235 if (mans[n->tok].post)
236 (*mans[n->tok].post)(m, n, h);
237 break;
238 }
239 }
240
241
242 static int
243 a2width(const struct man_node *n, struct roffsu *su)
244 {
245
246 if (MAN_TEXT != n->type)
247 return(0);
248 if (a2roffsu(n->string, su, SCALE_BU))
249 return(1);
250
251 return(0);
252 }
253
254
255 /* ARGSUSED */
256 static int
257 man_root_pre(MAN_ARGS)
258 {
259 struct htmlpair tag[3];
260 struct tag *t, *tt;
261 char b[BUFSIZ], title[BUFSIZ];
262
263 b[0] = 0;
264 if (m->vol)
265 (void)strlcat(b, m->vol, BUFSIZ);
266
267 snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
268
269 PAIR_CLASS_INIT(&tag[0], "header");
270 bufcat_style(h, "width", "100%");
271 PAIR_STYLE_INIT(&tag[1], h);
272 PAIR_SUMMARY_INIT(&tag[2], "header");
273
274 t = print_otag(h, TAG_TABLE, 3, tag);
275 tt = print_otag(h, TAG_TR, 0, NULL);
276
277 bufinit(h);
278 bufcat_style(h, "width", "10%");
279 PAIR_STYLE_INIT(&tag[0], h);
280 print_otag(h, TAG_TD, 1, tag);
281 print_text(h, title);
282 print_stagq(h, tt);
283
284 bufinit(h);
285 bufcat_style(h, "width", "80%");
286 bufcat_style(h, "white-space", "nowrap");
287 bufcat_style(h, "text-align", "center");
288 PAIR_STYLE_INIT(&tag[0], h);
289 print_otag(h, TAG_TD, 1, tag);
290 print_text(h, b);
291 print_stagq(h, tt);
292
293 bufinit(h);
294 bufcat_style(h, "width", "10%");
295 bufcat_style(h, "text-align", "right");
296 PAIR_STYLE_INIT(&tag[0], h);
297 print_otag(h, TAG_TD, 1, tag);
298 print_text(h, title);
299 print_tagq(h, t);
300 return(1);
301 }
302
303
304 /* ARGSUSED */
305 static void
306 man_root_post(MAN_ARGS)
307 {
308 struct htmlpair tag[3];
309 struct tag *t, *tt;
310 char b[DATESIZ];
311
312 if (m->rawdate)
313 strlcpy(b, m->rawdate, DATESIZ);
314 else
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 }