]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/mdocml/man_html.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / mdocml / man_html.c
1 /*      $Id: man_html.c,v 1.86 2012/01/03 15:16:24 kristaps 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
40 #define MAN_ARGS          const struct man_meta *m, \
41                           const struct man_node *n, \
42                           struct mhtml *mh, \
43                           struct html *h
44
45 struct  mhtml {
46         int               fl;
47 #define MANH_LITERAL     (1 << 0) /* literal context */
48 };
49
50 struct  htmlman {
51         int             (*pre)(MAN_ARGS);
52         int             (*post)(MAN_ARGS);
53 };
54
55 static  void              print_bvspace(struct html *, 
56                                 const struct man_node *);
57 static  void              print_man(MAN_ARGS);
58 static  void              print_man_head(MAN_ARGS);
59 static  void              print_man_nodelist(MAN_ARGS);
60 static  void              print_man_node(MAN_ARGS);
61 static  int               a2width(const struct man_node *,
62                                 struct roffsu *);
63 static  int               man_B_pre(MAN_ARGS);
64 static  int               man_HP_pre(MAN_ARGS);
65 static  int               man_IP_pre(MAN_ARGS);
66 static  int               man_I_pre(MAN_ARGS);
67 static  int               man_OP_pre(MAN_ARGS);
68 static  int               man_PP_pre(MAN_ARGS);
69 static  int               man_RS_pre(MAN_ARGS);
70 static  int               man_SH_pre(MAN_ARGS);
71 static  int               man_SM_pre(MAN_ARGS);
72 static  int               man_SS_pre(MAN_ARGS);
73 static  int               man_alt_pre(MAN_ARGS);
74 static  int               man_br_pre(MAN_ARGS);
75 static  int               man_ign_pre(MAN_ARGS);
76 static  int               man_in_pre(MAN_ARGS);
77 static  int               man_literal_pre(MAN_ARGS);
78 static  void              man_root_post(MAN_ARGS);
79 static  void              man_root_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         { man_OP_pre, NULL }, /* OP */
116 };
117
118 /*
119  * Printing leading vertical space before a block.
120  * This is used for the paragraph macros.
121  * The rules are pretty simple, since there's very little nesting going
122  * on here.  Basically, if we're the first within another block (SS/SH),
123  * then don't emit vertical space.  If we are (RS), then do.  If not the
124  * first, print it.
125  */
126 static void
127 print_bvspace(struct html *h, const struct man_node *n)
128 {
129
130         if (n->body && n->body->child)
131                 if (MAN_TBL == n->body->child->type)
132                         return;
133
134         if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
135                 if (NULL == n->prev)
136                         return;
137
138         print_otag(h, TAG_P, 0, NULL);
139 }
140
141 void
142 html_man(void *arg, const struct man *m)
143 {
144         struct mhtml     mh;
145
146         memset(&mh, 0, sizeof(struct mhtml));
147         print_man(man_meta(m), man_node(m), &mh, (struct html *)arg);
148         putchar('\n');
149 }
150
151 static void
152 print_man(MAN_ARGS) 
153 {
154         struct tag      *t, *tt;
155         struct htmlpair  tag;
156
157         PAIR_CLASS_INIT(&tag, "mandoc");
158
159         if ( ! (HTML_FRAGMENT & h->oflags)) {
160                 print_gen_decls(h);
161                 t = print_otag(h, TAG_HTML, 0, NULL);
162                 tt = print_otag(h, TAG_HEAD, 0, NULL);
163                 print_man_head(m, n, mh, h);
164                 print_tagq(h, tt);
165                 print_otag(h, TAG_BODY, 0, NULL);
166                 print_otag(h, TAG_DIV, 1, &tag);
167         } else 
168                 t = print_otag(h, TAG_DIV, 1, &tag);
169
170         print_man_nodelist(m, n, mh, h);
171         print_tagq(h, t);
172 }
173
174
175 /* ARGSUSED */
176 static void
177 print_man_head(MAN_ARGS)
178 {
179
180         print_gen_head(h);
181         assert(m->title);
182         assert(m->msec);
183         bufcat_fmt(h, "%s(%s)", m->title, m->msec);
184         print_otag(h, TAG_TITLE, 0, NULL);
185         print_text(h, h->buf);
186 }
187
188
189 static void
190 print_man_nodelist(MAN_ARGS)
191 {
192
193         print_man_node(m, n, mh, h);
194         if (n->next)
195                 print_man_nodelist(m, n->next, mh, h);
196 }
197
198
199 static void
200 print_man_node(MAN_ARGS)
201 {
202         int              child;
203         struct tag      *t;
204
205         child = 1;
206         t = h->tags.head;
207
208         switch (n->type) {
209         case (MAN_ROOT):
210                 man_root_pre(m, n, mh, h);
211                 break;
212         case (MAN_TEXT):
213                 /*
214                  * If we have a blank line, output a vertical space.
215                  * If we have a space as the first character, break
216                  * before printing the line's data.
217                  */
218                 if ('\0' == *n->string) {
219                         print_otag(h, TAG_P, 0, NULL);
220                         return;
221                 }
222
223                 if (' ' == *n->string && MAN_LINE & n->flags)
224                         print_otag(h, TAG_BR, 0, NULL);
225                 else if (MANH_LITERAL & mh->fl && n->prev)
226                         print_otag(h, TAG_BR, 0, NULL);
227
228                 print_text(h, n->string);
229                 return;
230         case (MAN_EQN):
231                 print_eqn(h, n->eqn);
232                 break;
233         case (MAN_TBL):
234                 /*
235                  * This will take care of initialising all of the table
236                  * state data for the first table, then tearing it down
237                  * for the last one.
238                  */
239                 print_tbl(h, n->span);
240                 return;
241         default:
242                 /* 
243                  * Close out scope of font prior to opening a macro
244                  * scope.
245                  */
246                 if (HTMLFONT_NONE != h->metac) {
247                         h->metal = h->metac;
248                         h->metac = HTMLFONT_NONE;
249                 }
250
251                 /*
252                  * Close out the current table, if it's open, and unset
253                  * the "meta" table state.  This will be reopened on the
254                  * next table element.
255                  */
256                 if (h->tblt) {
257                         print_tblclose(h);
258                         t = h->tags.head;
259                 }
260                 if (mans[n->tok].pre)
261                         child = (*mans[n->tok].pre)(m, n, mh, h);
262                 break;
263         }
264
265         if (child && n->child)
266                 print_man_nodelist(m, n->child, mh, h);
267
268         /* This will automatically close out any font scope. */
269         print_stagq(h, t);
270
271         switch (n->type) {
272         case (MAN_ROOT):
273                 man_root_post(m, n, mh, h);
274                 break;
275         case (MAN_EQN):
276                 break;
277         default:
278                 if (mans[n->tok].post)
279                         (*mans[n->tok].post)(m, n, mh, h);
280                 break;
281         }
282 }
283
284
285 static int
286 a2width(const struct man_node *n, struct roffsu *su)
287 {
288
289         if (MAN_TEXT != n->type)
290                 return(0);
291         if (a2roffsu(n->string, su, SCALE_BU))
292                 return(1);
293
294         return(0);
295 }
296
297
298 /* ARGSUSED */
299 static void
300 man_root_pre(MAN_ARGS)
301 {
302         struct htmlpair  tag[3];
303         struct tag      *t, *tt;
304         char             b[BUFSIZ], title[BUFSIZ];
305
306         b[0] = 0;
307         if (m->vol)
308                 (void)strlcat(b, m->vol, BUFSIZ);
309
310         assert(m->title);
311         assert(m->msec);
312         snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
313
314         PAIR_SUMMARY_INIT(&tag[0], "Document Header");
315         PAIR_CLASS_INIT(&tag[1], "head");
316         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
317         t = print_otag(h, TAG_TABLE, 3, tag);
318         PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
319         print_otag(h, TAG_COL, 1, tag);
320         print_otag(h, TAG_COL, 1, tag);
321         print_otag(h, TAG_COL, 1, tag);
322
323         print_otag(h, TAG_TBODY, 0, NULL);
324
325         tt = print_otag(h, TAG_TR, 0, NULL);
326
327         PAIR_CLASS_INIT(&tag[0], "head-ltitle");
328         print_otag(h, TAG_TD, 1, tag);
329         print_text(h, title);
330         print_stagq(h, tt);
331
332         PAIR_CLASS_INIT(&tag[0], "head-vol");
333         PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
334         print_otag(h, TAG_TD, 2, tag);
335         print_text(h, b);
336         print_stagq(h, tt);
337
338         PAIR_CLASS_INIT(&tag[0], "head-rtitle");
339         PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
340         print_otag(h, TAG_TD, 2, tag);
341         print_text(h, title);
342         print_tagq(h, t);
343 }
344
345
346 /* ARGSUSED */
347 static void
348 man_root_post(MAN_ARGS)
349 {
350         struct htmlpair  tag[3];
351         struct tag      *t, *tt;
352
353         PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
354         PAIR_CLASS_INIT(&tag[1], "foot");
355         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
356         t = print_otag(h, TAG_TABLE, 3, tag);
357         PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
358         print_otag(h, TAG_COL, 1, tag);
359         print_otag(h, TAG_COL, 1, tag);
360
361         tt = print_otag(h, TAG_TR, 0, NULL);
362
363         PAIR_CLASS_INIT(&tag[0], "foot-date");
364         print_otag(h, TAG_TD, 1, tag);
365
366         assert(m->date);
367         print_text(h, m->date);
368         print_stagq(h, tt);
369
370         PAIR_CLASS_INIT(&tag[0], "foot-os");
371         PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
372         print_otag(h, TAG_TD, 2, tag);
373
374         if (m->source)
375                 print_text(h, m->source);
376         print_tagq(h, t);
377 }
378
379
380 /* ARGSUSED */
381 static int
382 man_br_pre(MAN_ARGS)
383 {
384         struct roffsu    su;
385         struct htmlpair  tag;
386
387         SCALE_VS_INIT(&su, 1);
388
389         if (MAN_sp == n->tok) {
390                 if (NULL != (n = n->child))
391                         if ( ! a2roffsu(n->string, &su, SCALE_VS))
392                                 SCALE_VS_INIT(&su, atoi(n->string));
393         } else
394                 su.scale = 0;
395
396         bufinit(h);
397         bufcat_su(h, "height", &su);
398         PAIR_STYLE_INIT(&tag, h);
399         print_otag(h, TAG_DIV, 1, &tag);
400
401         /* So the div isn't empty: */
402         print_text(h, "\\~");
403
404         return(0);
405 }
406
407 /* ARGSUSED */
408 static int
409 man_SH_pre(MAN_ARGS)
410 {
411         struct htmlpair  tag;
412
413         if (MAN_BLOCK == n->type) {
414                 mh->fl &= ~MANH_LITERAL;
415                 PAIR_CLASS_INIT(&tag, "section");
416                 print_otag(h, TAG_DIV, 1, &tag);
417                 return(1);
418         } else if (MAN_BODY == n->type)
419                 return(1);
420
421         print_otag(h, TAG_H1, 0, NULL);
422         return(1);
423 }
424
425 /* ARGSUSED */
426 static int
427 man_alt_pre(MAN_ARGS)
428 {
429         const struct man_node   *nn;
430         int              i, savelit;
431         enum htmltag     fp;
432         struct tag      *t;
433
434         if ((savelit = mh->fl & MANH_LITERAL)) 
435                 print_otag(h, TAG_BR, 0, NULL);
436
437         mh->fl &= ~MANH_LITERAL;
438
439         for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
440                 t = NULL;
441                 switch (n->tok) {
442                 case (MAN_BI):
443                         fp = i % 2 ? TAG_I : TAG_B;
444                         break;
445                 case (MAN_IB):
446                         fp = i % 2 ? TAG_B : TAG_I;
447                         break;
448                 case (MAN_RI):
449                         fp = i % 2 ? TAG_I : TAG_MAX;
450                         break;
451                 case (MAN_IR):
452                         fp = i % 2 ? TAG_MAX : TAG_I;
453                         break;
454                 case (MAN_BR):
455                         fp = i % 2 ? TAG_MAX : TAG_B;
456                         break;
457                 case (MAN_RB):
458                         fp = i % 2 ? TAG_B : TAG_MAX;
459                         break;
460                 default:
461                         abort();
462                         /* NOTREACHED */
463                 }
464
465                 if (i)
466                         h->flags |= HTML_NOSPACE;
467
468                 if (TAG_MAX != fp)
469                         t = print_otag(h, fp, 0, NULL);
470
471                 print_man_node(m, nn, mh, h);
472
473                 if (t)
474                         print_tagq(h, t);
475         }
476
477         if (savelit)
478                 mh->fl |= MANH_LITERAL;
479
480         return(0);
481 }
482
483 /* ARGSUSED */
484 static int
485 man_SM_pre(MAN_ARGS)
486 {
487         
488         print_otag(h, TAG_SMALL, 0, NULL);
489         if (MAN_SB == n->tok)
490                 print_otag(h, TAG_B, 0, NULL);
491         return(1);
492 }
493
494 /* ARGSUSED */
495 static int
496 man_SS_pre(MAN_ARGS)
497 {
498         struct htmlpair  tag;
499
500         if (MAN_BLOCK == n->type) {
501                 mh->fl &= ~MANH_LITERAL;
502                 PAIR_CLASS_INIT(&tag, "subsection");
503                 print_otag(h, TAG_DIV, 1, &tag);
504                 return(1);
505         } else if (MAN_BODY == n->type)
506                 return(1);
507
508         print_otag(h, TAG_H2, 0, NULL);
509         return(1);
510 }
511
512 /* ARGSUSED */
513 static int
514 man_PP_pre(MAN_ARGS)
515 {
516
517         if (MAN_HEAD == n->type)
518                 return(0);
519         else if (MAN_BLOCK == n->type)
520                 print_bvspace(h, n);
521
522         return(1);
523 }
524
525 /* ARGSUSED */
526 static int
527 man_IP_pre(MAN_ARGS)
528 {
529         const struct man_node   *nn;
530
531         if (MAN_BODY == n->type) { 
532                 print_otag(h, TAG_DD, 0, NULL);
533                 return(1);
534         } else if (MAN_HEAD != n->type) {
535                 print_otag(h, TAG_DL, 0, NULL);
536                 return(1);
537         }
538
539         /* FIXME: width specification. */
540
541         print_otag(h, TAG_DT, 0, NULL);
542
543         /* For IP, only print the first header element. */
544
545         if (MAN_IP == n->tok && n->child)
546                 print_man_node(m, n->child, mh, h);
547
548         /* For TP, only print next-line header elements. */
549
550         if (MAN_TP == n->tok)
551                 for (nn = n->child; nn; nn = nn->next)
552                         if (nn->line > n->line)
553                                 print_man_node(m, nn, mh, h);
554
555         return(0);
556 }
557
558 /* ARGSUSED */
559 static int
560 man_HP_pre(MAN_ARGS)
561 {
562         struct htmlpair  tag;
563         struct roffsu    su;
564         const struct man_node *np;
565
566         if (MAN_HEAD == n->type)
567                 return(0);
568         else if (MAN_BLOCK != n->type)
569                 return(1);
570
571         np = n->head->child;
572
573         if (NULL == np || ! a2width(np, &su))
574                 SCALE_HS_INIT(&su, INDENT);
575
576         bufinit(h);
577
578         print_bvspace(h, n);
579         bufcat_su(h, "margin-left", &su);
580         su.scale = -su.scale;
581         bufcat_su(h, "text-indent", &su);
582         PAIR_STYLE_INIT(&tag, h);
583         print_otag(h, TAG_P, 1, &tag);
584         return(1);
585 }
586
587 /* ARGSUSED */
588 static int
589 man_OP_pre(MAN_ARGS)
590 {
591         struct tag      *tt;
592         struct htmlpair  tag;
593
594         print_text(h, "[");
595         h->flags |= HTML_NOSPACE;
596         PAIR_CLASS_INIT(&tag, "opt");
597         tt = print_otag(h, TAG_SPAN, 1, &tag);
598
599         if (NULL != (n = n->child)) {
600                 print_otag(h, TAG_B, 0, NULL);
601                 print_text(h, n->string);
602         }
603
604         print_stagq(h, tt);
605
606         if (NULL != n && NULL != n->next) {
607                 print_otag(h, TAG_I, 0, NULL);
608                 print_text(h, n->next->string);
609         }
610
611         print_stagq(h, tt);
612         h->flags |= HTML_NOSPACE;
613         print_text(h, "]");
614         return(0);
615 }
616
617
618 /* ARGSUSED */
619 static int
620 man_B_pre(MAN_ARGS)
621 {
622
623         print_otag(h, TAG_B, 0, NULL);
624         return(1);
625 }
626
627 /* ARGSUSED */
628 static int
629 man_I_pre(MAN_ARGS)
630 {
631         
632         print_otag(h, TAG_I, 0, NULL);
633         return(1);
634 }
635
636 /* ARGSUSED */
637 static int
638 man_literal_pre(MAN_ARGS)
639 {
640
641         if (MAN_nf != n->tok) {
642                 print_otag(h, TAG_BR, 0, NULL);
643                 mh->fl &= ~MANH_LITERAL;
644         } else
645                 mh->fl |= MANH_LITERAL;
646
647         return(0);
648 }
649
650 /* ARGSUSED */
651 static int
652 man_in_pre(MAN_ARGS)
653 {
654
655         print_otag(h, TAG_BR, 0, NULL);
656         return(0);
657 }
658
659 /* ARGSUSED */
660 static int
661 man_ign_pre(MAN_ARGS)
662 {
663
664         return(0);
665 }
666
667 /* ARGSUSED */
668 static int
669 man_RS_pre(MAN_ARGS)
670 {
671         struct htmlpair  tag;
672         struct roffsu    su;
673
674         if (MAN_HEAD == n->type)
675                 return(0);
676         else if (MAN_BODY == n->type)
677                 return(1);
678
679         SCALE_HS_INIT(&su, INDENT);
680         if (n->head->child)
681                 a2width(n->head->child, &su);
682
683         bufinit(h);
684         bufcat_su(h, "margin-left", &su);
685         PAIR_STYLE_INIT(&tag, h);
686         print_otag(h, TAG_DIV, 1, &tag);
687         return(1);
688 }