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