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