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