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