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