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