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