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