]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
Big check-in of compatibility layer. This should work on most major architectures...
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.25 2010/01/01 17:14:28 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_doctype(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 }