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