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