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