]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_man.c
Minimal COMPATIBILITY cleanup:
[mandoc.git] / mdoc_man.c
1 /* $Id: mdoc_man.c,v 1.63 2014/04/20 19:40:13 schwarze Exp $ */
2 /*
3 * Copyright (c) 2011, 2012, 2013, 2014 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 "mandoc_aux.h"
27 #include "out.h"
28 #include "man.h"
29 #include "mdoc.h"
30 #include "main.h"
31
32 #define DECL_ARGS const struct mdoc_meta *meta, \
33 const struct mdoc_node *n
34
35 struct manact {
36 int (*cond)(DECL_ARGS); /* DON'T run actions */
37 int (*pre)(DECL_ARGS); /* pre-node action */
38 void (*post)(DECL_ARGS); /* post-node action */
39 const char *prefix; /* pre-node string constant */
40 const char *suffix; /* post-node string constant */
41 };
42
43 static int cond_body(DECL_ARGS);
44 static int cond_head(DECL_ARGS);
45 static void font_push(char);
46 static void font_pop(void);
47 static void mid_it(void);
48 static void post__t(DECL_ARGS);
49 static void post_bd(DECL_ARGS);
50 static void post_bf(DECL_ARGS);
51 static void post_bk(DECL_ARGS);
52 static void post_bl(DECL_ARGS);
53 static void post_dl(DECL_ARGS);
54 static void post_enc(DECL_ARGS);
55 static void post_eo(DECL_ARGS);
56 static void post_fa(DECL_ARGS);
57 static void post_fd(DECL_ARGS);
58 static void post_fl(DECL_ARGS);
59 static void post_fn(DECL_ARGS);
60 static void post_fo(DECL_ARGS);
61 static void post_font(DECL_ARGS);
62 static void post_in(DECL_ARGS);
63 static void post_it(DECL_ARGS);
64 static void post_lb(DECL_ARGS);
65 static void post_nm(DECL_ARGS);
66 static void post_percent(DECL_ARGS);
67 static void post_pf(DECL_ARGS);
68 static void post_sect(DECL_ARGS);
69 static void post_sp(DECL_ARGS);
70 static void post_vt(DECL_ARGS);
71 static int pre__t(DECL_ARGS);
72 static int pre_an(DECL_ARGS);
73 static int pre_ap(DECL_ARGS);
74 static int pre_bd(DECL_ARGS);
75 static int pre_bf(DECL_ARGS);
76 static int pre_bk(DECL_ARGS);
77 static int pre_bl(DECL_ARGS);
78 static int pre_br(DECL_ARGS);
79 static int pre_bx(DECL_ARGS);
80 static int pre_dl(DECL_ARGS);
81 static int pre_enc(DECL_ARGS);
82 static int pre_em(DECL_ARGS);
83 static int pre_fa(DECL_ARGS);
84 static int pre_fd(DECL_ARGS);
85 static int pre_fl(DECL_ARGS);
86 static int pre_fn(DECL_ARGS);
87 static int pre_fo(DECL_ARGS);
88 static int pre_ft(DECL_ARGS);
89 static int pre_in(DECL_ARGS);
90 static int pre_it(DECL_ARGS);
91 static int pre_lk(DECL_ARGS);
92 static int pre_li(DECL_ARGS);
93 static int pre_ll(DECL_ARGS);
94 static int pre_nm(DECL_ARGS);
95 static int pre_no(DECL_ARGS);
96 static int pre_ns(DECL_ARGS);
97 static int pre_pp(DECL_ARGS);
98 static int pre_rs(DECL_ARGS);
99 static int pre_sm(DECL_ARGS);
100 static int pre_sp(DECL_ARGS);
101 static int pre_sect(DECL_ARGS);
102 static int pre_sy(DECL_ARGS);
103 static void pre_syn(const struct mdoc_node *);
104 static int pre_vt(DECL_ARGS);
105 static int pre_ux(DECL_ARGS);
106 static int pre_xr(DECL_ARGS);
107 static void print_word(const char *);
108 static void print_line(const char *, int);
109 static void print_block(const char *, int);
110 static void print_offs(const char *);
111 static void print_width(const char *,
112 const struct mdoc_node *, size_t);
113 static void print_count(int *);
114 static void print_node(DECL_ARGS);
115
116 static const struct manact manacts[MDOC_MAX + 1] = {
117 { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
118 { NULL, NULL, NULL, NULL, NULL }, /* Dd */
119 { NULL, NULL, NULL, NULL, NULL }, /* Dt */
120 { NULL, NULL, NULL, NULL, NULL }, /* Os */
121 { NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
122 { NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
123 { NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
124 { cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
125 { cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
126 { cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
127 { NULL, NULL, NULL, NULL, NULL }, /* Ed */
128 { cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
129 { NULL, NULL, NULL, NULL, NULL }, /* El */
130 { NULL, pre_it, post_it, NULL, NULL }, /* It */
131 { NULL, pre_em, post_font, NULL, NULL }, /* Ad */
132 { NULL, pre_an, NULL, NULL, NULL }, /* An */
133 { NULL, pre_em, post_font, NULL, NULL }, /* Ar */
134 { NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
135 { NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
136 { NULL, pre_li, post_font, NULL, NULL }, /* Dv */
137 { NULL, pre_li, post_font, NULL, NULL }, /* Er */
138 { NULL, pre_li, post_font, NULL, NULL }, /* Ev */
139 { NULL, pre_enc, post_enc, "The \\fB",
140 "\\fP\nutility exits 0 on success, and >0 if an error occurs."
141 }, /* Ex */
142 { NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
143 { NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
144 { NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
145 { NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
146 { NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
147 { NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
148 { NULL, pre_in, post_in, NULL, NULL }, /* In */
149 { NULL, pre_li, post_font, NULL, NULL }, /* Li */
150 { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
151 { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
152 { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
153 { NULL, NULL, NULL, NULL, NULL }, /* Ot */
154 { NULL, pre_em, post_font, NULL, NULL }, /* Pa */
155 { NULL, pre_enc, post_enc, "The \\fB",
156 "\\fP\nfunction returns the value 0 if successful;\n"
157 "otherwise the value -1 is returned and the global\n"
158 "variable \\fIerrno\\fP is set to indicate the error."
159 }, /* Rv */
160 { NULL, NULL, NULL, NULL, NULL }, /* St */
161 { NULL, pre_em, post_font, NULL, NULL }, /* Va */
162 { NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
163 { NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
164 { NULL, NULL, post_percent, NULL, NULL }, /* %A */
165 { NULL, pre_em, post_percent, NULL, NULL }, /* %B */
166 { NULL, NULL, post_percent, NULL, NULL }, /* %D */
167 { NULL, pre_em, post_percent, NULL, NULL }, /* %I */
168 { NULL, pre_em, post_percent, NULL, NULL }, /* %J */
169 { NULL, NULL, post_percent, NULL, NULL }, /* %N */
170 { NULL, NULL, post_percent, NULL, NULL }, /* %O */
171 { NULL, NULL, post_percent, NULL, NULL }, /* %P */
172 { NULL, NULL, post_percent, NULL, NULL }, /* %R */
173 { NULL, pre__t, post__t, NULL, NULL }, /* %T */
174 { NULL, NULL, post_percent, NULL, NULL }, /* %V */
175 { NULL, NULL, NULL, NULL, NULL }, /* Ac */
176 { cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
177 { cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
178 { NULL, NULL, NULL, NULL, NULL }, /* At */
179 { NULL, NULL, NULL, NULL, NULL }, /* Bc */
180 { NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
181 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
182 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
183 { NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
184 { NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
185 { NULL, NULL, NULL, NULL, NULL }, /* Db */
186 { NULL, NULL, NULL, NULL, NULL }, /* Dc */
187 { cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Do */
188 { cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Dq */
189 { NULL, NULL, NULL, NULL, NULL }, /* Ec */
190 { NULL, NULL, NULL, NULL, NULL }, /* Ef */
191 { NULL, pre_em, post_font, NULL, NULL }, /* Em */
192 { NULL, NULL, post_eo, NULL, NULL }, /* Eo */
193 { NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
194 { NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
195 { NULL, pre_no, NULL, NULL, NULL }, /* No */
196 { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
197 { NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
198 { NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
199 { NULL, NULL, NULL, NULL, NULL }, /* Pc */
200 { NULL, NULL, post_pf, NULL, NULL }, /* Pf */
201 { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
202 { cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
203 { NULL, NULL, NULL, NULL, NULL }, /* Qc */
204 { cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
205 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
206 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
207 { NULL, NULL, NULL, NULL, NULL }, /* Re */
208 { cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
209 { NULL, NULL, NULL, NULL, NULL }, /* Sc */
210 { cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
211 { cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
212 { NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
213 { NULL, pre_em, post_font, NULL, NULL }, /* Sx */
214 { NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
215 { NULL, pre_li, post_font, NULL, NULL }, /* Tn */
216 { NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
217 { NULL, NULL, NULL, NULL, NULL }, /* Xc */
218 { NULL, NULL, NULL, NULL, NULL }, /* Xo */
219 { NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
220 { NULL, NULL, NULL, NULL, NULL }, /* Fc */
221 { cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
222 { NULL, NULL, NULL, NULL, NULL }, /* Oc */
223 { NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
224 { NULL, NULL, NULL, NULL, NULL }, /* Ek */
225 { NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
226 { NULL, NULL, NULL, NULL, NULL }, /* Hf */
227 { NULL, NULL, NULL, NULL, NULL }, /* Fr */
228 { NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
229 { NULL, NULL, post_lb, NULL, NULL }, /* Lb */
230 { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
231 { NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
232 { NULL, pre_em, post_font, NULL, NULL }, /* Mt */
233 { cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
234 { cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
235 { NULL, NULL, NULL, NULL, NULL }, /* Brc */
236 { NULL, NULL, post_percent, NULL, NULL }, /* %C */
237 { NULL, NULL, NULL, NULL, NULL }, /* Es */
238 { NULL, NULL, NULL, NULL, NULL }, /* En */
239 { NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
240 { NULL, NULL, post_percent, NULL, NULL }, /* %Q */
241 { NULL, pre_br, NULL, NULL, NULL }, /* br */
242 { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
243 { NULL, NULL, post_percent, NULL, NULL }, /* %U */
244 { NULL, NULL, NULL, NULL, NULL }, /* Ta */
245 { NULL, pre_ll, post_sp, NULL, NULL }, /* ll */
246 { NULL, NULL, NULL, NULL, NULL }, /* ROOT */
247 };
248
249 static int outflags;
250 #define MMAN_spc (1 << 0) /* blank character before next word */
251 #define MMAN_spc_force (1 << 1) /* even before trailing punctuation */
252 #define MMAN_nl (1 << 2) /* break man(7) code line */
253 #define MMAN_br (1 << 3) /* break output line */
254 #define MMAN_sp (1 << 4) /* insert a blank output line */
255 #define MMAN_PP (1 << 5) /* reset indentation etc. */
256 #define MMAN_Sm (1 << 6) /* horizontal spacing mode */
257 #define MMAN_Bk (1 << 7) /* word keep mode */
258 #define MMAN_Bk_susp (1 << 8) /* suspend this (after a macro) */
259 #define MMAN_An_split (1 << 9) /* author mode is "split" */
260 #define MMAN_An_nosplit (1 << 10) /* author mode is "nosplit" */
261 #define MMAN_PD (1 << 11) /* inter-paragraph spacing disabled */
262 #define MMAN_nbrword (1 << 12) /* do not break the next word */
263
264 #define BL_STACK_MAX 32
265
266 static size_t Bl_stack[BL_STACK_MAX]; /* offsets [chars] */
267 static int Bl_stack_post[BL_STACK_MAX]; /* add final .RE */
268 static int Bl_stack_len; /* number of nested Bl blocks */
269 static int TPremain; /* characters before tag is full */
270
271 static struct {
272 char *head;
273 char *tail;
274 size_t size;
275 } fontqueue;
276
277
278 static void
279 font_push(char newfont)
280 {
281
282 if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
283 fontqueue.size += 8;
284 fontqueue.head = mandoc_realloc(fontqueue.head,
285 fontqueue.size);
286 }
287 *fontqueue.tail = newfont;
288 print_word("");
289 printf("\\f");
290 putchar(newfont);
291 outflags &= ~MMAN_spc;
292 }
293
294 static void
295 font_pop(void)
296 {
297
298 if (fontqueue.tail > fontqueue.head)
299 fontqueue.tail--;
300 outflags &= ~MMAN_spc;
301 print_word("");
302 printf("\\f");
303 putchar(*fontqueue.tail);
304 }
305
306 static void
307 print_word(const char *s)
308 {
309
310 if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
311 /*
312 * If we need a newline, print it now and start afresh.
313 */
314 if (MMAN_PP & outflags) {
315 if (MMAN_sp & outflags) {
316 if (MMAN_PD & outflags) {
317 printf("\n.PD");
318 outflags &= ~MMAN_PD;
319 }
320 } else if ( ! (MMAN_PD & outflags)) {
321 printf("\n.PD 0");
322 outflags |= MMAN_PD;
323 }
324 printf("\n.PP\n");
325 } else if (MMAN_sp & outflags)
326 printf("\n.sp\n");
327 else if (MMAN_br & outflags)
328 printf("\n.br\n");
329 else if (MMAN_nl & outflags)
330 putchar('\n');
331 outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
332 if (1 == TPremain)
333 printf(".br\n");
334 TPremain = 0;
335 } else if (MMAN_spc & outflags) {
336 /*
337 * If we need a space, only print it if
338 * (1) it is forced by `No' or
339 * (2) what follows is not terminating punctuation or
340 * (3) what follows is longer than one character.
341 */
342 if (MMAN_spc_force & outflags || '\0' == s[0] ||
343 NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
344 if (MMAN_Bk & outflags &&
345 ! (MMAN_Bk_susp & outflags))
346 putchar('\\');
347 putchar(' ');
348 if (TPremain)
349 TPremain--;
350 }
351 }
352
353 /*
354 * Reassign needing space if we're not following opening
355 * punctuation.
356 */
357 if (MMAN_Sm & outflags && ('\0' == s[0] ||
358 (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
359 outflags |= MMAN_spc;
360 else
361 outflags &= ~MMAN_spc;
362 outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
363
364 for ( ; *s; s++) {
365 switch (*s) {
366 case ASCII_NBRSP:
367 printf("\\ ");
368 break;
369 case ASCII_HYPH:
370 putchar('-');
371 break;
372 case ASCII_BREAK:
373 printf("\\:");
374 break;
375 case ' ':
376 if (MMAN_nbrword & outflags) {
377 printf("\\ ");
378 break;
379 }
380 /* FALLTHROUGH */
381 default:
382 putchar((unsigned char)*s);
383 break;
384 }
385 if (TPremain)
386 TPremain--;
387 }
388 outflags &= ~MMAN_nbrword;
389 }
390
391 static void
392 print_line(const char *s, int newflags)
393 {
394
395 outflags &= ~MMAN_br;
396 outflags |= MMAN_nl;
397 print_word(s);
398 outflags |= newflags;
399 }
400
401 static void
402 print_block(const char *s, int newflags)
403 {
404
405 outflags &= ~MMAN_PP;
406 if (MMAN_sp & outflags) {
407 outflags &= ~(MMAN_sp | MMAN_br);
408 if (MMAN_PD & outflags) {
409 print_line(".PD", 0);
410 outflags &= ~MMAN_PD;
411 }
412 } else if (! (MMAN_PD & outflags))
413 print_line(".PD 0", MMAN_PD);
414 outflags |= MMAN_nl;
415 print_word(s);
416 outflags |= MMAN_Bk_susp | newflags;
417 }
418
419 static void
420 print_offs(const char *v)
421 {
422 char buf[24];
423 struct roffsu su;
424 size_t sz;
425
426 print_line(".RS", MMAN_Bk_susp);
427
428 /* Convert v into a number (of characters). */
429 if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
430 sz = 0;
431 else if (0 == strcmp(v, "indent"))
432 sz = 6;
433 else if (0 == strcmp(v, "indent-two"))
434 sz = 12;
435 else if (a2roffsu(v, &su, SCALE_MAX)) {
436 if (SCALE_EN == su.unit)
437 sz = su.scale;
438 else {
439 /*
440 * XXX
441 * If we are inside an enclosing list,
442 * there is no easy way to add the two
443 * indentations because they are provided
444 * in terms of different units.
445 */
446 print_word(v);
447 outflags |= MMAN_nl;
448 return;
449 }
450 } else
451 sz = strlen(v);
452
453 /*
454 * We are inside an enclosing list.
455 * Add the two indentations.
456 */
457 if (Bl_stack_len)
458 sz += Bl_stack[Bl_stack_len - 1];
459
460 (void)snprintf(buf, sizeof(buf), "%zun", sz);
461 print_word(buf);
462 outflags |= MMAN_nl;
463 }
464
465 /*
466 * Set up the indentation for a list item; used from pre_it().
467 */
468 static void
469 print_width(const char *v, const struct mdoc_node *child, size_t defsz)
470 {
471 char buf[24];
472 struct roffsu su;
473 size_t sz, chsz;
474 int numeric, remain;
475
476 numeric = 1;
477 remain = 0;
478
479 /* Convert v into a number (of characters). */
480 if (NULL == v)
481 sz = defsz;
482 else if (a2roffsu(v, &su, SCALE_MAX)) {
483 if (SCALE_EN == su.unit)
484 sz = su.scale;
485 else {
486 sz = 0;
487 numeric = 0;
488 }
489 } else
490 sz = strlen(v);
491
492 /* XXX Rough estimation, might have multiple parts. */
493 chsz = (NULL != child && MDOC_TEXT == child->type) ?
494 strlen(child->string) : 0;
495
496 /* Maybe we are inside an enclosing list? */
497 mid_it();
498
499 /*
500 * Save our own indentation,
501 * such that child lists can use it.
502 */
503 Bl_stack[Bl_stack_len++] = sz + 2;
504
505 /* Set up the current list. */
506 if (defsz && chsz > sz)
507 print_block(".HP", 0);
508 else {
509 print_block(".TP", 0);
510 remain = sz + 2;
511 }
512 if (numeric) {
513 (void)snprintf(buf, sizeof(buf), "%zun", sz + 2);
514 print_word(buf);
515 } else
516 print_word(v);
517 TPremain = remain;
518 }
519
520 static void
521 print_count(int *count)
522 {
523 char buf[24];
524
525 (void)snprintf(buf, sizeof(buf), "%d.", ++*count);
526 print_word(buf);
527 }
528
529 void
530 man_man(void *arg, const struct man *man)
531 {
532
533 /*
534 * Dump the keep buffer.
535 * We're guaranteed by now that this exists (is non-NULL).
536 * Flush stdout afterward, just in case.
537 */
538 fputs(mparse_getkeep(man_mparse(man)), stdout);
539 fflush(stdout);
540 }
541
542 void
543 man_mdoc(void *arg, const struct mdoc *mdoc)
544 {
545 const struct mdoc_meta *meta;
546 const struct mdoc_node *n;
547
548 meta = mdoc_meta(mdoc);
549 n = mdoc_node(mdoc);
550
551 printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
552 meta->title, meta->msec, meta->date,
553 meta->os, meta->vol);
554
555 /* Disable hyphenation and if nroff, disable justification. */
556 printf(".nh\n.if n .ad l");
557
558 outflags = MMAN_nl | MMAN_Sm;
559 if (0 == fontqueue.size) {
560 fontqueue.size = 8;
561 fontqueue.head = fontqueue.tail = mandoc_malloc(8);
562 *fontqueue.tail = 'R';
563 }
564 print_node(meta, n);
565 putchar('\n');
566 }
567
568 static void
569 print_node(DECL_ARGS)
570 {
571 const struct mdoc_node *sub;
572 const struct manact *act;
573 int cond, do_sub;
574
575 /*
576 * Break the line if we were parsed subsequent the current node.
577 * This makes the page structure be more consistent.
578 */
579 if (MMAN_spc & outflags && MDOC_LINE & n->flags)
580 outflags |= MMAN_nl;
581
582 act = NULL;
583 cond = 0;
584 do_sub = 1;
585
586 if (MDOC_TEXT == n->type) {
587 /*
588 * Make sure that we don't happen to start with a
589 * control character at the start of a line.
590 */
591 if (MMAN_nl & outflags &&
592 ('.' == *n->string || '\'' == *n->string)) {
593 print_word("");
594 printf("\\&");
595 outflags &= ~MMAN_spc;
596 }
597 print_word(n->string);
598 } else {
599 /*
600 * Conditionally run the pre-node action handler for a
601 * node.
602 */
603 act = manacts + n->tok;
604 cond = NULL == act->cond || (*act->cond)(meta, n);
605 if (cond && act->pre && ENDBODY_NOT == n->end)
606 do_sub = (*act->pre)(meta, n);
607 }
608
609 /*
610 * Conditionally run all child nodes.
611 * Note that this iterates over children instead of using
612 * recursion. This prevents unnecessary depth in the stack.
613 */
614 if (do_sub)
615 for (sub = n->child; sub; sub = sub->next)
616 print_node(meta, sub);
617
618 /*
619 * Lastly, conditionally run the post-node handler.
620 */
621 if (MDOC_ENDED & n->flags)
622 return;
623
624 if (cond && act->post)
625 (*act->post)(meta, n);
626
627 if (ENDBODY_NOT != n->end)
628 n->pending->flags |= MDOC_ENDED;
629
630 if (ENDBODY_NOSPACE == n->end)
631 outflags &= ~(MMAN_spc | MMAN_nl);
632 }
633
634 static int
635 cond_head(DECL_ARGS)
636 {
637
638 return(MDOC_HEAD == n->type);
639 }
640
641 static int
642 cond_body(DECL_ARGS)
643 {
644
645 return(MDOC_BODY == n->type);
646 }
647
648 static int
649 pre_enc(DECL_ARGS)
650 {
651 const char *prefix;
652
653 prefix = manacts[n->tok].prefix;
654 if (NULL == prefix)
655 return(1);
656 print_word(prefix);
657 outflags &= ~MMAN_spc;
658 return(1);
659 }
660
661 static void
662 post_enc(DECL_ARGS)
663 {
664 const char *suffix;
665
666 suffix = manacts[n->tok].suffix;
667 if (NULL == suffix)
668 return;
669 outflags &= ~(MMAN_spc | MMAN_nl);
670 print_word(suffix);
671 }
672
673 static void
674 post_font(DECL_ARGS)
675 {
676
677 font_pop();
678 }
679
680 static void
681 post_percent(DECL_ARGS)
682 {
683
684 if (pre_em == manacts[n->tok].pre)
685 font_pop();
686 if (n->next) {
687 print_word(",");
688 if (n->prev && n->prev->tok == n->tok &&
689 n->next->tok == n->tok)
690 print_word("and");
691 } else {
692 print_word(".");
693 outflags |= MMAN_nl;
694 }
695 }
696
697 static int
698 pre__t(DECL_ARGS)
699 {
700
701 if (n->parent && MDOC_Rs == n->parent->tok &&
702 n->parent->norm->Rs.quote_T) {
703 print_word("");
704 putchar('\"');
705 outflags &= ~MMAN_spc;
706 } else
707 font_push('I');
708 return(1);
709 }
710
711 static void
712 post__t(DECL_ARGS)
713 {
714
715 if (n->parent && MDOC_Rs == n->parent->tok &&
716 n->parent->norm->Rs.quote_T) {
717 outflags &= ~MMAN_spc;
718 print_word("");
719 putchar('\"');
720 } else
721 font_pop();
722 post_percent(meta, n);
723 }
724
725 /*
726 * Print before a section header.
727 */
728 static int
729 pre_sect(DECL_ARGS)
730 {
731
732 if (MDOC_HEAD == n->type) {
733 outflags |= MMAN_sp;
734 print_block(manacts[n->tok].prefix, 0);
735 print_word("");
736 putchar('\"');
737 outflags &= ~MMAN_spc;
738 }
739 return(1);
740 }
741
742 /*
743 * Print subsequent a section header.
744 */
745 static void
746 post_sect(DECL_ARGS)
747 {
748
749 if (MDOC_HEAD != n->type)
750 return;
751 outflags &= ~MMAN_spc;
752 print_word("");
753 putchar('\"');
754 outflags |= MMAN_nl;
755 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
756 outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
757 }
758
759 /* See mdoc_term.c, synopsis_pre() for comments. */
760 static void
761 pre_syn(const struct mdoc_node *n)
762 {
763
764 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
765 return;
766
767 if (n->prev->tok == n->tok &&
768 MDOC_Ft != n->tok &&
769 MDOC_Fo != n->tok &&
770 MDOC_Fn != n->tok) {
771 outflags |= MMAN_br;
772 return;
773 }
774
775 switch (n->prev->tok) {
776 case MDOC_Fd:
777 /* FALLTHROUGH */
778 case MDOC_Fn:
779 /* FALLTHROUGH */
780 case MDOC_Fo:
781 /* FALLTHROUGH */
782 case MDOC_In:
783 /* FALLTHROUGH */
784 case MDOC_Vt:
785 outflags |= MMAN_sp;
786 break;
787 case MDOC_Ft:
788 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
789 outflags |= MMAN_sp;
790 break;
791 }
792 /* FALLTHROUGH */
793 default:
794 outflags |= MMAN_br;
795 break;
796 }
797 }
798
799 static int
800 pre_an(DECL_ARGS)
801 {
802
803 switch (n->norm->An.auth) {
804 case AUTH_split:
805 outflags &= ~MMAN_An_nosplit;
806 outflags |= MMAN_An_split;
807 return(0);
808 case AUTH_nosplit:
809 outflags &= ~MMAN_An_split;
810 outflags |= MMAN_An_nosplit;
811 return(0);
812 default:
813 if (MMAN_An_split & outflags)
814 outflags |= MMAN_br;
815 else if (SEC_AUTHORS == n->sec &&
816 ! (MMAN_An_nosplit & outflags))
817 outflags |= MMAN_An_split;
818 return(1);
819 }
820 }
821
822 static int
823 pre_ap(DECL_ARGS)
824 {
825
826 outflags &= ~MMAN_spc;
827 print_word("'");
828 outflags &= ~MMAN_spc;
829 return(0);
830 }
831
832 static int
833 pre_bd(DECL_ARGS)
834 {
835
836 outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
837
838 if (DISP_unfilled == n->norm->Bd.type ||
839 DISP_literal == n->norm->Bd.type)
840 print_line(".nf", 0);
841 if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
842 outflags |= MMAN_sp;
843 print_offs(n->norm->Bd.offs);
844 return(1);
845 }
846
847 static void
848 post_bd(DECL_ARGS)
849 {
850
851 /* Close out this display. */
852 print_line(".RE", MMAN_nl);
853 if (DISP_unfilled == n->norm->Bd.type ||
854 DISP_literal == n->norm->Bd.type)
855 print_line(".fi", MMAN_nl);
856
857 /* Maybe we are inside an enclosing list? */
858 if (NULL != n->parent->next)
859 mid_it();
860 }
861
862 static int
863 pre_bf(DECL_ARGS)
864 {
865
866 switch (n->type) {
867 case MDOC_BLOCK:
868 return(1);
869 case MDOC_BODY:
870 break;
871 default:
872 return(0);
873 }
874 switch (n->norm->Bf.font) {
875 case FONT_Em:
876 font_push('I');
877 break;
878 case FONT_Sy:
879 font_push('B');
880 break;
881 default:
882 font_push('R');
883 break;
884 }
885 return(1);
886 }
887
888 static void
889 post_bf(DECL_ARGS)
890 {
891
892 if (MDOC_BODY == n->type)
893 font_pop();
894 }
895
896 static int
897 pre_bk(DECL_ARGS)
898 {
899
900 switch (n->type) {
901 case MDOC_BLOCK:
902 return(1);
903 case MDOC_BODY:
904 outflags |= MMAN_Bk;
905 return(1);
906 default:
907 return(0);
908 }
909 }
910
911 static void
912 post_bk(DECL_ARGS)
913 {
914
915 if (MDOC_BODY == n->type)
916 outflags &= ~MMAN_Bk;
917 }
918
919 static int
920 pre_bl(DECL_ARGS)
921 {
922 size_t icol;
923
924 /*
925 * print_offs() will increase the -offset to account for
926 * a possible enclosing .It, but any enclosed .It blocks
927 * just nest and do not add up their indentation.
928 */
929 if (n->norm->Bl.offs) {
930 print_offs(n->norm->Bl.offs);
931 Bl_stack[Bl_stack_len++] = 0;
932 }
933
934 switch (n->norm->Bl.type) {
935 case LIST_enum:
936 n->norm->Bl.count = 0;
937 return(1);
938 case LIST_column:
939 break;
940 default:
941 return(1);
942 }
943
944 print_line(".TS", MMAN_nl);
945 for (icol = 0; icol < n->norm->Bl.ncols; icol++)
946 print_word("l");
947 print_word(".");
948 outflags |= MMAN_nl;
949 return(1);
950 }
951
952 static void
953 post_bl(DECL_ARGS)
954 {
955
956 switch (n->norm->Bl.type) {
957 case LIST_column:
958 print_line(".TE", 0);
959 break;
960 case LIST_enum:
961 n->norm->Bl.count = 0;
962 break;
963 default:
964 break;
965 }
966
967 if (n->norm->Bl.offs) {
968 print_line(".RE", MMAN_nl);
969 assert(Bl_stack_len);
970 Bl_stack_len--;
971 assert(0 == Bl_stack[Bl_stack_len]);
972 } else {
973 outflags |= MMAN_PP | MMAN_nl;
974 outflags &= ~(MMAN_sp | MMAN_br);
975 }
976
977 /* Maybe we are inside an enclosing list? */
978 if (NULL != n->parent->next)
979 mid_it();
980
981 }
982
983 static int
984 pre_br(DECL_ARGS)
985 {
986
987 outflags |= MMAN_br;
988 return(0);
989 }
990
991 static int
992 pre_bx(DECL_ARGS)
993 {
994
995 n = n->child;
996 if (n) {
997 print_word(n->string);
998 outflags &= ~MMAN_spc;
999 n = n->next;
1000 }
1001 print_word("BSD");
1002 if (NULL == n)
1003 return(0);
1004 outflags &= ~MMAN_spc;
1005 print_word("-");
1006 outflags &= ~MMAN_spc;
1007 print_word(n->string);
1008 return(0);
1009 }
1010
1011 static int
1012 pre_dl(DECL_ARGS)
1013 {
1014
1015 print_offs("6n");
1016 return(1);
1017 }
1018
1019 static void
1020 post_dl(DECL_ARGS)
1021 {
1022
1023 print_line(".RE", MMAN_nl);
1024
1025 /* Maybe we are inside an enclosing list? */
1026 if (NULL != n->parent->next)
1027 mid_it();
1028 }
1029
1030 static int
1031 pre_em(DECL_ARGS)
1032 {
1033
1034 font_push('I');
1035 return(1);
1036 }
1037
1038 static void
1039 post_eo(DECL_ARGS)
1040 {
1041
1042 if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
1043 outflags &= ~MMAN_spc;
1044 }
1045
1046 static int
1047 pre_fa(DECL_ARGS)
1048 {
1049 int am_Fa;
1050
1051 am_Fa = MDOC_Fa == n->tok;
1052
1053 if (am_Fa)
1054 n = n->child;
1055
1056 while (NULL != n) {
1057 font_push('I');
1058 if (am_Fa || MDOC_SYNPRETTY & n->flags)
1059 outflags |= MMAN_nbrword;
1060 print_node(meta, n);
1061 font_pop();
1062 if (NULL != (n = n->next))
1063 print_word(",");
1064 }
1065 return(0);
1066 }
1067
1068 static void
1069 post_fa(DECL_ARGS)
1070 {
1071
1072 if (NULL != n->next && MDOC_Fa == n->next->tok)
1073 print_word(",");
1074 }
1075
1076 static int
1077 pre_fd(DECL_ARGS)
1078 {
1079
1080 pre_syn(n);
1081 font_push('B');
1082 return(1);
1083 }
1084
1085 static void
1086 post_fd(DECL_ARGS)
1087 {
1088
1089 font_pop();
1090 outflags |= MMAN_br;
1091 }
1092
1093 static int
1094 pre_fl(DECL_ARGS)
1095 {
1096
1097 font_push('B');
1098 print_word("\\-");
1099 outflags &= ~MMAN_spc;
1100 return(1);
1101 }
1102
1103 static void
1104 post_fl(DECL_ARGS)
1105 {
1106
1107 font_pop();
1108 if (0 == n->nchild && NULL != n->next &&
1109 n->next->line == n->line)
1110 outflags &= ~MMAN_spc;
1111 }
1112
1113 static int
1114 pre_fn(DECL_ARGS)
1115 {
1116
1117 pre_syn(n);
1118
1119 n = n->child;
1120 if (NULL == n)
1121 return(0);
1122
1123 if (MDOC_SYNPRETTY & n->flags)
1124 print_block(".HP 4n", MMAN_nl);
1125
1126 font_push('B');
1127 print_node(meta, n);
1128 font_pop();
1129 outflags &= ~MMAN_spc;
1130 print_word("(");
1131 outflags &= ~MMAN_spc;
1132
1133 n = n->next;
1134 if (NULL != n)
1135 pre_fa(meta, n);
1136 return(0);
1137 }
1138
1139 static void
1140 post_fn(DECL_ARGS)
1141 {
1142
1143 print_word(")");
1144 if (MDOC_SYNPRETTY & n->flags) {
1145 print_word(";");
1146 outflags |= MMAN_PP;
1147 }
1148 }
1149
1150 static int
1151 pre_fo(DECL_ARGS)
1152 {
1153
1154 switch (n->type) {
1155 case MDOC_BLOCK:
1156 pre_syn(n);
1157 break;
1158 case MDOC_HEAD:
1159 if (MDOC_SYNPRETTY & n->flags)
1160 print_block(".HP 4n", MMAN_nl);
1161 font_push('B');
1162 break;
1163 case MDOC_BODY:
1164 outflags &= ~MMAN_spc;
1165 print_word("(");
1166 outflags &= ~MMAN_spc;
1167 break;
1168 default:
1169 break;
1170 }
1171 return(1);
1172 }
1173
1174 static void
1175 post_fo(DECL_ARGS)
1176 {
1177
1178 switch (n->type) {
1179 case MDOC_HEAD:
1180 font_pop();
1181 break;
1182 case MDOC_BODY:
1183 post_fn(meta, n);
1184 break;
1185 default:
1186 break;
1187 }
1188 }
1189
1190 static int
1191 pre_ft(DECL_ARGS)
1192 {
1193
1194 pre_syn(n);
1195 font_push('I');
1196 return(1);
1197 }
1198
1199 static int
1200 pre_in(DECL_ARGS)
1201 {
1202
1203 if (MDOC_SYNPRETTY & n->flags) {
1204 pre_syn(n);
1205 font_push('B');
1206 print_word("#include <");
1207 outflags &= ~MMAN_spc;
1208 } else {
1209 print_word("<");
1210 outflags &= ~MMAN_spc;
1211 font_push('I');
1212 }
1213 return(1);
1214 }
1215
1216 static void
1217 post_in(DECL_ARGS)
1218 {
1219
1220 if (MDOC_SYNPRETTY & n->flags) {
1221 outflags &= ~MMAN_spc;
1222 print_word(">");
1223 font_pop();
1224 outflags |= MMAN_br;
1225 } else {
1226 font_pop();
1227 outflags &= ~MMAN_spc;
1228 print_word(">");
1229 }
1230 }
1231
1232 static int
1233 pre_it(DECL_ARGS)
1234 {
1235 const struct mdoc_node *bln;
1236
1237 switch (n->type) {
1238 case MDOC_HEAD:
1239 outflags |= MMAN_PP | MMAN_nl;
1240 bln = n->parent->parent;
1241 if (0 == bln->norm->Bl.comp ||
1242 (NULL == n->parent->prev &&
1243 NULL == bln->parent->prev))
1244 outflags |= MMAN_sp;
1245 outflags &= ~MMAN_br;
1246 switch (bln->norm->Bl.type) {
1247 case LIST_item:
1248 return(0);
1249 case LIST_inset:
1250 /* FALLTHROUGH */
1251 case LIST_diag:
1252 /* FALLTHROUGH */
1253 case LIST_ohang:
1254 if (bln->norm->Bl.type == LIST_diag)
1255 print_line(".B \"", 0);
1256 else
1257 print_line(".R \"", 0);
1258 outflags &= ~MMAN_spc;
1259 return(1);
1260 case LIST_bullet:
1261 /* FALLTHROUGH */
1262 case LIST_dash:
1263 /* FALLTHROUGH */
1264 case LIST_hyphen:
1265 print_width(bln->norm->Bl.width, NULL, 0);
1266 TPremain = 0;
1267 outflags |= MMAN_nl;
1268 font_push('B');
1269 if (LIST_bullet == bln->norm->Bl.type)
1270 print_word("o");
1271 else
1272 print_word("-");
1273 font_pop();
1274 break;
1275 case LIST_enum:
1276 print_width(bln->norm->Bl.width, NULL, 0);
1277 TPremain = 0;
1278 outflags |= MMAN_nl;
1279 print_count(&bln->norm->Bl.count);
1280 break;
1281 case LIST_hang:
1282 print_width(bln->norm->Bl.width, n->child, 6);
1283 TPremain = 0;
1284 break;
1285 case LIST_tag:
1286 print_width(bln->norm->Bl.width, n->child, 0);
1287 putchar('\n');
1288 outflags &= ~MMAN_spc;
1289 return(1);
1290 default:
1291 return(1);
1292 }
1293 outflags |= MMAN_nl;
1294 default:
1295 break;
1296 }
1297 return(1);
1298 }
1299
1300 /*
1301 * This function is called after closing out an indented block.
1302 * If we are inside an enclosing list, restore its indentation.
1303 */
1304 static void
1305 mid_it(void)
1306 {
1307 char buf[24];
1308
1309 /* Nothing to do outside a list. */
1310 if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1311 return;
1312
1313 /* The indentation has already been set up. */
1314 if (Bl_stack_post[Bl_stack_len - 1])
1315 return;
1316
1317 /* Restore the indentation of the enclosing list. */
1318 print_line(".RS", MMAN_Bk_susp);
1319 (void)snprintf(buf, sizeof(buf), "%zun",
1320 Bl_stack[Bl_stack_len - 1]);
1321 print_word(buf);
1322
1323 /* Remeber to close out this .RS block later. */
1324 Bl_stack_post[Bl_stack_len - 1] = 1;
1325 }
1326
1327 static void
1328 post_it(DECL_ARGS)
1329 {
1330 const struct mdoc_node *bln;
1331
1332 bln = n->parent->parent;
1333
1334 switch (n->type) {
1335 case MDOC_HEAD:
1336 switch (bln->norm->Bl.type) {
1337 case LIST_diag:
1338 outflags &= ~MMAN_spc;
1339 print_word("\\ ");
1340 break;
1341 case LIST_ohang:
1342 outflags |= MMAN_br;
1343 break;
1344 default:
1345 break;
1346 }
1347 break;
1348 case MDOC_BODY:
1349 switch (bln->norm->Bl.type) {
1350 case LIST_bullet:
1351 /* FALLTHROUGH */
1352 case LIST_dash:
1353 /* FALLTHROUGH */
1354 case LIST_hyphen:
1355 /* FALLTHROUGH */
1356 case LIST_enum:
1357 /* FALLTHROUGH */
1358 case LIST_hang:
1359 /* FALLTHROUGH */
1360 case LIST_tag:
1361 assert(Bl_stack_len);
1362 Bl_stack[--Bl_stack_len] = 0;
1363
1364 /*
1365 * Our indentation had to be restored
1366 * after a child display or child list.
1367 * Close out that indentation block now.
1368 */
1369 if (Bl_stack_post[Bl_stack_len]) {
1370 print_line(".RE", MMAN_nl);
1371 Bl_stack_post[Bl_stack_len] = 0;
1372 }
1373 break;
1374 case LIST_column:
1375 if (NULL != n->next) {
1376 putchar('\t');
1377 outflags &= ~MMAN_spc;
1378 }
1379 break;
1380 default:
1381 break;
1382 }
1383 break;
1384 default:
1385 break;
1386 }
1387 }
1388
1389 static void
1390 post_lb(DECL_ARGS)
1391 {
1392
1393 if (SEC_LIBRARY == n->sec)
1394 outflags |= MMAN_br;
1395 }
1396
1397 static int
1398 pre_lk(DECL_ARGS)
1399 {
1400 const struct mdoc_node *link, *descr;
1401
1402 if (NULL == (link = n->child))
1403 return(0);
1404
1405 if (NULL != (descr = link->next)) {
1406 font_push('I');
1407 while (NULL != descr) {
1408 print_word(descr->string);
1409 descr = descr->next;
1410 }
1411 print_word(":");
1412 font_pop();
1413 }
1414
1415 font_push('B');
1416 print_word(link->string);
1417 font_pop();
1418 return(0);
1419 }
1420
1421 static int
1422 pre_ll(DECL_ARGS)
1423 {
1424
1425 print_line(".ll", 0);
1426 return(1);
1427 }
1428
1429 static int
1430 pre_li(DECL_ARGS)
1431 {
1432
1433 font_push('R');
1434 return(1);
1435 }
1436
1437 static int
1438 pre_nm(DECL_ARGS)
1439 {
1440 char *name;
1441
1442 if (MDOC_BLOCK == n->type) {
1443 outflags |= MMAN_Bk;
1444 pre_syn(n);
1445 }
1446 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1447 return(1);
1448 name = n->child ? n->child->string : meta->name;
1449 if (NULL == name)
1450 return(0);
1451 if (MDOC_HEAD == n->type) {
1452 if (NULL == n->parent->prev)
1453 outflags |= MMAN_sp;
1454 print_block(".HP", 0);
1455 printf(" %zun", strlen(name) + 1);
1456 outflags |= MMAN_nl;
1457 }
1458 font_push('B');
1459 if (NULL == n->child)
1460 print_word(meta->name);
1461 return(1);
1462 }
1463
1464 static void
1465 post_nm(DECL_ARGS)
1466 {
1467
1468 switch (n->type) {
1469 case MDOC_BLOCK:
1470 outflags &= ~MMAN_Bk;
1471 break;
1472 case MDOC_HEAD:
1473 /* FALLTHROUGH */
1474 case MDOC_ELEM:
1475 font_pop();
1476 break;
1477 default:
1478 break;
1479 }
1480 }
1481
1482 static int
1483 pre_no(DECL_ARGS)
1484 {
1485
1486 outflags |= MMAN_spc_force;
1487 return(1);
1488 }
1489
1490 static int
1491 pre_ns(DECL_ARGS)
1492 {
1493
1494 outflags &= ~MMAN_spc;
1495 return(0);
1496 }
1497
1498 static void
1499 post_pf(DECL_ARGS)
1500 {
1501
1502 outflags &= ~MMAN_spc;
1503 }
1504
1505 static int
1506 pre_pp(DECL_ARGS)
1507 {
1508
1509 if (MDOC_It != n->parent->tok)
1510 outflags |= MMAN_PP;
1511 outflags |= MMAN_sp | MMAN_nl;
1512 outflags &= ~MMAN_br;
1513 return(0);
1514 }
1515
1516 static int
1517 pre_rs(DECL_ARGS)
1518 {
1519
1520 if (SEC_SEE_ALSO == n->sec) {
1521 outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1522 outflags &= ~MMAN_br;
1523 }
1524 return(1);
1525 }
1526
1527 static int
1528 pre_sm(DECL_ARGS)
1529 {
1530
1531 assert(n->child && MDOC_TEXT == n->child->type);
1532 if (0 == strcmp("on", n->child->string))
1533 outflags |= MMAN_Sm | MMAN_spc;
1534 else
1535 outflags &= ~MMAN_Sm;
1536 return(0);
1537 }
1538
1539 static int
1540 pre_sp(DECL_ARGS)
1541 {
1542
1543 if (MMAN_PP & outflags) {
1544 outflags &= ~MMAN_PP;
1545 print_line(".PP", 0);
1546 } else
1547 print_line(".sp", 0);
1548 return(1);
1549 }
1550
1551 static void
1552 post_sp(DECL_ARGS)
1553 {
1554
1555 outflags |= MMAN_nl;
1556 }
1557
1558 static int
1559 pre_sy(DECL_ARGS)
1560 {
1561
1562 font_push('B');
1563 return(1);
1564 }
1565
1566 static int
1567 pre_vt(DECL_ARGS)
1568 {
1569
1570 if (MDOC_SYNPRETTY & n->flags) {
1571 switch (n->type) {
1572 case MDOC_BLOCK:
1573 pre_syn(n);
1574 return(1);
1575 case MDOC_BODY:
1576 break;
1577 default:
1578 return(0);
1579 }
1580 }
1581 font_push('I');
1582 return(1);
1583 }
1584
1585 static void
1586 post_vt(DECL_ARGS)
1587 {
1588
1589 if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
1590 return;
1591 font_pop();
1592 }
1593
1594 static int
1595 pre_xr(DECL_ARGS)
1596 {
1597
1598 n = n->child;
1599 if (NULL == n)
1600 return(0);
1601 print_node(meta, n);
1602 n = n->next;
1603 if (NULL == n)
1604 return(0);
1605 outflags &= ~MMAN_spc;
1606 print_word("(");
1607 print_node(meta, n);
1608 print_word(")");
1609 return(0);
1610 }
1611
1612 static int
1613 pre_ux(DECL_ARGS)
1614 {
1615
1616 print_word(manacts[n->tok].prefix);
1617 if (NULL == n->child)
1618 return(0);
1619 outflags &= ~MMAN_spc;
1620 print_word("\\ ");
1621 outflags &= ~MMAN_spc;
1622 return(1);
1623 }