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