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