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