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 *
mdoc_man_act(enum roff_tok tok)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
man_strlen(const char * cp)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
font_push(char newfont)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
font_pop(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
print_word(const char * s)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
print_line(const char * s,int newflags)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
print_block(const char * s,int newflags)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
print_offs(const char * v,int keywords)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
print_width(const struct mdoc_bl * bl,const struct roff_node * child)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
print_count(int * count)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
man_mdoc(void * arg,const struct roff_meta * mdoc)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
print_node(DECL_ARGS)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
cond_head(DECL_ARGS)718 cond_head(DECL_ARGS)
719 {
720 
721           return n->type == ROFFT_HEAD;
722 }
723 
724 static int
cond_body(DECL_ARGS)725 cond_body(DECL_ARGS)
726 {
727 
728           return n->type == ROFFT_BODY;
729 }
730 
731 static int
pre_abort(DECL_ARGS)732 pre_abort(DECL_ARGS)
733 {
734           abort();
735 }
736 
737 static int
pre_enc(DECL_ARGS)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
post_enc(DECL_ARGS)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
pre_ex(DECL_ARGS)763 pre_ex(DECL_ARGS)
764 {
765           outflags |= MMAN_br | MMAN_nl;
766           return 1;
767 }
768 
769 static void
post_font(DECL_ARGS)770 post_font(DECL_ARGS)
771 {
772 
773           font_pop();
774 }
775 
776 static void
post_percent(DECL_ARGS)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
pre__t(DECL_ARGS)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
post__t(DECL_ARGS)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
pre_sect(DECL_ARGS)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
post_sect(DECL_ARGS)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
pre_syn(const struct roff_node * n)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
pre_an(DECL_ARGS)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
pre_ap(DECL_ARGS)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
pre_aq(DECL_ARGS)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
post_aq(DECL_ARGS)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
pre_bd(DECL_ARGS)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
post_bd(DECL_ARGS)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
pre_bf(DECL_ARGS)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
post_bf(DECL_ARGS)1010 post_bf(DECL_ARGS)
1011 {
1012 
1013           if (n->type == ROFFT_BODY)
1014                     font_pop();
1015 }
1016 
1017 static int
pre_bk(DECL_ARGS)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
post_bk(DECL_ARGS)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
pre_bl(DECL_ARGS)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
post_bl(DECL_ARGS)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
pre_br(DECL_ARGS)1117 pre_br(DECL_ARGS)
1118 {
1119           outflags |= MMAN_br;
1120 }
1121 
1122 static int
pre_dl(DECL_ARGS)1123 pre_dl(DECL_ARGS)
1124 {
1125 
1126           print_offs("6n", 0);
1127           return 1;
1128 }
1129 
1130 static void
post_dl(DECL_ARGS)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
pre_em(DECL_ARGS)1142 pre_em(DECL_ARGS)
1143 {
1144 
1145           font_push('I');
1146           return 1;
1147 }
1148 
1149 static int
pre_en(DECL_ARGS)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
post_en(DECL_ARGS)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
pre_eo(DECL_ARGS)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
post_eo(DECL_ARGS)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
pre_fa(DECL_ARGS)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
post_fa(DECL_ARGS)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
pre_fd(DECL_ARGS)1244 pre_fd(DECL_ARGS)
1245 {
1246 
1247           pre_syn(n);
1248           font_push('B');
1249           return 1;
1250 }
1251 
1252 static void
post_fd(DECL_ARGS)1253 post_fd(DECL_ARGS)
1254 {
1255 
1256           font_pop();
1257           outflags |= MMAN_br;
1258 }
1259 
1260 static int
pre_fl(DECL_ARGS)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
post_fl(DECL_ARGS)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
pre_fn(DECL_ARGS)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
post_fn(DECL_ARGS)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
pre_fo(DECL_ARGS)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
post_fo(DECL_ARGS)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
pre_Ft(DECL_ARGS)1364 pre_Ft(DECL_ARGS)
1365 {
1366 
1367           pre_syn(n);
1368           font_push('I');
1369           return 1;
1370 }
1371 
1372 static void
pre_ft(DECL_ARGS)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
pre_in(DECL_ARGS)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
post_in(DECL_ARGS)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
pre_it(DECL_ARGS)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
mid_it(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
post_it(DECL_ARGS)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
post_lb(DECL_ARGS)1564 post_lb(DECL_ARGS)
1565 {
1566 
1567           if (SEC_LIBRARY == n->sec)
1568                     outflags |= MMAN_br;
1569 }
1570 
1571 static int
pre_lk(DECL_ARGS)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
pre_onearg(DECL_ARGS)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
pre_li(DECL_ARGS)1625 pre_li(DECL_ARGS)
1626 {
1627           font_push('R');
1628           return 1;
1629 }
1630 
1631 static int
pre_nm(DECL_ARGS)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
post_nm(DECL_ARGS)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
pre_no(DECL_ARGS)1674 pre_no(DECL_ARGS)
1675 {
1676           outflags |= MMAN_spc_force;
1677           return 1;
1678 }
1679 
1680 static void
pre_noarg(DECL_ARGS)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
pre_ns(DECL_ARGS)1691 pre_ns(DECL_ARGS)
1692 {
1693           outflags &= ~MMAN_spc;
1694           return 0;
1695 }
1696 
1697 static void
post_pf(DECL_ARGS)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
pre_pp(DECL_ARGS)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
pre_rs(DECL_ARGS)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
pre_skip(DECL_ARGS)1728 pre_skip(DECL_ARGS)
1729 {
1730 
1731           return 0;
1732 }
1733 
1734 static int
pre_sm(DECL_ARGS)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
pre_sp(DECL_ARGS)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
pre_sy(DECL_ARGS)1766 pre_sy(DECL_ARGS)
1767 {
1768 
1769           font_push('B');
1770           return 1;
1771 }
1772 
1773 static void
pre_ta(DECL_ARGS)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
pre_vt(DECL_ARGS)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
post_vt(DECL_ARGS)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
pre_xr(DECL_ARGS)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 }
1827