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