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