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