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