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