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