]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_man.c
The mdoc(7) \*(Ba predefined string actually forces roman font;
[mandoc.git] / mdoc_man.c
1 /* $Id: mdoc_man.c,v 1.41 2012/07/16 10:45:41 schwarze Exp $ */
2 /*
3 * Copyright (c) 2011, 2012 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 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "mandoc.h"
26 #include "out.h"
27 #include "man.h"
28 #include "mdoc.h"
29 #include "main.h"
30
31 #define DECL_ARGS const struct mdoc_meta *m, \
32 const struct mdoc_node *n
33
34 struct manact {
35 int (*cond)(DECL_ARGS); /* DON'T run actions */
36 int (*pre)(DECL_ARGS); /* pre-node action */
37 void (*post)(DECL_ARGS); /* post-node action */
38 const char *prefix; /* pre-node string constant */
39 const char *suffix; /* post-node string constant */
40 };
41
42 static int cond_body(DECL_ARGS);
43 static int cond_head(DECL_ARGS);
44 static void font_push(char);
45 static void font_pop(void);
46 static void post__t(DECL_ARGS);
47 static void post_bd(DECL_ARGS);
48 static void post_bf(DECL_ARGS);
49 static void post_bk(DECL_ARGS);
50 static void post_bl(DECL_ARGS);
51 static void post_dl(DECL_ARGS);
52 static void post_enc(DECL_ARGS);
53 static void post_eo(DECL_ARGS);
54 static void post_fa(DECL_ARGS);
55 static void post_fd(DECL_ARGS);
56 static void post_fl(DECL_ARGS);
57 static void post_fn(DECL_ARGS);
58 static void post_fo(DECL_ARGS);
59 static void post_font(DECL_ARGS);
60 static void post_in(DECL_ARGS);
61 static void post_it(DECL_ARGS);
62 static void post_lb(DECL_ARGS);
63 static void post_nm(DECL_ARGS);
64 static void post_percent(DECL_ARGS);
65 static void post_pf(DECL_ARGS);
66 static void post_sect(DECL_ARGS);
67 static void post_sp(DECL_ARGS);
68 static void post_vt(DECL_ARGS);
69 static int pre__t(DECL_ARGS);
70 static int pre_an(DECL_ARGS);
71 static int pre_ap(DECL_ARGS);
72 static int pre_bd(DECL_ARGS);
73 static int pre_bf(DECL_ARGS);
74 static int pre_bk(DECL_ARGS);
75 static int pre_bl(DECL_ARGS);
76 static int pre_br(DECL_ARGS);
77 static int pre_bx(DECL_ARGS);
78 static int pre_dl(DECL_ARGS);
79 static int pre_enc(DECL_ARGS);
80 static int pre_em(DECL_ARGS);
81 static int pre_fa(DECL_ARGS);
82 static int pre_fd(DECL_ARGS);
83 static int pre_fl(DECL_ARGS);
84 static int pre_fn(DECL_ARGS);
85 static int pre_fo(DECL_ARGS);
86 static int pre_ft(DECL_ARGS);
87 static int pre_in(DECL_ARGS);
88 static int pre_it(DECL_ARGS);
89 static int pre_lk(DECL_ARGS);
90 static int pre_li(DECL_ARGS);
91 static int pre_nm(DECL_ARGS);
92 static int pre_no(DECL_ARGS);
93 static int pre_ns(DECL_ARGS);
94 static int pre_pp(DECL_ARGS);
95 static int pre_rs(DECL_ARGS);
96 static int pre_sm(DECL_ARGS);
97 static int pre_sp(DECL_ARGS);
98 static int pre_sect(DECL_ARGS);
99 static int pre_sy(DECL_ARGS);
100 static void pre_syn(const struct mdoc_node *);
101 static int pre_vt(DECL_ARGS);
102 static int pre_ux(DECL_ARGS);
103 static int pre_xr(DECL_ARGS);
104 static void print_word(const char *);
105 static void print_line(const char *, int);
106 static void print_block(const char *, int);
107 static void print_offs(const char *);
108 static void print_width(const char *,
109 const struct mdoc_node *, size_t);
110 static void print_count(int *);
111 static void print_node(DECL_ARGS);
112
113 static const struct manact manacts[MDOC_MAX + 1] = {
114 { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
115 { NULL, NULL, NULL, NULL, NULL }, /* Dd */
116 { NULL, NULL, NULL, NULL, NULL }, /* Dt */
117 { NULL, NULL, NULL, NULL, NULL }, /* Os */
118 { NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
119 { NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
120 { NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
121 { cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
122 { cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
123 { cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
124 { NULL, NULL, NULL, NULL, NULL }, /* Ed */
125 { cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
126 { NULL, NULL, NULL, NULL, NULL }, /* El */
127 { NULL, pre_it, post_it, NULL, NULL }, /* It */
128 { NULL, pre_em, post_font, NULL, NULL }, /* Ad */
129 { NULL, pre_an, NULL, NULL, NULL }, /* An */
130 { NULL, pre_em, post_font, NULL, NULL }, /* Ar */
131 { NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
132 { NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
133 { NULL, pre_li, post_font, NULL, NULL }, /* Dv */
134 { NULL, pre_li, post_font, NULL, NULL }, /* Er */
135 { NULL, pre_li, post_font, NULL, NULL }, /* Ev */
136 { NULL, pre_enc, post_enc, "The \\fB",
137 "\\fP\nutility exits 0 on success, and >0 if an error occurs."
138 }, /* Ex */
139 { NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
140 { NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
141 { NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
142 { NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
143 { NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
144 { NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
145 { NULL, pre_in, post_in, NULL, NULL }, /* In */
146 { NULL, pre_li, post_font, NULL, NULL }, /* Li */
147 { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
148 { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
149 { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
150 { NULL, NULL, NULL, NULL, NULL }, /* Ot */
151 { NULL, pre_em, post_font, NULL, NULL }, /* Pa */
152 { NULL, pre_enc, post_enc, "The \\fB",
153 "\\fP\nfunction returns the value 0 if successful;\n"
154 "otherwise the value -1 is returned and the global\n"
155 "variable \\fIerrno\\fP is set to indicate the error."
156 }, /* Rv */
157 { NULL, NULL, NULL, NULL, NULL }, /* St */
158 { NULL, pre_em, post_font, NULL, NULL }, /* Va */
159 { NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
160 { NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
161 { NULL, NULL, post_percent, NULL, NULL }, /* %A */
162 { NULL, pre_em, post_percent, NULL, NULL }, /* %B */
163 { NULL, NULL, post_percent, NULL, NULL }, /* %D */
164 { NULL, pre_em, post_percent, NULL, NULL }, /* %I */
165 { NULL, pre_em, post_percent, NULL, NULL }, /* %J */
166 { NULL, NULL, post_percent, NULL, NULL }, /* %N */
167 { NULL, NULL, post_percent, NULL, NULL }, /* %O */
168 { NULL, NULL, post_percent, NULL, NULL }, /* %P */
169 { NULL, NULL, post_percent, NULL, NULL }, /* %R */
170 { NULL, pre__t, post__t, NULL, NULL }, /* %T */
171 { NULL, NULL, post_percent, NULL, NULL }, /* %V */
172 { NULL, NULL, NULL, NULL, NULL }, /* Ac */
173 { cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
174 { cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
175 { NULL, NULL, NULL, NULL, NULL }, /* At */
176 { NULL, NULL, NULL, NULL, NULL }, /* Bc */
177 { NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
178 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
179 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
180 { NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
181 { NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
182 { NULL, NULL, NULL, NULL, NULL }, /* Db */
183 { NULL, NULL, NULL, NULL, NULL }, /* Dc */
184 { cond_body, pre_enc, post_enc, "``", "''" }, /* Do */
185 { cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */
186 { NULL, NULL, NULL, NULL, NULL }, /* Ec */
187 { NULL, NULL, NULL, NULL, NULL }, /* Ef */
188 { NULL, pre_em, post_font, NULL, NULL }, /* Em */
189 { NULL, NULL, post_eo, NULL, NULL }, /* Eo */
190 { NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
191 { NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
192 { NULL, pre_no, NULL, NULL, NULL }, /* No */
193 { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
194 { NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
195 { NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
196 { NULL, NULL, NULL, NULL, NULL }, /* Pc */
197 { NULL, NULL, post_pf, NULL, NULL }, /* Pf */
198 { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
199 { cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
200 { NULL, NULL, NULL, NULL, NULL }, /* Qc */
201 { cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */
202 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
203 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
204 { NULL, NULL, NULL, NULL, NULL }, /* Re */
205 { cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
206 { NULL, NULL, NULL, NULL, NULL }, /* Sc */
207 { cond_body, pre_enc, post_enc, "`", "'" }, /* So */
208 { cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */
209 { NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
210 { NULL, pre_em, post_font, NULL, NULL }, /* Sx */
211 { NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
212 { NULL, pre_li, post_font, NULL, NULL }, /* Tn */
213 { NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
214 { NULL, NULL, NULL, NULL, NULL }, /* Xc */
215 { NULL, NULL, NULL, NULL, NULL }, /* Xo */
216 { NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
217 { NULL, NULL, NULL, NULL, NULL }, /* Fc */
218 { cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
219 { NULL, NULL, NULL, NULL, NULL }, /* Oc */
220 { NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
221 { NULL, NULL, NULL, NULL, NULL }, /* Ek */
222 { NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
223 { NULL, NULL, NULL, NULL, NULL }, /* Hf */
224 { NULL, NULL, NULL, NULL, NULL }, /* Fr */
225 { NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
226 { NULL, NULL, post_lb, NULL, NULL }, /* Lb */
227 { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
228 { NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
229 { NULL, pre_em, post_font, NULL, NULL }, /* Mt */
230 { cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
231 { cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
232 { NULL, NULL, NULL, NULL, NULL }, /* Brc */
233 { NULL, NULL, post_percent, NULL, NULL }, /* %C */
234 { NULL, NULL, NULL, NULL, NULL }, /* Es */
235 { NULL, NULL, NULL, NULL, NULL }, /* En */
236 { NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
237 { NULL, NULL, post_percent, NULL, NULL }, /* %Q */
238 { NULL, pre_br, NULL, NULL, NULL }, /* br */
239 { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
240 { NULL, NULL, post_percent, NULL, NULL }, /* %U */
241 { NULL, NULL, NULL, NULL, NULL }, /* Ta */
242 { NULL, NULL, NULL, NULL, NULL }, /* ROOT */
243 };
244
245 static int outflags;
246 #define MMAN_spc (1 << 0) /* blank character before next word */
247 #define MMAN_spc_force (1 << 1) /* even before trailing punctuation */
248 #define MMAN_nl (1 << 2) /* break man(7) code line */
249 #define MMAN_br (1 << 3) /* break output line */
250 #define MMAN_sp (1 << 4) /* insert a blank output line */
251 #define MMAN_PP (1 << 5) /* reset indentation etc. */
252 #define MMAN_Sm (1 << 6) /* horizontal spacing mode */
253 #define MMAN_Bk (1 << 7) /* word keep mode */
254 #define MMAN_An_split (1 << 8) /* author mode is "split" */
255 #define MMAN_An_nosplit (1 << 9) /* author mode is "nosplit" */
256
257 static int TPremain; /* characters before tag is full */
258
259 static struct {
260 char *head;
261 char *tail;
262 size_t size;
263 } fontqueue;
264
265 static void
266 font_push(char newfont)
267 {
268
269 if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
270 fontqueue.size += 8;
271 fontqueue.head = mandoc_realloc(fontqueue.head,
272 fontqueue.size);
273 }
274 *fontqueue.tail = newfont;
275 print_word("");
276 printf("\\f");
277 putchar(newfont);
278 outflags &= ~MMAN_spc;
279 }
280
281 static void
282 font_pop(void)
283 {
284
285 if (fontqueue.tail > fontqueue.head)
286 fontqueue.tail--;
287 outflags &= ~MMAN_spc;
288 print_word("");
289 printf("\\f");
290 putchar(*fontqueue.tail);
291 }
292
293 static void
294 print_word(const char *s)
295 {
296
297 if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
298 /*
299 * If we need a newline, print it now and start afresh.
300 */
301 if (MMAN_PP & outflags) {
302 if ( ! (MMAN_sp & outflags))
303 printf("\n.sp -1v");
304 printf("\n.PP\n");
305 } else if (MMAN_sp & outflags)
306 printf("\n.sp\n");
307 else if (MMAN_br & outflags)
308 printf("\n.br\n");
309 else if (MMAN_nl & outflags)
310 putchar('\n');
311 outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
312 if (1 == TPremain)
313 printf(".br\n");
314 TPremain = 0;
315 } else if (MMAN_spc & outflags) {
316 /*
317 * If we need a space, only print it if
318 * (1) it is forced by `No' or
319 * (2) what follows is not terminating punctuation or
320 * (3) what follows is longer than one character.
321 */
322 if (MMAN_spc_force & outflags || '\0' == s[0] ||
323 NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
324 if (MMAN_Bk & outflags) {
325 putchar('\\');
326 putchar('~');
327 } else
328 putchar(' ');
329 if (TPremain)
330 TPremain--;
331 }
332 }
333
334 /*
335 * Reassign needing space if we're not following opening
336 * punctuation.
337 */
338 if (MMAN_Sm & outflags && ('\0' == s[0] ||
339 (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
340 outflags |= MMAN_spc;
341 else
342 outflags &= ~MMAN_spc;
343 outflags &= ~MMAN_spc_force;
344
345 for ( ; *s; s++) {
346 switch (*s) {
347 case (ASCII_NBRSP):
348 printf("\\~");
349 break;
350 case (ASCII_HYPH):
351 putchar('-');
352 break;
353 default:
354 putchar((unsigned char)*s);
355 break;
356 }
357 if (TPremain)
358 TPremain--;
359 }
360 }
361
362 static void
363 print_line(const char *s, int newflags)
364 {
365
366 outflags &= ~MMAN_br;
367 outflags |= MMAN_nl;
368 print_word(s);
369 outflags |= newflags;
370 }
371
372 static void
373 print_block(const char *s, int newflags)
374 {
375
376 outflags &= ~MMAN_PP;
377 if (MMAN_sp & outflags)
378 outflags &= ~(MMAN_sp | MMAN_br);
379 else
380 print_line(".sp -1v", 0);
381 outflags |= MMAN_nl;
382 print_word(s);
383 outflags |= newflags;
384 }
385
386 static void
387 print_offs(const char *v)
388 {
389 char buf[24];
390 struct roffsu su;
391 size_t sz;
392
393 if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
394 sz = 0;
395 else if (0 == strcmp(v, "indent"))
396 sz = 6;
397 else if (0 == strcmp(v, "indent-two"))
398 sz = 12;
399 else if (a2roffsu(v, &su, SCALE_MAX)) {
400 print_word(v);
401 return;
402 } else
403 sz = strlen(v);
404
405 snprintf(buf, sizeof(buf), "%ldn", sz);
406 print_word(buf);
407 }
408
409 void
410 print_width(const char *v, const struct mdoc_node *child, size_t defsz)
411 {
412 char buf[24];
413 struct roffsu su;
414 size_t sz, chsz;
415 int numeric, remain;
416
417 numeric = 1;
418 remain = 0;
419 if (NULL == v)
420 sz = defsz;
421 else if (a2roffsu(v, &su, SCALE_MAX)) {
422 if (SCALE_EN == su.unit)
423 sz = su.scale;
424 else {
425 sz = 0;
426 numeric = 0;
427 }
428 } else
429 sz = strlen(v);
430
431 /* XXX Rough estimation, might have multiple parts. */
432 chsz = (NULL != child && MDOC_TEXT == child->type) ?
433 strlen(child->string) : 0;
434
435 if (defsz && chsz > sz)
436 print_block(".HP", 0);
437 else {
438 print_block(".TP", 0);
439 remain = sz + 2;
440 }
441 if (numeric) {
442 snprintf(buf, sizeof(buf), "%ldn", sz + 2);
443 print_word(buf);
444 } else
445 print_word(v);
446 TPremain = remain;
447 }
448
449 void
450 print_count(int *count)
451 {
452 char buf[12];
453
454 snprintf(buf, sizeof(buf), "%d.", ++*count);
455 print_word(buf);
456 }
457
458 void
459 man_man(void *arg, const struct man *man)
460 {
461
462 /*
463 * Dump the keep buffer.
464 * We're guaranteed by now that this exists (is non-NULL).
465 * Flush stdout afterward, just in case.
466 */
467 fputs(mparse_getkeep(man_mparse(man)), stdout);
468 fflush(stdout);
469 }
470
471 void
472 man_mdoc(void *arg, const struct mdoc *mdoc)
473 {
474 const struct mdoc_meta *m;
475 const struct mdoc_node *n;
476
477 m = mdoc_meta(mdoc);
478 n = mdoc_node(mdoc);
479
480 printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
481 m->title, m->msec, m->date, m->os, m->vol);
482
483 outflags = MMAN_nl | MMAN_Sm;
484 if (0 == fontqueue.size) {
485 fontqueue.size = 8;
486 fontqueue.head = fontqueue.tail = mandoc_malloc(8);
487 *fontqueue.tail = 'R';
488 }
489 print_node(m, n);
490 putchar('\n');
491 }
492
493 static void
494 print_node(DECL_ARGS)
495 {
496 const struct mdoc_node *prev, *sub;
497 const struct manact *act;
498 int cond, do_sub;
499
500 /*
501 * Break the line if we were parsed subsequent the current node.
502 * This makes the page structure be more consistent.
503 */
504 prev = n->prev ? n->prev : n->parent;
505 if (MMAN_spc & outflags && prev && prev->line < n->line)
506 outflags |= MMAN_nl;
507
508 act = NULL;
509 cond = 0;
510 do_sub = 1;
511
512 if (MDOC_TEXT == n->type) {
513 /*
514 * Make sure that we don't happen to start with a
515 * control character at the start of a line.
516 */
517 if (MMAN_nl & outflags && ('.' == *n->string ||
518 '\'' == *n->string)) {
519 print_word("");
520 printf("\\&");
521 outflags &= ~MMAN_spc;
522 }
523 print_word(n->string);
524 } else {
525 /*
526 * Conditionally run the pre-node action handler for a
527 * node.
528 */
529 act = manacts + n->tok;
530 cond = NULL == act->cond || (*act->cond)(m, n);
531 if (cond && act->pre)
532 do_sub = (*act->pre)(m, n);
533 }
534
535 /*
536 * Conditionally run all child nodes.
537 * Note that this iterates over children instead of using
538 * recursion. This prevents unnecessary depth in the stack.
539 */
540 if (do_sub)
541 for (sub = n->child; sub; sub = sub->next)
542 print_node(m, sub);
543
544 /*
545 * Lastly, conditionally run the post-node handler.
546 */
547 if (cond && act->post)
548 (*act->post)(m, n);
549 }
550
551 static int
552 cond_head(DECL_ARGS)
553 {
554
555 return(MDOC_HEAD == n->type);
556 }
557
558 static int
559 cond_body(DECL_ARGS)
560 {
561
562 return(MDOC_BODY == n->type);
563 }
564
565 static int
566 pre_enc(DECL_ARGS)
567 {
568 const char *prefix;
569
570 prefix = manacts[n->tok].prefix;
571 if (NULL == prefix)
572 return(1);
573 print_word(prefix);
574 outflags &= ~MMAN_spc;
575 return(1);
576 }
577
578 static void
579 post_enc(DECL_ARGS)
580 {
581 const char *suffix;
582
583 suffix = manacts[n->tok].suffix;
584 if (NULL == suffix)
585 return;
586 outflags &= ~MMAN_spc;
587 print_word(suffix);
588 }
589
590 static void
591 post_font(DECL_ARGS)
592 {
593
594 font_pop();
595 }
596
597 static void
598 post_percent(DECL_ARGS)
599 {
600
601 if (pre_em == manacts[n->tok].pre)
602 font_pop();
603 if (n->next) {
604 print_word(",");
605 if (n->prev && n->prev->tok == n->tok &&
606 n->next->tok == n->tok)
607 print_word("and");
608 } else {
609 print_word(".");
610 outflags |= MMAN_nl;
611 }
612 }
613
614 static int
615 pre__t(DECL_ARGS)
616 {
617
618 if (n->parent && MDOC_Rs == n->parent->tok &&
619 n->parent->norm->Rs.quote_T) {
620 print_word("");
621 putchar('\"');
622 outflags &= ~MMAN_spc;
623 } else
624 font_push('I');
625 return(1);
626 }
627
628 static void
629 post__t(DECL_ARGS)
630 {
631
632 if (n->parent && MDOC_Rs == n->parent->tok &&
633 n->parent->norm->Rs.quote_T) {
634 outflags &= ~MMAN_spc;
635 print_word("");
636 putchar('\"');
637 } else
638 font_pop();
639 post_percent(m, n);
640 }
641
642 /*
643 * Print before a section header.
644 */
645 static int
646 pre_sect(DECL_ARGS)
647 {
648
649 if (MDOC_HEAD != n->type)
650 return(1);
651 outflags |= MMAN_sp;
652 print_block(manacts[n->tok].prefix, 0);
653 print_word("");
654 putchar('\"');
655 outflags &= ~MMAN_spc;
656 return(1);
657 }
658
659 /*
660 * Print subsequent a section header.
661 */
662 static void
663 post_sect(DECL_ARGS)
664 {
665
666 if (MDOC_HEAD != n->type)
667 return;
668 outflags &= ~MMAN_spc;
669 print_word("");
670 putchar('\"');
671 outflags |= MMAN_nl;
672 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
673 outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
674 }
675
676 /* See mdoc_term.c, synopsis_pre() for comments. */
677 static void
678 pre_syn(const struct mdoc_node *n)
679 {
680
681 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
682 return;
683
684 if (n->prev->tok == n->tok &&
685 MDOC_Ft != n->tok &&
686 MDOC_Fo != n->tok &&
687 MDOC_Fn != n->tok) {
688 outflags |= MMAN_br;
689 return;
690 }
691
692 switch (n->prev->tok) {
693 case (MDOC_Fd):
694 /* FALLTHROUGH */
695 case (MDOC_Fn):
696 /* FALLTHROUGH */
697 case (MDOC_Fo):
698 /* FALLTHROUGH */
699 case (MDOC_In):
700 /* FALLTHROUGH */
701 case (MDOC_Vt):
702 outflags |= MMAN_sp;
703 break;
704 case (MDOC_Ft):
705 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
706 outflags |= MMAN_sp;
707 break;
708 }
709 /* FALLTHROUGH */
710 default:
711 outflags |= MMAN_br;
712 break;
713 }
714 }
715
716 static int
717 pre_an(DECL_ARGS)
718 {
719
720 switch (n->norm->An.auth) {
721 case (AUTH_split):
722 outflags &= ~MMAN_An_nosplit;
723 outflags |= MMAN_An_split;
724 return(0);
725 case (AUTH_nosplit):
726 outflags &= ~MMAN_An_split;
727 outflags |= MMAN_An_nosplit;
728 return(0);
729 default:
730 if (MMAN_An_split & outflags)
731 outflags |= MMAN_br;
732 else if (SEC_AUTHORS == n->sec &&
733 ! (MMAN_An_nosplit & outflags))
734 outflags |= MMAN_An_split;
735 return(1);
736 }
737 }
738
739 static int
740 pre_ap(DECL_ARGS)
741 {
742
743 outflags &= ~MMAN_spc;
744 print_word("'");
745 outflags &= ~MMAN_spc;
746 return(0);
747 }
748
749 static int
750 pre_bd(DECL_ARGS)
751 {
752
753 outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
754
755 if (DISP_unfilled == n->norm->Bd.type ||
756 DISP_literal == n->norm->Bd.type)
757 print_line(".nf", 0);
758 if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
759 outflags |= MMAN_sp;
760 print_line(".RS", 0);
761 print_offs(n->norm->Bd.offs);
762 outflags |= MMAN_nl;
763 return(1);
764 }
765
766 static void
767 post_bd(DECL_ARGS)
768 {
769
770 print_line(".RE", MMAN_nl);
771 if (DISP_unfilled == n->norm->Bd.type ||
772 DISP_literal == n->norm->Bd.type)
773 print_line(".fi", MMAN_nl);
774 }
775
776 static int
777 pre_bf(DECL_ARGS)
778 {
779
780 switch (n->type) {
781 case (MDOC_BLOCK):
782 return(1);
783 case (MDOC_BODY):
784 break;
785 default:
786 return(0);
787 }
788 switch (n->norm->Bf.font) {
789 case (FONT_Em):
790 font_push('I');
791 break;
792 case (FONT_Sy):
793 font_push('B');
794 break;
795 default:
796 font_push('R');
797 break;
798 }
799 return(1);
800 }
801
802 static void
803 post_bf(DECL_ARGS)
804 {
805
806 if (MDOC_BODY == n->type)
807 font_pop();
808 }
809
810 static int
811 pre_bk(DECL_ARGS)
812 {
813
814 switch (n->type) {
815 case (MDOC_BLOCK):
816 return(1);
817 case (MDOC_BODY):
818 outflags |= MMAN_Bk;
819 return(1);
820 default:
821 return(0);
822 }
823 }
824
825 static void
826 post_bk(DECL_ARGS)
827 {
828
829 if (MDOC_BODY == n->type)
830 outflags &= ~MMAN_Bk;
831 }
832
833 static int
834 pre_bl(DECL_ARGS)
835 {
836 size_t icol;
837
838 switch (n->norm->Bl.type) {
839 case (LIST_enum):
840 n->norm->Bl.count = 0;
841 return(1);
842 case (LIST_column):
843 break;
844 default:
845 return(1);
846 }
847
848 print_line(".TS", MMAN_nl);
849 for (icol = 0; icol < n->norm->Bl.ncols; icol++)
850 print_word("l");
851 print_word(".");
852 outflags |= MMAN_nl;
853 return(1);
854 }
855
856 static void
857 post_bl(DECL_ARGS)
858 {
859
860 switch (n->norm->Bl.type) {
861 case (LIST_column):
862 print_line(".TE", 0);
863 break;
864 case (LIST_enum):
865 n->norm->Bl.count = 0;
866 break;
867 default:
868 break;
869 }
870 outflags |= MMAN_PP | MMAN_nl;
871 outflags &= ~(MMAN_sp | MMAN_br);
872 }
873
874 static int
875 pre_br(DECL_ARGS)
876 {
877
878 outflags |= MMAN_br;
879 return(0);
880 }
881
882 static int
883 pre_bx(DECL_ARGS)
884 {
885
886 n = n->child;
887 if (n) {
888 print_word(n->string);
889 outflags &= ~MMAN_spc;
890 n = n->next;
891 }
892 print_word("BSD");
893 if (NULL == n)
894 return(0);
895 outflags &= ~MMAN_spc;
896 print_word("-");
897 outflags &= ~MMAN_spc;
898 print_word(n->string);
899 return(0);
900 }
901
902 static int
903 pre_dl(DECL_ARGS)
904 {
905
906 print_line(".RS 6n", MMAN_nl);
907 return(1);
908 }
909
910 static void
911 post_dl(DECL_ARGS)
912 {
913
914 print_line(".RE", MMAN_nl);
915 }
916
917 static int
918 pre_em(DECL_ARGS)
919 {
920
921 font_push('I');
922 return(1);
923 }
924
925 static void
926 post_eo(DECL_ARGS)
927 {
928
929 if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
930 outflags &= ~MMAN_spc;
931 }
932
933 static int
934 pre_fa(DECL_ARGS)
935 {
936
937 if (MDOC_Fa == n->tok)
938 n = n->child;
939
940 while (NULL != n) {
941 font_push('I');
942 print_node(m, n);
943 font_pop();
944 if (NULL != (n = n->next))
945 print_word(",");
946 }
947 return(0);
948 }
949
950 static void
951 post_fa(DECL_ARGS)
952 {
953
954 if (NULL != n->next && MDOC_Fa == n->next->tok)
955 print_word(",");
956 }
957
958 static int
959 pre_fd(DECL_ARGS)
960 {
961
962 pre_syn(n);
963 font_push('B');
964 return(1);
965 }
966
967 static void
968 post_fd(DECL_ARGS)
969 {
970
971 font_pop();
972 outflags |= MMAN_br;
973 }
974
975 static int
976 pre_fl(DECL_ARGS)
977 {
978
979 font_push('B');
980 print_word("-");
981 outflags &= ~MMAN_spc;
982 return(1);
983 }
984
985 static void
986 post_fl(DECL_ARGS)
987 {
988
989 font_pop();
990 if (0 == n->nchild && NULL != n->next &&
991 n->next->line == n->line)
992 outflags &= ~MMAN_spc;
993 }
994
995 static int
996 pre_fn(DECL_ARGS)
997 {
998
999 pre_syn(n);
1000
1001 n = n->child;
1002 if (NULL == n)
1003 return(0);
1004
1005 font_push('B');
1006 print_node(m, n);
1007 font_pop();
1008 outflags &= ~MMAN_spc;
1009 print_word("(");
1010 outflags &= ~MMAN_spc;
1011
1012 n = n->next;
1013 if (NULL != n)
1014 pre_fa(m, n);
1015 return(0);
1016 }
1017
1018 static void
1019 post_fn(DECL_ARGS)
1020 {
1021
1022 print_word(")");
1023 if (MDOC_SYNPRETTY & n->flags) {
1024 print_word(";");
1025 outflags |= MMAN_br;
1026 }
1027 }
1028
1029 static int
1030 pre_fo(DECL_ARGS)
1031 {
1032
1033 switch (n->type) {
1034 case (MDOC_BLOCK):
1035 pre_syn(n);
1036 break;
1037 case (MDOC_HEAD):
1038 font_push('B');
1039 break;
1040 case (MDOC_BODY):
1041 outflags &= ~MMAN_spc;
1042 print_word("(");
1043 outflags &= ~MMAN_spc;
1044 break;
1045 default:
1046 break;
1047 }
1048 return(1);
1049 }
1050
1051 static void
1052 post_fo(DECL_ARGS)
1053 {
1054
1055 switch (n->type) {
1056 case (MDOC_HEAD):
1057 font_pop();
1058 break;
1059 case (MDOC_BODY):
1060 post_fn(m, n);
1061 break;
1062 default:
1063 break;
1064 }
1065 }
1066
1067 static int
1068 pre_ft(DECL_ARGS)
1069 {
1070
1071 pre_syn(n);
1072 font_push('I');
1073 return(1);
1074 }
1075
1076 static int
1077 pre_in(DECL_ARGS)
1078 {
1079
1080 if (MDOC_SYNPRETTY & n->flags) {
1081 pre_syn(n);
1082 font_push('B');
1083 print_word("#include <");
1084 outflags &= ~MMAN_spc;
1085 } else {
1086 print_word("<");
1087 outflags &= ~MMAN_spc;
1088 font_push('I');
1089 }
1090 return(1);
1091 }
1092
1093 static void
1094 post_in(DECL_ARGS)
1095 {
1096
1097 if (MDOC_SYNPRETTY & n->flags) {
1098 outflags &= ~MMAN_spc;
1099 print_word(">");
1100 font_pop();
1101 outflags |= MMAN_br;
1102 } else {
1103 font_pop();
1104 outflags &= ~MMAN_spc;
1105 print_word(">");
1106 }
1107 }
1108
1109 static int
1110 pre_it(DECL_ARGS)
1111 {
1112 const struct mdoc_node *bln;
1113
1114 switch (n->type) {
1115 case (MDOC_HEAD):
1116 outflags |= MMAN_PP | MMAN_nl;
1117 bln = n->parent->parent;
1118 if (0 == bln->norm->Bl.comp ||
1119 (NULL == n->parent->prev &&
1120 NULL == bln->parent->prev))
1121 outflags |= MMAN_sp;
1122 outflags &= ~MMAN_br;
1123 switch (bln->norm->Bl.type) {
1124 case (LIST_item):
1125 return(0);
1126 case (LIST_inset):
1127 /* FALLTHROUGH */
1128 case (LIST_diag):
1129 /* FALLTHROUGH */
1130 case (LIST_ohang):
1131 if (bln->norm->Bl.type == LIST_diag)
1132 print_line(".B \"", 0);
1133 else
1134 print_line(".R \"", 0);
1135 outflags &= ~MMAN_spc;
1136 return(1);
1137 case (LIST_bullet):
1138 /* FALLTHROUGH */
1139 case (LIST_dash):
1140 /* FALLTHROUGH */
1141 case (LIST_hyphen):
1142 print_width(bln->norm->Bl.width, NULL, 0);
1143 TPremain = 0;
1144 outflags |= MMAN_nl;
1145 font_push('B');
1146 if (LIST_bullet == bln->norm->Bl.type)
1147 print_word("o");
1148 else
1149 print_word("-");
1150 font_pop();
1151 break;
1152 case (LIST_enum):
1153 print_width(bln->norm->Bl.width, NULL, 0);
1154 TPremain = 0;
1155 outflags |= MMAN_nl;
1156 print_count(&bln->norm->Bl.count);
1157 break;
1158 case (LIST_hang):
1159 print_width(bln->norm->Bl.width, n->child, 6);
1160 TPremain = 0;
1161 break;
1162 case (LIST_tag):
1163 print_width(bln->norm->Bl.width, n->child, 0);
1164 putchar('\n');
1165 outflags &= ~MMAN_spc;
1166 return(1);
1167 default:
1168 return(1);
1169 }
1170 outflags |= MMAN_nl;
1171 default:
1172 break;
1173 }
1174 return(1);
1175 }
1176
1177 static void
1178 post_it(DECL_ARGS)
1179 {
1180 const struct mdoc_node *bln;
1181
1182 bln = n->parent->parent;
1183
1184 switch (n->type) {
1185 case (MDOC_HEAD):
1186 switch (bln->norm->Bl.type) {
1187 case (LIST_diag):
1188 outflags &= ~MMAN_spc;
1189 print_word("\\ ");
1190 break;
1191 case (LIST_ohang):
1192 outflags |= MMAN_br;
1193 break;
1194 default:
1195 break;
1196 }
1197 break;
1198 case (MDOC_BODY):
1199 if (LIST_column == bln->norm->Bl.type &&
1200 NULL != n->next) {
1201 putchar('\t');
1202 outflags &= ~MMAN_spc;
1203 }
1204 break;
1205 default:
1206 break;
1207 }
1208 }
1209
1210 static void
1211 post_lb(DECL_ARGS)
1212 {
1213
1214 if (SEC_LIBRARY == n->sec)
1215 outflags |= MMAN_br;
1216 }
1217
1218 static int
1219 pre_lk(DECL_ARGS)
1220 {
1221 const struct mdoc_node *link, *descr;
1222
1223 if (NULL == (link = n->child))
1224 return(0);
1225
1226 if (NULL != (descr = link->next)) {
1227 font_push('I');
1228 while (NULL != descr) {
1229 print_word(descr->string);
1230 descr = descr->next;
1231 }
1232 print_word(":");
1233 font_pop();
1234 }
1235
1236 font_push('B');
1237 print_word(link->string);
1238 font_pop();
1239 return(0);
1240 }
1241
1242 static int
1243 pre_li(DECL_ARGS)
1244 {
1245
1246 font_push('R');
1247 return(1);
1248 }
1249
1250 static int
1251 pre_nm(DECL_ARGS)
1252 {
1253 char *name;
1254
1255 if (MDOC_BLOCK == n->type)
1256 pre_syn(n);
1257 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1258 return(1);
1259 name = n->child ? n->child->string : m->name;
1260 if (NULL == name)
1261 return(0);
1262 if (MDOC_HEAD == n->type) {
1263 if (NULL == n->parent->prev)
1264 outflags |= MMAN_sp;
1265 print_block(".HP", 0);
1266 printf(" %ldn", strlen(name) + 1);
1267 outflags |= MMAN_nl;
1268 }
1269 font_push('B');
1270 if (NULL == n->child)
1271 print_word(m->name);
1272 return(1);
1273 }
1274
1275 static void
1276 post_nm(DECL_ARGS)
1277 {
1278
1279 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1280 return;
1281 font_pop();
1282 }
1283
1284 static int
1285 pre_no(DECL_ARGS)
1286 {
1287
1288 outflags |= MMAN_spc_force;
1289 return(1);
1290 }
1291
1292 static int
1293 pre_ns(DECL_ARGS)
1294 {
1295
1296 outflags &= ~MMAN_spc;
1297 return(0);
1298 }
1299
1300 static void
1301 post_pf(DECL_ARGS)
1302 {
1303
1304 outflags &= ~MMAN_spc;
1305 }
1306
1307 static int
1308 pre_pp(DECL_ARGS)
1309 {
1310
1311 if (MDOC_It != n->parent->tok)
1312 outflags |= MMAN_PP;
1313 outflags |= MMAN_sp | MMAN_nl;
1314 outflags &= ~MMAN_br;
1315 return(0);
1316 }
1317
1318 static int
1319 pre_rs(DECL_ARGS)
1320 {
1321
1322 if (SEC_SEE_ALSO == n->sec) {
1323 outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1324 outflags &= ~MMAN_br;
1325 }
1326 return(1);
1327 }
1328
1329 static int
1330 pre_sm(DECL_ARGS)
1331 {
1332
1333 assert(n->child && MDOC_TEXT == n->child->type);
1334 if (0 == strcmp("on", n->child->string))
1335 outflags |= MMAN_Sm | MMAN_spc;
1336 else
1337 outflags &= ~MMAN_Sm;
1338 return(0);
1339 }
1340
1341 static int
1342 pre_sp(DECL_ARGS)
1343 {
1344
1345 if (MMAN_PP & outflags) {
1346 outflags &= ~MMAN_PP;
1347 print_line(".PP", 0);
1348 } else
1349 print_line(".sp", 0);
1350 return(1);
1351 }
1352
1353 static void
1354 post_sp(DECL_ARGS)
1355 {
1356
1357 outflags |= MMAN_nl;
1358 }
1359
1360 static int
1361 pre_sy(DECL_ARGS)
1362 {
1363
1364 font_push('B');
1365 return(1);
1366 }
1367
1368 static int
1369 pre_vt(DECL_ARGS)
1370 {
1371
1372 if (MDOC_SYNPRETTY & n->flags) {
1373 switch (n->type) {
1374 case (MDOC_BLOCK):
1375 pre_syn(n);
1376 return(1);
1377 case (MDOC_BODY):
1378 break;
1379 default:
1380 return(0);
1381 }
1382 }
1383 font_push('I');
1384 return(1);
1385 }
1386
1387 static void
1388 post_vt(DECL_ARGS)
1389 {
1390
1391 if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
1392 return;
1393 font_pop();
1394 }
1395
1396 static int
1397 pre_xr(DECL_ARGS)
1398 {
1399
1400 n = n->child;
1401 if (NULL == n)
1402 return(0);
1403 print_node(m, n);
1404 n = n->next;
1405 if (NULL == n)
1406 return(0);
1407 outflags &= ~MMAN_spc;
1408 print_word("(");
1409 print_node(m, n);
1410 print_word(")");
1411 return(0);
1412 }
1413
1414 static int
1415 pre_ux(DECL_ARGS)
1416 {
1417
1418 print_word(manacts[n->tok].prefix);
1419 if (NULL == n->child)
1420 return(0);
1421 outflags &= ~MMAN_spc;
1422 print_word("\\~");
1423 outflags &= ~MMAN_spc;
1424 return(1);
1425 }