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