Annotation of src/usr.bin/mandoc/mdoc_term.c, Revision 1.25
1.25 ! schwarze 1: /* $Id: mdoc_term.c,v 1.24 2009/07/13 00:26:24 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: {
1.25 ! schwarze 1132:
! 1133: if (MDOC_BODY != node->type)
! 1134: return(1);
! 1135:
! 1136: #if defined(__OpenBSD__) || defined(__linux__)
! 1137: term_word(p, "\\(en");
1.24 schwarze 1138: #else
1139: term_word(p, "\\(em");
1140: #endif
1.1 kristaps 1141: return(1);
1142: }
1143:
1144:
1145: /* ARGSUSED */
1146: static void
1147: termp_bl_post(DECL_ARGS)
1148: {
1149:
1150: if (MDOC_BLOCK == node->type)
1151: term_newln(p);
1152: }
1153:
1154:
1155: /* ARGSUSED */
1156: static void
1157: termp_op_post(DECL_ARGS)
1158: {
1159:
1160: if (MDOC_BODY != node->type)
1161: return;
1162: p->flags |= TERMP_NOSPACE;
1163: term_word(p, "\\(rB");
1164: }
1165:
1166:
1167: /* ARGSUSED */
1168: static int
1169: termp_xr_pre(DECL_ARGS)
1170: {
1171: const struct mdoc_node *n;
1172:
1.10 schwarze 1173: assert(node->child && MDOC_TEXT == node->child->type);
1174: n = node->child;
1175:
1.1 kristaps 1176: term_word(p, n->string);
1177: if (NULL == (n = n->next))
1178: return(0);
1179: p->flags |= TERMP_NOSPACE;
1180: term_word(p, "(");
1181: p->flags |= TERMP_NOSPACE;
1182: term_word(p, n->string);
1183: p->flags |= TERMP_NOSPACE;
1184: term_word(p, ")");
1185: return(0);
1186: }
1187:
1188:
1189: /* ARGSUSED */
1190: static int
1191: termp_vt_pre(DECL_ARGS)
1192: {
1193:
1194: /* FIXME: this can be "type name". */
1195: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1196: return(1);
1197: }
1198:
1199:
1200: /* ARGSUSED */
1201: static void
1202: termp_vt_post(DECL_ARGS)
1203: {
1204:
1205: if (node->sec == SEC_SYNOPSIS)
1206: term_vspace(p);
1207: }
1208:
1209:
1210: /* ARGSUSED */
1211: static int
1212: termp_fd_pre(DECL_ARGS)
1213: {
1214:
1215: /*
1216: * FIXME: this naming is bad. This value is used, in general,
1217: * for the #include header or other preprocessor statement.
1218: */
1219: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_DECL]);
1220: return(1);
1221: }
1222:
1223:
1224: /* ARGSUSED */
1225: static void
1226: termp_fd_post(DECL_ARGS)
1227: {
1228:
1229: if (node->sec != SEC_SYNOPSIS)
1230: return;
1231: term_newln(p);
1232: if (node->next && MDOC_Fd != node->next->tok)
1233: term_vspace(p);
1234: }
1235:
1236:
1237: /* ARGSUSED */
1238: static int
1239: termp_sh_pre(DECL_ARGS)
1240: {
1241:
1242: switch (node->type) {
1243: case (MDOC_HEAD):
1244: term_vspace(p);
1245: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SECTION]);
1246: break;
1247: case (MDOC_BODY):
1248: p->offset = INDENT;
1249: break;
1250: default:
1251: break;
1252: }
1253: return(1);
1254: }
1255:
1256:
1257: /* ARGSUSED */
1258: static void
1259: termp_sh_post(DECL_ARGS)
1260: {
1261:
1262: switch (node->type) {
1263: case (MDOC_HEAD):
1264: term_newln(p);
1265: break;
1266: case (MDOC_BODY):
1267: term_newln(p);
1268: p->offset = 0;
1269: break;
1270: default:
1271: break;
1272: }
1273: }
1274:
1275:
1276: /* ARGSUSED */
1277: static int
1278: termp_op_pre(DECL_ARGS)
1279: {
1280:
1281: switch (node->type) {
1282: case (MDOC_BODY):
1283: term_word(p, "\\(lB");
1284: p->flags |= TERMP_NOSPACE;
1285: break;
1286: default:
1287: break;
1288: }
1289: return(1);
1290: }
1291:
1292:
1293: /* ARGSUSED */
1294: static int
1295: termp_bt_pre(DECL_ARGS)
1296: {
1297:
1298: term_word(p, "is currently in beta test.");
1299: return(1);
1300: }
1301:
1302:
1303: /* ARGSUSED */
1304: static int
1305: termp_lb_pre(DECL_ARGS)
1306: {
1307: const char *lb;
1308:
1.10 schwarze 1309: assert(node->child && MDOC_TEXT == node->child->type);
1.14 schwarze 1310: lb = mdoc_a2lib(node->child->string);
1311: if (lb) {
1.1 kristaps 1312: term_word(p, lb);
1313: return(0);
1314: }
1315: term_word(p, "library");
1316: return(1);
1317: }
1318:
1319:
1320: /* ARGSUSED */
1321: static void
1322: termp_lb_post(DECL_ARGS)
1323: {
1324:
1325: term_newln(p);
1326: }
1327:
1328:
1329: /* ARGSUSED */
1330: static int
1331: termp_ud_pre(DECL_ARGS)
1332: {
1333:
1334: term_word(p, "currently under development.");
1335: return(1);
1336: }
1337:
1338:
1339: /* ARGSUSED */
1340: static int
1341: termp_d1_pre(DECL_ARGS)
1342: {
1343:
1344: if (MDOC_BLOCK != node->type)
1345: return(1);
1346: term_newln(p);
1.14 schwarze 1347: pair->offset = INDENT + 1;
1348: p->offset += pair->offset;
1.1 kristaps 1349: return(1);
1350: }
1351:
1352:
1353: /* ARGSUSED */
1354: static void
1355: termp_d1_post(DECL_ARGS)
1356: {
1357:
1358: if (MDOC_BLOCK != node->type)
1359: return;
1360: term_newln(p);
1361: p->offset -= pair->offset;
1362: }
1363:
1364:
1365: /* ARGSUSED */
1366: static int
1367: termp_aq_pre(DECL_ARGS)
1368: {
1369:
1370: if (MDOC_BODY != node->type)
1371: return(1);
1372: term_word(p, "\\(la");
1373: p->flags |= TERMP_NOSPACE;
1374: return(1);
1375: }
1376:
1377:
1378: /* ARGSUSED */
1379: static void
1380: termp_aq_post(DECL_ARGS)
1381: {
1382:
1383: if (MDOC_BODY != node->type)
1384: return;
1385: p->flags |= TERMP_NOSPACE;
1386: term_word(p, "\\(ra");
1387: }
1388:
1389:
1390: /* ARGSUSED */
1391: static int
1392: termp_ft_pre(DECL_ARGS)
1393: {
1394:
1395: if (SEC_SYNOPSIS == node->sec)
1396: if (node->prev && MDOC_Fo == node->prev->tok)
1397: term_vspace(p);
1398: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_TYPE]);
1399: return(1);
1400: }
1401:
1402:
1403: /* ARGSUSED */
1404: static void
1405: termp_ft_post(DECL_ARGS)
1406: {
1407:
1408: if (SEC_SYNOPSIS == node->sec)
1409: term_newln(p);
1410: }
1411:
1412:
1413: /* ARGSUSED */
1414: static int
1415: termp_fn_pre(DECL_ARGS)
1416: {
1417: const struct mdoc_node *n;
1418:
1.10 schwarze 1419: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 1420:
1421: /* FIXME: can be "type funcname" "type varname"... */
1422:
1423: p->flags |= ttypes[TTYPE_FUNC_NAME];
1424: term_word(p, node->child->string);
1425: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1426:
1427: p->flags |= TERMP_NOSPACE;
1428: term_word(p, "(");
1429:
1430: for (n = node->child->next; n; n = n->next) {
1431: p->flags |= ttypes[TTYPE_FUNC_ARG];
1432: term_word(p, n->string);
1433: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1434: if (n->next)
1435: term_word(p, ",");
1436: }
1437:
1438: term_word(p, ")");
1439:
1440: if (SEC_SYNOPSIS == node->sec)
1441: term_word(p, ";");
1442:
1443: return(0);
1444: }
1445:
1446:
1447: /* ARGSUSED */
1448: static void
1449: termp_fn_post(DECL_ARGS)
1450: {
1451:
1452: if (node->sec == SEC_SYNOPSIS && node->next)
1453: term_vspace(p);
1454:
1455: }
1456:
1457:
1458: /* ARGSUSED */
1459: static int
1460: termp_sx_pre(DECL_ARGS)
1461: {
1462:
1463: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK]);
1464: return(1);
1465: }
1466:
1467:
1468: /* ARGSUSED */
1469: static int
1470: termp_fa_pre(DECL_ARGS)
1471: {
1472: struct mdoc_node *n;
1473:
1474: if (node->parent->tok != MDOC_Fo) {
1475: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_ARG]);
1476: return(1);
1477: }
1478:
1479: for (n = node->child; n; n = n->next) {
1480: p->flags |= ttypes[TTYPE_FUNC_ARG];
1481: term_word(p, n->string);
1482: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1483: if (n->next)
1484: term_word(p, ",");
1485: }
1486:
1487: if (node->child && node->next && node->next->tok == MDOC_Fa)
1488: term_word(p, ",");
1489:
1490: return(0);
1491: }
1492:
1493:
1494: /* ARGSUSED */
1495: static int
1496: termp_va_pre(DECL_ARGS)
1497: {
1498:
1499: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1500: return(1);
1501: }
1502:
1503:
1504: /* ARGSUSED */
1505: static int
1506: termp_bd_pre(DECL_ARGS)
1507: {
1508: int i, type, ln;
1509:
1510: /*
1511: * This is fairly tricky due primarily to crappy documentation.
1512: * If -ragged or -filled are specified, the block does nothing
1513: * but change the indentation.
1514: *
1515: * If, on the other hand, -unfilled or -literal are specified,
1516: * then the game changes. Text is printed exactly as entered in
1517: * the display: if a macro line, a newline is appended to the
1518: * line. Blank lines are allowed.
1519: */
1520:
1521: if (MDOC_BLOCK == node->type)
1522: return(fmt_block_vspace(p, node, node));
1523: else if (MDOC_BODY != node->type)
1524: return(1);
1525:
1.10 schwarze 1526: /* FIXME: display type should be mandated by parser. */
1527:
1.1 kristaps 1528: if (NULL == node->parent->args)
1529: errx(1, "missing display type");
1530:
1531: pair->offset = p->offset;
1532:
1533: for (type = -1, i = 0;
1534: i < (int)node->parent->args->argc; i++) {
1535: switch (node->parent->args->argv[i].arg) {
1536: case (MDOC_Ragged):
1537: /* FALLTHROUGH */
1538: case (MDOC_Filled):
1539: /* FALLTHROUGH */
1540: case (MDOC_Unfilled):
1541: /* FALLTHROUGH */
1542: case (MDOC_Literal):
1543: type = node->parent->args->argv[i].arg;
1544: i = (int)node->parent->args->argc;
1545: break;
1546: default:
1547: break;
1548: }
1549: }
1550:
1551: if (NULL == node->parent->args)
1552: errx(1, "missing display type");
1553:
1554: i = arg_getattr(MDOC_Offset, node->parent);
1555: if (-1 != i) {
1556: if (1 != node->parent->args->argv[i].sz)
1557: errx(1, "expected single value");
1558: p->offset += arg_offset(&node->parent->args->argv[i]);
1559: }
1560:
1561: switch (type) {
1562: case (MDOC_Literal):
1563: /* FALLTHROUGH */
1564: case (MDOC_Unfilled):
1565: break;
1566: default:
1567: return(1);
1568: }
1569:
1570: /*
1571: * Tricky. Iterate through all children. If we're on a
1572: * different parse line, append a newline and then the contents.
1573: * Ew.
1574: */
1575:
1576: p->flags |= TERMP_LITERAL;
1577: ln = node->child ? node->child->line : 0;
1578:
1579: for (node = node->child; node; node = node->next) {
1580: if (ln < node->line) {
1581: term_flushln(p);
1582: p->flags |= TERMP_NOSPACE;
1583: }
1584: ln = node->line;
1585: print_node(p, pair, meta, node);
1586: }
1587:
1588: return(0);
1589: }
1590:
1591:
1592: /* ARGSUSED */
1593: static void
1594: termp_bd_post(DECL_ARGS)
1595: {
1596:
1597: if (MDOC_BODY != node->type)
1598: return;
1599:
1600: term_flushln(p);
1601: p->flags &= ~TERMP_LITERAL;
1602: p->offset = pair->offset;
1603: p->flags |= TERMP_NOSPACE;
1604: }
1605:
1606:
1607: /* ARGSUSED */
1608: static int
1609: termp_qq_pre(DECL_ARGS)
1610: {
1611:
1612: if (MDOC_BODY != node->type)
1613: return(1);
1614: term_word(p, "\"");
1615: p->flags |= TERMP_NOSPACE;
1616: return(1);
1617: }
1618:
1619:
1620: /* ARGSUSED */
1621: static void
1622: termp_qq_post(DECL_ARGS)
1623: {
1624:
1625: if (MDOC_BODY != node->type)
1626: return;
1627: p->flags |= TERMP_NOSPACE;
1628: term_word(p, "\"");
1629: }
1630:
1631:
1632: /* ARGSUSED */
1633: static int
1634: termp_bsx_pre(DECL_ARGS)
1635: {
1636:
1637: term_word(p, "BSDI BSD/OS");
1638: return(1);
1639: }
1640:
1641:
1642: /* ARGSUSED */
1643: static void
1644: termp_bx_post(DECL_ARGS)
1645: {
1646:
1647: if (node->child)
1648: p->flags |= TERMP_NOSPACE;
1649: term_word(p, "BSD");
1650: }
1651:
1652:
1653: /* ARGSUSED */
1654: static int
1655: termp_ox_pre(DECL_ARGS)
1656: {
1657:
1658: term_word(p, "OpenBSD");
1659: return(1);
1660: }
1661:
1662:
1663: /* ARGSUSED */
1664: static int
1665: termp_dx_pre(DECL_ARGS)
1666: {
1667:
1668: term_word(p, "DragonFly");
1669: return(1);
1670: }
1671:
1672:
1673: /* ARGSUSED */
1674: static int
1675: termp_ux_pre(DECL_ARGS)
1676: {
1677:
1678: term_word(p, "UNIX");
1679: return(1);
1680: }
1681:
1682:
1683: /* ARGSUSED */
1684: static int
1685: termp_fx_pre(DECL_ARGS)
1686: {
1687:
1688: term_word(p, "FreeBSD");
1689: return(1);
1690: }
1691:
1692:
1693: /* ARGSUSED */
1694: static int
1695: termp_nx_pre(DECL_ARGS)
1696: {
1697:
1698: term_word(p, "NetBSD");
1699: return(1);
1700: }
1701:
1702:
1703: /* ARGSUSED */
1704: static int
1705: termp_sq_pre(DECL_ARGS)
1706: {
1707:
1708: if (MDOC_BODY != node->type)
1709: return(1);
1710: term_word(p, "\\(oq");
1711: p->flags |= TERMP_NOSPACE;
1712: return(1);
1713: }
1714:
1715:
1716: /* ARGSUSED */
1717: static void
1718: termp_sq_post(DECL_ARGS)
1719: {
1720:
1721: if (MDOC_BODY != node->type)
1722: return;
1723: p->flags |= TERMP_NOSPACE;
1724: term_word(p, "\\(aq");
1725: }
1726:
1727:
1728: /* ARGSUSED */
1729: static int
1730: termp_pf_pre(DECL_ARGS)
1731: {
1732:
1733: p->flags |= TERMP_IGNDELIM;
1734: return(1);
1735: }
1736:
1737:
1738: /* ARGSUSED */
1739: static void
1740: termp_pf_post(DECL_ARGS)
1741: {
1742:
1743: p->flags &= ~TERMP_IGNDELIM;
1744: p->flags |= TERMP_NOSPACE;
1745: }
1746:
1747:
1748: /* ARGSUSED */
1749: static int
1750: termp_ss_pre(DECL_ARGS)
1751: {
1752:
1753: switch (node->type) {
1754: case (MDOC_BLOCK):
1755: term_newln(p);
1756: if (node->prev)
1757: term_vspace(p);
1758: break;
1759: case (MDOC_HEAD):
1760: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
1.3 schwarze 1761: p->offset = HALFINDENT;
1.1 kristaps 1762: break;
1763: default:
1764: break;
1765: }
1766:
1767: return(1);
1768: }
1769:
1770:
1771: /* ARGSUSED */
1772: static void
1773: termp_ss_post(DECL_ARGS)
1774: {
1775:
1776: switch (node->type) {
1777: case (MDOC_HEAD):
1778: term_newln(p);
1779: p->offset = INDENT;
1780: break;
1781: default:
1782: break;
1783: }
1784: }
1785:
1786:
1787: /* ARGSUSED */
1788: static int
1789: termp_pa_pre(DECL_ARGS)
1790: {
1791:
1792: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FILE]);
1793: return(1);
1794: }
1795:
1796:
1797: /* ARGSUSED */
1798: static int
1799: termp_em_pre(DECL_ARGS)
1800: {
1801:
1802: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1803: return(1);
1804: }
1805:
1806:
1807: /* ARGSUSED */
1808: static int
1809: termp_cd_pre(DECL_ARGS)
1810: {
1811:
1812: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CONFIG]);
1813: term_newln(p);
1814: return(1);
1815: }
1816:
1817:
1818: /* ARGSUSED */
1819: static int
1820: termp_cm_pre(DECL_ARGS)
1821: {
1822:
1823: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1824: return(1);
1825: }
1826:
1827:
1828: /* ARGSUSED */
1829: static int
1830: termp_ic_pre(DECL_ARGS)
1831: {
1832:
1833: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD]);
1834: return(1);
1835: }
1836:
1837:
1838: /* ARGSUSED */
1839: static int
1840: termp_in_pre(DECL_ARGS)
1841: {
1842:
1.23 schwarze 1843: /* XXX This conforms to new-groff style. */
1.1 kristaps 1844: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_INCLUDE]);
1.23 schwarze 1845:
1846: if (SEC_SYNOPSIS == node->sec)
1847: term_word(p, "#include");
1848:
1.1 kristaps 1849: term_word(p, "<");
1850: p->flags |= TERMP_NOSPACE;
1851: return(1);
1852: }
1853:
1854:
1855: /* ARGSUSED */
1856: static void
1857: termp_in_post(DECL_ARGS)
1858: {
1859:
1860: p->flags |= TERMP_NOSPACE;
1861: term_word(p, ">");
1862:
1863: if (SEC_SYNOPSIS != node->sec)
1864: return;
1.23 schwarze 1865:
1866: term_newln(p);
1867: /*
1868: * XXX Not entirely correct. If `.In foo bar' is specified in
1869: * the SYNOPSIS section, then it produces a single break after
1870: * the <foo>; mandoc asserts a vertical space. Since this
1871: * construction is rarely used, I think it's fine.
1872: */
1.1 kristaps 1873: if (node->next && MDOC_In != node->next->tok)
1874: term_vspace(p);
1875: }
1876:
1877:
1878: /* ARGSUSED */
1879: static int
1880: termp_at_pre(DECL_ARGS)
1881: {
1882: const char *att;
1883:
1884: att = NULL;
1885:
1886: if (node->child)
1887: att = mdoc_a2att(node->child->string);
1888: if (NULL == att)
1889: att = "AT&T UNIX";
1890:
1891: term_word(p, att);
1892: return(0);
1893: }
1894:
1895:
1896: /* ARGSUSED */
1897: static int
1898: termp_brq_pre(DECL_ARGS)
1899: {
1900:
1901: if (MDOC_BODY != node->type)
1902: return(1);
1903: term_word(p, "\\(lC");
1904: p->flags |= TERMP_NOSPACE;
1905: return(1);
1906: }
1907:
1908:
1909: /* ARGSUSED */
1910: static void
1911: termp_brq_post(DECL_ARGS)
1912: {
1913:
1914: if (MDOC_BODY != node->type)
1915: return;
1916: p->flags |= TERMP_NOSPACE;
1917: term_word(p, "\\(rC");
1918: }
1919:
1920:
1921: /* ARGSUSED */
1922: static int
1923: termp_bq_pre(DECL_ARGS)
1924: {
1925:
1926: if (MDOC_BODY != node->type)
1927: return(1);
1928: term_word(p, "\\(lB");
1929: p->flags |= TERMP_NOSPACE;
1930: return(1);
1931: }
1932:
1933:
1934: /* ARGSUSED */
1935: static void
1936: termp_bq_post(DECL_ARGS)
1937: {
1938:
1939: if (MDOC_BODY != node->type)
1940: return;
1941: p->flags |= TERMP_NOSPACE;
1942: term_word(p, "\\(rB");
1943: }
1944:
1945:
1946: /* ARGSUSED */
1947: static int
1948: termp_pq_pre(DECL_ARGS)
1949: {
1950:
1951: if (MDOC_BODY != node->type)
1952: return(1);
1953: term_word(p, "\\&(");
1954: p->flags |= TERMP_NOSPACE;
1955: return(1);
1956: }
1957:
1958:
1959: /* ARGSUSED */
1960: static void
1961: termp_pq_post(DECL_ARGS)
1962: {
1963:
1964: if (MDOC_BODY != node->type)
1965: return;
1966: term_word(p, ")");
1967: }
1968:
1969:
1970: /* ARGSUSED */
1971: static int
1972: termp_fo_pre(DECL_ARGS)
1973: {
1974: const struct mdoc_node *n;
1975:
1976: if (MDOC_BODY == node->type) {
1977: term_word(p, "(");
1978: p->flags |= TERMP_NOSPACE;
1979: return(1);
1980: } else if (MDOC_HEAD != node->type)
1981: return(1);
1982:
1983: /* XXX - groff shows only first parameter */
1984:
1985: p->flags |= ttypes[TTYPE_FUNC_NAME];
1986: for (n = node->child; n; n = n->next) {
1.10 schwarze 1987: assert(MDOC_TEXT == n->type);
1.1 kristaps 1988: term_word(p, n->string);
1989: }
1990: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1991:
1992: return(0);
1993: }
1994:
1995:
1996: /* ARGSUSED */
1997: static void
1998: termp_fo_post(DECL_ARGS)
1999: {
2000:
2001: if (MDOC_BODY != node->type)
2002: return;
2003: p->flags |= TERMP_NOSPACE;
2004: term_word(p, ")");
2005: p->flags |= TERMP_NOSPACE;
2006: term_word(p, ";");
2007: term_newln(p);
2008: }
2009:
2010:
2011: /* ARGSUSED */
2012: static int
2013: termp_bf_pre(DECL_ARGS)
2014: {
2015: const struct mdoc_node *n;
2016:
2017: if (MDOC_HEAD == node->type) {
2018: return(0);
2019: } else if (MDOC_BLOCK != node->type)
2020: return(1);
2021:
2022: if (NULL == (n = node->head->child)) {
2023: if (arg_hasattr(MDOC_Emphasis, node))
2024: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
2025: else if (arg_hasattr(MDOC_Symbolic, node))
2026: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
2027:
2028: return(1);
2029: }
2030:
1.10 schwarze 2031: assert(MDOC_TEXT == n->type);
1.1 kristaps 2032: if (0 == strcmp("Em", n->string))
2033: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
2034: else if (0 == strcmp("Sy", n->string))
2035: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
2036:
2037: return(1);
2038: }
2039:
2040:
2041: /* ARGSUSED */
2042: static int
2043: termp_sy_pre(DECL_ARGS)
2044: {
2045:
2046: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
2047: return(1);
2048: }
2049:
2050:
2051: /* ARGSUSED */
2052: static int
2053: termp_ms_pre(DECL_ARGS)
2054: {
2055:
2056: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMBOL]);
2057: return(1);
2058: }
2059:
2060:
2061:
2062: /* ARGSUSED */
2063: static int
2064: termp_sm_pre(DECL_ARGS)
2065: {
2066:
1.10 schwarze 2067: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 2068: if (0 == strcmp("on", node->child->string)) {
2069: p->flags &= ~TERMP_NONOSPACE;
2070: p->flags &= ~TERMP_NOSPACE;
2071: } else
2072: p->flags |= TERMP_NONOSPACE;
2073:
2074: return(0);
2075: }
2076:
2077:
2078: /* ARGSUSED */
2079: static int
2080: termp_ap_pre(DECL_ARGS)
2081: {
2082:
2083: p->flags |= TERMP_NOSPACE;
2084: term_word(p, "\\(aq");
2085: p->flags |= TERMP_NOSPACE;
2086: return(1);
2087: }
2088:
2089:
2090: /* ARGSUSED */
2091: static int
2092: termp__j_pre(DECL_ARGS)
2093: {
2094:
2095: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_REF_JOURNAL]);
2096: return(1);
2097: }
2098:
2099:
2100: /* ARGSUSED */
2101: static int
2102: termp__t_pre(DECL_ARGS)
2103: {
2104:
2105: term_word(p, "\"");
2106: p->flags |= TERMP_NOSPACE;
2107: return(1);
2108: }
2109:
2110:
2111: /* ARGSUSED */
2112: static void
2113: termp__t_post(DECL_ARGS)
2114: {
2115:
2116: p->flags |= TERMP_NOSPACE;
2117: term_word(p, "\"");
2118: termp____post(p, pair, meta, node);
2119: }
2120:
2121:
2122: /* ARGSUSED */
2123: static void
2124: termp____post(DECL_ARGS)
2125: {
2126:
2127: p->flags |= TERMP_NOSPACE;
2128: term_word(p, node->next ? "," : ".");
2129: }
2130:
2131:
2132: /* ARGSUSED */
2133: static int
2134: termp_lk_pre(DECL_ARGS)
2135: {
2136: const struct mdoc_node *n;
2137:
1.6 schwarze 2138: assert(node->child);
2139: n = node->child;
2140:
2141: if (NULL == n->next) {
2142: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK_ANCHOR]);
2143: return(1);
2144: }
1.1 kristaps 2145:
2146: p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2147: term_word(p, n->string);
2148: p->flags |= TERMP_NOSPACE;
2149: term_word(p, ":");
1.6 schwarze 2150: p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2151:
2152: p->flags |= ttypes[TTYPE_LINK_TEXT];
1.6 schwarze 2153: for (n = n->next; n; n = n->next)
1.1 kristaps 2154: term_word(p, n->string);
1.6 schwarze 2155:
1.1 kristaps 2156: p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2157: return(0);
2158: }
2159:
2160:
2161: /* ARGSUSED */
2162: static int
2163: termp_mt_pre(DECL_ARGS)
2164: {
2165:
2166: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK_ANCHOR]);
2167: return(1);
2168: }
2169:
2170: