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