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