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