]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
08ae01d4af13dca6f4b32bbbf594c61686991d3b
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.38 2009/07/13 07:23:07 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <sys/types.h>
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "term.h"
27 #include "mdoc.h"
28
29 /* FIXME: macro arguments can be escaped. */
30 /* FIXME: support more offset/width tokens. */
31
32 #define TTYPE_PROG 0
33 #define TTYPE_CMD_FLAG 1
34 #define TTYPE_CMD_ARG 2
35 #define TTYPE_SECTION 3
36 #define TTYPE_FUNC_DECL 4
37 #define TTYPE_VAR_DECL 5
38 #define TTYPE_FUNC_TYPE 6
39 #define TTYPE_FUNC_NAME 7
40 #define TTYPE_FUNC_ARG 8
41 #define TTYPE_LINK 9
42 #define TTYPE_SSECTION 10
43 #define TTYPE_FILE 11
44 #define TTYPE_EMPH 12
45 #define TTYPE_CONFIG 13
46 #define TTYPE_CMD 14
47 #define TTYPE_INCLUDE 15
48 #define TTYPE_SYMB 16
49 #define TTYPE_SYMBOL 17
50 #define TTYPE_DIAG 18
51 #define TTYPE_LINK_ANCHOR 19
52 #define TTYPE_LINK_TEXT 20
53 #define TTYPE_REF_JOURNAL 21
54 #define TTYPE_LIST 22
55 #define TTYPE_NMAX 23
56
57 const int ttypes[TTYPE_NMAX] = {
58 TERMP_BOLD, /* TTYPE_PROG */
59 TERMP_BOLD, /* TTYPE_CMD_FLAG */
60 TERMP_UNDER, /* TTYPE_CMD_ARG */
61 TERMP_BOLD, /* TTYPE_SECTION */
62 TERMP_BOLD, /* TTYPE_FUNC_DECL */
63 TERMP_UNDER, /* TTYPE_VAR_DECL */
64 TERMP_UNDER, /* TTYPE_FUNC_TYPE */
65 TERMP_BOLD, /* TTYPE_FUNC_NAME */
66 TERMP_UNDER, /* TTYPE_FUNC_ARG */
67 TERMP_UNDER, /* TTYPE_LINK */
68 TERMP_BOLD, /* TTYPE_SSECTION */
69 TERMP_UNDER, /* TTYPE_FILE */
70 TERMP_UNDER, /* TTYPE_EMPH */
71 TERMP_BOLD, /* TTYPE_CONFIG */
72 TERMP_BOLD, /* TTYPE_CMD */
73 TERMP_BOLD, /* TTYPE_INCLUDE */
74 TERMP_BOLD, /* TTYPE_SYMB */
75 TERMP_BOLD, /* TTYPE_SYMBOL */
76 TERMP_BOLD, /* TTYPE_DIAG */
77 TERMP_UNDER, /* TTYPE_LINK_ANCHOR */
78 TERMP_BOLD, /* TTYPE_LINK_TEXT */
79 TERMP_UNDER, /* TTYPE_REF_JOURNAL */
80 TERMP_BOLD /* TTYPE_LIST */
81 };
82
83 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], sv;
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 /* FALLTHROUGH */
773 case (MDOC_Tag):
774 if (0 == width)
775 width = 10;
776 break;
777 default:
778 break;
779 }
780
781 /*
782 * Whitespace control. Inset bodies need an initial space,
783 * while diagonal bodies need two.
784 */
785
786 switch (type) {
787 case (MDOC_Diag):
788 term_word(p, "\\ ");
789 /* FALLTHROUGH */
790 case (MDOC_Hang):
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 /* FALLTHROUGH */
833 case (MDOC_Hang):
834 /* FALLTHROUGH */
835 case (MDOC_Tag):
836 if (MDOC_HEAD == node->type)
837 p->flags |= TERMP_NOBREAK;
838 else
839 p->flags |= TERMP_NOLPAD;
840
841 if (MDOC_HEAD == node->type && MDOC_Hang == type)
842 p->flags |= TERMP_NONOBREAK;
843
844 if (MDOC_HEAD == node->type && MDOC_Tag == type)
845 if (NULL == node->next ||
846 NULL == node->next->child)
847 p->flags |= TERMP_NONOBREAK;
848 break;
849 case (MDOC_Column):
850 if (MDOC_HEAD == node->type) {
851 assert(node->next);
852 if (MDOC_BODY == node->next->type)
853 p->flags &= ~TERMP_NOBREAK;
854 else
855 p->flags |= TERMP_NOBREAK;
856 if (node->prev)
857 p->flags |= TERMP_NOLPAD;
858 }
859 break;
860 case (MDOC_Diag):
861 if (MDOC_HEAD == node->type)
862 p->flags |= TERMP_NOBREAK;
863 break;
864 default:
865 break;
866 }
867
868 /*
869 * Margin control. Set-head-width lists have their right
870 * margins shortened. The body for these lists has the offset
871 * necessarily lengthened. Everybody gets the offset.
872 */
873
874 p->offset += offset;
875
876 switch (type) {
877 case (MDOC_Bullet):
878 /* FALLTHROUGH */
879 case (MDOC_Dash):
880 /* FALLTHROUGH */
881 case (MDOC_Enum):
882 /* FALLTHROUGH */
883 case (MDOC_Hyphen):
884 /* FALLTHROUGH */
885 case (MDOC_Hang):
886 /* FALLTHROUGH */
887 case (MDOC_Tag):
888 if (MDOC_HEAD == node->type)
889 p->rmargin = p->offset + width;
890 else
891 p->offset += width;
892 break;
893 case (MDOC_Column):
894 p->rmargin = p->offset + width;
895 break;
896 default:
897 break;
898 }
899
900 /*
901 * The dash, hyphen, bullet and enum lists all have a special
902 * HEAD character (temporarily bold, in some cases).
903 */
904
905 sv = p->flags;
906 if (MDOC_HEAD == node->type)
907 switch (type) {
908 case (MDOC_Bullet):
909 p->flags |= TERMP_BOLD;
910 term_word(p, "\\[bu]");
911 break;
912 case (MDOC_Dash):
913 /* FALLTHROUGH */
914 case (MDOC_Hyphen):
915 p->flags |= TERMP_BOLD;
916 term_word(p, "\\(hy");
917 break;
918 case (MDOC_Enum):
919 (pair->ppair->ppair->count)++;
920 (void)snprintf(buf, sizeof(buf), "%d.",
921 pair->ppair->ppair->count);
922 term_word(p, buf);
923 break;
924 default:
925 break;
926 }
927
928 p->flags = sv; /* Restore saved flags. */
929
930 /*
931 * If we're not going to process our children, indicate so here.
932 */
933
934 switch (type) {
935 case (MDOC_Bullet):
936 /* FALLTHROUGH */
937 case (MDOC_Item):
938 /* FALLTHROUGH */
939 case (MDOC_Dash):
940 /* FALLTHROUGH */
941 case (MDOC_Hyphen):
942 /* FALLTHROUGH */
943 case (MDOC_Enum):
944 if (MDOC_HEAD == node->type)
945 return(0);
946 break;
947 case (MDOC_Column):
948 if (MDOC_BODY == node->type)
949 return(0);
950 break;
951 default:
952 break;
953 }
954
955 return(1);
956 }
957
958
959 /* ARGSUSED */
960 static void
961 termp_it_post(DECL_ARGS)
962 {
963 int type;
964
965 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
966 return;
967
968 type = arg_listtype(node->parent->parent->parent);
969 assert(-1 != type);
970
971 switch (type) {
972 case (MDOC_Diag):
973 /* FALLTHROUGH */
974 case (MDOC_Item):
975 /* FALLTHROUGH */
976 case (MDOC_Inset):
977 if (MDOC_BODY == node->type)
978 term_flushln(p);
979 break;
980 case (MDOC_Column):
981 if (MDOC_HEAD == node->type)
982 term_flushln(p);
983 break;
984 default:
985 term_flushln(p);
986 break;
987 }
988
989 p->flags = pair->flag;
990 }
991
992
993 /* ARGSUSED */
994 static int
995 termp_nm_pre(DECL_ARGS)
996 {
997
998 if (SEC_SYNOPSIS == node->sec)
999 term_newln(p);
1000
1001 pair->flag |= ttypes[TTYPE_PROG];
1002 p->flags |= ttypes[TTYPE_PROG];
1003
1004 if (NULL == node->child)
1005 term_word(p, meta->name);
1006
1007 return(1);
1008 }
1009
1010
1011 /* ARGSUSED */
1012 static int
1013 termp_fl_pre(DECL_ARGS)
1014 {
1015
1016 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1017 p->flags |= ttypes[TTYPE_CMD_FLAG];
1018 term_word(p, "\\-");
1019 p->flags |= TERMP_NOSPACE;
1020 return(1);
1021 }
1022
1023
1024 /* ARGSUSED */
1025 static int
1026 termp_ar_pre(DECL_ARGS)
1027 {
1028
1029 pair->flag |= ttypes[TTYPE_CMD_ARG];
1030 return(1);
1031 }
1032
1033
1034 /* ARGSUSED */
1035 static int
1036 termp_ns_pre(DECL_ARGS)
1037 {
1038
1039 p->flags |= TERMP_NOSPACE;
1040 return(1);
1041 }
1042
1043
1044 /* ARGSUSED */
1045 static int
1046 termp_pp_pre(DECL_ARGS)
1047 {
1048
1049 term_vspace(p);
1050 return(1);
1051 }
1052
1053
1054 /* ARGSUSED */
1055 static int
1056 termp_rs_pre(DECL_ARGS)
1057 {
1058
1059 if (MDOC_BLOCK == node->type && node->prev)
1060 term_vspace(p);
1061 return(1);
1062 }
1063
1064
1065 /* ARGSUSED */
1066 static int
1067 termp_rv_pre(DECL_ARGS)
1068 {
1069 int i;
1070
1071 i = arg_getattr(MDOC_Std, node);
1072 assert(-1 != i);
1073 assert(node->args->argv[i].sz);
1074
1075 term_newln(p);
1076 term_word(p, "The");
1077
1078 p->flags |= ttypes[TTYPE_FUNC_NAME];
1079 term_word(p, *node->args->argv[i].value);
1080 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1081 p->flags |= TERMP_NOSPACE;
1082
1083 term_word(p, "() function returns the value 0 if successful;");
1084 term_word(p, "otherwise the value -1 is returned and the");
1085 term_word(p, "global variable");
1086
1087 p->flags |= ttypes[TTYPE_VAR_DECL];
1088 term_word(p, "errno");
1089 p->flags &= ~ttypes[TTYPE_VAR_DECL];
1090
1091 term_word(p, "is set to indicate the error.");
1092
1093 return(1);
1094 }
1095
1096
1097 /* ARGSUSED */
1098 static int
1099 termp_ex_pre(DECL_ARGS)
1100 {
1101 int i;
1102
1103 i = arg_getattr(MDOC_Std, node);
1104 assert(-1 != i);
1105 assert(node->args->argv[i].sz);
1106
1107 term_word(p, "The");
1108 p->flags |= ttypes[TTYPE_PROG];
1109 term_word(p, *node->args->argv[i].value);
1110 p->flags &= ~ttypes[TTYPE_PROG];
1111 term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1112
1113 return(1);
1114 }
1115
1116
1117 /* ARGSUSED */
1118 static int
1119 termp_nd_pre(DECL_ARGS)
1120 {
1121
1122 if (MDOC_BODY != node->type)
1123 return(1);
1124
1125 /*
1126 * XXX: signed off by jmc@openbsd.org. This technically
1127 * produces a minus sign after the Nd, which is wrong, but is
1128 * consistent with the historic OpenBSD tmac file.
1129 */
1130 #if defined(__OpenBSD__) || defined(__linux__)
1131 term_word(p, "\\-");
1132 #else
1133 term_word(p, "\\(em");
1134 #endif
1135 return(1);
1136 }
1137
1138
1139 /* ARGSUSED */
1140 static void
1141 termp_bl_post(DECL_ARGS)
1142 {
1143
1144 if (MDOC_BLOCK == node->type)
1145 term_newln(p);
1146 }
1147
1148
1149 /* ARGSUSED */
1150 static void
1151 termp_op_post(DECL_ARGS)
1152 {
1153
1154 if (MDOC_BODY != node->type)
1155 return;
1156 p->flags |= TERMP_NOSPACE;
1157 term_word(p, "\\(rB");
1158 }
1159
1160
1161 /* ARGSUSED */
1162 static int
1163 termp_xr_pre(DECL_ARGS)
1164 {
1165 const struct mdoc_node *n;
1166
1167 assert(node->child && MDOC_TEXT == node->child->type);
1168 n = node->child;
1169
1170 term_word(p, n->string);
1171 if (NULL == (n = n->next))
1172 return(0);
1173 p->flags |= TERMP_NOSPACE;
1174 term_word(p, "(");
1175 p->flags |= TERMP_NOSPACE;
1176 term_word(p, n->string);
1177 p->flags |= TERMP_NOSPACE;
1178 term_word(p, ")");
1179 return(0);
1180 }
1181
1182
1183 /* ARGSUSED */
1184 static int
1185 termp_vt_pre(DECL_ARGS)
1186 {
1187
1188 /* FIXME: this can be "type name". */
1189 pair->flag |= ttypes[TTYPE_VAR_DECL];
1190 return(1);
1191 }
1192
1193
1194 /* ARGSUSED */
1195 static void
1196 termp_vt_post(DECL_ARGS)
1197 {
1198
1199 if (node->sec != SEC_SYNOPSIS)
1200 return;
1201 if (node->next && MDOC_Vt == node->next->tok)
1202 term_newln(p);
1203 else if (node->next)
1204 term_vspace(p);
1205 }
1206
1207
1208 /* ARGSUSED */
1209 static int
1210 termp_fd_pre(DECL_ARGS)
1211 {
1212
1213 pair->flag |= ttypes[TTYPE_FUNC_DECL];
1214 return(1);
1215 }
1216
1217
1218 /* ARGSUSED */
1219 static void
1220 termp_fd_post(DECL_ARGS)
1221 {
1222
1223 if (node->sec != SEC_SYNOPSIS)
1224 return;
1225
1226 term_newln(p);
1227 if (node->next && MDOC_Fd != node->next->tok)
1228 term_vspace(p);
1229 }
1230
1231
1232 /* ARGSUSED */
1233 static int
1234 termp_sh_pre(DECL_ARGS)
1235 {
1236
1237 switch (node->type) {
1238 case (MDOC_HEAD):
1239 term_vspace(p);
1240 pair->flag |= ttypes[TTYPE_SECTION];
1241 break;
1242 case (MDOC_BODY):
1243 p->offset = INDENT;
1244 break;
1245 default:
1246 break;
1247 }
1248 return(1);
1249 }
1250
1251
1252 /* ARGSUSED */
1253 static void
1254 termp_sh_post(DECL_ARGS)
1255 {
1256
1257 switch (node->type) {
1258 case (MDOC_HEAD):
1259 term_newln(p);
1260 break;
1261 case (MDOC_BODY):
1262 term_newln(p);
1263 p->offset = 0;
1264 break;
1265 default:
1266 break;
1267 }
1268 }
1269
1270
1271 /* ARGSUSED */
1272 static int
1273 termp_op_pre(DECL_ARGS)
1274 {
1275
1276 switch (node->type) {
1277 case (MDOC_BODY):
1278 term_word(p, "\\(lB");
1279 p->flags |= TERMP_NOSPACE;
1280 break;
1281 default:
1282 break;
1283 }
1284 return(1);
1285 }
1286
1287
1288 /* ARGSUSED */
1289 static int
1290 termp_bt_pre(DECL_ARGS)
1291 {
1292
1293 term_word(p, "is currently in beta test.");
1294 return(1);
1295 }
1296
1297
1298 /* ARGSUSED */
1299 static void
1300 termp_lb_post(DECL_ARGS)
1301 {
1302
1303 term_newln(p);
1304 }
1305
1306
1307 /* ARGSUSED */
1308 static int
1309 termp_ud_pre(DECL_ARGS)
1310 {
1311
1312 term_word(p, "currently under development.");
1313 return(1);
1314 }
1315
1316
1317 /* ARGSUSED */
1318 static int
1319 termp_d1_pre(DECL_ARGS)
1320 {
1321
1322 if (MDOC_BLOCK != node->type)
1323 return(1);
1324 term_newln(p);
1325 p->offset += (INDENT + 1);
1326 return(1);
1327 }
1328
1329
1330 /* ARGSUSED */
1331 static void
1332 termp_d1_post(DECL_ARGS)
1333 {
1334
1335 if (MDOC_BLOCK != node->type)
1336 return;
1337 term_newln(p);
1338 }
1339
1340
1341 /* ARGSUSED */
1342 static int
1343 termp_aq_pre(DECL_ARGS)
1344 {
1345
1346 if (MDOC_BODY != node->type)
1347 return(1);
1348 term_word(p, "\\(la");
1349 p->flags |= TERMP_NOSPACE;
1350 return(1);
1351 }
1352
1353
1354 /* ARGSUSED */
1355 static void
1356 termp_aq_post(DECL_ARGS)
1357 {
1358
1359 if (MDOC_BODY != node->type)
1360 return;
1361 p->flags |= TERMP_NOSPACE;
1362 term_word(p, "\\(ra");
1363 }
1364
1365
1366 /* ARGSUSED */
1367 static int
1368 termp_ft_pre(DECL_ARGS)
1369 {
1370
1371 if (SEC_SYNOPSIS == node->sec)
1372 if (node->prev && MDOC_Fo == node->prev->tok)
1373 term_vspace(p);
1374 pair->flag |= ttypes[TTYPE_FUNC_TYPE];
1375 return(1);
1376 }
1377
1378
1379 /* ARGSUSED */
1380 static void
1381 termp_ft_post(DECL_ARGS)
1382 {
1383
1384 if (SEC_SYNOPSIS == node->sec)
1385 term_newln(p);
1386 }
1387
1388
1389 /* ARGSUSED */
1390 static int
1391 termp_fn_pre(DECL_ARGS)
1392 {
1393 const struct mdoc_node *n;
1394
1395 assert(node->child && MDOC_TEXT == node->child->type);
1396
1397 /* FIXME: can be "type funcname" "type varname"... */
1398
1399 p->flags |= ttypes[TTYPE_FUNC_NAME];
1400 term_word(p, node->child->string);
1401 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1402
1403 p->flags |= TERMP_NOSPACE;
1404 term_word(p, "(");
1405
1406 for (n = node->child->next; n; n = n->next) {
1407 p->flags |= ttypes[TTYPE_FUNC_ARG];
1408 term_word(p, n->string);
1409 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1410 if (n->next)
1411 term_word(p, ",");
1412 }
1413
1414 term_word(p, ")");
1415
1416 if (SEC_SYNOPSIS == node->sec)
1417 term_word(p, ";");
1418
1419 return(0);
1420 }
1421
1422
1423 /* ARGSUSED */
1424 static void
1425 termp_fn_post(DECL_ARGS)
1426 {
1427
1428 if (node->sec == SEC_SYNOPSIS && node->next)
1429 term_vspace(p);
1430 }
1431
1432
1433 /* ARGSUSED */
1434 static int
1435 termp_sx_pre(DECL_ARGS)
1436 {
1437
1438 pair->flag |= ttypes[TTYPE_LINK];
1439 return(1);
1440 }
1441
1442
1443 /* ARGSUSED */
1444 static int
1445 termp_fa_pre(DECL_ARGS)
1446 {
1447 struct mdoc_node *n;
1448
1449 if (node->parent->tok != MDOC_Fo) {
1450 pair->flag |= ttypes[TTYPE_FUNC_ARG];
1451 return(1);
1452 }
1453
1454 for (n = node->child; n; n = n->next) {
1455 p->flags |= ttypes[TTYPE_FUNC_ARG];
1456 term_word(p, n->string);
1457 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1458 if (n->next)
1459 term_word(p, ",");
1460 }
1461
1462 if (node->child && node->next && node->next->tok == MDOC_Fa)
1463 term_word(p, ",");
1464
1465 return(0);
1466 }
1467
1468
1469 /* ARGSUSED */
1470 static int
1471 termp_va_pre(DECL_ARGS)
1472 {
1473
1474 pair->flag |= ttypes[TTYPE_VAR_DECL];
1475 return(1);
1476 }
1477
1478
1479 /* ARGSUSED */
1480 static int
1481 termp_bd_pre(DECL_ARGS)
1482 {
1483 int i, type, ln;
1484
1485 /*
1486 * This is fairly tricky due primarily to crappy documentation.
1487 * If -ragged or -filled are specified, the block does nothing
1488 * but change the indentation.
1489 *
1490 * If, on the other hand, -unfilled or -literal are specified,
1491 * then the game changes. Text is printed exactly as entered in
1492 * the display: if a macro line, a newline is appended to the
1493 * line. Blank lines are allowed.
1494 */
1495
1496 if (MDOC_BLOCK == node->type)
1497 return(fmt_block_vspace(p, node, node));
1498 else if (MDOC_BODY != node->type)
1499 return(1);
1500
1501 /* FIXME: display type should be mandated by parser. */
1502
1503 if (NULL == node->parent->args)
1504 errx(1, "missing display type");
1505
1506 for (type = -1, i = 0;
1507 i < (int)node->parent->args->argc; i++) {
1508 switch (node->parent->args->argv[i].arg) {
1509 case (MDOC_Ragged):
1510 /* FALLTHROUGH */
1511 case (MDOC_Filled):
1512 /* FALLTHROUGH */
1513 case (MDOC_Unfilled):
1514 /* FALLTHROUGH */
1515 case (MDOC_Literal):
1516 type = node->parent->args->argv[i].arg;
1517 i = (int)node->parent->args->argc;
1518 break;
1519 default:
1520 break;
1521 }
1522 }
1523
1524 if (NULL == node->parent->args)
1525 errx(1, "missing display type");
1526
1527 i = arg_getattr(MDOC_Offset, node->parent);
1528 if (-1 != i) {
1529 if (1 != node->parent->args->argv[i].sz)
1530 errx(1, "expected single value");
1531 p->offset += arg_offset(&node->parent->args->argv[i]);
1532 }
1533
1534 switch (type) {
1535 case (MDOC_Literal):
1536 /* FALLTHROUGH */
1537 case (MDOC_Unfilled):
1538 break;
1539 default:
1540 return(1);
1541 }
1542
1543 /*
1544 * Tricky. Iterate through all children. If we're on a
1545 * different parse line, append a newline and then the contents.
1546 * Ew.
1547 */
1548
1549 p->flags |= TERMP_LITERAL;
1550 ln = node->child ? node->child->line : 0;
1551
1552 for (node = node->child; node; node = node->next) {
1553 if (ln < node->line) {
1554 term_flushln(p);
1555 p->flags |= TERMP_NOSPACE;
1556 }
1557 ln = node->line;
1558 print_node(p, pair, meta, node);
1559 }
1560
1561 return(0);
1562 }
1563
1564
1565 /* ARGSUSED */
1566 static void
1567 termp_bd_post(DECL_ARGS)
1568 {
1569
1570 if (MDOC_BODY != node->type)
1571 return;
1572
1573 term_flushln(p);
1574 p->flags &= ~TERMP_LITERAL;
1575 p->flags |= TERMP_NOSPACE;
1576 }
1577
1578
1579 /* ARGSUSED */
1580 static int
1581 termp_qq_pre(DECL_ARGS)
1582 {
1583
1584 if (MDOC_BODY != node->type)
1585 return(1);
1586 term_word(p, "\"");
1587 p->flags |= TERMP_NOSPACE;
1588 return(1);
1589 }
1590
1591
1592 /* ARGSUSED */
1593 static void
1594 termp_qq_post(DECL_ARGS)
1595 {
1596
1597 if (MDOC_BODY != node->type)
1598 return;
1599 p->flags |= TERMP_NOSPACE;
1600 term_word(p, "\"");
1601 }
1602
1603
1604 /* ARGSUSED */
1605 static void
1606 termp_bx_post(DECL_ARGS)
1607 {
1608
1609 if (node->child)
1610 p->flags |= TERMP_NOSPACE;
1611 term_word(p, "BSD");
1612 }
1613
1614
1615 /* ARGSUSED */
1616 static int
1617 termp_xx_pre(DECL_ARGS)
1618 {
1619 const char *pp;
1620
1621 pp = NULL;
1622 switch (node->tok) {
1623 case (MDOC_Bsx):
1624 pp = "BSDI BSD/OS";
1625 break;
1626 case (MDOC_Dx):
1627 pp = "DragonFlyBSD";
1628 break;
1629 case (MDOC_Fx):
1630 pp = "FreeBSD";
1631 break;
1632 case (MDOC_Nx):
1633 pp = "NetBSD";
1634 break;
1635 case (MDOC_Ox):
1636 pp = "OpenBSD";
1637 break;
1638 case (MDOC_Ux):
1639 pp = "UNIX";
1640 break;
1641 default:
1642 break;
1643 }
1644
1645 assert(pp);
1646 term_word(p, pp);
1647 return(1);
1648 }
1649
1650
1651 /* ARGSUSED */
1652 static int
1653 termp_sq_pre(DECL_ARGS)
1654 {
1655
1656 if (MDOC_BODY != node->type)
1657 return(1);
1658 term_word(p, "\\(oq");
1659 p->flags |= TERMP_NOSPACE;
1660 return(1);
1661 }
1662
1663
1664 /* ARGSUSED */
1665 static void
1666 termp_sq_post(DECL_ARGS)
1667 {
1668
1669 if (MDOC_BODY != node->type)
1670 return;
1671 p->flags |= TERMP_NOSPACE;
1672 term_word(p, "\\(aq");
1673 }
1674
1675
1676 /* ARGSUSED */
1677 static int
1678 termp_pf_pre(DECL_ARGS)
1679 {
1680
1681 p->flags |= TERMP_IGNDELIM;
1682 return(1);
1683 }
1684
1685
1686 /* ARGSUSED */
1687 static void
1688 termp_pf_post(DECL_ARGS)
1689 {
1690
1691 p->flags &= ~TERMP_IGNDELIM;
1692 p->flags |= TERMP_NOSPACE;
1693 }
1694
1695
1696 /* ARGSUSED */
1697 static int
1698 termp_ss_pre(DECL_ARGS)
1699 {
1700
1701 switch (node->type) {
1702 case (MDOC_BLOCK):
1703 term_newln(p);
1704 if (node->prev)
1705 term_vspace(p);
1706 break;
1707 case (MDOC_HEAD):
1708 pair->flag |= ttypes[TTYPE_SSECTION];
1709 p->offset = HALFINDENT;
1710 break;
1711 default:
1712 break;
1713 }
1714
1715 return(1);
1716 }
1717
1718
1719 /* ARGSUSED */
1720 static void
1721 termp_ss_post(DECL_ARGS)
1722 {
1723
1724 if (MDOC_HEAD == node->type)
1725 term_newln(p);
1726 }
1727
1728
1729 /* ARGSUSED */
1730 static int
1731 termp_pa_pre(DECL_ARGS)
1732 {
1733
1734 pair->flag |= ttypes[TTYPE_FILE];
1735 return(1);
1736 }
1737
1738
1739 /* ARGSUSED */
1740 static int
1741 termp_em_pre(DECL_ARGS)
1742 {
1743
1744 pair->flag |= ttypes[TTYPE_EMPH];
1745 return(1);
1746 }
1747
1748
1749 /* ARGSUSED */
1750 static int
1751 termp_cd_pre(DECL_ARGS)
1752 {
1753
1754 pair->flag |= ttypes[TTYPE_CONFIG];
1755 term_newln(p);
1756 return(1);
1757 }
1758
1759
1760 /* ARGSUSED */
1761 static int
1762 termp_cm_pre(DECL_ARGS)
1763 {
1764
1765 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1766 return(1);
1767 }
1768
1769
1770 /* ARGSUSED */
1771 static int
1772 termp_ic_pre(DECL_ARGS)
1773 {
1774
1775 pair->flag |= ttypes[TTYPE_CMD];
1776 return(1);
1777 }
1778
1779
1780 /* ARGSUSED */
1781 static int
1782 termp_in_pre(DECL_ARGS)
1783 {
1784
1785 pair->flag |= ttypes[TTYPE_INCLUDE];
1786 p->flags |= ttypes[TTYPE_INCLUDE];
1787
1788 if (SEC_SYNOPSIS == node->sec)
1789 term_word(p, "#include");
1790
1791 term_word(p, "<");
1792 p->flags |= TERMP_NOSPACE;
1793 return(1);
1794 }
1795
1796
1797 /* ARGSUSED */
1798 static void
1799 termp_in_post(DECL_ARGS)
1800 {
1801
1802 p->flags |= TERMP_NOSPACE;
1803 term_word(p, ">");
1804
1805 if (SEC_SYNOPSIS != node->sec)
1806 return;
1807
1808 term_newln(p);
1809 /*
1810 * XXX Not entirely correct. If `.In foo bar' is specified in
1811 * the SYNOPSIS section, then it produces a single break after
1812 * the <foo>; mandoc asserts a vertical space. Since this
1813 * construction is rarely used, I think it's fine.
1814 */
1815 if (node->next && MDOC_In != node->next->tok)
1816 term_vspace(p);
1817 }
1818
1819
1820 /* ARGSUSED */
1821 static int
1822 termp_brq_pre(DECL_ARGS)
1823 {
1824
1825 if (MDOC_BODY != node->type)
1826 return(1);
1827 term_word(p, "\\(lC");
1828 p->flags |= TERMP_NOSPACE;
1829 return(1);
1830 }
1831
1832
1833 /* ARGSUSED */
1834 static void
1835 termp_brq_post(DECL_ARGS)
1836 {
1837
1838 if (MDOC_BODY != node->type)
1839 return;
1840 p->flags |= TERMP_NOSPACE;
1841 term_word(p, "\\(rC");
1842 }
1843
1844
1845 /* ARGSUSED */
1846 static int
1847 termp_bq_pre(DECL_ARGS)
1848 {
1849
1850 if (MDOC_BODY != node->type)
1851 return(1);
1852 term_word(p, "\\(lB");
1853 p->flags |= TERMP_NOSPACE;
1854 return(1);
1855 }
1856
1857
1858 /* ARGSUSED */
1859 static void
1860 termp_bq_post(DECL_ARGS)
1861 {
1862
1863 if (MDOC_BODY != node->type)
1864 return;
1865 p->flags |= TERMP_NOSPACE;
1866 term_word(p, "\\(rB");
1867 }
1868
1869
1870 /* ARGSUSED */
1871 static int
1872 termp_pq_pre(DECL_ARGS)
1873 {
1874
1875 if (MDOC_BODY != node->type)
1876 return(1);
1877 term_word(p, "\\&(");
1878 p->flags |= TERMP_NOSPACE;
1879 return(1);
1880 }
1881
1882
1883 /* ARGSUSED */
1884 static void
1885 termp_pq_post(DECL_ARGS)
1886 {
1887
1888 if (MDOC_BODY != node->type)
1889 return;
1890 term_word(p, ")");
1891 }
1892
1893
1894 /* ARGSUSED */
1895 static int
1896 termp_fo_pre(DECL_ARGS)
1897 {
1898 const struct mdoc_node *n;
1899
1900 if (MDOC_BODY == node->type) {
1901 p->flags |= TERMP_NOSPACE;
1902 term_word(p, "(");
1903 p->flags |= TERMP_NOSPACE;
1904 return(1);
1905 } else if (MDOC_HEAD != node->type)
1906 return(1);
1907
1908 p->flags |= ttypes[TTYPE_FUNC_NAME];
1909 for (n = node->child; n; n = n->next) {
1910 assert(MDOC_TEXT == n->type);
1911 term_word(p, n->string);
1912 }
1913 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1914
1915 return(0);
1916 }
1917
1918
1919 /* ARGSUSED */
1920 static void
1921 termp_fo_post(DECL_ARGS)
1922 {
1923
1924 if (MDOC_BODY != node->type)
1925 return;
1926 p->flags |= TERMP_NOSPACE;
1927 term_word(p, ")");
1928 p->flags |= TERMP_NOSPACE;
1929 term_word(p, ";");
1930 term_newln(p);
1931 }
1932
1933
1934 /* ARGSUSED */
1935 static int
1936 termp_bf_pre(DECL_ARGS)
1937 {
1938 const struct mdoc_node *n;
1939
1940 if (MDOC_HEAD == node->type)
1941 return(0);
1942 else if (MDOC_BLOCK != node->type)
1943 return(1);
1944
1945 if (NULL == (n = node->head->child)) {
1946 if (arg_hasattr(MDOC_Emphasis, node))
1947 pair->flag |= ttypes[TTYPE_EMPH];
1948 else if (arg_hasattr(MDOC_Symbolic, node))
1949 pair->flag |= ttypes[TTYPE_SYMB];
1950
1951 return(1);
1952 }
1953
1954 assert(MDOC_TEXT == n->type);
1955 if (0 == strcmp("Em", n->string))
1956 pair->flag |= ttypes[TTYPE_EMPH];
1957 else if (0 == strcmp("Sy", n->string))
1958 pair->flag |= ttypes[TTYPE_SYMB];
1959
1960 return(1);
1961 }
1962
1963
1964 /* ARGSUSED */
1965 static int
1966 termp_sy_pre(DECL_ARGS)
1967 {
1968
1969 pair->flag |= ttypes[TTYPE_SYMB];
1970 return(1);
1971 }
1972
1973
1974 /* ARGSUSED */
1975 static int
1976 termp_ms_pre(DECL_ARGS)
1977 {
1978
1979 pair->flag |= ttypes[TTYPE_SYMBOL];
1980 return(1);
1981 }
1982
1983
1984
1985 /* ARGSUSED */
1986 static int
1987 termp_sm_pre(DECL_ARGS)
1988 {
1989
1990 assert(node->child && MDOC_TEXT == node->child->type);
1991 if (0 == strcmp("on", node->child->string)) {
1992 p->flags &= ~TERMP_NONOSPACE;
1993 p->flags &= ~TERMP_NOSPACE;
1994 } else
1995 p->flags |= TERMP_NONOSPACE;
1996
1997 return(0);
1998 }
1999
2000
2001 /* ARGSUSED */
2002 static int
2003 termp_ap_pre(DECL_ARGS)
2004 {
2005
2006 p->flags |= TERMP_NOSPACE;
2007 term_word(p, "\\(aq");
2008 p->flags |= TERMP_NOSPACE;
2009 return(1);
2010 }
2011
2012
2013 /* ARGSUSED */
2014 static int
2015 termp__j_pre(DECL_ARGS)
2016 {
2017
2018 pair->flag |= ttypes[TTYPE_REF_JOURNAL];
2019 return(1);
2020 }
2021
2022
2023 /* ARGSUSED */
2024 static int
2025 termp__t_pre(DECL_ARGS)
2026 {
2027
2028 term_word(p, "\"");
2029 p->flags |= TERMP_NOSPACE;
2030 return(1);
2031 }
2032
2033
2034 /* ARGSUSED */
2035 static void
2036 termp__t_post(DECL_ARGS)
2037 {
2038
2039 p->flags |= TERMP_NOSPACE;
2040 term_word(p, "\"");
2041 termp____post(p, pair, meta, node);
2042 }
2043
2044
2045 /* ARGSUSED */
2046 static void
2047 termp____post(DECL_ARGS)
2048 {
2049
2050 p->flags |= TERMP_NOSPACE;
2051 term_word(p, node->next ? "," : ".");
2052 }
2053
2054
2055 /* ARGSUSED */
2056 static int
2057 termp_lk_pre(DECL_ARGS)
2058 {
2059 const struct mdoc_node *n;
2060
2061 assert(node->child);
2062 n = node->child;
2063
2064 if (NULL == n->next) {
2065 pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
2066 return(1);
2067 }
2068
2069 p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2070 term_word(p, n->string);
2071 p->flags |= TERMP_NOSPACE;
2072 term_word(p, ":");
2073 p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
2074
2075 p->flags |= ttypes[TTYPE_LINK_TEXT];
2076 for (n = n->next; n; n = n->next)
2077 term_word(p, n->string);
2078
2079 p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2080 return(0);
2081 }
2082
2083
2084 /* ARGSUSED */
2085 static int
2086 termp_mt_pre(DECL_ARGS)
2087 {
2088
2089 pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
2090 return(1);
2091 }
2092
2093