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