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