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