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