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