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