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