]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_man.c
The post_nm() validation function crashed when the first .Nm child node
[mandoc.git] / mdoc_man.c
1 /* $Id: mdoc_man.c,v 1.35 2012/07/12 08:55:48 schwarze Exp $ */
2 /*
3 * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "mandoc.h"
26 #include "out.h"
27 #include "man.h"
28 #include "mdoc.h"
29 #include "main.h"
30
31 #define DECL_ARGS const struct mdoc_meta *m, \
32 const struct mdoc_node *n
33
34 struct manact {
35 int (*cond)(DECL_ARGS); /* DON'T run actions */
36 int (*pre)(DECL_ARGS); /* pre-node action */
37 void (*post)(DECL_ARGS); /* post-node action */
38 const char *prefix; /* pre-node string constant */
39 const char *suffix; /* post-node string constant */
40 };
41
42 static int cond_body(DECL_ARGS);
43 static int cond_head(DECL_ARGS);
44 static void font_push(char);
45 static void font_pop(void);
46 static void post__t(DECL_ARGS);
47 static void post_bd(DECL_ARGS);
48 static void post_bf(DECL_ARGS);
49 static void post_bk(DECL_ARGS);
50 static void post_bl(DECL_ARGS);
51 static void post_dl(DECL_ARGS);
52 static void post_enc(DECL_ARGS);
53 static void post_eo(DECL_ARGS);
54 static void post_fa(DECL_ARGS);
55 static void post_fd(DECL_ARGS);
56 static void post_fl(DECL_ARGS);
57 static void post_fn(DECL_ARGS);
58 static void post_fo(DECL_ARGS);
59 static void post_font(DECL_ARGS);
60 static void post_in(DECL_ARGS);
61 static void post_it(DECL_ARGS);
62 static void post_lb(DECL_ARGS);
63 static void post_nm(DECL_ARGS);
64 static void post_percent(DECL_ARGS);
65 static void post_pf(DECL_ARGS);
66 static void post_sect(DECL_ARGS);
67 static void post_sp(DECL_ARGS);
68 static void post_vt(DECL_ARGS);
69 static int pre__t(DECL_ARGS);
70 static int pre_an(DECL_ARGS);
71 static int pre_ap(DECL_ARGS);
72 static int pre_bd(DECL_ARGS);
73 static int pre_bf(DECL_ARGS);
74 static int pre_bk(DECL_ARGS);
75 static int pre_bl(DECL_ARGS);
76 static int pre_br(DECL_ARGS);
77 static int pre_bx(DECL_ARGS);
78 static int pre_dl(DECL_ARGS);
79 static int pre_enc(DECL_ARGS);
80 static int pre_em(DECL_ARGS);
81 static int pre_fa(DECL_ARGS);
82 static int pre_fd(DECL_ARGS);
83 static int pre_fl(DECL_ARGS);
84 static int pre_fn(DECL_ARGS);
85 static int pre_fo(DECL_ARGS);
86 static int pre_ft(DECL_ARGS);
87 static int pre_in(DECL_ARGS);
88 static int pre_it(DECL_ARGS);
89 static int pre_lk(DECL_ARGS);
90 static int pre_li(DECL_ARGS);
91 static int pre_nm(DECL_ARGS);
92 static int pre_no(DECL_ARGS);
93 static int pre_ns(DECL_ARGS);
94 static int pre_pp(DECL_ARGS);
95 static int pre_rs(DECL_ARGS);
96 static int pre_sm(DECL_ARGS);
97 static int pre_sp(DECL_ARGS);
98 static int pre_sect(DECL_ARGS);
99 static int pre_sy(DECL_ARGS);
100 static void pre_syn(const struct mdoc_node *);
101 static int pre_vt(DECL_ARGS);
102 static int pre_ux(DECL_ARGS);
103 static int pre_xr(DECL_ARGS);
104 static void print_word(const char *);
105 static void print_offs(const char *);
106 static void print_width(const char *,
107 const struct mdoc_node *, size_t);
108 static void print_count(int *);
109 static void print_node(DECL_ARGS);
110
111 static const struct manact manacts[MDOC_MAX + 1] = {
112 { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
113 { NULL, NULL, NULL, NULL, NULL }, /* Dd */
114 { NULL, NULL, NULL, NULL, NULL }, /* Dt */
115 { NULL, NULL, NULL, NULL, NULL }, /* Os */
116 { NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
117 { NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
118 { NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
119 { cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
120 { cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
121 { cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
122 { NULL, NULL, NULL, NULL, NULL }, /* Ed */
123 { cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
124 { NULL, NULL, NULL, NULL, NULL }, /* El */
125 { NULL, pre_it, post_it, NULL, NULL }, /* It */
126 { NULL, pre_em, post_font, NULL, NULL }, /* Ad */
127 { NULL, pre_an, NULL, NULL, NULL }, /* An */
128 { NULL, pre_em, post_font, NULL, NULL }, /* Ar */
129 { NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
130 { NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
131 { NULL, pre_li, post_font, NULL, NULL }, /* Dv */
132 { NULL, pre_li, post_font, NULL, NULL }, /* Er */
133 { NULL, pre_li, post_font, NULL, NULL }, /* Ev */
134 { NULL, pre_enc, post_enc, "The \\fB",
135 "\\fP\nutility exits 0 on success, and >0 if an error occurs."
136 }, /* Ex */
137 { NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
138 { NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
139 { NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
140 { NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
141 { NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
142 { NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
143 { NULL, pre_in, post_in, NULL, NULL }, /* In */
144 { NULL, pre_li, post_font, NULL, NULL }, /* Li */
145 { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
146 { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
147 { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
148 { NULL, NULL, NULL, NULL, NULL }, /* Ot */
149 { NULL, pre_em, post_font, NULL, NULL }, /* Pa */
150 { NULL, pre_enc, post_enc, "The \\fB",
151 "\\fP\nfunction returns the value 0 if successful;\n"
152 "otherwise the value -1 is returned and the global\n"
153 "variable \\fIerrno\\fP is set to indicate the error."
154 }, /* Rv */
155 { NULL, NULL, NULL, NULL, NULL }, /* St */
156 { NULL, pre_em, post_font, NULL, NULL }, /* Va */
157 { NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
158 { NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
159 { NULL, NULL, post_percent, NULL, NULL }, /* %A */
160 { NULL, pre_em, post_percent, NULL, NULL }, /* %B */
161 { NULL, NULL, post_percent, NULL, NULL }, /* %D */
162 { NULL, pre_em, post_percent, NULL, NULL }, /* %I */
163 { NULL, pre_em, post_percent, NULL, NULL }, /* %J */
164 { NULL, NULL, post_percent, NULL, NULL }, /* %N */
165 { NULL, NULL, post_percent, NULL, NULL }, /* %O */
166 { NULL, NULL, post_percent, NULL, NULL }, /* %P */
167 { NULL, NULL, post_percent, NULL, NULL }, /* %R */
168 { NULL, pre__t, post__t, NULL, NULL }, /* %T */
169 { NULL, NULL, post_percent, NULL, NULL }, /* %V */
170 { NULL, NULL, NULL, NULL, NULL }, /* Ac */
171 { cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
172 { cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
173 { NULL, NULL, NULL, NULL, NULL }, /* At */
174 { NULL, NULL, NULL, NULL, NULL }, /* Bc */
175 { NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
176 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
177 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
178 { NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
179 { NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
180 { NULL, NULL, NULL, NULL, NULL }, /* Db */
181 { NULL, NULL, NULL, NULL, NULL }, /* Dc */
182 { cond_body, pre_enc, post_enc, "``", "''" }, /* Do */
183 { cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */
184 { NULL, NULL, NULL, NULL, NULL }, /* Ec */
185 { NULL, NULL, NULL, NULL, NULL }, /* Ef */
186 { NULL, pre_em, post_font, NULL, NULL }, /* Em */
187 { NULL, NULL, post_eo, NULL, NULL }, /* Eo */
188 { NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
189 { NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
190 { NULL, pre_no, NULL, NULL, NULL }, /* No */
191 { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
192 { NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
193 { NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
194 { NULL, NULL, NULL, NULL, NULL }, /* Pc */
195 { NULL, NULL, post_pf, NULL, NULL }, /* Pf */
196 { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
197 { cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
198 { NULL, NULL, NULL, NULL, NULL }, /* Qc */
199 { cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */
200 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
201 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
202 { NULL, NULL, NULL, NULL, NULL }, /* Re */
203 { cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
204 { NULL, NULL, NULL, NULL, NULL }, /* Sc */
205 { cond_body, pre_enc, post_enc, "`", "'" }, /* So */
206 { cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */
207 { NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
208 { NULL, pre_em, post_font, NULL, NULL }, /* Sx */
209 { NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
210 { NULL, pre_li, post_font, NULL, NULL }, /* Tn */
211 { NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
212 { NULL, NULL, NULL, NULL, NULL }, /* Xc */
213 { NULL, NULL, NULL, NULL, NULL }, /* Xo */
214 { NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
215 { NULL, NULL, NULL, NULL, NULL }, /* Fc */
216 { cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
217 { NULL, NULL, NULL, NULL, NULL }, /* Oc */
218 { NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
219 { NULL, NULL, NULL, NULL, NULL }, /* Ek */
220 { NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
221 { NULL, NULL, NULL, NULL, NULL }, /* Hf */
222 { NULL, NULL, NULL, NULL, NULL }, /* Fr */
223 { NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
224 { NULL, NULL, post_lb, NULL, NULL }, /* Lb */
225 { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
226 { NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
227 { NULL, pre_em, post_font, NULL, NULL }, /* Mt */
228 { cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
229 { cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
230 { NULL, NULL, NULL, NULL, NULL }, /* Brc */
231 { NULL, NULL, post_percent, NULL, NULL }, /* %C */
232 { NULL, NULL, NULL, NULL, NULL }, /* Es */
233 { NULL, NULL, NULL, NULL, NULL }, /* En */
234 { NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
235 { NULL, NULL, post_percent, NULL, NULL }, /* %Q */
236 { NULL, pre_br, NULL, NULL, NULL }, /* br */
237 { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
238 { NULL, NULL, post_percent, NULL, NULL }, /* %U */
239 { NULL, NULL, NULL, NULL, NULL }, /* Ta */
240 { NULL, NULL, NULL, NULL, NULL }, /* ROOT */
241 };
242
243 static int outflags;
244 #define MMAN_spc (1 << 0)
245 #define MMAN_spc_force (1 << 1)
246 #define MMAN_nl (1 << 2)
247 #define MMAN_br (1 << 3)
248 #define MMAN_sp (1 << 4)
249 #define MMAN_Sm (1 << 5)
250 #define MMAN_Bk (1 << 6)
251 #define MMAN_An_split (1 << 7)
252 #define MMAN_An_nosplit (1 << 8)
253
254 static struct {
255 char *head;
256 char *tail;
257 size_t size;
258 } fontqueue;
259
260 static void
261 font_push(char newfont)
262 {
263
264 if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
265 fontqueue.size += 8;
266 fontqueue.head = mandoc_realloc(fontqueue.head,
267 fontqueue.size);
268 }
269 *fontqueue.tail = newfont;
270 print_word("\\f");
271 putchar(newfont);
272 outflags &= ~MMAN_spc;
273 }
274
275 static void
276 font_pop(void)
277 {
278
279 if (fontqueue.tail > fontqueue.head)
280 fontqueue.tail--;
281 outflags &= ~MMAN_spc;
282 print_word("\\f");
283 putchar(*fontqueue.tail);
284 }
285
286 static void
287 print_word(const char *s)
288 {
289
290 if ((MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
291 /*
292 * If we need a newline, print it now and start afresh.
293 */
294 if (MMAN_sp & outflags)
295 printf("\n.sp\n");
296 else if (MMAN_br & outflags)
297 printf("\n.br\n");
298 else if (MMAN_nl & outflags)
299 putchar('\n');
300 outflags &= ~(MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
301 } else if (MMAN_spc & outflags && '\0' != s[0])
302 /*
303 * If we need a space, only print it if
304 * (1) it is forced by `No' or
305 * (2) what follows is not terminating punctuation or
306 * (3) what follows is longer than one character.
307 */
308 if (MMAN_spc_force & outflags ||
309 NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
310 if (MMAN_Bk & outflags) {
311 putchar('\\');
312 putchar('~');
313 } else
314 putchar(' ');
315 }
316
317 /*
318 * Reassign needing space if we're not following opening
319 * punctuation.
320 */
321 if (MMAN_Sm & outflags &&
322 (('(' != s[0] && '[' != s[0]) || '\0' != s[1]))
323 outflags |= MMAN_spc;
324 else
325 outflags &= ~MMAN_spc;
326 outflags &= ~MMAN_spc_force;
327
328 for ( ; *s; s++) {
329 switch (*s) {
330 case (ASCII_NBRSP):
331 printf("\\~");
332 break;
333 case (ASCII_HYPH):
334 putchar('-');
335 break;
336 default:
337 putchar((unsigned char)*s);
338 break;
339 }
340 }
341 }
342
343 static void
344 print_offs(const char *v)
345 {
346 char buf[24];
347 struct roffsu su;
348 size_t sz;
349
350 if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
351 sz = 0;
352 else if (0 == strcmp(v, "indent"))
353 sz = 6;
354 else if (0 == strcmp(v, "indent-two"))
355 sz = 12;
356 else if (a2roffsu(v, &su, SCALE_MAX)) {
357 print_word(v);
358 return;
359 } else
360 sz = strlen(v);
361
362 snprintf(buf, sizeof(buf), "%ldn", sz);
363 print_word(buf);
364 }
365
366 void
367 print_width(const char *v, const struct mdoc_node *child, size_t defsz)
368 {
369 char buf[24];
370 struct roffsu su;
371 size_t sz, chsz;
372
373 /* XXX Rough estimation, might have multiple parts. */
374 chsz = (NULL != child && MDOC_TEXT == child->type) ?
375 strlen(child->string) : 0;
376
377 if (NULL == v)
378 sz = defsz;
379 else if (a2roffsu(v, &su, SCALE_MAX)) {
380 if (SCALE_EN == su.unit)
381 sz = su.scale;
382 else {
383 if (chsz)
384 print_word(".HP");
385 else
386 print_word(".TP");
387 print_word(v);
388 return;
389 }
390 } else
391 sz = strlen(v);
392
393 if (chsz > sz)
394 print_word(".HP");
395 else
396 print_word(".TP");
397 snprintf(buf, sizeof(buf), "%ldn", sz + 2);
398 print_word(buf);
399 }
400
401 void
402 print_count(int *count)
403 {
404 char buf[12];
405
406 snprintf(buf, sizeof(buf), "%d.", ++*count);
407 print_word(buf);
408 }
409
410 void
411 man_man(void *arg, const struct man *man)
412 {
413
414 /*
415 * Dump the keep buffer.
416 * We're guaranteed by now that this exists (is non-NULL).
417 * Flush stdout afterward, just in case.
418 */
419 fputs(mparse_getkeep(man_mparse(man)), stdout);
420 fflush(stdout);
421 }
422
423 void
424 man_mdoc(void *arg, const struct mdoc *mdoc)
425 {
426 const struct mdoc_meta *m;
427 const struct mdoc_node *n;
428
429 m = mdoc_meta(mdoc);
430 n = mdoc_node(mdoc);
431
432 printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
433 m->title, m->msec, m->date, m->os, m->vol);
434
435 outflags = MMAN_nl | MMAN_Sm;
436 if (0 == fontqueue.size) {
437 fontqueue.size = 8;
438 fontqueue.head = fontqueue.tail = mandoc_malloc(8);
439 *fontqueue.tail = 'R';
440 }
441 print_node(m, n);
442 putchar('\n');
443 }
444
445 static void
446 print_node(DECL_ARGS)
447 {
448 const struct mdoc_node *prev, *sub;
449 const struct manact *act;
450 int cond, do_sub;
451
452 /*
453 * Break the line if we were parsed subsequent the current node.
454 * This makes the page structure be more consistent.
455 */
456 prev = n->prev ? n->prev : n->parent;
457 if (MMAN_spc & outflags && prev && prev->line < n->line)
458 outflags |= MMAN_nl;
459
460 act = NULL;
461 cond = 0;
462 do_sub = 1;
463
464 if (MDOC_TEXT == n->type) {
465 /*
466 * Make sure that we don't happen to start with a
467 * control character at the start of a line.
468 */
469 if (MMAN_nl & outflags && ('.' == *n->string ||
470 '\'' == *n->string)) {
471 print_word("\\&");
472 outflags &= ~MMAN_spc;
473 }
474 print_word(n->string);
475 } else {
476 /*
477 * Conditionally run the pre-node action handler for a
478 * node.
479 */
480 act = manacts + n->tok;
481 cond = NULL == act->cond || (*act->cond)(m, n);
482 if (cond && act->pre)
483 do_sub = (*act->pre)(m, n);
484 }
485
486 /*
487 * Conditionally run all child nodes.
488 * Note that this iterates over children instead of using
489 * recursion. This prevents unnecessary depth in the stack.
490 */
491 if (do_sub)
492 for (sub = n->child; sub; sub = sub->next)
493 print_node(m, sub);
494
495 /*
496 * Lastly, conditionally run the post-node handler.
497 */
498 if (cond && act->post)
499 (*act->post)(m, n);
500 }
501
502 static int
503 cond_head(DECL_ARGS)
504 {
505
506 return(MDOC_HEAD == n->type);
507 }
508
509 static int
510 cond_body(DECL_ARGS)
511 {
512
513 return(MDOC_BODY == n->type);
514 }
515
516 static int
517 pre_enc(DECL_ARGS)
518 {
519 const char *prefix;
520
521 prefix = manacts[n->tok].prefix;
522 if (NULL == prefix)
523 return(1);
524 print_word(prefix);
525 outflags &= ~MMAN_spc;
526 return(1);
527 }
528
529 static void
530 post_enc(DECL_ARGS)
531 {
532 const char *suffix;
533
534 suffix = manacts[n->tok].suffix;
535 if (NULL == suffix)
536 return;
537 outflags &= ~MMAN_spc;
538 print_word(suffix);
539 }
540
541 static void
542 post_font(DECL_ARGS)
543 {
544
545 font_pop();
546 }
547
548 static void
549 post_percent(DECL_ARGS)
550 {
551
552 if (pre_em == manacts[n->tok].pre)
553 font_pop();
554 if (n->next) {
555 print_word(",");
556 if (n->prev && n->prev->tok == n->tok &&
557 n->next->tok == n->tok)
558 print_word("and");
559 } else {
560 print_word(".");
561 outflags |= MMAN_nl;
562 }
563 }
564
565 static int
566 pre__t(DECL_ARGS)
567 {
568
569 if (n->parent && MDOC_Rs == n->parent->tok &&
570 n->parent->norm->Rs.quote_T) {
571 print_word("\"");
572 outflags &= ~MMAN_spc;
573 } else
574 font_push('I');
575 return(1);
576 }
577
578 static void
579 post__t(DECL_ARGS)
580 {
581
582 if (n->parent && MDOC_Rs == n->parent->tok &&
583 n->parent->norm->Rs.quote_T) {
584 outflags &= ~MMAN_spc;
585 print_word("\"");
586 } else
587 font_pop();
588 post_percent(m, n);
589 }
590
591 /*
592 * Print before a section header.
593 */
594 static int
595 pre_sect(DECL_ARGS)
596 {
597
598 if (MDOC_HEAD != n->type)
599 return(1);
600 outflags |= MMAN_nl;
601 print_word(manacts[n->tok].prefix);
602 print_word("\"");
603 outflags &= ~MMAN_spc;
604 return(1);
605 }
606
607 /*
608 * Print subsequent a section header.
609 */
610 static void
611 post_sect(DECL_ARGS)
612 {
613
614 if (MDOC_HEAD != n->type)
615 return;
616 outflags &= ~MMAN_spc;
617 print_word("\"");
618 outflags |= MMAN_nl;
619 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
620 outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
621 }
622
623 /* See mdoc_term.c, synopsis_pre() for comments. */
624 static void
625 pre_syn(const struct mdoc_node *n)
626 {
627
628 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
629 return;
630
631 if (n->prev->tok == n->tok &&
632 MDOC_Ft != n->tok &&
633 MDOC_Fo != n->tok &&
634 MDOC_Fn != n->tok) {
635 outflags |= MMAN_br;
636 return;
637 }
638
639 switch (n->prev->tok) {
640 case (MDOC_Fd):
641 /* FALLTHROUGH */
642 case (MDOC_Fn):
643 /* FALLTHROUGH */
644 case (MDOC_Fo):
645 /* FALLTHROUGH */
646 case (MDOC_In):
647 /* FALLTHROUGH */
648 case (MDOC_Vt):
649 outflags |= MMAN_sp;
650 break;
651 case (MDOC_Ft):
652 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
653 outflags |= MMAN_sp;
654 break;
655 }
656 /* FALLTHROUGH */
657 default:
658 outflags |= MMAN_br;
659 break;
660 }
661 }
662
663 static int
664 pre_an(DECL_ARGS)
665 {
666
667 switch (n->norm->An.auth) {
668 case (AUTH_split):
669 outflags &= ~MMAN_An_nosplit;
670 outflags |= MMAN_An_split;
671 return(0);
672 case (AUTH_nosplit):
673 outflags &= ~MMAN_An_split;
674 outflags |= MMAN_An_nosplit;
675 return(0);
676 default:
677 if (MMAN_An_split & outflags)
678 outflags |= MMAN_br;
679 else if (SEC_AUTHORS == n->sec &&
680 ! (MMAN_An_nosplit & outflags))
681 outflags |= MMAN_An_split;
682 return(1);
683 }
684 }
685
686 static int
687 pre_ap(DECL_ARGS)
688 {
689
690 outflags &= ~MMAN_spc;
691 print_word("'");
692 outflags &= ~MMAN_spc;
693 return(0);
694 }
695
696 static int
697 pre_bd(DECL_ARGS)
698 {
699
700 if (0 == n->norm->Bd.comp)
701 outflags |= MMAN_sp;
702 if (DISP_unfilled == n->norm->Bd.type ||
703 DISP_literal == n->norm->Bd.type) {
704 outflags |= MMAN_nl;
705 print_word(".nf");
706 }
707 outflags |= MMAN_nl;
708 print_word(".RS");
709 print_offs(n->norm->Bd.offs);
710 outflags |= MMAN_nl;
711 return(1);
712 }
713
714 static void
715 post_bd(DECL_ARGS)
716 {
717
718 outflags |= MMAN_nl;
719 print_word(".RE");
720 if (DISP_unfilled == n->norm->Bd.type ||
721 DISP_literal == n->norm->Bd.type) {
722 outflags |= MMAN_nl;
723 print_word(".fi");
724 }
725 outflags |= MMAN_nl;
726 }
727
728 static int
729 pre_bf(DECL_ARGS)
730 {
731
732 switch (n->type) {
733 case (MDOC_BLOCK):
734 return(1);
735 case (MDOC_BODY):
736 break;
737 default:
738 return(0);
739 }
740 switch (n->norm->Bf.font) {
741 case (FONT_Em):
742 font_push('I');
743 break;
744 case (FONT_Sy):
745 font_push('B');
746 break;
747 default:
748 font_push('R');
749 break;
750 }
751 return(1);
752 }
753
754 static void
755 post_bf(DECL_ARGS)
756 {
757
758 if (MDOC_BODY == n->type)
759 font_pop();
760 }
761
762 static int
763 pre_bk(DECL_ARGS)
764 {
765
766 switch (n->type) {
767 case (MDOC_BLOCK):
768 return(1);
769 case (MDOC_BODY):
770 outflags |= MMAN_Bk;
771 return(1);
772 default:
773 return(0);
774 }
775 }
776
777 static void
778 post_bk(DECL_ARGS)
779 {
780
781 if (MDOC_BODY == n->type)
782 outflags &= ~MMAN_Bk;
783 }
784
785 static int
786 pre_bl(DECL_ARGS)
787 {
788 size_t icol;
789
790 switch (n->norm->Bl.type) {
791 case (LIST_enum):
792 n->norm->Bl.count = 0;
793 return(1);
794 case (LIST_column):
795 break;
796 default:
797 return(1);
798 }
799
800 outflags |= MMAN_nl;
801 print_word(".TS");
802 outflags |= MMAN_nl;
803 for (icol = 0; icol < n->norm->Bl.ncols; icol++)
804 print_word("l");
805 print_word(".");
806 return(1);
807 }
808
809 static void
810 post_bl(DECL_ARGS)
811 {
812
813 switch (n->norm->Bl.type) {
814 case (LIST_enum):
815 n->norm->Bl.count = 0;
816 break;
817 case (LIST_column):
818 outflags |= MMAN_nl;
819 print_word(".TE");
820 break;
821 default:
822 break;
823 }
824 outflags |= MMAN_br;
825 }
826
827 static int
828 pre_br(DECL_ARGS)
829 {
830
831 outflags |= MMAN_br;
832 return(0);
833 }
834
835 static int
836 pre_bx(DECL_ARGS)
837 {
838
839 n = n->child;
840 if (n) {
841 print_word(n->string);
842 outflags &= ~MMAN_spc;
843 n = n->next;
844 }
845 print_word("BSD");
846 if (NULL == n)
847 return(0);
848 outflags &= ~MMAN_spc;
849 print_word("-");
850 outflags &= ~MMAN_spc;
851 print_word(n->string);
852 return(0);
853 }
854
855 static int
856 pre_dl(DECL_ARGS)
857 {
858
859 outflags |= MMAN_nl;
860 print_word(".RS 6n");
861 outflags |= MMAN_nl;
862 return(1);
863 }
864
865 static void
866 post_dl(DECL_ARGS)
867 {
868
869 outflags |= MMAN_nl;
870 print_word(".RE");
871 outflags |= MMAN_nl;
872 }
873
874 static int
875 pre_em(DECL_ARGS)
876 {
877
878 font_push('I');
879 return(1);
880 }
881
882 static void
883 post_eo(DECL_ARGS)
884 {
885
886 if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
887 outflags &= ~MMAN_spc;
888 }
889
890 static int
891 pre_fa(DECL_ARGS)
892 {
893
894 if (MDOC_Fa == n->tok)
895 n = n->child;
896
897 while (NULL != n) {
898 font_push('I');
899 print_node(m, n);
900 font_pop();
901 if (NULL != (n = n->next))
902 print_word(",");
903 }
904 return(0);
905 }
906
907 static void
908 post_fa(DECL_ARGS)
909 {
910
911 if (NULL != n->next && MDOC_Fa == n->next->tok)
912 print_word(",");
913 }
914
915 static int
916 pre_fd(DECL_ARGS)
917 {
918
919 pre_syn(n);
920 font_push('B');
921 return(1);
922 }
923
924 static void
925 post_fd(DECL_ARGS)
926 {
927
928 font_pop();
929 outflags |= MMAN_br;
930 }
931
932 static int
933 pre_fl(DECL_ARGS)
934 {
935
936 font_push('B');
937 print_word("-");
938 outflags &= ~MMAN_spc;
939 return(1);
940 }
941
942 static void
943 post_fl(DECL_ARGS)
944 {
945
946 font_pop();
947 if (0 == n->nchild && NULL != n->next &&
948 n->next->line == n->line)
949 outflags &= ~MMAN_spc;
950 }
951
952 static int
953 pre_fn(DECL_ARGS)
954 {
955
956 pre_syn(n);
957
958 n = n->child;
959 if (NULL == n)
960 return(0);
961
962 font_push('B');
963 print_node(m, n);
964 font_pop();
965 outflags &= ~MMAN_spc;
966 print_word("(");
967 outflags &= ~MMAN_spc;
968
969 n = n->next;
970 if (NULL != n)
971 pre_fa(m, n);
972 return(0);
973 }
974
975 static void
976 post_fn(DECL_ARGS)
977 {
978
979 print_word(")");
980 if (MDOC_SYNPRETTY & n->flags) {
981 print_word(";");
982 outflags |= MMAN_br;
983 }
984 }
985
986 static int
987 pre_fo(DECL_ARGS)
988 {
989
990 switch (n->type) {
991 case (MDOC_BLOCK):
992 pre_syn(n);
993 break;
994 case (MDOC_HEAD):
995 font_push('B');
996 break;
997 case (MDOC_BODY):
998 outflags &= ~MMAN_spc;
999 print_word("(");
1000 outflags &= ~MMAN_spc;
1001 break;
1002 default:
1003 break;
1004 }
1005 return(1);
1006 }
1007
1008 static void
1009 post_fo(DECL_ARGS)
1010 {
1011
1012 switch (n->type) {
1013 case (MDOC_HEAD):
1014 font_pop();
1015 break;
1016 case (MDOC_BODY):
1017 post_fn(m, n);
1018 break;
1019 default:
1020 break;
1021 }
1022 }
1023
1024 static int
1025 pre_ft(DECL_ARGS)
1026 {
1027
1028 pre_syn(n);
1029 font_push('I');
1030 return(1);
1031 }
1032
1033 static int
1034 pre_in(DECL_ARGS)
1035 {
1036
1037 if (MDOC_SYNPRETTY & n->flags) {
1038 pre_syn(n);
1039 font_push('B');
1040 print_word("#include <");
1041 outflags &= ~MMAN_spc;
1042 } else {
1043 print_word("<");
1044 outflags &= ~MMAN_spc;
1045 font_push('I');
1046 }
1047 return(1);
1048 }
1049
1050 static void
1051 post_in(DECL_ARGS)
1052 {
1053
1054 if (MDOC_SYNPRETTY & n->flags) {
1055 outflags &= ~MMAN_spc;
1056 print_word(">");
1057 font_pop();
1058 outflags |= MMAN_br;
1059 } else {
1060 font_pop();
1061 outflags &= ~MMAN_spc;
1062 print_word(">");
1063 }
1064 }
1065
1066 static int
1067 pre_it(DECL_ARGS)
1068 {
1069 const struct mdoc_node *bln;
1070
1071 switch (n->type) {
1072 case (MDOC_HEAD):
1073 outflags |= MMAN_nl;
1074 bln = n->parent->parent;
1075 switch (bln->norm->Bl.type) {
1076 case (LIST_item):
1077 if (bln->norm->Bl.comp)
1078 outflags |= MMAN_br;
1079 else
1080 outflags |= MMAN_sp;
1081 return(0);
1082 case (LIST_inset):
1083 /* FALLTHROUGH */
1084 case (LIST_diag):
1085 /* FALLTHROUGH */
1086 case (LIST_ohang):
1087 if (bln->norm->Bl.comp)
1088 outflags |= MMAN_br;
1089 else
1090 outflags |= MMAN_sp;
1091 if (bln->norm->Bl.type == LIST_diag)
1092 print_word(".B \"");
1093 else
1094 print_word(".R \"");
1095 outflags &= ~MMAN_spc;
1096 return(1);
1097 case (LIST_bullet):
1098 /* FALLTHROUGH */
1099 case (LIST_dash):
1100 /* FALLTHROUGH */
1101 case (LIST_hyphen):
1102 print_width(bln->norm->Bl.width, NULL, 0);
1103 outflags |= MMAN_nl;
1104 font_push('B');
1105 if (LIST_bullet == bln->norm->Bl.type)
1106 print_word("o");
1107 else
1108 print_word("-");
1109 font_pop();
1110 break;
1111 case (LIST_enum):
1112 print_width(bln->norm->Bl.width, NULL, 0);
1113 outflags |= MMAN_nl;
1114 print_count(&bln->norm->Bl.count);
1115 break;
1116 case (LIST_hang):
1117 print_width(bln->norm->Bl.width, n->child, 6);
1118 break;
1119 case (LIST_tag):
1120 print_width(bln->norm->Bl.width, NULL, 8);
1121 break;
1122 default:
1123 return(1);
1124 }
1125 outflags |= MMAN_nl;
1126 default:
1127 break;
1128 }
1129 return(1);
1130 }
1131
1132 static void
1133 post_it(DECL_ARGS)
1134 {
1135 const struct mdoc_node *bln;
1136
1137 bln = n->parent->parent;
1138
1139 switch (n->type) {
1140 case (MDOC_HEAD):
1141 switch (bln->norm->Bl.type) {
1142 case (LIST_diag):
1143 outflags &= ~MMAN_spc;
1144 print_word("\\ ");
1145 break;
1146 case (LIST_ohang):
1147 outflags |= MMAN_br;
1148 break;
1149 default:
1150 break;
1151 }
1152 break;
1153 case (MDOC_BODY):
1154 if (LIST_column == bln->norm->Bl.type &&
1155 NULL != n->next) {
1156 putchar('\t');
1157 outflags &= ~MMAN_spc;
1158 }
1159 break;
1160 default:
1161 break;
1162 }
1163 }
1164
1165 static void
1166 post_lb(DECL_ARGS)
1167 {
1168
1169 if (SEC_LIBRARY == n->sec)
1170 outflags |= MMAN_br;
1171 }
1172
1173 static int
1174 pre_lk(DECL_ARGS)
1175 {
1176 const struct mdoc_node *link, *descr;
1177
1178 if (NULL == (link = n->child))
1179 return(0);
1180
1181 if (NULL != (descr = link->next)) {
1182 font_push('I');
1183 while (NULL != descr) {
1184 print_word(descr->string);
1185 descr = descr->next;
1186 }
1187 print_word(":");
1188 font_pop();
1189 }
1190
1191 font_push('B');
1192 print_word(link->string);
1193 font_pop();
1194 return(0);
1195 }
1196
1197 static int
1198 pre_li(DECL_ARGS)
1199 {
1200
1201 font_push('R');
1202 return(1);
1203 }
1204
1205 static int
1206 pre_nm(DECL_ARGS)
1207 {
1208
1209 if (MDOC_BLOCK == n->type)
1210 pre_syn(n);
1211 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1212 return(1);
1213 if (NULL == n->child && NULL == m->name)
1214 return(0);
1215 font_push('B');
1216 if (NULL == n->child)
1217 print_word(m->name);
1218 return(1);
1219 }
1220
1221 static void
1222 post_nm(DECL_ARGS)
1223 {
1224
1225 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1226 return;
1227 font_pop();
1228 }
1229
1230 static int
1231 pre_no(DECL_ARGS)
1232 {
1233
1234 outflags |= MMAN_spc_force;
1235 return(1);
1236 }
1237
1238 static int
1239 pre_ns(DECL_ARGS)
1240 {
1241
1242 outflags &= ~MMAN_spc;
1243 return(0);
1244 }
1245
1246 static void
1247 post_pf(DECL_ARGS)
1248 {
1249
1250 outflags &= ~MMAN_spc;
1251 }
1252
1253 static int
1254 pre_pp(DECL_ARGS)
1255 {
1256
1257 outflags |= MMAN_nl;
1258 if (MDOC_It == n->parent->tok)
1259 print_word(".sp");
1260 else
1261 print_word(".PP");
1262 outflags |= MMAN_nl;
1263 return(0);
1264 }
1265
1266 static int
1267 pre_rs(DECL_ARGS)
1268 {
1269
1270 if (SEC_SEE_ALSO == n->sec) {
1271 outflags |= MMAN_nl;
1272 print_word(".PP");
1273 outflags |= MMAN_nl;
1274 }
1275 return(1);
1276 }
1277
1278 static int
1279 pre_sm(DECL_ARGS)
1280 {
1281
1282 assert(n->child && MDOC_TEXT == n->child->type);
1283 if (0 == strcmp("on", n->child->string))
1284 outflags |= MMAN_Sm | MMAN_spc;
1285 else
1286 outflags &= ~MMAN_Sm;
1287 return(0);
1288 }
1289
1290 static int
1291 pre_sp(DECL_ARGS)
1292 {
1293
1294 outflags |= MMAN_nl;
1295 print_word(".sp");
1296 return(1);
1297 }
1298
1299 static void
1300 post_sp(DECL_ARGS)
1301 {
1302
1303 outflags |= MMAN_nl;
1304 }
1305
1306 static int
1307 pre_sy(DECL_ARGS)
1308 {
1309
1310 font_push('B');
1311 return(1);
1312 }
1313
1314 static int
1315 pre_vt(DECL_ARGS)
1316 {
1317
1318 if (MDOC_SYNPRETTY & n->flags) {
1319 switch (n->type) {
1320 case (MDOC_BLOCK):
1321 pre_syn(n);
1322 return(1);
1323 case (MDOC_BODY):
1324 break;
1325 default:
1326 return(0);
1327 }
1328 }
1329 font_push('I');
1330 return(1);
1331 }
1332
1333 static void
1334 post_vt(DECL_ARGS)
1335 {
1336
1337 if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
1338 return;
1339 font_pop();
1340 }
1341
1342 static int
1343 pre_xr(DECL_ARGS)
1344 {
1345
1346 n = n->child;
1347 if (NULL == n)
1348 return(0);
1349 print_node(m, n);
1350 n = n->next;
1351 if (NULL == n)
1352 return(0);
1353 outflags &= ~MMAN_spc;
1354 print_word("(");
1355 print_node(m, n);
1356 print_word(")");
1357 return(0);
1358 }
1359
1360 static int
1361 pre_ux(DECL_ARGS)
1362 {
1363
1364 print_word(manacts[n->tok].prefix);
1365 if (NULL == n->child)
1366 return(0);
1367 outflags &= ~MMAN_spc;
1368 print_word("\\~");
1369 outflags &= ~MMAN_spc;
1370 return(1);
1371 }