Annotation of src/usr.bin/mandoc/man_html.c, Revision 1.6
1.6 ! schwarze 1: /* $Id: man_html.c,v 1.5 2010/02/18 02:11:26 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
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: #include <sys/types.h>
18:
19: #include <assert.h>
20: #include <ctype.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "out.h"
26: #include "html.h"
27: #include "man.h"
28: #include "main.h"
29:
30: /* TODO: preserve ident widths. */
1.2 schwarze 31: /* FIXME: have PD set the default vspace width. */
1.1 schwarze 32:
33: #define INDENT 5
34: #define HALFINDENT 3
35:
36: #define MAN_ARGS const struct man_meta *m, \
37: const struct man_node *n, \
38: struct html *h
39:
40: struct htmlman {
41: int (*pre)(MAN_ARGS);
42: int (*post)(MAN_ARGS);
43: };
44:
45: static void print_man(MAN_ARGS);
46: static void print_man_head(MAN_ARGS);
47: static void print_man_nodelist(MAN_ARGS);
48: static void print_man_node(MAN_ARGS);
49:
50: static int a2width(const struct man_node *,
51: struct roffsu *);
52:
53: static int man_alt_pre(MAN_ARGS);
54: static int man_br_pre(MAN_ARGS);
55: static int man_ign_pre(MAN_ARGS);
56: static void man_root_post(MAN_ARGS);
57: static int man_root_pre(MAN_ARGS);
58: static int man_B_pre(MAN_ARGS);
59: static int man_HP_pre(MAN_ARGS);
60: static int man_I_pre(MAN_ARGS);
61: static int man_IP_pre(MAN_ARGS);
62: static int man_PP_pre(MAN_ARGS);
63: static int man_RS_pre(MAN_ARGS);
64: static int man_SB_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:
69: static const struct htmlman mans[MAN_MAX] = {
70: { man_br_pre, NULL }, /* br */
71: { NULL, NULL }, /* TH */
72: { man_SH_pre, NULL }, /* SH */
73: { man_SS_pre, NULL }, /* SS */
74: { man_IP_pre, NULL }, /* TP */
75: { man_PP_pre, NULL }, /* LP */
76: { man_PP_pre, NULL }, /* PP */
77: { man_PP_pre, NULL }, /* P */
78: { man_IP_pre, NULL }, /* IP */
79: { man_HP_pre, NULL }, /* HP */
80: { man_SM_pre, NULL }, /* SM */
81: { man_SB_pre, NULL }, /* SB */
82: { man_alt_pre, NULL }, /* BI */
83: { man_alt_pre, NULL }, /* IB */
84: { man_alt_pre, NULL }, /* BR */
85: { man_alt_pre, NULL }, /* RB */
86: { NULL, NULL }, /* R */
87: { man_B_pre, NULL }, /* B */
88: { man_I_pre, NULL }, /* I */
89: { man_alt_pre, NULL }, /* IR */
90: { man_alt_pre, NULL }, /* RI */
91: { NULL, NULL }, /* na */
92: { NULL, NULL }, /* i */
93: { man_br_pre, NULL }, /* sp */
94: { NULL, NULL }, /* nf */
95: { NULL, NULL }, /* fi */
96: { NULL, NULL }, /* r */
97: { NULL, NULL }, /* RE */
98: { man_RS_pre, NULL }, /* RS */
99: { man_ign_pre, NULL }, /* DT */
100: { man_ign_pre, NULL }, /* UC */
1.2 schwarze 101: { man_ign_pre, NULL }, /* PD */
1.6 ! schwarze 102: { man_br_pre, NULL }, /* Sp */
! 103: { NULL, NULL }, /* Vb */
! 104: { NULL, NULL }, /* Vi */
1.1 schwarze 105: };
106:
107:
108: void
109: html_man(void *arg, const struct man *m)
110: {
111: struct html *h;
112: struct tag *t;
113:
114: h = (struct html *)arg;
115:
1.5 schwarze 116: print_gen_decls(h);
1.1 schwarze 117:
118: t = print_otag(h, TAG_HTML, 0, NULL);
119: print_man(man_meta(m), man_node(m), h);
120: print_tagq(h, t);
121:
122: printf("\n");
123: }
124:
125:
126: static void
127: print_man(MAN_ARGS)
128: {
129: struct tag *t;
130: struct htmlpair tag;
131:
132: t = print_otag(h, TAG_HEAD, 0, NULL);
133:
134: print_man_head(m, n, h);
135: print_tagq(h, t);
136: t = print_otag(h, TAG_BODY, 0, NULL);
137:
138: tag.key = ATTR_CLASS;
139: tag.val = "body";
140: print_otag(h, TAG_DIV, 1, &tag);
141:
142: print_man_nodelist(m, n, h);
143:
144: print_tagq(h, t);
145: }
146:
147:
148: /* ARGSUSED */
149: static void
150: print_man_head(MAN_ARGS)
151: {
152:
153: print_gen_head(h);
154: bufinit(h);
155: buffmt(h, "%s(%d)", m->title, m->msec);
156:
157: print_otag(h, TAG_TITLE, 0, NULL);
158: print_text(h, h->buf);
159: }
160:
161:
162: static void
163: print_man_nodelist(MAN_ARGS)
164: {
165:
166: print_man_node(m, n, h);
167: if (n->next)
168: print_man_nodelist(m, n->next, h);
169: }
170:
171:
172: static void
173: print_man_node(MAN_ARGS)
174: {
175: int child;
176: struct tag *t;
177:
178: child = 1;
1.2 schwarze 179: t = h->tags.head;
1.1 schwarze 180:
181: bufinit(h);
182:
183: switch (n->type) {
184: case (MAN_ROOT):
185: child = man_root_pre(m, n, h);
186: break;
187: case (MAN_TEXT):
188: print_text(h, n->string);
1.4 schwarze 189: return;
1.1 schwarze 190: default:
1.4 schwarze 191: /*
192: * Close out scope of font prior to opening a macro
193: * scope. Assert that the metafont is on the top of the
194: * stack (it's never nested).
195: */
196: if (h->metaf) {
197: assert(h->metaf == t);
198: print_tagq(h, h->metaf);
199: assert(NULL == h->metaf);
200: t = h->tags.head;
201: }
1.1 schwarze 202: if (mans[n->tok].pre)
203: child = (*mans[n->tok].pre)(m, n, h);
204: break;
205: }
206:
207: if (child && n->child)
208: print_man_nodelist(m, n->child, h);
209:
1.4 schwarze 210: /* This will automatically close out any font scope. */
1.1 schwarze 211: print_stagq(h, t);
212:
213: bufinit(h);
214:
215: switch (n->type) {
216: case (MAN_ROOT):
217: man_root_post(m, n, h);
218: break;
219: case (MAN_TEXT):
220: break;
221: default:
222: if (mans[n->tok].post)
223: (*mans[n->tok].post)(m, n, h);
224: break;
225: }
226: }
227:
228:
229: static int
230: a2width(const struct man_node *n, struct roffsu *su)
231: {
232:
233: if (MAN_TEXT != n->type)
234: return(0);
235: if (a2roffsu(n->string, su, SCALE_BU))
236: return(1);
237:
238: return(0);
239: }
240:
241:
242: /* ARGSUSED */
243: static int
244: man_root_pre(MAN_ARGS)
245: {
1.3 schwarze 246: struct htmlpair tag[3];
1.1 schwarze 247: struct tag *t, *tt;
248: char b[BUFSIZ], title[BUFSIZ];
249:
250: b[0] = 0;
251: if (m->vol)
252: (void)strlcat(b, m->vol, BUFSIZ);
253:
1.4 schwarze 254: snprintf(title, BUFSIZ - 1, "%s(%d)", m->title, m->msec);
1.1 schwarze 255:
256: PAIR_CLASS_INIT(&tag[0], "header");
257: bufcat_style(h, "width", "100%");
258: PAIR_STYLE_INIT(&tag[1], h);
1.3 schwarze 259: PAIR_SUMMARY_INIT(&tag[2], "header");
260:
261: t = print_otag(h, TAG_TABLE, 3, tag);
1.1 schwarze 262: tt = print_otag(h, TAG_TR, 0, NULL);
263:
264: bufinit(h);
265: bufcat_style(h, "width", "10%");
266: PAIR_STYLE_INIT(&tag[0], h);
267: print_otag(h, TAG_TD, 1, tag);
268: print_text(h, title);
269: print_stagq(h, tt);
270:
271: bufinit(h);
272: bufcat_style(h, "width", "80%");
273: bufcat_style(h, "white-space", "nowrap");
274: bufcat_style(h, "text-align", "center");
275: PAIR_STYLE_INIT(&tag[0], h);
276: print_otag(h, TAG_TD, 1, tag);
277: print_text(h, b);
278: print_stagq(h, tt);
279:
280: bufinit(h);
281: bufcat_style(h, "width", "10%");
282: bufcat_style(h, "text-align", "right");
283: PAIR_STYLE_INIT(&tag[0], h);
284: print_otag(h, TAG_TD, 1, tag);
285: print_text(h, title);
286: print_tagq(h, t);
287: return(1);
288: }
289:
290:
291: /* ARGSUSED */
292: static void
293: man_root_post(MAN_ARGS)
294: {
1.3 schwarze 295: struct htmlpair tag[3];
1.1 schwarze 296: struct tag *t, *tt;
1.2 schwarze 297: char b[DATESIZ];
1.1 schwarze 298:
1.2 schwarze 299: time2a(m->date, b, DATESIZ);
1.1 schwarze 300:
301: PAIR_CLASS_INIT(&tag[0], "footer");
302: bufcat_style(h, "width", "100%");
303: PAIR_STYLE_INIT(&tag[1], h);
1.3 schwarze 304: PAIR_SUMMARY_INIT(&tag[2], "footer");
305:
306: t = print_otag(h, TAG_TABLE, 3, tag);
1.1 schwarze 307: tt = print_otag(h, TAG_TR, 0, NULL);
308:
309: bufinit(h);
310: bufcat_style(h, "width", "50%");
311: PAIR_STYLE_INIT(&tag[0], h);
312: print_otag(h, TAG_TD, 1, tag);
313: print_text(h, b);
314: print_stagq(h, tt);
315:
316: bufinit(h);
317: bufcat_style(h, "width", "50%");
318: bufcat_style(h, "text-align", "right");
319: PAIR_STYLE_INIT(&tag[0], h);
320: print_otag(h, TAG_TD, 1, tag);
321: if (m->source)
322: print_text(h, m->source);
323: print_tagq(h, t);
324: }
325:
326:
327:
328: /* ARGSUSED */
329: static int
330: man_br_pre(MAN_ARGS)
331: {
332: struct roffsu su;
333: struct htmlpair tag;
334:
335: SCALE_VS_INIT(&su, 1);
336:
1.6 ! schwarze 337: if ((MAN_sp == n->tok || MAN_Sp == n->tok) && n->child)
1.1 schwarze 338: a2roffsu(n->child->string, &su, SCALE_VS);
339: else if (MAN_br == n->tok)
340: su.scale = 0;
341:
342: bufcat_su(h, "height", &su);
343: PAIR_STYLE_INIT(&tag, h);
344: print_otag(h, TAG_DIV, 1, &tag);
1.4 schwarze 345:
1.3 schwarze 346: /* So the div isn't empty: */
347: print_text(h, "\\~");
348:
1.1 schwarze 349: return(0);
350: }
351:
352:
353: /* ARGSUSED */
354: static int
355: man_SH_pre(MAN_ARGS)
356: {
357: struct htmlpair tag[2];
358: struct roffsu su;
359:
360: if (MAN_BODY == n->type) {
361: SCALE_HS_INIT(&su, INDENT);
362: bufcat_su(h, "margin-left", &su);
363: PAIR_CLASS_INIT(&tag[0], "sec-body");
364: PAIR_STYLE_INIT(&tag[1], h);
365: print_otag(h, TAG_DIV, 2, tag);
366: return(1);
367: } else if (MAN_BLOCK == n->type) {
368: PAIR_CLASS_INIT(&tag[0], "sec-block");
369: if (n->prev && MAN_SH == n->prev->tok)
370: if (NULL == n->prev->body->child) {
371: print_otag(h, TAG_DIV, 1, tag);
372: return(1);
373: }
374:
375: SCALE_VS_INIT(&su, 1);
376: bufcat_su(h, "margin-top", &su);
377: if (NULL == n->next)
378: bufcat_su(h, "margin-bottom", &su);
379: PAIR_STYLE_INIT(&tag[1], h);
380: print_otag(h, TAG_DIV, 2, tag);
381: return(1);
382: }
383:
384: PAIR_CLASS_INIT(&tag[0], "sec-head");
385: print_otag(h, TAG_DIV, 1, tag);
386: return(1);
387: }
388:
389:
390: /* ARGSUSED */
391: static int
392: man_alt_pre(MAN_ARGS)
393: {
394: const struct man_node *nn;
395: struct tag *t;
396: int i;
1.4 schwarze 397: enum htmlfont fp;
1.1 schwarze 398:
399: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
400: switch (n->tok) {
401: case (MAN_BI):
1.4 schwarze 402: fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
1.1 schwarze 403: break;
404: case (MAN_IB):
1.4 schwarze 405: fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
1.1 schwarze 406: break;
407: case (MAN_RI):
1.4 schwarze 408: fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
1.1 schwarze 409: break;
410: case (MAN_IR):
1.4 schwarze 411: fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
1.1 schwarze 412: break;
413: case (MAN_BR):
1.4 schwarze 414: fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
1.1 schwarze 415: break;
416: case (MAN_RB):
1.4 schwarze 417: fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
1.1 schwarze 418: break;
419: default:
420: abort();
421: /* NOTREACHED */
422: }
423:
424: if (i)
425: h->flags |= HTML_NOSPACE;
426:
1.4 schwarze 427: /*
428: * Open and close the scope with each argument, so that
429: * internal \f escapes, which are common, are also
430: * closed out with the scope.
431: */
432: t = print_ofont(h, fp);
433: print_man_node(m, nn, h);
434: print_tagq(h, t);
1.1 schwarze 435: }
436:
437: return(0);
438: }
439:
440:
441: /* ARGSUSED */
442: static int
443: man_SB_pre(MAN_ARGS)
444: {
445: struct htmlpair tag;
446:
1.4 schwarze 447: /* FIXME: print_ofont(). */
1.1 schwarze 448: PAIR_CLASS_INIT(&tag, "small bold");
449: print_otag(h, TAG_SPAN, 1, &tag);
450: return(1);
451: }
452:
453:
454: /* ARGSUSED */
455: static int
456: man_SM_pre(MAN_ARGS)
457: {
458: struct htmlpair tag;
459:
460: PAIR_CLASS_INIT(&tag, "small");
461: print_otag(h, TAG_SPAN, 1, &tag);
462: return(1);
463: }
464:
465:
466: /* ARGSUSED */
467: static int
468: man_SS_pre(MAN_ARGS)
469: {
470: struct htmlpair tag[3];
471: struct roffsu su;
472:
473: SCALE_VS_INIT(&su, 1);
474:
475: if (MAN_BODY == n->type) {
476: PAIR_CLASS_INIT(&tag[0], "ssec-body");
477: if (n->parent->next && n->child) {
478: bufcat_su(h, "margin-bottom", &su);
479: PAIR_STYLE_INIT(&tag[1], h);
480: print_otag(h, TAG_DIV, 2, tag);
481: return(1);
482: }
483:
484: print_otag(h, TAG_DIV, 1, tag);
485: return(1);
486: } else if (MAN_BLOCK == n->type) {
487: PAIR_CLASS_INIT(&tag[0], "ssec-block");
488: if (n->prev && MAN_SS == n->prev->tok)
489: if (n->prev->body->child) {
490: bufcat_su(h, "margin-top", &su);
491: PAIR_STYLE_INIT(&tag[1], h);
492: print_otag(h, TAG_DIV, 2, tag);
493: return(1);
494: }
495:
496: print_otag(h, TAG_DIV, 1, tag);
497: return(1);
498: }
499:
500: SCALE_HS_INIT(&su, INDENT - HALFINDENT);
501: bufcat_su(h, "margin-left", &su);
502: PAIR_CLASS_INIT(&tag[0], "ssec-head");
503: PAIR_STYLE_INIT(&tag[1], h);
504: print_otag(h, TAG_DIV, 2, tag);
505: return(1);
506: }
507:
508:
509: /* ARGSUSED */
510: static int
511: man_PP_pre(MAN_ARGS)
512: {
513: struct htmlpair tag;
514: struct roffsu su;
515: int i;
516:
517: if (MAN_BLOCK != n->type)
518: return(1);
519:
520: i = 0;
521:
1.4 schwarze 522: if (MAN_ROOT == n->parent->type) {
1.1 schwarze 523: SCALE_HS_INIT(&su, INDENT);
524: bufcat_su(h, "margin-left", &su);
1.4 schwarze 525: i = 1;
1.1 schwarze 526: }
1.4 schwarze 527: if (n->prev) {
1.1 schwarze 528: SCALE_VS_INIT(&su, 1);
1.4 schwarze 529: bufcat_su(h, "margin-top", &su);
530: i = 1;
1.1 schwarze 531: }
532:
533: PAIR_STYLE_INIT(&tag, h);
1.4 schwarze 534: print_otag(h, TAG_DIV, i, &tag);
1.1 schwarze 535: return(1);
536: }
537:
538:
539: /* ARGSUSED */
540: static int
541: man_IP_pre(MAN_ARGS)
542: {
543: struct roffsu su;
544: struct htmlpair tag;
545: const struct man_node *nn;
546: int width;
547:
548: /*
549: * This scattering of 1-BU margins and pads is to make sure that
550: * when text overruns its box, the subsequent text isn't flush
551: * up against it. However, the rest of the right-hand box must
552: * also be adjusted in consideration of this 1-BU space.
553: */
554:
555: if (MAN_BODY == n->type) {
556: SCALE_HS_INIT(&su, INDENT);
557: bufcat_su(h, "margin-left", &su);
558: PAIR_STYLE_INIT(&tag, h);
559: print_otag(h, TAG_DIV, 1, &tag);
560: return(1);
561: }
562:
563: nn = MAN_BLOCK == n->type ?
564: n->head->child : n->parent->head->child;
565:
566: SCALE_HS_INIT(&su, INDENT);
567: width = 0;
568:
569: if (MAN_IP == n->tok && NULL != nn)
570: if (NULL != (nn = nn->next)) {
571: for ( ; nn->next; nn = nn->next)
572: /* Do nothing. */ ;
573: width = a2width(nn, &su);
574: }
575:
576: if (MAN_TP == n->tok && NULL != nn)
577: width = a2width(nn, &su);
578:
579: if (MAN_BLOCK == n->type) {
580: bufcat_su(h, "margin-left", &su);
581: SCALE_VS_INIT(&su, 1);
582: bufcat_su(h, "margin-top", &su);
583: bufcat_style(h, "clear", "both");
584: PAIR_STYLE_INIT(&tag, h);
585: print_otag(h, TAG_DIV, 1, &tag);
586: return(1);
587: }
588:
589: bufcat_su(h, "min-width", &su);
590: SCALE_INVERT(&su);
591: bufcat_su(h, "margin-left", &su);
592: SCALE_HS_INIT(&su, 1);
593: bufcat_su(h, "margin-right", &su);
594: bufcat_style(h, "clear", "left");
595:
596: if (n->next && n->next->child)
597: bufcat_style(h, "float", "left");
598:
599: PAIR_STYLE_INIT(&tag, h);
600: print_otag(h, TAG_DIV, 1, &tag);
601:
602: /* With a length string, manually omit the last child. */
603:
604: if ( ! width)
605: return(1);
606:
607: if (MAN_IP == n->tok)
608: for (nn = n->child; nn->next; nn = nn->next)
609: print_man_node(m, nn, h);
610: if (MAN_TP == n->tok)
611: for (nn = n->child->next; nn; nn = nn->next)
612: print_man_node(m, nn, h);
613:
614: return(0);
615: }
616:
617:
618: /* ARGSUSED */
619: static int
620: man_HP_pre(MAN_ARGS)
621: {
622: const struct man_node *nn;
623: struct htmlpair tag;
624: struct roffsu su;
625:
626: if (MAN_HEAD == n->type)
627: return(0);
628:
629: nn = MAN_BLOCK == n->type ?
630: n->head->child : n->parent->head->child;
631:
632: SCALE_HS_INIT(&su, INDENT);
633:
634: if (NULL != nn)
635: (void)a2width(nn, &su);
636:
637: if (MAN_BLOCK == n->type) {
638: bufcat_su(h, "margin-left", &su);
639: SCALE_VS_INIT(&su, 1);
640: bufcat_su(h, "margin-top", &su);
641: bufcat_style(h, "clear", "both");
642: PAIR_STYLE_INIT(&tag, h);
643: print_otag(h, TAG_DIV, 1, &tag);
644: return(1);
645: }
646:
647: bufcat_su(h, "margin-left", &su);
648: SCALE_INVERT(&su);
649: bufcat_su(h, "text-indent", &su);
650:
651: PAIR_STYLE_INIT(&tag, h);
652: print_otag(h, TAG_DIV, 1, &tag);
653: return(1);
654: }
655:
656:
657: /* ARGSUSED */
658: static int
659: man_B_pre(MAN_ARGS)
660: {
661:
1.4 schwarze 662: print_ofont(h, HTMLFONT_BOLD);
1.1 schwarze 663: return(1);
664: }
665:
666:
667: /* ARGSUSED */
668: static int
669: man_I_pre(MAN_ARGS)
670: {
1.4 schwarze 671:
672: print_ofont(h, HTMLFONT_ITALIC);
1.1 schwarze 673: return(1);
674: }
675:
676:
677: /* ARGSUSED */
678: static int
679: man_ign_pre(MAN_ARGS)
680: {
681:
682: return(0);
683: }
684:
685:
686: /* ARGSUSED */
687: static int
688: man_RS_pre(MAN_ARGS)
689: {
690: struct htmlpair tag;
691: struct roffsu su;
692:
693: if (MAN_HEAD == n->type)
694: return(0);
695: else if (MAN_BODY == n->type)
696: return(1);
697:
698: SCALE_HS_INIT(&su, INDENT);
699: bufcat_su(h, "margin-left", &su);
700:
701: if (n->head->child) {
702: SCALE_VS_INIT(&su, 1);
703: a2width(n->head->child, &su);
704: bufcat_su(h, "margin-top", &su);
705: }
706:
707: PAIR_STYLE_INIT(&tag, h);
708: print_otag(h, TAG_DIV, 1, &tag);
709: return(1);
710: }