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