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