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