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