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