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