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