]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
Noted .Cd tabs-ok issue (will fix later).
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.17 2009/06/22 12:38:07 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <sys/types.h>
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "term.h"
27 #include "mdoc.h"
28
29 /* FIXME: macro arguments can be escaped. */
30 /* FIXME: support more offset/width tokens. */
31
32 #define TTYPE_PROG 0
33 #define TTYPE_CMD_FLAG 1
34 #define TTYPE_CMD_ARG 2
35 #define TTYPE_SECTION 3
36 #define TTYPE_FUNC_DECL 4
37 #define TTYPE_VAR_DECL 5
38 #define TTYPE_FUNC_TYPE 6
39 #define TTYPE_FUNC_NAME 7
40 #define TTYPE_FUNC_ARG 8
41 #define TTYPE_LINK 9
42 #define TTYPE_SSECTION 10
43 #define TTYPE_FILE 11
44 #define TTYPE_EMPH 12
45 #define TTYPE_CONFIG 13
46 #define TTYPE_CMD 14
47 #define TTYPE_INCLUDE 15
48 #define TTYPE_SYMB 16
49 #define TTYPE_SYMBOL 17
50 #define TTYPE_DIAG 18
51 #define TTYPE_LINK_ANCHOR 19
52 #define TTYPE_LINK_TEXT 20
53 #define TTYPE_REF_JOURNAL 21
54 #define TTYPE_LIST 22
55 #define TTYPE_NMAX 23
56
57 const int ttypes[TTYPE_NMAX] = {
58 TERMP_BOLD, /* TTYPE_PROG */
59 TERMP_BOLD, /* TTYPE_CMD_FLAG */
60 TERMP_UNDER, /* TTYPE_CMD_ARG */
61 TERMP_BOLD, /* TTYPE_SECTION */
62 TERMP_BOLD, /* TTYPE_FUNC_DECL */
63 TERMP_UNDER, /* TTYPE_VAR_DECL */
64 TERMP_UNDER, /* TTYPE_FUNC_TYPE */
65 TERMP_BOLD, /* TTYPE_FUNC_NAME */
66 TERMP_UNDER, /* TTYPE_FUNC_ARG */
67 TERMP_UNDER, /* TTYPE_LINK */
68 TERMP_BOLD, /* TTYPE_SSECTION */
69 TERMP_UNDER, /* TTYPE_FILE */
70 TERMP_UNDER, /* TTYPE_EMPH */
71 TERMP_BOLD, /* TTYPE_CONFIG */
72 TERMP_BOLD, /* TTYPE_CMD */
73 TERMP_BOLD, /* TTYPE_INCLUDE */
74 TERMP_BOLD, /* TTYPE_SYMB */
75 TERMP_BOLD, /* TTYPE_SYMBOL */
76 TERMP_BOLD, /* TTYPE_DIAG */
77 TERMP_UNDER, /* TTYPE_LINK_ANCHOR */
78 TERMP_BOLD, /* TTYPE_LINK_TEXT */
79 TERMP_UNDER, /* TTYPE_REF_JOURNAL */
80 TERMP_BOLD /* TTYPE_LIST */
81 };
82
83 /* 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 if (0 == strcmp(arg->value[pos], "indent"))
536 return(INDENT + 3);
537 if (0 == strcmp(arg->value[pos], "indent-two"))
538 return(INDENT * 2 + 2);
539
540 if (0 == (len = (int)strlen(arg->value[pos])))
541 return(0);
542
543 for (i = 0; i < len - 1; i++)
544 if ( ! isdigit((u_char)arg->value[pos][i]))
545 break;
546
547 if (i == len - 1) {
548 if ('n' == arg->value[pos][len - 1] ||
549 'm' == arg->value[pos][len - 1]) {
550 v = (size_t)atoi(arg->value[pos]);
551 return(v + 2);
552 }
553
554 }
555 return(strlen(arg->value[pos]) + 2);
556 }
557
558
559 static int
560 arg_listtype(const struct mdoc_node *n)
561 {
562 int i, len;
563
564 assert(MDOC_BLOCK == n->type);
565
566 len = (int)(n->args ? n->args->argc : 0);
567
568 for (i = 0; i < len; i++)
569 switch (n->args->argv[i].arg) {
570 case (MDOC_Bullet):
571 /* FALLTHROUGH */
572 case (MDOC_Dash):
573 /* FALLTHROUGH */
574 case (MDOC_Enum):
575 /* FALLTHROUGH */
576 case (MDOC_Hyphen):
577 /* FALLTHROUGH */
578 case (MDOC_Tag):
579 /* FALLTHROUGH */
580 case (MDOC_Inset):
581 /* FALLTHROUGH */
582 case (MDOC_Diag):
583 /* FALLTHROUGH */
584 case (MDOC_Item):
585 /* FALLTHROUGH */
586 case (MDOC_Column):
587 /* FALLTHROUGH */
588 case (MDOC_Ohang):
589 return(n->args->argv[i].arg);
590 default:
591 break;
592 }
593
594 /* FIXME: mandated by parser. */
595
596 errx(1, "list type not supported");
597 /* NOTREACHED */
598 }
599
600
601 static size_t
602 arg_offset(const struct mdoc_argv *arg)
603 {
604
605 assert(*arg->value);
606 if (0 == strcmp(*arg->value, "left"))
607 return(INDENT - 1);
608 if (0 == strcmp(*arg->value, "indent"))
609 return(INDENT + 1);
610 if (0 == strcmp(*arg->value, "indent-two"))
611 return(INDENT * 2);
612
613 /* FIXME: needs to support field-widths (10n, etc.). */
614
615 return(strlen(*arg->value));
616 }
617
618
619 static int
620 arg_hasattr(int arg, const struct mdoc_node *n)
621 {
622
623 return(-1 != arg_getattr(arg, n));
624 }
625
626
627 static int
628 arg_getattr(int v, const struct mdoc_node *n)
629 {
630 int val;
631
632 return(arg_getattrs(&v, &val, 1, n) ? val : -1);
633 }
634
635
636 static int
637 arg_getattrs(const int *keys, int *vals,
638 size_t sz, const struct mdoc_node *n)
639 {
640 int i, j, k;
641
642 if (NULL == n->args)
643 return(0);
644
645 for (k = i = 0; i < (int)n->args->argc; i++)
646 for (j = 0; j < (int)sz; j++)
647 if (n->args->argv[i].arg == keys[j]) {
648 vals[j] = i;
649 k++;
650 }
651 return(k);
652 }
653
654
655 /* ARGSUSED */
656 static int
657 fmt_block_vspace(struct termp *p,
658 const struct mdoc_node *bl,
659 const struct mdoc_node *node)
660 {
661 const struct mdoc_node *n;
662
663 term_newln(p);
664
665 if (arg_hasattr(MDOC_Compact, bl))
666 return(1);
667
668 for (n = node; n; n = n->parent) {
669 if (MDOC_BLOCK != n->type)
670 continue;
671 if (MDOC_Ss == n->tok)
672 break;
673 if (MDOC_Sh == n->tok)
674 break;
675 if (NULL == n->prev)
676 continue;
677 term_vspace(p);
678 break;
679 }
680
681 return(1);
682 }
683
684
685 /* ARGSUSED */
686 static int
687 termp_dq_pre(DECL_ARGS)
688 {
689
690 if (MDOC_BODY != node->type)
691 return(1);
692
693 term_word(p, "\\(lq");
694 p->flags |= TERMP_NOSPACE;
695 return(1);
696 }
697
698
699 /* ARGSUSED */
700 static void
701 termp_dq_post(DECL_ARGS)
702 {
703
704 if (MDOC_BODY != node->type)
705 return;
706
707 p->flags |= TERMP_NOSPACE;
708 term_word(p, "\\(rq");
709 }
710
711
712 /* ARGSUSED */
713 static int
714 termp_it_pre(DECL_ARGS)
715 {
716 const struct mdoc_node *bl, *n;
717 char buf[7];
718 int i, type, keys[3], vals[3], sv;
719 size_t width, offset;
720
721 if (MDOC_BLOCK == node->type)
722 return(fmt_block_vspace(p, node->parent->parent, node));
723
724 bl = node->parent->parent->parent;
725
726 /* Save parent attributes. */
727
728 pair->offset = p->offset;
729 pair->rmargin = p->rmargin;
730 pair->flag = p->flags;
731
732 /* Get list width and offset. */
733
734 keys[0] = MDOC_Width;
735 keys[1] = MDOC_Offset;
736 keys[2] = MDOC_Column;
737
738 vals[0] = vals[1] = vals[2] = -1;
739
740 width = offset = 0;
741
742 (void)arg_getattrs(keys, vals, 3, bl);
743
744 type = arg_listtype(bl);
745
746 /* Calculate real width and offset. */
747
748 switch (type) {
749 case (MDOC_Column):
750 if (MDOC_BODY == node->type)
751 break;
752 for (i = 0, n = node->prev; n; n = n->prev, i++)
753 offset += arg_width
754 (&bl->args->argv[vals[2]], i);
755 assert(i < (int)bl->args->argv[vals[2]].sz);
756 width = arg_width(&bl->args->argv[vals[2]], i);
757 if (vals[1] >= 0)
758 offset += arg_offset(&bl->args->argv[vals[1]]);
759 break;
760 default:
761 if (vals[0] >= 0)
762 width = arg_width(&bl->args->argv[vals[0]], 0);
763 if (vals[1] >= 0)
764 offset = arg_offset(&bl->args->argv[vals[1]]);
765 break;
766 }
767
768 /*
769 * List-type can override the width in the case of fixed-head
770 * values (bullet, dash/hyphen, enum). Tags need a non-zero
771 * offset.
772 */
773
774 switch (type) {
775 case (MDOC_Bullet):
776 /* FALLTHROUGH */
777 case (MDOC_Dash):
778 /* FALLTHROUGH */
779 case (MDOC_Hyphen):
780 if (width < 4)
781 width = 4;
782 break;
783 case (MDOC_Enum):
784 if (width < 5)
785 width = 5;
786 break;
787 case (MDOC_Tag):
788 if (0 == width)
789 width = 10;
790 break;
791 default:
792 break;
793 }
794
795 /*
796 * Whitespace control. Inset bodies need an initial space,
797 * while diagonal bodies need two.
798 */
799
800 switch (type) {
801 case (MDOC_Diag):
802 term_word(p, "\\ ");
803 /* FALLTHROUGH */
804 case (MDOC_Inset):
805 if (MDOC_BODY == node->type)
806 p->flags &= ~TERMP_NOSPACE;
807 else
808 p->flags |= TERMP_NOSPACE;
809 break;
810 default:
811 p->flags |= TERMP_NOSPACE;
812 break;
813 }
814
815 /*
816 * Style flags. Diagnostic heads need TTYPE_DIAG.
817 */
818
819 switch (type) {
820 case (MDOC_Diag):
821 if (MDOC_HEAD == node->type)
822 p->flags |= ttypes[TTYPE_DIAG];
823 break;
824 default:
825 break;
826 }
827
828 /*
829 * Pad and break control. This is the tricker part. Lists with
830 * set right-margins for the head get TERMP_NOBREAK because, if
831 * they overrun the margin, they wrap to the new margin.
832 * Correspondingly, the body for these types don't left-pad, as
833 * the head will pad out to to the right.
834 */
835
836 switch (type) {
837 case (MDOC_Bullet):
838 /* FALLTHROUGH */
839 case (MDOC_Dash):
840 /* FALLTHROUGH */
841 case (MDOC_Enum):
842 /* FALLTHROUGH */
843 case (MDOC_Hyphen):
844 /* FALLTHROUGH */
845 case (MDOC_Tag):
846 if (MDOC_HEAD == node->type)
847 p->flags |= TERMP_NOBREAK;
848 else
849 p->flags |= TERMP_NOLPAD;
850 if (MDOC_HEAD == node->type && MDOC_Tag == type)
851 if (NULL == node->next ||
852 NULL == node->next->child)
853 p->flags |= TERMP_NONOBREAK;
854 break;
855 case (MDOC_Column):
856 if (MDOC_HEAD == node->type) {
857 assert(node->next);
858 if (MDOC_BODY == node->next->type)
859 p->flags &= ~TERMP_NOBREAK;
860 else
861 p->flags |= TERMP_NOBREAK;
862 if (node->prev)
863 p->flags |= TERMP_NOLPAD;
864 }
865 break;
866 case (MDOC_Diag):
867 if (MDOC_HEAD == node->type)
868 p->flags |= TERMP_NOBREAK;
869 break;
870 default:
871 break;
872 }
873
874 /*
875 * Margin control. Set-head-width lists have their right
876 * margins shortened. The body for these lists has the offset
877 * necessarily lengthened. Everybody gets the offset.
878 */
879
880 p->offset += offset;
881
882 switch (type) {
883 case (MDOC_Bullet):
884 /* FALLTHROUGH */
885 case (MDOC_Dash):
886 /* FALLTHROUGH */
887 case (MDOC_Enum):
888 /* FALLTHROUGH */
889 case (MDOC_Hyphen):
890 /* FALLTHROUGH */
891 case (MDOC_Tag):
892 if (MDOC_HEAD == node->type)
893 p->rmargin = p->offset + width;
894 else
895 p->offset += width;
896 break;
897 case (MDOC_Column):
898 p->rmargin = p->offset + width;
899 break;
900 default:
901 break;
902 }
903
904 /*
905 * The dash, hyphen, bullet and enum lists all have a special
906 * HEAD character (temporarily bold, in some cases).
907 */
908
909 sv = p->flags;
910 if (MDOC_HEAD == node->type)
911 switch (type) {
912 case (MDOC_Bullet):
913 p->flags |= TERMP_BOLD;
914 term_word(p, "\\[bu]");
915 break;
916 case (MDOC_Dash):
917 /* FALLTHROUGH */
918 case (MDOC_Hyphen):
919 p->flags |= TERMP_BOLD;
920 term_word(p, "\\-");
921 break;
922 case (MDOC_Enum):
923 (pair->ppair->ppair->count)++;
924 (void)snprintf(buf, sizeof(buf), "%d.",
925 pair->ppair->ppair->count);
926 term_word(p, buf);
927 break;
928 default:
929 break;
930 }
931
932 p->flags = sv; /* Restore saved flags. */
933
934 /*
935 * If we're not going to process our children, indicate so here.
936 */
937
938 switch (type) {
939 case (MDOC_Bullet):
940 /* FALLTHROUGH */
941 case (MDOC_Item):
942 /* FALLTHROUGH */
943 case (MDOC_Dash):
944 /* FALLTHROUGH */
945 case (MDOC_Hyphen):
946 /* FALLTHROUGH */
947 case (MDOC_Enum):
948 if (MDOC_HEAD == node->type)
949 return(0);
950 break;
951 case (MDOC_Column):
952 if (MDOC_BODY == node->type)
953 return(0);
954 break;
955 default:
956 break;
957 }
958
959 return(1);
960 }
961
962
963 /* ARGSUSED */
964 static void
965 termp_it_post(DECL_ARGS)
966 {
967 int type;
968
969 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
970 return;
971
972 type = arg_listtype(node->parent->parent->parent);
973
974 switch (type) {
975 case (MDOC_Diag):
976 /* FALLTHROUGH */
977 case (MDOC_Item):
978 /* FALLTHROUGH */
979 case (MDOC_Inset):
980 if (MDOC_BODY == node->type)
981 term_flushln(p);
982 break;
983 case (MDOC_Column):
984 if (MDOC_HEAD == node->type)
985 term_flushln(p);
986 break;
987 default:
988 term_flushln(p);
989 break;
990 }
991
992 p->offset = pair->offset;
993 p->rmargin = pair->rmargin;
994 p->flags = pair->flag;
995 }
996
997
998 /* ARGSUSED */
999 static int
1000 termp_nm_pre(DECL_ARGS)
1001 {
1002
1003 if (SEC_SYNOPSIS == node->sec)
1004 term_newln(p);
1005
1006 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_PROG]);
1007 if (NULL == node->child)
1008 term_word(p, meta->name);
1009
1010 return(1);
1011 }
1012
1013
1014 /* ARGSUSED */
1015 static int
1016 termp_fl_pre(DECL_ARGS)
1017 {
1018
1019 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1020 term_word(p, "\\-");
1021 p->flags |= TERMP_NOSPACE;
1022 return(1);
1023 }
1024
1025
1026 /* ARGSUSED */
1027 static int
1028 termp_ar_pre(DECL_ARGS)
1029 {
1030
1031 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_ARG]);
1032 return(1);
1033 }
1034
1035
1036 /* ARGSUSED */
1037 static int
1038 termp_ns_pre(DECL_ARGS)
1039 {
1040
1041 p->flags |= TERMP_NOSPACE;
1042 return(1);
1043 }
1044
1045
1046 /* ARGSUSED */
1047 static int
1048 termp_pp_pre(DECL_ARGS)
1049 {
1050
1051 term_vspace(p);
1052 return(1);
1053 }
1054
1055
1056 /* ARGSUSED */
1057 static int
1058 termp_st_pre(DECL_ARGS)
1059 {
1060 const char *cp;
1061
1062 if (node->child && (cp = mdoc_a2st(node->child->string)))
1063 term_word(p, cp);
1064 return(0);
1065 }
1066
1067
1068 /* ARGSUSED */
1069 static int
1070 termp_rs_pre(DECL_ARGS)
1071 {
1072
1073 if (MDOC_BLOCK == node->type && node->prev)
1074 term_vspace(p);
1075 return(1);
1076 }
1077
1078
1079 /* ARGSUSED */
1080 static int
1081 termp_rv_pre(DECL_ARGS)
1082 {
1083 int i;
1084
1085 /* FIXME: mandated by parser. */
1086
1087 if (-1 == (i = arg_getattr(MDOC_Std, node)))
1088 errx(1, "expected -std argument");
1089 if (1 != node->args->argv[i].sz)
1090 errx(1, "expected -std argument");
1091
1092 term_newln(p);
1093 term_word(p, "The");
1094
1095 p->flags |= ttypes[TTYPE_FUNC_NAME];
1096 term_word(p, *node->args->argv[i].value);
1097 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1098 p->flags |= TERMP_NOSPACE;
1099
1100 term_word(p, "() function returns the value 0 if successful;");
1101 term_word(p, "otherwise the value -1 is returned and the");
1102 term_word(p, "global variable");
1103
1104 p->flags |= ttypes[TTYPE_VAR_DECL];
1105 term_word(p, "errno");
1106 p->flags &= ~ttypes[TTYPE_VAR_DECL];
1107
1108 term_word(p, "is set to indicate the error.");
1109
1110 return(1);
1111 }
1112
1113
1114 /* ARGSUSED */
1115 static int
1116 termp_ex_pre(DECL_ARGS)
1117 {
1118 int i;
1119
1120 /* FIXME: mandated by parser? */
1121
1122 if (-1 == (i = arg_getattr(MDOC_Std, node)))
1123 errx(1, "expected -std argument");
1124 if (1 != node->args->argv[i].sz)
1125 errx(1, "expected -std argument");
1126
1127 term_word(p, "The");
1128 p->flags |= ttypes[TTYPE_PROG];
1129 term_word(p, *node->args->argv[i].value);
1130 p->flags &= ~ttypes[TTYPE_PROG];
1131 term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1132
1133 return(1);
1134 }
1135
1136
1137 /* ARGSUSED */
1138 static int
1139 termp_nd_pre(DECL_ARGS)
1140 {
1141
1142 term_word(p, "\\-");
1143 return(1);
1144 }
1145
1146
1147 /* ARGSUSED */
1148 static void
1149 termp_bl_post(DECL_ARGS)
1150 {
1151
1152 if (MDOC_BLOCK == node->type)
1153 term_newln(p);
1154 }
1155
1156
1157 /* ARGSUSED */
1158 static void
1159 termp_op_post(DECL_ARGS)
1160 {
1161
1162 if (MDOC_BODY != node->type)
1163 return;
1164 p->flags |= TERMP_NOSPACE;
1165 term_word(p, "\\(rB");
1166 }
1167
1168
1169 /* ARGSUSED */
1170 static int
1171 termp_xr_pre(DECL_ARGS)
1172 {
1173 const struct mdoc_node *n;
1174
1175 assert(node->child && MDOC_TEXT == node->child->type);
1176 n = node->child;
1177
1178 term_word(p, n->string);
1179 if (NULL == (n = n->next))
1180 return(0);
1181 p->flags |= TERMP_NOSPACE;
1182 term_word(p, "(");
1183 p->flags |= TERMP_NOSPACE;
1184 term_word(p, n->string);
1185 p->flags |= TERMP_NOSPACE;
1186 term_word(p, ")");
1187 return(0);
1188 }
1189
1190
1191 /* ARGSUSED */
1192 static int
1193 termp_vt_pre(DECL_ARGS)
1194 {
1195
1196 /* FIXME: this can be "type name". */
1197 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1198 return(1);
1199 }
1200
1201
1202 /* ARGSUSED */
1203 static void
1204 termp_vt_post(DECL_ARGS)
1205 {
1206
1207 if (node->sec == SEC_SYNOPSIS)
1208 term_vspace(p);
1209 }
1210
1211
1212 /* ARGSUSED */
1213 static int
1214 termp_fd_pre(DECL_ARGS)
1215 {
1216
1217 /*
1218 * FIXME: this naming is bad. This value is used, in general,
1219 * for the #include header or other preprocessor statement.
1220 */
1221 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_DECL]);
1222 return(1);
1223 }
1224
1225
1226 /* ARGSUSED */
1227 static void
1228 termp_fd_post(DECL_ARGS)
1229 {
1230
1231 if (node->sec != SEC_SYNOPSIS)
1232 return;
1233 term_newln(p);
1234 if (node->next && MDOC_Fd != node->next->tok)
1235 term_vspace(p);
1236 }
1237
1238
1239 /* ARGSUSED */
1240 static int
1241 termp_sh_pre(DECL_ARGS)
1242 {
1243
1244 switch (node->type) {
1245 case (MDOC_HEAD):
1246 term_vspace(p);
1247 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SECTION]);
1248 break;
1249 case (MDOC_BODY):
1250 p->offset = INDENT;
1251 break;
1252 default:
1253 break;
1254 }
1255 return(1);
1256 }
1257
1258
1259 /* ARGSUSED */
1260 static void
1261 termp_sh_post(DECL_ARGS)
1262 {
1263
1264 switch (node->type) {
1265 case (MDOC_HEAD):
1266 term_newln(p);
1267 break;
1268 case (MDOC_BODY):
1269 term_newln(p);
1270 p->offset = 0;
1271 break;
1272 default:
1273 break;
1274 }
1275 }
1276
1277
1278 /* ARGSUSED */
1279 static int
1280 termp_op_pre(DECL_ARGS)
1281 {
1282
1283 switch (node->type) {
1284 case (MDOC_BODY):
1285 term_word(p, "\\(lB");
1286 p->flags |= TERMP_NOSPACE;
1287 break;
1288 default:
1289 break;
1290 }
1291 return(1);
1292 }
1293
1294
1295 /* ARGSUSED */
1296 static int
1297 termp_bt_pre(DECL_ARGS)
1298 {
1299
1300 term_word(p, "is currently in beta test.");
1301 return(1);
1302 }
1303
1304
1305 /* ARGSUSED */
1306 static int
1307 termp_lb_pre(DECL_ARGS)
1308 {
1309 const char *lb;
1310
1311 assert(node->child && MDOC_TEXT == node->child->type);
1312 if ((lb = mdoc_a2lib(node->child->string))) {
1313 term_word(p, lb);
1314 return(0);
1315 }
1316 term_word(p, "library");
1317 return(1);
1318 }
1319
1320
1321 /* ARGSUSED */
1322 static void
1323 termp_lb_post(DECL_ARGS)
1324 {
1325
1326 term_newln(p);
1327 }
1328
1329
1330 /* ARGSUSED */
1331 static int
1332 termp_ud_pre(DECL_ARGS)
1333 {
1334
1335 term_word(p, "currently under development.");
1336 return(1);
1337 }
1338
1339
1340 /* ARGSUSED */
1341 static int
1342 termp_d1_pre(DECL_ARGS)
1343 {
1344
1345 if (MDOC_BLOCK != node->type)
1346 return(1);
1347 term_newln(p);
1348 pair->offset = INDENT + 1;
1349 p->offset += pair->offset;
1350 return(1);
1351 }
1352
1353
1354 /* ARGSUSED */
1355 static void
1356 termp_d1_post(DECL_ARGS)
1357 {
1358
1359 if (MDOC_BLOCK != node->type)
1360 return;
1361 term_newln(p);
1362 p->offset -= pair->offset;
1363 }
1364
1365
1366 /* ARGSUSED */
1367 static int
1368 termp_aq_pre(DECL_ARGS)
1369 {
1370
1371 if (MDOC_BODY != node->type)
1372 return(1);
1373 term_word(p, "\\(la");
1374 p->flags |= TERMP_NOSPACE;
1375 return(1);
1376 }
1377
1378
1379 /* ARGSUSED */
1380 static void
1381 termp_aq_post(DECL_ARGS)
1382 {
1383
1384 if (MDOC_BODY != node->type)
1385 return;
1386 p->flags |= TERMP_NOSPACE;
1387 term_word(p, "\\(ra");
1388 }
1389
1390
1391 /* ARGSUSED */
1392 static int
1393 termp_ft_pre(DECL_ARGS)
1394 {
1395
1396 if (SEC_SYNOPSIS == node->sec)
1397 if (node->prev && MDOC_Fo == node->prev->tok)
1398 term_vspace(p);
1399 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_TYPE]);
1400 return(1);
1401 }
1402
1403
1404 /* ARGSUSED */
1405 static void
1406 termp_ft_post(DECL_ARGS)
1407 {
1408
1409 if (SEC_SYNOPSIS == node->sec)
1410 term_newln(p);
1411 }
1412
1413
1414 /* ARGSUSED */
1415 static int
1416 termp_fn_pre(DECL_ARGS)
1417 {
1418 const struct mdoc_node *n;
1419
1420 assert(node->child && MDOC_TEXT == node->child->type);
1421
1422 /* FIXME: can be "type funcname" "type varname"... */
1423
1424 p->flags |= ttypes[TTYPE_FUNC_NAME];
1425 term_word(p, node->child->string);
1426 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1427
1428 p->flags |= TERMP_NOSPACE;
1429 term_word(p, "(");
1430
1431 for (n = node->child->next; n; n = n->next) {
1432 p->flags |= ttypes[TTYPE_FUNC_ARG];
1433 term_word(p, n->string);
1434 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1435 if (n->next)
1436 term_word(p, ",");
1437 }
1438
1439 term_word(p, ")");
1440
1441 if (SEC_SYNOPSIS == node->sec)
1442 term_word(p, ";");
1443
1444 return(0);
1445 }
1446
1447
1448 /* ARGSUSED */
1449 static void
1450 termp_fn_post(DECL_ARGS)
1451 {
1452
1453 if (node->sec == SEC_SYNOPSIS && node->next)
1454 term_vspace(p);
1455
1456 }
1457
1458
1459 /* ARGSUSED */
1460 static int
1461 termp_sx_pre(DECL_ARGS)
1462 {
1463
1464 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK]);
1465 return(1);
1466 }
1467
1468
1469 /* ARGSUSED */
1470 static int
1471 termp_fa_pre(DECL_ARGS)
1472 {
1473 struct mdoc_node *n;
1474
1475 if (node->parent->tok != MDOC_Fo) {
1476 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_ARG]);
1477 return(1);
1478 }
1479
1480 for (n = node->child; n; n = n->next) {
1481 p->flags |= ttypes[TTYPE_FUNC_ARG];
1482 term_word(p, n->string);
1483 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1484 if (n->next)
1485 term_word(p, ",");
1486 }
1487
1488 if (node->child && node->next && node->next->tok == MDOC_Fa)
1489 term_word(p, ",");
1490
1491 return(0);
1492 }
1493
1494
1495 /* ARGSUSED */
1496 static int
1497 termp_va_pre(DECL_ARGS)
1498 {
1499
1500 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1501 return(1);
1502 }
1503
1504
1505 /* ARGSUSED */
1506 static int
1507 termp_bd_pre(DECL_ARGS)
1508 {
1509 int i, type, ln;
1510
1511 /*
1512 * This is fairly tricky due primarily to crappy documentation.
1513 * If -ragged or -filled are specified, the block does nothing
1514 * but change the indentation.
1515 *
1516 * If, on the other hand, -unfilled or -literal are specified,
1517 * then the game changes. Text is printed exactly as entered in
1518 * the display: if a macro line, a newline is appended to the
1519 * line. Blank lines are allowed.
1520 */
1521
1522 if (MDOC_BLOCK == node->type)
1523 return(fmt_block_vspace(p, node, node));
1524 else if (MDOC_BODY != node->type)
1525 return(1);
1526
1527 /* FIXME: display type should be mandated by parser. */
1528
1529 if (NULL == node->parent->args)
1530 errx(1, "missing display type");
1531
1532 pair->offset = p->offset;
1533
1534 for (type = -1, i = 0;
1535 i < (int)node->parent->args->argc; i++) {
1536 switch (node->parent->args->argv[i].arg) {
1537 case (MDOC_Ragged):
1538 /* FALLTHROUGH */
1539 case (MDOC_Filled):
1540 /* FALLTHROUGH */
1541 case (MDOC_Unfilled):
1542 /* FALLTHROUGH */
1543 case (MDOC_Literal):
1544 type = node->parent->args->argv[i].arg;
1545 i = (int)node->parent->args->argc;
1546 break;
1547 default:
1548 break;
1549 }
1550 }
1551
1552 if (NULL == node->parent->args)
1553 errx(1, "missing display type");
1554
1555 i = arg_getattr(MDOC_Offset, node->parent);
1556 if (-1 != i) {
1557 if (1 != node->parent->args->argv[i].sz)
1558 errx(1, "expected single value");
1559 p->offset += arg_offset(&node->parent->args->argv[i]);
1560 }
1561
1562 switch (type) {
1563 case (MDOC_Literal):
1564 /* FALLTHROUGH */
1565 case (MDOC_Unfilled):
1566 break;
1567 default:
1568 return(1);
1569 }
1570
1571 /*
1572 * Tricky. Iterate through all children. If we're on a
1573 * different parse line, append a newline and then the contents.
1574 * Ew.
1575 */
1576
1577 p->flags |= TERMP_LITERAL;
1578 ln = node->child ? node->child->line : 0;
1579
1580 for (node = node->child; node; node = node->next) {
1581 if (ln < node->line) {
1582 term_flushln(p);
1583 p->flags |= TERMP_NOSPACE;
1584 }
1585 ln = node->line;
1586 print_node(p, pair, meta, node);
1587 }
1588
1589 return(0);
1590 }
1591
1592
1593 /* ARGSUSED */
1594 static void
1595 termp_bd_post(DECL_ARGS)
1596 {
1597
1598 if (MDOC_BODY != node->type)
1599 return;
1600
1601 term_flushln(p);
1602 p->flags &= ~TERMP_LITERAL;
1603 p->offset = pair->offset;
1604 p->flags |= TERMP_NOSPACE;
1605 }
1606
1607
1608 /* ARGSUSED */
1609 static int
1610 termp_qq_pre(DECL_ARGS)
1611 {
1612
1613 if (MDOC_BODY != node->type)
1614 return(1);
1615 term_word(p, "\"");
1616 p->flags |= TERMP_NOSPACE;
1617 return(1);
1618 }
1619
1620
1621 /* ARGSUSED */
1622 static void
1623 termp_qq_post(DECL_ARGS)
1624 {
1625
1626 if (MDOC_BODY != node->type)
1627 return;
1628 p->flags |= TERMP_NOSPACE;
1629 term_word(p, "\"");
1630 }
1631
1632
1633 /* ARGSUSED */
1634 static int
1635 termp_bsx_pre(DECL_ARGS)
1636 {
1637
1638 term_word(p, "BSDI BSD/OS");
1639 return(1);
1640 }
1641
1642
1643 /* ARGSUSED */
1644 static void
1645 termp_bx_post(DECL_ARGS)
1646 {
1647
1648 if (node->child)
1649 p->flags |= TERMP_NOSPACE;
1650 term_word(p, "BSD");
1651 }
1652
1653
1654 /* ARGSUSED */
1655 static int
1656 termp_ox_pre(DECL_ARGS)
1657 {
1658
1659 term_word(p, "OpenBSD");
1660 return(1);
1661 }
1662
1663
1664 /* ARGSUSED */
1665 static int
1666 termp_dx_pre(DECL_ARGS)
1667 {
1668
1669 term_word(p, "DragonFly");
1670 return(1);
1671 }
1672
1673
1674 /* ARGSUSED */
1675 static int
1676 termp_ux_pre(DECL_ARGS)
1677 {
1678
1679 term_word(p, "UNIX");
1680 return(1);
1681 }
1682
1683
1684 /* ARGSUSED */
1685 static int
1686 termp_fx_pre(DECL_ARGS)
1687 {
1688
1689 term_word(p, "FreeBSD");
1690 return(1);
1691 }
1692
1693
1694 /* ARGSUSED */
1695 static int
1696 termp_nx_pre(DECL_ARGS)
1697 {
1698
1699 term_word(p, "NetBSD");
1700 return(1);
1701 }
1702
1703
1704 /* ARGSUSED */
1705 static int
1706 termp_sq_pre(DECL_ARGS)
1707 {
1708
1709 if (MDOC_BODY != node->type)
1710 return(1);
1711 term_word(p, "\\(oq");
1712 p->flags |= TERMP_NOSPACE;
1713 return(1);
1714 }
1715
1716
1717 /* ARGSUSED */
1718 static void
1719 termp_sq_post(DECL_ARGS)
1720 {
1721
1722 if (MDOC_BODY != node->type)
1723 return;
1724 p->flags |= TERMP_NOSPACE;
1725 term_word(p, "\\(aq");
1726 }
1727
1728
1729 /* ARGSUSED */
1730 static int
1731 termp_pf_pre(DECL_ARGS)
1732 {
1733
1734 p->flags |= TERMP_IGNDELIM;
1735 return(1);
1736 }
1737
1738
1739 /* ARGSUSED */
1740 static void
1741 termp_pf_post(DECL_ARGS)
1742 {
1743
1744 p->flags &= ~TERMP_IGNDELIM;
1745 p->flags |= TERMP_NOSPACE;
1746 }
1747
1748
1749 /* ARGSUSED */
1750 static int
1751 termp_ss_pre(DECL_ARGS)
1752 {
1753
1754 switch (node->type) {
1755 case (MDOC_BLOCK):
1756 term_newln(p);
1757 if (node->prev)
1758 term_vspace(p);
1759 break;
1760 case (MDOC_HEAD):
1761 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
1762 p->offset = HALFINDENT;
1763 break;
1764 default:
1765 break;
1766 }
1767
1768 return(1);
1769 }
1770
1771
1772 /* ARGSUSED */
1773 static void
1774 termp_ss_post(DECL_ARGS)
1775 {
1776
1777 switch (node->type) {
1778 case (MDOC_HEAD):
1779 term_newln(p);
1780 p->offset = INDENT;
1781 break;
1782 default:
1783 break;
1784 }
1785 }
1786
1787
1788 /* ARGSUSED */
1789 static int
1790 termp_pa_pre(DECL_ARGS)
1791 {
1792
1793 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FILE]);
1794 return(1);
1795 }
1796
1797
1798 /* ARGSUSED */
1799 static int
1800 termp_em_pre(DECL_ARGS)
1801 {
1802
1803 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1804 return(1);
1805 }
1806
1807
1808 /* ARGSUSED */
1809 static int
1810 termp_cd_pre(DECL_ARGS)
1811 {
1812
1813 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CONFIG]);
1814 term_newln(p);
1815 return(1);
1816 }
1817
1818
1819 /* ARGSUSED */
1820 static int
1821 termp_cm_pre(DECL_ARGS)
1822 {
1823
1824 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1825 return(1);
1826 }
1827
1828
1829 /* ARGSUSED */
1830 static int
1831 termp_ic_pre(DECL_ARGS)
1832 {
1833
1834 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD]);
1835 return(1);
1836 }
1837
1838
1839 /* ARGSUSED */
1840 static int
1841 termp_in_pre(DECL_ARGS)
1842 {
1843
1844 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_INCLUDE]);
1845 term_word(p, "#include");
1846 term_word(p, "<");
1847 p->flags |= TERMP_NOSPACE;
1848 return(1);
1849 }
1850
1851
1852 /* ARGSUSED */
1853 static void
1854 termp_in_post(DECL_ARGS)
1855 {
1856
1857 p->flags |= TERMP_NOSPACE;
1858 term_word(p, ">");
1859
1860 term_newln(p);
1861 if (SEC_SYNOPSIS != node->sec)
1862 return;
1863 if (node->next && MDOC_In != node->next->tok)
1864 term_vspace(p);
1865 }
1866
1867
1868 /* ARGSUSED */
1869 static int
1870 termp_at_pre(DECL_ARGS)
1871 {
1872 const char *att;
1873
1874 att = NULL;
1875
1876 if (node->child)
1877 att = mdoc_a2att(node->child->string);
1878 if (NULL == att)
1879 att = "AT&T UNIX";
1880
1881 term_word(p, att);
1882 return(0);
1883 }
1884
1885
1886 /* ARGSUSED */
1887 static int
1888 termp_brq_pre(DECL_ARGS)
1889 {
1890
1891 if (MDOC_BODY != node->type)
1892 return(1);
1893 term_word(p, "\\(lC");
1894 p->flags |= TERMP_NOSPACE;
1895 return(1);
1896 }
1897
1898
1899 /* ARGSUSED */
1900 static void
1901 termp_brq_post(DECL_ARGS)
1902 {
1903
1904 if (MDOC_BODY != node->type)
1905 return;
1906 p->flags |= TERMP_NOSPACE;
1907 term_word(p, "\\(rC");
1908 }
1909
1910
1911 /* ARGSUSED */
1912 static int
1913 termp_bq_pre(DECL_ARGS)
1914 {
1915
1916 if (MDOC_BODY != node->type)
1917 return(1);
1918 term_word(p, "\\(lB");
1919 p->flags |= TERMP_NOSPACE;
1920 return(1);
1921 }
1922
1923
1924 /* ARGSUSED */
1925 static void
1926 termp_bq_post(DECL_ARGS)
1927 {
1928
1929 if (MDOC_BODY != node->type)
1930 return;
1931 p->flags |= TERMP_NOSPACE;
1932 term_word(p, "\\(rB");
1933 }
1934
1935
1936 /* ARGSUSED */
1937 static int
1938 termp_pq_pre(DECL_ARGS)
1939 {
1940
1941 if (MDOC_BODY != node->type)
1942 return(1);
1943 term_word(p, "\\&(");
1944 p->flags |= TERMP_NOSPACE;
1945 return(1);
1946 }
1947
1948
1949 /* ARGSUSED */
1950 static void
1951 termp_pq_post(DECL_ARGS)
1952 {
1953
1954 if (MDOC_BODY != node->type)
1955 return;
1956 term_word(p, ")");
1957 }
1958
1959
1960 /* ARGSUSED */
1961 static int
1962 termp_fo_pre(DECL_ARGS)
1963 {
1964 const struct mdoc_node *n;
1965
1966 if (MDOC_BODY == node->type) {
1967 term_word(p, "(");
1968 p->flags |= TERMP_NOSPACE;
1969 return(1);
1970 } else if (MDOC_HEAD != node->type)
1971 return(1);
1972
1973 /* XXX - groff shows only first parameter */
1974
1975 p->flags |= ttypes[TTYPE_FUNC_NAME];
1976 for (n = node->child; n; n = n->next) {
1977 assert(MDOC_TEXT == n->type);
1978 term_word(p, n->string);
1979 }
1980 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1981
1982 return(0);
1983 }
1984
1985
1986 /* ARGSUSED */
1987 static void
1988 termp_fo_post(DECL_ARGS)
1989 {
1990
1991 if (MDOC_BODY != node->type)
1992 return;
1993 p->flags |= TERMP_NOSPACE;
1994 term_word(p, ")");
1995 p->flags |= TERMP_NOSPACE;
1996 term_word(p, ";");
1997 term_newln(p);
1998 }
1999
2000
2001 /* ARGSUSED */
2002 static int
2003 termp_bf_pre(DECL_ARGS)
2004 {
2005 const struct mdoc_node *n;
2006
2007 if (MDOC_HEAD == node->type) {
2008 return(0);
2009 } else if (MDOC_BLOCK != node->type)
2010 return(1);
2011
2012 if (NULL == (n = node->head->child)) {
2013 if (arg_hasattr(MDOC_Emphasis, node))
2014 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
2015 else if (arg_hasattr(MDOC_Symbolic, node))
2016 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
2017
2018 return(1);
2019 }
2020
2021 assert(MDOC_TEXT == n->type);
2022 if (0 == strcmp("Em", n->string))
2023 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
2024 else if (0 == strcmp("Sy", n->string))
2025 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
2026
2027 return(1);
2028 }
2029
2030
2031 /* ARGSUSED */
2032 static int
2033 termp_sy_pre(DECL_ARGS)
2034 {
2035
2036 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
2037 return(1);
2038 }
2039
2040
2041 /* ARGSUSED */
2042 static int
2043 termp_ms_pre(DECL_ARGS)
2044 {
2045
2046 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMBOL]);
2047 return(1);
2048 }
2049
2050
2051
2052 /* ARGSUSED */
2053 static int
2054 termp_sm_pre(DECL_ARGS)
2055 {
2056
2057 assert(node->child && MDOC_TEXT == node->child->type);
2058 if (0 == strcmp("on", node->child->string)) {
2059 p->flags &= ~TERMP_NONOSPACE;
2060 p->flags &= ~TERMP_NOSPACE;
2061 } else
2062 p->flags |= TERMP_NONOSPACE;
2063
2064 return(0);
2065 }
2066
2067
2068 /* ARGSUSED */
2069 static int
2070 termp_ap_pre(DECL_ARGS)
2071 {
2072
2073 p->flags |= TERMP_NOSPACE;
2074 term_word(p, "\\(aq");
2075 p->flags |= TERMP_NOSPACE;
2076 return(1);
2077 }
2078
2079
2080 /* ARGSUSED */
2081 static int
2082 termp__j_pre(DECL_ARGS)
2083 {
2084
2085 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_REF_JOURNAL]);
2086 return(1);
2087 }
2088
2089
2090 /* ARGSUSED */
2091 static int
2092 termp__t_pre(DECL_ARGS)
2093 {
2094
2095 term_word(p, "\"");
2096 p->flags |= TERMP_NOSPACE;
2097 return(1);
2098 }
2099
2100
2101 /* ARGSUSED */
2102 static void
2103 termp__t_post(DECL_ARGS)
2104 {
2105
2106 p->flags |= TERMP_NOSPACE;
2107 term_word(p, "\"");
2108 termp____post(p, pair, meta, node);
2109 }
2110
2111
2112 /* ARGSUSED */
2113 static void
2114 termp____post(DECL_ARGS)
2115 {
2116
2117 p->flags |= TERMP_NOSPACE;
2118 term_word(p, node->next ? "," : ".");
2119 }
2120
2121
2122 /* ARGSUSED */
2123 static int
2124 termp_lk_pre(DECL_ARGS)
2125 {
2126 const struct mdoc_node *n;
2127
2128 assert(node->child);
2129 n = node->child;
2130
2131 if (NULL == n->next) {
2132 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK_ANCHOR]);
2133 return(1);
2134 }
2135
2136 p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2137 term_word(p, n->string);
2138 p->flags |= TERMP_NOSPACE;
2139 term_word(p, ":");
2140 p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
2141
2142 p->flags |= ttypes[TTYPE_LINK_TEXT];
2143 for (n = n->next; n; n = n->next)
2144 term_word(p, n->string);
2145
2146 p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2147 return(0);
2148 }
2149
2150
2151 /* ARGSUSED */
2152 static int
2153 termp_mt_pre(DECL_ARGS)
2154 {
2155
2156 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK_ANCHOR]);
2157 return(1);
2158 }
2159
2160