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