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