]> git.cameronkatri.com Git - mandoc.git/blob - html.c
Fixed body/divbody structure of html pages.
[mandoc.git] / html.c
1 /* $Id: html.c,v 1.46 2009/09/21 13:44:56 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 #include <unistd.h>
27
28 #include "chars.h"
29 #include "mdoc.h"
30 #include "man.h"
31
32 #define DOCTYPE "-//W3C//DTD HTML 4.01//EN"
33 #define DTD "http://www.w3.org/TR/html4/strict.dtd"
34
35 #define INDENT 5
36 #define HALFINDENT 3
37
38 enum htmltag {
39 TAG_HTML,
40 TAG_HEAD,
41 TAG_BODY,
42 TAG_META,
43 TAG_TITLE,
44 TAG_DIV,
45 TAG_H1,
46 TAG_H2,
47 TAG_P,
48 TAG_SPAN,
49 TAG_LINK,
50 TAG_BR,
51 TAG_A,
52 TAG_TABLE,
53 TAG_COL,
54 TAG_TR,
55 TAG_TD,
56 TAG_LI,
57 TAG_UL,
58 TAG_OL,
59 TAG_BASE,
60 TAG_MAX
61 };
62
63 enum htmlattr {
64 ATTR_HTTPEQUIV,
65 ATTR_CONTENT,
66 ATTR_NAME,
67 ATTR_REL,
68 ATTR_HREF,
69 ATTR_TYPE,
70 ATTR_MEDIA,
71 ATTR_CLASS,
72 ATTR_STYLE,
73 ATTR_WIDTH,
74 ATTR_VALIGN,
75 ATTR_MAX
76 };
77
78 struct htmldata {
79 char *name;
80 int flags;
81 #define HTML_CLRLINE (1 << 0)
82 #define HTML_NOSTACK (1 << 1)
83 };
84
85 static const struct htmldata htmltags[TAG_MAX] = {
86 {"html", HTML_CLRLINE}, /* TAG_HTML */
87 {"head", HTML_CLRLINE}, /* TAG_HEAD */
88 {"body", HTML_CLRLINE}, /* TAG_BODY */
89 {"meta", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_META */
90 {"title", HTML_CLRLINE}, /* TAG_TITLE */
91 {"div", HTML_CLRLINE}, /* TAG_DIV */
92 {"h1", 0}, /* TAG_H1 */
93 {"h2", 0}, /* TAG_H2 */
94 {"p", HTML_CLRLINE}, /* TAG_P */
95 {"span", 0}, /* TAG_SPAN */
96 {"link", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
97 {"br", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
98 {"a", 0}, /* TAG_A */
99 {"table", HTML_CLRLINE}, /* TAG_TABLE */
100 {"col", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_COL */
101 {"tr", HTML_CLRLINE}, /* TAG_TR */
102 {"td", HTML_CLRLINE}, /* TAG_TD */
103 {"li", HTML_CLRLINE}, /* TAG_LI */
104 {"ul", HTML_CLRLINE}, /* TAG_UL */
105 {"ol", HTML_CLRLINE}, /* TAG_OL */
106 {"base", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_BASE */
107 };
108
109 static const char *const htmlattrs[ATTR_MAX] = {
110 "http-equiv",
111 "content",
112 "name",
113 "rel",
114 "href",
115 "type",
116 "media",
117 "class",
118 "style",
119 "width",
120 "valign",
121 };
122
123 struct htmlpair {
124 enum htmlattr key;
125 char *val;
126 };
127
128 struct tag {
129 enum htmltag tag;
130 SLIST_ENTRY(tag) entry;
131 };
132
133 struct ord {
134 int pos;
135 const void *cookie;
136 SLIST_ENTRY(ord) entry;
137 };
138
139 SLIST_HEAD(tagq, tag);
140 SLIST_HEAD(ordq, ord);
141
142 struct html {
143 int flags;
144 #define HTML_NOSPACE (1 << 0)
145 #define HTML_NEWLINE (1 << 1)
146 struct tagq tags;
147 struct ordq ords;
148 void *symtab;
149 char *base;
150 char *style;
151 };
152
153 #define MDOC_ARGS const struct mdoc_meta *m, \
154 const struct mdoc_node *n, \
155 struct html *h
156 #define MAN_ARGS const struct man_meta *m, \
157 const struct man_node *n, \
158 struct html *h
159 struct htmlmdoc {
160 int (*pre)(MDOC_ARGS);
161 void (*post)(MDOC_ARGS);
162 };
163
164 static void print_gen_doctype(struct html *);
165 static void print_gen_head(struct html *);
166 static void print_mdoc(MDOC_ARGS);
167 static void print_mdoc_head(MDOC_ARGS);
168 static void print_mdoc_node(MDOC_ARGS);
169 static void print_mdoc_nodelist(MDOC_ARGS);
170 static void print_man(MAN_ARGS);
171 static void print_man_head(MAN_ARGS);
172 static struct tag *print_otag(struct html *, enum htmltag,
173 int, const struct htmlpair *);
174 static void print_tagq(struct html *, const struct tag *);
175 static void print_stagq(struct html *, const struct tag *);
176 static void print_ctag(struct html *, enum htmltag);
177 static void print_encode(struct html *, const char *);
178 static void print_escape(struct html *, const char **);
179 static void print_text(struct html *, const char *);
180 static void print_res(struct html *, const char *, int);
181 static void print_spec(struct html *, const char *, int);
182
183 static int a2width(const char *);
184 static int a2offs(const char *);
185 static int a2list(const struct mdoc_node *);
186
187 static void mdoc_root_post(MDOC_ARGS);
188 static int mdoc_root_pre(MDOC_ARGS);
189 static int mdoc_tbl_pre(MDOC_ARGS, int);
190 static int mdoc_tbl_block_pre(MDOC_ARGS, int, int, int, int);
191 static int mdoc_tbl_body_pre(MDOC_ARGS, int, int);
192 static int mdoc_tbl_head_pre(MDOC_ARGS, int, int);
193
194 static int mdoc_ad_pre(MDOC_ARGS);
195 static int mdoc_an_pre(MDOC_ARGS);
196 static void mdoc_aq_post(MDOC_ARGS);
197 static int mdoc_aq_pre(MDOC_ARGS);
198 static int mdoc_ar_pre(MDOC_ARGS);
199 static int mdoc_bd_pre(MDOC_ARGS);
200 static void mdoc_bl_post(MDOC_ARGS);
201 static int mdoc_bl_pre(MDOC_ARGS);
202 static int mdoc_cd_pre(MDOC_ARGS);
203 static int mdoc_d1_pre(MDOC_ARGS);
204 static void mdoc_dq_post(MDOC_ARGS);
205 static int mdoc_dq_pre(MDOC_ARGS);
206 static int mdoc_dv_pre(MDOC_ARGS);
207 static int mdoc_fa_pre(MDOC_ARGS);
208 static int mdoc_fd_pre(MDOC_ARGS);
209 static int mdoc_fl_pre(MDOC_ARGS);
210 static int mdoc_fn_pre(MDOC_ARGS);
211 static int mdoc_ft_pre(MDOC_ARGS);
212 static int mdoc_em_pre(MDOC_ARGS);
213 static int mdoc_er_pre(MDOC_ARGS);
214 static int mdoc_ev_pre(MDOC_ARGS);
215 static int mdoc_ex_pre(MDOC_ARGS);
216 static int mdoc_it_pre(MDOC_ARGS);
217 static int mdoc_nd_pre(MDOC_ARGS);
218 static int mdoc_nm_pre(MDOC_ARGS);
219 static int mdoc_ns_pre(MDOC_ARGS);
220 static void mdoc_op_post(MDOC_ARGS);
221 static int mdoc_op_pre(MDOC_ARGS);
222 static int mdoc_pa_pre(MDOC_ARGS);
223 static int mdoc_pp_pre(MDOC_ARGS);
224 static void mdoc_pq_post(MDOC_ARGS);
225 static int mdoc_pq_pre(MDOC_ARGS);
226 static void mdoc_qq_post(MDOC_ARGS);
227 static int mdoc_qq_pre(MDOC_ARGS);
228 static int mdoc_sh_pre(MDOC_ARGS);
229 static void mdoc_sq_post(MDOC_ARGS);
230 static int mdoc_sq_pre(MDOC_ARGS);
231 static int mdoc_ss_pre(MDOC_ARGS);
232 static int mdoc_sx_pre(MDOC_ARGS);
233 static int mdoc_vt_pre(MDOC_ARGS);
234 static int mdoc_xr_pre(MDOC_ARGS);
235 static int mdoc_xx_pre(MDOC_ARGS);
236
237 #ifdef __linux__
238 extern int getsubopt(char **, char * const *, char **);
239 extern size_t strlcpy(char *, const char *, size_t);
240 extern size_t strlcat(char *, const char *, size_t);
241 #endif
242
243 static const struct htmlmdoc mdocs[MDOC_MAX] = {
244 {mdoc_pp_pre, NULL}, /* Ap */
245 {NULL, NULL}, /* Dd */
246 {NULL, NULL}, /* Dt */
247 {NULL, NULL}, /* Os */
248 {mdoc_sh_pre, NULL }, /* Sh */
249 {mdoc_ss_pre, NULL }, /* Ss */
250 {mdoc_pp_pre, NULL}, /* Pp */
251 {mdoc_d1_pre, NULL}, /* D1 */
252 {mdoc_d1_pre, NULL}, /* Dl */
253 {mdoc_bd_pre, NULL}, /* Bd */
254 {NULL, NULL}, /* Ed */
255 {mdoc_bl_pre, mdoc_bl_post}, /* Bl */
256 {NULL, NULL}, /* El */
257 {mdoc_it_pre, NULL}, /* It */
258 {mdoc_ad_pre, NULL}, /* Ad */
259 {mdoc_an_pre, NULL}, /* An */
260 {mdoc_ar_pre, NULL}, /* Ar */
261 {mdoc_cd_pre, NULL}, /* Cd */
262 {mdoc_fl_pre, NULL}, /* Cm */
263 {mdoc_dv_pre, NULL}, /* Dv */
264 {mdoc_er_pre, NULL}, /* Er */
265 {mdoc_ev_pre, NULL}, /* Ev */
266 {mdoc_ex_pre, NULL}, /* Ex */
267 {mdoc_fa_pre, NULL}, /* Fa */
268 {mdoc_fd_pre, NULL}, /* Fd */
269 {mdoc_fl_pre, NULL}, /* Fl */
270 {mdoc_fn_pre, NULL}, /* Fn */
271 {mdoc_ft_pre, NULL}, /* Ft */
272 {NULL, NULL}, /* Ic */
273 {NULL, NULL}, /* In */
274 {NULL, NULL}, /* Li */
275 {mdoc_nd_pre, NULL}, /* Nd */
276 {mdoc_nm_pre, NULL}, /* Nm */
277 {mdoc_op_pre, mdoc_op_post}, /* Op */
278 {NULL, NULL}, /* Ot */
279 {mdoc_pa_pre, NULL}, /* Pa */
280 {NULL, NULL}, /* Rv */
281 {NULL, NULL}, /* St */
282 {NULL, NULL}, /* Va */
283 {mdoc_vt_pre, NULL}, /* Vt */
284 {mdoc_xr_pre, NULL}, /* Xr */
285 {NULL, NULL}, /* %A */
286 {NULL, NULL}, /* %B */
287 {NULL, NULL}, /* %D */
288 {NULL, NULL}, /* %I */
289 {NULL, NULL}, /* %J */
290 {NULL, NULL}, /* %N */
291 {NULL, NULL}, /* %O */
292 {NULL, NULL}, /* %P */
293 {NULL, NULL}, /* %R */
294 {NULL, NULL}, /* %T */
295 {NULL, NULL}, /* %V */
296 {NULL, NULL}, /* Ac */
297 {mdoc_aq_pre, mdoc_aq_post}, /* Ao */
298 {mdoc_aq_pre, mdoc_aq_post}, /* Aq */
299 {NULL, NULL}, /* At */
300 {NULL, NULL}, /* Bc */
301 {NULL, NULL}, /* Bf */
302 {NULL, NULL}, /* Bo */
303 {NULL, NULL}, /* Bq */
304 {mdoc_xx_pre, NULL}, /* Bsx */
305 {NULL, NULL}, /* Bx */
306 {NULL, NULL}, /* Db */
307 {NULL, NULL}, /* Dc */
308 {NULL, NULL}, /* Do */
309 {mdoc_dq_pre, mdoc_dq_post}, /* Dq */
310 {NULL, NULL}, /* Ec */
311 {NULL, NULL}, /* Ef */
312 {mdoc_em_pre, NULL}, /* Em */
313 {NULL, NULL}, /* Eo */
314 {mdoc_xx_pre, NULL}, /* Fx */
315 {NULL, NULL}, /* Ms */
316 {NULL, NULL}, /* No */
317 {mdoc_ns_pre, NULL}, /* Ns */
318 {mdoc_xx_pre, NULL}, /* Nx */
319 {mdoc_xx_pre, NULL}, /* Ox */
320 {NULL, NULL}, /* Pc */
321 {NULL, NULL}, /* Pf */
322 {mdoc_pq_pre, mdoc_pq_post}, /* Po */
323 {mdoc_pq_pre, mdoc_pq_post}, /* Pq */
324 {NULL, NULL}, /* Qc */
325 {NULL, NULL}, /* Ql */
326 {mdoc_qq_pre, mdoc_qq_post}, /* Qo */
327 {mdoc_qq_pre, mdoc_qq_post}, /* Qq */
328 {NULL, NULL}, /* Re */
329 {NULL, NULL}, /* Rs */
330 {NULL, NULL}, /* Sc */
331 {mdoc_sq_pre, mdoc_sq_post}, /* So */
332 {mdoc_sq_pre, mdoc_sq_post}, /* Sq */
333 {NULL, NULL}, /* Sm */
334 {mdoc_sx_pre, NULL}, /* Sx */
335 {NULL, NULL}, /* Sy */
336 {NULL, NULL}, /* Tn */
337 {mdoc_xx_pre, NULL}, /* Ux */
338 {NULL, NULL}, /* Xc */
339 {NULL, NULL}, /* Xo */
340 {NULL, NULL}, /* Fo */
341 {NULL, NULL}, /* Fc */
342 {NULL, NULL}, /* Oo */
343 {NULL, NULL}, /* Oc */
344 {NULL, NULL}, /* Bk */
345 {NULL, NULL}, /* Ek */
346 {NULL, NULL}, /* Bt */
347 {NULL, NULL}, /* Hf */
348 {NULL, NULL}, /* Fr */
349 {NULL, NULL}, /* Ud */
350 {NULL, NULL}, /* Lb */
351 {NULL, NULL}, /* Lp */
352 {NULL, NULL}, /* Lk */
353 {NULL, NULL}, /* Mt */
354 {NULL, NULL}, /* Brq */
355 {NULL, NULL}, /* Bro */
356 {NULL, NULL}, /* Brc */
357 {NULL, NULL}, /* %C */
358 {NULL, NULL}, /* Es */
359 {NULL, NULL}, /* En */
360 {mdoc_xx_pre, NULL}, /* Dx */
361 {NULL, NULL}, /* %Q */
362 {NULL, NULL}, /* br */
363 {NULL, NULL}, /* sp */
364 };
365
366 static char buf[BUFSIZ]; /* XXX */
367
368 #define bufcat(x) (void)strlcat(buf, (x), BUFSIZ)
369 #define bufinit() buf[0] = 0
370 #define buffmt(...) (void)snprintf(buf, BUFSIZ - 1, __VA_ARGS__)
371
372 void
373 html_mdoc(void *arg, const struct mdoc *m)
374 {
375 struct html *h;
376 struct tag *t;
377
378 h = (struct html *)arg;
379
380 print_gen_doctype(h);
381 t = print_otag(h, TAG_HTML, 0, NULL);
382 print_mdoc(mdoc_meta(m), mdoc_node(m), h);
383 print_tagq(h, t);
384
385 printf("\n");
386 }
387
388
389 void
390 html_man(void *arg, const struct man *m)
391 {
392 struct html *h;
393 struct tag *t;
394
395 h = (struct html *)arg;
396
397 print_gen_doctype(h);
398 t = print_otag(h, TAG_HTML, 0, NULL);
399 print_man(man_meta(m), man_node(m), h);
400 print_tagq(h, t);
401
402 printf("\n");
403 }
404
405
406 void *
407 html_alloc(char *outopts)
408 {
409 struct html *h;
410 char *toks[3], *v;
411
412 toks[0] = "style";
413 toks[1] = "base";
414 toks[2] = NULL;
415
416 if (NULL == (h = calloc(1, sizeof(struct html))))
417 return(NULL);
418
419 SLIST_INIT(&h->tags);
420 SLIST_INIT(&h->ords);
421
422 if (NULL == (h->symtab = chars_init(CHARS_HTML))) {
423 free(h);
424 return(NULL);
425 }
426
427 while (*outopts)
428 switch (getsubopt(&outopts, toks, &v)) {
429 case (0):
430 h->style = v;
431 break;
432 case (1):
433 h->base = v;
434 break;
435 default:
436 break;
437 }
438
439 return(h);
440 }
441
442
443 void
444 html_free(void *p)
445 {
446 struct tag *tag;
447 struct ord *ord;
448 struct html *h;
449
450 h = (struct html *)p;
451
452 while ( ! SLIST_EMPTY(&h->ords)) {
453 ord = SLIST_FIRST(&h->ords);
454 SLIST_REMOVE_HEAD(&h->ords, entry);
455 free(ord);
456 }
457
458 while ( ! SLIST_EMPTY(&h->tags)) {
459 tag = SLIST_FIRST(&h->tags);
460 SLIST_REMOVE_HEAD(&h->tags, entry);
461 free(tag);
462 }
463
464 if (h->symtab)
465 chars_free(h->symtab);
466 free(h);
467 }
468
469
470 static int
471 a2list(const struct mdoc_node *n)
472 {
473 int i;
474
475 assert(MDOC_BLOCK == n->type && MDOC_Bl == n->tok);
476 assert(n->args);
477
478 for (i = 0; i < (int)n->args->argc; i++)
479 switch (n->args->argv[i].arg) {
480 case (MDOC_Enum):
481 /* FALLTHROUGH */
482 case (MDOC_Dash):
483 /* FALLTHROUGH */
484 case (MDOC_Hyphen):
485 /* FALLTHROUGH */
486 case (MDOC_Bullet):
487 /* FALLTHROUGH */
488 case (MDOC_Tag):
489 /* FALLTHROUGH */
490 case (MDOC_Hang):
491 /* FALLTHROUGH */
492 case (MDOC_Inset):
493 /* FALLTHROUGH */
494 case (MDOC_Diag):
495 /* FALLTHROUGH */
496 case (MDOC_Item):
497 /* FALLTHROUGH */
498 case (MDOC_Column):
499 /* FALLTHROUGH */
500 case (MDOC_Ohang):
501 return(n->args->argv[i].arg);
502 default:
503 break;
504 }
505
506 abort();
507 /* NOTREACHED */
508 }
509
510
511 static int
512 a2width(const char *p)
513 {
514 int i, len;
515
516 if (0 == (len = (int)strlen(p)))
517 return(0);
518 for (i = 0; i < len - 1; i++)
519 if ( ! isdigit((u_char)p[i]))
520 break;
521
522 if (i == len - 1)
523 if ('n' == p[len - 1] || 'm' == p[len - 1])
524 return(atoi(p) + 2);
525
526 return(len + 2);
527 }
528
529
530 static int
531 a2offs(const char *p)
532 {
533 int len, i;
534
535 if (0 == strcmp(p, "left"))
536 return(0);
537 if (0 == strcmp(p, "indent"))
538 return(INDENT + 1);
539 if (0 == strcmp(p, "indent-two"))
540 return((INDENT + 1) * 2);
541
542 if (0 == (len = (int)strlen(p)))
543 return(0);
544
545 for (i = 0; i < len - 1; i++)
546 if ( ! isdigit((u_char)p[i]))
547 break;
548
549 if (i == len - 1)
550 if ('n' == p[len - 1] || 'm' == p[len - 1])
551 return(atoi(p));
552
553 return(len);
554 }
555
556
557 static void
558 print_mdoc(MDOC_ARGS)
559 {
560 struct tag *t;
561 struct htmlpair tag;
562
563 t = print_otag(h, TAG_HEAD, 0, NULL);
564 print_mdoc_head(m, n, h);
565 print_tagq(h, t);
566
567 t = print_otag(h, TAG_BODY, 0, NULL);
568
569 tag.key = ATTR_CLASS;
570 tag.val = "body";
571 print_otag(h, TAG_DIV, 1, &tag);
572
573 print_mdoc_nodelist(m, n, h);
574 print_tagq(h, t);
575 }
576
577
578 static void
579 print_gen_head(struct html *h)
580 {
581 struct htmlpair tag[4];
582
583 tag[0].key = ATTR_HTTPEQUIV;
584 tag[0].val = "Content-Type";
585 tag[1].key = ATTR_CONTENT;
586 tag[1].val = "text/html; charset=utf-8";
587 print_otag(h, TAG_META, 2, tag);
588
589 tag[0].key = ATTR_NAME;
590 tag[0].val = "resource-type";
591 tag[1].key = ATTR_CONTENT;
592 tag[1].val = "document";
593 print_otag(h, TAG_META, 2, tag);
594
595 if (h->style) {
596 tag[0].key = ATTR_REL;
597 tag[0].val = "stylesheet";
598 tag[1].key = ATTR_HREF;
599 tag[1].val = h->style;
600 tag[2].key = ATTR_TYPE;
601 tag[2].val = "text/css";
602 tag[3].key = ATTR_MEDIA;
603 tag[3].val = "all";
604 print_otag(h, TAG_LINK, 4, tag);
605 }
606
607 if (h->base) {
608 tag[0].key = ATTR_HREF;
609 tag[1].val = h->base;
610 print_otag(h, TAG_BASE, 1, tag);
611 }
612 }
613
614
615 /* ARGSUSED */
616 static void
617 print_mdoc_head(MDOC_ARGS)
618 {
619
620 print_gen_head(h);
621 print_otag(h, TAG_TITLE, 0, NULL);
622 print_text(h, m->title);
623 }
624
625
626 static void
627 print_mdoc_nodelist(MDOC_ARGS)
628 {
629
630 print_mdoc_node(m, n, h);
631 if (n->next)
632 print_mdoc_nodelist(m, n->next, h);
633 }
634
635
636 static void
637 print_mdoc_node(MDOC_ARGS)
638 {
639 int child;
640 struct tag *t;
641
642 child = 1;
643 t = SLIST_FIRST(&h->tags);
644
645 bufinit();
646
647 switch (n->type) {
648 case (MDOC_ROOT):
649 child = mdoc_root_pre(m, n, h);
650 break;
651 case (MDOC_TEXT):
652 print_text(h, n->string);
653 break;
654 default:
655 if (mdocs[n->tok].pre)
656 child = (*mdocs[n->tok].pre)(m, n, h);
657 break;
658 }
659
660 if (child && n->child)
661 print_mdoc_nodelist(m, n->child, h);
662
663 print_stagq(h, t);
664
665 bufinit();
666
667 switch (n->type) {
668 case (MDOC_ROOT):
669 mdoc_root_post(m, n, h);
670 break;
671 case (MDOC_TEXT):
672 break;
673 default:
674 if (mdocs[n->tok].post)
675 (*mdocs[n->tok].post)(m, n, h);
676 break;
677 }
678 }
679
680
681 static void
682 print_man(MAN_ARGS)
683 {
684 struct tag *t;
685
686 t = print_otag(h, TAG_HEAD, 0, NULL);
687 print_man_head(m, n, h);
688 print_tagq(h, t);
689
690 t = print_otag(h, TAG_BODY, 0, NULL);
691 /*print_man_body(m, n, h);*/
692 print_tagq(h, t);
693 }
694
695
696 /* ARGSUSED */
697 static void
698 print_man_head(MAN_ARGS)
699 {
700
701 print_gen_head(h);
702 print_otag(h, TAG_TITLE, 0, NULL);
703 print_text(h, m->title);
704 }
705
706
707 static void
708 print_spec(struct html *h, const char *p, int len)
709 {
710 const char *rhs;
711 int i;
712 size_t sz;
713
714 rhs = chars_a2ascii(h->symtab, p, (size_t)len, &sz);
715
716 if (NULL == rhs)
717 return;
718 for (i = 0; i < (int)sz; i++)
719 putchar(rhs[i]);
720 }
721
722
723 static void
724 print_res(struct html *h, const char *p, int len)
725 {
726 const char *rhs;
727 int i;
728 size_t sz;
729
730 rhs = chars_a2res(h->symtab, p, (size_t)len, &sz);
731
732 if (NULL == rhs)
733 return;
734 for (i = 0; i < (int)sz; i++)
735 putchar(rhs[i]);
736 }
737
738
739 static void
740 print_escape(struct html *h, const char **p)
741 {
742 int j, type;
743 const char *wp;
744
745 wp = *p;
746 type = 1;
747
748 if (0 == *(++wp)) {
749 *p = wp;
750 return;
751 }
752
753 if ('(' == *wp) {
754 wp++;
755 if (0 == *wp || 0 == *(wp + 1)) {
756 *p = 0 == *wp ? wp : wp + 1;
757 return;
758 }
759
760 print_spec(h, wp, 2);
761 *p = ++wp;
762 return;
763
764 } else if ('*' == *wp) {
765 if (0 == *(++wp)) {
766 *p = wp;
767 return;
768 }
769
770 switch (*wp) {
771 case ('('):
772 wp++;
773 if (0 == *wp || 0 == *(wp + 1)) {
774 *p = 0 == *wp ? wp : wp + 1;
775 return;
776 }
777
778 print_res(h, wp, 2);
779 *p = ++wp;
780 return;
781 case ('['):
782 type = 0;
783 break;
784 default:
785 print_res(h, wp, 1);
786 *p = wp;
787 return;
788 }
789
790 } else if ('f' == *wp) {
791 if (0 == *(++wp)) {
792 *p = wp;
793 return;
794 }
795
796 switch (*wp) {
797 case ('B'):
798 /* TODO */
799 break;
800 case ('I'):
801 /* TODO */
802 break;
803 case ('P'):
804 /* FALLTHROUGH */
805 case ('R'):
806 /* TODO */
807 break;
808 default:
809 break;
810 }
811
812 *p = wp;
813 return;
814
815 } else if ('[' != *wp) {
816 print_spec(h, wp, 1);
817 *p = wp;
818 return;
819 }
820
821 wp++;
822 for (j = 0; *wp && ']' != *wp; wp++, j++)
823 /* Loop... */ ;
824
825 if (0 == *wp) {
826 *p = wp;
827 return;
828 }
829
830 if (type)
831 print_spec(h, wp - j, j);
832 else
833 print_res(h, wp - j, j);
834
835 *p = wp;
836 }
837
838
839 static void
840 print_encode(struct html *h, const char *p)
841 {
842
843 for (; *p; p++) {
844 if ('\\' == *p) {
845 print_escape(h, &p);
846 continue;
847 }
848 switch (*p) {
849 case ('<'):
850 printf("&lt;");
851 break;
852 case ('>'):
853 printf("&gt;");
854 break;
855 case ('&'):
856 printf("&amp;");
857 break;
858 default:
859 putchar(*p);
860 break;
861 }
862 }
863 }
864
865
866 static struct tag *
867 print_otag(struct html *h, enum htmltag tag,
868 int sz, const struct htmlpair *p)
869 {
870 int i;
871 struct tag *t;
872
873 if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
874 if (NULL == (t = malloc(sizeof(struct tag))))
875 err(EXIT_FAILURE, "malloc");
876 t->tag = tag;
877 SLIST_INSERT_HEAD(&h->tags, t, entry);
878 } else
879 t = NULL;
880
881 if ( ! (HTML_NOSPACE & h->flags))
882 if ( ! (HTML_CLRLINE & htmltags[tag].flags))
883 printf(" ");
884
885 printf("<%s", htmltags[tag].name);
886 for (i = 0; i < sz; i++) {
887 printf(" %s=\"", htmlattrs[p[i].key]);
888 assert(p->val);
889 print_encode(h, p[i].val);
890 printf("\"");
891 }
892 printf(">");
893
894 h->flags |= HTML_NOSPACE;
895 if (HTML_CLRLINE & htmltags[tag].flags)
896 h->flags |= HTML_NEWLINE;
897 else
898 h->flags &= ~HTML_NEWLINE;
899
900 return(t);
901 }
902
903
904 /* ARGSUSED */
905 static void
906 print_ctag(struct html *h, enum htmltag tag)
907 {
908
909 printf("</%s>", htmltags[tag].name);
910 if (HTML_CLRLINE & htmltags[tag].flags)
911 h->flags |= HTML_NOSPACE;
912 if (HTML_CLRLINE & htmltags[tag].flags)
913 h->flags |= HTML_NEWLINE;
914 else
915 h->flags &= ~HTML_NEWLINE;
916 }
917
918
919 /* ARGSUSED */
920 static void
921 print_gen_doctype(struct html *h)
922 {
923
924 printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
925 }
926
927
928 static void
929 print_text(struct html *h, const char *p)
930 {
931
932 if (*p && 0 == *(p + 1))
933 switch (*p) {
934 case('.'):
935 /* FALLTHROUGH */
936 case(','):
937 /* FALLTHROUGH */
938 case(';'):
939 /* FALLTHROUGH */
940 case(':'):
941 /* FALLTHROUGH */
942 case('?'):
943 /* FALLTHROUGH */
944 case('!'):
945 /* FALLTHROUGH */
946 case(')'):
947 /* FALLTHROUGH */
948 case(']'):
949 /* FALLTHROUGH */
950 case('}'):
951 h->flags |= HTML_NOSPACE;
952 break;
953 default:
954 break;
955 }
956
957 if ( ! (h->flags & HTML_NOSPACE))
958 printf(" ");
959
960 h->flags &= ~HTML_NOSPACE;
961 h->flags &= ~HTML_NEWLINE;
962
963 if (p)
964 print_encode(h, p);
965
966 if (*p && 0 == *(p + 1))
967 switch (*p) {
968 case('('):
969 /* FALLTHROUGH */
970 case('['):
971 /* FALLTHROUGH */
972 case('{'):
973 h->flags |= HTML_NOSPACE;
974 break;
975 default:
976 break;
977 }
978 }
979
980
981 static void
982 print_tagq(struct html *h, const struct tag *until)
983 {
984 struct tag *tag;
985
986 while ( ! SLIST_EMPTY(&h->tags)) {
987 tag = SLIST_FIRST(&h->tags);
988 print_ctag(h, tag->tag);
989 SLIST_REMOVE_HEAD(&h->tags, entry);
990 free(tag);
991 if (until && tag == until)
992 return;
993 }
994 }
995
996
997 static void
998 print_stagq(struct html *h, const struct tag *suntil)
999 {
1000 struct tag *tag;
1001
1002 while ( ! SLIST_EMPTY(&h->tags)) {
1003 tag = SLIST_FIRST(&h->tags);
1004 if (suntil && tag == suntil)
1005 return;
1006 print_ctag(h, tag->tag);
1007 SLIST_REMOVE_HEAD(&h->tags, entry);
1008 free(tag);
1009 }
1010 }
1011
1012
1013 /* ARGSUSED */
1014 static void
1015 mdoc_root_post(MDOC_ARGS)
1016 {
1017 struct tm tm;
1018 struct htmlpair tag[2];
1019 struct tag *t, *tt;
1020 char b[BUFSIZ];
1021
1022 (void)localtime_r(&m->date, &tm);
1023
1024 if (0 == strftime(b, BUFSIZ - 1, "%B %e, %Y", &tm))
1025 err(EXIT_FAILURE, "strftime");
1026
1027 tag[0].key = ATTR_CLASS;
1028 tag[0].val = "footer";
1029 tag[1].key = ATTR_STYLE;
1030 tag[1].val = "width: 100%;";
1031 t = print_otag(h, TAG_TABLE, 2, tag);
1032 tt = print_otag(h, TAG_TR, 0, NULL);
1033
1034 tag[0].key = ATTR_STYLE;
1035 tag[0].val = "width: 50%;";
1036 print_otag(h, TAG_TD, 1, tag);
1037 print_text(h, b);
1038 print_stagq(h, tt);
1039
1040 tag[0].key = ATTR_STYLE;
1041 tag[0].val = "width: 50%; text-align: right;";
1042 print_otag(h, TAG_TD, 1, tag);
1043 print_text(h, m->os);
1044 print_tagq(h, t);
1045 }
1046
1047
1048 /* ARGSUSED */
1049 static int
1050 mdoc_root_pre(MDOC_ARGS)
1051 {
1052 struct htmlpair tag[2];
1053 struct tag *t, *tt;
1054 char b[BUFSIZ], title[BUFSIZ];
1055
1056 (void)strlcpy(b, m->vol, BUFSIZ);
1057
1058 if (m->arch) {
1059 (void)strlcat(b, " (", BUFSIZ);
1060 (void)strlcat(b, m->arch, BUFSIZ);
1061 (void)strlcat(b, ")", BUFSIZ);
1062 }
1063
1064 (void)snprintf(title, BUFSIZ - 1,
1065 "%s(%d)", m->title, m->msec);
1066
1067 tag[0].key = ATTR_CLASS;
1068 tag[0].val = "header";
1069 tag[1].key = ATTR_STYLE;
1070 tag[1].val = "width: 100%;";
1071 t = print_otag(h, TAG_TABLE, 2, tag);
1072 tt = print_otag(h, TAG_TR, 0, NULL);
1073
1074 tag[0].key = ATTR_STYLE;
1075 tag[0].val = "width: 33%;";
1076 print_otag(h, TAG_TD, 1, tag);
1077 print_text(h, b);
1078 print_stagq(h, tt);
1079
1080 tag[0].key = ATTR_STYLE;
1081 tag[0].val = "width: 33%; text-align: center;";
1082 print_otag(h, TAG_TD, 1, tag);
1083 print_text(h, title);
1084 print_stagq(h, tt);
1085
1086 tag[0].key = ATTR_STYLE;
1087 tag[0].val = "width: 33%; text-align: right;";
1088 print_otag(h, TAG_TD, 1, tag);
1089 print_text(h, b);
1090 print_tagq(h, t);
1091
1092 return(1);
1093 }
1094
1095
1096 /* ARGSUSED */
1097 static int
1098 mdoc_sh_pre(MDOC_ARGS)
1099 {
1100 struct htmlpair tag[2];
1101 const struct mdoc_node *nn;
1102
1103 if (MDOC_HEAD == n->type) {
1104 tag[0].key = ATTR_CLASS;
1105 tag[0].val = "sec-head";
1106 print_otag(h, TAG_DIV, 1, tag);
1107 print_otag(h, TAG_SPAN, 1, tag);
1108
1109 for (nn = n->child; nn; nn = nn->next) {
1110 bufcat(nn->string);
1111 if (nn->next)
1112 bufcat(" ");
1113 }
1114 tag[0].key = ATTR_NAME;
1115 tag[0].val = buf;
1116 print_otag(h, TAG_A, 1, tag);
1117 return(1);
1118 } else if (MDOC_BLOCK == n->type) {
1119 tag[0].key = ATTR_CLASS;
1120 tag[0].val = "sec-block";
1121
1122 if (n->prev && NULL == n->prev->body->child) {
1123 print_otag(h, TAG_DIV, 1, tag);
1124 return(1);
1125 }
1126
1127 bufcat("margin-top: 1em;");
1128 if (NULL == n->next)
1129 bufcat("margin-bottom: 1em;");
1130
1131 tag[1].key = ATTR_STYLE;
1132 tag[1].val = buf;
1133
1134 print_otag(h, TAG_DIV, 2, tag);
1135 return(1);
1136 }
1137
1138 buffmt("margin-left: %dem;", INDENT);
1139
1140 tag[0].key = ATTR_CLASS;
1141 tag[0].val = "sec-body";
1142 tag[1].key = ATTR_STYLE;
1143 tag[1].val = buf;
1144
1145 print_otag(h, TAG_DIV, 2, tag);
1146 return(1);
1147 }
1148
1149
1150 /* ARGSUSED */
1151 static int
1152 mdoc_ss_pre(MDOC_ARGS)
1153 {
1154 struct htmlpair tag[2];
1155 int i;
1156 const struct mdoc_node *nn;
1157
1158 i = 0;
1159
1160 if (MDOC_BODY == n->type) {
1161 tag[i].key = ATTR_CLASS;
1162 tag[i++].val = "ssec-body";
1163 if (n->parent->next && n->child) {
1164 bufcat("margin-bottom: 1em;");
1165 tag[i].key = ATTR_STYLE;
1166 tag[i++].val = buf;
1167 }
1168 print_otag(h, TAG_DIV, i, tag);
1169 return(1);
1170 } else if (MDOC_BLOCK == n->type) {
1171 tag[i].key = ATTR_CLASS;
1172 tag[i++].val = "ssec-block";
1173 if (n->prev) {
1174 bufcat("margin-top: 1em;");
1175 tag[i].key = ATTR_STYLE;
1176 tag[i++].val = buf;
1177 }
1178 print_otag(h, TAG_DIV, i, tag);
1179 return(1);
1180 }
1181
1182 buffmt("margin-left: -%dem;", INDENT - HALFINDENT);
1183
1184 tag[0].key = ATTR_CLASS;
1185 tag[0].val = "ssec-head";
1186 tag[1].key = ATTR_STYLE;
1187 tag[1].val = buf;
1188
1189 print_otag(h, TAG_DIV, 2, tag);
1190 print_otag(h, TAG_SPAN, 1, tag);
1191
1192 bufinit();
1193 for (nn = n->child; nn; nn = nn->next) {
1194 bufcat(nn->string);
1195 if (nn->next)
1196 bufcat(" ");
1197 }
1198 tag[0].key = ATTR_NAME;
1199 tag[0].val = buf;
1200 print_otag(h, TAG_A, 1, tag);
1201
1202 return(1);
1203 }
1204
1205
1206 /* ARGSUSED */
1207 static int
1208 mdoc_fl_pre(MDOC_ARGS)
1209 {
1210 struct htmlpair tag;
1211
1212 tag.key = ATTR_CLASS;
1213 tag.val = "flag";
1214
1215 print_otag(h, TAG_SPAN, 1, &tag);
1216 if (MDOC_Fl == n->tok) {
1217 print_text(h, "\\-");
1218 h->flags |= HTML_NOSPACE;
1219 }
1220 return(1);
1221 }
1222
1223
1224 /* ARGSUSED */
1225 static int
1226 mdoc_pp_pre(MDOC_ARGS)
1227 {
1228 struct htmlpair tag;
1229
1230 tag.key = ATTR_STYLE;
1231 tag.val = "clear: both; height: 1em;";
1232 print_otag(h, TAG_DIV, 1, &tag);
1233 return(0);
1234 }
1235
1236
1237 /* ARGSUSED */
1238 static int
1239 mdoc_nd_pre(MDOC_ARGS)
1240 {
1241 struct htmlpair tag;
1242
1243 if (MDOC_BODY != n->type)
1244 return(1);
1245
1246 /* XXX - this can contain block elements! */
1247 print_text(h, "\\(em");
1248 tag.key = ATTR_CLASS;
1249 tag.val = "desc-body";
1250 print_otag(h, TAG_SPAN, 1, &tag);
1251 return(1);
1252 }
1253
1254
1255 /* ARGSUSED */
1256 static int
1257 mdoc_op_pre(MDOC_ARGS)
1258 {
1259 struct htmlpair tag;
1260
1261 if (MDOC_BODY != n->type)
1262 return(1);
1263
1264 /* XXX - this can contain block elements! */
1265 print_text(h, "\\(lB");
1266 h->flags |= HTML_NOSPACE;
1267 tag.key = ATTR_CLASS;
1268 tag.val = "opt";
1269 print_otag(h, TAG_SPAN, 1, &tag);
1270 return(1);
1271 }
1272
1273
1274 /* ARGSUSED */
1275 static void
1276 mdoc_op_post(MDOC_ARGS)
1277 {
1278
1279 if (MDOC_BODY != n->type)
1280 return;
1281 h->flags |= HTML_NOSPACE;
1282 print_text(h, "\\(rB");
1283 }
1284
1285
1286 static int
1287 mdoc_nm_pre(MDOC_ARGS)
1288 {
1289 struct htmlpair tag;
1290
1291 if ( ! (HTML_NEWLINE & h->flags))
1292 if (SEC_SYNOPSIS == n->sec) {
1293 tag.key = ATTR_STYLE;
1294 tag.val = "clear: both;";
1295 print_otag(h, TAG_BR, 1, &tag);
1296 }
1297
1298 tag.key = ATTR_CLASS;
1299 tag.val = "name";
1300
1301 print_otag(h, TAG_SPAN, 1, &tag);
1302 if (NULL == n->child)
1303 print_text(h, m->name);
1304
1305 return(1);
1306 }
1307
1308
1309 /* ARGSUSED */
1310 static int
1311 mdoc_xr_pre(MDOC_ARGS)
1312 {
1313 struct htmlpair tag[2];
1314 const char *name, *sec;
1315 const struct mdoc_node *nn;
1316
1317 nn = n->child;
1318 name = nn && nn->string ? nn->string : "";
1319 nn = nn ? nn->next : NULL;
1320 sec = nn && nn->string ? nn->string : "";
1321
1322 buffmt("%s%s%s.html", name, name && sec ? "." : "", sec);
1323
1324 tag[0].key = ATTR_CLASS;
1325 tag[0].val = "link-man";
1326 tag[1].key = ATTR_HREF;
1327 tag[1].val = buf;
1328 print_otag(h, TAG_A, 2, tag);
1329
1330 nn = n->child;
1331 print_text(h, nn->string);
1332 if (NULL == (nn = nn->next))
1333 return(0);
1334
1335 h->flags |= HTML_NOSPACE;
1336 print_text(h, "(");
1337 h->flags |= HTML_NOSPACE;
1338 print_text(h, nn->string);
1339 h->flags |= HTML_NOSPACE;
1340 print_text(h, ")");
1341
1342 return(0);
1343 }
1344
1345
1346 /* ARGSUSED */
1347 static int
1348 mdoc_ns_pre(MDOC_ARGS)
1349 {
1350
1351 h->flags |= HTML_NOSPACE;
1352 return(1);
1353 }
1354
1355
1356 /* ARGSUSED */
1357 static int
1358 mdoc_ar_pre(MDOC_ARGS)
1359 {
1360 struct htmlpair tag;
1361
1362 tag.key = ATTR_CLASS;
1363 tag.val = "arg";
1364
1365 print_otag(h, TAG_SPAN, 1, &tag);
1366 return(1);
1367 }
1368
1369
1370 /* ARGSUSED */
1371 static int
1372 mdoc_xx_pre(MDOC_ARGS)
1373 {
1374 const char *pp;
1375 struct htmlpair tag;
1376
1377 switch (n->tok) {
1378 case (MDOC_Bsx):
1379 pp = "BSDI BSD/OS";
1380 break;
1381 case (MDOC_Dx):
1382 pp = "DragonFlyBSD";
1383 break;
1384 case (MDOC_Fx):
1385 pp = "FreeBSD";
1386 break;
1387 case (MDOC_Nx):
1388 pp = "NetBSD";
1389 break;
1390 case (MDOC_Ox):
1391 pp = "OpenBSD";
1392 break;
1393 case (MDOC_Ux):
1394 pp = "UNIX";
1395 break;
1396 default:
1397 return(1);
1398 }
1399
1400 tag.key = ATTR_CLASS;
1401 tag.val = "unix";
1402
1403 print_otag(h, TAG_SPAN, 1, &tag);
1404 print_text(h, pp);
1405 return(1);
1406 }
1407
1408
1409 /* ARGSUSED */
1410 static int
1411 mdoc_tbl_block_pre(MDOC_ARGS, int t, int w, int o, int c)
1412 {
1413 struct htmlpair tag;
1414
1415 switch (t) {
1416 case (MDOC_Column):
1417 /* FALLTHROUGH */
1418 case (MDOC_Item):
1419 /* FALLTHROUGH */
1420 case (MDOC_Ohang):
1421 buffmt("margin-left: %dem; clear: both;", o);
1422 break;
1423 default:
1424 buffmt("margin-left: %dem; clear: both;", w + o);
1425 break;
1426 }
1427
1428 if ( ! c && MDOC_Column != t) {
1429 if (n->prev && n->prev->body->child)
1430 bufcat("padding-top: 1em;");
1431 else if (NULL == n->prev)
1432 bufcat("padding-top: 1em;");
1433 }
1434
1435 tag.key = ATTR_STYLE;
1436 tag.val = buf;
1437 print_otag(h, TAG_DIV, 1, &tag);
1438 return(1);
1439 }
1440
1441
1442 /* ARGSUSED */
1443 static int
1444 mdoc_tbl_body_pre(MDOC_ARGS, int t, int w)
1445 {
1446
1447 print_otag(h, TAG_DIV, 0, NULL);
1448 return(1);
1449 }
1450
1451
1452 /* ARGSUSED */
1453 static int
1454 mdoc_tbl_head_pre(MDOC_ARGS, int t, int w)
1455 {
1456 struct htmlpair tag;
1457 struct ord *ord;
1458 char nbuf[BUFSIZ];
1459
1460 switch (t) {
1461 case (MDOC_Item):
1462 /* FALLTHROUGH */
1463 case (MDOC_Ohang):
1464 print_otag(h, TAG_DIV, 0, NULL);
1465 break;
1466 case (MDOC_Column):
1467 buffmt("min-width: %dem;", w);
1468 bufcat("clear: none;");
1469 if (n->next && MDOC_HEAD == n->next->type)
1470 bufcat("float: left;");
1471 tag.key = ATTR_STYLE;
1472 tag.val = buf;
1473 print_otag(h, TAG_DIV, 1, &tag);
1474 break;
1475 default:
1476 buffmt("margin-left: -%dem;", w);
1477 bufcat("clear: left;");
1478 bufcat("float: left;");
1479 bufcat("padding-right: 1em;");
1480 tag.key = ATTR_STYLE;
1481 tag.val = buf;
1482 print_otag(h, TAG_DIV, 1, &tag);
1483 break;
1484 }
1485
1486 switch (t) {
1487 case (MDOC_Diag):
1488 tag.key = ATTR_CLASS;
1489 tag.val = "diag";
1490 print_otag(h, TAG_SPAN, 1, &tag);
1491 break;
1492 case (MDOC_Enum):
1493 ord = SLIST_FIRST(&h->ords);
1494 assert(ord);
1495 nbuf[BUFSIZ - 1] = 0;
1496 (void)snprintf(nbuf, BUFSIZ - 1, "%d.", ord->pos++);
1497 print_text(h, nbuf);
1498 return(0);
1499 case (MDOC_Dash):
1500 print_text(h, "\\(en");
1501 return(0);
1502 case (MDOC_Hyphen):
1503 print_text(h, "\\-");
1504 return(0);
1505 case (MDOC_Bullet):
1506 print_text(h, "\\(bu");
1507 return(0);
1508 default:
1509 break;
1510 }
1511
1512 return(1);
1513 }
1514
1515
1516 static int
1517 mdoc_tbl_pre(MDOC_ARGS, int type)
1518 {
1519 int i, w, o, c, wp;
1520 const struct mdoc_node *bl, *nn;
1521
1522 bl = n->parent->parent;
1523 if (MDOC_BLOCK != n->type)
1524 bl = bl->parent;
1525
1526 /* FIXME: fmt_vspace() equivalent. */
1527
1528 assert(bl->args);
1529
1530 w = o = c = 0;
1531 wp = -1;
1532
1533 for (i = 0; i < (int)bl->args->argc; i++)
1534 if (MDOC_Width == bl->args->argv[i].arg) {
1535 assert(bl->args->argv[i].sz);
1536 wp = i;
1537 w = a2width(bl->args->argv[i].value[0]);
1538 } else if (MDOC_Offset == bl->args->argv[i].arg) {
1539 assert(bl->args->argv[i].sz);
1540 o = a2offs(bl->args->argv[i].value[0]);
1541 } else if (MDOC_Compact == bl->args->argv[i].arg)
1542 c = 1;
1543
1544 if (MDOC_HEAD == n->type && MDOC_Column == type) {
1545 nn = n->parent->child;
1546 assert(nn && MDOC_HEAD == nn->type);
1547 for (i = 0; nn && nn != n; nn = nn->next, i++)
1548 /* Counter... */ ;
1549 assert(nn);
1550 if (wp >= 0 && i < (int)bl->args[wp].argv->sz)
1551 w = a2width(bl->args->argv[wp].value[i]);
1552 }
1553
1554 switch (type) {
1555 case (MDOC_Enum):
1556 /* FALLTHROUGH */
1557 case (MDOC_Dash):
1558 /* FALLTHROUGH */
1559 case (MDOC_Hyphen):
1560 /* FALLTHROUGH */
1561 case (MDOC_Bullet):
1562 if (w < 4)
1563 w = 4;
1564 break;
1565 case (MDOC_Inset):
1566 /* FALLTHROUGH */
1567 case (MDOC_Diag):
1568 w = 1;
1569 break;
1570 default:
1571 if (0 == w)
1572 w = 10;
1573 break;
1574 }
1575
1576 switch (n->type) {
1577 case (MDOC_BLOCK):
1578 break;
1579 case (MDOC_HEAD):
1580 return(mdoc_tbl_head_pre(m, n, h, type, w));
1581 case (MDOC_BODY):
1582 return(mdoc_tbl_body_pre(m, n, h, type, w));
1583 default:
1584 abort();
1585 /* NOTREACHED */
1586 }
1587
1588 return(mdoc_tbl_block_pre(m, n, h, type, w, o, c));
1589 }
1590
1591
1592 /* ARGSUSED */
1593 static int
1594 mdoc_bl_pre(MDOC_ARGS)
1595 {
1596 struct ord *ord;
1597
1598 if (MDOC_BLOCK != n->type)
1599 return(1);
1600 if (MDOC_Enum != a2list(n))
1601 return(1);
1602
1603 ord = malloc(sizeof(struct ord));
1604 if (NULL == ord)
1605 err(EXIT_FAILURE, "malloc");
1606 ord->cookie = n;
1607 ord->pos = 1;
1608 SLIST_INSERT_HEAD(&h->ords, ord, entry);
1609
1610 return(1);
1611 }
1612
1613
1614 /* ARGSUSED */
1615 static void
1616 mdoc_bl_post(MDOC_ARGS)
1617 {
1618 struct ord *ord;
1619
1620 if (MDOC_BLOCK != n->type)
1621 return;
1622 if (MDOC_Enum != a2list(n))
1623 return;
1624
1625 ord = SLIST_FIRST(&h->ords);
1626 assert(ord);
1627 SLIST_REMOVE_HEAD(&h->ords, entry);
1628 free(ord);
1629 }
1630
1631
1632 static int
1633 mdoc_it_pre(MDOC_ARGS)
1634 {
1635 int type;
1636
1637 if (MDOC_BLOCK == n->type)
1638 type = a2list(n->parent->parent);
1639 else
1640 type = a2list(n->parent->parent->parent);
1641
1642 return(mdoc_tbl_pre(m, n, h, type));
1643 }
1644
1645
1646 /* ARGSUSED */
1647 static int
1648 mdoc_ex_pre(MDOC_ARGS)
1649 {
1650 const struct mdoc_node *nn;
1651 struct tag *t;
1652 struct htmlpair tag;
1653
1654 print_text(h, "The");
1655
1656 tag.key = ATTR_CLASS;
1657 tag.val = "utility";
1658
1659 for (nn = n->child; nn; nn = nn->next) {
1660 t = print_otag(h, TAG_SPAN, 1, &tag);
1661 print_text(h, nn->string);
1662 print_tagq(h, t);
1663
1664 h->flags |= HTML_NOSPACE;
1665
1666 if (nn->next && NULL == nn->next->next)
1667 print_text(h, ", and");
1668 else if (nn->next)
1669 print_text(h, ",");
1670 else
1671 h->flags &= ~HTML_NOSPACE;
1672 }
1673
1674 if (n->child->next)
1675 print_text(h, "utilities exit");
1676 else
1677 print_text(h, "utility exits");
1678
1679 print_text(h, "0 on success, and >0 if an error occurs.");
1680 return(0);
1681 }
1682
1683
1684 /* ARGSUSED */
1685 static int
1686 mdoc_dq_pre(MDOC_ARGS)
1687 {
1688
1689 if (MDOC_BODY != n->type)
1690 return(1);
1691 print_text(h, "\\(lq");
1692 h->flags |= HTML_NOSPACE;
1693 return(1);
1694 }
1695
1696
1697 /* ARGSUSED */
1698 static void
1699 mdoc_dq_post(MDOC_ARGS)
1700 {
1701
1702 if (MDOC_BODY != n->type)
1703 return;
1704 h->flags |= HTML_NOSPACE;
1705 print_text(h, "\\(rq");
1706 }
1707
1708
1709 /* ARGSUSED */
1710 static int
1711 mdoc_pq_pre(MDOC_ARGS)
1712 {
1713
1714 if (MDOC_BODY != n->type)
1715 return(1);
1716 print_text(h, "\\&(");
1717 h->flags |= HTML_NOSPACE;
1718 return(1);
1719 }
1720
1721
1722 /* ARGSUSED */
1723 static void
1724 mdoc_pq_post(MDOC_ARGS)
1725 {
1726
1727 if (MDOC_BODY != n->type)
1728 return;
1729 print_text(h, ")");
1730 }
1731
1732
1733 /* ARGSUSED */
1734 static int
1735 mdoc_sq_pre(MDOC_ARGS)
1736 {
1737
1738 if (MDOC_BODY != n->type)
1739 return(1);
1740 print_text(h, "\\(oq");
1741 h->flags |= HTML_NOSPACE;
1742 return(1);
1743 }
1744
1745
1746 /* ARGSUSED */
1747 static void
1748 mdoc_sq_post(MDOC_ARGS)
1749 {
1750
1751 if (MDOC_BODY != n->type)
1752 return;
1753 h->flags |= HTML_NOSPACE;
1754 print_text(h, "\\(aq");
1755 }
1756
1757
1758 /* ARGSUSED */
1759 static int
1760 mdoc_em_pre(MDOC_ARGS)
1761 {
1762 struct htmlpair tag;
1763
1764 tag.key = ATTR_CLASS;
1765 tag.val = "emph";
1766
1767 print_otag(h, TAG_SPAN, 1, &tag);
1768 return(1);
1769 }
1770
1771
1772 /* ARGSUSED */
1773 static int
1774 mdoc_d1_pre(MDOC_ARGS)
1775 {
1776 struct htmlpair tag[2];
1777
1778 if (MDOC_BLOCK != n->type)
1779 return(1);
1780
1781 buffmt("margin-left: %dem;", INDENT);
1782
1783 tag[0].key = ATTR_CLASS;
1784 tag[0].val = "lit-block";
1785 tag[1].key = ATTR_STYLE;
1786 tag[1].val = buf;
1787
1788 print_otag(h, TAG_DIV, 2, tag);
1789 return(1);
1790 }
1791
1792
1793 /* ARGSUSED */
1794 static int
1795 mdoc_sx_pre(MDOC_ARGS)
1796 {
1797 struct htmlpair tag[2];
1798 const struct mdoc_node *nn;
1799
1800 bufcat("#");
1801 for (nn = n->child; nn; nn = nn->next) {
1802 bufcat(nn->string);
1803 if (nn->next)
1804 bufcat(" ");
1805 }
1806
1807 tag[0].key = ATTR_HREF;
1808 tag[0].val = buf;
1809 tag[1].key = ATTR_CLASS;
1810 tag[1].val = "link-sec";
1811
1812 print_otag(h, TAG_A, 2, tag);
1813 return(1);
1814 }
1815
1816
1817 /* ARGSUSED */
1818 static int
1819 mdoc_aq_pre(MDOC_ARGS)
1820 {
1821
1822 if (MDOC_BODY != n->type)
1823 return(1);
1824 print_text(h, "\\(la");
1825 h->flags |= HTML_NOSPACE;
1826 return(1);
1827 }
1828
1829
1830 /* ARGSUSED */
1831 static void
1832 mdoc_aq_post(MDOC_ARGS)
1833 {
1834
1835 if (MDOC_BODY != n->type)
1836 return;
1837 h->flags |= HTML_NOSPACE;
1838 print_text(h, "\\(ra");
1839 }
1840
1841
1842 /* ARGSUSED */
1843 static int
1844 mdoc_bd_pre(MDOC_ARGS)
1845 {
1846 struct htmlpair tag[2];
1847 int t, c, o, i;
1848 const struct mdoc_node *bl;
1849
1850 /* FIXME: fmt_vspace() shit. */
1851
1852 if (MDOC_BLOCK == n->type)
1853 bl = n;
1854 else if (MDOC_HEAD == n->type)
1855 return(0);
1856 else
1857 bl = n->parent;
1858
1859 t = o = c = 0;
1860
1861 for (i = 0; i < (int)bl->args->argc; i++)
1862 switch (bl->args->argv[i].arg) {
1863 case (MDOC_Offset):
1864 assert(bl->args->argv[i].sz);
1865 o = a2offs (bl->args->argv[i].value[0]);
1866 break;
1867 case (MDOC_Compact):
1868 c = 1;
1869 break;
1870 case (MDOC_Ragged):
1871 /* FALLTHROUGH */
1872 case (MDOC_Filled):
1873 /* FALLTHROUGH */
1874 case (MDOC_Unfilled):
1875 /* FALLTHROUGH */
1876 case (MDOC_Literal):
1877 t = bl->args->argv[i].arg;
1878 break;
1879 }
1880
1881 if (MDOC_BLOCK == n->type) {
1882 if (o)
1883 buffmt("margin-left: %dem;", o);
1884 bufcat("margin-top: 1em;");
1885 tag[0].key = ATTR_STYLE;
1886 tag[0].val = buf;
1887 print_otag(h, TAG_DIV, 1, tag);
1888 return(1);
1889 }
1890
1891 switch (t) {
1892 case (MDOC_Unfilled):
1893 case (MDOC_Literal):
1894 break;
1895 default:
1896 return(1);
1897 }
1898
1899 bufcat("white-space: pre;");
1900 tag[0].key = ATTR_STYLE;
1901 tag[0].val = buf;
1902 tag[1].key = ATTR_CLASS;
1903 tag[1].val = "lit-block";
1904
1905 print_otag(h, TAG_DIV, 2, tag);
1906
1907 for (n = n->child; n; n = n->next) {
1908 h->flags |= HTML_NOSPACE;
1909 print_mdoc_node(m, n, h);
1910 if (n->next)
1911 print_text(h, "\n");
1912 }
1913
1914 return(0);
1915 }
1916
1917
1918 /* ARGSUSED */
1919 static int
1920 mdoc_pa_pre(MDOC_ARGS)
1921 {
1922 struct htmlpair tag;
1923
1924 tag.key = ATTR_CLASS;
1925 tag.val = "file";
1926
1927 print_otag(h, TAG_SPAN, 1, &tag);
1928 return(1);
1929 }
1930
1931
1932 /* ARGSUSED */
1933 static int
1934 mdoc_qq_pre(MDOC_ARGS)
1935 {
1936
1937 if (MDOC_BODY != n->type)
1938 return(1);
1939 print_text(h, "\\*q");
1940 h->flags |= HTML_NOSPACE;
1941 return(1);
1942 }
1943
1944
1945 /* ARGSUSED */
1946 static void
1947 mdoc_qq_post(MDOC_ARGS)
1948 {
1949
1950 if (MDOC_BODY != n->type)
1951 return;
1952 h->flags |= HTML_NOSPACE;
1953 print_text(h, "\\*q");
1954 }
1955
1956
1957 /* ARGSUSED */
1958 static int
1959 mdoc_ad_pre(MDOC_ARGS)
1960 {
1961 struct htmlpair tag;
1962
1963 tag.key = ATTR_CLASS;
1964 tag.val = "addr";
1965 print_otag(h, TAG_SPAN, 1, &tag);
1966 return(1);
1967 }
1968
1969
1970 /* ARGSUSED */
1971 static int
1972 mdoc_an_pre(MDOC_ARGS)
1973 {
1974 struct htmlpair tag;
1975
1976 tag.key = ATTR_CLASS;
1977 tag.val = "author";
1978 print_otag(h, TAG_SPAN, 1, &tag);
1979 return(1);
1980 }
1981
1982
1983 /* ARGSUSED */
1984 static int
1985 mdoc_cd_pre(MDOC_ARGS)
1986 {
1987 struct htmlpair tag;
1988
1989 tag.key = ATTR_CLASS;
1990 tag.val = "config";
1991 print_otag(h, TAG_SPAN, 1, &tag);
1992 return(1);
1993 }
1994
1995
1996 /* ARGSUSED */
1997 static int
1998 mdoc_dv_pre(MDOC_ARGS)
1999 {
2000 struct htmlpair tag;
2001
2002 tag.key = ATTR_CLASS;
2003 tag.val = "define";
2004 print_otag(h, TAG_SPAN, 1, &tag);
2005 return(1);
2006 }
2007
2008
2009 /* ARGSUSED */
2010 static int
2011 mdoc_ev_pre(MDOC_ARGS)
2012 {
2013 struct htmlpair tag;
2014
2015 tag.key = ATTR_CLASS;
2016 tag.val = "env";
2017 print_otag(h, TAG_SPAN, 1, &tag);
2018 return(1);
2019 }
2020
2021
2022 /* ARGSUSED */
2023 static int
2024 mdoc_er_pre(MDOC_ARGS)
2025 {
2026 struct htmlpair tag;
2027
2028 tag.key = ATTR_CLASS;
2029 tag.val = "errno";
2030 print_otag(h, TAG_SPAN, 1, &tag);
2031 return(1);
2032 }
2033
2034
2035 /* ARGSUSED */
2036 static int
2037 mdoc_fa_pre(MDOC_ARGS)
2038 {
2039 const struct mdoc_node *nn;
2040 struct htmlpair tag;
2041 struct tag *t;
2042
2043 tag.key = ATTR_CLASS;
2044 tag.val = "farg";
2045
2046 if (n->parent->tok != MDOC_Fo) {
2047 print_otag(h, TAG_SPAN, 1, &tag);
2048 return(1);
2049 }
2050
2051 for (nn = n->child; nn; nn = nn->next) {
2052 t = print_otag(h, TAG_SPAN, 1, &tag);
2053 print_text(h, nn->string);
2054 print_tagq(h, t);
2055 if (nn->next)
2056 print_text(h, ",");
2057 }
2058
2059 if (n->child && n->next && n->next->tok == MDOC_Fa)
2060 print_text(h, ",");
2061
2062 return(0);
2063 }
2064
2065
2066 /* ARGSUSED */
2067 static int
2068 mdoc_fd_pre(MDOC_ARGS)
2069 {
2070 struct htmlpair tag;
2071
2072 if (SEC_SYNOPSIS == n->sec) {
2073 if (n->next && MDOC_Fd != n->next->tok) {
2074 tag.key = ATTR_STYLE;
2075 tag.val = "margin-bottom: 1em;";
2076 print_otag(h, TAG_DIV, 1, &tag);
2077 } else
2078 print_otag(h, TAG_DIV, 0, NULL);
2079 }
2080
2081 tag.key = ATTR_CLASS;
2082 tag.val = "macro";
2083 print_otag(h, TAG_SPAN, 1, &tag);
2084 return(1);
2085 }
2086
2087
2088 /* ARGSUSED */
2089 static int
2090 mdoc_vt_pre(MDOC_ARGS)
2091 {
2092 struct htmlpair tag;
2093
2094 if (SEC_SYNOPSIS == n->sec) {
2095 if (n->next && MDOC_Vt != n->next->tok) {
2096 tag.key = ATTR_STYLE;
2097 tag.val = "margin-bottom: 1em;";
2098 print_otag(h, TAG_DIV, 1, &tag);
2099 } else
2100 print_otag(h, TAG_DIV, 0, NULL);
2101 }
2102
2103 tag.key = ATTR_CLASS;
2104 tag.val = "type";
2105 print_otag(h, TAG_SPAN, 1, &tag);
2106 return(1);
2107 }
2108
2109 /* ARGSUSED */
2110 static int
2111 mdoc_ft_pre(MDOC_ARGS)
2112 {
2113 struct htmlpair tag;
2114
2115 if (SEC_SYNOPSIS == n->sec) {
2116 if (n->prev && MDOC_Fo == n->prev->tok) {
2117 tag.key = ATTR_STYLE;
2118 tag.val = "magin-bottom: 1em;";
2119 print_otag(h, TAG_DIV, 1, &tag);
2120 } else
2121 print_otag(h, TAG_DIV, 0, NULL);
2122 }
2123
2124 tag.key = ATTR_CLASS;
2125 tag.val = "type";
2126 print_otag(h, TAG_SPAN, 1, &tag);
2127 return(1);
2128 }
2129
2130
2131 /* ARGSUSED */
2132 static int
2133 mdoc_fn_pre(MDOC_ARGS)
2134 {
2135 struct tag *t;
2136 struct htmlpair tag;
2137 const struct mdoc_node *nn;
2138
2139 if (SEC_SYNOPSIS == n->sec) {
2140 if (n->next) {
2141 tag.key = ATTR_STYLE;
2142 tag.val = "margin-bottom: 1em";
2143 print_otag(h, TAG_DIV, 1, &tag);
2144 } else
2145 print_otag(h, TAG_DIV, 0, NULL);
2146 }
2147
2148 tag.key = ATTR_CLASS;
2149 tag.val = "type";
2150
2151 t = print_otag(h, TAG_SPAN, 1, &tag);
2152 print_text(h, n->child->string);
2153 print_tagq(h, t);
2154
2155 h->flags |= HTML_NOSPACE;
2156 print_text(h, "(");
2157
2158 for (nn = n->child->next; nn; nn = nn->next) {
2159 tag.key = ATTR_CLASS;
2160 tag.val = "farg";
2161 t = print_otag(h, TAG_SPAN, 1, &tag);
2162 print_text(h, nn->string);
2163 print_tagq(h, t);
2164 if (nn->next)
2165 print_text(h, ",");
2166 }
2167
2168 print_text(h, ")");
2169
2170 if (SEC_SYNOPSIS == n->sec)
2171 print_text(h, ";");
2172
2173 return(0);
2174 }