610e58fdbdf3f779df938d4f8edacf69ca98234f
[dragonfly.git] / contrib / mdocml / man_html.c
1 /*      $Id: man_html.c,v 1.70 2011/03/07 01:35:51 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
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 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "mandoc.h"
30 #include "out.h"
31 #include "html.h"
32 #include "man.h"
33 #include "main.h"
34
35 /* TODO: preserve ident widths. */
36 /* FIXME: have PD set the default vspace width. */
37
38 #define INDENT            5
39 #define HALFINDENT        3
40
41 #define MAN_ARGS          const struct man_meta *m, \
42                           const struct man_node *n, \
43                           struct mhtml *mh, \
44                           struct html *h
45
46 struct  mhtml {
47         int               fl;
48 #define MANH_LITERAL     (1 << 0) /* literal context */
49 };
50
51 struct  htmlman {
52         int             (*pre)(MAN_ARGS);
53         int             (*post)(MAN_ARGS);
54 };
55
56 static  void              print_man(MAN_ARGS);
57 static  void              print_man_head(MAN_ARGS);
58 static  void              print_man_nodelist(MAN_ARGS);
59 static  void              print_man_node(MAN_ARGS);
60
61 static  int               a2width(const struct man_node *,
62                                 struct roffsu *);
63
64 static  int               man_alt_pre(MAN_ARGS);
65 static  int               man_br_pre(MAN_ARGS);
66 static  int               man_ign_pre(MAN_ARGS);
67 static  int               man_in_pre(MAN_ARGS);
68 static  int               man_literal_pre(MAN_ARGS);
69 static  void              man_root_post(MAN_ARGS);
70 static  void              man_root_pre(MAN_ARGS);
71 static  int               man_B_pre(MAN_ARGS);
72 static  int               man_HP_pre(MAN_ARGS);
73 static  int               man_I_pre(MAN_ARGS);
74 static  int               man_IP_pre(MAN_ARGS);
75 static  int               man_PP_pre(MAN_ARGS);
76 static  int               man_RS_pre(MAN_ARGS);
77 static  int               man_SH_pre(MAN_ARGS);
78 static  int               man_SM_pre(MAN_ARGS);
79 static  int               man_SS_pre(MAN_ARGS);
80
81 static  const struct htmlman mans[MAN_MAX] = {
82         { man_br_pre, NULL }, /* br */
83         { NULL, NULL }, /* TH */
84         { man_SH_pre, NULL }, /* SH */
85         { man_SS_pre, NULL }, /* SS */
86         { man_IP_pre, NULL }, /* TP */
87         { man_PP_pre, NULL }, /* LP */
88         { man_PP_pre, NULL }, /* PP */
89         { man_PP_pre, NULL }, /* P */
90         { man_IP_pre, NULL }, /* IP */
91         { man_HP_pre, NULL }, /* HP */ 
92         { man_SM_pre, NULL }, /* SM */
93         { man_SM_pre, NULL }, /* SB */
94         { man_alt_pre, NULL }, /* BI */
95         { man_alt_pre, NULL }, /* IB */
96         { man_alt_pre, NULL }, /* BR */
97         { man_alt_pre, NULL }, /* RB */
98         { NULL, NULL }, /* R */
99         { man_B_pre, NULL }, /* B */
100         { man_I_pre, NULL }, /* I */
101         { man_alt_pre, NULL }, /* IR */
102         { man_alt_pre, NULL }, /* RI */
103         { man_ign_pre, NULL }, /* na */
104         { man_br_pre, NULL }, /* sp */
105         { man_literal_pre, NULL }, /* nf */
106         { man_literal_pre, NULL }, /* fi */
107         { NULL, NULL }, /* RE */
108         { man_RS_pre, NULL }, /* RS */
109         { man_ign_pre, NULL }, /* DT */
110         { man_ign_pre, NULL }, /* UC */
111         { man_ign_pre, NULL }, /* PD */
112         { man_ign_pre, NULL }, /* AT */
113         { man_in_pre, NULL }, /* in */
114         { man_ign_pre, NULL }, /* ft */
115 };
116
117
118 void
119 html_man(void *arg, const struct man *m)
120 {
121         struct html     *h;
122         struct tag      *t;
123         struct mhtml     mh;
124
125         h = (struct html *)arg;
126
127         print_gen_decls(h);
128
129         memset(&mh, 0, sizeof(struct mhtml));
130
131         t = print_otag(h, TAG_HTML, 0, NULL);
132         print_man(man_meta(m), man_node(m), &mh, h);
133         print_tagq(h, t);
134
135         printf("\n");
136 }
137
138
139 static void
140 print_man(MAN_ARGS) 
141 {
142         struct tag      *t;
143
144         t = print_otag(h, TAG_HEAD, 0, NULL);
145         print_man_head(m, n, mh, h);
146         print_tagq(h, t);
147
148         t = print_otag(h, TAG_BODY, 0, NULL);
149         print_man_nodelist(m, n, mh, h);
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(%s)", 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, mh, h);
173         if (n->next)
174                 print_man_nodelist(m, n->next, mh, h);
175 }
176
177
178 static void
179 print_man_node(MAN_ARGS)
180 {
181         int              child;
182         struct tag      *t;
183         struct htmlpair  tag;
184
185         child = 1;
186         t = h->tags.head;
187         bufinit(h);
188
189         switch (n->type) {
190         case (MAN_ROOT):
191                 man_root_pre(m, n, mh, h);
192                 break;
193         case (MAN_TEXT):
194                 /*
195                  * If we have a blank line, output a vertical space.
196                  * If we have a space as the first character, break
197                  * before printing the line's data.
198                  */
199                 if ('\0' == *n->string) {
200                         print_otag(h, TAG_P, 0, NULL);
201                         return;
202                 } else if (' ' == *n->string && MAN_LINE & n->flags)
203                         print_otag(h, TAG_BR, 0, NULL);
204
205                 print_text(h, n->string);
206
207                 /*
208                  * If we're in a literal context, make sure that words
209                  * togehter on the same line stay together.  This is a
210                  * POST-printing call, so we check the NEXT word.  Since
211                  * -man doesn't have nested macros, we don't need to be
212                  * more specific than this.
213                  */
214                 if (MANH_LITERAL & mh->fl &&
215                                 (NULL == n->next ||
216                                  n->next->line > n->line))
217                         print_otag(h, TAG_BR, 0, NULL);
218                 return;
219         case (MAN_EQN):
220                 PAIR_CLASS_INIT(&tag, "eqn");
221                 print_otag(h, TAG_SPAN, 1, &tag);
222                 print_text(h, n->eqn->data);
223                 break;
224         case (MAN_TBL):
225                 /*
226                  * This will take care of initialising all of the table
227                  * state data for the first table, then tearing it down
228                  * for the last one.
229                  */
230                 print_tbl(h, n->span);
231                 return;
232         default:
233                 /* 
234                  * Close out scope of font prior to opening a macro
235                  * scope.
236                  */
237                 if (HTMLFONT_NONE != h->metac) {
238                         h->metal = h->metac;
239                         h->metac = HTMLFONT_NONE;
240                 }
241
242                 /*
243                  * Close out the current table, if it's open, and unset
244                  * the "meta" table state.  This will be reopened on the
245                  * next table element.
246                  */
247                 if (h->tblt) {
248                         print_tblclose(h);
249                         t = h->tags.head;
250                 }
251                 if (mans[n->tok].pre)
252                         child = (*mans[n->tok].pre)(m, n, mh, h);
253                 break;
254         }
255
256         if (child && n->child)
257                 print_man_nodelist(m, n->child, mh, h);
258
259         /* This will automatically close out any font scope. */
260         print_stagq(h, t);
261
262         bufinit(h);
263
264         switch (n->type) {
265         case (MAN_ROOT):
266                 man_root_post(m, n, mh, h);
267                 break;
268         case (MAN_EQN):
269                 break;
270         default:
271                 if (mans[n->tok].post)
272                         (*mans[n->tok].post)(m, n, mh, h);
273                 break;
274         }
275 }
276
277
278 static int
279 a2width(const struct man_node *n, struct roffsu *su)
280 {
281
282         if (MAN_TEXT != n->type)
283                 return(0);
284         if (a2roffsu(n->string, su, SCALE_BU))
285                 return(1);
286
287         return(0);
288 }
289
290
291 /* ARGSUSED */
292 static void
293 man_root_pre(MAN_ARGS)
294 {
295         struct htmlpair  tag[3];
296         struct tag      *t, *tt;
297         char             b[BUFSIZ], title[BUFSIZ];
298
299         b[0] = 0;
300         if (m->vol)
301                 (void)strlcat(b, m->vol, BUFSIZ);
302
303         snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
304
305         PAIR_SUMMARY_INIT(&tag[0], "Document Header");
306         PAIR_CLASS_INIT(&tag[1], "head");
307         if (NULL == h->style) {
308                 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
309                 t = print_otag(h, TAG_TABLE, 3, tag);
310                 PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
311                 print_otag(h, TAG_COL, 1, tag);
312                 print_otag(h, TAG_COL, 1, tag);
313                 print_otag(h, TAG_COL, 1, tag);
314         } else
315                 t = print_otag(h, TAG_TABLE, 2, tag);
316
317         print_otag(h, TAG_TBODY, 0, NULL);
318
319         tt = print_otag(h, TAG_TR, 0, NULL);
320
321         PAIR_CLASS_INIT(&tag[0], "head-ltitle");
322         print_otag(h, TAG_TD, 1, tag);
323
324         print_text(h, title);
325         print_stagq(h, tt);
326
327         PAIR_CLASS_INIT(&tag[0], "head-vol");
328         if (NULL == h->style) {
329                 PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
330                 print_otag(h, TAG_TD, 2, tag);
331         } else 
332                 print_otag(h, TAG_TD, 1, tag);
333
334         print_text(h, b);
335         print_stagq(h, tt);
336
337         PAIR_CLASS_INIT(&tag[0], "head-rtitle");
338         if (NULL == h->style) {
339                 PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
340                 print_otag(h, TAG_TD, 2, tag);
341         } else 
342                 print_otag(h, TAG_TD, 1, tag);
343
344         print_text(h, title);
345         print_tagq(h, t);
346 }
347
348
349 /* ARGSUSED */
350 static void
351 man_root_post(MAN_ARGS)
352 {
353         struct htmlpair  tag[3];
354         struct tag      *t, *tt;
355
356         PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
357         PAIR_CLASS_INIT(&tag[1], "foot");
358         if (NULL == h->style) {
359                 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
360                 t = print_otag(h, TAG_TABLE, 3, tag);
361                 PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
362                 print_otag(h, TAG_COL, 1, tag);
363                 print_otag(h, TAG_COL, 1, tag);
364         } else
365                 t = print_otag(h, TAG_TABLE, 2, tag);
366
367         tt = print_otag(h, TAG_TR, 0, NULL);
368
369         PAIR_CLASS_INIT(&tag[0], "foot-date");
370         print_otag(h, TAG_TD, 1, tag);
371
372         print_text(h, m->date);
373         print_stagq(h, tt);
374
375         PAIR_CLASS_INIT(&tag[0], "foot-os");
376         if (NULL == h->style) {
377                 PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
378                 print_otag(h, TAG_TD, 2, tag);
379         } else 
380                 print_otag(h, TAG_TD, 1, tag);
381
382         if (m->source)
383                 print_text(h, m->source);
384         print_tagq(h, t);
385 }
386
387
388
389 /* ARGSUSED */
390 static int
391 man_br_pre(MAN_ARGS)
392 {
393         struct roffsu    su;
394         struct htmlpair  tag;
395
396         SCALE_VS_INIT(&su, 1);
397
398         if (MAN_sp == n->tok) {
399                 if (n->child)
400                         a2roffsu(n->child->string, &su, SCALE_VS);
401         } else
402                 su.scale = 0;
403
404         bufcat_su(h, "height", &su);
405         PAIR_STYLE_INIT(&tag, h);
406         print_otag(h, TAG_DIV, 1, &tag);
407
408         /* So the div isn't empty: */
409         print_text(h, "\\~");
410
411         return(0);
412 }
413
414
415 /* ARGSUSED */
416 static int
417 man_SH_pre(MAN_ARGS)
418 {
419         struct htmlpair  tag;
420
421         if (MAN_BLOCK == n->type) {
422                 PAIR_CLASS_INIT(&tag, "section");
423                 print_otag(h, TAG_DIV, 1, &tag);
424                 return(1);
425         } else if (MAN_BODY == n->type)
426                 return(1);
427
428         print_otag(h, TAG_H1, 0, NULL);
429         return(1);
430 }
431
432
433 /* ARGSUSED */
434 static int
435 man_alt_pre(MAN_ARGS)
436 {
437         const struct man_node   *nn;
438         int              i;
439         enum htmltag     fp;
440         struct tag      *t;
441
442         for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
443                 t = NULL;
444                 switch (n->tok) {
445                 case (MAN_BI):
446                         fp = i % 2 ? TAG_I : TAG_B;
447                         break;
448                 case (MAN_IB):
449                         fp = i % 2 ? TAG_B : TAG_I;
450                         break;
451                 case (MAN_RI):
452                         fp = i % 2 ? TAG_I : TAG_MAX;
453                         break;
454                 case (MAN_IR):
455                         fp = i % 2 ? TAG_MAX : TAG_I;
456                         break;
457                 case (MAN_BR):
458                         fp = i % 2 ? TAG_MAX : TAG_B;
459                         break;
460                 case (MAN_RB):
461                         fp = i % 2 ? TAG_B : TAG_MAX;
462                         break;
463                 default:
464                         abort();
465                         /* NOTREACHED */
466                 }
467
468                 if (i)
469                         h->flags |= HTML_NOSPACE;
470
471                 if (TAG_MAX != fp)
472                         t = print_otag(h, fp, 0, NULL);
473
474                 print_man_node(m, nn, mh, h);
475
476                 if (t)
477                         print_tagq(h, t);
478         }
479
480         return(0);
481 }
482
483
484 /* ARGSUSED */
485 static int
486 man_SM_pre(MAN_ARGS)
487 {
488         
489         print_otag(h, TAG_SMALL, 0, NULL);
490         if (MAN_SB == n->tok)
491                 print_otag(h, TAG_B, 0, NULL);
492         return(1);
493 }
494
495
496 /* ARGSUSED */
497 static int
498 man_SS_pre(MAN_ARGS)
499 {
500         struct htmlpair  tag;
501
502         if (MAN_BLOCK == n->type) {
503                 PAIR_CLASS_INIT(&tag, "subsection");
504                 print_otag(h, TAG_DIV, 1, &tag);
505                 return(1);
506         } else if (MAN_BODY == n->type)
507                 return(1);
508
509         print_otag(h, TAG_H2, 0, NULL);
510         return(1);
511 }
512
513
514 /* ARGSUSED */
515 static int
516 man_PP_pre(MAN_ARGS)
517 {
518
519         if (MAN_HEAD == n->type)
520                 return(0);
521         else if (MAN_BODY == n->type && n->prev)
522                 print_otag(h, TAG_P, 0, NULL);
523
524         return(1);
525 }
526
527
528 /* ARGSUSED */
529 static int
530 man_IP_pre(MAN_ARGS)
531 {
532         struct roffsu            su;
533         struct htmlpair          tag;
534         const struct man_node   *nn;
535
536         /*
537          * This scattering of 1-BU margins and pads is to make sure that
538          * when text overruns its box, the subsequent text isn't flush
539          * up against it.  However, the rest of the right-hand box must
540          * also be adjusted in consideration of this 1-BU space.
541          */
542
543         if (MAN_BODY == n->type) { 
544                 print_otag(h, TAG_TD, 0, NULL);
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
553         /* Width is the second token. */
554
555         if (MAN_IP == n->tok && NULL != nn)
556                 if (NULL != (nn = nn->next))
557                         a2width(nn, &su);
558
559         /* Width is the first token. */
560
561         if (MAN_TP == n->tok && NULL != nn) {
562                 /* Skip past non-text children. */
563                 while (nn && MAN_TEXT != nn->type)
564                         nn = nn->next;
565                 if (nn)
566                         a2width(nn, &su);
567         }
568
569         if (MAN_BLOCK == n->type) {
570                 print_otag(h, TAG_P, 0, NULL);
571                 print_otag(h, TAG_TABLE, 0, NULL);
572                 bufcat_su(h, "width", &su);
573                 PAIR_STYLE_INIT(&tag, h);
574                 print_otag(h, TAG_COL, 1, &tag);
575                 print_otag(h, TAG_COL, 0, NULL);
576                 print_otag(h, TAG_TBODY, 0, NULL);
577                 print_otag(h, TAG_TR, 0, NULL);
578                 return(1);
579         } 
580
581         print_otag(h, TAG_TD, 0, NULL);
582
583         /* For IP, only print the first header element. */
584
585         if (MAN_IP == n->tok && n->child)
586                 print_man_node(m, n->child, mh, h);
587
588         /* For TP, only print next-line header elements. */
589
590         if (MAN_TP == n->tok)
591                 for (nn = n->child; nn; nn = nn->next)
592                         if (nn->line > n->line)
593                                 print_man_node(m, nn, mh, h);
594
595         return(0);
596 }
597
598
599 /* ARGSUSED */
600 static int
601 man_HP_pre(MAN_ARGS)
602 {
603         struct htmlpair  tag;
604         struct roffsu    su;
605         const struct man_node *np;
606
607         np = MAN_BLOCK == n->type ? 
608                 n->head->child : 
609                 n->parent->head->child;
610
611         if (NULL == np || ! a2width(np, &su))
612                 SCALE_HS_INIT(&su, INDENT);
613
614         if (MAN_HEAD == n->type) {
615                 print_otag(h, TAG_TD, 0, NULL);
616                 return(0);
617         } else if (MAN_BLOCK == n->type) {
618                 print_otag(h, TAG_P, 0, NULL);
619                 print_otag(h, TAG_TABLE, 0, NULL);
620                 bufcat_su(h, "width", &su);
621                 PAIR_STYLE_INIT(&tag, h);
622                 print_otag(h, TAG_COL, 1, &tag);
623                 print_otag(h, TAG_COL, 0, NULL);
624                 print_otag(h, TAG_TBODY, 0, NULL);
625                 print_otag(h, TAG_TR, 0, NULL);
626                 return(1);
627         }
628
629         su.scale = -su.scale;
630         bufcat_su(h, "text-indent", &su);
631         PAIR_STYLE_INIT(&tag, h);
632         print_otag(h, TAG_TD, 1, &tag);
633         return(1);
634 }
635
636
637 /* ARGSUSED */
638 static int
639 man_B_pre(MAN_ARGS)
640 {
641
642         print_otag(h, TAG_B, 0, NULL);
643         return(1);
644 }
645
646
647 /* ARGSUSED */
648 static int
649 man_I_pre(MAN_ARGS)
650 {
651         
652         print_otag(h, TAG_I, 0, NULL);
653         return(1);
654 }
655
656
657 /* ARGSUSED */
658 static int
659 man_literal_pre(MAN_ARGS)
660 {
661
662         if (MAN_nf == n->tok) {
663                 print_otag(h, TAG_BR, 0, NULL);
664                 mh->fl |= MANH_LITERAL;
665         } else
666                 mh->fl &= ~MANH_LITERAL;
667
668         return(0);
669 }
670
671
672 /* ARGSUSED */
673 static int
674 man_in_pre(MAN_ARGS)
675 {
676
677         print_otag(h, TAG_BR, 0, NULL);
678         return(0);
679 }
680
681
682 /* ARGSUSED */
683 static int
684 man_ign_pre(MAN_ARGS)
685 {
686
687         return(0);
688 }
689
690
691 /* ARGSUSED */
692 static int
693 man_RS_pre(MAN_ARGS)
694 {
695         struct htmlpair  tag;
696         struct roffsu    su;
697
698         if (MAN_HEAD == n->type)
699                 return(0);
700         else if (MAN_BODY == n->type)
701                 return(1);
702
703         SCALE_HS_INIT(&su, INDENT);
704         if (n->head->child)
705                 a2width(n->head->child, &su);
706
707         bufcat_su(h, "margin-left", &su);
708         PAIR_STYLE_INIT(&tag, h);
709         print_otag(h, TAG_DIV, 1, &tag);
710         return(1);
711 }