Annotation of mandoc/man_html.c, Revision 1.6
1.6 ! kristaps 1: /* $Id: man_html.c,v 1.5 2009/10/04 10:24:31 kristaps Exp $ */
1.1 kristaps 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: #include <sys/queue.h>
19:
1.5 kristaps 20: #include <assert.h>
21: #include <ctype.h>
1.4 kristaps 22: #include <err.h>
1.2 kristaps 23: #include <stdio.h>
1.1 kristaps 24: #include <stdlib.h>
1.4 kristaps 25: #include <string.h>
1.1 kristaps 26:
27: #include "html.h"
28: #include "man.h"
29:
1.6 ! kristaps 30: /* TODO: preserve ident widths. */
! 31:
! 32: #define INDENT 5
1.4 kristaps 33: #define HALFINDENT 3
34:
1.3 kristaps 35: #define MAN_ARGS const struct man_meta *m, \
36: const struct man_node *n, \
37: struct html *h
38:
39: struct htmlman {
40: int (*pre)(MAN_ARGS);
41: int (*post)(MAN_ARGS);
42: };
43:
44: static void print_man(MAN_ARGS);
45: static void print_man_head(MAN_ARGS);
1.4 kristaps 46: static void print_man_nodelist(MAN_ARGS);
47: static void print_man_node(MAN_ARGS);
1.3 kristaps 48:
1.5 kristaps 49: static int a2width(const struct man_node *);
50:
1.4 kristaps 51: static int man_br_pre(MAN_ARGS);
1.6 ! kristaps 52: static int man_HP_pre(MAN_ARGS);
1.5 kristaps 53: static int man_IP_pre(MAN_ARGS);
1.4 kristaps 54: static int man_PP_pre(MAN_ARGS);
55: static void man_root_post(MAN_ARGS);
56: static int man_root_pre(MAN_ARGS);
57: static int man_SH_pre(MAN_ARGS);
58: static int man_SS_pre(MAN_ARGS);
59:
60: #ifdef __linux__
61: extern size_t strlcpy(char *, const char *, size_t);
62: extern size_t strlcat(char *, const char *, size_t);
63: #endif
1.3 kristaps 64:
65: static const struct htmlman mans[MAN_MAX] = {
1.4 kristaps 66: { man_br_pre, NULL }, /* br */
1.3 kristaps 67: { NULL, NULL }, /* TH */
1.4 kristaps 68: { man_SH_pre, NULL }, /* SH */
69: { man_SS_pre, NULL }, /* SS */
1.6 ! kristaps 70: { man_IP_pre, NULL }, /* TP */
1.4 kristaps 71: { man_PP_pre, NULL }, /* LP */
72: { man_PP_pre, NULL }, /* PP */
73: { man_PP_pre, NULL }, /* P */
1.5 kristaps 74: { man_IP_pre, NULL }, /* IP */
1.6 ! kristaps 75: { man_HP_pre, NULL }, /* HP */
1.3 kristaps 76: { NULL, NULL }, /* SM */
77: { NULL, NULL }, /* SB */
78: { NULL, NULL }, /* BI */
79: { NULL, NULL }, /* IB */
80: { NULL, NULL }, /* BR */
81: { NULL, NULL }, /* RB */
82: { NULL, NULL }, /* R */
83: { NULL, NULL }, /* B */
84: { NULL, NULL }, /* I */
85: { NULL, NULL }, /* IR */
86: { NULL, NULL }, /* RI */
87: { NULL, NULL }, /* na */
88: { NULL, NULL }, /* i */
1.4 kristaps 89: { man_br_pre, NULL }, /* sp */
1.3 kristaps 90: { NULL, NULL }, /* nf */
91: { NULL, NULL }, /* fi */
92: { NULL, NULL }, /* r */
93: { NULL, NULL }, /* RE */
94: { NULL, NULL }, /* RS */
95: { NULL, NULL }, /* DT */
96: { NULL, NULL }, /* UC */
97: };
98:
1.1 kristaps 99:
100: void
101: html_man(void *arg, const struct man *m)
102: {
1.3 kristaps 103: struct html *h;
104: struct tag *t;
105:
106: h = (struct html *)arg;
107:
108: print_gen_doctype(h);
109:
110: t = print_otag(h, TAG_HTML, 0, NULL);
111: print_man(man_meta(m), man_node(m), h);
112: print_tagq(h, t);
113:
114: printf("\n");
115: }
116:
117:
118: static void
119: print_man(MAN_ARGS)
120: {
121: struct tag *t;
122: struct htmlpair tag;
123:
124: t = print_otag(h, TAG_HEAD, 0, NULL);
125:
126: print_man_head(m, n, h);
127: print_tagq(h, t);
128: t = print_otag(h, TAG_BODY, 0, NULL);
129:
130: tag.key = ATTR_CLASS;
131: tag.val = "body";
132: print_otag(h, TAG_DIV, 1, &tag);
133:
1.4 kristaps 134: print_man_nodelist(m, n, h);
1.3 kristaps 135:
136: print_tagq(h, t);
137: }
138:
139:
140: /* ARGSUSED */
141: static void
142: print_man_head(MAN_ARGS)
143: {
144:
145: print_gen_head(h);
146: bufinit(h);
147: buffmt(h, "%s(%d)", m->title, m->msec);
148:
149: print_otag(h, TAG_TITLE, 0, NULL);
150: print_text(h, h->buf);
1.1 kristaps 151: }
1.4 kristaps 152:
153:
154: static void
155: print_man_nodelist(MAN_ARGS)
156: {
157:
158: print_man_node(m, n, h);
159: if (n->next)
160: print_man_nodelist(m, n->next, h);
161: }
162:
163:
164: static void
165: print_man_node(MAN_ARGS)
166: {
167: int child;
168: struct tag *t;
169:
170: child = 1;
171: t = SLIST_FIRST(&h->tags);
172:
173: bufinit(h);
174:
175: switch (n->type) {
176: case (MAN_ROOT):
177: child = man_root_pre(m, n, h);
178: break;
179: case (MAN_TEXT):
180: print_text(h, n->string);
181: break;
182: default:
183: if (mans[n->tok].pre)
184: child = (*mans[n->tok].pre)(m, n, h);
185: break;
186: }
187:
188: if (child && n->child)
189: print_man_nodelist(m, n->child, h);
190:
191: print_stagq(h, t);
192:
193: bufinit(h);
194:
195: switch (n->type) {
196: case (MAN_ROOT):
197: man_root_post(m, n, h);
198: break;
199: case (MAN_TEXT):
200: break;
201: default:
202: if (mans[n->tok].post)
203: (*mans[n->tok].post)(m, n, h);
204: break;
205: }
206: }
207:
208:
1.5 kristaps 209: static int
210: a2width(const struct man_node *n)
211: {
212: int i, len;
213: const char *p;
214:
1.6 ! kristaps 215: if (MAN_TEXT != n->type)
! 216: return(-1);
1.5 kristaps 217:
218: p = n->string;
219:
220: if (0 == (len = (int)strlen(p)))
221: return(-1);
222:
223: for (i = 0; i < len; i++)
224: if ( ! isdigit((u_char)p[i]))
225: break;
226:
227: if (i == len - 1) {
228: if ('n' == p[len - 1] || 'm' == p[len - 1])
229: return(atoi(p));
230: } else if (i == len)
231: return(atoi(p));
232:
233: return(-1);
234: }
235:
236:
1.4 kristaps 237: /* ARGSUSED */
238: static int
239: man_root_pre(MAN_ARGS)
240: {
241: struct htmlpair tag[2];
242: struct tag *t, *tt;
243: char b[BUFSIZ], title[BUFSIZ];
244:
245: b[0] = 0;
246: if (m->vol)
247: (void)strlcat(b, m->vol, BUFSIZ);
248:
249: (void)snprintf(title, BUFSIZ - 1,
250: "%s(%d)", m->title, m->msec);
251:
252: tag[0].key = ATTR_CLASS;
253: tag[0].val = "header";
254: tag[1].key = ATTR_STYLE;
255: tag[1].val = "width: 100%;";
256: t = print_otag(h, TAG_TABLE, 2, tag);
257: tt = print_otag(h, TAG_TR, 0, NULL);
258:
259: tag[0].key = ATTR_STYLE;
260: tag[0].val = "width: 10%;";
261: print_otag(h, TAG_TD, 1, tag);
262: print_text(h, title);
263: print_stagq(h, tt);
264:
265: tag[0].key = ATTR_STYLE;
266: tag[0].val = "width: 80%; white-space: nowrap; text-align: center;";
267: print_otag(h, TAG_TD, 1, tag);
268: print_text(h, b);
269: print_stagq(h, tt);
270:
271: tag[0].key = ATTR_STYLE;
272: tag[0].val = "width: 10%; text-align: right;";
273: print_otag(h, TAG_TD, 1, tag);
274: print_text(h, title);
275: print_tagq(h, t);
276:
277: return(1);
278: }
279:
280:
281: /* ARGSUSED */
282: static void
283: man_root_post(MAN_ARGS)
284: {
285: struct tm tm;
286: struct htmlpair tag[2];
287: struct tag *t, *tt;
288: char b[BUFSIZ];
289:
290: (void)localtime_r(&m->date, &tm);
291:
292: if (0 == strftime(b, BUFSIZ - 1, "%B %e, %Y", &tm))
293: err(EXIT_FAILURE, "strftime");
294:
295: tag[0].key = ATTR_CLASS;
296: tag[0].val = "footer";
297: tag[1].key = ATTR_STYLE;
298: tag[1].val = "width: 100%;";
299: t = print_otag(h, TAG_TABLE, 2, tag);
300: tt = print_otag(h, TAG_TR, 0, NULL);
301:
302: tag[0].key = ATTR_STYLE;
303: tag[0].val = "width: 50%;";
304: print_otag(h, TAG_TD, 1, tag);
305: print_text(h, b);
306: print_stagq(h, tt);
307:
308: tag[0].key = ATTR_STYLE;
309: tag[0].val = "width: 50%; text-align: right;";
310: print_otag(h, TAG_TD, 1, tag);
311: if (m->source)
312: print_text(h, m->source);
313: print_tagq(h, t);
314: }
315:
316:
317:
318: /* ARGSUSED */
319: static int
320: man_br_pre(MAN_ARGS)
321: {
322: int len;
323: struct htmlpair tag;
324:
325: switch (n->tok) {
326: case (MAN_sp):
327: len = n->child ? atoi(n->child->string) : 1;
328: break;
329: case (MAN_br):
330: len = 0;
331: break;
332: default:
333: len = 1;
334: break;
335: }
336:
337: buffmt(h, "height: %dem;", len);
338: tag.key = ATTR_STYLE;
339: tag.val = h->buf;
340: print_otag(h, TAG_DIV, 1, &tag);
341: return(1);
342: }
343:
344:
345: /* ARGSUSED */
346: static int
347: man_SH_pre(MAN_ARGS)
348: {
349: struct htmlpair tag[2];
350:
351: if (MAN_BODY == n->type) {
352: buffmt(h, "margin-left: %dem;", INDENT);
353:
354: tag[0].key = ATTR_CLASS;
355: tag[0].val = "sec-body";
356: tag[1].key = ATTR_STYLE;
357: tag[1].val = h->buf;
358:
359: print_otag(h, TAG_DIV, 2, tag);
360: return(1);
361: } else if (MAN_BLOCK == n->type) {
362: tag[0].key = ATTR_CLASS;
363: tag[0].val = "sec-block";
364:
365: if (n->prev && MAN_SH == n->prev->tok)
366: if (NULL == n->prev->body->child) {
367: print_otag(h, TAG_DIV, 1, tag);
368: return(1);
369: }
370:
371: bufcat(h, "margin-top: 1em;");
372: if (NULL == n->next)
373: bufcat(h, "margin-bottom: 1em;");
374:
375: tag[1].key = ATTR_STYLE;
376: tag[1].val = h->buf;
377:
378: print_otag(h, TAG_DIV, 2, tag);
379: return(1);
380: }
381:
382: tag[0].key = ATTR_CLASS;
383: tag[0].val = "sec-head";
384:
385: print_otag(h, TAG_DIV, 1, tag);
386: return(1);
387: }
388:
389:
390: /* ARGSUSED */
391: static int
392: man_SS_pre(MAN_ARGS)
393: {
394: struct htmlpair tag[3];
395: int i;
396:
397: i = 0;
398:
399: if (MAN_BODY == n->type) {
400: tag[i].key = ATTR_CLASS;
401: tag[i++].val = "ssec-body";
402:
403: if (n->parent->next && n->child) {
404: bufcat(h, "margin-bottom: 1em;");
405: tag[i].key = ATTR_STYLE;
406: tag[i++].val = h->buf;
407: }
408:
409: print_otag(h, TAG_DIV, i, tag);
410: return(1);
411: } else if (MAN_BLOCK == n->type) {
412: tag[i].key = ATTR_CLASS;
413: tag[i++].val = "ssec-block";
414:
415: if (n->prev && MAN_SS == n->prev->tok)
416: if (n->prev->body->child) {
417: bufcat(h, "margin-top: 1em;");
418: tag[i].key = ATTR_STYLE;
419: tag[i++].val = h->buf;
420: }
421:
422: print_otag(h, TAG_DIV, i, tag);
423: return(1);
424: }
425:
426: buffmt(h, "margin-left: -%dem;", INDENT - HALFINDENT);
427:
428: tag[0].key = ATTR_CLASS;
429: tag[0].val = "ssec-head";
430: tag[1].key = ATTR_STYLE;
431: tag[1].val = h->buf;
432:
433: print_otag(h, TAG_DIV, 2, tag);
434: return(1);
435: }
436:
437:
438: /* ARGSUSED */
439: static int
440: man_PP_pre(MAN_ARGS)
441: {
1.5 kristaps 442: struct htmlpair tag;
443: int i;
1.4 kristaps 444:
445: if (MAN_BLOCK != n->type)
446: return(1);
447:
1.5 kristaps 448: i = 0;
449:
450: if (MAN_ROOT == n->parent->tok) {
451: buffmt(h, "margin-left: %dem;", INDENT);
452: i = 1;
453: }
454: if (n->next && n->next->child) {
455: i = 1;
1.4 kristaps 456: bufcat(h, "margin-bottom: 1em;");
1.5 kristaps 457: }
1.4 kristaps 458:
459: tag.key = ATTR_STYLE;
460: tag.val = h->buf;
1.5 kristaps 461: print_otag(h, TAG_DIV, i, &tag);
462: return(1);
463: }
464:
465:
466: /* ARGSUSED */
467: static int
468: man_IP_pre(MAN_ARGS)
469: {
470: struct htmlpair tag;
471: int len, ival;
472: const struct man_node *nn;
473:
1.6 ! kristaps 474: if (MAN_BODY == n->type) {
! 475: print_otag(h, TAG_DIV, 0, NULL);
! 476: return(1);
! 477: }
! 478:
! 479: nn = MAN_BLOCK == n->type ?
! 480: n->head->child : n->parent->head->child;
! 481: len = INDENT;
! 482: ival = -1;
! 483:
! 484: /* Calculate the indentation length. */
! 485:
! 486: if (NULL != nn)
1.5 kristaps 487: if (NULL != (nn = nn->next)) {
488: for ( ; nn->next; nn = nn->next)
489: /* Do nothing. */ ;
490: if ((ival = a2width(nn)) >= 0)
491: len = ival;
492: }
493:
494: if (MAN_BLOCK == n->type) {
495: buffmt(h, "clear: both; margin-left: %dem;", len);
496: tag.key = ATTR_STYLE;
497: tag.val = h->buf;
498: print_otag(h, TAG_DIV, 1, &tag);
499: return(1);
1.6 ! kristaps 500: }
! 501:
! 502: /* If there's an indent string, print it out. */
! 503:
! 504: buffmt(h, "margin-left: -%dem; min-width: %dem;",
! 505: len, len - 1);
! 506: bufcat(h, "clear: left; padding-right: 1em;");
! 507:
! 508: if (n->next && n->next->child)
! 509: bufcat(h, "float: left;");
! 510:
! 511: tag.key = ATTR_STYLE;
! 512: tag.val = h->buf;
! 513: print_otag(h, TAG_DIV, 1, &tag);
! 514:
! 515: if (ival < 0)
! 516: return(1);
! 517:
! 518: /* With a length string, omit the last child. */
! 519:
! 520: for (nn = n->child; nn->next; nn = nn->next)
! 521: print_man_node(m, nn, h);
! 522:
! 523: return(0);
! 524: }
! 525:
! 526:
! 527: /* ARGSUSED */
! 528: static int
! 529: man_HP_pre(MAN_ARGS)
! 530: {
! 531: int ival, len;
! 532: const struct man_node *nn;
! 533: struct htmlpair tag;
! 534:
! 535: if (MAN_HEAD == n->type)
! 536: return(0);
! 537:
! 538: nn = MAN_BLOCK == n->type ?
! 539: n->head->child : n->parent->head->child;
! 540:
! 541: len = INDENT;
! 542:
! 543: if (NULL != nn)
! 544: if ((ival = a2width(nn)) >= 0)
! 545: len = ival;
! 546:
! 547: if (MAN_BLOCK == n->type) {
! 548: buffmt(h, "clear: both; margin-left: %dem;", len);
1.5 kristaps 549: tag.key = ATTR_STYLE;
550: tag.val = h->buf;
551: print_otag(h, TAG_DIV, 1, &tag);
1.6 ! kristaps 552: return(1);
! 553: }
1.5 kristaps 554:
1.6 ! kristaps 555: buffmt(h, "text-indent: -%dem;", len);
1.5 kristaps 556:
1.6 ! kristaps 557: tag.key = ATTR_STYLE;
! 558: tag.val = h->buf;
! 559: print_otag(h, TAG_DIV, 1, &tag);
1.5 kristaps 560:
1.4 kristaps 561: return(1);
562: }
1.6 ! kristaps 563:
CVSweb