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