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