]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
Removed ambiguous TERMPAIR_SETFLAG from mdoc_term.c (pairs/flags must be manually...
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.28 2009/07/12 17:25:07 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <sys/types.h>
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "term.h"
27 #include "mdoc.h"
28
29 /* FIXME: macro arguments can be escaped. */
30 /* FIXME: support more offset/width tokens. */
31
32 #define TTYPE_PROG 0
33 #define TTYPE_CMD_FLAG 1
34 #define TTYPE_CMD_ARG 2
35 #define TTYPE_SECTION 3
36 #define TTYPE_FUNC_DECL 4
37 #define TTYPE_VAR_DECL 5
38 #define TTYPE_FUNC_TYPE 6
39 #define TTYPE_FUNC_NAME 7
40 #define TTYPE_FUNC_ARG 8
41 #define TTYPE_LINK 9
42 #define TTYPE_SSECTION 10
43 #define TTYPE_FILE 11
44 #define TTYPE_EMPH 12
45 #define TTYPE_CONFIG 13
46 #define TTYPE_CMD 14
47 #define TTYPE_INCLUDE 15
48 #define TTYPE_SYMB 16
49 #define TTYPE_SYMBOL 17
50 #define TTYPE_DIAG 18
51 #define TTYPE_LINK_ANCHOR 19
52 #define TTYPE_LINK_TEXT 20
53 #define TTYPE_REF_JOURNAL 21
54 #define TTYPE_LIST 22
55 #define TTYPE_NMAX 23
56
57 const int ttypes[TTYPE_NMAX] = {
58 TERMP_BOLD, /* TTYPE_PROG */
59 TERMP_BOLD, /* TTYPE_CMD_FLAG */
60 TERMP_UNDER, /* TTYPE_CMD_ARG */
61 TERMP_BOLD, /* TTYPE_SECTION */
62 TERMP_BOLD, /* TTYPE_FUNC_DECL */
63 TERMP_UNDER, /* TTYPE_VAR_DECL */
64 TERMP_UNDER, /* TTYPE_FUNC_TYPE */
65 TERMP_BOLD, /* TTYPE_FUNC_NAME */
66 TERMP_UNDER, /* TTYPE_FUNC_ARG */
67 TERMP_UNDER, /* TTYPE_LINK */
68 TERMP_BOLD, /* TTYPE_SSECTION */
69 TERMP_UNDER, /* TTYPE_FILE */
70 TERMP_UNDER, /* TTYPE_EMPH */
71 TERMP_BOLD, /* TTYPE_CONFIG */
72 TERMP_BOLD, /* TTYPE_CMD */
73 TERMP_BOLD, /* TTYPE_INCLUDE */
74 TERMP_BOLD, /* TTYPE_SYMB */
75 TERMP_BOLD, /* TTYPE_SYMBOL */
76 TERMP_BOLD, /* TTYPE_DIAG */
77 TERMP_UNDER, /* TTYPE_LINK_ANCHOR */
78 TERMP_BOLD, /* TTYPE_LINK_TEXT */
79 TERMP_UNDER, /* TTYPE_REF_JOURNAL */
80 TERMP_BOLD /* TTYPE_LIST */
81 };
82
83 /*
84 * This is used to preserve a style of value across a macro, instead of
85 * losing it while the body is processed.
86 */
87 struct termpair {
88 struct termpair *ppair;
89 int flag; /* Whether being used. */
90 size_t offset; /* Left margin. */
91 size_t rmargin; /* Right margin. */
92 int count; /* Enum count. */
93 };
94
95 #define DECL_ARGS \
96 struct termp *p, struct termpair *pair, \
97 const struct mdoc_meta *meta, \
98 const struct mdoc_node *node
99
100 #define DECL_PRE(name) \
101 static int name##_pre(DECL_ARGS)
102 #define DECL_POST(name) \
103 static void name##_post(DECL_ARGS)
104 #define DECL_PREPOST(name) \
105 DECL_PRE(name); \
106 DECL_POST(name);
107
108 DECL_PREPOST(termp__t);
109 DECL_PREPOST(termp_aq);
110 DECL_PREPOST(termp_bd);
111 DECL_PREPOST(termp_bq);
112 DECL_PREPOST(termp_brq);
113 DECL_PREPOST(termp_d1);
114 DECL_PREPOST(termp_dq);
115 DECL_PREPOST(termp_fd);
116 DECL_PREPOST(termp_fn);
117 DECL_PREPOST(termp_fo);
118 DECL_PREPOST(termp_ft);
119 DECL_PREPOST(termp_in);
120 DECL_PREPOST(termp_it);
121 DECL_PREPOST(termp_lb);
122 DECL_PREPOST(termp_op);
123 DECL_PREPOST(termp_pf);
124 DECL_PREPOST(termp_pq);
125 DECL_PREPOST(termp_qq);
126 DECL_PREPOST(termp_sh);
127 DECL_PREPOST(termp_ss);
128 DECL_PREPOST(termp_sq);
129 DECL_PREPOST(termp_vt);
130
131 DECL_PRE(termp__j);
132 DECL_PRE(termp_ap);
133 DECL_PRE(termp_ar);
134 DECL_PRE(termp_at);
135 DECL_PRE(termp_bf);
136 DECL_PRE(termp_bt);
137 DECL_PRE(termp_cd);
138 DECL_PRE(termp_cm);
139 DECL_PRE(termp_em);
140 DECL_PRE(termp_ex);
141 DECL_PRE(termp_fa);
142 DECL_PRE(termp_fl);
143 DECL_PRE(termp_ic);
144 DECL_PRE(termp_lk);
145 DECL_PRE(termp_ms);
146 DECL_PRE(termp_mt);
147 DECL_PRE(termp_nd);
148 DECL_PRE(termp_nm);
149 DECL_PRE(termp_ns);
150 DECL_PRE(termp_xx);
151 DECL_PRE(termp_pa);
152 DECL_PRE(termp_pp);
153 DECL_PRE(termp_rs);
154 DECL_PRE(termp_rv);
155 DECL_PRE(termp_sm);
156 DECL_PRE(termp_st);
157 DECL_PRE(termp_sx);
158 DECL_PRE(termp_sy);
159 DECL_PRE(termp_ud);
160 DECL_PRE(termp_va);
161 DECL_PRE(termp_xr);
162
163 DECL_POST(termp___);
164 DECL_POST(termp_bl);
165 DECL_POST(termp_bx);
166
167 struct termact {
168 int (*pre)(DECL_ARGS);
169 void (*post)(DECL_ARGS);
170 };
171
172 static const struct termact termacts[MDOC_MAX] = {
173 { termp_ap_pre, NULL }, /* Ap */
174 { NULL, NULL }, /* Dd */
175 { NULL, NULL }, /* Dt */
176 { NULL, NULL }, /* Os */
177 { termp_sh_pre, termp_sh_post }, /* Sh */
178 { termp_ss_pre, termp_ss_post }, /* Ss */
179 { termp_pp_pre, NULL }, /* Pp */
180 { termp_d1_pre, termp_d1_post }, /* D1 */
181 { termp_d1_pre, termp_d1_post }, /* Dl */
182 { termp_bd_pre, termp_bd_post }, /* Bd */
183 { NULL, NULL }, /* Ed */
184 { NULL, termp_bl_post }, /* Bl */
185 { NULL, NULL }, /* El */
186 { termp_it_pre, termp_it_post }, /* It */
187 { NULL, NULL }, /* Ad */
188 { NULL, NULL }, /* An */
189 { termp_ar_pre, NULL }, /* Ar */
190 { termp_cd_pre, NULL }, /* Cd */
191 { termp_cm_pre, NULL }, /* Cm */
192 { NULL, NULL }, /* Dv */
193 { NULL, NULL }, /* Er */
194 { NULL, NULL }, /* Ev */
195 { termp_ex_pre, NULL }, /* Ex */
196 { termp_fa_pre, NULL }, /* Fa */
197 { termp_fd_pre, termp_fd_post }, /* Fd */
198 { termp_fl_pre, NULL }, /* Fl */
199 { termp_fn_pre, termp_fn_post }, /* Fn */
200 { termp_ft_pre, termp_ft_post }, /* Ft */
201 { termp_ic_pre, NULL }, /* Ic */
202 { termp_in_pre, termp_in_post }, /* In */
203 { NULL, NULL }, /* Li */
204 { termp_nd_pre, NULL }, /* Nd */
205 { termp_nm_pre, NULL }, /* Nm */
206 { termp_op_pre, termp_op_post }, /* Op */
207 { NULL, NULL }, /* Ot */
208 { termp_pa_pre, NULL }, /* Pa */
209 { termp_rv_pre, NULL }, /* Rv */
210 { termp_st_pre, NULL }, /* St */
211 { termp_va_pre, NULL }, /* Va */
212 { termp_vt_pre, termp_vt_post }, /* Vt */
213 { termp_xr_pre, NULL }, /* Xr */
214 { NULL, termp____post }, /* %A */
215 { NULL, termp____post }, /* %B */
216 { NULL, termp____post }, /* %D */
217 { NULL, termp____post }, /* %I */
218 { termp__j_pre, termp____post }, /* %J */
219 { NULL, termp____post }, /* %N */
220 { NULL, termp____post }, /* %O */
221 { NULL, termp____post }, /* %P */
222 { NULL, termp____post }, /* %R */
223 { termp__t_pre, termp__t_post }, /* %T */
224 { NULL, termp____post }, /* %V */
225 { NULL, NULL }, /* Ac */
226 { termp_aq_pre, termp_aq_post }, /* Ao */
227 { termp_aq_pre, termp_aq_post }, /* Aq */
228 { termp_at_pre, NULL }, /* At */
229 { NULL, NULL }, /* Bc */
230 { termp_bf_pre, NULL }, /* Bf */
231 { termp_bq_pre, termp_bq_post }, /* Bo */
232 { termp_bq_pre, termp_bq_post }, /* Bq */
233 { termp_xx_pre, NULL }, /* Bsx */
234 { NULL, termp_bx_post }, /* Bx */
235 { NULL, NULL }, /* Db */
236 { NULL, NULL }, /* Dc */
237 { termp_dq_pre, termp_dq_post }, /* Do */
238 { termp_dq_pre, termp_dq_post }, /* Dq */
239 { NULL, NULL }, /* Ec */
240 { NULL, NULL }, /* Ef */
241 { termp_em_pre, NULL }, /* Em */
242 { NULL, NULL }, /* Eo */
243 { termp_xx_pre, NULL }, /* Fx */
244 { termp_ms_pre, NULL }, /* Ms */
245 { NULL, NULL }, /* No */
246 { termp_ns_pre, NULL }, /* Ns */
247 { termp_xx_pre, NULL }, /* Nx */
248 { termp_xx_pre, NULL }, /* Ox */
249 { NULL, NULL }, /* Pc */
250 { termp_pf_pre, termp_pf_post }, /* Pf */
251 { termp_pq_pre, termp_pq_post }, /* Po */
252 { termp_pq_pre, termp_pq_post }, /* Pq */
253 { NULL, NULL }, /* Qc */
254 { termp_sq_pre, termp_sq_post }, /* Ql */
255 { termp_qq_pre, termp_qq_post }, /* Qo */
256 { termp_qq_pre, termp_qq_post }, /* Qq */
257 { NULL, NULL }, /* Re */
258 { termp_rs_pre, NULL }, /* Rs */
259 { NULL, NULL }, /* Sc */
260 { termp_sq_pre, termp_sq_post }, /* So */
261 { termp_sq_pre, termp_sq_post }, /* Sq */
262 { termp_sm_pre, NULL }, /* Sm */
263 { termp_sx_pre, NULL }, /* Sx */
264 { termp_sy_pre, NULL }, /* Sy */
265 { NULL, NULL }, /* Tn */
266 { termp_xx_pre, NULL }, /* Ux */
267 { NULL, NULL }, /* Xc */
268 { NULL, NULL }, /* Xo */
269 { termp_fo_pre, termp_fo_post }, /* Fo */
270 { NULL, NULL }, /* Fc */
271 { termp_op_pre, termp_op_post }, /* Oo */
272 { NULL, NULL }, /* Oc */
273 { NULL, NULL }, /* Bk */
274 { NULL, NULL }, /* Ek */
275 { termp_bt_pre, NULL }, /* Bt */
276 { NULL, NULL }, /* Hf */
277 { NULL, NULL }, /* Fr */
278 { termp_ud_pre, NULL }, /* Ud */
279 { termp_lb_pre, termp_lb_post }, /* Lb */
280 { termp_pp_pre, NULL }, /* Lp */
281 { termp_lk_pre, NULL }, /* Lk */
282 { termp_mt_pre, NULL }, /* Mt */
283 { termp_brq_pre, termp_brq_post }, /* Brq */
284 { termp_brq_pre, termp_brq_post }, /* Bro */
285 { NULL, NULL }, /* Brc */
286 { NULL, NULL }, /* %C */
287 { NULL, NULL }, /* Es */
288 { NULL, NULL }, /* En */
289 { termp_xx_pre, NULL }, /* Dx */
290 { NULL, NULL }, /* %Q */
291 };
292
293 #ifdef __linux__
294 extern size_t strlcpy(char *, const char *, size_t);
295 extern size_t strlcat(char *, const char *, size_t);
296 #endif
297
298 static int arg_hasattr(int, const struct mdoc_node *);
299 static int arg_getattrs(const int *, int *, size_t,
300 const struct mdoc_node *);
301 static int arg_getattr(int, const struct mdoc_node *);
302 static size_t arg_offset(const struct mdoc_argv *);
303 static size_t arg_width(const struct mdoc_argv *, int);
304 static int arg_listtype(const struct mdoc_node *);
305 static int fmt_block_vspace(struct termp *,
306 const struct mdoc_node *,
307 const struct mdoc_node *);
308 static void print_node(DECL_ARGS);
309 static void print_head(struct termp *,
310 const struct mdoc_meta *);
311 static void print_body(DECL_ARGS);
312 static void print_foot(struct termp *,
313 const struct mdoc_meta *);
314
315
316 int
317 mdoc_run(struct termp *p, const struct mdoc *m)
318 {
319 /*
320 * Main output function. When this is called, assume that the
321 * tree is properly formed.
322 */
323
324 print_head(p, mdoc_meta(m));
325 assert(mdoc_node(m));
326 assert(MDOC_ROOT == mdoc_node(m)->type);
327 if (mdoc_node(m)->child)
328 print_body(p, NULL, mdoc_meta(m), mdoc_node(m)->child);
329 print_foot(p, mdoc_meta(m));
330 return(1);
331 }
332
333
334 static void
335 print_body(DECL_ARGS)
336 {
337
338 print_node(p, pair, meta, node);
339 if ( ! node->next)
340 return;
341 print_body(p, pair, meta, node->next);
342 }
343
344
345 static void
346 print_node(DECL_ARGS)
347 {
348 int dochild;
349 struct termpair npair;
350
351 /* Pre-processing. */
352
353 dochild = 1;
354 npair.ppair = pair;
355 npair.offset = npair.rmargin = 0;
356 npair.flag = 0;
357 npair.count = 0;
358
359 if (MDOC_TEXT != node->type) {
360 if (termacts[node->tok].pre)
361 if ( ! (*termacts[node->tok].pre)(p, &npair, meta, node))
362 dochild = 0;
363 } else /* MDOC_TEXT == node->type */
364 term_word(p, node->string);
365
366 /* Children. */
367
368 p->flags |= npair.flag;
369
370 if (dochild && node->child)
371 print_body(p, &npair, meta, node->child);
372
373 p->flags &= ~npair.flag;
374
375 /* Post-processing. */
376
377 if (MDOC_TEXT != node->type)
378 if (termacts[node->tok].post)
379 (*termacts[node->tok].post)(p, &npair, meta, node);
380 }
381
382
383 static void
384 print_foot(struct termp *p, const struct mdoc_meta *meta)
385 {
386 struct tm *tm;
387 char *buf, *os;
388
389 /*
390 * Output the footer in new-groff style, that is, three columns
391 * with the middle being the manual date and flanking columns
392 * being the operating system:
393 *
394 * SYSTEM DATE SYSTEM
395 */
396
397 if (NULL == (buf = malloc(p->rmargin)))
398 err(1, "malloc");
399 if (NULL == (os = malloc(p->rmargin)))
400 err(1, "malloc");
401
402 tm = localtime(&meta->date);
403
404 if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
405 err(1, "strftime");
406
407 (void)strlcpy(os, meta->os, p->rmargin);
408
409 term_vspace(p);
410
411 p->offset = 0;
412 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
413 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
414
415 term_word(p, os);
416 term_flushln(p);
417
418 p->offset = p->rmargin;
419 p->rmargin = p->maxrmargin - strlen(os);
420 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
421
422 term_word(p, buf);
423 term_flushln(p);
424
425 p->offset = p->rmargin;
426 p->rmargin = p->maxrmargin;
427 p->flags &= ~TERMP_NOBREAK;
428 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
429
430 term_word(p, os);
431 term_flushln(p);
432
433 p->offset = 0;
434 p->rmargin = p->maxrmargin;
435 p->flags = 0;
436
437 free(buf);
438 free(os);
439 }
440
441
442 static void
443 print_head(struct termp *p, const struct mdoc_meta *meta)
444 {
445 char *buf, *title;
446
447 p->rmargin = p->maxrmargin;
448 p->offset = 0;
449
450 if (NULL == (buf = malloc(p->rmargin)))
451 err(1, "malloc");
452 if (NULL == (title = malloc(p->rmargin)))
453 err(1, "malloc");
454
455 /*
456 * The header is strange. It has three components, which are
457 * really two with the first duplicated. It goes like this:
458 *
459 * IDENTIFIER TITLE IDENTIFIER
460 *
461 * The IDENTIFIER is NAME(SECTION), which is the command-name
462 * (if given, or "unknown" if not) followed by the manual page
463 * section. These are given in `Dt'. The TITLE is a free-form
464 * string depending on the manual volume. If not specified, it
465 * switches on the manual section.
466 */
467
468 assert(meta->vol);
469 (void)strlcpy(buf, meta->vol, p->rmargin);
470
471 if (meta->arch) {
472 (void)strlcat(buf, " (", p->rmargin);
473 (void)strlcat(buf, meta->arch, p->rmargin);
474 (void)strlcat(buf, ")", p->rmargin);
475 }
476
477 (void)snprintf(title, p->rmargin, "%s(%d)",
478 meta->title, meta->msec);
479
480 p->offset = 0;
481 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
482 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
483
484 term_word(p, title);
485 term_flushln(p);
486
487 p->offset = p->rmargin;
488 p->rmargin = p->maxrmargin - strlen(title);
489 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
490
491 term_word(p, buf);
492 term_flushln(p);
493
494 p->offset = p->rmargin;
495 p->rmargin = p->maxrmargin;
496 p->flags &= ~TERMP_NOBREAK;
497 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
498
499 term_word(p, title);
500 term_flushln(p);
501
502 p->offset = 0;
503 p->rmargin = p->maxrmargin;
504 p->flags &= ~TERMP_NOSPACE;
505
506 free(title);
507 free(buf);
508 }
509
510
511 static size_t
512 arg_width(const struct mdoc_argv *arg, int pos)
513 {
514 size_t v;
515 int i, len;
516
517 assert(pos < (int)arg->sz && pos >= 0);
518 assert(arg->value[pos]);
519
520 if (0 == (len = (int)strlen(arg->value[pos])))
521 return(0);
522
523 for (i = 0; i < len - 1; i++)
524 if ( ! isdigit((u_char)arg->value[pos][i]))
525 break;
526
527 if (i == len - 1) {
528 if ('n' == arg->value[pos][len - 1] ||
529 'm' == arg->value[pos][len - 1]) {
530 v = (size_t)atoi(arg->value[pos]);
531 return(v + 2);
532 }
533
534 }
535 return(strlen(arg->value[pos]) + 2);
536 }
537
538
539 static int
540 arg_listtype(const struct mdoc_node *n)
541 {
542 int i, len;
543
544 assert(MDOC_BLOCK == n->type);
545
546 len = (int)(n->args ? n->args->argc : 0);
547
548 for (i = 0; i < len; i++)
549 switch (n->args->argv[i].arg) {
550 case (MDOC_Bullet):
551 /* FALLTHROUGH */
552 case (MDOC_Dash):
553 /* FALLTHROUGH */
554 case (MDOC_Enum):
555 /* FALLTHROUGH */
556 case (MDOC_Hyphen):
557 /* FALLTHROUGH */
558 case (MDOC_Tag):
559 /* FALLTHROUGH */
560 case (MDOC_Inset):
561 /* FALLTHROUGH */
562 case (MDOC_Diag):
563 /* FALLTHROUGH */
564 case (MDOC_Item):
565 /* FALLTHROUGH */
566 case (MDOC_Column):
567 /* FALLTHROUGH */
568 case (MDOC_Ohang):
569 return(n->args->argv[i].arg);
570 default:
571 break;
572 }
573
574 /* FIXME: mandated by parser. */
575
576 errx(1, "list type not supported");
577 /* NOTREACHED */
578 }
579
580
581 static size_t
582 arg_offset(const struct mdoc_argv *arg)
583 {
584
585 assert(*arg->value);
586 if (0 == strcmp(*arg->value, "left"))
587 return(0);
588 if (0 == strcmp(*arg->value, "indent"))
589 return(INDENT + 1);
590 if (0 == strcmp(*arg->value, "indent-two"))
591 return((INDENT + 1) * 2);
592
593 /* FIXME: needs to support field-widths (10n, etc.). */
594
595 return(strlen(*arg->value));
596 }
597
598
599 static int
600 arg_hasattr(int arg, const struct mdoc_node *n)
601 {
602
603 return(-1 != arg_getattr(arg, n));
604 }
605
606
607 static int
608 arg_getattr(int v, const struct mdoc_node *n)
609 {
610 int val;
611
612 return(arg_getattrs(&v, &val, 1, n) ? val : -1);
613 }
614
615
616 static int
617 arg_getattrs(const int *keys, int *vals,
618 size_t sz, const struct mdoc_node *n)
619 {
620 int i, j, k;
621
622 if (NULL == n->args)
623 return(0);
624
625 for (k = i = 0; i < (int)n->args->argc; i++)
626 for (j = 0; j < (int)sz; j++)
627 if (n->args->argv[i].arg == keys[j]) {
628 vals[j] = i;
629 k++;
630 }
631 return(k);
632 }
633
634
635 /* ARGSUSED */
636 static int
637 fmt_block_vspace(struct termp *p,
638 const struct mdoc_node *bl,
639 const struct mdoc_node *node)
640 {
641 const struct mdoc_node *n;
642
643 term_newln(p);
644
645 if (arg_hasattr(MDOC_Compact, bl))
646 return(1);
647
648 for (n = node; n; n = n->parent) {
649 if (MDOC_BLOCK != n->type)
650 continue;
651 if (MDOC_Ss == n->tok)
652 break;
653 if (MDOC_Sh == n->tok)
654 break;
655 if (NULL == n->prev)
656 continue;
657 term_vspace(p);
658 break;
659 }
660
661 return(1);
662 }
663
664
665 /* ARGSUSED */
666 static int
667 termp_dq_pre(DECL_ARGS)
668 {
669
670 if (MDOC_BODY != node->type)
671 return(1);
672
673 term_word(p, "\\(lq");
674 p->flags |= TERMP_NOSPACE;
675 return(1);
676 }
677
678
679 /* ARGSUSED */
680 static void
681 termp_dq_post(DECL_ARGS)
682 {
683
684 if (MDOC_BODY != node->type)
685 return;
686
687 p->flags |= TERMP_NOSPACE;
688 term_word(p, "\\(rq");
689 }
690
691
692 /* ARGSUSED */
693 static int
694 termp_it_pre(DECL_ARGS)
695 {
696 const struct mdoc_node *bl, *n;
697 char buf[7];
698 int i, type, keys[3], vals[3], sv;
699 size_t width, offset;
700
701 if (MDOC_BLOCK == node->type)
702 return(fmt_block_vspace(p, node->parent->parent, node));
703
704 bl = node->parent->parent->parent;
705
706 /* Save parent attributes. */
707
708 pair->offset = p->offset;
709 pair->rmargin = p->rmargin;
710 pair->flag = p->flags;
711
712 /* Get list width and offset. */
713
714 keys[0] = MDOC_Width;
715 keys[1] = MDOC_Offset;
716 keys[2] = MDOC_Column;
717
718 vals[0] = vals[1] = vals[2] = -1;
719
720 width = offset = 0;
721
722 (void)arg_getattrs(keys, vals, 3, bl);
723
724 type = arg_listtype(bl);
725
726 /* Calculate real width and offset. */
727
728 switch (type) {
729 case (MDOC_Column):
730 if (MDOC_BODY == node->type)
731 break;
732 for (i = 0, n = node->prev; n; n = n->prev, i++)
733 offset += arg_width
734 (&bl->args->argv[vals[2]], i);
735 assert(i < (int)bl->args->argv[vals[2]].sz);
736 width = arg_width(&bl->args->argv[vals[2]], i);
737 if (vals[1] >= 0)
738 offset += arg_offset(&bl->args->argv[vals[1]]);
739 break;
740 default:
741 if (vals[0] >= 0)
742 width = arg_width(&bl->args->argv[vals[0]], 0);
743 if (vals[1] >= 0)
744 offset += arg_offset(&bl->args->argv[vals[1]]);
745 break;
746 }
747
748 /*
749 * List-type can override the width in the case of fixed-head
750 * values (bullet, dash/hyphen, enum). Tags need a non-zero
751 * offset.
752 */
753
754 switch (type) {
755 case (MDOC_Bullet):
756 /* FALLTHROUGH */
757 case (MDOC_Dash):
758 /* FALLTHROUGH */
759 case (MDOC_Hyphen):
760 if (width < 4)
761 width = 4;
762 break;
763 case (MDOC_Enum):
764 if (width < 5)
765 width = 5;
766 break;
767 case (MDOC_Tag):
768 if (0 == width)
769 width = 10;
770 break;
771 default:
772 break;
773 }
774
775 /*
776 * Whitespace control. Inset bodies need an initial space,
777 * while diagonal bodies need two.
778 */
779
780 switch (type) {
781 case (MDOC_Diag):
782 term_word(p, "\\ ");
783 /* FALLTHROUGH */
784 case (MDOC_Inset):
785 if (MDOC_BODY == node->type)
786 p->flags &= ~TERMP_NOSPACE;
787 else
788 p->flags |= TERMP_NOSPACE;
789 break;
790 default:
791 p->flags |= TERMP_NOSPACE;
792 break;
793 }
794
795 /*
796 * Style flags. Diagnostic heads need TTYPE_DIAG.
797 */
798
799 switch (type) {
800 case (MDOC_Diag):
801 if (MDOC_HEAD == node->type)
802 p->flags |= ttypes[TTYPE_DIAG];
803 break;
804 default:
805 break;
806 }
807
808 /*
809 * Pad and break control. This is the tricker part. Lists with
810 * set right-margins for the head get TERMP_NOBREAK because, if
811 * they overrun the margin, they wrap to the new margin.
812 * Correspondingly, the body for these types don't left-pad, as
813 * the head will pad out to to the right.
814 */
815
816 switch (type) {
817 case (MDOC_Bullet):
818 /* FALLTHROUGH */
819 case (MDOC_Dash):
820 /* FALLTHROUGH */
821 case (MDOC_Enum):
822 /* FALLTHROUGH */
823 case (MDOC_Hyphen):
824 /* FALLTHROUGH */
825 case (MDOC_Tag):
826 if (MDOC_HEAD == node->type)
827 p->flags |= TERMP_NOBREAK;
828 else
829 p->flags |= TERMP_NOLPAD;
830 if (MDOC_HEAD == node->type && MDOC_Tag == type)
831 if (NULL == node->next ||
832 NULL == node->next->child)
833 p->flags |= TERMP_NONOBREAK;
834 break;
835 case (MDOC_Column):
836 if (MDOC_HEAD == node->type) {
837 assert(node->next);
838 if (MDOC_BODY == node->next->type)
839 p->flags &= ~TERMP_NOBREAK;
840 else
841 p->flags |= TERMP_NOBREAK;
842 if (node->prev)
843 p->flags |= TERMP_NOLPAD;
844 }
845 break;
846 case (MDOC_Diag):
847 if (MDOC_HEAD == node->type)
848 p->flags |= TERMP_NOBREAK;
849 break;
850 default:
851 break;
852 }
853
854 /*
855 * Margin control. Set-head-width lists have their right
856 * margins shortened. The body for these lists has the offset
857 * necessarily lengthened. Everybody gets the offset.
858 */
859
860 p->offset += offset;
861
862 switch (type) {
863 case (MDOC_Bullet):
864 /* FALLTHROUGH */
865 case (MDOC_Dash):
866 /* FALLTHROUGH */
867 case (MDOC_Enum):
868 /* FALLTHROUGH */
869 case (MDOC_Hyphen):
870 /* FALLTHROUGH */
871 case (MDOC_Tag):
872 if (MDOC_HEAD == node->type)
873 p->rmargin = p->offset + width;
874 else
875 p->offset += width;
876 break;
877 case (MDOC_Column):
878 p->rmargin = p->offset + width;
879 break;
880 default:
881 break;
882 }
883
884 /*
885 * The dash, hyphen, bullet and enum lists all have a special
886 * HEAD character (temporarily bold, in some cases).
887 */
888
889 sv = p->flags;
890 if (MDOC_HEAD == node->type)
891 switch (type) {
892 case (MDOC_Bullet):
893 p->flags |= TERMP_BOLD;
894 term_word(p, "\\[bu]");
895 break;
896 case (MDOC_Dash):
897 /* FALLTHROUGH */
898 case (MDOC_Hyphen):
899 p->flags |= TERMP_BOLD;
900 term_word(p, "\\(hy");
901 break;
902 case (MDOC_Enum):
903 (pair->ppair->ppair->count)++;
904 (void)snprintf(buf, sizeof(buf), "%d.",
905 pair->ppair->ppair->count);
906 term_word(p, buf);
907 break;
908 default:
909 break;
910 }
911
912 p->flags = sv; /* Restore saved flags. */
913
914 /*
915 * If we're not going to process our children, indicate so here.
916 */
917
918 switch (type) {
919 case (MDOC_Bullet):
920 /* FALLTHROUGH */
921 case (MDOC_Item):
922 /* FALLTHROUGH */
923 case (MDOC_Dash):
924 /* FALLTHROUGH */
925 case (MDOC_Hyphen):
926 /* FALLTHROUGH */
927 case (MDOC_Enum):
928 if (MDOC_HEAD == node->type)
929 return(0);
930 break;
931 case (MDOC_Column):
932 if (MDOC_BODY == node->type)
933 return(0);
934 break;
935 default:
936 break;
937 }
938
939 return(1);
940 }
941
942
943 /* ARGSUSED */
944 static void
945 termp_it_post(DECL_ARGS)
946 {
947 int type;
948
949 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
950 return;
951
952 type = arg_listtype(node->parent->parent->parent);
953
954 switch (type) {
955 case (MDOC_Diag):
956 /* FALLTHROUGH */
957 case (MDOC_Item):
958 /* FALLTHROUGH */
959 case (MDOC_Inset):
960 if (MDOC_BODY == node->type)
961 term_flushln(p);
962 break;
963 case (MDOC_Column):
964 if (MDOC_HEAD == node->type)
965 term_flushln(p);
966 break;
967 default:
968 term_flushln(p);
969 break;
970 }
971
972 p->offset = pair->offset;
973 p->rmargin = pair->rmargin;
974 p->flags = pair->flag;
975 }
976
977
978 /* ARGSUSED */
979 static int
980 termp_nm_pre(DECL_ARGS)
981 {
982
983 if (SEC_SYNOPSIS == node->sec)
984 term_newln(p);
985
986 pair->flag |= ttypes[TTYPE_PROG];
987 p->flags |= ttypes[TTYPE_PROG];
988
989 if (NULL == node->child)
990 term_word(p, meta->name);
991
992 return(1);
993 }
994
995
996 /* ARGSUSED */
997 static int
998 termp_fl_pre(DECL_ARGS)
999 {
1000
1001 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1002 p->flags |= ttypes[TTYPE_CMD_FLAG];
1003 term_word(p, "\\-");
1004 p->flags |= TERMP_NOSPACE;
1005 return(1);
1006 }
1007
1008
1009 /* ARGSUSED */
1010 static int
1011 termp_ar_pre(DECL_ARGS)
1012 {
1013
1014 pair->flag |= ttypes[TTYPE_CMD_ARG];
1015 return(1);
1016 }
1017
1018
1019 /* ARGSUSED */
1020 static int
1021 termp_ns_pre(DECL_ARGS)
1022 {
1023
1024 p->flags |= TERMP_NOSPACE;
1025 return(1);
1026 }
1027
1028
1029 /* ARGSUSED */
1030 static int
1031 termp_pp_pre(DECL_ARGS)
1032 {
1033
1034 term_vspace(p);
1035 return(1);
1036 }
1037
1038
1039 /* ARGSUSED */
1040 static int
1041 termp_st_pre(DECL_ARGS)
1042 {
1043 const char *cp;
1044
1045 if (node->child && (cp = mdoc_a2st(node->child->string)))
1046 term_word(p, cp);
1047 return(0);
1048 }
1049
1050
1051 /* ARGSUSED */
1052 static int
1053 termp_rs_pre(DECL_ARGS)
1054 {
1055
1056 if (MDOC_BLOCK == node->type && node->prev)
1057 term_vspace(p);
1058 return(1);
1059 }
1060
1061
1062 /* ARGSUSED */
1063 static int
1064 termp_rv_pre(DECL_ARGS)
1065 {
1066 int i;
1067
1068 /* FIXME: mandated by parser. */
1069
1070 if (-1 == (i = arg_getattr(MDOC_Std, node)))
1071 errx(1, "expected -std argument");
1072 if (1 != node->args->argv[i].sz)
1073 errx(1, "expected -std argument");
1074
1075 term_newln(p);
1076 term_word(p, "The");
1077
1078 p->flags |= ttypes[TTYPE_FUNC_NAME];
1079 term_word(p, *node->args->argv[i].value);
1080 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1081 p->flags |= TERMP_NOSPACE;
1082
1083 term_word(p, "() function returns the value 0 if successful;");
1084 term_word(p, "otherwise the value -1 is returned and the");
1085 term_word(p, "global variable");
1086
1087 p->flags |= ttypes[TTYPE_VAR_DECL];
1088 term_word(p, "errno");
1089 p->flags &= ~ttypes[TTYPE_VAR_DECL];
1090
1091 term_word(p, "is set to indicate the error.");
1092
1093 return(1);
1094 }
1095
1096
1097 /* ARGSUSED */
1098 static int
1099 termp_ex_pre(DECL_ARGS)
1100 {
1101 int i;
1102
1103 /* FIXME: mandated by parser? */
1104
1105 if (-1 == (i = arg_getattr(MDOC_Std, node)))
1106 errx(1, "expected -std argument");
1107 if (1 != node->args->argv[i].sz)
1108 errx(1, "expected -std argument");
1109
1110 term_word(p, "The");
1111 p->flags |= ttypes[TTYPE_PROG];
1112 term_word(p, *node->args->argv[i].value);
1113 p->flags &= ~ttypes[TTYPE_PROG];
1114 term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1115
1116 return(1);
1117 }
1118
1119
1120 /* ARGSUSED */
1121 static int
1122 termp_nd_pre(DECL_ARGS)
1123 {
1124
1125 if (MDOC_BODY != node->type)
1126 return(1);
1127 /*
1128 * XXX: signed off by jmc@openbsd.org. This technically
1129 * produces a minus sign after the Nd, which is wrong, but is
1130 * consistent with the historic OpenBSD tmac file.
1131 */
1132 #if defined(__OpenBSD__) || defined(__linux__)
1133 term_word(p, "\\-");
1134 #else
1135 term_word(p, "\\(em");
1136 #endif
1137 return(1);
1138 }
1139
1140
1141 /* ARGSUSED */
1142 static void
1143 termp_bl_post(DECL_ARGS)
1144 {
1145
1146 if (MDOC_BLOCK == node->type)
1147 term_newln(p);
1148 }
1149
1150
1151 /* ARGSUSED */
1152 static void
1153 termp_op_post(DECL_ARGS)
1154 {
1155
1156 if (MDOC_BODY != node->type)
1157 return;
1158 p->flags |= TERMP_NOSPACE;
1159 term_word(p, "\\(rB");
1160 }
1161
1162
1163 /* ARGSUSED */
1164 static int
1165 termp_xr_pre(DECL_ARGS)
1166 {
1167 const struct mdoc_node *n;
1168
1169 assert(node->child && MDOC_TEXT == node->child->type);
1170 n = node->child;
1171
1172 term_word(p, n->string);
1173 if (NULL == (n = n->next))
1174 return(0);
1175 p->flags |= TERMP_NOSPACE;
1176 term_word(p, "(");
1177 p->flags |= TERMP_NOSPACE;
1178 term_word(p, n->string);
1179 p->flags |= TERMP_NOSPACE;
1180 term_word(p, ")");
1181 return(0);
1182 }
1183
1184
1185 /* ARGSUSED */
1186 static int
1187 termp_vt_pre(DECL_ARGS)
1188 {
1189
1190 /* FIXME: this can be "type name". */
1191 pair->flag |= TTYPE_VAR_DECL;
1192 return(1);
1193 }
1194
1195
1196 /* ARGSUSED */
1197 static void
1198 termp_vt_post(DECL_ARGS)
1199 {
1200
1201 if (node->sec == SEC_SYNOPSIS)
1202 term_vspace(p);
1203 }
1204
1205
1206 /* ARGSUSED */
1207 static int
1208 termp_fd_pre(DECL_ARGS)
1209 {
1210
1211 pair->flag |= TTYPE_FUNC_DECL;
1212 return(1);
1213 }
1214
1215
1216 /* ARGSUSED */
1217 static void
1218 termp_fd_post(DECL_ARGS)
1219 {
1220
1221 if (node->sec != SEC_SYNOPSIS)
1222 return;
1223 term_newln(p);
1224 if (node->next && MDOC_Fd != node->next->tok)
1225 term_vspace(p);
1226 }
1227
1228
1229 /* ARGSUSED */
1230 static int
1231 termp_sh_pre(DECL_ARGS)
1232 {
1233
1234 switch (node->type) {
1235 case (MDOC_HEAD):
1236 term_vspace(p);
1237 pair->flag |= ttypes[TTYPE_SECTION];
1238 break;
1239 case (MDOC_BODY):
1240 p->offset = INDENT;
1241 break;
1242 default:
1243 break;
1244 }
1245 return(1);
1246 }
1247
1248
1249 /* ARGSUSED */
1250 static void
1251 termp_sh_post(DECL_ARGS)
1252 {
1253
1254 switch (node->type) {
1255 case (MDOC_HEAD):
1256 term_newln(p);
1257 break;
1258 case (MDOC_BODY):
1259 term_newln(p);
1260 p->offset = 0;
1261 break;
1262 default:
1263 break;
1264 }
1265 }
1266
1267
1268 /* ARGSUSED */
1269 static int
1270 termp_op_pre(DECL_ARGS)
1271 {
1272
1273 switch (node->type) {
1274 case (MDOC_BODY):
1275 term_word(p, "\\(lB");
1276 p->flags |= TERMP_NOSPACE;
1277 break;
1278 default:
1279 break;
1280 }
1281 return(1);
1282 }
1283
1284
1285 /* ARGSUSED */
1286 static int
1287 termp_bt_pre(DECL_ARGS)
1288 {
1289
1290 term_word(p, "is currently in beta test.");
1291 return(1);
1292 }
1293
1294
1295 /* ARGSUSED */
1296 static int
1297 termp_lb_pre(DECL_ARGS)
1298 {
1299 const char *lb;
1300
1301 assert(node->child && MDOC_TEXT == node->child->type);
1302 lb = mdoc_a2lib(node->child->string);
1303 if (lb) {
1304 term_word(p, lb);
1305 return(0);
1306 }
1307 term_word(p, "library");
1308 return(1);
1309 }
1310
1311
1312 /* ARGSUSED */
1313 static void
1314 termp_lb_post(DECL_ARGS)
1315 {
1316
1317 term_newln(p);
1318 }
1319
1320
1321 /* ARGSUSED */
1322 static int
1323 termp_ud_pre(DECL_ARGS)
1324 {
1325
1326 term_word(p, "currently under development.");
1327 return(1);
1328 }
1329
1330
1331 /* ARGSUSED */
1332 static int
1333 termp_d1_pre(DECL_ARGS)
1334 {
1335
1336 if (MDOC_BLOCK != node->type)
1337 return(1);
1338 term_newln(p);
1339 pair->offset = INDENT + 1;
1340 p->offset += pair->offset;
1341 return(1);
1342 }
1343
1344
1345 /* ARGSUSED */
1346 static void
1347 termp_d1_post(DECL_ARGS)
1348 {
1349
1350 if (MDOC_BLOCK != node->type)
1351 return;
1352 term_newln(p);
1353 p->offset -= pair->offset;
1354 }
1355
1356
1357 /* ARGSUSED */
1358 static int
1359 termp_aq_pre(DECL_ARGS)
1360 {
1361
1362 if (MDOC_BODY != node->type)
1363 return(1);
1364 term_word(p, "\\(la");
1365 p->flags |= TERMP_NOSPACE;
1366 return(1);
1367 }
1368
1369
1370 /* ARGSUSED */
1371 static void
1372 termp_aq_post(DECL_ARGS)
1373 {
1374
1375 if (MDOC_BODY != node->type)
1376 return;
1377 p->flags |= TERMP_NOSPACE;
1378 term_word(p, "\\(ra");
1379 }
1380
1381
1382 /* ARGSUSED */
1383 static int
1384 termp_ft_pre(DECL_ARGS)
1385 {
1386
1387 if (SEC_SYNOPSIS == node->sec)
1388 if (node->prev && MDOC_Fo == node->prev->tok)
1389 term_vspace(p);
1390 pair->flag |= ttypes[TTYPE_FUNC_TYPE];
1391 return(1);
1392 }
1393
1394
1395 /* ARGSUSED */
1396 static void
1397 termp_ft_post(DECL_ARGS)
1398 {
1399
1400 if (SEC_SYNOPSIS == node->sec)
1401 term_newln(p);
1402 }
1403
1404
1405 /* ARGSUSED */
1406 static int
1407 termp_fn_pre(DECL_ARGS)
1408 {
1409 const struct mdoc_node *n;
1410
1411 assert(node->child && MDOC_TEXT == node->child->type);
1412
1413 /* FIXME: can be "type funcname" "type varname"... */
1414
1415 p->flags |= ttypes[TTYPE_FUNC_NAME];
1416 term_word(p, node->child->string);
1417 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1418
1419 p->flags |= TERMP_NOSPACE;
1420 term_word(p, "(");
1421
1422 for (n = node->child->next; n; n = n->next) {
1423 p->flags |= ttypes[TTYPE_FUNC_ARG];
1424 term_word(p, n->string);
1425 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1426 if (n->next)
1427 term_word(p, ",");
1428 }
1429
1430 term_word(p, ")");
1431
1432 if (SEC_SYNOPSIS == node->sec)
1433 term_word(p, ";");
1434
1435 return(0);
1436 }
1437
1438
1439 /* ARGSUSED */
1440 static void
1441 termp_fn_post(DECL_ARGS)
1442 {
1443
1444 if (node->sec == SEC_SYNOPSIS && node->next)
1445 term_vspace(p);
1446 }
1447
1448
1449 /* ARGSUSED */
1450 static int
1451 termp_sx_pre(DECL_ARGS)
1452 {
1453
1454 pair->flag |= ttypes[TTYPE_LINK];
1455 return(1);
1456 }
1457
1458
1459 /* ARGSUSED */
1460 static int
1461 termp_fa_pre(DECL_ARGS)
1462 {
1463 struct mdoc_node *n;
1464
1465 if (node->parent->tok != MDOC_Fo) {
1466 pair->flag |= ttypes[TTYPE_FUNC_ARG];
1467 return(1);
1468 }
1469
1470 for (n = node->child; n; n = n->next) {
1471 p->flags |= ttypes[TTYPE_FUNC_ARG];
1472 term_word(p, n->string);
1473 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1474 if (n->next)
1475 term_word(p, ",");
1476 }
1477
1478 if (node->child && node->next && node->next->tok == MDOC_Fa)
1479 term_word(p, ",");
1480
1481 return(0);
1482 }
1483
1484
1485 /* ARGSUSED */
1486 static int
1487 termp_va_pre(DECL_ARGS)
1488 {
1489
1490 pair->flag |= ttypes[TTYPE_VAR_DECL];
1491 return(1);
1492 }
1493
1494
1495 /* ARGSUSED */
1496 static int
1497 termp_bd_pre(DECL_ARGS)
1498 {
1499 int i, type, ln;
1500
1501 /*
1502 * This is fairly tricky due primarily to crappy documentation.
1503 * If -ragged or -filled are specified, the block does nothing
1504 * but change the indentation.
1505 *
1506 * If, on the other hand, -unfilled or -literal are specified,
1507 * then the game changes. Text is printed exactly as entered in
1508 * the display: if a macro line, a newline is appended to the
1509 * line. Blank lines are allowed.
1510 */
1511
1512 if (MDOC_BLOCK == node->type)
1513 return(fmt_block_vspace(p, node, node));
1514 else if (MDOC_BODY != node->type)
1515 return(1);
1516
1517 /* FIXME: display type should be mandated by parser. */
1518
1519 if (NULL == node->parent->args)
1520 errx(1, "missing display type");
1521
1522 pair->offset = p->offset;
1523
1524 for (type = -1, i = 0;
1525 i < (int)node->parent->args->argc; i++) {
1526 switch (node->parent->args->argv[i].arg) {
1527 case (MDOC_Ragged):
1528 /* FALLTHROUGH */
1529 case (MDOC_Filled):
1530 /* FALLTHROUGH */
1531 case (MDOC_Unfilled):
1532 /* FALLTHROUGH */
1533 case (MDOC_Literal):
1534 type = node->parent->args->argv[i].arg;
1535 i = (int)node->parent->args->argc;
1536 break;
1537 default:
1538 break;
1539 }
1540 }
1541
1542 if (NULL == node->parent->args)
1543 errx(1, "missing display type");
1544
1545 i = arg_getattr(MDOC_Offset, node->parent);
1546 if (-1 != i) {
1547 if (1 != node->parent->args->argv[i].sz)
1548 errx(1, "expected single value");
1549 p->offset += arg_offset(&node->parent->args->argv[i]);
1550 }
1551
1552 switch (type) {
1553 case (MDOC_Literal):
1554 /* FALLTHROUGH */
1555 case (MDOC_Unfilled):
1556 break;
1557 default:
1558 return(1);
1559 }
1560
1561 /*
1562 * Tricky. Iterate through all children. If we're on a
1563 * different parse line, append a newline and then the contents.
1564 * Ew.
1565 */
1566
1567 p->flags |= TERMP_LITERAL;
1568 ln = node->child ? node->child->line : 0;
1569
1570 for (node = node->child; node; node = node->next) {
1571 if (ln < node->line) {
1572 term_flushln(p);
1573 p->flags |= TERMP_NOSPACE;
1574 }
1575 ln = node->line;
1576 print_node(p, pair, meta, node);
1577 }
1578
1579 return(0);
1580 }
1581
1582
1583 /* ARGSUSED */
1584 static void
1585 termp_bd_post(DECL_ARGS)
1586 {
1587
1588 if (MDOC_BODY != node->type)
1589 return;
1590
1591 term_flushln(p);
1592 p->flags &= ~TERMP_LITERAL;
1593 p->offset = pair->offset;
1594 p->flags |= TERMP_NOSPACE;
1595 }
1596
1597
1598 /* ARGSUSED */
1599 static int
1600 termp_qq_pre(DECL_ARGS)
1601 {
1602
1603 if (MDOC_BODY != node->type)
1604 return(1);
1605 term_word(p, "\"");
1606 p->flags |= TERMP_NOSPACE;
1607 return(1);
1608 }
1609
1610
1611 /* ARGSUSED */
1612 static void
1613 termp_qq_post(DECL_ARGS)
1614 {
1615
1616 if (MDOC_BODY != node->type)
1617 return;
1618 p->flags |= TERMP_NOSPACE;
1619 term_word(p, "\"");
1620 }
1621
1622
1623 /* ARGSUSED */
1624 static void
1625 termp_bx_post(DECL_ARGS)
1626 {
1627
1628 if (node->child)
1629 p->flags |= TERMP_NOSPACE;
1630 term_word(p, "BSD");
1631 }
1632
1633
1634 /* ARGSUSED */
1635 static int
1636 termp_xx_pre(DECL_ARGS)
1637 {
1638 const char *pp;
1639
1640 pp = NULL;
1641 switch (node->tok) {
1642 case (MDOC_Bsx):
1643 pp = "BSDI BSD/OS";
1644 break;
1645 case (MDOC_Dx):
1646 pp = "DragonFlyBSD";
1647 break;
1648 case (MDOC_Fx):
1649 pp = "FreeBSD";
1650 break;
1651 case (MDOC_Nx):
1652 pp = "NetBSD";
1653 break;
1654 case (MDOC_Ox):
1655 pp = "OpenBSD";
1656 break;
1657 case (MDOC_Ux):
1658 pp = "UNIX";
1659 break;
1660 default:
1661 break;
1662 }
1663
1664 assert(pp);
1665 term_word(p, pp);
1666 return(1);
1667 }
1668
1669
1670 /* ARGSUSED */
1671 static int
1672 termp_sq_pre(DECL_ARGS)
1673 {
1674
1675 if (MDOC_BODY != node->type)
1676 return(1);
1677 term_word(p, "\\(oq");
1678 p->flags |= TERMP_NOSPACE;
1679 return(1);
1680 }
1681
1682
1683 /* ARGSUSED */
1684 static void
1685 termp_sq_post(DECL_ARGS)
1686 {
1687
1688 if (MDOC_BODY != node->type)
1689 return;
1690 p->flags |= TERMP_NOSPACE;
1691 term_word(p, "\\(aq");
1692 }
1693
1694
1695 /* ARGSUSED */
1696 static int
1697 termp_pf_pre(DECL_ARGS)
1698 {
1699
1700 p->flags |= TERMP_IGNDELIM;
1701 return(1);
1702 }
1703
1704
1705 /* ARGSUSED */
1706 static void
1707 termp_pf_post(DECL_ARGS)
1708 {
1709
1710 p->flags &= ~TERMP_IGNDELIM;
1711 p->flags |= TERMP_NOSPACE;
1712 }
1713
1714
1715 /* ARGSUSED */
1716 static int
1717 termp_ss_pre(DECL_ARGS)
1718 {
1719
1720 switch (node->type) {
1721 case (MDOC_BLOCK):
1722 term_newln(p);
1723 if (node->prev)
1724 term_vspace(p);
1725 break;
1726 case (MDOC_HEAD):
1727 pair->flag |= ttypes[TTYPE_SSECTION];
1728 p->offset = HALFINDENT;
1729 break;
1730 default:
1731 break;
1732 }
1733
1734 return(1);
1735 }
1736
1737
1738 /* ARGSUSED */
1739 static void
1740 termp_ss_post(DECL_ARGS)
1741 {
1742
1743 switch (node->type) {
1744 case (MDOC_HEAD):
1745 term_newln(p);
1746 p->offset = INDENT;
1747 break;
1748 default:
1749 break;
1750 }
1751 }
1752
1753
1754 /* ARGSUSED */
1755 static int
1756 termp_pa_pre(DECL_ARGS)
1757 {
1758
1759 pair->flag |= ttypes[TTYPE_FILE];
1760 return(1);
1761 }
1762
1763
1764 /* ARGSUSED */
1765 static int
1766 termp_em_pre(DECL_ARGS)
1767 {
1768
1769 pair->flag |= ttypes[TTYPE_EMPH];
1770 return(1);
1771 }
1772
1773
1774 /* ARGSUSED */
1775 static int
1776 termp_cd_pre(DECL_ARGS)
1777 {
1778
1779 pair->flag |= ttypes[TTYPE_CONFIG];
1780 term_newln(p);
1781 return(1);
1782 }
1783
1784
1785 /* ARGSUSED */
1786 static int
1787 termp_cm_pre(DECL_ARGS)
1788 {
1789
1790 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1791 return(1);
1792 }
1793
1794
1795 /* ARGSUSED */
1796 static int
1797 termp_ic_pre(DECL_ARGS)
1798 {
1799
1800 pair->flag |= ttypes[TTYPE_CMD];
1801 return(1);
1802 }
1803
1804
1805 /* ARGSUSED */
1806 static int
1807 termp_in_pre(DECL_ARGS)
1808 {
1809
1810 pair->flag |= ttypes[TTYPE_INCLUDE];
1811 p->flags |= ttypes[TTYPE_INCLUDE];
1812
1813 if (SEC_SYNOPSIS == node->sec)
1814 term_word(p, "#include");
1815
1816 term_word(p, "<");
1817 p->flags |= TERMP_NOSPACE;
1818 return(1);
1819 }
1820
1821
1822 /* ARGSUSED */
1823 static void
1824 termp_in_post(DECL_ARGS)
1825 {
1826
1827 p->flags |= TERMP_NOSPACE;
1828 term_word(p, ">");
1829
1830 if (SEC_SYNOPSIS != node->sec)
1831 return;
1832
1833 term_newln(p);
1834 /*
1835 * XXX Not entirely correct. If `.In foo bar' is specified in
1836 * the SYNOPSIS section, then it produces a single break after
1837 * the <foo>; mandoc asserts a vertical space. Since this
1838 * construction is rarely used, I think it's fine.
1839 */
1840 if (node->next && MDOC_In != node->next->tok)
1841 term_vspace(p);
1842 }
1843
1844
1845 /* ARGSUSED */
1846 static int
1847 termp_at_pre(DECL_ARGS)
1848 {
1849 const char *att;
1850
1851 att = NULL;
1852
1853 if (node->child)
1854 att = mdoc_a2att(node->child->string);
1855 if (NULL == att)
1856 att = "AT&T UNIX";
1857
1858 term_word(p, att);
1859 return(0);
1860 }
1861
1862
1863 /* ARGSUSED */
1864 static int
1865 termp_brq_pre(DECL_ARGS)
1866 {
1867
1868 if (MDOC_BODY != node->type)
1869 return(1);
1870 term_word(p, "\\(lC");
1871 p->flags |= TERMP_NOSPACE;
1872 return(1);
1873 }
1874
1875
1876 /* ARGSUSED */
1877 static void
1878 termp_brq_post(DECL_ARGS)
1879 {
1880
1881 if (MDOC_BODY != node->type)
1882 return;
1883 p->flags |= TERMP_NOSPACE;
1884 term_word(p, "\\(rC");
1885 }
1886
1887
1888 /* ARGSUSED */
1889 static int
1890 termp_bq_pre(DECL_ARGS)
1891 {
1892
1893 if (MDOC_BODY != node->type)
1894 return(1);
1895 term_word(p, "\\(lB");
1896 p->flags |= TERMP_NOSPACE;
1897 return(1);
1898 }
1899
1900
1901 /* ARGSUSED */
1902 static void
1903 termp_bq_post(DECL_ARGS)
1904 {
1905
1906 if (MDOC_BODY != node->type)
1907 return;
1908 p->flags |= TERMP_NOSPACE;
1909 term_word(p, "\\(rB");
1910 }
1911
1912
1913 /* ARGSUSED */
1914 static int
1915 termp_pq_pre(DECL_ARGS)
1916 {
1917
1918 if (MDOC_BODY != node->type)
1919 return(1);
1920 term_word(p, "\\&(");
1921 p->flags |= TERMP_NOSPACE;
1922 return(1);
1923 }
1924
1925
1926 /* ARGSUSED */
1927 static void
1928 termp_pq_post(DECL_ARGS)
1929 {
1930
1931 if (MDOC_BODY != node->type)
1932 return;
1933 term_word(p, ")");
1934 }
1935
1936
1937 /* ARGSUSED */
1938 static int
1939 termp_fo_pre(DECL_ARGS)
1940 {
1941 const struct mdoc_node *n;
1942
1943 if (MDOC_BODY == node->type) {
1944 term_word(p, "(");
1945 p->flags |= TERMP_NOSPACE;
1946 return(1);
1947 } else if (MDOC_HEAD != node->type)
1948 return(1);
1949
1950 /* XXX - groff shows only first parameter */
1951
1952 p->flags |= ttypes[TTYPE_FUNC_NAME];
1953 for (n = node->child; n; n = n->next) {
1954 assert(MDOC_TEXT == n->type);
1955 term_word(p, n->string);
1956 }
1957 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1958
1959 return(0);
1960 }
1961
1962
1963 /* ARGSUSED */
1964 static void
1965 termp_fo_post(DECL_ARGS)
1966 {
1967
1968 if (MDOC_BODY != node->type)
1969 return;
1970 p->flags |= TERMP_NOSPACE;
1971 term_word(p, ")");
1972 p->flags |= TERMP_NOSPACE;
1973 term_word(p, ";");
1974 term_newln(p);
1975 }
1976
1977
1978 /* ARGSUSED */
1979 static int
1980 termp_bf_pre(DECL_ARGS)
1981 {
1982 const struct mdoc_node *n;
1983
1984 if (MDOC_HEAD == node->type)
1985 return(0);
1986 else if (MDOC_BLOCK != node->type)
1987 return(1);
1988
1989 if (NULL == (n = node->head->child)) {
1990 if (arg_hasattr(MDOC_Emphasis, node))
1991 pair->flag |= ttypes[TTYPE_EMPH];
1992 else if (arg_hasattr(MDOC_Symbolic, node))
1993 pair->flag |= ttypes[TTYPE_SYMB];
1994
1995 return(1);
1996 }
1997
1998 assert(MDOC_TEXT == n->type);
1999 if (0 == strcmp("Em", n->string))
2000 pair->flag |= ttypes[TTYPE_EMPH];
2001 else if (0 == strcmp("Sy", n->string))
2002 pair->flag |= ttypes[TTYPE_SYMB];
2003
2004 return(1);
2005 }
2006
2007
2008 /* ARGSUSED */
2009 static int
2010 termp_sy_pre(DECL_ARGS)
2011 {
2012
2013 pair->flag |= ttypes[TTYPE_SYMB];
2014 return(1);
2015 }
2016
2017
2018 /* ARGSUSED */
2019 static int
2020 termp_ms_pre(DECL_ARGS)
2021 {
2022
2023 pair->flag |= ttypes[TTYPE_SYMBOL];
2024 return(1);
2025 }
2026
2027
2028
2029 /* ARGSUSED */
2030 static int
2031 termp_sm_pre(DECL_ARGS)
2032 {
2033
2034 assert(node->child && MDOC_TEXT == node->child->type);
2035 if (0 == strcmp("on", node->child->string)) {
2036 p->flags &= ~TERMP_NONOSPACE;
2037 p->flags &= ~TERMP_NOSPACE;
2038 } else
2039 p->flags |= TERMP_NONOSPACE;
2040
2041 return(0);
2042 }
2043
2044
2045 /* ARGSUSED */
2046 static int
2047 termp_ap_pre(DECL_ARGS)
2048 {
2049
2050 p->flags |= TERMP_NOSPACE;
2051 term_word(p, "\\(aq");
2052 p->flags |= TERMP_NOSPACE;
2053 return(1);
2054 }
2055
2056
2057 /* ARGSUSED */
2058 static int
2059 termp__j_pre(DECL_ARGS)
2060 {
2061
2062 pair->flag |= ttypes[TTYPE_REF_JOURNAL];
2063 return(1);
2064 }
2065
2066
2067 /* ARGSUSED */
2068 static int
2069 termp__t_pre(DECL_ARGS)
2070 {
2071
2072 term_word(p, "\"");
2073 p->flags |= TERMP_NOSPACE;
2074 return(1);
2075 }
2076
2077
2078 /* ARGSUSED */
2079 static void
2080 termp__t_post(DECL_ARGS)
2081 {
2082
2083 p->flags |= TERMP_NOSPACE;
2084 term_word(p, "\"");
2085 termp____post(p, pair, meta, node);
2086 }
2087
2088
2089 /* ARGSUSED */
2090 static void
2091 termp____post(DECL_ARGS)
2092 {
2093
2094 p->flags |= TERMP_NOSPACE;
2095 term_word(p, node->next ? "," : ".");
2096 }
2097
2098
2099 /* ARGSUSED */
2100 static int
2101 termp_lk_pre(DECL_ARGS)
2102 {
2103 const struct mdoc_node *n;
2104
2105 assert(node->child);
2106 n = node->child;
2107
2108 if (NULL == n->next) {
2109 pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
2110 return(1);
2111 }
2112
2113 p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2114 term_word(p, n->string);
2115 p->flags |= TERMP_NOSPACE;
2116 term_word(p, ":");
2117 p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
2118
2119 p->flags |= ttypes[TTYPE_LINK_TEXT];
2120 for (n = n->next; n; n = n->next)
2121 term_word(p, n->string);
2122
2123 p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2124 return(0);
2125 }
2126
2127
2128 /* ARGSUSED */
2129 static int
2130 termp_mt_pre(DECL_ARGS)
2131 {
2132
2133 pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
2134 return(1);
2135 }
2136
2137