]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - man_html.c
Import mdocml 1.13.3
[FreeBSD/FreeBSD.git] / man_html.c
1 /*      $Id: man_html.c,v 1.112 2015/03/03 21:11:34 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "mandoc_aux.h"
29 #include "man.h"
30 #include "out.h"
31 #include "html.h"
32 #include "main.h"
33
34 /* TODO: preserve ident widths. */
35 /* FIXME: have PD set the default vspace width. */
36
37 #define INDENT            5
38
39 #define MAN_ARGS          const struct man_meta *man, \
40                           const struct man_node *n, \
41                           struct mhtml *mh, \
42                           struct html *h
43
44 struct  mhtml {
45         int               fl;
46 #define MANH_LITERAL     (1 << 0) /* literal context */
47 };
48
49 struct  htmlman {
50         int             (*pre)(MAN_ARGS);
51         int             (*post)(MAN_ARGS);
52 };
53
54 static  void              print_bvspace(struct html *,
55                                 const struct man_node *);
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 static  int               a2width(const struct man_node *,
61                                 struct roffsu *);
62 static  int               man_B_pre(MAN_ARGS);
63 static  int               man_HP_pre(MAN_ARGS);
64 static  int               man_IP_pre(MAN_ARGS);
65 static  int               man_I_pre(MAN_ARGS);
66 static  int               man_OP_pre(MAN_ARGS);
67 static  int               man_PP_pre(MAN_ARGS);
68 static  int               man_RS_pre(MAN_ARGS);
69 static  int               man_SH_pre(MAN_ARGS);
70 static  int               man_SM_pre(MAN_ARGS);
71 static  int               man_SS_pre(MAN_ARGS);
72 static  int               man_UR_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_br_pre, NULL }, /* sp */
104         { man_literal_pre, NULL }, /* nf */
105         { man_literal_pre, NULL }, /* fi */
106         { NULL, NULL }, /* RE */
107         { man_RS_pre, NULL }, /* RS */
108         { man_ign_pre, NULL }, /* DT */
109         { man_ign_pre, NULL }, /* UC */
110         { man_ign_pre, NULL }, /* PD */
111         { man_ign_pre, NULL }, /* AT */
112         { man_in_pre, NULL }, /* in */
113         { man_ign_pre, NULL }, /* ft */
114         { man_OP_pre, NULL }, /* OP */
115         { man_literal_pre, NULL }, /* EX */
116         { man_literal_pre, NULL }, /* EE */
117         { man_UR_pre, NULL }, /* UR */
118         { NULL, NULL }, /* UE */
119         { man_ign_pre, NULL }, /* ll */
120 };
121
122
123 /*
124  * Printing leading vertical space before a block.
125  * This is used for the paragraph macros.
126  * The rules are pretty simple, since there's very little nesting going
127  * on here.  Basically, if we're the first within another block (SS/SH),
128  * then don't emit vertical space.  If we are (RS), then do.  If not the
129  * first, print it.
130  */
131 static void
132 print_bvspace(struct html *h, const struct man_node *n)
133 {
134
135         if (n->body && n->body->child)
136                 if (MAN_TBL == n->body->child->type)
137                         return;
138
139         if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
140                 if (NULL == n->prev)
141                         return;
142
143         print_paragraph(h);
144 }
145
146 void
147 html_man(void *arg, const struct man *man)
148 {
149         struct mhtml     mh;
150
151         memset(&mh, 0, sizeof(struct mhtml));
152         print_man(man_meta(man), man_node(man), &mh, (struct html *)arg);
153         putchar('\n');
154 }
155
156 static void
157 print_man(MAN_ARGS)
158 {
159         struct tag      *t, *tt;
160         struct htmlpair  tag;
161
162         PAIR_CLASS_INIT(&tag, "mandoc");
163
164         if ( ! (HTML_FRAGMENT & h->oflags)) {
165                 print_gen_decls(h);
166                 t = print_otag(h, TAG_HTML, 0, NULL);
167                 tt = print_otag(h, TAG_HEAD, 0, NULL);
168                 print_man_head(man, n, mh, h);
169                 print_tagq(h, tt);
170                 print_otag(h, TAG_BODY, 0, NULL);
171                 print_otag(h, TAG_DIV, 1, &tag);
172         } else
173                 t = print_otag(h, TAG_DIV, 1, &tag);
174
175         print_man_nodelist(man, n, mh, h);
176         print_tagq(h, t);
177 }
178
179 static void
180 print_man_head(MAN_ARGS)
181 {
182
183         print_gen_head(h);
184         assert(man->title);
185         assert(man->msec);
186         bufcat_fmt(h, "%s(%s)", man->title, man->msec);
187         print_otag(h, TAG_TITLE, 0, NULL);
188         print_text(h, h->buf);
189 }
190
191 static void
192 print_man_nodelist(MAN_ARGS)
193 {
194
195         while (n != NULL) {
196                 print_man_node(man, n, mh, h);
197                 n = n->next;
198         }
199 }
200
201 static void
202 print_man_node(MAN_ARGS)
203 {
204         int              child;
205         struct tag      *t;
206
207         child = 1;
208         t = h->tags.head;
209
210         switch (n->type) {
211         case MAN_ROOT:
212                 man_root_pre(man, n, mh, h);
213                 break;
214         case MAN_TEXT:
215                 if ('\0' == *n->string) {
216                         print_paragraph(h);
217                         return;
218                 }
219                 if (n->flags & MAN_LINE && (*n->string == ' ' ||
220                     (n->prev != NULL && mh->fl & MANH_LITERAL &&
221                      ! (h->flags & HTML_NONEWLINE))))
222                         print_otag(h, TAG_BR, 0, NULL);
223                 print_text(h, n->string);
224                 return;
225         case MAN_EQN:
226                 if (n->flags & MAN_LINE)
227                         putchar('\n');
228                 print_eqn(h, n->eqn);
229                 break;
230         case MAN_TBL:
231                 /*
232                  * This will take care of initialising all of the table
233                  * state data for the first table, then tearing it down
234                  * for the last one.
235                  */
236                 print_tbl(h, n->span);
237                 return;
238         default:
239                 /*
240                  * Close out scope of font prior to opening a macro
241                  * scope.
242                  */
243                 if (HTMLFONT_NONE != h->metac) {
244                         h->metal = h->metac;
245                         h->metac = HTMLFONT_NONE;
246                 }
247
248                 /*
249                  * Close out the current table, if it's open, and unset
250                  * the "meta" table state.  This will be reopened on the
251                  * next table element.
252                  */
253                 if (h->tblt) {
254                         print_tblclose(h);
255                         t = h->tags.head;
256                 }
257                 if (mans[n->tok].pre)
258                         child = (*mans[n->tok].pre)(man, n, mh, h);
259                 break;
260         }
261
262         if (child && n->child)
263                 print_man_nodelist(man, n->child, mh, h);
264
265         /* This will automatically close out any font scope. */
266         print_stagq(h, t);
267
268         switch (n->type) {
269         case MAN_ROOT:
270                 man_root_post(man, n, mh, h);
271                 break;
272         case MAN_EQN:
273                 break;
274         default:
275                 if (mans[n->tok].post)
276                         (*mans[n->tok].post)(man, n, mh, h);
277                 break;
278         }
279 }
280
281 static int
282 a2width(const struct man_node *n, struct roffsu *su)
283 {
284
285         if (MAN_TEXT != n->type)
286                 return(0);
287         if (a2roffsu(n->string, su, SCALE_EN))
288                 return(1);
289
290         return(0);
291 }
292
293 static void
294 man_root_pre(MAN_ARGS)
295 {
296         struct htmlpair  tag;
297         struct tag      *t, *tt;
298         char            *title;
299
300         assert(man->title);
301         assert(man->msec);
302         mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
303
304         PAIR_CLASS_INIT(&tag, "head");
305         t = print_otag(h, TAG_TABLE, 1, &tag);
306
307         print_otag(h, TAG_TBODY, 0, NULL);
308
309         tt = print_otag(h, TAG_TR, 0, NULL);
310
311         PAIR_CLASS_INIT(&tag, "head-ltitle");
312         print_otag(h, TAG_TD, 1, &tag);
313         print_text(h, title);
314         print_stagq(h, tt);
315
316         PAIR_CLASS_INIT(&tag, "head-vol");
317         print_otag(h, TAG_TD, 1, &tag);
318         if (NULL != man->vol)
319                 print_text(h, man->vol);
320         print_stagq(h, tt);
321
322         PAIR_CLASS_INIT(&tag, "head-rtitle");
323         print_otag(h, TAG_TD, 1, &tag);
324         print_text(h, title);
325         print_tagq(h, t);
326         free(title);
327 }
328
329 static void
330 man_root_post(MAN_ARGS)
331 {
332         struct htmlpair  tag;
333         struct tag      *t, *tt;
334
335         PAIR_CLASS_INIT(&tag, "foot");
336         t = print_otag(h, TAG_TABLE, 1, &tag);
337
338         tt = print_otag(h, TAG_TR, 0, NULL);
339
340         PAIR_CLASS_INIT(&tag, "foot-date");
341         print_otag(h, TAG_TD, 1, &tag);
342
343         assert(man->date);
344         print_text(h, man->date);
345         print_stagq(h, tt);
346
347         PAIR_CLASS_INIT(&tag, "foot-os");
348         print_otag(h, TAG_TD, 1, &tag);
349
350         if (man->source)
351                 print_text(h, man->source);
352         print_tagq(h, t);
353 }
354
355
356 static int
357 man_br_pre(MAN_ARGS)
358 {
359         struct roffsu    su;
360         struct htmlpair  tag;
361
362         SCALE_VS_INIT(&su, 1);
363
364         if (MAN_sp == n->tok) {
365                 if (NULL != (n = n->child))
366                         if ( ! a2roffsu(n->string, &su, SCALE_VS))
367                                 su.scale = 1.0;
368         } else
369                 su.scale = 0.0;
370
371         bufinit(h);
372         bufcat_su(h, "height", &su);
373         PAIR_STYLE_INIT(&tag, h);
374         print_otag(h, TAG_DIV, 1, &tag);
375
376         /* So the div isn't empty: */
377         print_text(h, "\\~");
378
379         return(0);
380 }
381
382 static int
383 man_SH_pre(MAN_ARGS)
384 {
385         struct htmlpair  tag;
386
387         if (MAN_BLOCK == n->type) {
388                 mh->fl &= ~MANH_LITERAL;
389                 PAIR_CLASS_INIT(&tag, "section");
390                 print_otag(h, TAG_DIV, 1, &tag);
391                 return(1);
392         } else if (MAN_BODY == n->type)
393                 return(1);
394
395         print_otag(h, TAG_H1, 0, NULL);
396         return(1);
397 }
398
399 static int
400 man_alt_pre(MAN_ARGS)
401 {
402         const struct man_node   *nn;
403         int              i, savelit;
404         enum htmltag     fp;
405         struct tag      *t;
406
407         if ((savelit = mh->fl & MANH_LITERAL))
408                 print_otag(h, TAG_BR, 0, NULL);
409
410         mh->fl &= ~MANH_LITERAL;
411
412         for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
413                 t = NULL;
414                 switch (n->tok) {
415                 case MAN_BI:
416                         fp = i % 2 ? TAG_I : TAG_B;
417                         break;
418                 case MAN_IB:
419                         fp = i % 2 ? TAG_B : TAG_I;
420                         break;
421                 case MAN_RI:
422                         fp = i % 2 ? TAG_I : TAG_MAX;
423                         break;
424                 case MAN_IR:
425                         fp = i % 2 ? TAG_MAX : TAG_I;
426                         break;
427                 case MAN_BR:
428                         fp = i % 2 ? TAG_MAX : TAG_B;
429                         break;
430                 case MAN_RB:
431                         fp = i % 2 ? TAG_B : TAG_MAX;
432                         break;
433                 default:
434                         abort();
435                         /* NOTREACHED */
436                 }
437
438                 if (i)
439                         h->flags |= HTML_NOSPACE;
440
441                 if (TAG_MAX != fp)
442                         t = print_otag(h, fp, 0, NULL);
443
444                 print_man_node(man, nn, mh, h);
445
446                 if (t)
447                         print_tagq(h, t);
448         }
449
450         if (savelit)
451                 mh->fl |= MANH_LITERAL;
452
453         return(0);
454 }
455
456 static int
457 man_SM_pre(MAN_ARGS)
458 {
459
460         print_otag(h, TAG_SMALL, 0, NULL);
461         if (MAN_SB == n->tok)
462                 print_otag(h, TAG_B, 0, NULL);
463         return(1);
464 }
465
466 static int
467 man_SS_pre(MAN_ARGS)
468 {
469         struct htmlpair  tag;
470
471         if (MAN_BLOCK == n->type) {
472                 mh->fl &= ~MANH_LITERAL;
473                 PAIR_CLASS_INIT(&tag, "subsection");
474                 print_otag(h, TAG_DIV, 1, &tag);
475                 return(1);
476         } else if (MAN_BODY == n->type)
477                 return(1);
478
479         print_otag(h, TAG_H2, 0, NULL);
480         return(1);
481 }
482
483 static int
484 man_PP_pre(MAN_ARGS)
485 {
486
487         if (MAN_HEAD == n->type)
488                 return(0);
489         else if (MAN_BLOCK == n->type)
490                 print_bvspace(h, n);
491
492         return(1);
493 }
494
495 static int
496 man_IP_pre(MAN_ARGS)
497 {
498         const struct man_node   *nn;
499
500         if (MAN_BODY == n->type) {
501                 print_otag(h, TAG_DD, 0, NULL);
502                 return(1);
503         } else if (MAN_HEAD != n->type) {
504                 print_otag(h, TAG_DL, 0, NULL);
505                 return(1);
506         }
507
508         /* FIXME: width specification. */
509
510         print_otag(h, TAG_DT, 0, NULL);
511
512         /* For IP, only print the first header element. */
513
514         if (MAN_IP == n->tok && n->child)
515                 print_man_node(man, n->child, mh, h);
516
517         /* For TP, only print next-line header elements. */
518
519         if (MAN_TP == n->tok) {
520                 nn = n->child;
521                 while (NULL != nn && 0 == (MAN_LINE & nn->flags))
522                         nn = nn->next;
523                 while (NULL != nn) {
524                         print_man_node(man, nn, mh, h);
525                         nn = nn->next;
526                 }
527         }
528
529         return(0);
530 }
531
532 static int
533 man_HP_pre(MAN_ARGS)
534 {
535         struct htmlpair  tag[2];
536         struct roffsu    su;
537         const struct man_node *np;
538
539         if (MAN_HEAD == n->type)
540                 return(0);
541         else if (MAN_BLOCK != n->type)
542                 return(1);
543
544         np = n->head->child;
545
546         if (NULL == np || ! a2width(np, &su))
547                 SCALE_HS_INIT(&su, INDENT);
548
549         bufinit(h);
550
551         print_bvspace(h, n);
552         bufcat_su(h, "margin-left", &su);
553         su.scale = -su.scale;
554         bufcat_su(h, "text-indent", &su);
555         PAIR_STYLE_INIT(&tag[0], h);
556         PAIR_CLASS_INIT(&tag[1], "spacer");
557         print_otag(h, TAG_DIV, 2, tag);
558         return(1);
559 }
560
561 static int
562 man_OP_pre(MAN_ARGS)
563 {
564         struct tag      *tt;
565         struct htmlpair  tag;
566
567         print_text(h, "[");
568         h->flags |= HTML_NOSPACE;
569         PAIR_CLASS_INIT(&tag, "opt");
570         tt = print_otag(h, TAG_SPAN, 1, &tag);
571
572         if (NULL != (n = n->child)) {
573                 print_otag(h, TAG_B, 0, NULL);
574                 print_text(h, n->string);
575         }
576
577         print_stagq(h, tt);
578
579         if (NULL != n && NULL != n->next) {
580                 print_otag(h, TAG_I, 0, NULL);
581                 print_text(h, n->next->string);
582         }
583
584         print_stagq(h, tt);
585         h->flags |= HTML_NOSPACE;
586         print_text(h, "]");
587         return(0);
588 }
589
590 static int
591 man_B_pre(MAN_ARGS)
592 {
593
594         print_otag(h, TAG_B, 0, NULL);
595         return(1);
596 }
597
598 static int
599 man_I_pre(MAN_ARGS)
600 {
601
602         print_otag(h, TAG_I, 0, NULL);
603         return(1);
604 }
605
606 static int
607 man_literal_pre(MAN_ARGS)
608 {
609
610         if (MAN_fi == n->tok || MAN_EE == n->tok) {
611                 print_otag(h, TAG_BR, 0, NULL);
612                 mh->fl &= ~MANH_LITERAL;
613         } else
614                 mh->fl |= MANH_LITERAL;
615
616         return(0);
617 }
618
619 static int
620 man_in_pre(MAN_ARGS)
621 {
622
623         print_otag(h, TAG_BR, 0, NULL);
624         return(0);
625 }
626
627 static int
628 man_ign_pre(MAN_ARGS)
629 {
630
631         return(0);
632 }
633
634 static int
635 man_RS_pre(MAN_ARGS)
636 {
637         struct htmlpair  tag;
638         struct roffsu    su;
639
640         if (MAN_HEAD == n->type)
641                 return(0);
642         else if (MAN_BODY == n->type)
643                 return(1);
644
645         SCALE_HS_INIT(&su, INDENT);
646         if (n->head->child)
647                 a2width(n->head->child, &su);
648
649         bufinit(h);
650         bufcat_su(h, "margin-left", &su);
651         PAIR_STYLE_INIT(&tag, h);
652         print_otag(h, TAG_DIV, 1, &tag);
653         return(1);
654 }
655
656 static int
657 man_UR_pre(MAN_ARGS)
658 {
659         struct htmlpair          tag[2];
660
661         n = n->child;
662         assert(MAN_HEAD == n->type);
663         if (n->nchild) {
664                 assert(MAN_TEXT == n->child->type);
665                 PAIR_CLASS_INIT(&tag[0], "link-ext");
666                 PAIR_HREF_INIT(&tag[1], n->child->string);
667                 print_otag(h, TAG_A, 2, tag);
668         }
669
670         assert(MAN_BODY == n->next->type);
671         if (n->next->nchild)
672                 n = n->next;
673
674         print_man_nodelist(man, n->child, mh, h);
675
676         return(0);
677 }