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