]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_html.c
Removed need for superfluous `os' value in overstep calculation (thanks Ingo Schwarze).
[mandoc.git] / mdoc_html.c
1 /* $Id: mdoc_html.c,v 1.37 2009/10/24 05:52:13 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/queue.h>
20
21 #include <assert.h>
22 #include <ctype.h>
23 #include <err.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "out.h"
30 #include "html.h"
31 #include "mdoc.h"
32 #include "main.h"
33
34 #define INDENT 5
35 #define HALFINDENT 3
36
37 #define MDOC_ARGS const struct mdoc_meta *m, \
38 const struct mdoc_node *n, \
39 struct html *h
40
41 struct htmlmdoc {
42 int (*pre)(MDOC_ARGS);
43 void (*post)(MDOC_ARGS);
44 };
45
46 static void print_mdoc(MDOC_ARGS);
47 static void print_mdoc_head(MDOC_ARGS);
48 static void print_mdoc_node(MDOC_ARGS);
49 static void print_mdoc_nodelist(MDOC_ARGS);
50
51 static void a2width(const char *, struct roffsu *);
52 static void a2offs(const char *, struct roffsu *);
53
54 static int a2list(const struct mdoc_node *);
55
56 static void mdoc_root_post(MDOC_ARGS);
57 static int mdoc_root_pre(MDOC_ARGS);
58
59 static void mdoc__x_post(MDOC_ARGS);
60 static int mdoc__x_pre(MDOC_ARGS);
61 static int mdoc_ad_pre(MDOC_ARGS);
62 static int mdoc_an_pre(MDOC_ARGS);
63 static int mdoc_ap_pre(MDOC_ARGS);
64 static void mdoc_aq_post(MDOC_ARGS);
65 static int mdoc_aq_pre(MDOC_ARGS);
66 static int mdoc_ar_pre(MDOC_ARGS);
67 static int mdoc_bd_pre(MDOC_ARGS);
68 static int mdoc_bf_pre(MDOC_ARGS);
69 static void mdoc_bl_post(MDOC_ARGS);
70 static int mdoc_bl_pre(MDOC_ARGS);
71 static void mdoc_bq_post(MDOC_ARGS);
72 static int mdoc_bq_pre(MDOC_ARGS);
73 static void mdoc_brq_post(MDOC_ARGS);
74 static int mdoc_brq_pre(MDOC_ARGS);
75 static int mdoc_bt_pre(MDOC_ARGS);
76 static int mdoc_bx_pre(MDOC_ARGS);
77 static int mdoc_cd_pre(MDOC_ARGS);
78 static int mdoc_d1_pre(MDOC_ARGS);
79 static void mdoc_dq_post(MDOC_ARGS);
80 static int mdoc_dq_pre(MDOC_ARGS);
81 static int mdoc_dv_pre(MDOC_ARGS);
82 static int mdoc_fa_pre(MDOC_ARGS);
83 static int mdoc_fd_pre(MDOC_ARGS);
84 static int mdoc_fl_pre(MDOC_ARGS);
85 static int mdoc_fn_pre(MDOC_ARGS);
86 static int mdoc_ft_pre(MDOC_ARGS);
87 static int mdoc_em_pre(MDOC_ARGS);
88 static int mdoc_er_pre(MDOC_ARGS);
89 static int mdoc_ev_pre(MDOC_ARGS);
90 static int mdoc_ex_pre(MDOC_ARGS);
91 static void mdoc_fo_post(MDOC_ARGS);
92 static int mdoc_fo_pre(MDOC_ARGS);
93 static int mdoc_ic_pre(MDOC_ARGS);
94 static int mdoc_in_pre(MDOC_ARGS);
95 static int mdoc_it_block_pre(MDOC_ARGS, int, int,
96 struct roffsu *, struct roffsu *);
97 static int mdoc_it_head_pre(MDOC_ARGS, int,
98 struct roffsu *);
99 static int mdoc_it_body_pre(MDOC_ARGS, int);
100 static int mdoc_it_pre(MDOC_ARGS);
101 static int mdoc_lb_pre(MDOC_ARGS);
102 static int mdoc_li_pre(MDOC_ARGS);
103 static int mdoc_lk_pre(MDOC_ARGS);
104 static int mdoc_mt_pre(MDOC_ARGS);
105 static int mdoc_ms_pre(MDOC_ARGS);
106 static int mdoc_nd_pre(MDOC_ARGS);
107 static int mdoc_nm_pre(MDOC_ARGS);
108 static int mdoc_ns_pre(MDOC_ARGS);
109 static void mdoc_op_post(MDOC_ARGS);
110 static int mdoc_op_pre(MDOC_ARGS);
111 static int mdoc_pa_pre(MDOC_ARGS);
112 static void mdoc_pf_post(MDOC_ARGS);
113 static int mdoc_pf_pre(MDOC_ARGS);
114 static void mdoc_pq_post(MDOC_ARGS);
115 static int mdoc_pq_pre(MDOC_ARGS);
116 static int mdoc_rs_pre(MDOC_ARGS);
117 static int mdoc_rv_pre(MDOC_ARGS);
118 static int mdoc_sh_pre(MDOC_ARGS);
119 static int mdoc_sp_pre(MDOC_ARGS);
120 static void mdoc_sq_post(MDOC_ARGS);
121 static int mdoc_sq_pre(MDOC_ARGS);
122 static int mdoc_ss_pre(MDOC_ARGS);
123 static int mdoc_sx_pre(MDOC_ARGS);
124 static int mdoc_sy_pre(MDOC_ARGS);
125 static int mdoc_ud_pre(MDOC_ARGS);
126 static int mdoc_va_pre(MDOC_ARGS);
127 static int mdoc_vt_pre(MDOC_ARGS);
128 static int mdoc_xr_pre(MDOC_ARGS);
129 static int mdoc_xx_pre(MDOC_ARGS);
130
131 #ifdef __linux__
132 extern size_t strlcpy(char *, const char *, size_t);
133 extern size_t strlcat(char *, const char *, size_t);
134 #endif
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 = SLIST_FIRST(&h->tags);
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 break;
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 case (MDOC_TEXT):
447 break;
448 default:
449 if (mdocs[n->tok].post)
450 (*mdocs[n->tok].post)(m, n, h);
451 break;
452 }
453 }
454
455
456 /* ARGSUSED */
457 static void
458 mdoc_root_post(MDOC_ARGS)
459 {
460 struct htmlpair tag[2];
461 struct tag *t, *tt;
462 char b[DATESIZ];
463
464 time2a(m->date, b, DATESIZ);
465
466 /*
467 * XXX: this should use divs, but in Firefox, divs with nested
468 * divs for some reason puke when trying to put a border line
469 * below. So I use tables, instead.
470 */
471
472 PAIR_CLASS_INIT(&tag[0], "footer");
473 bufcat_style(h, "width", "100%");
474 PAIR_STYLE_INIT(&tag[1], h);
475 t = print_otag(h, TAG_TABLE, 2, 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[2];
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 t = print_otag(h, TAG_TABLE, 2, tag);
520 tt = print_otag(h, TAG_TR, 0, NULL);
521
522 bufinit(h);
523 bufcat_style(h, "width", "10%");
524 PAIR_STYLE_INIT(&tag[0], h);
525 print_otag(h, TAG_TD, 1, tag);
526 print_text(h, title);
527 print_stagq(h, tt);
528
529 bufinit(h);
530 bufcat_style(h, "text-align", "center");
531 bufcat_style(h, "white-space", "nowrap");
532 bufcat_style(h, "width", "80%");
533 PAIR_STYLE_INIT(&tag[0], h);
534 print_otag(h, TAG_TD, 1, tag);
535 print_text(h, b);
536 print_stagq(h, tt);
537
538 bufinit(h);
539 bufcat_style(h, "text-align", "right");
540 bufcat_style(h, "width", "10%");
541 PAIR_STYLE_INIT(&tag[0], h);
542 print_otag(h, TAG_TD, 1, tag);
543 print_text(h, title);
544 print_tagq(h, t);
545 return(1);
546 }
547
548
549 /* ARGSUSED */
550 static int
551 mdoc_sh_pre(MDOC_ARGS)
552 {
553 struct htmlpair tag[2];
554 const struct mdoc_node *nn;
555 char lbuf[BUFSIZ];
556 struct roffsu su;
557
558 if (MDOC_BODY == n->type) {
559 SCALE_HS_INIT(&su, INDENT);
560 bufcat_su(h, "margin-left", &su);
561 PAIR_CLASS_INIT(&tag[0], "sec-body");
562 PAIR_STYLE_INIT(&tag[1], h);
563 print_otag(h, TAG_DIV, 2, tag);
564 return(1);
565 } else if (MDOC_BLOCK == n->type) {
566 PAIR_CLASS_INIT(&tag[0], "sec-block");
567 if (n->prev && NULL == n->prev->body->child) {
568 print_otag(h, TAG_DIV, 1, tag);
569 return(1);
570 }
571
572 SCALE_VS_INIT(&su, 1);
573 bufcat_su(h, "margin-top", &su);
574 if (NULL == n->next)
575 bufcat_su(h, "margin-bottom", &su);
576
577 PAIR_STYLE_INIT(&tag[1], h);
578 print_otag(h, TAG_DIV, 2, tag);
579 return(1);
580 }
581
582 lbuf[0] = 0;
583 for (nn = n->child; nn; nn = nn->next) {
584 (void)strlcat(lbuf, nn->string, BUFSIZ);
585 if (nn->next)
586 (void)strlcat(lbuf, "_", BUFSIZ);
587 }
588
589 /*
590 * TODO: make sure there are no duplicates, as HTML does not
591 * allow for multiple `id' tags of the same name.
592 */
593
594 PAIR_CLASS_INIT(&tag[0], "sec-head");
595 tag[1].key = ATTR_ID;
596 tag[1].val = lbuf;
597 print_otag(h, TAG_DIV, 2, tag);
598 return(1);
599 }
600
601
602 /* ARGSUSED */
603 static int
604 mdoc_ss_pre(MDOC_ARGS)
605 {
606 struct htmlpair tag[3];
607 const struct mdoc_node *nn;
608 char lbuf[BUFSIZ];
609 struct roffsu su;
610
611 SCALE_VS_INIT(&su, 1);
612
613 if (MDOC_BODY == n->type) {
614 PAIR_CLASS_INIT(&tag[0], "ssec-body");
615 if (n->parent->next && n->child) {
616 bufcat_su(h, "margin-bottom", &su);
617 PAIR_STYLE_INIT(&tag[1], h);
618 print_otag(h, TAG_DIV, 2, tag);
619 } else
620 print_otag(h, TAG_DIV, 1, tag);
621 return(1);
622 } else if (MDOC_BLOCK == n->type) {
623 PAIR_CLASS_INIT(&tag[0], "ssec-block");
624 if (n->prev) {
625 bufcat_su(h, "margin-top", &su);
626 PAIR_STYLE_INIT(&tag[1], h);
627 print_otag(h, TAG_DIV, 2, tag);
628 } else
629 print_otag(h, TAG_DIV, 1, tag);
630 return(1);
631 }
632
633 /* TODO: see note in mdoc_sh_pre() about duplicates. */
634
635 lbuf[0] = 0;
636 for (nn = n->child; nn; nn = nn->next) {
637 (void)strlcat(lbuf, nn->string, BUFSIZ);
638 if (nn->next)
639 (void)strlcat(lbuf, "_", BUFSIZ);
640 }
641
642 SCALE_HS_INIT(&su, INDENT - HALFINDENT);
643 su.scale = -su.scale;
644 bufcat_su(h, "margin-left", &su);
645
646 PAIR_CLASS_INIT(&tag[0], "ssec-head");
647 PAIR_STYLE_INIT(&tag[1], h);
648 tag[2].key = ATTR_ID;
649 tag[2].val = lbuf;
650 print_otag(h, TAG_DIV, 3, tag);
651 return(1);
652 }
653
654
655 /* ARGSUSED */
656 static int
657 mdoc_fl_pre(MDOC_ARGS)
658 {
659 struct htmlpair tag;
660
661 PAIR_CLASS_INIT(&tag, "flag");
662 print_otag(h, TAG_SPAN, 1, &tag);
663 if (MDOC_Fl == n->tok) {
664 print_text(h, "\\-");
665 h->flags |= HTML_NOSPACE;
666 }
667 return(1);
668 }
669
670
671 /* ARGSUSED */
672 static int
673 mdoc_nd_pre(MDOC_ARGS)
674 {
675 struct htmlpair tag;
676
677 if (MDOC_BODY != n->type)
678 return(1);
679
680 /* XXX: this tag in theory can contain block elements. */
681
682 print_text(h, "\\(em");
683 PAIR_CLASS_INIT(&tag, "desc-body");
684 print_otag(h, TAG_SPAN, 1, &tag);
685 return(1);
686 }
687
688
689 /* ARGSUSED */
690 static int
691 mdoc_op_pre(MDOC_ARGS)
692 {
693 struct htmlpair tag;
694
695 if (MDOC_BODY != n->type)
696 return(1);
697
698 /* XXX: this tag in theory can contain block elements. */
699
700 print_text(h, "\\(lB");
701 h->flags |= HTML_NOSPACE;
702 PAIR_CLASS_INIT(&tag, "opt");
703 print_otag(h, TAG_SPAN, 1, &tag);
704 return(1);
705 }
706
707
708 /* ARGSUSED */
709 static void
710 mdoc_op_post(MDOC_ARGS)
711 {
712
713 if (MDOC_BODY != n->type)
714 return;
715 h->flags |= HTML_NOSPACE;
716 print_text(h, "\\(rB");
717 }
718
719
720 static int
721 mdoc_nm_pre(MDOC_ARGS)
722 {
723 struct htmlpair tag;
724
725 if ( ! (HTML_NEWLINE & h->flags))
726 if (SEC_SYNOPSIS == n->sec) {
727 bufcat_style(h, "clear", "both");
728 PAIR_STYLE_INIT(&tag, h);
729 print_otag(h, TAG_BR, 1, &tag);
730 }
731
732 PAIR_CLASS_INIT(&tag, "name");
733 print_otag(h, TAG_SPAN, 1, &tag);
734 if (NULL == n->child)
735 print_text(h, m->name);
736
737 return(1);
738 }
739
740
741 /* ARGSUSED */
742 static int
743 mdoc_xr_pre(MDOC_ARGS)
744 {
745 struct htmlpair tag[2];
746 const struct mdoc_node *nn;
747
748 PAIR_CLASS_INIT(&tag[0], "link-man");
749
750 if (h->base_man) {
751 buffmt_man(h, n->child->string,
752 n->child->next ?
753 n->child->next->string : NULL);
754 tag[1].key = ATTR_HREF;
755 tag[1].val = h->buf;
756 print_otag(h, TAG_A, 2, tag);
757 } else
758 print_otag(h, TAG_A, 1, tag);
759
760 nn = n->child;
761 print_text(h, nn->string);
762
763 if (NULL == (nn = nn->next))
764 return(0);
765
766 h->flags |= HTML_NOSPACE;
767 print_text(h, "(");
768 h->flags |= HTML_NOSPACE;
769 print_text(h, nn->string);
770 h->flags |= HTML_NOSPACE;
771 print_text(h, ")");
772 return(0);
773 }
774
775
776 /* ARGSUSED */
777 static int
778 mdoc_ns_pre(MDOC_ARGS)
779 {
780
781 h->flags |= HTML_NOSPACE;
782 return(1);
783 }
784
785
786 /* ARGSUSED */
787 static int
788 mdoc_ar_pre(MDOC_ARGS)
789 {
790 struct htmlpair tag;
791
792 PAIR_CLASS_INIT(&tag, "arg");
793 print_otag(h, TAG_SPAN, 1, &tag);
794 return(1);
795 }
796
797
798 /* ARGSUSED */
799 static int
800 mdoc_xx_pre(MDOC_ARGS)
801 {
802 const char *pp;
803 struct htmlpair tag;
804
805 switch (n->tok) {
806 case (MDOC_Bsx):
807 pp = "BSDI BSD/OS";
808 break;
809 case (MDOC_Dx):
810 pp = "DragonFlyBSD";
811 break;
812 case (MDOC_Fx):
813 pp = "FreeBSD";
814 break;
815 case (MDOC_Nx):
816 pp = "NetBSD";
817 break;
818 case (MDOC_Ox):
819 pp = "OpenBSD";
820 break;
821 case (MDOC_Ux):
822 pp = "UNIX";
823 break;
824 default:
825 return(1);
826 }
827
828 PAIR_CLASS_INIT(&tag, "unix");
829 print_otag(h, TAG_SPAN, 1, &tag);
830 print_text(h, pp);
831 return(1);
832 }
833
834
835 /* ARGSUSED */
836 static int
837 mdoc_bx_pre(MDOC_ARGS)
838 {
839 const struct mdoc_node *nn;
840 struct htmlpair tag;
841
842 PAIR_CLASS_INIT(&tag, "unix");
843 print_otag(h, TAG_SPAN, 1, &tag);
844
845 for (nn = n->child; nn; nn = nn->next)
846 print_mdoc_node(m, nn, h);
847
848 if (n->child)
849 h->flags |= HTML_NOSPACE;
850
851 print_text(h, "BSD");
852 return(0);
853 }
854
855
856 /* ARGSUSED */
857 static int
858 mdoc_it_block_pre(MDOC_ARGS, int type, int comp,
859 struct roffsu *offs, struct roffsu *width)
860 {
861 struct htmlpair tag;
862 const struct mdoc_node *nn;
863 struct roffsu su;
864
865 nn = n->parent->parent;
866 assert(nn->args);
867
868 /* XXX: see notes in mdoc_it_pre(). */
869
870 if (MDOC_Column == type) {
871 /* Don't width-pad on the left. */
872 SCALE_HS_INIT(width, 0);
873 /* Also disallow non-compact. */
874 comp = 1;
875 }
876 if (MDOC_Diag == type)
877 /* Mandate non-compact with empty prior. */
878 if (n->prev && NULL == n->prev->body->child)
879 comp = 1;
880
881 bufcat_style(h, "clear", "both");
882 if (offs->scale > 0)
883 bufcat_su(h, "margin-left", offs);
884 if (width->scale > 0)
885 bufcat_su(h, "padding-left", width);
886
887 PAIR_STYLE_INIT(&tag, h);
888
889 /* Mandate compact following `Ss' and `Sh' starts. */
890
891 for (nn = n; nn && ! comp; nn = nn->parent) {
892 if (MDOC_BLOCK != nn->type)
893 continue;
894 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
895 comp = 1;
896 if (nn->prev)
897 break;
898 }
899
900 if ( ! comp) {
901 SCALE_VS_INIT(&su, 1);
902 bufcat_su(h, "padding-top", &su);
903 }
904
905 PAIR_STYLE_INIT(&tag, h);
906 print_otag(h, TAG_DIV, 1, &tag);
907 return(1);
908 }
909
910
911 /* ARGSUSED */
912 static int
913 mdoc_it_body_pre(MDOC_ARGS, int type)
914 {
915 struct htmlpair tag;
916 struct roffsu su;
917
918 switch (type) {
919 case (MDOC_Item):
920 /* FALLTHROUGH */
921 case (MDOC_Ohang):
922 /* FALLTHROUGH */
923 case (MDOC_Column):
924 break;
925 default:
926 /*
927 * XXX: this tricks CSS into aligning the bodies with
928 * the right-padding in the head.
929 */
930 SCALE_HS_INIT(&su, 2);
931 bufcat_su(h, "margin-left", &su);
932 PAIR_STYLE_INIT(&tag, h);
933 print_otag(h, TAG_DIV, 1, &tag);
934 break;
935 }
936
937 return(1);
938 }
939
940
941 /* ARGSUSED */
942 static int
943 mdoc_it_head_pre(MDOC_ARGS, int type, struct roffsu *width)
944 {
945 struct htmlpair tag;
946 struct ord *ord;
947 char nbuf[BUFSIZ];
948
949 switch (type) {
950 case (MDOC_Item):
951 /* FALLTHROUGH */
952 case (MDOC_Ohang):
953 print_otag(h, TAG_DIV, 0, NULL);
954 break;
955 case (MDOC_Column):
956 bufcat_su(h, "min-width", width);
957 bufcat_style(h, "clear", "none");
958 if (n->next && MDOC_HEAD == n->next->type)
959 bufcat_style(h, "float", "left");
960 PAIR_STYLE_INIT(&tag, h);
961 print_otag(h, TAG_DIV, 1, &tag);
962 break;
963 default:
964 bufcat_su(h, "min-width", width);
965 SCALE_INVERT(width);
966 bufcat_su(h, "margin-left", width);
967 if (n->next && n->next->child)
968 bufcat_style(h, "float", "left");
969
970 /* XXX: buffer if we run into body. */
971 SCALE_HS_INIT(width, 1);
972 bufcat_su(h, "margin-right", width);
973 PAIR_STYLE_INIT(&tag, h);
974 print_otag(h, TAG_DIV, 1, &tag);
975 break;
976 }
977
978 switch (type) {
979 case (MDOC_Diag):
980 PAIR_CLASS_INIT(&tag, "diag");
981 print_otag(h, TAG_SPAN, 1, &tag);
982 break;
983 case (MDOC_Enum):
984 ord = SLIST_FIRST(&h->ords);
985 assert(ord);
986 nbuf[BUFSIZ - 1] = 0;
987 (void)snprintf(nbuf, BUFSIZ - 1, "%d.", ord->pos++);
988 print_text(h, nbuf);
989 return(0);
990 case (MDOC_Dash):
991 print_text(h, "\\(en");
992 return(0);
993 case (MDOC_Hyphen):
994 print_text(h, "\\(hy");
995 return(0);
996 case (MDOC_Bullet):
997 print_text(h, "\\(bu");
998 return(0);
999 default:
1000 break;
1001 }
1002
1003 return(1);
1004 }
1005
1006
1007 static int
1008 mdoc_it_pre(MDOC_ARGS)
1009 {
1010 int i, type, wp, comp;
1011 const struct mdoc_node *bl, *nn;
1012 struct roffsu width, offs;
1013
1014 /*
1015 * XXX: be very careful in changing anything, here. Lists in
1016 * mandoc have many peculiarities; furthermore, they don't
1017 * translate well into HTML and require a bit of mangling.
1018 */
1019
1020 bl = n->parent->parent;
1021 if (MDOC_BLOCK != n->type)
1022 bl = bl->parent;
1023
1024 type = a2list(bl);
1025
1026 /* Set default width and offset. */
1027
1028 SCALE_HS_INIT(&offs, 0);
1029
1030 switch (type) {
1031 case (MDOC_Enum):
1032 /* FALLTHROUGH */
1033 case (MDOC_Dash):
1034 /* FALLTHROUGH */
1035 case (MDOC_Hyphen):
1036 /* FALLTHROUGH */
1037 case (MDOC_Bullet):
1038 SCALE_HS_INIT(&width, 2);
1039 break;
1040 default:
1041 SCALE_HS_INIT(&width, INDENT);
1042 break;
1043 }
1044
1045 /* Get width, offset, and compact arguments. */
1046
1047 for (wp = -1, comp = i = 0; i < (int)bl->args->argc; i++)
1048 switch (bl->args->argv[i].arg) {
1049 case (MDOC_Column):
1050 wp = i; /* Save for later. */
1051 break;
1052 case (MDOC_Width):
1053 a2width(bl->args->argv[i].value[0], &width);
1054 break;
1055 case (MDOC_Offset):
1056 a2offs(bl->args->argv[i].value[0], &offs);
1057 break;
1058 case (MDOC_Compact):
1059 comp = 1;
1060 break;
1061 default:
1062 break;
1063 }
1064
1065 /* Override width in some cases. */
1066
1067 switch (type) {
1068 case (MDOC_Item):
1069 /* FALLTHROUGH */
1070 case (MDOC_Inset):
1071 /* FALLTHROUGH */
1072 case (MDOC_Diag):
1073 SCALE_HS_INIT(&width, 0);
1074 break;
1075 default:
1076 if (0 == width.scale)
1077 SCALE_HS_INIT(&width, INDENT);
1078 break;
1079 }
1080
1081 /* Flip to body/block processing. */
1082
1083 if (MDOC_BODY == n->type)
1084 return(mdoc_it_body_pre(m, n, h, type));
1085 if (MDOC_BLOCK == n->type)
1086 return(mdoc_it_block_pre(m, n, h, type, comp,
1087 &offs, &width));
1088
1089 /* Override column widths. */
1090
1091 if (MDOC_Column == type) {
1092 nn = n->parent->child;
1093 for (i = 0; nn && nn != n; nn = nn->next, i++)
1094 /* Counter... */ ;
1095 if (i < (int)bl->args->argv[wp].sz)
1096 a2width(bl->args->argv[wp].value[i], &width);
1097 }
1098
1099 return(mdoc_it_head_pre(m, n, h, type, &width));
1100 }
1101
1102
1103 /* ARGSUSED */
1104 static int
1105 mdoc_bl_pre(MDOC_ARGS)
1106 {
1107 struct ord *ord;
1108
1109 if (MDOC_BLOCK != n->type)
1110 return(1);
1111 if (MDOC_Enum != a2list(n))
1112 return(1);
1113
1114 ord = malloc(sizeof(struct ord));
1115 if (NULL == ord)
1116 err(EXIT_FAILURE, "malloc");
1117 ord->cookie = n;
1118 ord->pos = 1;
1119 SLIST_INSERT_HEAD(&h->ords, ord, entry);
1120 return(1);
1121 }
1122
1123
1124 /* ARGSUSED */
1125 static void
1126 mdoc_bl_post(MDOC_ARGS)
1127 {
1128 struct ord *ord;
1129
1130 if (MDOC_BLOCK != n->type)
1131 return;
1132 if (MDOC_Enum != a2list(n))
1133 return;
1134
1135 ord = SLIST_FIRST(&h->ords);
1136 assert(ord);
1137 SLIST_REMOVE_HEAD(&h->ords, entry);
1138 free(ord);
1139 }
1140
1141
1142 /* ARGSUSED */
1143 static int
1144 mdoc_ex_pre(MDOC_ARGS)
1145 {
1146 const struct mdoc_node *nn;
1147 struct tag *t;
1148 struct htmlpair tag;
1149
1150 PAIR_CLASS_INIT(&tag, "utility");
1151
1152 print_text(h, "The");
1153 for (nn = n->child; nn; nn = nn->next) {
1154 t = print_otag(h, TAG_SPAN, 1, &tag);
1155 print_text(h, nn->string);
1156 print_tagq(h, t);
1157
1158 h->flags |= HTML_NOSPACE;
1159
1160 if (nn->next && NULL == nn->next->next)
1161 print_text(h, ", and");
1162 else if (nn->next)
1163 print_text(h, ",");
1164 else
1165 h->flags &= ~HTML_NOSPACE;
1166 }
1167
1168 if (n->child->next)
1169 print_text(h, "utilities exit");
1170 else
1171 print_text(h, "utility exits");
1172
1173 print_text(h, "0 on success, and >0 if an error occurs.");
1174 return(0);
1175 }
1176
1177
1178 /* ARGSUSED */
1179 static int
1180 mdoc_dq_pre(MDOC_ARGS)
1181 {
1182
1183 if (MDOC_BODY != n->type)
1184 return(1);
1185 print_text(h, "\\(lq");
1186 h->flags |= HTML_NOSPACE;
1187 return(1);
1188 }
1189
1190
1191 /* ARGSUSED */
1192 static void
1193 mdoc_dq_post(MDOC_ARGS)
1194 {
1195
1196 if (MDOC_BODY != n->type)
1197 return;
1198 h->flags |= HTML_NOSPACE;
1199 print_text(h, "\\(rq");
1200 }
1201
1202
1203 /* ARGSUSED */
1204 static int
1205 mdoc_pq_pre(MDOC_ARGS)
1206 {
1207
1208 if (MDOC_BODY != n->type)
1209 return(1);
1210 print_text(h, "\\&(");
1211 h->flags |= HTML_NOSPACE;
1212 return(1);
1213 }
1214
1215
1216 /* ARGSUSED */
1217 static void
1218 mdoc_pq_post(MDOC_ARGS)
1219 {
1220
1221 if (MDOC_BODY != n->type)
1222 return;
1223 print_text(h, ")");
1224 }
1225
1226
1227 /* ARGSUSED */
1228 static int
1229 mdoc_sq_pre(MDOC_ARGS)
1230 {
1231
1232 if (MDOC_BODY != n->type)
1233 return(1);
1234 print_text(h, "\\(oq");
1235 h->flags |= HTML_NOSPACE;
1236 return(1);
1237 }
1238
1239
1240 /* ARGSUSED */
1241 static void
1242 mdoc_sq_post(MDOC_ARGS)
1243 {
1244
1245 if (MDOC_BODY != n->type)
1246 return;
1247 h->flags |= HTML_NOSPACE;
1248 print_text(h, "\\(aq");
1249 }
1250
1251
1252 /* ARGSUSED */
1253 static int
1254 mdoc_em_pre(MDOC_ARGS)
1255 {
1256 struct htmlpair tag;
1257
1258 PAIR_CLASS_INIT(&tag, "emph");
1259 print_otag(h, TAG_SPAN, 1, &tag);
1260 return(1);
1261 }
1262
1263
1264 /* ARGSUSED */
1265 static int
1266 mdoc_d1_pre(MDOC_ARGS)
1267 {
1268 struct htmlpair tag[2];
1269 struct roffsu su;
1270
1271 if (MDOC_BLOCK != n->type)
1272 return(1);
1273
1274 /* FIXME: D1 shouldn't be literal. */
1275
1276 SCALE_VS_INIT(&su, INDENT - 2);
1277 bufcat_su(h, "margin-left", &su);
1278 PAIR_CLASS_INIT(&tag[0], "lit");
1279 PAIR_STYLE_INIT(&tag[1], h);
1280 print_otag(h, TAG_DIV, 2, tag);
1281 return(1);
1282 }
1283
1284
1285 /* ARGSUSED */
1286 static int
1287 mdoc_sx_pre(MDOC_ARGS)
1288 {
1289 struct htmlpair tag[2];
1290 const struct mdoc_node *nn;
1291 char buf[BUFSIZ];
1292
1293 /* FIXME: duplicates? */
1294
1295 (void)strlcpy(buf, "#", BUFSIZ);
1296 for (nn = n->child; nn; nn = nn->next) {
1297 (void)strlcat(buf, nn->string, BUFSIZ);
1298 if (nn->next)
1299 (void)strlcat(buf, "_", BUFSIZ);
1300 }
1301
1302 PAIR_CLASS_INIT(&tag[0], "link-sec");
1303 tag[1].key = ATTR_HREF;
1304 tag[1].val = buf;
1305
1306 print_otag(h, TAG_A, 2, tag);
1307 return(1);
1308 }
1309
1310
1311 /* ARGSUSED */
1312 static int
1313 mdoc_aq_pre(MDOC_ARGS)
1314 {
1315
1316 if (MDOC_BODY != n->type)
1317 return(1);
1318 print_text(h, "\\(la");
1319 h->flags |= HTML_NOSPACE;
1320 return(1);
1321 }
1322
1323
1324 /* ARGSUSED */
1325 static void
1326 mdoc_aq_post(MDOC_ARGS)
1327 {
1328
1329 if (MDOC_BODY != n->type)
1330 return;
1331 h->flags |= HTML_NOSPACE;
1332 print_text(h, "\\(ra");
1333 }
1334
1335
1336 /* ARGSUSED */
1337 static int
1338 mdoc_bd_pre(MDOC_ARGS)
1339 {
1340 struct htmlpair tag[2];
1341 int type, comp, i;
1342 const struct mdoc_node *bl, *nn;
1343 struct roffsu su;
1344
1345 if (MDOC_BLOCK == n->type)
1346 bl = n;
1347 else if (MDOC_HEAD == n->type)
1348 return(0);
1349 else
1350 bl = n->parent;
1351
1352 SCALE_VS_INIT(&su, 0);
1353
1354 type = comp = 0;
1355 for (i = 0; i < (int)bl->args->argc; i++)
1356 switch (bl->args->argv[i].arg) {
1357 case (MDOC_Offset):
1358 a2offs(bl->args->argv[i].value[0], &su);
1359 break;
1360 case (MDOC_Compact):
1361 comp = 1;
1362 break;
1363 case (MDOC_Centred):
1364 /* FALLTHROUGH */
1365 case (MDOC_Ragged):
1366 /* FALLTHROUGH */
1367 case (MDOC_Filled):
1368 /* FALLTHROUGH */
1369 case (MDOC_Unfilled):
1370 /* FALLTHROUGH */
1371 case (MDOC_Literal):
1372 type = bl->args->argv[i].arg;
1373 break;
1374 default:
1375 break;
1376 }
1377
1378 /* FIXME: -centered, etc. formatting. */
1379
1380 if (MDOC_BLOCK == n->type) {
1381 bufcat_su(h, "margin-left", &su);
1382 for (nn = n; nn && ! comp; nn = nn->parent) {
1383 if (MDOC_BLOCK != nn->type)
1384 continue;
1385 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
1386 comp = 1;
1387 if (nn->prev)
1388 break;
1389 }
1390 if (comp) {
1391 print_otag(h, TAG_DIV, 0, tag);
1392 return(1);
1393 }
1394 SCALE_VS_INIT(&su, 1);
1395 bufcat_su(h, "margin-top", &su);
1396 PAIR_STYLE_INIT(&tag[0], h);
1397 print_otag(h, TAG_DIV, 1, tag);
1398 return(1);
1399 }
1400
1401 if (MDOC_Unfilled != type && MDOC_Literal != type)
1402 return(1);
1403
1404 PAIR_CLASS_INIT(&tag[0], "lit");
1405 bufcat_style(h, "white-space", "pre");
1406 PAIR_STYLE_INIT(&tag[1], h);
1407 print_otag(h, TAG_DIV, 2, tag);
1408
1409 for (nn = n->child; nn; nn = nn->next) {
1410 h->flags |= HTML_NOSPACE;
1411 print_mdoc_node(m, nn, h);
1412 if (NULL == nn->next)
1413 continue;
1414 if (nn->prev && nn->prev->line < nn->line)
1415 print_text(h, "\n");
1416 else if (NULL == nn->prev)
1417 print_text(h, "\n");
1418 }
1419
1420 return(0);
1421 }
1422
1423
1424 /* ARGSUSED */
1425 static int
1426 mdoc_pa_pre(MDOC_ARGS)
1427 {
1428 struct htmlpair tag;
1429
1430 PAIR_CLASS_INIT(&tag, "file");
1431 print_otag(h, TAG_SPAN, 1, &tag);
1432 return(1);
1433 }
1434
1435
1436 /* ARGSUSED */
1437 static int
1438 mdoc_ad_pre(MDOC_ARGS)
1439 {
1440 struct htmlpair tag;
1441
1442 PAIR_CLASS_INIT(&tag, "addr");
1443 print_otag(h, TAG_SPAN, 1, &tag);
1444 return(1);
1445 }
1446
1447
1448 /* ARGSUSED */
1449 static int
1450 mdoc_an_pre(MDOC_ARGS)
1451 {
1452 struct htmlpair tag;
1453
1454 /* TODO: -split and -nosplit (see termp_an_pre()). */
1455
1456 PAIR_CLASS_INIT(&tag, "author");
1457 print_otag(h, TAG_SPAN, 1, &tag);
1458 return(1);
1459 }
1460
1461
1462 /* ARGSUSED */
1463 static int
1464 mdoc_cd_pre(MDOC_ARGS)
1465 {
1466 struct htmlpair tag;
1467
1468 print_otag(h, TAG_DIV, 0, NULL);
1469 PAIR_CLASS_INIT(&tag, "config");
1470 print_otag(h, TAG_SPAN, 1, &tag);
1471 return(1);
1472 }
1473
1474
1475 /* ARGSUSED */
1476 static int
1477 mdoc_dv_pre(MDOC_ARGS)
1478 {
1479 struct htmlpair tag;
1480
1481 PAIR_CLASS_INIT(&tag, "define");
1482 print_otag(h, TAG_SPAN, 1, &tag);
1483 return(1);
1484 }
1485
1486
1487 /* ARGSUSED */
1488 static int
1489 mdoc_ev_pre(MDOC_ARGS)
1490 {
1491 struct htmlpair tag;
1492
1493 PAIR_CLASS_INIT(&tag, "env");
1494 print_otag(h, TAG_SPAN, 1, &tag);
1495 return(1);
1496 }
1497
1498
1499 /* ARGSUSED */
1500 static int
1501 mdoc_er_pre(MDOC_ARGS)
1502 {
1503 struct htmlpair tag;
1504
1505 PAIR_CLASS_INIT(&tag, "errno");
1506 print_otag(h, TAG_SPAN, 1, &tag);
1507 return(1);
1508 }
1509
1510
1511 /* ARGSUSED */
1512 static int
1513 mdoc_fa_pre(MDOC_ARGS)
1514 {
1515 const struct mdoc_node *nn;
1516 struct htmlpair tag;
1517 struct tag *t;
1518
1519 PAIR_CLASS_INIT(&tag, "farg");
1520 if (n->parent->tok != MDOC_Fo) {
1521 print_otag(h, TAG_SPAN, 1, &tag);
1522 return(1);
1523 }
1524
1525 for (nn = n->child; nn; nn = nn->next) {
1526 t = print_otag(h, TAG_SPAN, 1, &tag);
1527 print_text(h, nn->string);
1528 print_tagq(h, t);
1529 if (nn->next)
1530 print_text(h, ",");
1531 }
1532
1533 if (n->child && n->next && n->next->tok == MDOC_Fa)
1534 print_text(h, ",");
1535
1536 return(0);
1537 }
1538
1539
1540 /* ARGSUSED */
1541 static int
1542 mdoc_fd_pre(MDOC_ARGS)
1543 {
1544 struct htmlpair tag;
1545 struct roffsu su;
1546
1547 if (SEC_SYNOPSIS == n->sec) {
1548 if (n->next && MDOC_Fd != n->next->tok) {
1549 SCALE_VS_INIT(&su, 1);
1550 bufcat_su(h, "margin-bottom", &su);
1551 PAIR_STYLE_INIT(&tag, h);
1552 print_otag(h, TAG_DIV, 1, &tag);
1553 } else
1554 print_otag(h, TAG_DIV, 0, NULL);
1555 }
1556
1557 PAIR_CLASS_INIT(&tag, "macro");
1558 print_otag(h, TAG_SPAN, 1, &tag);
1559 return(1);
1560 }
1561
1562
1563 /* ARGSUSED */
1564 static int
1565 mdoc_vt_pre(MDOC_ARGS)
1566 {
1567 struct htmlpair tag;
1568 struct roffsu su;
1569
1570 if (SEC_SYNOPSIS == n->sec) {
1571 if (n->next && MDOC_Vt != n->next->tok) {
1572 SCALE_VS_INIT(&su, 1);
1573 bufcat_su(h, "margin-bottom", &su);
1574 PAIR_STYLE_INIT(&tag, h);
1575 print_otag(h, TAG_DIV, 1, &tag);
1576 } else
1577 print_otag(h, TAG_DIV, 0, NULL);
1578 }
1579
1580 PAIR_CLASS_INIT(&tag, "type");
1581 print_otag(h, TAG_SPAN, 1, &tag);
1582 return(1);
1583 }
1584
1585
1586 /* ARGSUSED */
1587 static int
1588 mdoc_ft_pre(MDOC_ARGS)
1589 {
1590 struct htmlpair tag;
1591 struct roffsu su;
1592
1593 if (SEC_SYNOPSIS == n->sec) {
1594 if (n->prev && MDOC_Fo == n->prev->tok) {
1595 SCALE_VS_INIT(&su, 1);
1596 bufcat_su(h, "margin-top", &su);
1597 PAIR_STYLE_INIT(&tag, h);
1598 print_otag(h, TAG_DIV, 1, &tag);
1599 } else
1600 print_otag(h, TAG_DIV, 0, NULL);
1601 }
1602
1603 PAIR_CLASS_INIT(&tag, "ftype");
1604 print_otag(h, TAG_SPAN, 1, &tag);
1605 return(1);
1606 }
1607
1608
1609 /* ARGSUSED */
1610 static int
1611 mdoc_fn_pre(MDOC_ARGS)
1612 {
1613 struct tag *t;
1614 struct htmlpair tag[2];
1615 const struct mdoc_node *nn;
1616 char nbuf[BUFSIZ];
1617 const char *sp, *ep;
1618 int sz, i;
1619 struct roffsu su;
1620
1621 if (SEC_SYNOPSIS == n->sec) {
1622 SCALE_HS_INIT(&su, INDENT);
1623 bufcat_su(h, "margin-left", &su);
1624 su.scale = -su.scale;
1625 bufcat_su(h, "text-indent", &su);
1626 if (n->next) {
1627 SCALE_VS_INIT(&su, 1);
1628 bufcat_su(h, "margin-bottom", &su);
1629 }
1630 PAIR_STYLE_INIT(&tag[0], h);
1631 print_otag(h, TAG_DIV, 1, tag);
1632 }
1633
1634 /* Split apart into type and name. */
1635 assert(n->child->string);
1636 sp = n->child->string;
1637
1638 ep = strchr(sp, ' ');
1639 if (NULL != ep) {
1640 PAIR_CLASS_INIT(&tag[0], "ftype");
1641 t = print_otag(h, TAG_SPAN, 1, tag);
1642
1643 while (ep) {
1644 sz = MIN((int)(ep - sp), BUFSIZ - 1);
1645 (void)memcpy(nbuf, sp, (size_t)sz);
1646 nbuf[sz] = '\0';
1647 print_text(h, nbuf);
1648 sp = ++ep;
1649 ep = strchr(sp, ' ');
1650 }
1651 print_tagq(h, t);
1652 }
1653
1654 PAIR_CLASS_INIT(&tag[0], "fname");
1655 t = print_otag(h, TAG_SPAN, 1, tag);
1656
1657 if (sp) {
1658 (void)strlcpy(nbuf, sp, BUFSIZ);
1659 print_text(h, nbuf);
1660 }
1661
1662 print_tagq(h, t);
1663
1664 h->flags |= HTML_NOSPACE;
1665 print_text(h, "(");
1666
1667 bufinit(h);
1668 PAIR_CLASS_INIT(&tag[0], "farg");
1669 bufcat_style(h, "white-space", "nowrap");
1670 PAIR_STYLE_INIT(&tag[1], h);
1671
1672 for (nn = n->child->next; nn; nn = nn->next) {
1673 i = 1;
1674 if (SEC_SYNOPSIS == n->sec)
1675 i = 2;
1676 t = print_otag(h, TAG_SPAN, i, tag);
1677 print_text(h, nn->string);
1678 print_tagq(h, t);
1679 if (nn->next)
1680 print_text(h, ",");
1681 }
1682
1683 print_text(h, ")");
1684 if (SEC_SYNOPSIS == n->sec)
1685 print_text(h, ";");
1686
1687 return(0);
1688 }
1689
1690
1691 /* ARGSUSED */
1692 static int
1693 mdoc_sp_pre(MDOC_ARGS)
1694 {
1695 int len;
1696 struct htmlpair tag;
1697 struct roffsu su;
1698
1699 switch (n->tok) {
1700 case (MDOC_sp):
1701 /* FIXME: can this have a scaling indicator? */
1702 len = n->child ? atoi(n->child->string) : 1;
1703 break;
1704 case (MDOC_br):
1705 len = 0;
1706 break;
1707 default:
1708 len = 1;
1709 break;
1710 }
1711
1712 SCALE_VS_INIT(&su, len);
1713 bufcat_su(h, "height", &su);
1714 PAIR_STYLE_INIT(&tag, h);
1715 print_otag(h, TAG_DIV, 1, &tag);
1716 return(1);
1717
1718 }
1719
1720
1721 /* ARGSUSED */
1722 static int
1723 mdoc_brq_pre(MDOC_ARGS)
1724 {
1725
1726 if (MDOC_BODY != n->type)
1727 return(1);
1728 print_text(h, "\\(lC");
1729 h->flags |= HTML_NOSPACE;
1730 return(1);
1731 }
1732
1733
1734 /* ARGSUSED */
1735 static void
1736 mdoc_brq_post(MDOC_ARGS)
1737 {
1738
1739 if (MDOC_BODY != n->type)
1740 return;
1741 h->flags |= HTML_NOSPACE;
1742 print_text(h, "\\(rC");
1743 }
1744
1745
1746 /* ARGSUSED */
1747 static int
1748 mdoc_lk_pre(MDOC_ARGS)
1749 {
1750 const struct mdoc_node *nn;
1751 struct htmlpair tag[2];
1752
1753 nn = n->child;
1754
1755 PAIR_CLASS_INIT(&tag[0], "link-ext");
1756 tag[1].key = ATTR_HREF;
1757 tag[1].val = nn->string;
1758 print_otag(h, TAG_A, 2, tag);
1759
1760 for (nn = nn->next; nn; nn = nn->next)
1761 print_text(h, nn->string);
1762
1763 return(0);
1764 }
1765
1766
1767 /* ARGSUSED */
1768 static int
1769 mdoc_mt_pre(MDOC_ARGS)
1770 {
1771 struct htmlpair tag[2];
1772 struct tag *t;
1773 const struct mdoc_node *nn;
1774
1775 PAIR_CLASS_INIT(&tag[0], "link-mail");
1776
1777 for (nn = n->child; nn; nn = nn->next) {
1778 bufinit(h);
1779 bufcat(h, "mailto:");
1780 bufcat(h, nn->string);
1781 PAIR_STYLE_INIT(&tag[1], h);
1782 t = print_otag(h, TAG_A, 2, tag);
1783 print_text(h, nn->string);
1784 print_tagq(h, t);
1785 }
1786
1787 return(0);
1788 }
1789
1790
1791 /* ARGSUSED */
1792 static int
1793 mdoc_fo_pre(MDOC_ARGS)
1794 {
1795 struct htmlpair tag;
1796
1797 if (MDOC_BODY == n->type) {
1798 h->flags |= HTML_NOSPACE;
1799 print_text(h, "(");
1800 h->flags |= HTML_NOSPACE;
1801 return(1);
1802 } else if (MDOC_BLOCK == n->type)
1803 return(1);
1804
1805 PAIR_CLASS_INIT(&tag, "fname");
1806 print_otag(h, TAG_SPAN, 1, &tag);
1807 return(1);
1808 }
1809
1810
1811 /* ARGSUSED */
1812 static void
1813 mdoc_fo_post(MDOC_ARGS)
1814 {
1815 if (MDOC_BODY != n->type)
1816 return;
1817 h->flags |= HTML_NOSPACE;
1818 print_text(h, ")");
1819 h->flags |= HTML_NOSPACE;
1820 print_text(h, ";");
1821 }
1822
1823
1824 /* ARGSUSED */
1825 static int
1826 mdoc_in_pre(MDOC_ARGS)
1827 {
1828 const struct mdoc_node *nn;
1829 struct tag *t;
1830 struct htmlpair tag[2];
1831 int i;
1832 struct roffsu su;
1833
1834 if (SEC_SYNOPSIS == n->sec) {
1835 if (n->next && MDOC_In != n->next->tok) {
1836 SCALE_VS_INIT(&su, 1);
1837 bufcat_su(h, "margin-bottom", &su);
1838 PAIR_STYLE_INIT(&tag[0], h);
1839 print_otag(h, TAG_DIV, 1, tag);
1840 } else
1841 print_otag(h, TAG_DIV, 0, NULL);
1842 }
1843
1844 /* FIXME: there's a buffer bug in here somewhere. */
1845
1846 PAIR_CLASS_INIT(&tag[0], "includes");
1847 print_otag(h, TAG_SPAN, 1, tag);
1848
1849 if (SEC_SYNOPSIS == n->sec)
1850 print_text(h, "#include");
1851
1852 print_text(h, "<");
1853 h->flags |= HTML_NOSPACE;
1854
1855 /* XXX -- see warning in termp_in_post(). */
1856
1857 for (nn = n->child; nn; nn = nn->next) {
1858 PAIR_CLASS_INIT(&tag[0], "link-includes");
1859 i = 1;
1860 if (h->base_includes) {
1861 buffmt_includes(h, nn->string);
1862 tag[i].key = ATTR_HREF;
1863 tag[i++].val = h->buf;
1864 }
1865 t = print_otag(h, TAG_A, i, tag);
1866 print_mdoc_node(m, nn, h);
1867 print_tagq(h, t);
1868 }
1869
1870 h->flags |= HTML_NOSPACE;
1871 print_text(h, ">");
1872
1873 return(0);
1874 }
1875
1876
1877 /* ARGSUSED */
1878 static int
1879 mdoc_ic_pre(MDOC_ARGS)
1880 {
1881 struct htmlpair tag;
1882
1883 PAIR_CLASS_INIT(&tag, "cmd");
1884 print_otag(h, TAG_SPAN, 1, &tag);
1885 return(1);
1886 }
1887
1888
1889 /* ARGSUSED */
1890 static int
1891 mdoc_rv_pre(MDOC_ARGS)
1892 {
1893 const struct mdoc_node *nn;
1894 struct htmlpair tag;
1895 struct tag *t;
1896
1897 print_otag(h, TAG_DIV, 0, NULL);
1898 print_text(h, "The");
1899
1900 for (nn = n->child; nn; nn = nn->next) {
1901 PAIR_CLASS_INIT(&tag, "fname");
1902 t = print_otag(h, TAG_SPAN, 1, &tag);
1903 print_text(h, nn->string);
1904 print_tagq(h, t);
1905
1906 h->flags |= HTML_NOSPACE;
1907 if (nn->next && NULL == nn->next->next)
1908 print_text(h, "(), and");
1909 else if (nn->next)
1910 print_text(h, "(),");
1911 else
1912 print_text(h, "()");
1913 }
1914
1915 if (n->child->next)
1916 print_text(h, "functions return");
1917 else
1918 print_text(h, "function returns");
1919
1920 print_text(h, "the value 0 if successful; otherwise the value "
1921 "-1 is returned and the global variable");
1922
1923 PAIR_CLASS_INIT(&tag, "var");
1924 t = print_otag(h, TAG_SPAN, 1, &tag);
1925 print_text(h, "errno");
1926 print_tagq(h, t);
1927 print_text(h, "is set to indicate the error.");
1928 return(0);
1929 }
1930
1931
1932 /* ARGSUSED */
1933 static int
1934 mdoc_va_pre(MDOC_ARGS)
1935 {
1936 struct htmlpair tag;
1937
1938 PAIR_CLASS_INIT(&tag, "var");
1939 print_otag(h, TAG_SPAN, 1, &tag);
1940 return(1);
1941 }
1942
1943
1944 /* ARGSUSED */
1945 static int
1946 mdoc_bq_pre(MDOC_ARGS)
1947 {
1948
1949 if (MDOC_BODY != n->type)
1950 return(1);
1951 print_text(h, "\\(lB");
1952 h->flags |= HTML_NOSPACE;
1953 return(1);
1954 }
1955
1956
1957 /* ARGSUSED */
1958 static void
1959 mdoc_bq_post(MDOC_ARGS)
1960 {
1961
1962 if (MDOC_BODY != n->type)
1963 return;
1964 h->flags |= HTML_NOSPACE;
1965 print_text(h, "\\(rB");
1966 }
1967
1968
1969 /* ARGSUSED */
1970 static int
1971 mdoc_ap_pre(MDOC_ARGS)
1972 {
1973
1974 h->flags |= HTML_NOSPACE;
1975 print_text(h, "\\(aq");
1976 h->flags |= HTML_NOSPACE;
1977 return(1);
1978 }
1979
1980
1981 /* ARGSUSED */
1982 static int
1983 mdoc_bf_pre(MDOC_ARGS)
1984 {
1985 int i;
1986 struct htmlpair tag[2];
1987 struct roffsu su;
1988
1989 if (MDOC_HEAD == n->type)
1990 return(0);
1991 else if (MDOC_BLOCK != n->type)
1992 return(1);
1993
1994 PAIR_CLASS_INIT(&tag[0], "lit");
1995
1996 if (n->head->child) {
1997 if ( ! strcmp("Em", n->head->child->string))
1998 PAIR_CLASS_INIT(&tag[0], "emph");
1999 else if ( ! strcmp("Sy", n->head->child->string))
2000 PAIR_CLASS_INIT(&tag[0], "symb");
2001 else if ( ! strcmp("Li", n->head->child->string))
2002 PAIR_CLASS_INIT(&tag[0], "lit");
2003 } else {
2004 assert(n->args);
2005 for (i = 0; i < (int)n->args->argc; i++)
2006 switch (n->args->argv[i].arg) {
2007 case (MDOC_Symbolic):
2008 PAIR_CLASS_INIT(&tag[0], "symb");
2009 break;
2010 case (MDOC_Literal):
2011 PAIR_CLASS_INIT(&tag[0], "lit");
2012 break;
2013 case (MDOC_Emphasis):
2014 PAIR_CLASS_INIT(&tag[0], "emph");
2015 break;
2016 default:
2017 break;
2018 }
2019 }
2020
2021 /* FIXME: div's have spaces stripped--we want them. */
2022
2023 bufcat_style(h, "display", "inline");
2024 SCALE_HS_INIT(&su, 1);
2025 bufcat_su(h, "margin-right", &su);
2026 PAIR_STYLE_INIT(&tag[1], h);
2027 print_otag(h, TAG_DIV, 2, tag);
2028 return(1);
2029 }
2030
2031
2032 /* ARGSUSED */
2033 static int
2034 mdoc_ms_pre(MDOC_ARGS)
2035 {
2036 struct htmlpair tag;
2037
2038 PAIR_CLASS_INIT(&tag, "symb");
2039 print_otag(h, TAG_SPAN, 1, &tag);
2040 return(1);
2041 }
2042
2043
2044 /* ARGSUSED */
2045 static int
2046 mdoc_pf_pre(MDOC_ARGS)
2047 {
2048
2049 h->flags |= HTML_IGNDELIM;
2050 return(1);
2051 }
2052
2053
2054 /* ARGSUSED */
2055 static void
2056 mdoc_pf_post(MDOC_ARGS)
2057 {
2058
2059 h->flags &= ~HTML_IGNDELIM;
2060 h->flags |= HTML_NOSPACE;
2061 }
2062
2063
2064 /* ARGSUSED */
2065 static int
2066 mdoc_rs_pre(MDOC_ARGS)
2067 {
2068 struct htmlpair tag;
2069 struct roffsu su;
2070
2071 if (MDOC_BLOCK != n->type)
2072 return(1);
2073
2074 if (n->prev && SEC_SEE_ALSO == n->sec) {
2075 SCALE_VS_INIT(&su, 1);
2076 bufcat_su(h, "margin-top", &su);
2077 PAIR_STYLE_INIT(&tag, h);
2078 print_otag(h, TAG_DIV, 1, &tag);
2079 }
2080
2081 PAIR_CLASS_INIT(&tag, "ref");
2082 print_otag(h, TAG_SPAN, 1, &tag);
2083 return(1);
2084 }
2085
2086
2087
2088 /* ARGSUSED */
2089 static int
2090 mdoc_li_pre(MDOC_ARGS)
2091 {
2092 struct htmlpair tag;
2093
2094 PAIR_CLASS_INIT(&tag, "lit");
2095 print_otag(h, TAG_SPAN, 1, &tag);
2096 return(1);
2097 }
2098
2099
2100 /* ARGSUSED */
2101 static int
2102 mdoc_sy_pre(MDOC_ARGS)
2103 {
2104 struct htmlpair tag;
2105
2106 PAIR_CLASS_INIT(&tag, "symb");
2107 print_otag(h, TAG_SPAN, 1, &tag);
2108 return(1);
2109 }
2110
2111
2112 /* ARGSUSED */
2113 static int
2114 mdoc_bt_pre(MDOC_ARGS)
2115 {
2116
2117 print_text(h, "is currently in beta test.");
2118 return(0);
2119 }
2120
2121
2122 /* ARGSUSED */
2123 static int
2124 mdoc_ud_pre(MDOC_ARGS)
2125 {
2126
2127 print_text(h, "currently under development.");
2128 return(0);
2129 }
2130
2131
2132 /* ARGSUSED */
2133 static int
2134 mdoc_lb_pre(MDOC_ARGS)
2135 {
2136 struct htmlpair tag;
2137
2138 if (SEC_SYNOPSIS == n->sec)
2139 print_otag(h, TAG_DIV, 0, NULL);
2140 PAIR_CLASS_INIT(&tag, "lib");
2141 print_otag(h, TAG_SPAN, 1, &tag);
2142 return(1);
2143 }
2144
2145
2146 /* ARGSUSED */
2147 static int
2148 mdoc__x_pre(MDOC_ARGS)
2149 {
2150 struct htmlpair tag;
2151
2152 /* TODO: %U. */
2153
2154 switch (n->tok) {
2155 case(MDOC__A):
2156 PAIR_CLASS_INIT(&tag, "ref-auth");
2157 break;
2158 case(MDOC__B):
2159 PAIR_CLASS_INIT(&tag, "ref-book");
2160 break;
2161 case(MDOC__C):
2162 PAIR_CLASS_INIT(&tag, "ref-city");
2163 break;
2164 case(MDOC__D):
2165 PAIR_CLASS_INIT(&tag, "ref-date");
2166 break;
2167 case(MDOC__I):
2168 PAIR_CLASS_INIT(&tag, "ref-issue");
2169 break;
2170 case(MDOC__J):
2171 PAIR_CLASS_INIT(&tag, "ref-jrnl");
2172 break;
2173 case(MDOC__N):
2174 PAIR_CLASS_INIT(&tag, "ref-num");
2175 break;
2176 case(MDOC__O):
2177 PAIR_CLASS_INIT(&tag, "ref-opt");
2178 break;
2179 case(MDOC__P):
2180 PAIR_CLASS_INIT(&tag, "ref-page");
2181 break;
2182 case(MDOC__Q):
2183 PAIR_CLASS_INIT(&tag, "ref-corp");
2184 break;
2185 case(MDOC__R):
2186 PAIR_CLASS_INIT(&tag, "ref-rep");
2187 break;
2188 case(MDOC__T):
2189 PAIR_CLASS_INIT(&tag, "ref-title");
2190 print_text(h, "\\(lq");
2191 h->flags |= HTML_NOSPACE;
2192 break;
2193 case(MDOC__V):
2194 PAIR_CLASS_INIT(&tag, "ref-vol");
2195 break;
2196 default:
2197 abort();
2198 /* NOTREACHED */
2199 }
2200
2201 print_otag(h, TAG_SPAN, 1, &tag);
2202 return(1);
2203 }
2204
2205
2206 /* ARGSUSED */
2207 static void
2208 mdoc__x_post(MDOC_ARGS)
2209 {
2210
2211 h->flags |= HTML_NOSPACE;
2212 switch (n->tok) {
2213 case (MDOC__T):
2214 print_text(h, "\\(rq");
2215 h->flags |= HTML_NOSPACE;
2216 break;
2217 default:
2218 break;
2219 }
2220 print_text(h, n->next ? "," : ".");
2221 }