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