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