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