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