Annotation of src/usr.bin/mandoc/mdoc_term.c, Revision 1.38
1.38 ! schwarze 1: /* $Id: mdoc_term.c,v 1.37 2009/07/26 00:49:19 schwarze Exp $ */
1.1 kristaps 2: /*
1.2 schwarze 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.2 schwarze 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.2 schwarze 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.28 schwarze 85: int flag;
1.27 schwarze 86: int count;
1.1 kristaps 87: };
88:
1.29 schwarze 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: };
1.29 schwarze 98:
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);
1.38 ! schwarze 133: static int termp_br_pre(DECL_ARGS);
1.29 schwarze 134: static int termp_brq_pre(DECL_ARGS);
135: static int termp_bt_pre(DECL_ARGS);
136: static int termp_cd_pre(DECL_ARGS);
137: static int termp_cm_pre(DECL_ARGS);
138: static int termp_d1_pre(DECL_ARGS);
139: static int termp_dq_pre(DECL_ARGS);
140: static int termp_em_pre(DECL_ARGS);
141: static int termp_ex_pre(DECL_ARGS);
142: static int termp_fa_pre(DECL_ARGS);
143: static int termp_fd_pre(DECL_ARGS);
144: static int termp_fl_pre(DECL_ARGS);
145: static int termp_fn_pre(DECL_ARGS);
146: static int termp_fo_pre(DECL_ARGS);
147: static int termp_ft_pre(DECL_ARGS);
148: static int termp_ic_pre(DECL_ARGS);
149: static int termp_in_pre(DECL_ARGS);
150: static int termp_it_pre(DECL_ARGS);
151: static int termp_lk_pre(DECL_ARGS);
152: static int termp_ms_pre(DECL_ARGS);
153: static int termp_mt_pre(DECL_ARGS);
154: static int termp_nd_pre(DECL_ARGS);
155: static int termp_nm_pre(DECL_ARGS);
156: static int termp_ns_pre(DECL_ARGS);
157: static int termp_op_pre(DECL_ARGS);
158: static int termp_pa_pre(DECL_ARGS);
159: static int termp_pf_pre(DECL_ARGS);
160: static int termp_pp_pre(DECL_ARGS);
161: static int termp_pq_pre(DECL_ARGS);
162: static int termp_qq_pre(DECL_ARGS);
163: static int termp_rs_pre(DECL_ARGS);
164: static int termp_rv_pre(DECL_ARGS);
165: static int termp_sh_pre(DECL_ARGS);
166: static int termp_sm_pre(DECL_ARGS);
1.38 ! schwarze 167: static int termp_sp_pre(DECL_ARGS);
1.29 schwarze 168: static int termp_sq_pre(DECL_ARGS);
169: static int termp_ss_pre(DECL_ARGS);
170: static int termp_sx_pre(DECL_ARGS);
171: static int termp_sy_pre(DECL_ARGS);
172: static int termp_ud_pre(DECL_ARGS);
173: static int termp_va_pre(DECL_ARGS);
174: static int termp_vt_pre(DECL_ARGS);
175: static int termp_xr_pre(DECL_ARGS);
176: static int termp_xx_pre(DECL_ARGS);
1.1 kristaps 177:
178: static const struct termact termacts[MDOC_MAX] = {
1.11 schwarze 179: { termp_ap_pre, NULL }, /* Ap */
1.1 kristaps 180: { NULL, NULL }, /* Dd */
181: { NULL, NULL }, /* Dt */
182: { NULL, NULL }, /* Os */
183: { termp_sh_pre, termp_sh_post }, /* Sh */
184: { termp_ss_pre, termp_ss_post }, /* Ss */
185: { termp_pp_pre, NULL }, /* Pp */
186: { termp_d1_pre, termp_d1_post }, /* D1 */
187: { termp_d1_pre, termp_d1_post }, /* Dl */
188: { termp_bd_pre, termp_bd_post }, /* Bd */
189: { NULL, NULL }, /* Ed */
190: { NULL, termp_bl_post }, /* Bl */
191: { NULL, NULL }, /* El */
192: { termp_it_pre, termp_it_post }, /* It */
193: { NULL, NULL }, /* Ad */
194: { NULL, NULL }, /* An */
195: { termp_ar_pre, NULL }, /* Ar */
196: { termp_cd_pre, NULL }, /* Cd */
197: { termp_cm_pre, NULL }, /* Cm */
198: { NULL, NULL }, /* Dv */
199: { NULL, NULL }, /* Er */
200: { NULL, NULL }, /* Ev */
201: { termp_ex_pre, NULL }, /* Ex */
202: { termp_fa_pre, NULL }, /* Fa */
203: { termp_fd_pre, termp_fd_post }, /* Fd */
204: { termp_fl_pre, NULL }, /* Fl */
205: { termp_fn_pre, termp_fn_post }, /* Fn */
206: { termp_ft_pre, termp_ft_post }, /* Ft */
207: { termp_ic_pre, NULL }, /* Ic */
208: { termp_in_pre, termp_in_post }, /* In */
209: { NULL, NULL }, /* Li */
210: { termp_nd_pre, NULL }, /* Nd */
211: { termp_nm_pre, NULL }, /* Nm */
212: { termp_op_pre, termp_op_post }, /* Op */
213: { NULL, NULL }, /* Ot */
214: { termp_pa_pre, NULL }, /* Pa */
215: { termp_rv_pre, NULL }, /* Rv */
1.32 schwarze 216: { NULL, NULL }, /* St */
1.1 kristaps 217: { termp_va_pre, NULL }, /* Va */
218: { termp_vt_pre, termp_vt_post }, /* Vt */
219: { termp_xr_pre, NULL }, /* Xr */
220: { NULL, termp____post }, /* %A */
221: { NULL, termp____post }, /* %B */
222: { NULL, termp____post }, /* %D */
223: { NULL, termp____post }, /* %I */
224: { termp__j_pre, termp____post }, /* %J */
225: { NULL, termp____post }, /* %N */
226: { NULL, termp____post }, /* %O */
227: { NULL, termp____post }, /* %P */
228: { NULL, termp____post }, /* %R */
229: { termp__t_pre, termp__t_post }, /* %T */
230: { NULL, termp____post }, /* %V */
231: { NULL, NULL }, /* Ac */
232: { termp_aq_pre, termp_aq_post }, /* Ao */
233: { termp_aq_pre, termp_aq_post }, /* Aq */
1.32 schwarze 234: { NULL, NULL }, /* At */
1.1 kristaps 235: { NULL, NULL }, /* Bc */
236: { termp_bf_pre, NULL }, /* Bf */
237: { termp_bq_pre, termp_bq_post }, /* Bo */
238: { termp_bq_pre, termp_bq_post }, /* Bq */
1.26 schwarze 239: { termp_xx_pre, NULL }, /* Bsx */
1.1 kristaps 240: { NULL, termp_bx_post }, /* Bx */
241: { NULL, NULL }, /* Db */
242: { NULL, NULL }, /* Dc */
243: { termp_dq_pre, termp_dq_post }, /* Do */
244: { termp_dq_pre, termp_dq_post }, /* Dq */
245: { NULL, NULL }, /* Ec */
246: { NULL, NULL }, /* Ef */
247: { termp_em_pre, NULL }, /* Em */
248: { NULL, NULL }, /* Eo */
1.26 schwarze 249: { termp_xx_pre, NULL }, /* Fx */
1.1 kristaps 250: { termp_ms_pre, NULL }, /* Ms */
251: { NULL, NULL }, /* No */
252: { termp_ns_pre, NULL }, /* Ns */
1.26 schwarze 253: { termp_xx_pre, NULL }, /* Nx */
254: { termp_xx_pre, NULL }, /* Ox */
1.1 kristaps 255: { NULL, NULL }, /* Pc */
256: { termp_pf_pre, termp_pf_post }, /* Pf */
257: { termp_pq_pre, termp_pq_post }, /* Po */
258: { termp_pq_pre, termp_pq_post }, /* Pq */
259: { NULL, NULL }, /* Qc */
260: { termp_sq_pre, termp_sq_post }, /* Ql */
261: { termp_qq_pre, termp_qq_post }, /* Qo */
262: { termp_qq_pre, termp_qq_post }, /* Qq */
263: { NULL, NULL }, /* Re */
264: { termp_rs_pre, NULL }, /* Rs */
265: { NULL, NULL }, /* Sc */
266: { termp_sq_pre, termp_sq_post }, /* So */
267: { termp_sq_pre, termp_sq_post }, /* Sq */
268: { termp_sm_pre, NULL }, /* Sm */
269: { termp_sx_pre, NULL }, /* Sx */
270: { termp_sy_pre, NULL }, /* Sy */
271: { NULL, NULL }, /* Tn */
1.26 schwarze 272: { termp_xx_pre, NULL }, /* Ux */
1.1 kristaps 273: { NULL, NULL }, /* Xc */
274: { NULL, NULL }, /* Xo */
275: { termp_fo_pre, termp_fo_post }, /* Fo */
276: { NULL, NULL }, /* Fc */
277: { termp_op_pre, termp_op_post }, /* Oo */
278: { NULL, NULL }, /* Oc */
279: { NULL, NULL }, /* Bk */
280: { NULL, NULL }, /* Ek */
281: { termp_bt_pre, NULL }, /* Bt */
282: { NULL, NULL }, /* Hf */
283: { NULL, NULL }, /* Fr */
284: { termp_ud_pre, NULL }, /* Ud */
1.32 schwarze 285: { NULL, termp_lb_post }, /* Lb */
1.13 schwarze 286: { termp_pp_pre, NULL }, /* Lp */
1.1 kristaps 287: { termp_lk_pre, NULL }, /* Lk */
288: { termp_mt_pre, NULL }, /* Mt */
289: { termp_brq_pre, termp_brq_post }, /* Brq */
290: { termp_brq_pre, termp_brq_post }, /* Bro */
291: { NULL, NULL }, /* Brc */
292: { NULL, NULL }, /* %C */
293: { NULL, NULL }, /* Es */
294: { NULL, NULL }, /* En */
1.26 schwarze 295: { termp_xx_pre, NULL }, /* Dx */
1.1 kristaps 296: { NULL, NULL }, /* %Q */
1.38 ! schwarze 297: { termp_br_pre, NULL }, /* br */
! 298: { termp_sp_pre, NULL }, /* sp */
1.1 kristaps 299: };
300:
301: static int arg_hasattr(int, const struct mdoc_node *);
302: static int arg_getattrs(const int *, int *, size_t,
303: const struct mdoc_node *);
304: static int arg_getattr(int, const struct mdoc_node *);
305: static size_t arg_offset(const struct mdoc_argv *);
306: static size_t arg_width(const struct mdoc_argv *, int);
307: static int arg_listtype(const struct mdoc_node *);
308: static int fmt_block_vspace(struct termp *,
309: const struct mdoc_node *,
310: const struct mdoc_node *);
311: static void print_node(DECL_ARGS);
312: static void print_head(struct termp *,
313: const struct mdoc_meta *);
314: static void print_body(DECL_ARGS);
315: static void print_foot(struct termp *,
316: const struct mdoc_meta *);
317:
318:
319: int
320: mdoc_run(struct termp *p, const struct mdoc *m)
321: {
1.4 schwarze 322: /*
323: * Main output function. When this is called, assume that the
324: * tree is properly formed.
325: */
1.1 kristaps 326: print_head(p, mdoc_meta(m));
1.13 schwarze 327: assert(mdoc_node(m));
328: assert(MDOC_ROOT == mdoc_node(m)->type);
329: if (mdoc_node(m)->child)
330: print_body(p, NULL, mdoc_meta(m), mdoc_node(m)->child);
1.1 kristaps 331: print_foot(p, mdoc_meta(m));
332: return(1);
333: }
334:
335:
336: static void
337: print_body(DECL_ARGS)
338: {
339:
340: print_node(p, pair, meta, node);
341: if ( ! node->next)
342: return;
343: print_body(p, pair, meta, node->next);
344: }
345:
346:
347: static void
348: print_node(DECL_ARGS)
349: {
350: int dochild;
351: struct termpair npair;
1.28 schwarze 352: size_t offset, rmargin;
1.1 kristaps 353:
1.28 schwarze 354: dochild = 1;
355: offset = p->offset;
356: rmargin = p->rmargin;
1.1 kristaps 357:
358: npair.ppair = pair;
359: npair.flag = 0;
360: npair.count = 0;
361:
362: if (MDOC_TEXT != node->type) {
363: if (termacts[node->tok].pre)
364: if ( ! (*termacts[node->tok].pre)(p, &npair, meta, node))
365: dochild = 0;
366: } else /* MDOC_TEXT == node->type */
367: term_word(p, node->string);
368:
369: /* Children. */
370:
1.27 schwarze 371: p->flags |= npair.flag;
1.1 kristaps 372:
373: if (dochild && node->child)
374: print_body(p, &npair, meta, node->child);
375:
1.27 schwarze 376: p->flags &= ~npair.flag;
1.1 kristaps 377:
378: /* Post-processing. */
379:
380: if (MDOC_TEXT != node->type)
381: if (termacts[node->tok].post)
382: (*termacts[node->tok].post)(p, &npair, meta, node);
1.28 schwarze 383:
384: p->offset = offset;
385: p->rmargin = rmargin;
1.1 kristaps 386: }
387:
388:
389: static void
390: print_foot(struct termp *p, const struct mdoc_meta *meta)
391: {
392: struct tm *tm;
393: char *buf, *os;
394:
1.7 schwarze 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.36 schwarze 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.7 schwarze 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.7 schwarze 424: p->offset = p->rmargin;
425: p->rmargin = p->maxrmargin - strlen(os);
1.1 kristaps 426: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.7 schwarze 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.7 schwarze 434: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.1 kristaps 435:
1.7 schwarze 436: term_word(p, os);
1.1 kristaps 437: term_flushln(p);
1.7 schwarze 438:
439: p->offset = 0;
440: p->rmargin = p->maxrmargin;
441: p->flags = 0;
1.1 kristaps 442:
443: free(buf);
444: free(os);
445: }
446:
447:
448: static void
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 schwarze 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.8 schwarze 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.8 schwarze 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.34 schwarze 521: const char *p;
1.1 kristaps 522:
523: assert(pos < (int)arg->sz && pos >= 0);
524: assert(arg->value[pos]);
525:
1.34 schwarze 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.34 schwarze 532: if ( ! isdigit((u_char)p[i]))
1.1 kristaps 533: break;
534:
1.34 schwarze 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.34 schwarze 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.33 schwarze 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.33 schwarze 580: return(-1);
1.1 kristaps 581: }
582:
583:
584: static size_t
585: arg_offset(const struct mdoc_argv *arg)
586: {
1.34 schwarze 587: int len, i;
588: const char *p;
1.1 kristaps 589:
590: assert(*arg->value);
1.34 schwarze 591: p = *arg->value;
592:
593: if (0 == strcmp(p, "left"))
1.9 schwarze 594: return(0);
1.34 schwarze 595: if (0 == strcmp(p, "indent"))
1.17 schwarze 596: return(INDENT + 1);
1.34 schwarze 597: if (0 == strcmp(p, "indent-two"))
1.20 schwarze 598: return((INDENT + 1) * 2);
1.10 schwarze 599:
1.34 schwarze 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.10 schwarze 610:
1.34 schwarze 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.28 schwarze 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.33 schwarze 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.21 schwarze 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.33 schwarze 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;
1.19 schwarze 777: break;
778: case (MDOC_Enum):
779: if (width < 5)
780: width = 5;
1.1 kristaps 781: break;
1.33 schwarze 782: case (MDOC_Hang):
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.16 schwarze 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.33 schwarze 840: if (MDOC_HEAD == node->type)
841: p->flags |= TERMP_NOBREAK;
842: else
843: p->flags |= TERMP_NOLPAD;
844: break;
845: case (MDOC_Hang):
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.33 schwarze 859:
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.33 schwarze 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.12 schwarze 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.12 schwarze 924: p->flags |= TERMP_BOLD;
1.1 kristaps 925: term_word(p, "\\[bu]");
1.35 schwarze 926: p->flags &= ~TERMP_BOLD;
1.1 kristaps 927: break;
928: case (MDOC_Dash):
929: /* FALLTHROUGH */
930: case (MDOC_Hyphen):
1.12 schwarze 931: p->flags |= TERMP_BOLD;
1.35 schwarze 932: term_word(p, "\\(hy");
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: }
1.12 schwarze 944:
1.1 kristaps 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.33 schwarze 984: assert(-1 != type);
1.1 kristaps 985:
986: switch (type) {
987: case (MDOC_Diag):
1.37 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 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.35 schwarze 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.35 schwarze 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 schwarze 1137:
1138: if (MDOC_BODY != node->type)
1139: return(1);
1140:
1141: #if defined(__OpenBSD__) || defined(__linux__)
1142: term_word(p, "\\(en");
1.24 schwarze 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.10 schwarze 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.27 schwarze 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.30 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 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:
1333: if (MDOC_BLOCK != node->type)
1334: return(1);
1335: term_newln(p);
1.28 schwarze 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:
1346: if (MDOC_BLOCK != node->type)
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.27 schwarze 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.10 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 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.10 schwarze 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 schwarze 1628: termp_xx_pre(DECL_ARGS)
1.1 kristaps 1629: {
1.26 schwarze 1630: const char *pp;
1.1 kristaps 1631:
1.26 schwarze 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 schwarze 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.27 schwarze 1719: pair->flag |= ttypes[TTYPE_SSECTION];
1.3 schwarze 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.35 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 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.27 schwarze 1796: pair->flag |= ttypes[TTYPE_INCLUDE];
1797: p->flags |= ttypes[TTYPE_INCLUDE];
1.23 schwarze 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.23 schwarze 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);
1.38 ! schwarze 1828: }
! 1829:
! 1830:
! 1831: /* ARGSUSED */
! 1832: static int
! 1833: termp_sp_pre(DECL_ARGS)
! 1834: {
! 1835: int i, len;
! 1836:
! 1837: if (NULL == node->child) {
! 1838: term_vspace(p);
! 1839: return(0);
! 1840: }
! 1841:
! 1842: len = atoi(node->child->string);
! 1843: if (0 == len)
! 1844: term_newln(p);
! 1845: for (i = 0; i < len; i++)
! 1846: term_vspace(p);
! 1847:
! 1848: return(0);
! 1849: }
! 1850:
! 1851:
! 1852: /* ARGSUSED */
! 1853: static int
! 1854: termp_br_pre(DECL_ARGS)
! 1855: {
! 1856:
! 1857: term_newln(p);
! 1858: return(1);
1.1 kristaps 1859: }
1860:
1861:
1862: /* ARGSUSED */
1863: static int
1864: termp_brq_pre(DECL_ARGS)
1865: {
1866:
1867: if (MDOC_BODY != node->type)
1868: return(1);
1869: term_word(p, "\\(lC");
1870: p->flags |= TERMP_NOSPACE;
1871: return(1);
1872: }
1873:
1874:
1875: /* ARGSUSED */
1876: static void
1877: termp_brq_post(DECL_ARGS)
1878: {
1879:
1880: if (MDOC_BODY != node->type)
1881: return;
1882: p->flags |= TERMP_NOSPACE;
1883: term_word(p, "\\(rC");
1884: }
1885:
1886:
1887: /* ARGSUSED */
1888: static int
1889: termp_bq_pre(DECL_ARGS)
1890: {
1891:
1892: if (MDOC_BODY != node->type)
1893: return(1);
1894: term_word(p, "\\(lB");
1895: p->flags |= TERMP_NOSPACE;
1896: return(1);
1897: }
1898:
1899:
1900: /* ARGSUSED */
1901: static void
1902: termp_bq_post(DECL_ARGS)
1903: {
1904:
1905: if (MDOC_BODY != node->type)
1906: return;
1907: p->flags |= TERMP_NOSPACE;
1908: term_word(p, "\\(rB");
1909: }
1910:
1911:
1912: /* ARGSUSED */
1913: static int
1914: termp_pq_pre(DECL_ARGS)
1915: {
1916:
1917: if (MDOC_BODY != node->type)
1918: return(1);
1919: term_word(p, "\\&(");
1920: p->flags |= TERMP_NOSPACE;
1921: return(1);
1922: }
1923:
1924:
1925: /* ARGSUSED */
1926: static void
1927: termp_pq_post(DECL_ARGS)
1928: {
1929:
1930: if (MDOC_BODY != node->type)
1931: return;
1932: term_word(p, ")");
1933: }
1934:
1935:
1936: /* ARGSUSED */
1937: static int
1938: termp_fo_pre(DECL_ARGS)
1939: {
1940: const struct mdoc_node *n;
1941:
1942: if (MDOC_BODY == node->type) {
1.31 schwarze 1943: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1944: term_word(p, "(");
1945: p->flags |= TERMP_NOSPACE;
1946: return(1);
1947: } else if (MDOC_HEAD != node->type)
1948: return(1);
1949:
1950: p->flags |= ttypes[TTYPE_FUNC_NAME];
1951: for (n = node->child; n; n = n->next) {
1.10 schwarze 1952: assert(MDOC_TEXT == n->type);
1.1 kristaps 1953: term_word(p, n->string);
1954: }
1955: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1956:
1957: return(0);
1958: }
1959:
1960:
1961: /* ARGSUSED */
1962: static void
1963: termp_fo_post(DECL_ARGS)
1964: {
1965:
1966: if (MDOC_BODY != node->type)
1967: return;
1968: p->flags |= TERMP_NOSPACE;
1969: term_word(p, ")");
1970: p->flags |= TERMP_NOSPACE;
1971: term_word(p, ";");
1972: term_newln(p);
1973: }
1974:
1975:
1976: /* ARGSUSED */
1977: static int
1978: termp_bf_pre(DECL_ARGS)
1979: {
1980: const struct mdoc_node *n;
1981:
1.27 schwarze 1982: if (MDOC_HEAD == node->type)
1.1 kristaps 1983: return(0);
1.27 schwarze 1984: else if (MDOC_BLOCK != node->type)
1.1 kristaps 1985: return(1);
1986:
1987: if (NULL == (n = node->head->child)) {
1988: if (arg_hasattr(MDOC_Emphasis, node))
1.27 schwarze 1989: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 1990: else if (arg_hasattr(MDOC_Symbolic, node))
1.27 schwarze 1991: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 1992:
1993: return(1);
1994: }
1995:
1.10 schwarze 1996: assert(MDOC_TEXT == n->type);
1.1 kristaps 1997: if (0 == strcmp("Em", n->string))
1.27 schwarze 1998: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 1999: else if (0 == strcmp("Sy", n->string))
1.27 schwarze 2000: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2001:
2002: return(1);
2003: }
2004:
2005:
2006: /* ARGSUSED */
2007: static int
2008: termp_sy_pre(DECL_ARGS)
2009: {
2010:
1.27 schwarze 2011: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2012: return(1);
2013: }
2014:
2015:
2016: /* ARGSUSED */
2017: static int
2018: termp_ms_pre(DECL_ARGS)
2019: {
2020:
1.27 schwarze 2021: pair->flag |= ttypes[TTYPE_SYMBOL];
1.1 kristaps 2022: return(1);
2023: }
2024:
2025:
2026:
2027: /* ARGSUSED */
2028: static int
2029: termp_sm_pre(DECL_ARGS)
2030: {
2031:
1.10 schwarze 2032: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 2033: if (0 == strcmp("on", node->child->string)) {
2034: p->flags &= ~TERMP_NONOSPACE;
2035: p->flags &= ~TERMP_NOSPACE;
2036: } else
2037: p->flags |= TERMP_NONOSPACE;
2038:
2039: return(0);
2040: }
2041:
2042:
2043: /* ARGSUSED */
2044: static int
2045: termp_ap_pre(DECL_ARGS)
2046: {
2047:
2048: p->flags |= TERMP_NOSPACE;
2049: term_word(p, "\\(aq");
2050: p->flags |= TERMP_NOSPACE;
2051: return(1);
2052: }
2053:
2054:
2055: /* ARGSUSED */
2056: static int
2057: termp__j_pre(DECL_ARGS)
2058: {
2059:
1.27 schwarze 2060: pair->flag |= ttypes[TTYPE_REF_JOURNAL];
1.1 kristaps 2061: return(1);
2062: }
2063:
2064:
2065: /* ARGSUSED */
2066: static int
2067: termp__t_pre(DECL_ARGS)
2068: {
2069:
2070: term_word(p, "\"");
2071: p->flags |= TERMP_NOSPACE;
2072: return(1);
2073: }
2074:
2075:
2076: /* ARGSUSED */
2077: static void
2078: termp__t_post(DECL_ARGS)
2079: {
2080:
2081: p->flags |= TERMP_NOSPACE;
2082: term_word(p, "\"");
2083: termp____post(p, pair, meta, node);
2084: }
2085:
2086:
2087: /* ARGSUSED */
2088: static void
2089: termp____post(DECL_ARGS)
2090: {
2091:
2092: p->flags |= TERMP_NOSPACE;
2093: term_word(p, node->next ? "," : ".");
2094: }
2095:
2096:
2097: /* ARGSUSED */
2098: static int
2099: termp_lk_pre(DECL_ARGS)
2100: {
2101: const struct mdoc_node *n;
2102:
1.6 schwarze 2103: assert(node->child);
2104: n = node->child;
2105:
2106: if (NULL == n->next) {
1.27 schwarze 2107: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.6 schwarze 2108: return(1);
2109: }
1.1 kristaps 2110:
2111: p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2112: term_word(p, n->string);
2113: p->flags |= TERMP_NOSPACE;
2114: term_word(p, ":");
1.6 schwarze 2115: p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2116:
2117: p->flags |= ttypes[TTYPE_LINK_TEXT];
1.6 schwarze 2118: for (n = n->next; n; n = n->next)
1.1 kristaps 2119: term_word(p, n->string);
1.6 schwarze 2120:
1.1 kristaps 2121: p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2122: return(0);
2123: }
2124:
2125:
2126: /* ARGSUSED */
2127: static int
2128: termp_mt_pre(DECL_ARGS)
2129: {
2130:
1.27 schwarze 2131: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2132: return(1);
2133: }
2134:
2135: