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