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