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