]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
Plug in the "head" concept for tables. A tbl_head specifies the full
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.58 2011/01/01 12:59:17 kristaps 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 int 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 { NULL, 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
187 bufinit(h);
188
189 /*
190 * FIXME: embedded elements within next-line scopes (e.g., `br'
191 * within an empty `B') will cause formatting to be forgotten
192 * due to scope closing out.
193 */
194
195 switch (n->type) {
196 case (MAN_ROOT):
197 child = man_root_pre(m, n, mh, h);
198 break;
199 case (MAN_TEXT):
200 print_text(h, n->string);
201 if (MANH_LITERAL & mh->fl)
202 print_otag(h, TAG_BR, 0, NULL);
203 return;
204 case (MAN_TBL):
205 return;
206 default:
207 /*
208 * Close out scope of font prior to opening a macro
209 * scope. Assert that the metafont is on the top of the
210 * stack (it's never nested).
211 */
212 if (HTMLFONT_NONE != h->metac) {
213 h->metal = h->metac;
214 h->metac = HTMLFONT_NONE;
215 }
216 if (mans[n->tok].pre)
217 child = (*mans[n->tok].pre)(m, n, mh, h);
218 break;
219 }
220
221 if (child && n->child)
222 print_man_nodelist(m, n->child, mh, h);
223
224 /* This will automatically close out any font scope. */
225 print_stagq(h, t);
226
227 bufinit(h);
228
229 if (MAN_ROOT == n->type)
230 man_root_post(m, n, mh, h);
231 else if (mans[n->tok].post)
232 (*mans[n->tok].post)(m, n, mh, h);
233 }
234
235
236 static int
237 a2width(const struct man_node *n, struct roffsu *su)
238 {
239
240 if (MAN_TEXT != n->type)
241 return(0);
242 if (a2roffsu(n->string, su, SCALE_BU))
243 return(1);
244
245 return(0);
246 }
247
248
249 /* ARGSUSED */
250 static int
251 man_root_pre(MAN_ARGS)
252 {
253 struct htmlpair tag[3];
254 struct tag *t, *tt;
255 char b[BUFSIZ], title[BUFSIZ];
256
257 b[0] = 0;
258 if (m->vol)
259 (void)strlcat(b, m->vol, BUFSIZ);
260
261 snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
262
263 PAIR_SUMMARY_INIT(&tag[0], "Document Header");
264 PAIR_CLASS_INIT(&tag[1], "head");
265 if (NULL == h->style) {
266 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
267 t = print_otag(h, TAG_TABLE, 3, tag);
268 PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
269 print_otag(h, TAG_COL, 1, tag);
270 print_otag(h, TAG_COL, 1, tag);
271 print_otag(h, TAG_COL, 1, tag);
272 } else
273 t = print_otag(h, TAG_TABLE, 2, tag);
274
275 print_otag(h, TAG_TBODY, 0, NULL);
276
277 tt = print_otag(h, TAG_TR, 0, NULL);
278
279 PAIR_CLASS_INIT(&tag[0], "head-ltitle");
280 print_otag(h, TAG_TD, 1, tag);
281
282 print_text(h, title);
283 print_stagq(h, tt);
284
285 PAIR_CLASS_INIT(&tag[0], "head-vol");
286 if (NULL == h->style) {
287 PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
288 print_otag(h, TAG_TD, 2, tag);
289 } else
290 print_otag(h, TAG_TD, 1, tag);
291
292 print_text(h, b);
293 print_stagq(h, tt);
294
295 PAIR_CLASS_INIT(&tag[0], "head-rtitle");
296 if (NULL == h->style) {
297 PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
298 print_otag(h, TAG_TD, 2, tag);
299 } else
300 print_otag(h, TAG_TD, 1, tag);
301
302 print_text(h, title);
303 print_tagq(h, t);
304 return(1);
305 }
306
307
308 /* ARGSUSED */
309 static void
310 man_root_post(MAN_ARGS)
311 {
312 struct htmlpair tag[3];
313 struct tag *t, *tt;
314 char b[DATESIZ];
315
316 if (m->rawdate)
317 strlcpy(b, m->rawdate, DATESIZ);
318 else
319 time2a(m->date, b, DATESIZ);
320
321 PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
322 PAIR_CLASS_INIT(&tag[1], "foot");
323 if (NULL == h->style) {
324 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
325 t = print_otag(h, TAG_TABLE, 3, tag);
326 PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
327 print_otag(h, TAG_COL, 1, tag);
328 print_otag(h, TAG_COL, 1, tag);
329 } else
330 t = print_otag(h, TAG_TABLE, 2, tag);
331
332 tt = print_otag(h, TAG_TR, 0, NULL);
333
334 PAIR_CLASS_INIT(&tag[0], "foot-date");
335 print_otag(h, TAG_TD, 1, tag);
336
337 print_text(h, b);
338 print_stagq(h, tt);
339
340 PAIR_CLASS_INIT(&tag[0], "foot-os");
341 if (NULL == h->style) {
342 PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
343 print_otag(h, TAG_TD, 2, tag);
344 } else
345 print_otag(h, TAG_TD, 1, tag);
346
347 if (m->source)
348 print_text(h, m->source);
349 print_tagq(h, t);
350 }
351
352
353
354 /* ARGSUSED */
355 static int
356 man_br_pre(MAN_ARGS)
357 {
358 struct roffsu su;
359 struct htmlpair tag;
360
361 SCALE_VS_INIT(&su, 1);
362
363 if (MAN_sp == n->tok) {
364 if (n->child)
365 a2roffsu(n->child->string, &su, SCALE_VS);
366 } else
367 su.scale = 0;
368
369 bufcat_su(h, "height", &su);
370 PAIR_STYLE_INIT(&tag, h);
371 print_otag(h, TAG_DIV, 1, &tag);
372
373 /* So the div isn't empty: */
374 print_text(h, "\\~");
375
376 return(0);
377 }
378
379
380 /* ARGSUSED */
381 static int
382 man_SH_pre(MAN_ARGS)
383 {
384 struct htmlpair tag;
385
386 if (MAN_BLOCK == n->type) {
387 PAIR_CLASS_INIT(&tag, "section");
388 print_otag(h, TAG_DIV, 1, &tag);
389 return(1);
390 } else if (MAN_BODY == n->type)
391 return(1);
392
393 print_otag(h, TAG_H1, 0, NULL);
394 return(1);
395 }
396
397
398 /* ARGSUSED */
399 static int
400 man_alt_pre(MAN_ARGS)
401 {
402 const struct man_node *nn;
403 int i;
404 enum htmltag fp;
405 struct tag *t;
406
407 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
408 t = NULL;
409 switch (n->tok) {
410 case (MAN_BI):
411 fp = i % 2 ? TAG_I : TAG_B;
412 break;
413 case (MAN_IB):
414 fp = i % 2 ? TAG_B : TAG_I;
415 break;
416 case (MAN_RI):
417 fp = i % 2 ? TAG_I : TAG_MAX;
418 break;
419 case (MAN_IR):
420 fp = i % 2 ? TAG_MAX : TAG_I;
421 break;
422 case (MAN_BR):
423 fp = i % 2 ? TAG_MAX : TAG_B;
424 break;
425 case (MAN_RB):
426 fp = i % 2 ? TAG_B : TAG_MAX;
427 break;
428 default:
429 abort();
430 /* NOTREACHED */
431 }
432
433 if (i)
434 h->flags |= HTML_NOSPACE;
435
436 if (TAG_MAX != fp)
437 t = print_otag(h, fp, 0, NULL);
438
439 print_man_node(m, nn, mh, h);
440
441 if (t)
442 print_tagq(h, t);
443 }
444
445 return(0);
446 }
447
448
449 /* ARGSUSED */
450 static int
451 man_SM_pre(MAN_ARGS)
452 {
453
454 print_otag(h, TAG_SMALL, 0, NULL);
455 if (MAN_SB == n->tok)
456 print_otag(h, TAG_B, 0, NULL);
457 return(1);
458 }
459
460
461 /* ARGSUSED */
462 static int
463 man_SS_pre(MAN_ARGS)
464 {
465 struct htmlpair tag;
466
467 if (MAN_BLOCK == n->type) {
468 PAIR_CLASS_INIT(&tag, "subsection");
469 print_otag(h, TAG_DIV, 1, &tag);
470 return(1);
471 } else if (MAN_BODY == n->type)
472 return(1);
473
474 print_otag(h, TAG_H2, 0, NULL);
475 return(1);
476 }
477
478
479 /* ARGSUSED */
480 static int
481 man_PP_pre(MAN_ARGS)
482 {
483
484 if (MAN_HEAD == n->type)
485 return(0);
486 else if (MAN_BODY == n->type && n->prev)
487 print_otag(h, TAG_P, 0, NULL);
488
489 return(1);
490 }
491
492
493 /* ARGSUSED */
494 static int
495 man_IP_pre(MAN_ARGS)
496 {
497 struct roffsu su;
498 struct htmlpair tag;
499 const struct man_node *nn;
500 int width;
501
502 /*
503 * This scattering of 1-BU margins and pads is to make sure that
504 * when text overruns its box, the subsequent text isn't flush
505 * up against it. However, the rest of the right-hand box must
506 * also be adjusted in consideration of this 1-BU space.
507 */
508
509 if (MAN_BODY == n->type) {
510 print_otag(h, TAG_TD, 0, NULL);
511 return(1);
512 }
513
514 nn = MAN_BLOCK == n->type ?
515 n->head->child : n->parent->head->child;
516
517 SCALE_HS_INIT(&su, INDENT);
518 width = 0;
519
520 /* Width is the last token. */
521
522 if (MAN_IP == n->tok && NULL != nn)
523 if (NULL != (nn = nn->next)) {
524 for ( ; nn->next; nn = nn->next)
525 /* Do nothing. */ ;
526 width = a2width(nn, &su);
527 }
528
529 /* Width is the first token. */
530
531 if (MAN_TP == n->tok && NULL != nn) {
532 /* Skip past non-text children. */
533 while (nn && MAN_TEXT != nn->type)
534 nn = nn->next;
535 if (nn)
536 width = a2width(nn, &su);
537 }
538
539 if (MAN_BLOCK == n->type) {
540 print_otag(h, TAG_P, 0, NULL);
541 print_otag(h, TAG_TABLE, 0, NULL);
542 bufcat_su(h, "width", &su);
543 PAIR_STYLE_INIT(&tag, h);
544 print_otag(h, TAG_COL, 1, &tag);
545 print_otag(h, TAG_COL, 0, NULL);
546 print_otag(h, TAG_TBODY, 0, NULL);
547 print_otag(h, TAG_TR, 0, NULL);
548 return(1);
549 }
550
551 print_otag(h, TAG_TD, 0, NULL);
552
553 /*
554 * Without a length string, we can print all of our children.
555 */
556
557 if ( ! width)
558 return(1);
559
560 /*
561 * When a length has been specified, we need to carefully print
562 * our child context: IP gets all children printed but the last
563 * (the width), while TP gets all children printed but the first
564 * (the width).
565 */
566
567 if (MAN_IP == n->tok)
568 for (nn = n->child; nn->next; nn = nn->next)
569 print_man_node(m, nn, mh, h);
570 if (MAN_TP == n->tok)
571 for (nn = n->child->next; nn; nn = nn->next)
572 print_man_node(m, nn, mh, h);
573
574 return(0);
575 }
576
577
578 /* ARGSUSED */
579 static int
580 man_HP_pre(MAN_ARGS)
581 {
582 struct htmlpair tag;
583 struct roffsu su;
584 const struct man_node *np;
585
586 np = MAN_BLOCK == n->type ?
587 n->head->child :
588 n->parent->head->child;
589
590 if (NULL == np || ! a2width(np, &su))
591 SCALE_HS_INIT(&su, INDENT);
592
593 if (MAN_HEAD == n->type) {
594 print_otag(h, TAG_TD, 0, NULL);
595 return(0);
596 } else if (MAN_BLOCK == n->type) {
597 print_otag(h, TAG_P, 0, NULL);
598 print_otag(h, TAG_TABLE, 0, NULL);
599 bufcat_su(h, "width", &su);
600 PAIR_STYLE_INIT(&tag, h);
601 print_otag(h, TAG_COL, 1, &tag);
602 print_otag(h, TAG_COL, 0, NULL);
603 print_otag(h, TAG_TBODY, 0, NULL);
604 print_otag(h, TAG_TR, 0, NULL);
605 return(1);
606 }
607
608 su.scale = -su.scale;
609 bufcat_su(h, "text-indent", &su);
610 PAIR_STYLE_INIT(&tag, h);
611 print_otag(h, TAG_TD, 1, &tag);
612 return(1);
613 }
614
615
616 /* ARGSUSED */
617 static int
618 man_B_pre(MAN_ARGS)
619 {
620
621 print_otag(h, TAG_B, 0, NULL);
622 return(1);
623 }
624
625
626 /* ARGSUSED */
627 static int
628 man_I_pre(MAN_ARGS)
629 {
630
631 print_otag(h, TAG_I, 0, NULL);
632 return(1);
633 }
634
635
636 /* ARGSUSED */
637 static int
638 man_literal_pre(MAN_ARGS)
639 {
640
641 if (MAN_nf == n->tok) {
642 print_otag(h, TAG_BR, 0, NULL);
643 mh->fl |= MANH_LITERAL;
644 } else
645 mh->fl &= ~MANH_LITERAL;
646
647 return(1);
648 }
649
650
651 /* ARGSUSED */
652 static int
653 man_in_pre(MAN_ARGS)
654 {
655
656 print_otag(h, TAG_BR, 0, NULL);
657 return(0);
658 }
659
660
661 /* ARGSUSED */
662 static int
663 man_ign_pre(MAN_ARGS)
664 {
665
666 return(0);
667 }
668
669
670 /* ARGSUSED */
671 static int
672 man_RS_pre(MAN_ARGS)
673 {
674 struct htmlpair tag;
675 struct roffsu su;
676
677 if (MAN_HEAD == n->type)
678 return(0);
679 else if (MAN_BODY == n->type)
680 return(1);
681
682 SCALE_HS_INIT(&su, INDENT);
683 if (n->head->child)
684 a2width(n->head->child, &su);
685
686 bufcat_su(h, "margin-left", &su);
687 PAIR_STYLE_INIT(&tag, h);
688 print_otag(h, TAG_DIV, 1, &tag);
689 return(1);
690 }