]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_man.c
Finally delete support for the "_whatdb" configuration directive,
[mandoc.git] / mdoc_man.c
1 /* $Id: mdoc_man.c,v 1.133 2020/01/19 18:02:00 schwarze Exp $ */
2 /*
3 * Copyright (c) 2011-2019 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(const 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, NULL, 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 *
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
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
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
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
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
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
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
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 print_line(".RS", MMAN_Bk_susp);
498
499 /* Convert v into a number (of characters). */
500 if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
501 sz = 0;
502 else if (keywords && !strcmp(v, "indent"))
503 sz = 6;
504 else if (keywords && !strcmp(v, "indent-two"))
505 sz = 12;
506 else {
507 end = a2roffsu(v, &su, SCALE_EN);
508 if (end == NULL || *end != '\0')
509 sz = man_strlen(v);
510 else if (SCALE_EN == su.unit)
511 sz = su.scale;
512 else {
513 /*
514 * XXX
515 * If we are inside an enclosing list,
516 * there is no easy way to add the two
517 * indentations because they are provided
518 * in terms of different units.
519 */
520 print_word(v);
521 outflags |= MMAN_nl;
522 return;
523 }
524 }
525
526 /*
527 * We are inside an enclosing list.
528 * Add the two indentations.
529 */
530 if (Bl_stack_len)
531 sz += Bl_stack[Bl_stack_len - 1];
532
533 (void)snprintf(buf, sizeof(buf), "%dn", sz);
534 print_word(buf);
535 outflags |= MMAN_nl;
536 }
537
538 /*
539 * Set up the indentation for a list item; used from pre_it().
540 */
541 static void
542 print_width(const struct mdoc_bl *bl, const struct roff_node *child)
543 {
544 char buf[24];
545 struct roffsu su;
546 const char *end;
547 int numeric, remain, sz, chsz;
548
549 numeric = 1;
550 remain = 0;
551
552 /* Convert the width into a number (of characters). */
553 if (bl->width == NULL)
554 sz = (bl->type == LIST_hang) ? 6 : 0;
555 else {
556 end = a2roffsu(bl->width, &su, SCALE_MAX);
557 if (end == NULL || *end != '\0')
558 sz = man_strlen(bl->width);
559 else if (SCALE_EN == su.unit)
560 sz = su.scale;
561 else {
562 sz = 0;
563 numeric = 0;
564 }
565 }
566
567 /* XXX Rough estimation, might have multiple parts. */
568 if (bl->type == LIST_enum)
569 chsz = (bl->count > 8) + 1;
570 else if (child != NULL && child->type == ROFFT_TEXT)
571 chsz = man_strlen(child->string);
572 else
573 chsz = 0;
574
575 /* Maybe we are inside an enclosing list? */
576 mid_it();
577
578 /*
579 * Save our own indentation,
580 * such that child lists can use it.
581 */
582 Bl_stack[Bl_stack_len++] = sz + 2;
583
584 /* Set up the current list. */
585 if (chsz > sz && bl->type != LIST_tag)
586 print_block(".HP", 0);
587 else {
588 print_block(".TP", 0);
589 remain = sz + 2;
590 }
591 if (numeric) {
592 (void)snprintf(buf, sizeof(buf), "%dn", sz + 2);
593 print_word(buf);
594 } else
595 print_word(bl->width);
596 TPremain = remain;
597 }
598
599 static void
600 print_count(int *count)
601 {
602 char buf[24];
603
604 (void)snprintf(buf, sizeof(buf), "%d.\\&", ++*count);
605 print_word(buf);
606 }
607
608 void
609 man_mdoc(void *arg, const struct roff_meta *mdoc)
610 {
611 struct roff_node *n;
612
613 printf(".\\\" Automatically generated from an mdoc input file."
614 " Do not edit.\n");
615 for (n = mdoc->first->child; n != NULL; n = n->next) {
616 if (n->type != ROFFT_COMMENT)
617 break;
618 printf(".\\\"%s\n", n->string);
619 }
620
621 printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
622 mdoc->title, (mdoc->msec == NULL ? "" : mdoc->msec),
623 mdoc->date, mdoc->os, mdoc->vol);
624
625 /* Disable hyphenation and if nroff, disable justification. */
626 printf(".nh\n.if n .ad l");
627
628 outflags = MMAN_nl | MMAN_Sm;
629 if (0 == fontqueue.size) {
630 fontqueue.size = 8;
631 fontqueue.head = fontqueue.tail = mandoc_malloc(8);
632 *fontqueue.tail = 'R';
633 }
634 for (; n != NULL; n = n->next)
635 print_node(mdoc, n);
636 putchar('\n');
637 }
638
639 static void
640 print_node(DECL_ARGS)
641 {
642 const struct mdoc_man_act *act;
643 struct roff_node *sub;
644 int cond, do_sub;
645
646 if (n->flags & NODE_NOPRT)
647 return;
648
649 /*
650 * Break the line if we were parsed subsequent the current node.
651 * This makes the page structure be more consistent.
652 */
653 if (MMAN_spc & outflags && NODE_LINE & n->flags)
654 outflags |= MMAN_nl;
655
656 act = NULL;
657 cond = 0;
658 do_sub = 1;
659 n->flags &= ~NODE_ENDED;
660
661 if (n->type == ROFFT_TEXT) {
662 /*
663 * Make sure that we don't happen to start with a
664 * control character at the start of a line.
665 */
666 if (MMAN_nl & outflags &&
667 ('.' == *n->string || '\'' == *n->string)) {
668 print_word("");
669 printf("\\&");
670 outflags &= ~MMAN_spc;
671 }
672 if (n->flags & NODE_DELIMC)
673 outflags &= ~(MMAN_spc | MMAN_spc_force);
674 else if (outflags & MMAN_Sm)
675 outflags |= MMAN_spc_force;
676 print_word(n->string);
677 if (n->flags & NODE_DELIMO)
678 outflags &= ~(MMAN_spc | MMAN_spc_force);
679 else if (outflags & MMAN_Sm)
680 outflags |= MMAN_spc;
681 } else if (n->tok < ROFF_MAX) {
682 (*roff_man_acts[n->tok])(meta, n);
683 return;
684 } else {
685 /*
686 * Conditionally run the pre-node action handler for a
687 * node.
688 */
689 act = mdoc_man_act(n->tok);
690 cond = act->cond == NULL || (*act->cond)(meta, n);
691 if (cond && act->pre != NULL &&
692 (n->end == ENDBODY_NOT || n->child != NULL))
693 do_sub = (*act->pre)(meta, n);
694 }
695
696 /*
697 * Conditionally run all child nodes.
698 * Note that this iterates over children instead of using
699 * recursion. This prevents unnecessary depth in the stack.
700 */
701 if (do_sub)
702 for (sub = n->child; sub; sub = sub->next)
703 print_node(meta, sub);
704
705 /*
706 * Lastly, conditionally run the post-node handler.
707 */
708 if (NODE_ENDED & n->flags)
709 return;
710
711 if (cond && act->post)
712 (*act->post)(meta, n);
713
714 if (ENDBODY_NOT != n->end)
715 n->body->flags |= NODE_ENDED;
716 }
717
718 static int
719 cond_head(DECL_ARGS)
720 {
721
722 return n->type == ROFFT_HEAD;
723 }
724
725 static int
726 cond_body(DECL_ARGS)
727 {
728
729 return n->type == ROFFT_BODY;
730 }
731
732 static int
733 pre_abort(DECL_ARGS)
734 {
735 abort();
736 }
737
738 static int
739 pre_enc(DECL_ARGS)
740 {
741 const char *prefix;
742
743 prefix = mdoc_man_act(n->tok)->prefix;
744 if (NULL == prefix)
745 return 1;
746 print_word(prefix);
747 outflags &= ~MMAN_spc;
748 return 1;
749 }
750
751 static void
752 post_enc(DECL_ARGS)
753 {
754 const char *suffix;
755
756 suffix = mdoc_man_act(n->tok)->suffix;
757 if (NULL == suffix)
758 return;
759 outflags &= ~(MMAN_spc | MMAN_nl);
760 print_word(suffix);
761 }
762
763 static int
764 pre_ex(DECL_ARGS)
765 {
766 outflags |= MMAN_br | MMAN_nl;
767 return 1;
768 }
769
770 static void
771 post_font(DECL_ARGS)
772 {
773
774 font_pop();
775 }
776
777 static void
778 post_percent(DECL_ARGS)
779 {
780
781 if (mdoc_man_act(n->tok)->pre == pre_em)
782 font_pop();
783 if (n->next) {
784 print_word(",");
785 if (n->prev && n->prev->tok == n->tok &&
786 n->next->tok == n->tok)
787 print_word("and");
788 } else {
789 print_word(".");
790 outflags |= MMAN_nl;
791 }
792 }
793
794 static int
795 pre__t(DECL_ARGS)
796 {
797
798 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) {
799 print_word("\\(lq");
800 outflags &= ~MMAN_spc;
801 } else
802 font_push('I');
803 return 1;
804 }
805
806 static void
807 post__t(DECL_ARGS)
808 {
809
810 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) {
811 outflags &= ~MMAN_spc;
812 print_word("\\(rq");
813 } else
814 font_pop();
815 post_percent(meta, n);
816 }
817
818 /*
819 * Print before a section header.
820 */
821 static int
822 pre_sect(DECL_ARGS)
823 {
824
825 if (n->type == ROFFT_HEAD) {
826 outflags |= MMAN_sp;
827 print_block(mdoc_man_act(n->tok)->prefix, 0);
828 print_word("");
829 putchar('\"');
830 outflags &= ~MMAN_spc;
831 }
832 return 1;
833 }
834
835 /*
836 * Print subsequent a section header.
837 */
838 static void
839 post_sect(DECL_ARGS)
840 {
841
842 if (n->type != ROFFT_HEAD)
843 return;
844 outflags &= ~MMAN_spc;
845 print_word("");
846 putchar('\"');
847 outflags |= MMAN_nl;
848 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
849 outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
850 }
851
852 /* See mdoc_term.c, synopsis_pre() for comments. */
853 static void
854 pre_syn(const struct roff_node *n)
855 {
856
857 if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
858 return;
859
860 if (n->prev->tok == n->tok &&
861 MDOC_Ft != n->tok &&
862 MDOC_Fo != n->tok &&
863 MDOC_Fn != n->tok) {
864 outflags |= MMAN_br;
865 return;
866 }
867
868 switch (n->prev->tok) {
869 case MDOC_Fd:
870 case MDOC_Fn:
871 case MDOC_Fo:
872 case MDOC_In:
873 case MDOC_Vt:
874 outflags |= MMAN_sp;
875 break;
876 case MDOC_Ft:
877 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
878 outflags |= MMAN_sp;
879 break;
880 }
881 /* FALLTHROUGH */
882 default:
883 outflags |= MMAN_br;
884 break;
885 }
886 }
887
888 static int
889 pre_an(DECL_ARGS)
890 {
891
892 switch (n->norm->An.auth) {
893 case AUTH_split:
894 outflags &= ~MMAN_An_nosplit;
895 outflags |= MMAN_An_split;
896 return 0;
897 case AUTH_nosplit:
898 outflags &= ~MMAN_An_split;
899 outflags |= MMAN_An_nosplit;
900 return 0;
901 default:
902 if (MMAN_An_split & outflags)
903 outflags |= MMAN_br;
904 else if (SEC_AUTHORS == n->sec &&
905 ! (MMAN_An_nosplit & outflags))
906 outflags |= MMAN_An_split;
907 return 1;
908 }
909 }
910
911 static int
912 pre_ap(DECL_ARGS)
913 {
914
915 outflags &= ~MMAN_spc;
916 print_word("'");
917 outflags &= ~MMAN_spc;
918 return 0;
919 }
920
921 static int
922 pre_aq(DECL_ARGS)
923 {
924
925 print_word(n->child != NULL && n->child->next == NULL &&
926 n->child->tok == MDOC_Mt ? "<" : "\\(la");
927 outflags &= ~MMAN_spc;
928 return 1;
929 }
930
931 static void
932 post_aq(DECL_ARGS)
933 {
934
935 outflags &= ~(MMAN_spc | MMAN_nl);
936 print_word(n->child != NULL && n->child->next == NULL &&
937 n->child->tok == MDOC_Mt ? ">" : "\\(ra");
938 }
939
940 static int
941 pre_bd(DECL_ARGS)
942 {
943 outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
944
945 if (DISP_unfilled == n->norm->Bd.type ||
946 DISP_literal == n->norm->Bd.type)
947 print_line(".nf", 0);
948 if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
949 outflags |= MMAN_sp;
950 print_offs(n->norm->Bd.offs, 1);
951 return 1;
952 }
953
954 static void
955 post_bd(DECL_ARGS)
956 {
957 enum roff_tok bef, now;
958
959 /* Close out this display. */
960 print_line(".RE", MMAN_nl);
961 bef = n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
962 if (n->last == NULL)
963 now = n->norm->Bd.type == DISP_unfilled ||
964 n->norm->Bd.type == DISP_literal ? ROFF_nf : ROFF_fi;
965 else if (n->last->tok == ROFF_nf)
966 now = ROFF_nf;
967 else if (n->last->tok == ROFF_fi)
968 now = ROFF_fi;
969 else
970 now = n->last->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
971 if (bef != now) {
972 outflags |= MMAN_nl;
973 print_word(".");
974 outflags &= ~MMAN_spc;
975 print_word(roff_name[bef]);
976 outflags |= MMAN_nl;
977 }
978
979 /* Maybe we are inside an enclosing list? */
980 if (NULL != n->parent->next)
981 mid_it();
982 }
983
984 static int
985 pre_bf(DECL_ARGS)
986 {
987
988 switch (n->type) {
989 case ROFFT_BLOCK:
990 return 1;
991 case ROFFT_BODY:
992 break;
993 default:
994 return 0;
995 }
996 switch (n->norm->Bf.font) {
997 case FONT_Em:
998 font_push('I');
999 break;
1000 case FONT_Sy:
1001 font_push('B');
1002 break;
1003 default:
1004 font_push('R');
1005 break;
1006 }
1007 return 1;
1008 }
1009
1010 static void
1011 post_bf(DECL_ARGS)
1012 {
1013
1014 if (n->type == ROFFT_BODY)
1015 font_pop();
1016 }
1017
1018 static int
1019 pre_bk(DECL_ARGS)
1020 {
1021 switch (n->type) {
1022 case ROFFT_BLOCK:
1023 return 1;
1024 case ROFFT_BODY:
1025 case ROFFT_ELEM:
1026 outflags |= MMAN_Bk;
1027 return 1;
1028 default:
1029 return 0;
1030 }
1031 }
1032
1033 static void
1034 post_bk(DECL_ARGS)
1035 {
1036 switch (n->type) {
1037 case ROFFT_ELEM:
1038 while ((n = n->parent) != NULL)
1039 if (n->tok == MDOC_Bk)
1040 return;
1041 /* FALLTHROUGH */
1042 case ROFFT_BODY:
1043 outflags &= ~MMAN_Bk;
1044 break;
1045 default:
1046 break;
1047 }
1048 }
1049
1050 static int
1051 pre_bl(DECL_ARGS)
1052 {
1053 size_t icol;
1054
1055 /*
1056 * print_offs() will increase the -offset to account for
1057 * a possible enclosing .It, but any enclosed .It blocks
1058 * just nest and do not add up their indentation.
1059 */
1060 if (n->norm->Bl.offs) {
1061 print_offs(n->norm->Bl.offs, 0);
1062 Bl_stack[Bl_stack_len++] = 0;
1063 }
1064
1065 switch (n->norm->Bl.type) {
1066 case LIST_enum:
1067 n->norm->Bl.count = 0;
1068 return 1;
1069 case LIST_column:
1070 break;
1071 default:
1072 return 1;
1073 }
1074
1075 if (n->child != NULL) {
1076 print_line(".TS", MMAN_nl);
1077 for (icol = 0; icol < n->norm->Bl.ncols; icol++)
1078 print_word("l");
1079 print_word(".");
1080 }
1081 outflags |= MMAN_nl;
1082 return 1;
1083 }
1084
1085 static void
1086 post_bl(DECL_ARGS)
1087 {
1088
1089 switch (n->norm->Bl.type) {
1090 case LIST_column:
1091 if (n->child != NULL)
1092 print_line(".TE", 0);
1093 break;
1094 case LIST_enum:
1095 n->norm->Bl.count = 0;
1096 break;
1097 default:
1098 break;
1099 }
1100
1101 if (n->norm->Bl.offs) {
1102 print_line(".RE", MMAN_nl);
1103 assert(Bl_stack_len);
1104 Bl_stack_len--;
1105 assert(0 == Bl_stack[Bl_stack_len]);
1106 } else {
1107 outflags |= MMAN_PP | MMAN_nl;
1108 outflags &= ~(MMAN_sp | MMAN_br);
1109 }
1110
1111 /* Maybe we are inside an enclosing list? */
1112 if (NULL != n->parent->next)
1113 mid_it();
1114
1115 }
1116
1117 static void
1118 pre_br(DECL_ARGS)
1119 {
1120 outflags |= MMAN_br;
1121 }
1122
1123 static int
1124 pre_dl(DECL_ARGS)
1125 {
1126
1127 print_offs("6n", 0);
1128 return 1;
1129 }
1130
1131 static void
1132 post_dl(DECL_ARGS)
1133 {
1134
1135 print_line(".RE", MMAN_nl);
1136
1137 /* Maybe we are inside an enclosing list? */
1138 if (NULL != n->parent->next)
1139 mid_it();
1140 }
1141
1142 static int
1143 pre_em(DECL_ARGS)
1144 {
1145
1146 font_push('I');
1147 return 1;
1148 }
1149
1150 static int
1151 pre_en(DECL_ARGS)
1152 {
1153
1154 if (NULL == n->norm->Es ||
1155 NULL == n->norm->Es->child)
1156 return 1;
1157
1158 print_word(n->norm->Es->child->string);
1159 outflags &= ~MMAN_spc;
1160 return 1;
1161 }
1162
1163 static void
1164 post_en(DECL_ARGS)
1165 {
1166
1167 if (NULL == n->norm->Es ||
1168 NULL == n->norm->Es->child ||
1169 NULL == n->norm->Es->child->next)
1170 return;
1171
1172 outflags &= ~MMAN_spc;
1173 print_word(n->norm->Es->child->next->string);
1174 return;
1175 }
1176
1177 static int
1178 pre_eo(DECL_ARGS)
1179 {
1180
1181 if (n->end == ENDBODY_NOT &&
1182 n->parent->head->child == NULL &&
1183 n->child != NULL &&
1184 n->child->end != ENDBODY_NOT)
1185 print_word("\\&");
1186 else if (n->end != ENDBODY_NOT ? n->child != NULL :
1187 n->parent->head->child != NULL && (n->child != NULL ||
1188 (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1189 outflags &= ~(MMAN_spc | MMAN_nl);
1190 return 1;
1191 }
1192
1193 static void
1194 post_eo(DECL_ARGS)
1195 {
1196 int body, tail;
1197
1198 if (n->end != ENDBODY_NOT) {
1199 outflags |= MMAN_spc;
1200 return;
1201 }
1202
1203 body = n->child != NULL || n->parent->head->child != NULL;
1204 tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1205
1206 if (body && tail)
1207 outflags &= ~MMAN_spc;
1208 else if ( ! (body || tail))
1209 print_word("\\&");
1210 else if ( ! tail)
1211 outflags |= MMAN_spc;
1212 }
1213
1214 static int
1215 pre_fa(DECL_ARGS)
1216 {
1217 int am_Fa;
1218
1219 am_Fa = MDOC_Fa == n->tok;
1220
1221 if (am_Fa)
1222 n = n->child;
1223
1224 while (NULL != n) {
1225 font_push('I');
1226 if (am_Fa || NODE_SYNPRETTY & n->flags)
1227 outflags |= MMAN_nbrword;
1228 print_node(meta, n);
1229 font_pop();
1230 if (NULL != (n = n->next))
1231 print_word(",");
1232 }
1233 return 0;
1234 }
1235
1236 static void
1237 post_fa(DECL_ARGS)
1238 {
1239
1240 if (NULL != n->next && MDOC_Fa == n->next->tok)
1241 print_word(",");
1242 }
1243
1244 static int
1245 pre_fd(DECL_ARGS)
1246 {
1247
1248 pre_syn(n);
1249 font_push('B');
1250 return 1;
1251 }
1252
1253 static void
1254 post_fd(DECL_ARGS)
1255 {
1256
1257 font_pop();
1258 outflags |= MMAN_br;
1259 }
1260
1261 static int
1262 pre_fl(DECL_ARGS)
1263 {
1264
1265 font_push('B');
1266 print_word("\\-");
1267 if (n->child != NULL)
1268 outflags &= ~MMAN_spc;
1269 return 1;
1270 }
1271
1272 static void
1273 post_fl(DECL_ARGS)
1274 {
1275
1276 font_pop();
1277 if (!(n->child != NULL ||
1278 n->next == NULL ||
1279 n->next->type == ROFFT_TEXT ||
1280 n->next->flags & NODE_LINE))
1281 outflags &= ~MMAN_spc;
1282 }
1283
1284 static int
1285 pre_fn(DECL_ARGS)
1286 {
1287
1288 pre_syn(n);
1289
1290 n = n->child;
1291 if (NULL == n)
1292 return 0;
1293
1294 if (NODE_SYNPRETTY & n->flags)
1295 print_block(".HP 4n", MMAN_nl);
1296
1297 font_push('B');
1298 print_node(meta, n);
1299 font_pop();
1300 outflags &= ~MMAN_spc;
1301 print_word("(");
1302 outflags &= ~MMAN_spc;
1303
1304 n = n->next;
1305 if (NULL != n)
1306 pre_fa(meta, n);
1307 return 0;
1308 }
1309
1310 static void
1311 post_fn(DECL_ARGS)
1312 {
1313
1314 print_word(")");
1315 if (NODE_SYNPRETTY & n->flags) {
1316 print_word(";");
1317 outflags |= MMAN_PP;
1318 }
1319 }
1320
1321 static int
1322 pre_fo(DECL_ARGS)
1323 {
1324
1325 switch (n->type) {
1326 case ROFFT_BLOCK:
1327 pre_syn(n);
1328 break;
1329 case ROFFT_HEAD:
1330 if (n->child == NULL)
1331 return 0;
1332 if (NODE_SYNPRETTY & n->flags)
1333 print_block(".HP 4n", MMAN_nl);
1334 font_push('B');
1335 break;
1336 case ROFFT_BODY:
1337 outflags &= ~(MMAN_spc | MMAN_nl);
1338 print_word("(");
1339 outflags &= ~MMAN_spc;
1340 break;
1341 default:
1342 break;
1343 }
1344 return 1;
1345 }
1346
1347 static void
1348 post_fo(DECL_ARGS)
1349 {
1350
1351 switch (n->type) {
1352 case ROFFT_HEAD:
1353 if (n->child != NULL)
1354 font_pop();
1355 break;
1356 case ROFFT_BODY:
1357 post_fn(meta, n);
1358 break;
1359 default:
1360 break;
1361 }
1362 }
1363
1364 static int
1365 pre_Ft(DECL_ARGS)
1366 {
1367
1368 pre_syn(n);
1369 font_push('I');
1370 return 1;
1371 }
1372
1373 static void
1374 pre_ft(DECL_ARGS)
1375 {
1376 print_line(".ft", 0);
1377 print_word(n->child->string);
1378 outflags |= MMAN_nl;
1379 }
1380
1381 static int
1382 pre_in(DECL_ARGS)
1383 {
1384
1385 if (NODE_SYNPRETTY & n->flags) {
1386 pre_syn(n);
1387 font_push('B');
1388 print_word("#include <");
1389 outflags &= ~MMAN_spc;
1390 } else {
1391 print_word("<");
1392 outflags &= ~MMAN_spc;
1393 font_push('I');
1394 }
1395 return 1;
1396 }
1397
1398 static void
1399 post_in(DECL_ARGS)
1400 {
1401
1402 if (NODE_SYNPRETTY & n->flags) {
1403 outflags &= ~MMAN_spc;
1404 print_word(">");
1405 font_pop();
1406 outflags |= MMAN_br;
1407 } else {
1408 font_pop();
1409 outflags &= ~MMAN_spc;
1410 print_word(">");
1411 }
1412 }
1413
1414 static int
1415 pre_it(DECL_ARGS)
1416 {
1417 const struct roff_node *bln;
1418
1419 switch (n->type) {
1420 case ROFFT_HEAD:
1421 outflags |= MMAN_PP | MMAN_nl;
1422 bln = n->parent->parent;
1423 if (0 == bln->norm->Bl.comp ||
1424 (NULL == n->parent->prev &&
1425 NULL == bln->parent->prev))
1426 outflags |= MMAN_sp;
1427 outflags &= ~MMAN_br;
1428 switch (bln->norm->Bl.type) {
1429 case LIST_item:
1430 return 0;
1431 case LIST_inset:
1432 case LIST_diag:
1433 case LIST_ohang:
1434 if (bln->norm->Bl.type == LIST_diag)
1435 print_line(".B \"", 0);
1436 else
1437 print_line(".BR \\& \"", 0);
1438 outflags &= ~MMAN_spc;
1439 return 1;
1440 case LIST_bullet:
1441 case LIST_dash:
1442 case LIST_hyphen:
1443 print_width(&bln->norm->Bl, NULL);
1444 TPremain = 0;
1445 outflags |= MMAN_nl;
1446 font_push('B');
1447 if (LIST_bullet == bln->norm->Bl.type)
1448 print_word("\\(bu");
1449 else
1450 print_word("-");
1451 font_pop();
1452 outflags |= MMAN_nl;
1453 return 0;
1454 case LIST_enum:
1455 print_width(&bln->norm->Bl, NULL);
1456 TPremain = 0;
1457 outflags |= MMAN_nl;
1458 print_count(&bln->norm->Bl.count);
1459 outflags |= MMAN_nl;
1460 return 0;
1461 case LIST_hang:
1462 print_width(&bln->norm->Bl, n->child);
1463 TPremain = 0;
1464 outflags |= MMAN_nl;
1465 return 1;
1466 case LIST_tag:
1467 print_width(&bln->norm->Bl, n->child);
1468 putchar('\n');
1469 outflags &= ~MMAN_spc;
1470 return 1;
1471 default:
1472 return 1;
1473 }
1474 default:
1475 break;
1476 }
1477 return 1;
1478 }
1479
1480 /*
1481 * This function is called after closing out an indented block.
1482 * If we are inside an enclosing list, restore its indentation.
1483 */
1484 static void
1485 mid_it(void)
1486 {
1487 char buf[24];
1488
1489 /* Nothing to do outside a list. */
1490 if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1491 return;
1492
1493 /* The indentation has already been set up. */
1494 if (Bl_stack_post[Bl_stack_len - 1])
1495 return;
1496
1497 /* Restore the indentation of the enclosing list. */
1498 print_line(".RS", MMAN_Bk_susp);
1499 (void)snprintf(buf, sizeof(buf), "%dn",
1500 Bl_stack[Bl_stack_len - 1]);
1501 print_word(buf);
1502
1503 /* Remeber to close out this .RS block later. */
1504 Bl_stack_post[Bl_stack_len - 1] = 1;
1505 }
1506
1507 static void
1508 post_it(DECL_ARGS)
1509 {
1510 const struct roff_node *bln;
1511
1512 bln = n->parent->parent;
1513
1514 switch (n->type) {
1515 case ROFFT_HEAD:
1516 switch (bln->norm->Bl.type) {
1517 case LIST_diag:
1518 outflags &= ~MMAN_spc;
1519 print_word("\\ ");
1520 break;
1521 case LIST_ohang:
1522 outflags |= MMAN_br;
1523 break;
1524 default:
1525 break;
1526 }
1527 break;
1528 case ROFFT_BODY:
1529 switch (bln->norm->Bl.type) {
1530 case LIST_bullet:
1531 case LIST_dash:
1532 case LIST_hyphen:
1533 case LIST_enum:
1534 case LIST_hang:
1535 case LIST_tag:
1536 assert(Bl_stack_len);
1537 Bl_stack[--Bl_stack_len] = 0;
1538
1539 /*
1540 * Our indentation had to be restored
1541 * after a child display or child list.
1542 * Close out that indentation block now.
1543 */
1544 if (Bl_stack_post[Bl_stack_len]) {
1545 print_line(".RE", MMAN_nl);
1546 Bl_stack_post[Bl_stack_len] = 0;
1547 }
1548 break;
1549 case LIST_column:
1550 if (NULL != n->next) {
1551 putchar('\t');
1552 outflags &= ~MMAN_spc;
1553 }
1554 break;
1555 default:
1556 break;
1557 }
1558 break;
1559 default:
1560 break;
1561 }
1562 }
1563
1564 static void
1565 post_lb(DECL_ARGS)
1566 {
1567
1568 if (SEC_LIBRARY == n->sec)
1569 outflags |= MMAN_br;
1570 }
1571
1572 static int
1573 pre_lk(DECL_ARGS)
1574 {
1575 const struct roff_node *link, *descr, *punct;
1576
1577 if ((link = n->child) == NULL)
1578 return 0;
1579
1580 /* Find beginning of trailing punctuation. */
1581 punct = n->last;
1582 while (punct != link && punct->flags & NODE_DELIMC)
1583 punct = punct->prev;
1584 punct = punct->next;
1585
1586 /* Link text. */
1587 if ((descr = link->next) != NULL && descr != punct) {
1588 font_push('I');
1589 while (descr != punct) {
1590 print_word(descr->string);
1591 descr = descr->next;
1592 }
1593 font_pop();
1594 print_word(":");
1595 }
1596
1597 /* Link target. */
1598 font_push('B');
1599 print_word(link->string);
1600 font_pop();
1601
1602 /* Trailing punctuation. */
1603 while (punct != NULL) {
1604 print_word(punct->string);
1605 punct = punct->next;
1606 }
1607 return 0;
1608 }
1609
1610 static void
1611 pre_onearg(DECL_ARGS)
1612 {
1613 outflags |= MMAN_nl;
1614 print_word(".");
1615 outflags &= ~MMAN_spc;
1616 print_word(roff_name[n->tok]);
1617 if (n->child != NULL)
1618 print_word(n->child->string);
1619 outflags |= MMAN_nl;
1620 if (n->tok == ROFF_ce)
1621 for (n = n->child->next; n != NULL; n = n->next)
1622 print_node(meta, n);
1623 }
1624
1625 static int
1626 pre_li(DECL_ARGS)
1627 {
1628 font_push('R');
1629 return 1;
1630 }
1631
1632 static int
1633 pre_nm(DECL_ARGS)
1634 {
1635 char *name;
1636
1637 if (n->type == ROFFT_BLOCK) {
1638 outflags |= MMAN_Bk;
1639 pre_syn(n);
1640 }
1641 if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD)
1642 return 1;
1643 name = n->child == NULL ? NULL : n->child->string;
1644 if (NULL == name)
1645 return 0;
1646 if (n->type == ROFFT_HEAD) {
1647 if (NULL == n->parent->prev)
1648 outflags |= MMAN_sp;
1649 print_block(".HP", 0);
1650 printf(" %dn", man_strlen(name) + 1);
1651 outflags |= MMAN_nl;
1652 }
1653 font_push('B');
1654 return 1;
1655 }
1656
1657 static void
1658 post_nm(DECL_ARGS)
1659 {
1660 switch (n->type) {
1661 case ROFFT_BLOCK:
1662 outflags &= ~MMAN_Bk;
1663 break;
1664 case ROFFT_HEAD:
1665 case ROFFT_ELEM:
1666 if (n->child != NULL && n->child->string != NULL)
1667 font_pop();
1668 break;
1669 default:
1670 break;
1671 }
1672 }
1673
1674 static int
1675 pre_no(DECL_ARGS)
1676 {
1677 outflags |= MMAN_spc_force;
1678 return 1;
1679 }
1680
1681 static void
1682 pre_noarg(DECL_ARGS)
1683 {
1684 outflags |= MMAN_nl;
1685 print_word(".");
1686 outflags &= ~MMAN_spc;
1687 print_word(roff_name[n->tok]);
1688 outflags |= MMAN_nl;
1689 }
1690
1691 static int
1692 pre_ns(DECL_ARGS)
1693 {
1694 outflags &= ~MMAN_spc;
1695 return 0;
1696 }
1697
1698 static void
1699 post_pf(DECL_ARGS)
1700 {
1701
1702 if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1703 outflags &= ~MMAN_spc;
1704 }
1705
1706 static int
1707 pre_pp(DECL_ARGS)
1708 {
1709
1710 if (MDOC_It != n->parent->tok)
1711 outflags |= MMAN_PP;
1712 outflags |= MMAN_sp | MMAN_nl;
1713 outflags &= ~MMAN_br;
1714 return 0;
1715 }
1716
1717 static int
1718 pre_rs(DECL_ARGS)
1719 {
1720
1721 if (SEC_SEE_ALSO == n->sec) {
1722 outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1723 outflags &= ~MMAN_br;
1724 }
1725 return 1;
1726 }
1727
1728 static int
1729 pre_skip(DECL_ARGS)
1730 {
1731
1732 return 0;
1733 }
1734
1735 static int
1736 pre_sm(DECL_ARGS)
1737 {
1738
1739 if (NULL == n->child)
1740 outflags ^= MMAN_Sm;
1741 else if (0 == strcmp("on", n->child->string))
1742 outflags |= MMAN_Sm;
1743 else
1744 outflags &= ~MMAN_Sm;
1745
1746 if (MMAN_Sm & outflags)
1747 outflags |= MMAN_spc;
1748
1749 return 0;
1750 }
1751
1752 static void
1753 pre_sp(DECL_ARGS)
1754 {
1755 if (outflags & MMAN_PP) {
1756 outflags &= ~MMAN_PP;
1757 print_line(".PP", 0);
1758 } else {
1759 print_line(".sp", 0);
1760 if (n->child != NULL)
1761 print_word(n->child->string);
1762 }
1763 outflags |= MMAN_nl;
1764 }
1765
1766 static int
1767 pre_sy(DECL_ARGS)
1768 {
1769
1770 font_push('B');
1771 return 1;
1772 }
1773
1774 static void
1775 pre_ta(DECL_ARGS)
1776 {
1777 print_line(".ta", 0);
1778 for (n = n->child; n != NULL; n = n->next)
1779 print_word(n->string);
1780 outflags |= MMAN_nl;
1781 }
1782
1783 static int
1784 pre_vt(DECL_ARGS)
1785 {
1786
1787 if (NODE_SYNPRETTY & n->flags) {
1788 switch (n->type) {
1789 case ROFFT_BLOCK:
1790 pre_syn(n);
1791 return 1;
1792 case ROFFT_BODY:
1793 break;
1794 default:
1795 return 0;
1796 }
1797 }
1798 font_push('I');
1799 return 1;
1800 }
1801
1802 static void
1803 post_vt(DECL_ARGS)
1804 {
1805
1806 if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY)
1807 return;
1808 font_pop();
1809 }
1810
1811 static int
1812 pre_xr(DECL_ARGS)
1813 {
1814
1815 n = n->child;
1816 if (NULL == n)
1817 return 0;
1818 print_node(meta, n);
1819 n = n->next;
1820 if (NULL == n)
1821 return 0;
1822 outflags &= ~MMAN_spc;
1823 print_word("(");
1824 print_node(meta, n);
1825 print_word(")");
1826 return 0;
1827 }