Annotation of mandoc/mdoc_term.c, Revision 1.32
1.32 ! kristaps 1: /* $Id: mdoc_term.c,v 1.31 2009/07/12 19:13:12 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.11 kristaps 1073: /* FIXME: mandated by parser. */
1074:
1.1 kristaps 1075: if (-1 == (i = arg_getattr(MDOC_Std, node)))
1076: errx(1, "expected -std argument");
1077: if (1 != node->args->argv[i].sz)
1078: errx(1, "expected -std argument");
1079:
1080: term_newln(p);
1081: term_word(p, "The");
1082:
1083: p->flags |= ttypes[TTYPE_FUNC_NAME];
1084: term_word(p, *node->args->argv[i].value);
1085: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1086: p->flags |= TERMP_NOSPACE;
1087:
1088: term_word(p, "() function returns the value 0 if successful;");
1089: term_word(p, "otherwise the value -1 is returned and the");
1090: term_word(p, "global variable");
1091:
1092: p->flags |= ttypes[TTYPE_VAR_DECL];
1093: term_word(p, "errno");
1094: p->flags &= ~ttypes[TTYPE_VAR_DECL];
1095:
1096: term_word(p, "is set to indicate the error.");
1097:
1098: return(1);
1099: }
1100:
1101:
1102: /* ARGSUSED */
1103: static int
1104: termp_ex_pre(DECL_ARGS)
1105: {
1106: int i;
1107:
1.11 kristaps 1108: /* FIXME: mandated by parser? */
1109:
1.1 kristaps 1110: if (-1 == (i = arg_getattr(MDOC_Std, node)))
1111: errx(1, "expected -std argument");
1112: if (1 != node->args->argv[i].sz)
1113: errx(1, "expected -std argument");
1114:
1115: term_word(p, "The");
1116: p->flags |= ttypes[TTYPE_PROG];
1117: term_word(p, *node->args->argv[i].value);
1118: p->flags &= ~ttypes[TTYPE_PROG];
1119: term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1120:
1121: return(1);
1122: }
1123:
1124:
1125: /* ARGSUSED */
1126: static int
1127: termp_nd_pre(DECL_ARGS)
1128: {
1.25 kristaps 1129:
1130: if (MDOC_BODY != node->type)
1131: return(1);
1.30 kristaps 1132:
1.24 kristaps 1133: /*
1134: * XXX: signed off by jmc@openbsd.org. This technically
1135: * produces a minus sign after the Nd, which is wrong, but is
1136: * consistent with the historic OpenBSD tmac file.
1137: */
1.27 kristaps 1138: #if defined(__OpenBSD__) || defined(__linux__)
1.1 kristaps 1139: term_word(p, "\\-");
1.23 kristaps 1140: #else
1141: term_word(p, "\\(em");
1142: #endif
1.1 kristaps 1143: return(1);
1144: }
1145:
1146:
1147: /* ARGSUSED */
1148: static void
1149: termp_bl_post(DECL_ARGS)
1150: {
1151:
1152: if (MDOC_BLOCK == node->type)
1153: term_newln(p);
1154: }
1155:
1156:
1157: /* ARGSUSED */
1158: static void
1159: termp_op_post(DECL_ARGS)
1160: {
1161:
1162: if (MDOC_BODY != node->type)
1163: return;
1164: p->flags |= TERMP_NOSPACE;
1165: term_word(p, "\\(rB");
1166: }
1167:
1168:
1169: /* ARGSUSED */
1170: static int
1171: termp_xr_pre(DECL_ARGS)
1172: {
1173: const struct mdoc_node *n;
1174:
1.11 kristaps 1175: assert(node->child && MDOC_TEXT == node->child->type);
1176: n = node->child;
1177:
1.1 kristaps 1178: term_word(p, n->string);
1179: if (NULL == (n = n->next))
1180: return(0);
1181: p->flags |= TERMP_NOSPACE;
1182: term_word(p, "(");
1183: p->flags |= TERMP_NOSPACE;
1184: term_word(p, n->string);
1185: p->flags |= TERMP_NOSPACE;
1186: term_word(p, ")");
1187: return(0);
1188: }
1189:
1190:
1191: /* ARGSUSED */
1192: static int
1193: termp_vt_pre(DECL_ARGS)
1194: {
1195:
1196: /* FIXME: this can be "type name". */
1.29 kristaps 1197: pair->flag |= ttypes[TTYPE_VAR_DECL];
1.1 kristaps 1198: return(1);
1199: }
1200:
1201:
1202: /* ARGSUSED */
1203: static void
1204: termp_vt_post(DECL_ARGS)
1205: {
1206:
1.32 ! kristaps 1207: if (node->sec != SEC_SYNOPSIS)
! 1208: return;
! 1209: if (node->next && MDOC_Vt == node->next->tok)
! 1210: term_newln(p);
! 1211: else if (node->next)
1.1 kristaps 1212: term_vspace(p);
1213: }
1214:
1215:
1216: /* ARGSUSED */
1217: static int
1218: termp_fd_pre(DECL_ARGS)
1219: {
1220:
1.29 kristaps 1221: pair->flag |= ttypes[TTYPE_FUNC_DECL];
1.1 kristaps 1222: return(1);
1223: }
1224:
1225:
1226: /* ARGSUSED */
1227: static void
1228: termp_fd_post(DECL_ARGS)
1229: {
1230:
1231: if (node->sec != SEC_SYNOPSIS)
1232: return;
1.29 kristaps 1233:
1.1 kristaps 1234: term_newln(p);
1235: if (node->next && MDOC_Fd != node->next->tok)
1236: term_vspace(p);
1237: }
1238:
1239:
1240: /* ARGSUSED */
1241: static int
1242: termp_sh_pre(DECL_ARGS)
1243: {
1244:
1245: switch (node->type) {
1246: case (MDOC_HEAD):
1247: term_vspace(p);
1.28 kristaps 1248: pair->flag |= ttypes[TTYPE_SECTION];
1.1 kristaps 1249: break;
1250: case (MDOC_BODY):
1251: p->offset = INDENT;
1252: break;
1253: default:
1254: break;
1255: }
1256: return(1);
1257: }
1258:
1259:
1260: /* ARGSUSED */
1261: static void
1262: termp_sh_post(DECL_ARGS)
1263: {
1264:
1265: switch (node->type) {
1266: case (MDOC_HEAD):
1267: term_newln(p);
1268: break;
1269: case (MDOC_BODY):
1270: term_newln(p);
1271: p->offset = 0;
1272: break;
1273: default:
1274: break;
1275: }
1276: }
1277:
1278:
1279: /* ARGSUSED */
1280: static int
1281: termp_op_pre(DECL_ARGS)
1282: {
1283:
1284: switch (node->type) {
1285: case (MDOC_BODY):
1286: term_word(p, "\\(lB");
1287: p->flags |= TERMP_NOSPACE;
1288: break;
1289: default:
1290: break;
1291: }
1292: return(1);
1293: }
1294:
1295:
1296: /* ARGSUSED */
1297: static int
1298: termp_bt_pre(DECL_ARGS)
1299: {
1300:
1301: term_word(p, "is currently in beta test.");
1302: return(1);
1303: }
1304:
1305:
1306: /* ARGSUSED */
1307: static int
1308: termp_lb_pre(DECL_ARGS)
1309: {
1310: const char *lb;
1311:
1.11 kristaps 1312: assert(node->child && MDOC_TEXT == node->child->type);
1.18 kristaps 1313: lb = mdoc_a2lib(node->child->string);
1314: if (lb) {
1.1 kristaps 1315: term_word(p, lb);
1316: return(0);
1317: }
1318: term_word(p, "library");
1319: return(1);
1320: }
1321:
1322:
1323: /* ARGSUSED */
1324: static void
1325: termp_lb_post(DECL_ARGS)
1326: {
1327:
1328: term_newln(p);
1329: }
1330:
1331:
1332: /* ARGSUSED */
1333: static int
1334: termp_ud_pre(DECL_ARGS)
1335: {
1336:
1337: term_word(p, "currently under development.");
1338: return(1);
1339: }
1340:
1341:
1342: /* ARGSUSED */
1343: static int
1344: termp_d1_pre(DECL_ARGS)
1345: {
1346:
1.4 kristaps 1347: if (MDOC_BLOCK != node->type)
1.1 kristaps 1348: return(1);
1349: term_newln(p);
1.29 kristaps 1350: p->offset += (INDENT + 1);
1.1 kristaps 1351: return(1);
1352: }
1353:
1354:
1355: /* ARGSUSED */
1356: static void
1357: termp_d1_post(DECL_ARGS)
1358: {
1359:
1.4 kristaps 1360: if (MDOC_BLOCK != node->type)
1.1 kristaps 1361: return;
1362: term_newln(p);
1363: }
1364:
1365:
1366: /* ARGSUSED */
1367: static int
1368: termp_aq_pre(DECL_ARGS)
1369: {
1370:
1371: if (MDOC_BODY != node->type)
1372: return(1);
1373: term_word(p, "\\(la");
1374: p->flags |= TERMP_NOSPACE;
1375: return(1);
1376: }
1377:
1378:
1379: /* ARGSUSED */
1380: static void
1381: termp_aq_post(DECL_ARGS)
1382: {
1383:
1384: if (MDOC_BODY != node->type)
1385: return;
1386: p->flags |= TERMP_NOSPACE;
1387: term_word(p, "\\(ra");
1388: }
1389:
1390:
1391: /* ARGSUSED */
1392: static int
1393: termp_ft_pre(DECL_ARGS)
1394: {
1395:
1396: if (SEC_SYNOPSIS == node->sec)
1397: if (node->prev && MDOC_Fo == node->prev->tok)
1398: term_vspace(p);
1.28 kristaps 1399: pair->flag |= ttypes[TTYPE_FUNC_TYPE];
1.1 kristaps 1400: return(1);
1401: }
1402:
1403:
1404: /* ARGSUSED */
1405: static void
1406: termp_ft_post(DECL_ARGS)
1407: {
1408:
1409: if (SEC_SYNOPSIS == node->sec)
1410: term_newln(p);
1411: }
1412:
1413:
1414: /* ARGSUSED */
1415: static int
1416: termp_fn_pre(DECL_ARGS)
1417: {
1418: const struct mdoc_node *n;
1419:
1.11 kristaps 1420: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 1421:
1422: /* FIXME: can be "type funcname" "type varname"... */
1423:
1424: p->flags |= ttypes[TTYPE_FUNC_NAME];
1425: term_word(p, node->child->string);
1426: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1427:
1428: p->flags |= TERMP_NOSPACE;
1429: term_word(p, "(");
1430:
1431: for (n = node->child->next; n; n = n->next) {
1432: p->flags |= ttypes[TTYPE_FUNC_ARG];
1433: term_word(p, n->string);
1434: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1435: if (n->next)
1436: term_word(p, ",");
1437: }
1438:
1439: term_word(p, ")");
1440:
1441: if (SEC_SYNOPSIS == node->sec)
1442: term_word(p, ";");
1443:
1444: return(0);
1445: }
1446:
1447:
1448: /* ARGSUSED */
1449: static void
1450: termp_fn_post(DECL_ARGS)
1451: {
1452:
1453: if (node->sec == SEC_SYNOPSIS && node->next)
1454: term_vspace(p);
1455: }
1456:
1457:
1458: /* ARGSUSED */
1459: static int
1460: termp_sx_pre(DECL_ARGS)
1461: {
1462:
1.28 kristaps 1463: pair->flag |= ttypes[TTYPE_LINK];
1.1 kristaps 1464: return(1);
1465: }
1466:
1467:
1468: /* ARGSUSED */
1469: static int
1470: termp_fa_pre(DECL_ARGS)
1471: {
1472: struct mdoc_node *n;
1473:
1474: if (node->parent->tok != MDOC_Fo) {
1.28 kristaps 1475: pair->flag |= ttypes[TTYPE_FUNC_ARG];
1.1 kristaps 1476: return(1);
1477: }
1478:
1479: for (n = node->child; n; n = n->next) {
1480: p->flags |= ttypes[TTYPE_FUNC_ARG];
1481: term_word(p, n->string);
1482: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1483: if (n->next)
1484: term_word(p, ",");
1485: }
1486:
1487: if (node->child && node->next && node->next->tok == MDOC_Fa)
1488: term_word(p, ",");
1489:
1490: return(0);
1491: }
1492:
1493:
1494: /* ARGSUSED */
1495: static int
1496: termp_va_pre(DECL_ARGS)
1497: {
1498:
1.28 kristaps 1499: pair->flag |= ttypes[TTYPE_VAR_DECL];
1.1 kristaps 1500: return(1);
1501: }
1502:
1503:
1504: /* ARGSUSED */
1505: static int
1506: termp_bd_pre(DECL_ARGS)
1507: {
1508: int i, type, ln;
1509:
1510: /*
1511: * This is fairly tricky due primarily to crappy documentation.
1512: * If -ragged or -filled are specified, the block does nothing
1513: * but change the indentation.
1514: *
1515: * If, on the other hand, -unfilled or -literal are specified,
1516: * then the game changes. Text is printed exactly as entered in
1517: * the display: if a macro line, a newline is appended to the
1518: * line. Blank lines are allowed.
1519: */
1520:
1521: if (MDOC_BLOCK == node->type)
1522: return(fmt_block_vspace(p, node, node));
1523: else if (MDOC_BODY != node->type)
1524: return(1);
1525:
1.11 kristaps 1526: /* FIXME: display type should be mandated by parser. */
1527:
1.1 kristaps 1528: if (NULL == node->parent->args)
1529: errx(1, "missing display type");
1530:
1531: for (type = -1, i = 0;
1532: i < (int)node->parent->args->argc; i++) {
1533: switch (node->parent->args->argv[i].arg) {
1534: case (MDOC_Ragged):
1535: /* FALLTHROUGH */
1536: case (MDOC_Filled):
1537: /* FALLTHROUGH */
1538: case (MDOC_Unfilled):
1539: /* FALLTHROUGH */
1540: case (MDOC_Literal):
1541: type = node->parent->args->argv[i].arg;
1542: i = (int)node->parent->args->argc;
1543: break;
1544: default:
1545: break;
1546: }
1547: }
1548:
1549: if (NULL == node->parent->args)
1550: errx(1, "missing display type");
1551:
1552: i = arg_getattr(MDOC_Offset, node->parent);
1553: if (-1 != i) {
1554: if (1 != node->parent->args->argv[i].sz)
1555: errx(1, "expected single value");
1556: p->offset += arg_offset(&node->parent->args->argv[i]);
1557: }
1558:
1559: switch (type) {
1560: case (MDOC_Literal):
1561: /* FALLTHROUGH */
1562: case (MDOC_Unfilled):
1563: break;
1564: default:
1565: return(1);
1566: }
1567:
1568: /*
1569: * Tricky. Iterate through all children. If we're on a
1570: * different parse line, append a newline and then the contents.
1571: * Ew.
1572: */
1573:
1574: p->flags |= TERMP_LITERAL;
1575: ln = node->child ? node->child->line : 0;
1576:
1577: for (node = node->child; node; node = node->next) {
1578: if (ln < node->line) {
1579: term_flushln(p);
1580: p->flags |= TERMP_NOSPACE;
1581: }
1582: ln = node->line;
1583: print_node(p, pair, meta, node);
1584: }
1585:
1586: return(0);
1587: }
1588:
1589:
1590: /* ARGSUSED */
1591: static void
1592: termp_bd_post(DECL_ARGS)
1593: {
1594:
1595: if (MDOC_BODY != node->type)
1596: return;
1597:
1598: term_flushln(p);
1599: p->flags &= ~TERMP_LITERAL;
1600: p->flags |= TERMP_NOSPACE;
1601: }
1602:
1603:
1604: /* ARGSUSED */
1605: static int
1606: termp_qq_pre(DECL_ARGS)
1607: {
1608:
1609: if (MDOC_BODY != node->type)
1610: return(1);
1611: term_word(p, "\"");
1612: p->flags |= TERMP_NOSPACE;
1613: return(1);
1614: }
1615:
1616:
1617: /* ARGSUSED */
1618: static void
1619: termp_qq_post(DECL_ARGS)
1620: {
1621:
1622: if (MDOC_BODY != node->type)
1623: return;
1624: p->flags |= TERMP_NOSPACE;
1625: term_word(p, "\"");
1626: }
1627:
1628:
1629: /* ARGSUSED */
1630: static void
1631: termp_bx_post(DECL_ARGS)
1632: {
1633:
1634: if (node->child)
1635: p->flags |= TERMP_NOSPACE;
1636: term_word(p, "BSD");
1637: }
1638:
1639:
1640: /* ARGSUSED */
1641: static int
1.26 kristaps 1642: termp_xx_pre(DECL_ARGS)
1.1 kristaps 1643: {
1.26 kristaps 1644: const char *pp;
1.1 kristaps 1645:
1.26 kristaps 1646: pp = NULL;
1647: switch (node->tok) {
1648: case (MDOC_Bsx):
1649: pp = "BSDI BSD/OS";
1650: break;
1651: case (MDOC_Dx):
1652: pp = "DragonFlyBSD";
1653: break;
1654: case (MDOC_Fx):
1655: pp = "FreeBSD";
1656: break;
1657: case (MDOC_Nx):
1658: pp = "NetBSD";
1659: break;
1660: case (MDOC_Ox):
1661: pp = "OpenBSD";
1662: break;
1663: case (MDOC_Ux):
1664: pp = "UNIX";
1665: break;
1666: default:
1667: break;
1668: }
1.1 kristaps 1669:
1.26 kristaps 1670: assert(pp);
1671: term_word(p, pp);
1.1 kristaps 1672: return(1);
1673: }
1674:
1675:
1676: /* ARGSUSED */
1677: static int
1678: termp_sq_pre(DECL_ARGS)
1679: {
1680:
1681: if (MDOC_BODY != node->type)
1682: return(1);
1683: term_word(p, "\\(oq");
1684: p->flags |= TERMP_NOSPACE;
1685: return(1);
1686: }
1687:
1688:
1689: /* ARGSUSED */
1690: static void
1691: termp_sq_post(DECL_ARGS)
1692: {
1693:
1694: if (MDOC_BODY != node->type)
1695: return;
1696: p->flags |= TERMP_NOSPACE;
1697: term_word(p, "\\(aq");
1698: }
1699:
1700:
1701: /* ARGSUSED */
1702: static int
1703: termp_pf_pre(DECL_ARGS)
1704: {
1705:
1706: p->flags |= TERMP_IGNDELIM;
1707: return(1);
1708: }
1709:
1710:
1711: /* ARGSUSED */
1712: static void
1713: termp_pf_post(DECL_ARGS)
1714: {
1715:
1716: p->flags &= ~TERMP_IGNDELIM;
1717: p->flags |= TERMP_NOSPACE;
1718: }
1719:
1720:
1721: /* ARGSUSED */
1722: static int
1723: termp_ss_pre(DECL_ARGS)
1724: {
1725:
1726: switch (node->type) {
1727: case (MDOC_BLOCK):
1728: term_newln(p);
1729: if (node->prev)
1730: term_vspace(p);
1731: break;
1732: case (MDOC_HEAD):
1.28 kristaps 1733: pair->flag |= ttypes[TTYPE_SSECTION];
1.5 kristaps 1734: p->offset = HALFINDENT;
1.1 kristaps 1735: break;
1736: default:
1737: break;
1738: }
1739:
1740: return(1);
1741: }
1742:
1743:
1744: /* ARGSUSED */
1745: static void
1746: termp_ss_post(DECL_ARGS)
1747: {
1748:
1.29 kristaps 1749: if (MDOC_HEAD == node->type)
1.1 kristaps 1750: term_newln(p);
1751: }
1752:
1753:
1754: /* ARGSUSED */
1755: static int
1756: termp_pa_pre(DECL_ARGS)
1757: {
1758:
1.28 kristaps 1759: pair->flag |= ttypes[TTYPE_FILE];
1.1 kristaps 1760: return(1);
1761: }
1762:
1763:
1764: /* ARGSUSED */
1765: static int
1766: termp_em_pre(DECL_ARGS)
1767: {
1768:
1.28 kristaps 1769: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 1770: return(1);
1771: }
1772:
1773:
1774: /* ARGSUSED */
1775: static int
1776: termp_cd_pre(DECL_ARGS)
1777: {
1778:
1.28 kristaps 1779: pair->flag |= ttypes[TTYPE_CONFIG];
1.1 kristaps 1780: term_newln(p);
1781: return(1);
1782: }
1783:
1784:
1785: /* ARGSUSED */
1786: static int
1787: termp_cm_pre(DECL_ARGS)
1788: {
1789:
1.28 kristaps 1790: pair->flag |= ttypes[TTYPE_CMD_FLAG];
1.1 kristaps 1791: return(1);
1792: }
1793:
1794:
1795: /* ARGSUSED */
1796: static int
1797: termp_ic_pre(DECL_ARGS)
1798: {
1799:
1.28 kristaps 1800: pair->flag |= ttypes[TTYPE_CMD];
1.1 kristaps 1801: return(1);
1802: }
1803:
1804:
1805: /* ARGSUSED */
1806: static int
1807: termp_in_pre(DECL_ARGS)
1808: {
1809:
1.28 kristaps 1810: pair->flag |= ttypes[TTYPE_INCLUDE];
1811: p->flags |= ttypes[TTYPE_INCLUDE];
1.22 kristaps 1812:
1813: if (SEC_SYNOPSIS == node->sec)
1814: term_word(p, "#include");
1815:
1.1 kristaps 1816: term_word(p, "<");
1817: p->flags |= TERMP_NOSPACE;
1818: return(1);
1819: }
1820:
1821:
1822: /* ARGSUSED */
1823: static void
1824: termp_in_post(DECL_ARGS)
1825: {
1826:
1827: p->flags |= TERMP_NOSPACE;
1828: term_word(p, ">");
1829:
1830: if (SEC_SYNOPSIS != node->sec)
1831: return;
1.22 kristaps 1832:
1833: term_newln(p);
1834: /*
1835: * XXX Not entirely correct. If `.In foo bar' is specified in
1836: * the SYNOPSIS section, then it produces a single break after
1837: * the <foo>; mandoc asserts a vertical space. Since this
1838: * construction is rarely used, I think it's fine.
1839: */
1.1 kristaps 1840: if (node->next && MDOC_In != node->next->tok)
1841: term_vspace(p);
1842: }
1843:
1844:
1845: /* ARGSUSED */
1846: static int
1847: termp_at_pre(DECL_ARGS)
1848: {
1849: const char *att;
1850:
1851: att = NULL;
1852:
1853: if (node->child)
1854: att = mdoc_a2att(node->child->string);
1855: if (NULL == att)
1856: att = "AT&T UNIX";
1857:
1858: term_word(p, att);
1859: return(0);
1860: }
1861:
1862:
1863: /* ARGSUSED */
1864: static int
1865: termp_brq_pre(DECL_ARGS)
1866: {
1867:
1868: if (MDOC_BODY != node->type)
1869: return(1);
1870: term_word(p, "\\(lC");
1871: p->flags |= TERMP_NOSPACE;
1872: return(1);
1873: }
1874:
1875:
1876: /* ARGSUSED */
1877: static void
1878: termp_brq_post(DECL_ARGS)
1879: {
1880:
1881: if (MDOC_BODY != node->type)
1882: return;
1883: p->flags |= TERMP_NOSPACE;
1884: term_word(p, "\\(rC");
1885: }
1886:
1887:
1888: /* ARGSUSED */
1889: static int
1890: termp_bq_pre(DECL_ARGS)
1891: {
1892:
1893: if (MDOC_BODY != node->type)
1894: return(1);
1895: term_word(p, "\\(lB");
1896: p->flags |= TERMP_NOSPACE;
1897: return(1);
1898: }
1899:
1900:
1901: /* ARGSUSED */
1902: static void
1903: termp_bq_post(DECL_ARGS)
1904: {
1905:
1906: if (MDOC_BODY != node->type)
1907: return;
1908: p->flags |= TERMP_NOSPACE;
1909: term_word(p, "\\(rB");
1910: }
1911:
1912:
1913: /* ARGSUSED */
1914: static int
1915: termp_pq_pre(DECL_ARGS)
1916: {
1917:
1918: if (MDOC_BODY != node->type)
1919: return(1);
1920: term_word(p, "\\&(");
1921: p->flags |= TERMP_NOSPACE;
1922: return(1);
1923: }
1924:
1925:
1926: /* ARGSUSED */
1927: static void
1928: termp_pq_post(DECL_ARGS)
1929: {
1930:
1931: if (MDOC_BODY != node->type)
1932: return;
1933: term_word(p, ")");
1934: }
1935:
1936:
1937: /* ARGSUSED */
1938: static int
1939: termp_fo_pre(DECL_ARGS)
1940: {
1941: const struct mdoc_node *n;
1942:
1943: if (MDOC_BODY == node->type) {
1944: term_word(p, "(");
1945: p->flags |= TERMP_NOSPACE;
1946: return(1);
1947: } else if (MDOC_HEAD != node->type)
1948: return(1);
1949:
1950: /* XXX - groff shows only first parameter */
1951:
1952: p->flags |= ttypes[TTYPE_FUNC_NAME];
1953: for (n = node->child; n; n = n->next) {
1.11 kristaps 1954: assert(MDOC_TEXT == n->type);
1.1 kristaps 1955: term_word(p, n->string);
1956: }
1957: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1958:
1959: return(0);
1960: }
1961:
1962:
1963: /* ARGSUSED */
1964: static void
1965: termp_fo_post(DECL_ARGS)
1966: {
1967:
1968: if (MDOC_BODY != node->type)
1969: return;
1970: p->flags |= TERMP_NOSPACE;
1971: term_word(p, ")");
1972: p->flags |= TERMP_NOSPACE;
1973: term_word(p, ";");
1974: term_newln(p);
1975: }
1976:
1977:
1978: /* ARGSUSED */
1979: static int
1980: termp_bf_pre(DECL_ARGS)
1981: {
1982: const struct mdoc_node *n;
1983:
1.28 kristaps 1984: if (MDOC_HEAD == node->type)
1.1 kristaps 1985: return(0);
1.28 kristaps 1986: else if (MDOC_BLOCK != node->type)
1.1 kristaps 1987: return(1);
1988:
1989: if (NULL == (n = node->head->child)) {
1990: if (arg_hasattr(MDOC_Emphasis, node))
1.28 kristaps 1991: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 1992: else if (arg_hasattr(MDOC_Symbolic, node))
1.28 kristaps 1993: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 1994:
1995: return(1);
1996: }
1997:
1.11 kristaps 1998: assert(MDOC_TEXT == n->type);
1.1 kristaps 1999: if (0 == strcmp("Em", n->string))
1.28 kristaps 2000: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 2001: else if (0 == strcmp("Sy", n->string))
1.28 kristaps 2002: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2003:
2004: return(1);
2005: }
2006:
2007:
2008: /* ARGSUSED */
2009: static int
2010: termp_sy_pre(DECL_ARGS)
2011: {
2012:
1.28 kristaps 2013: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2014: return(1);
2015: }
2016:
2017:
2018: /* ARGSUSED */
2019: static int
2020: termp_ms_pre(DECL_ARGS)
2021: {
2022:
1.28 kristaps 2023: pair->flag |= ttypes[TTYPE_SYMBOL];
1.1 kristaps 2024: return(1);
2025: }
2026:
2027:
2028:
2029: /* ARGSUSED */
2030: static int
2031: termp_sm_pre(DECL_ARGS)
2032: {
2033:
1.11 kristaps 2034: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 2035: if (0 == strcmp("on", node->child->string)) {
2036: p->flags &= ~TERMP_NONOSPACE;
2037: p->flags &= ~TERMP_NOSPACE;
2038: } else
2039: p->flags |= TERMP_NONOSPACE;
2040:
2041: return(0);
2042: }
2043:
2044:
2045: /* ARGSUSED */
2046: static int
2047: termp_ap_pre(DECL_ARGS)
2048: {
2049:
2050: p->flags |= TERMP_NOSPACE;
2051: term_word(p, "\\(aq");
2052: p->flags |= TERMP_NOSPACE;
2053: return(1);
2054: }
2055:
2056:
2057: /* ARGSUSED */
2058: static int
2059: termp__j_pre(DECL_ARGS)
2060: {
2061:
1.28 kristaps 2062: pair->flag |= ttypes[TTYPE_REF_JOURNAL];
1.1 kristaps 2063: return(1);
2064: }
2065:
2066:
2067: /* ARGSUSED */
2068: static int
2069: termp__t_pre(DECL_ARGS)
2070: {
2071:
2072: term_word(p, "\"");
2073: p->flags |= TERMP_NOSPACE;
2074: return(1);
2075: }
2076:
2077:
2078: /* ARGSUSED */
2079: static void
2080: termp__t_post(DECL_ARGS)
2081: {
2082:
2083: p->flags |= TERMP_NOSPACE;
2084: term_word(p, "\"");
2085: termp____post(p, pair, meta, node);
2086: }
2087:
2088:
2089: /* ARGSUSED */
2090: static void
2091: termp____post(DECL_ARGS)
2092: {
2093:
2094: p->flags |= TERMP_NOSPACE;
2095: term_word(p, node->next ? "," : ".");
2096: }
2097:
2098:
2099: /* ARGSUSED */
2100: static int
2101: termp_lk_pre(DECL_ARGS)
2102: {
2103: const struct mdoc_node *n;
2104:
1.11 kristaps 2105: assert(node->child);
2106: n = node->child;
1.1 kristaps 2107:
1.12 kristaps 2108: if (NULL == n->next) {
1.28 kristaps 2109: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.12 kristaps 2110: return(1);
2111: }
2112:
1.1 kristaps 2113: p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2114: term_word(p, n->string);
2115: p->flags |= TERMP_NOSPACE;
2116: term_word(p, ":");
1.12 kristaps 2117: p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2118:
2119: p->flags |= ttypes[TTYPE_LINK_TEXT];
1.12 kristaps 2120: for (n = n->next; n; n = n->next)
1.1 kristaps 2121: term_word(p, n->string);
1.12 kristaps 2122:
1.1 kristaps 2123: p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2124: return(0);
2125: }
2126:
2127:
2128: /* ARGSUSED */
2129: static int
2130: termp_mt_pre(DECL_ARGS)
2131: {
2132:
1.28 kristaps 2133: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2134: return(1);
2135: }
2136:
2137:
CVSweb