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