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