]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/man_html.c
Merge ^/head r319548 through r319778.
[FreeBSD/FreeBSD.git] / contrib / mdocml / man_html.c
1 /*      $Id: man_html.c,v 1.143 2017/06/08 12:54:58 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_ign_pre(MAN_ARGS);
69 static  int               man_in_pre(MAN_ARGS);
70 static  void              man_root_post(MAN_ARGS);
71 static  void              man_root_pre(MAN_ARGS);
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 };
109 static  const struct htmlman *const mans = __mans - MAN_TH;
110
111
112 /*
113  * Printing leading vertical space before a block.
114  * This is used for the paragraph macros.
115  * The rules are pretty simple, since there's very little nesting going
116  * on here.  Basically, if we're the first within another block (SS/SH),
117  * then don't emit vertical space.  If we are (RS), then do.  If not the
118  * first, print it.
119  */
120 static void
121 print_bvspace(struct html *h, const struct roff_node *n)
122 {
123
124         if (n->body && n->body->child)
125                 if (n->body->child->type == ROFFT_TBL)
126                         return;
127
128         if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
129                 if (NULL == n->prev)
130                         return;
131
132         print_paragraph(h);
133 }
134
135 void
136 html_man(void *arg, const struct roff_man *man)
137 {
138         struct html     *h;
139         struct tag      *t;
140
141         h = (struct html *)arg;
142
143         if ((h->oflags & HTML_FRAGMENT) == 0) {
144                 print_gen_decls(h);
145                 print_otag(h, TAG_HTML, "");
146                 t = print_otag(h, TAG_HEAD, "");
147                 print_man_head(&man->meta, man->first, h);
148                 print_tagq(h, t);
149                 print_otag(h, TAG_BODY, "");
150         }
151
152         man_root_pre(&man->meta, man->first, h);
153         t = print_otag(h, TAG_DIV, "c", "manual-text");
154         print_man_nodelist(&man->meta, man->first->child, h);
155         print_tagq(h, t);
156         man_root_post(&man->meta, man->first, h);
157         print_tagq(h, NULL);
158 }
159
160 static void
161 print_man_head(MAN_ARGS)
162 {
163         char    *cp;
164
165         print_gen_head(h);
166         mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
167         print_otag(h, TAG_TITLE, "");
168         print_text(h, cp);
169         free(cp);
170 }
171
172 static void
173 print_man_nodelist(MAN_ARGS)
174 {
175
176         while (n != NULL) {
177                 print_man_node(man, n, h);
178                 n = n->next;
179         }
180 }
181
182 static void
183 print_man_node(MAN_ARGS)
184 {
185         static int       want_fillmode = MAN_fi;
186         static int       save_fillmode;
187
188         struct tag      *t;
189         int              child;
190
191         /*
192          * Handle fill mode switch requests up front,
193          * they would just cause trouble in the subsequent code.
194          */
195
196         switch (n->tok) {
197         case MAN_nf:
198         case MAN_EX:
199                 want_fillmode = MAN_nf;
200                 return;
201         case MAN_fi:
202         case MAN_EE:
203                 want_fillmode = MAN_fi;
204                 if (fillmode(h, 0) == MAN_fi)
205                         print_otag(h, TAG_BR, "");
206                 return;
207         default:
208                 break;
209         }
210
211         /* Set up fill mode for the upcoming node. */
212
213         switch (n->type) {
214         case ROFFT_BLOCK:
215                 save_fillmode = 0;
216                 /* Some block macros suspend or cancel .nf. */
217                 switch (n->tok) {
218                 case MAN_TP:  /* Tagged paragraphs              */
219                 case MAN_IP:  /* temporarily disable .nf        */
220                 case MAN_HP:  /* for the head.                  */
221                         save_fillmode = want_fillmode;
222                         /* FALLTHROUGH */
223                 case MAN_SH:  /* Section headers                */
224                 case MAN_SS:  /* permanently cancel .nf.        */
225                         want_fillmode = MAN_fi;
226                         /* FALLTHROUGH */
227                 case MAN_PP:  /* These have no head.            */
228                 case MAN_LP:  /* They will simply               */
229                 case MAN_P:   /* reopen .nf in the body.        */
230                 case MAN_RS:
231                 case MAN_UR:
232                         fillmode(h, MAN_fi);
233                         break;
234                 default:
235                         break;
236                 }
237                 break;
238         case ROFFT_TBL:
239                 fillmode(h, MAN_fi);
240                 break;
241         case ROFFT_ELEM:
242                 /*
243                  * Some in-line macros produce tags and/or text
244                  * in the handler, so they require fill mode to be
245                  * configured up front just like for text nodes.
246                  * For the others, keep the traditional approach
247                  * of doing the same, for now.
248                  */
249                 fillmode(h, want_fillmode);
250                 break;
251         case ROFFT_TEXT:
252                 if (fillmode(h, want_fillmode) == MAN_fi &&
253                     want_fillmode == MAN_fi &&
254                     n->flags & NODE_LINE && *n->string == ' ' &&
255                     (h->flags & HTML_NONEWLINE) == 0)
256                         print_otag(h, TAG_BR, "");
257                 if (*n->string != '\0')
258                         break;
259                 print_paragraph(h);
260                 return;
261         default:
262                 break;
263         }
264
265         /* Produce output for this node. */
266
267         child = 1;
268         switch (n->type) {
269         case ROFFT_TEXT:
270                 t = h->tag;
271                 print_text(h, n->string);
272                 break;
273         case ROFFT_EQN:
274                 t = h->tag;
275                 print_eqn(h, n->eqn);
276                 break;
277         case ROFFT_TBL:
278                 /*
279                  * This will take care of initialising all of the table
280                  * state data for the first table, then tearing it down
281                  * for the last one.
282                  */
283                 print_tbl(h, n->span);
284                 return;
285         default:
286                 /*
287                  * Close out scope of font prior to opening a macro
288                  * scope.
289                  */
290                 if (HTMLFONT_NONE != h->metac) {
291                         h->metal = h->metac;
292                         h->metac = HTMLFONT_NONE;
293                 }
294
295                 /*
296                  * Close out the current table, if it's open, and unset
297                  * the "meta" table state.  This will be reopened on the
298                  * next table element.
299                  */
300                 if (h->tblt)
301                         print_tblclose(h);
302
303                 t = h->tag;
304                 if (n->tok < ROFF_MAX) {
305                         roff_html_pre(h, n);
306                         child = 0;
307                         break;
308                 }
309
310                 assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
311                 if (mans[n->tok].pre)
312                         child = (*mans[n->tok].pre)(man, n, h);
313
314                 /* Some block macros resume .nf in the body. */
315                 if (save_fillmode && n->type == ROFFT_BODY)
316                         want_fillmode = save_fillmode;
317
318                 break;
319         }
320
321         if (child && n->child)
322                 print_man_nodelist(man, n->child, h);
323
324         /* This will automatically close out any font scope. */
325         print_stagq(h, t);
326
327         if (fillmode(h, 0) == MAN_nf &&
328             n->next != NULL && n->next->flags & NODE_LINE)
329                 print_endline(h);
330 }
331
332 /*
333  * MAN_nf switches to no-fill mode, MAN_fi to fill mode.
334  * Other arguments do not switch.
335  * The old mode is returned.
336  */
337 static int
338 fillmode(struct html *h, int want)
339 {
340         struct tag      *pre;
341         int              had;
342
343         for (pre = h->tag; pre != NULL; pre = pre->next)
344                 if (pre->tag == TAG_PRE)
345                         break;
346
347         had = pre == NULL ? MAN_fi : MAN_nf;
348
349         if (want && want != had) {
350                 if (want == MAN_nf)
351                         print_otag(h, TAG_PRE, "");
352                 else
353                         print_tagq(h, pre);
354         }
355         return had;
356 }
357
358 static int
359 a2width(const struct roff_node *n, struct roffsu *su)
360 {
361         if (n->type != ROFFT_TEXT)
362                 return 0;
363         return a2roffsu(n->string, su, SCALE_EN) != NULL;
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 static int
413 man_SH_pre(MAN_ARGS)
414 {
415         char    *id;
416
417         if (n->type == ROFFT_HEAD) {
418                 id = html_make_id(n);
419                 print_otag(h, TAG_H1, "cTi", "Sh", id);
420                 if (id != NULL)
421                         print_otag(h, TAG_A, "chR", "selflink", id);
422                 free(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);
489                 print_otag(h, TAG_H2, "cTi", "Ss", id);
490                 if (id != NULL)
491                         print_otag(h, TAG_A, "chR", "selflink", id);
492                 free(id);
493         }
494         return 1;
495 }
496
497 static int
498 man_PP_pre(MAN_ARGS)
499 {
500
501         if (n->type == ROFFT_HEAD)
502                 return 0;
503         else if (n->type == ROFFT_BLOCK)
504                 print_bvspace(h, n);
505
506         return 1;
507 }
508
509 static int
510 man_IP_pre(MAN_ARGS)
511 {
512         const struct roff_node  *nn;
513
514         if (n->type == ROFFT_BODY) {
515                 print_otag(h, TAG_DD, "c", "It-tag");
516                 return 1;
517         } else if (n->type != ROFFT_HEAD) {
518                 print_otag(h, TAG_DL, "c", "Bl-tag");
519                 return 1;
520         }
521
522         /* FIXME: width specification. */
523
524         print_otag(h, TAG_DT, "c", "It-tag");
525
526         /* For IP, only print the first header element. */
527
528         if (MAN_IP == n->tok && n->child)
529                 print_man_node(man, n->child, h);
530
531         /* For TP, only print next-line header elements. */
532
533         if (MAN_TP == n->tok) {
534                 nn = n->child;
535                 while (NULL != nn && 0 == (NODE_LINE & nn->flags))
536                         nn = nn->next;
537                 while (NULL != nn) {
538                         print_man_node(man, nn, h);
539                         nn = nn->next;
540                 }
541         }
542
543         return 0;
544 }
545
546 static int
547 man_HP_pre(MAN_ARGS)
548 {
549         struct roffsu    sum, sui;
550         const struct roff_node *np;
551
552         if (n->type == ROFFT_HEAD)
553                 return 0;
554         else if (n->type != ROFFT_BLOCK)
555                 return 1;
556
557         np = n->head->child;
558
559         if (np == NULL || !a2width(np, &sum))
560                 SCALE_HS_INIT(&sum, INDENT);
561
562         sui.unit = sum.unit;
563         sui.scale = -sum.scale;
564
565         print_bvspace(h, n);
566         print_otag(h, TAG_DIV, "csului", "Pp", &sum, &sui);
567         return 1;
568 }
569
570 static int
571 man_OP_pre(MAN_ARGS)
572 {
573         struct tag      *tt;
574
575         print_text(h, "[");
576         h->flags |= HTML_NOSPACE;
577         tt = print_otag(h, TAG_SPAN, "c", "Op");
578
579         if (NULL != (n = n->child)) {
580                 print_otag(h, TAG_B, "");
581                 print_text(h, n->string);
582         }
583
584         print_stagq(h, tt);
585
586         if (NULL != n && NULL != n->next) {
587                 print_otag(h, TAG_I, "");
588                 print_text(h, n->next->string);
589         }
590
591         print_stagq(h, tt);
592         h->flags |= HTML_NOSPACE;
593         print_text(h, "]");
594         return 0;
595 }
596
597 static int
598 man_B_pre(MAN_ARGS)
599 {
600         print_otag(h, TAG_B, "");
601         return 1;
602 }
603
604 static int
605 man_I_pre(MAN_ARGS)
606 {
607         print_otag(h, TAG_I, "");
608         return 1;
609 }
610
611 static int
612 man_in_pre(MAN_ARGS)
613 {
614         print_otag(h, TAG_BR, "");
615         return 0;
616 }
617
618 static int
619 man_ign_pre(MAN_ARGS)
620 {
621
622         return 0;
623 }
624
625 static int
626 man_RS_pre(MAN_ARGS)
627 {
628         struct roffsu    su;
629
630         if (n->type == ROFFT_HEAD)
631                 return 0;
632         else if (n->type == ROFFT_BODY)
633                 return 1;
634
635         SCALE_HS_INIT(&su, INDENT);
636         if (n->head->child)
637                 a2width(n->head->child, &su);
638
639         print_otag(h, TAG_DIV, "sul", &su);
640         return 1;
641 }
642
643 static int
644 man_UR_pre(MAN_ARGS)
645 {
646         n = n->child;
647         assert(n->type == ROFFT_HEAD);
648         if (n->child != NULL) {
649                 assert(n->child->type == ROFFT_TEXT);
650                 print_otag(h, TAG_A, "cTh", "Lk", n->child->string);
651         }
652
653         assert(n->next->type == ROFFT_BODY);
654         if (n->next->child != NULL)
655                 n = n->next;
656
657         print_man_nodelist(man, n->child, h);
658
659         return 0;
660 }