]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/man_html.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r303571, and update
[FreeBSD/FreeBSD.git] / contrib / mdocml / man_html.c
1 /*      $Id: man_html.c,v 1.133 2017/02/05 18:15:39 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2013, 2014, 2015, 2017 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 AUTHORS DISCLAIM ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 "roff.h"
30 #include "man.h"
31 #include "out.h"
32 #include "html.h"
33 #include "main.h"
34
35 /* FIXME: have PD set the default vspace width. */
36
37 #define INDENT            5
38
39 #define MAN_ARGS          const struct roff_meta *man, \
40                           const struct roff_node *n, \
41                           struct html *h
42
43 struct  htmlman {
44         int             (*pre)(MAN_ARGS);
45         int             (*post)(MAN_ARGS);
46 };
47
48 static  void              print_bvspace(struct html *,
49                                 const struct roff_node *);
50 static  void              print_man_head(MAN_ARGS);
51 static  void              print_man_nodelist(MAN_ARGS);
52 static  void              print_man_node(MAN_ARGS);
53 static  int               fillmode(struct html *, int);
54 static  int               a2width(const struct roff_node *,
55                                 struct roffsu *);
56 static  int               man_B_pre(MAN_ARGS);
57 static  int               man_HP_pre(MAN_ARGS);
58 static  int               man_IP_pre(MAN_ARGS);
59 static  int               man_I_pre(MAN_ARGS);
60 static  int               man_OP_pre(MAN_ARGS);
61 static  int               man_PP_pre(MAN_ARGS);
62 static  int               man_RS_pre(MAN_ARGS);
63 static  int               man_SH_pre(MAN_ARGS);
64 static  int               man_SM_pre(MAN_ARGS);
65 static  int               man_SS_pre(MAN_ARGS);
66 static  int               man_UR_pre(MAN_ARGS);
67 static  int               man_alt_pre(MAN_ARGS);
68 static  int               man_br_pre(MAN_ARGS);
69 static  int               man_ign_pre(MAN_ARGS);
70 static  int               man_in_pre(MAN_ARGS);
71 static  void              man_root_post(MAN_ARGS);
72 static  void              man_root_pre(MAN_ARGS);
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_SM_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         { man_br_pre, NULL }, /* sp */
97         { NULL, NULL }, /* nf */
98         { NULL, NULL }, /* fi */
99         { NULL, NULL }, /* RE */
100         { man_RS_pre, NULL }, /* RS */
101         { man_ign_pre, NULL }, /* DT */
102         { man_ign_pre, NULL }, /* UC */
103         { man_ign_pre, NULL }, /* PD */
104         { man_ign_pre, NULL }, /* AT */
105         { man_in_pre, NULL }, /* in */
106         { man_ign_pre, NULL }, /* ft */
107         { man_OP_pre, NULL }, /* OP */
108         { NULL, NULL }, /* EX */
109         { NULL, NULL }, /* EE */
110         { man_UR_pre, NULL }, /* UR */
111         { NULL, NULL }, /* UE */
112         { man_ign_pre, NULL }, /* ll */
113 };
114
115
116 /*
117  * Printing leading vertical space before a block.
118  * This is used for the paragraph macros.
119  * The rules are pretty simple, since there's very little nesting going
120  * on here.  Basically, if we're the first within another block (SS/SH),
121  * then don't emit vertical space.  If we are (RS), then do.  If not the
122  * first, print it.
123  */
124 static void
125 print_bvspace(struct html *h, const struct roff_node *n)
126 {
127
128         if (n->body && n->body->child)
129                 if (n->body->child->type == ROFFT_TBL)
130                         return;
131
132         if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
133                 if (NULL == n->prev)
134                         return;
135
136         print_paragraph(h);
137 }
138
139 void
140 html_man(void *arg, const struct roff_man *man)
141 {
142         struct html     *h;
143         struct tag      *t;
144
145         h = (struct html *)arg;
146
147         if ((h->oflags & HTML_FRAGMENT) == 0) {
148                 print_gen_decls(h);
149                 print_otag(h, TAG_HTML, "");
150                 t = print_otag(h, TAG_HEAD, "");
151                 print_man_head(&man->meta, man->first, h);
152                 print_tagq(h, t);
153                 print_otag(h, TAG_BODY, "");
154         }
155
156         man_root_pre(&man->meta, man->first, h);
157         t = print_otag(h, TAG_DIV, "c", "manual-text");
158         print_man_nodelist(&man->meta, man->first->child, h);
159         print_tagq(h, t);
160         man_root_post(&man->meta, man->first, h);
161         print_tagq(h, NULL);
162 }
163
164 static void
165 print_man_head(MAN_ARGS)
166 {
167         char    *cp;
168
169         print_gen_head(h);
170         mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
171         print_otag(h, TAG_TITLE, "");
172         print_text(h, cp);
173         free(cp);
174 }
175
176 static void
177 print_man_nodelist(MAN_ARGS)
178 {
179
180         while (n != NULL) {
181                 print_man_node(man, n, h);
182                 n = n->next;
183         }
184 }
185
186 static void
187 print_man_node(MAN_ARGS)
188 {
189         static int       want_fillmode = MAN_fi;
190         static int       save_fillmode;
191
192         struct tag      *t;
193         int              child;
194
195         /*
196          * Handle fill mode switch requests up front,
197          * they would just cause trouble in the subsequent code.
198          */
199
200         switch (n->tok) {
201         case MAN_nf:
202         case MAN_EX:
203                 want_fillmode = MAN_nf;
204                 return;
205         case MAN_fi:
206         case MAN_EE:
207                 want_fillmode = MAN_fi;
208                 if (fillmode(h, 0) == MAN_fi)
209                         print_otag(h, TAG_BR, "");
210                 return;
211         default:
212                 break;
213         }
214
215         /* Set up fill mode for the upcoming node. */
216
217         switch (n->type) {
218         case ROFFT_BLOCK:
219                 save_fillmode = 0;
220                 /* Some block macros suspend or cancel .nf. */
221                 switch (n->tok) {
222                 case MAN_TP:  /* Tagged paragraphs              */
223                 case MAN_IP:  /* temporarily disable .nf        */
224                 case MAN_HP:  /* for the head.                  */
225                         save_fillmode = want_fillmode;
226                         /* FALLTHROUGH */
227                 case MAN_SH:  /* Section headers                */
228                 case MAN_SS:  /* permanently cancel .nf.        */
229                         want_fillmode = MAN_fi;
230                         /* FALLTHROUGH */
231                 case MAN_PP:  /* These have no head.            */
232                 case MAN_LP:  /* They will simply               */
233                 case MAN_P:   /* reopen .nf in the body.        */
234                 case MAN_RS:
235                 case MAN_UR:
236                         fillmode(h, MAN_fi);
237                         break;
238                 default:
239                         break;
240                 }
241                 break;
242         case ROFFT_TBL:
243                 fillmode(h, MAN_fi);
244                 break;
245         case ROFFT_ELEM:
246                 /*
247                  * Some in-line macros produce tags and/or text
248                  * in the handler, so they require fill mode to be
249                  * configured up front just like for text nodes.
250                  * For the others, keep the traditional approach
251                  * of doing the same, for now.
252                  */
253                 fillmode(h, want_fillmode);
254                 break;
255         case ROFFT_TEXT:
256                 if (fillmode(h, want_fillmode) == MAN_fi &&
257                     want_fillmode == MAN_fi &&
258                     n->flags & NODE_LINE && *n->string == ' ')
259                         print_otag(h, TAG_BR, "");
260                 if (*n->string != '\0')
261                         break;
262                 print_paragraph(h);
263                 return;
264         default:
265                 break;
266         }
267
268         /* Produce output for this node. */
269
270         child = 1;
271         switch (n->type) {
272         case ROFFT_TEXT:
273                 t = h->tag;
274                 print_text(h, n->string);
275                 break;
276         case ROFFT_EQN:
277                 t = h->tag;
278                 print_eqn(h, n->eqn);
279                 break;
280         case ROFFT_TBL:
281                 /*
282                  * This will take care of initialising all of the table
283                  * state data for the first table, then tearing it down
284                  * for the last one.
285                  */
286                 print_tbl(h, n->span);
287                 return;
288         default:
289                 /*
290                  * Close out scope of font prior to opening a macro
291                  * scope.
292                  */
293                 if (HTMLFONT_NONE != h->metac) {
294                         h->metal = h->metac;
295                         h->metac = HTMLFONT_NONE;
296                 }
297
298                 /*
299                  * Close out the current table, if it's open, and unset
300                  * the "meta" table state.  This will be reopened on the
301                  * next table element.
302                  */
303                 if (h->tblt)
304                         print_tblclose(h);
305
306                 t = h->tag;
307                 if (mans[n->tok].pre)
308                         child = (*mans[n->tok].pre)(man, n, h);
309
310                 /* Some block macros resume .nf in the body. */
311                 if (save_fillmode && n->type == ROFFT_BODY)
312                         want_fillmode = save_fillmode;
313
314                 break;
315         }
316
317         if (child && n->child)
318                 print_man_nodelist(man, n->child, h);
319
320         /* This will automatically close out any font scope. */
321         print_stagq(h, t);
322
323         if (fillmode(h, 0) == MAN_nf &&
324             n->next != NULL && n->next->flags & NODE_LINE)
325                 print_endline(h);
326 }
327
328 /*
329  * MAN_nf switches to no-fill mode, MAN_fi to fill mode.
330  * Other arguments do not switch.
331  * The old mode is returned.
332  */
333 static int
334 fillmode(struct html *h, int want)
335 {
336         struct tag      *pre;
337         int              had;
338
339         for (pre = h->tag; pre != NULL; pre = pre->next)
340                 if (pre->tag == TAG_PRE)
341                         break;
342
343         had = pre == NULL ? MAN_fi : MAN_nf;
344
345         if (want && want != had) {
346                 if (want == MAN_nf)
347                         print_otag(h, TAG_PRE, "");
348                 else
349                         print_tagq(h, pre);
350         }
351         return had;
352 }
353
354 static int
355 a2width(const struct roff_node *n, struct roffsu *su)
356 {
357
358         if (n->type != ROFFT_TEXT)
359                 return 0;
360         if (a2roffsu(n->string, su, SCALE_EN))
361                 return 1;
362
363         return 0;
364 }
365
366 static void
367 man_root_pre(MAN_ARGS)
368 {
369         struct tag      *t, *tt;
370         char            *title;
371
372         assert(man->title);
373         assert(man->msec);
374         mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
375
376         t = print_otag(h, TAG_TABLE, "c", "head");
377         tt = print_otag(h, TAG_TR, "");
378
379         print_otag(h, TAG_TD, "c", "head-ltitle");
380         print_text(h, title);
381         print_stagq(h, tt);
382
383         print_otag(h, TAG_TD, "c", "head-vol");
384         if (NULL != man->vol)
385                 print_text(h, man->vol);
386         print_stagq(h, tt);
387
388         print_otag(h, TAG_TD, "c", "head-rtitle");
389         print_text(h, title);
390         print_tagq(h, t);
391         free(title);
392 }
393
394 static void
395 man_root_post(MAN_ARGS)
396 {
397         struct tag      *t, *tt;
398
399         t = print_otag(h, TAG_TABLE, "c", "foot");
400         tt = print_otag(h, TAG_TR, "");
401
402         print_otag(h, TAG_TD, "c", "foot-date");
403         print_text(h, man->date);
404         print_stagq(h, tt);
405
406         print_otag(h, TAG_TD, "c", "foot-os");
407         if (man->os)
408                 print_text(h, man->os);
409         print_tagq(h, t);
410 }
411
412
413 static int
414 man_br_pre(MAN_ARGS)
415 {
416         struct roffsu    su;
417
418         SCALE_VS_INIT(&su, 1);
419
420         if (MAN_sp == n->tok) {
421                 if (NULL != (n = n->child))
422                         if ( ! a2roffsu(n->string, &su, SCALE_VS))
423                                 su.scale = 1.0;
424         } else
425                 su.scale = 0.0;
426
427         print_otag(h, TAG_DIV, "suh", &su);
428
429         /* So the div isn't empty: */
430         print_text(h, "\\~");
431
432         return 0;
433 }
434
435 static int
436 man_SH_pre(MAN_ARGS)
437 {
438         if (n->type == ROFFT_HEAD)
439                 print_otag(h, TAG_H1, "c", "Sh");
440         return 1;
441 }
442
443 static int
444 man_alt_pre(MAN_ARGS)
445 {
446         const struct roff_node  *nn;
447         int              i;
448         enum htmltag     fp;
449         struct tag      *t;
450
451         for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
452                 switch (n->tok) {
453                 case MAN_BI:
454                         fp = i % 2 ? TAG_I : TAG_B;
455                         break;
456                 case MAN_IB:
457                         fp = i % 2 ? TAG_B : TAG_I;
458                         break;
459                 case MAN_RI:
460                         fp = i % 2 ? TAG_I : TAG_MAX;
461                         break;
462                 case MAN_IR:
463                         fp = i % 2 ? TAG_MAX : TAG_I;
464                         break;
465                 case MAN_BR:
466                         fp = i % 2 ? TAG_MAX : TAG_B;
467                         break;
468                 case MAN_RB:
469                         fp = i % 2 ? TAG_B : TAG_MAX;
470                         break;
471                 default:
472                         abort();
473                 }
474
475                 if (i)
476                         h->flags |= HTML_NOSPACE;
477
478                 if (fp != TAG_MAX)
479                         t = print_otag(h, fp, "");
480
481                 print_text(h, nn->string);
482
483                 if (fp != TAG_MAX)
484                         print_tagq(h, t);
485         }
486         return 0;
487 }
488
489 static int
490 man_SM_pre(MAN_ARGS)
491 {
492         print_otag(h, TAG_SMALL, "");
493         if (MAN_SB == n->tok)
494                 print_otag(h, TAG_B, "");
495         return 1;
496 }
497
498 static int
499 man_SS_pre(MAN_ARGS)
500 {
501         if (n->type == ROFFT_HEAD)
502                 print_otag(h, TAG_H2, "c", "Ss");
503         return 1;
504 }
505
506 static int
507 man_PP_pre(MAN_ARGS)
508 {
509
510         if (n->type == ROFFT_HEAD)
511                 return 0;
512         else if (n->type == ROFFT_BLOCK)
513                 print_bvspace(h, n);
514
515         return 1;
516 }
517
518 static int
519 man_IP_pre(MAN_ARGS)
520 {
521         const struct roff_node  *nn;
522
523         if (n->type == ROFFT_BODY) {
524                 print_otag(h, TAG_DD, "c", "It-tag");
525                 return 1;
526         } else if (n->type != ROFFT_HEAD) {
527                 print_otag(h, TAG_DL, "c", "Bl-tag");
528                 return 1;
529         }
530
531         /* FIXME: width specification. */
532
533         print_otag(h, TAG_DT, "c", "It-tag");
534
535         /* For IP, only print the first header element. */
536
537         if (MAN_IP == n->tok && n->child)
538                 print_man_node(man, n->child, h);
539
540         /* For TP, only print next-line header elements. */
541
542         if (MAN_TP == n->tok) {
543                 nn = n->child;
544                 while (NULL != nn && 0 == (NODE_LINE & nn->flags))
545                         nn = nn->next;
546                 while (NULL != nn) {
547                         print_man_node(man, nn, h);
548                         nn = nn->next;
549                 }
550         }
551
552         return 0;
553 }
554
555 static int
556 man_HP_pre(MAN_ARGS)
557 {
558         struct roffsu    sum, sui;
559         const struct roff_node *np;
560
561         if (n->type == ROFFT_HEAD)
562                 return 0;
563         else if (n->type != ROFFT_BLOCK)
564                 return 1;
565
566         np = n->head->child;
567
568         if (np == NULL || !a2width(np, &sum))
569                 SCALE_HS_INIT(&sum, INDENT);
570
571         sui.unit = sum.unit;
572         sui.scale = -sum.scale;
573
574         print_bvspace(h, n);
575         print_otag(h, TAG_DIV, "csului", "Pp", &sum, &sui);
576         return 1;
577 }
578
579 static int
580 man_OP_pre(MAN_ARGS)
581 {
582         struct tag      *tt;
583
584         print_text(h, "[");
585         h->flags |= HTML_NOSPACE;
586         tt = print_otag(h, TAG_SPAN, "c", "Op");
587
588         if (NULL != (n = n->child)) {
589                 print_otag(h, TAG_B, "");
590                 print_text(h, n->string);
591         }
592
593         print_stagq(h, tt);
594
595         if (NULL != n && NULL != n->next) {
596                 print_otag(h, TAG_I, "");
597                 print_text(h, n->next->string);
598         }
599
600         print_stagq(h, tt);
601         h->flags |= HTML_NOSPACE;
602         print_text(h, "]");
603         return 0;
604 }
605
606 static int
607 man_B_pre(MAN_ARGS)
608 {
609         print_otag(h, TAG_B, "");
610         return 1;
611 }
612
613 static int
614 man_I_pre(MAN_ARGS)
615 {
616         print_otag(h, TAG_I, "");
617         return 1;
618 }
619
620 static int
621 man_in_pre(MAN_ARGS)
622 {
623         print_otag(h, TAG_BR, "");
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 roffsu    su;
638
639         if (n->type == ROFFT_HEAD)
640                 return 0;
641         else if (n->type == ROFFT_BODY)
642                 return 1;
643
644         SCALE_HS_INIT(&su, INDENT);
645         if (n->head->child)
646                 a2width(n->head->child, &su);
647
648         print_otag(h, TAG_DIV, "sul", &su);
649         return 1;
650 }
651
652 static int
653 man_UR_pre(MAN_ARGS)
654 {
655         n = n->child;
656         assert(n->type == ROFFT_HEAD);
657         if (n->child != NULL) {
658                 assert(n->child->type == ROFFT_TEXT);
659                 print_otag(h, TAG_A, "ch", "Lk", n->child->string);
660         }
661
662         assert(n->next->type == ROFFT_BODY);
663         if (n->next->child != NULL)
664                 n = n->next;
665
666         print_man_nodelist(man, n->child, h);
667
668         return 0;
669 }