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