]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
Structural components of -man -Thtml in place (note that HP is the same as IP, as...
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.6 2009/10/04 15:24:54 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 /* TODO: preserve ident widths. */
31
32 #define INDENT 5
33 #define HALFINDENT 3
34
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);
46 static void print_man_nodelist(MAN_ARGS);
47 static void print_man_node(MAN_ARGS);
48
49 static int a2width(const struct man_node *);
50
51 static int man_br_pre(MAN_ARGS);
52 static int man_HP_pre(MAN_ARGS);
53 static int man_IP_pre(MAN_ARGS);
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
64
65 static const struct htmlman mans[MAN_MAX] = {
66 { man_br_pre, NULL }, /* br */
67 { NULL, NULL }, /* TH */
68 { man_SH_pre, NULL }, /* SH */
69 { man_SS_pre, NULL }, /* SS */
70 { man_IP_pre, NULL }, /* TP */
71 { man_PP_pre, NULL }, /* LP */
72 { man_PP_pre, NULL }, /* PP */
73 { man_PP_pre, NULL }, /* P */
74 { man_IP_pre, NULL }, /* IP */
75 { man_HP_pre, NULL }, /* HP */
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 */
89 { man_br_pre, NULL }, /* sp */
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
99
100 void
101 html_man(void *arg, const struct man *m)
102 {
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
134 print_man_nodelist(m, n, h);
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);
151 }
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
209 static int
210 a2width(const struct man_node *n)
211 {
212 int i, len;
213 const char *p;
214
215 if (MAN_TEXT != n->type)
216 return(-1);
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
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 {
442 struct htmlpair tag;
443 int i;
444
445 if (MAN_BLOCK != n->type)
446 return(1);
447
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;
456 bufcat(h, "margin-bottom: 1em;");
457 }
458
459 tag.key = ATTR_STYLE;
460 tag.val = h->buf;
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
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)
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);
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);
549 tag.key = ATTR_STYLE;
550 tag.val = h->buf;
551 print_otag(h, TAG_DIV, 1, &tag);
552 return(1);
553 }
554
555 buffmt(h, "text-indent: -%dem;", len);
556
557 tag.key = ATTR_STYLE;
558 tag.val = h->buf;
559 print_otag(h, TAG_DIV, 1, &tag);
560
561 return(1);
562 }
563