]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
3ff82ae942466929aeb8226cbf19de54ae54f1e7
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.13 2009/10/24 05:45:04 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 #include <sys/types.h>
18 #include <sys/queue.h>
19
20 #include <assert.h>
21 #include <ctype.h>
22 #include <err.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "out.h"
28 #include "html.h"
29 #include "man.h"
30 #include "main.h"
31
32 /* TODO: preserve ident widths. */
33 /* FIXME: have PD set the default vspace width. */
34
35 #define INDENT 5
36 #define HALFINDENT 3
37
38 #define MAN_ARGS const struct man_meta *m, \
39 const struct man_node *n, \
40 struct html *h
41
42 struct htmlman {
43 int (*pre)(MAN_ARGS);
44 int (*post)(MAN_ARGS);
45 };
46
47 static void print_man(MAN_ARGS);
48 static void print_man_head(MAN_ARGS);
49 static void print_man_nodelist(MAN_ARGS);
50 static void print_man_node(MAN_ARGS);
51
52 static int a2width(const struct man_node *,
53 struct roffsu *);
54
55 static int man_alt_pre(MAN_ARGS);
56 static int man_br_pre(MAN_ARGS);
57 static int man_ign_pre(MAN_ARGS);
58 static void man_root_post(MAN_ARGS);
59 static int man_root_pre(MAN_ARGS);
60 static int man_B_pre(MAN_ARGS);
61 static int man_HP_pre(MAN_ARGS);
62 static int man_I_pre(MAN_ARGS);
63 static int man_IP_pre(MAN_ARGS);
64 static int man_PP_pre(MAN_ARGS);
65 static int man_RS_pre(MAN_ARGS);
66 static int man_SB_pre(MAN_ARGS);
67 static int man_SH_pre(MAN_ARGS);
68 static int man_SM_pre(MAN_ARGS);
69 static int man_SS_pre(MAN_ARGS);
70
71 #ifdef __linux__
72 extern size_t strlcpy(char *, const char *, size_t);
73 extern size_t strlcat(char *, const char *, size_t);
74 #endif
75
76 static const struct htmlman mans[MAN_MAX] = {
77 { man_br_pre, NULL }, /* br */
78 { NULL, NULL }, /* TH */
79 { man_SH_pre, NULL }, /* SH */
80 { man_SS_pre, NULL }, /* SS */
81 { man_IP_pre, NULL }, /* TP */
82 { man_PP_pre, NULL }, /* LP */
83 { man_PP_pre, NULL }, /* PP */
84 { man_PP_pre, NULL }, /* P */
85 { man_IP_pre, NULL }, /* IP */
86 { man_HP_pre, NULL }, /* HP */
87 { man_SM_pre, NULL }, /* SM */
88 { man_SB_pre, NULL }, /* SB */
89 { man_alt_pre, NULL }, /* BI */
90 { man_alt_pre, NULL }, /* IB */
91 { man_alt_pre, NULL }, /* BR */
92 { man_alt_pre, NULL }, /* RB */
93 { NULL, NULL }, /* R */
94 { man_B_pre, NULL }, /* B */
95 { man_I_pre, NULL }, /* I */
96 { man_alt_pre, NULL }, /* IR */
97 { man_alt_pre, NULL }, /* RI */
98 { NULL, NULL }, /* na */
99 { NULL, NULL }, /* i */
100 { man_br_pre, NULL }, /* sp */
101 { NULL, NULL }, /* nf */
102 { NULL, NULL }, /* fi */
103 { NULL, NULL }, /* r */
104 { NULL, NULL }, /* RE */
105 { man_RS_pre, NULL }, /* RS */
106 { man_ign_pre, NULL }, /* DT */
107 { man_ign_pre, NULL }, /* UC */
108 { man_ign_pre, NULL }, /* PD */
109 };
110
111
112 void
113 html_man(void *arg, const struct man *m)
114 {
115 struct html *h;
116 struct tag *t;
117
118 h = (struct html *)arg;
119
120 print_gen_doctype(h);
121
122 t = print_otag(h, TAG_HTML, 0, NULL);
123 print_man(man_meta(m), man_node(m), h);
124 print_tagq(h, t);
125
126 printf("\n");
127 }
128
129
130 static void
131 print_man(MAN_ARGS)
132 {
133 struct tag *t;
134 struct htmlpair tag;
135
136 t = print_otag(h, TAG_HEAD, 0, NULL);
137
138 print_man_head(m, n, h);
139 print_tagq(h, t);
140 t = print_otag(h, TAG_BODY, 0, NULL);
141
142 tag.key = ATTR_CLASS;
143 tag.val = "body";
144 print_otag(h, TAG_DIV, 1, &tag);
145
146 print_man_nodelist(m, n, h);
147
148 print_tagq(h, t);
149 }
150
151
152 /* ARGSUSED */
153 static void
154 print_man_head(MAN_ARGS)
155 {
156
157 print_gen_head(h);
158 bufinit(h);
159 buffmt(h, "%s(%d)", m->title, m->msec);
160
161 print_otag(h, TAG_TITLE, 0, NULL);
162 print_text(h, h->buf);
163 }
164
165
166 static void
167 print_man_nodelist(MAN_ARGS)
168 {
169
170 print_man_node(m, n, h);
171 if (n->next)
172 print_man_nodelist(m, n->next, h);
173 }
174
175
176 static void
177 print_man_node(MAN_ARGS)
178 {
179 int child;
180 struct tag *t;
181
182 child = 1;
183 t = SLIST_FIRST(&h->tags);
184
185 bufinit(h);
186
187 switch (n->type) {
188 case (MAN_ROOT):
189 child = man_root_pre(m, n, h);
190 break;
191 case (MAN_TEXT):
192 print_text(h, n->string);
193 break;
194 default:
195 if (mans[n->tok].pre)
196 child = (*mans[n->tok].pre)(m, n, h);
197 break;
198 }
199
200 if (child && n->child)
201 print_man_nodelist(m, n->child, h);
202
203 print_stagq(h, t);
204
205 bufinit(h);
206
207 switch (n->type) {
208 case (MAN_ROOT):
209 man_root_post(m, n, h);
210 break;
211 case (MAN_TEXT):
212 break;
213 default:
214 if (mans[n->tok].post)
215 (*mans[n->tok].post)(m, n, h);
216 break;
217 }
218 }
219
220
221 static int
222 a2width(const struct man_node *n, struct roffsu *su)
223 {
224
225 if (MAN_TEXT != n->type)
226 return(0);
227 if (a2roffsu(n->string, su, SCALE_BU))
228 return(1);
229
230 return(0);
231 }
232
233
234 /* ARGSUSED */
235 static int
236 man_root_pre(MAN_ARGS)
237 {
238 struct htmlpair tag[2];
239 struct tag *t, *tt;
240 char b[BUFSIZ], title[BUFSIZ];
241
242 b[0] = 0;
243 if (m->vol)
244 (void)strlcat(b, m->vol, BUFSIZ);
245
246 (void)snprintf(title, BUFSIZ - 1,
247 "%s(%d)", m->title, m->msec);
248
249 PAIR_CLASS_INIT(&tag[0], "header");
250 bufcat_style(h, "width", "100%");
251 PAIR_STYLE_INIT(&tag[1], h);
252 t = print_otag(h, TAG_TABLE, 2, tag);
253 tt = print_otag(h, TAG_TR, 0, NULL);
254
255 bufinit(h);
256 bufcat_style(h, "width", "10%");
257 PAIR_STYLE_INIT(&tag[0], h);
258 print_otag(h, TAG_TD, 1, tag);
259 print_text(h, title);
260 print_stagq(h, tt);
261
262 bufinit(h);
263 bufcat_style(h, "width", "80%");
264 bufcat_style(h, "white-space", "nowrap");
265 bufcat_style(h, "text-align", "center");
266 PAIR_STYLE_INIT(&tag[0], h);
267 print_otag(h, TAG_TD, 1, tag);
268 print_text(h, b);
269 print_stagq(h, tt);
270
271 bufinit(h);
272 bufcat_style(h, "width", "10%");
273 bufcat_style(h, "text-align", "right");
274 PAIR_STYLE_INIT(&tag[0], h);
275 print_otag(h, TAG_TD, 1, tag);
276 print_text(h, title);
277 print_tagq(h, t);
278 return(1);
279 }
280
281
282 /* ARGSUSED */
283 static void
284 man_root_post(MAN_ARGS)
285 {
286 struct htmlpair tag[2];
287 struct tag *t, *tt;
288 char b[DATESIZ];
289
290 time2a(m->date, b, DATESIZ);
291
292 PAIR_CLASS_INIT(&tag[0], "footer");
293 bufcat_style(h, "width", "100%");
294 PAIR_STYLE_INIT(&tag[1], h);
295 t = print_otag(h, TAG_TABLE, 2, tag);
296 tt = print_otag(h, TAG_TR, 0, NULL);
297
298 bufinit(h);
299 bufcat_style(h, "width", "50%");
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", "50%");
307 bufcat_style(h, "text-align", "right");
308 PAIR_STYLE_INIT(&tag[0], h);
309 print_otag(h, TAG_TD, 1, tag);
310 if (m->source)
311 print_text(h, m->source);
312 print_tagq(h, t);
313 }
314
315
316
317 /* ARGSUSED */
318 static int
319 man_br_pre(MAN_ARGS)
320 {
321 struct roffsu su;
322 struct htmlpair tag;
323
324 SCALE_VS_INIT(&su, 1);
325
326 if (MAN_sp == n->tok && n->child)
327 a2roffsu(n->child->string, &su, SCALE_VS);
328 else if (MAN_br == n->tok)
329 su.scale = 0;
330
331 bufcat_su(h, "height", &su);
332 PAIR_STYLE_INIT(&tag, h);
333 print_otag(h, TAG_DIV, 1, &tag);
334 return(0);
335 }
336
337
338 /* ARGSUSED */
339 static int
340 man_SH_pre(MAN_ARGS)
341 {
342 struct htmlpair tag[2];
343 struct roffsu su;
344
345 if (MAN_BODY == n->type) {
346 SCALE_HS_INIT(&su, INDENT);
347 bufcat_su(h, "margin-left", &su);
348 PAIR_CLASS_INIT(&tag[0], "sec-body");
349 PAIR_STYLE_INIT(&tag[1], h);
350 print_otag(h, TAG_DIV, 2, tag);
351 return(1);
352 } else if (MAN_BLOCK == n->type) {
353 PAIR_CLASS_INIT(&tag[0], "sec-block");
354 if (n->prev && MAN_SH == n->prev->tok)
355 if (NULL == n->prev->body->child) {
356 print_otag(h, TAG_DIV, 1, tag);
357 return(1);
358 }
359
360 SCALE_VS_INIT(&su, 1);
361 bufcat_su(h, "margin-top", &su);
362 if (NULL == n->next)
363 bufcat_su(h, "margin-bottom", &su);
364 PAIR_STYLE_INIT(&tag[1], h);
365 print_otag(h, TAG_DIV, 2, tag);
366 return(1);
367 }
368
369 PAIR_CLASS_INIT(&tag[0], "sec-head");
370 print_otag(h, TAG_DIV, 1, tag);
371 return(1);
372 }
373
374
375 /* ARGSUSED */
376 static int
377 man_alt_pre(MAN_ARGS)
378 {
379 const struct man_node *nn;
380 struct tag *t;
381 int i;
382 struct htmlpair tagi, tagb, *tagp;
383
384 PAIR_CLASS_INIT(&tagi, "italic");
385 PAIR_CLASS_INIT(&tagb, "bold");
386
387 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
388 switch (n->tok) {
389 case (MAN_BI):
390 tagp = i % 2 ? &tagi : &tagb;
391 break;
392 case (MAN_IB):
393 tagp = i % 2 ? &tagb : &tagi;
394 break;
395 case (MAN_RI):
396 tagp = i % 2 ? &tagi : NULL;
397 break;
398 case (MAN_IR):
399 tagp = i % 2 ? NULL : &tagi;
400 break;
401 case (MAN_BR):
402 tagp = i % 2 ? NULL : &tagb;
403 break;
404 case (MAN_RB):
405 tagp = i % 2 ? &tagb : NULL;
406 break;
407 default:
408 abort();
409 /* NOTREACHED */
410 }
411
412 if (i)
413 h->flags |= HTML_NOSPACE;
414
415 if (tagp) {
416 t = print_otag(h, TAG_SPAN, 1, tagp);
417 print_man_node(m, nn, h);
418 print_tagq(h, t);
419 } else
420 print_man_node(m, nn, h);
421 }
422
423 return(0);
424 }
425
426
427 /* ARGSUSED */
428 static int
429 man_SB_pre(MAN_ARGS)
430 {
431 struct htmlpair tag;
432
433 PAIR_CLASS_INIT(&tag, "small bold");
434 print_otag(h, TAG_SPAN, 1, &tag);
435 return(1);
436 }
437
438
439 /* ARGSUSED */
440 static int
441 man_SM_pre(MAN_ARGS)
442 {
443 struct htmlpair tag;
444
445 PAIR_CLASS_INIT(&tag, "small");
446 print_otag(h, TAG_SPAN, 1, &tag);
447 return(1);
448 }
449
450
451 /* ARGSUSED */
452 static int
453 man_SS_pre(MAN_ARGS)
454 {
455 struct htmlpair tag[3];
456 struct roffsu su;
457
458 SCALE_VS_INIT(&su, 1);
459
460 if (MAN_BODY == n->type) {
461 PAIR_CLASS_INIT(&tag[0], "ssec-body");
462 if (n->parent->next && n->child) {
463 bufcat_su(h, "margin-bottom", &su);
464 PAIR_STYLE_INIT(&tag[1], h);
465 print_otag(h, TAG_DIV, 2, tag);
466 return(1);
467 }
468
469 print_otag(h, TAG_DIV, 1, tag);
470 return(1);
471 } else if (MAN_BLOCK == n->type) {
472 PAIR_CLASS_INIT(&tag[0], "ssec-block");
473 if (n->prev && MAN_SS == n->prev->tok)
474 if (n->prev->body->child) {
475 bufcat_su(h, "margin-top", &su);
476 PAIR_STYLE_INIT(&tag[1], h);
477 print_otag(h, TAG_DIV, 2, tag);
478 return(1);
479 }
480
481 print_otag(h, TAG_DIV, 1, tag);
482 return(1);
483 }
484
485 SCALE_HS_INIT(&su, INDENT - HALFINDENT);
486 bufcat_su(h, "margin-left", &su);
487 PAIR_CLASS_INIT(&tag[0], "ssec-head");
488 PAIR_STYLE_INIT(&tag[1], h);
489 print_otag(h, TAG_DIV, 2, tag);
490 return(1);
491 }
492
493
494 /* ARGSUSED */
495 static int
496 man_PP_pre(MAN_ARGS)
497 {
498 struct htmlpair tag;
499 struct roffsu su;
500 int i;
501
502 if (MAN_BLOCK != n->type)
503 return(1);
504
505 i = 0;
506
507 if (MAN_ROOT == n->parent->tok) {
508 SCALE_HS_INIT(&su, INDENT);
509 bufcat_su(h, "margin-left", &su);
510 i++;
511 }
512 if (n->next && n->next->child) {
513 SCALE_VS_INIT(&su, 1);
514 bufcat_su(h, "margin-bottom", &su);
515 i++;
516 }
517
518 PAIR_STYLE_INIT(&tag, h);
519 print_otag(h, TAG_DIV, i ? 1 : 0, &tag);
520 return(1);
521 }
522
523
524 /* ARGSUSED */
525 static int
526 man_IP_pre(MAN_ARGS)
527 {
528 struct roffsu su;
529 struct htmlpair tag;
530 const struct man_node *nn;
531 int width;
532
533 /*
534 * This scattering of 1-BU margins and pads is to make sure that
535 * when text overruns its box, the subsequent text isn't flush
536 * up against it. However, the rest of the right-hand box must
537 * also be adjusted in consideration of this 1-BU space.
538 */
539
540 if (MAN_BODY == n->type) {
541 SCALE_HS_INIT(&su, INDENT);
542 bufcat_su(h, "margin-left", &su);
543 PAIR_STYLE_INIT(&tag, h);
544 print_otag(h, TAG_DIV, 1, &tag);
545 return(1);
546 }
547
548 nn = MAN_BLOCK == n->type ?
549 n->head->child : n->parent->head->child;
550
551 SCALE_HS_INIT(&su, INDENT);
552 width = 0;
553
554 if (MAN_IP == n->tok && NULL != nn)
555 if (NULL != (nn = nn->next)) {
556 for ( ; nn->next; nn = nn->next)
557 /* Do nothing. */ ;
558 width = a2width(nn, &su);
559 }
560
561 if (MAN_TP == n->tok && NULL != nn)
562 width = a2width(nn, &su);
563
564 if (MAN_BLOCK == n->type) {
565 bufcat_su(h, "margin-left", &su);
566 SCALE_VS_INIT(&su, 1);
567 bufcat_su(h, "margin-top", &su);
568 bufcat_style(h, "clear", "both");
569 PAIR_STYLE_INIT(&tag, h);
570 print_otag(h, TAG_DIV, 1, &tag);
571 return(1);
572 }
573
574 bufcat_su(h, "min-width", &su);
575 SCALE_INVERT(&su);
576 bufcat_su(h, "margin-left", &su);
577 SCALE_HS_INIT(&su, 1);
578 bufcat_su(h, "margin-right", &su);
579 bufcat_style(h, "clear", "left");
580
581 if (n->next && n->next->child)
582 bufcat_style(h, "float", "left");
583
584 PAIR_STYLE_INIT(&tag, h);
585 print_otag(h, TAG_DIV, 1, &tag);
586
587 /* With a length string, manually omit the last child. */
588
589 if ( ! width)
590 return(1);
591
592 if (MAN_IP == n->tok)
593 for (nn = n->child; nn->next; nn = nn->next)
594 print_man_node(m, nn, h);
595 if (MAN_TP == n->tok)
596 for (nn = n->child->next; nn; nn = nn->next)
597 print_man_node(m, nn, h);
598
599 return(0);
600 }
601
602
603 /* ARGSUSED */
604 static int
605 man_HP_pre(MAN_ARGS)
606 {
607 const struct man_node *nn;
608 struct htmlpair tag;
609 struct roffsu su;
610
611 if (MAN_HEAD == n->type)
612 return(0);
613
614 nn = MAN_BLOCK == n->type ?
615 n->head->child : n->parent->head->child;
616
617 SCALE_HS_INIT(&su, INDENT);
618
619 if (NULL != nn)
620 (void)a2width(nn, &su);
621
622 if (MAN_BLOCK == n->type) {
623 bufcat_su(h, "margin-left", &su);
624 SCALE_VS_INIT(&su, 1);
625 bufcat_su(h, "margin-top", &su);
626 bufcat_style(h, "clear", "both");
627 PAIR_STYLE_INIT(&tag, h);
628 print_otag(h, TAG_DIV, 1, &tag);
629 return(1);
630 }
631
632 bufcat_su(h, "margin-left", &su);
633 SCALE_INVERT(&su);
634 bufcat_su(h, "text-indent", &su);
635
636 PAIR_STYLE_INIT(&tag, h);
637 print_otag(h, TAG_DIV, 1, &tag);
638 return(1);
639 }
640
641
642 /* ARGSUSED */
643 static int
644 man_B_pre(MAN_ARGS)
645 {
646 struct htmlpair tag;
647
648 PAIR_CLASS_INIT(&tag, "bold");
649 print_otag(h, TAG_SPAN, 1, &tag);
650 return(1);
651 }
652
653
654 /* ARGSUSED */
655 static int
656 man_I_pre(MAN_ARGS)
657 {
658 struct htmlpair tag;
659
660 PAIR_CLASS_INIT(&tag, "italic");
661 print_otag(h, TAG_SPAN, 1, &tag);
662 return(1);
663 }
664
665
666 /* ARGSUSED */
667 static int
668 man_ign_pre(MAN_ARGS)
669 {
670
671 return(0);
672 }
673
674
675 /* ARGSUSED */
676 static int
677 man_RS_pre(MAN_ARGS)
678 {
679 struct htmlpair tag;
680 struct roffsu su;
681
682 if (MAN_HEAD == n->type)
683 return(0);
684 else if (MAN_BODY == n->type)
685 return(1);
686
687 SCALE_HS_INIT(&su, INDENT);
688 bufcat_su(h, "margin-left", &su);
689
690 if (n->head->child) {
691 SCALE_VS_INIT(&su, 1);
692 a2width(n->head->child, &su);
693 bufcat_su(h, "margin-top", &su);
694 }
695
696 PAIR_STYLE_INIT(&tag, h);
697 print_otag(h, TAG_DIV, 1, &tag);
698 return(1);
699 }