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