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