Annotation of mandoc/mdoc_term.c, Revision 1.71
1.71 ! kristaps 1: /* $Id: mdoc_term.c,v 1.70 2009/09/16 09:41:24 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.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.71 ! kristaps 496: /* TODO: put into util file. */
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:
523: static int
524: arg_listtype(const struct mdoc_node *n)
525: {
526: int i, len;
527:
528: assert(MDOC_BLOCK == n->type);
529:
530: len = (int)(n->args ? n->args->argc : 0);
531:
532: for (i = 0; i < len; i++)
533: switch (n->args->argv[i].arg) {
534: case (MDOC_Bullet):
535: /* FALLTHROUGH */
536: case (MDOC_Dash):
537: /* FALLTHROUGH */
538: case (MDOC_Enum):
539: /* FALLTHROUGH */
540: case (MDOC_Hyphen):
541: /* FALLTHROUGH */
542: case (MDOC_Tag):
543: /* FALLTHROUGH */
544: case (MDOC_Inset):
545: /* FALLTHROUGH */
546: case (MDOC_Diag):
547: /* FALLTHROUGH */
548: case (MDOC_Item):
549: /* FALLTHROUGH */
550: case (MDOC_Column):
551: /* FALLTHROUGH */
1.38 kristaps 552: case (MDOC_Hang):
553: /* FALLTHROUGH */
1.1 kristaps 554: case (MDOC_Ohang):
555: return(n->args->argv[i].arg);
556: default:
557: break;
558: }
559:
1.38 kristaps 560: return(-1);
1.1 kristaps 561: }
562:
563:
564: static size_t
565: arg_offset(const struct mdoc_argv *arg)
566: {
1.40 kristaps 567: int len, i;
568: const char *p;
1.1 kristaps 569:
570: assert(*arg->value);
1.40 kristaps 571: p = *arg->value;
572:
573: if (0 == strcmp(p, "left"))
1.19 kristaps 574: return(0);
1.40 kristaps 575: if (0 == strcmp(p, "indent"))
1.16 kristaps 576: return(INDENT + 1);
1.40 kristaps 577: if (0 == strcmp(p, "indent-two"))
1.20 kristaps 578: return((INDENT + 1) * 2);
1.10 kristaps 579:
1.40 kristaps 580: if (0 == (len = (int)strlen(p)))
581: return(0);
582:
583: for (i = 0; i < len - 1; i++)
584: if ( ! isdigit((u_char)p[i]))
585: break;
586:
587: if (i == len - 1)
588: if ('n' == p[len - 1] || 'm' == p[len - 1])
589: return((size_t)atoi(p));
1.11 kristaps 590:
1.40 kristaps 591: return((size_t)len);
1.1 kristaps 592: }
593:
594:
595: static int
596: arg_hasattr(int arg, const struct mdoc_node *n)
597: {
598:
599: return(-1 != arg_getattr(arg, n));
600: }
601:
602:
603: static int
604: arg_getattr(int v, const struct mdoc_node *n)
605: {
606: int val;
607:
608: return(arg_getattrs(&v, &val, 1, n) ? val : -1);
609: }
610:
611:
612: static int
613: arg_getattrs(const int *keys, int *vals,
614: size_t sz, const struct mdoc_node *n)
615: {
616: int i, j, k;
617:
618: if (NULL == n->args)
619: return(0);
620:
621: for (k = i = 0; i < (int)n->args->argc; i++)
622: for (j = 0; j < (int)sz; j++)
623: if (n->args->argv[i].arg == keys[j]) {
624: vals[j] = i;
625: k++;
626: }
627: return(k);
628: }
629:
630:
631: /* ARGSUSED */
1.54 kristaps 632: static void
1.1 kristaps 633: fmt_block_vspace(struct termp *p,
634: const struct mdoc_node *bl,
635: const struct mdoc_node *node)
636: {
637: const struct mdoc_node *n;
638:
639: term_newln(p);
640:
1.59 kristaps 641: if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Compact, bl))
1.54 kristaps 642: return;
1.63 kristaps 643: assert(node);
1.54 kristaps 644:
645: /*
646: * Search through our prior nodes. If we follow a `Ss' or `Sh',
647: * then don't vspace.
648: */
1.1 kristaps 649:
650: for (n = node; n; n = n->parent) {
651: if (MDOC_BLOCK != n->type)
652: continue;
653: if (MDOC_Ss == n->tok)
1.54 kristaps 654: return;
1.1 kristaps 655: if (MDOC_Sh == n->tok)
1.54 kristaps 656: return;
1.1 kristaps 657: if (NULL == n->prev)
658: continue;
659: break;
660: }
661:
1.54 kristaps 662: /*
1.55 kristaps 663: * XXX - not documented: a `-column' does not ever assert vspace
664: * within the list.
1.54 kristaps 665: */
666:
1.59 kristaps 667: if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Column, bl))
1.54 kristaps 668: if (node->prev && MDOC_It == node->prev->tok)
669: return;
670:
1.55 kristaps 671: /*
672: * XXX - not documented: a `-diag' without a body does not
673: * assert a vspace prior to the next element.
674: */
1.59 kristaps 675: if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Diag, bl))
1.55 kristaps 676: if (node->prev && MDOC_It == node->prev->tok) {
677: assert(node->prev->body);
678: if (NULL == node->prev->body->child)
679: return;
680: }
681:
1.54 kristaps 682: term_vspace(p);
1.1 kristaps 683: }
684:
685:
686: /* ARGSUSED */
687: static int
688: termp_dq_pre(DECL_ARGS)
689: {
690:
691: if (MDOC_BODY != node->type)
692: return(1);
693:
694: term_word(p, "\\(lq");
695: p->flags |= TERMP_NOSPACE;
696: return(1);
697: }
698:
699:
700: /* ARGSUSED */
701: static void
702: termp_dq_post(DECL_ARGS)
703: {
704:
705: if (MDOC_BODY != node->type)
706: return;
707:
708: p->flags |= TERMP_NOSPACE;
709: term_word(p, "\\(rq");
710: }
711:
712:
713: /* ARGSUSED */
714: static int
715: termp_it_pre(DECL_ARGS)
716: {
717: const struct mdoc_node *bl, *n;
718: char buf[7];
1.39 kristaps 719: int i, type, keys[3], vals[3];
1.1 kristaps 720: size_t width, offset;
721:
1.54 kristaps 722: if (MDOC_BLOCK == node->type) {
723: fmt_block_vspace(p, node->parent->parent, node);
724: return(1);
725: }
1.1 kristaps 726:
727: bl = node->parent->parent->parent;
728:
729: /* Save parent attributes. */
730:
731: pair->flag = p->flags;
732:
733: /* Get list width and offset. */
734:
735: keys[0] = MDOC_Width;
736: keys[1] = MDOC_Offset;
737: keys[2] = MDOC_Column;
738:
739: vals[0] = vals[1] = vals[2] = -1;
740:
741: width = offset = 0;
742:
743: (void)arg_getattrs(keys, vals, 3, bl);
744:
745: type = arg_listtype(bl);
1.38 kristaps 746: assert(-1 != type);
1.1 kristaps 747:
748: /* Calculate real width and offset. */
749:
750: switch (type) {
751: case (MDOC_Column):
752: if (MDOC_BODY == node->type)
753: break;
1.58 kristaps 754: /*
755: * Work around groff's column handling. The offset is
756: * equal to the sum of all widths leading to the current
757: * column (plus the -offset value). If this column
758: * exceeds the stated number of columns, the width is
759: * set as 0, else it's the stated column width (later
760: * the 0 will be adjusted to default 10 or, if in the
761: * last column case, set to stretch to the margin).
762: */
763: for (i = 0, n = node->prev; n && n &&
764: i < (int)bl->args[vals[2]].argv->sz;
765: n = n->prev, i++)
1.1 kristaps 766: offset += arg_width
767: (&bl->args->argv[vals[2]], i);
1.58 kristaps 768:
769: /* Whether exceeds maximum column. */
770: if (i < (int)bl->args[vals[2]].argv->sz)
771: width = arg_width(&bl->args->argv[vals[2]], i);
772: else
773: width = 0;
774:
1.1 kristaps 775: if (vals[1] >= 0)
776: offset += arg_offset(&bl->args->argv[vals[1]]);
777: break;
778: default:
779: if (vals[0] >= 0)
780: width = arg_width(&bl->args->argv[vals[0]], 0);
781: if (vals[1] >= 0)
1.19 kristaps 782: offset += arg_offset(&bl->args->argv[vals[1]]);
1.1 kristaps 783: break;
784: }
785:
786: /*
787: * List-type can override the width in the case of fixed-head
788: * values (bullet, dash/hyphen, enum). Tags need a non-zero
1.67 kristaps 789: * offset.
1.1 kristaps 790: */
791:
792: switch (type) {
793: case (MDOC_Bullet):
794: /* FALLTHROUGH */
795: case (MDOC_Dash):
796: /* FALLTHROUGH */
797: case (MDOC_Hyphen):
798: if (width < 4)
799: width = 4;
800: break;
1.17 kristaps 801: case (MDOC_Enum):
802: if (width < 5)
803: width = 5;
804: break;
1.38 kristaps 805: case (MDOC_Hang):
1.39 kristaps 806: if (0 == width)
807: width = 8;
808: break;
1.49 kristaps 809: case (MDOC_Column):
810: /* FALLTHROUGH */
1.1 kristaps 811: case (MDOC_Tag):
812: if (0 == width)
813: width = 10;
814: break;
815: default:
816: break;
817: }
818:
819: /*
1.17 kristaps 820: * Whitespace control. Inset bodies need an initial space,
821: * while diagonal bodies need two.
1.1 kristaps 822: */
823:
1.51 kristaps 824: p->flags |= TERMP_NOSPACE;
825:
1.1 kristaps 826: switch (type) {
1.51 kristaps 827: case (MDOC_Diag):
1.62 kristaps 828: if (MDOC_BODY == node->type)
829: term_word(p, "\\ \\ ");
1.51 kristaps 830: break;
1.1 kristaps 831: case (MDOC_Inset):
832: if (MDOC_BODY == node->type)
1.51 kristaps 833: term_word(p, "\\ ");
1.1 kristaps 834: break;
835: default:
836: break;
837: }
838:
1.51 kristaps 839: p->flags |= TERMP_NOSPACE;
840:
1.1 kristaps 841: switch (type) {
842: case (MDOC_Diag):
843: if (MDOC_HEAD == node->type)
1.69 kristaps 844: p->bold++;
1.1 kristaps 845: break;
846: default:
847: break;
848: }
849:
850: /*
851: * Pad and break control. This is the tricker part. Lists with
852: * set right-margins for the head get TERMP_NOBREAK because, if
853: * they overrun the margin, they wrap to the new margin.
854: * Correspondingly, the body for these types don't left-pad, as
855: * the head will pad out to to the right.
856: */
857:
858: switch (type) {
859: case (MDOC_Bullet):
860: /* FALLTHROUGH */
861: case (MDOC_Dash):
862: /* FALLTHROUGH */
863: case (MDOC_Enum):
864: /* FALLTHROUGH */
865: case (MDOC_Hyphen):
1.39 kristaps 866: if (MDOC_HEAD == node->type)
867: p->flags |= TERMP_NOBREAK;
868: else
869: p->flags |= TERMP_NOLPAD;
870: break;
1.38 kristaps 871: case (MDOC_Hang):
1.39 kristaps 872: if (MDOC_HEAD == node->type)
873: p->flags |= TERMP_NOBREAK;
874: else
875: p->flags |= TERMP_NOLPAD;
876:
1.59 kristaps 877: if (MDOC_HEAD != node->type)
878: break;
879:
880: /*
881: * This is ugly. If `-hang' is specified and the body
882: * is a `Bl' or `Bd', then we want basically to nullify
883: * the "overstep" effect in term_flushln() and treat
884: * this as a `-ohang' list instead.
885: */
886: if (node->next->child &&
887: (MDOC_Bl == node->next->child->tok ||
888: MDOC_Bd == node->next->child->tok)) {
889: p->flags &= ~TERMP_NOBREAK;
890: p->flags &= ~TERMP_NOLPAD;
891: } else
1.39 kristaps 892: p->flags |= TERMP_HANG;
893: break;
1.1 kristaps 894: case (MDOC_Tag):
895: if (MDOC_HEAD == node->type)
1.51 kristaps 896: p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
1.1 kristaps 897: else
898: p->flags |= TERMP_NOLPAD;
1.38 kristaps 899:
1.39 kristaps 900: if (MDOC_HEAD != node->type)
901: break;
902: if (NULL == node->next || NULL == node->next->child)
903: p->flags |= TERMP_DANGLE;
1.1 kristaps 904: break;
905: case (MDOC_Column):
906: if (MDOC_HEAD == node->type) {
907: assert(node->next);
908: if (MDOC_BODY == node->next->type)
909: p->flags &= ~TERMP_NOBREAK;
910: else
911: p->flags |= TERMP_NOBREAK;
912: if (node->prev)
913: p->flags |= TERMP_NOLPAD;
914: }
915: break;
916: case (MDOC_Diag):
917: if (MDOC_HEAD == node->type)
918: p->flags |= TERMP_NOBREAK;
919: break;
920: default:
921: break;
922: }
923:
924: /*
925: * Margin control. Set-head-width lists have their right
926: * margins shortened. The body for these lists has the offset
927: * necessarily lengthened. Everybody gets the offset.
928: */
929:
930: p->offset += offset;
931:
932: switch (type) {
1.59 kristaps 933: case (MDOC_Hang):
934: /*
935: * Same stipulation as above, regarding `-hang'. We
936: * don't want to recalculate rmargin and offsets when
937: * using `Bd' or `Bl' within `-hang' overstep lists.
938: */
939: if (MDOC_HEAD == node->type && node->next->child &&
940: (MDOC_Bl == node->next->child->tok ||
941: MDOC_Bd == node->next->child->tok))
942: break;
943: /* FALLTHROUGH */
1.1 kristaps 944: case (MDOC_Bullet):
945: /* FALLTHROUGH */
946: case (MDOC_Dash):
947: /* FALLTHROUGH */
948: case (MDOC_Enum):
949: /* FALLTHROUGH */
950: case (MDOC_Hyphen):
951: /* FALLTHROUGH */
952: case (MDOC_Tag):
1.49 kristaps 953: assert(width);
1.1 kristaps 954: if (MDOC_HEAD == node->type)
955: p->rmargin = p->offset + width;
956: else
957: p->offset += width;
958: break;
959: case (MDOC_Column):
1.49 kristaps 960: assert(width);
1.1 kristaps 961: p->rmargin = p->offset + width;
1.50 kristaps 962: /*
963: * XXX - this behaviour is not documented: the
964: * right-most column is filled to the right margin.
965: */
966: if (MDOC_HEAD == node->type &&
967: MDOC_BODY == node->next->type)
968: p->rmargin = p->maxrmargin;
1.1 kristaps 969: break;
970: default:
971: break;
972: }
973:
974: /*
975: * The dash, hyphen, bullet and enum lists all have a special
1.15 kristaps 976: * HEAD character (temporarily bold, in some cases).
1.1 kristaps 977: */
978:
979: if (MDOC_HEAD == node->type)
980: switch (type) {
981: case (MDOC_Bullet):
1.69 kristaps 982: p->bold++;
1.1 kristaps 983: term_word(p, "\\[bu]");
1.69 kristaps 984: p->bold--;
1.1 kristaps 985: break;
986: case (MDOC_Dash):
987: /* FALLTHROUGH */
988: case (MDOC_Hyphen):
1.69 kristaps 989: p->bold++;
1.27 kristaps 990: term_word(p, "\\(hy");
1.69 kristaps 991: p->bold--;
1.1 kristaps 992: break;
993: case (MDOC_Enum):
994: (pair->ppair->ppair->count)++;
995: (void)snprintf(buf, sizeof(buf), "%d.",
996: pair->ppair->ppair->count);
997: term_word(p, buf);
998: break;
999: default:
1000: break;
1001: }
1002:
1003: /*
1004: * If we're not going to process our children, indicate so here.
1005: */
1006:
1007: switch (type) {
1008: case (MDOC_Bullet):
1009: /* FALLTHROUGH */
1010: case (MDOC_Item):
1011: /* FALLTHROUGH */
1012: case (MDOC_Dash):
1013: /* FALLTHROUGH */
1014: case (MDOC_Hyphen):
1015: /* FALLTHROUGH */
1016: case (MDOC_Enum):
1017: if (MDOC_HEAD == node->type)
1018: return(0);
1019: break;
1020: case (MDOC_Column):
1021: if (MDOC_BODY == node->type)
1022: return(0);
1023: break;
1024: default:
1025: break;
1026: }
1027:
1028: return(1);
1029: }
1030:
1031:
1032: /* ARGSUSED */
1033: static void
1034: termp_it_post(DECL_ARGS)
1035: {
1036: int type;
1037:
1038: if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
1039: return;
1040:
1041: type = arg_listtype(node->parent->parent->parent);
1.38 kristaps 1042: assert(-1 != type);
1.1 kristaps 1043:
1044: switch (type) {
1045: case (MDOC_Item):
1046: /* FALLTHROUGH */
1.53 kristaps 1047: case (MDOC_Diag):
1048: /* FALLTHROUGH */
1.1 kristaps 1049: case (MDOC_Inset):
1050: if (MDOC_BODY == node->type)
1051: term_flushln(p);
1052: break;
1053: case (MDOC_Column):
1054: if (MDOC_HEAD == node->type)
1055: term_flushln(p);
1056: break;
1057: default:
1058: term_flushln(p);
1059: break;
1060: }
1061:
1062: p->flags = pair->flag;
1063: }
1064:
1065:
1066: /* ARGSUSED */
1067: static int
1068: termp_nm_pre(DECL_ARGS)
1069: {
1070:
1071: if (SEC_SYNOPSIS == node->sec)
1072: term_newln(p);
1.69 kristaps 1073: p->bold++;
1.1 kristaps 1074: if (NULL == node->child)
1075: term_word(p, meta->name);
1076: return(1);
1077: }
1078:
1079:
1080: /* ARGSUSED */
1081: static int
1082: termp_fl_pre(DECL_ARGS)
1083: {
1084:
1.69 kristaps 1085: p->bold++;
1.1 kristaps 1086: term_word(p, "\\-");
1087: p->flags |= TERMP_NOSPACE;
1088: return(1);
1089: }
1090:
1091:
1092: /* ARGSUSED */
1093: static int
1.61 kristaps 1094: termp_an_pre(DECL_ARGS)
1095: {
1096:
1097: if (NULL == node->child)
1098: return(1);
1099:
1100: /*
1101: * XXX: this is poorly documented. If not in the AUTHORS
1102: * section, `An -split' will cause newlines to occur before the
1103: * author name. If in the AUTHORS section, by default, the
1104: * first `An' invocation is nosplit, then all subsequent ones,
1105: * regardless of whether interspersed with other macros/text,
1106: * are split. -split, in this case, will override the condition
1107: * of the implied first -nosplit.
1108: */
1109:
1110: if (node->sec == SEC_AUTHORS) {
1111: if ( ! (TERMP_ANPREC & p->flags)) {
1112: if (TERMP_SPLIT & p->flags)
1113: term_newln(p);
1114: return(1);
1115: }
1116: if (TERMP_NOSPLIT & p->flags)
1117: return(1);
1118: term_newln(p);
1119: return(1);
1120: }
1121:
1122: if (TERMP_SPLIT & p->flags)
1123: term_newln(p);
1124:
1125: return(1);
1126: }
1127:
1128:
1129: /* ARGSUSED */
1130: static void
1131: termp_an_post(DECL_ARGS)
1132: {
1133:
1134: if (node->child) {
1135: if (SEC_AUTHORS == node->sec)
1136: p->flags |= TERMP_ANPREC;
1137: return;
1138: }
1139:
1140: if (arg_getattr(MDOC_Split, node) > -1) {
1141: p->flags &= ~TERMP_NOSPLIT;
1142: p->flags |= TERMP_SPLIT;
1143: } else {
1144: p->flags &= ~TERMP_SPLIT;
1145: p->flags |= TERMP_NOSPLIT;
1146: }
1147:
1148: }
1149:
1150:
1151: /* ARGSUSED */
1152: static int
1.1 kristaps 1153: termp_ns_pre(DECL_ARGS)
1154: {
1155:
1156: p->flags |= TERMP_NOSPACE;
1157: return(1);
1158: }
1159:
1160:
1161: /* ARGSUSED */
1162: static int
1163: termp_pp_pre(DECL_ARGS)
1164: {
1165:
1166: term_vspace(p);
1167: return(1);
1168: }
1169:
1170:
1171: /* ARGSUSED */
1172: static int
1173: termp_rs_pre(DECL_ARGS)
1174: {
1175:
1176: if (MDOC_BLOCK == node->type && node->prev)
1177: term_vspace(p);
1178: return(1);
1179: }
1180:
1181:
1182: /* ARGSUSED */
1183: static int
1184: termp_rv_pre(DECL_ARGS)
1185: {
1.68 kristaps 1186: const struct mdoc_node *nn;
1.1 kristaps 1187:
1188: term_newln(p);
1189: term_word(p, "The");
1190:
1.68 kristaps 1191: nn = node->child;
1192: assert(nn);
1193: for ( ; nn; nn = nn->next) {
1.69 kristaps 1194: p->bold++;
1.68 kristaps 1195: term_word(p, nn->string);
1.69 kristaps 1196: p->bold--;
1.68 kristaps 1197: p->flags |= TERMP_NOSPACE;
1198: if (nn->next && NULL == nn->next->next)
1199: term_word(p, "(), and");
1200: else if (nn->next)
1201: term_word(p, "(),");
1202: else
1203: term_word(p, "()");
1204: }
1205:
1206: if (node->child->next)
1207: term_word(p, "functions return");
1208: else
1209: term_word(p, "function returns");
1.1 kristaps 1210:
1.68 kristaps 1211: term_word(p, "the value 0 if successful; otherwise the value "
1212: "-1 is returned and the global variable");
1.1 kristaps 1213:
1.69 kristaps 1214: p->under++;
1.1 kristaps 1215: term_word(p, "errno");
1.69 kristaps 1216: p->under--;
1.1 kristaps 1217:
1218: term_word(p, "is set to indicate the error.");
1219:
1.68 kristaps 1220: return(0);
1.1 kristaps 1221: }
1222:
1223:
1224: /* ARGSUSED */
1225: static int
1226: termp_ex_pre(DECL_ARGS)
1227: {
1.68 kristaps 1228: const struct mdoc_node *nn;
1.1 kristaps 1229:
1.68 kristaps 1230: term_word(p, "The");
1.1 kristaps 1231:
1.68 kristaps 1232: nn = node->child;
1233: assert(nn);
1234: for ( ; nn; nn = nn->next) {
1.69 kristaps 1235: p->bold++;
1.68 kristaps 1236: term_word(p, nn->string);
1.69 kristaps 1237: p->bold--;
1.68 kristaps 1238: p->flags |= TERMP_NOSPACE;
1239: if (nn->next && NULL == nn->next->next)
1240: term_word(p, ", and");
1241: else if (nn->next)
1242: term_word(p, ",");
1243: else
1244: p->flags &= ~TERMP_NOSPACE;
1245: }
1246:
1247: if (node->child->next)
1248: term_word(p, "utilities exit");
1249: else
1250: term_word(p, "utility exits");
1251:
1252: term_word(p, "0 on success, and >0 if an error occurs.");
1.1 kristaps 1253:
1.68 kristaps 1254: return(0);
1.1 kristaps 1255: }
1256:
1257:
1258: /* ARGSUSED */
1259: static int
1260: termp_nd_pre(DECL_ARGS)
1261: {
1.25 kristaps 1262:
1263: if (MDOC_BODY != node->type)
1264: return(1);
1.30 kristaps 1265:
1.27 kristaps 1266: #if defined(__OpenBSD__) || defined(__linux__)
1.41 kristaps 1267: term_word(p, "\\(en");
1.23 kristaps 1268: #else
1269: term_word(p, "\\(em");
1270: #endif
1.1 kristaps 1271: return(1);
1272: }
1273:
1274:
1275: /* ARGSUSED */
1276: static void
1277: termp_bl_post(DECL_ARGS)
1278: {
1279:
1280: if (MDOC_BLOCK == node->type)
1281: term_newln(p);
1282: }
1283:
1284:
1285: /* ARGSUSED */
1286: static void
1287: termp_op_post(DECL_ARGS)
1288: {
1289:
1290: if (MDOC_BODY != node->type)
1291: return;
1292: p->flags |= TERMP_NOSPACE;
1293: term_word(p, "\\(rB");
1294: }
1295:
1296:
1297: /* ARGSUSED */
1298: static int
1299: termp_xr_pre(DECL_ARGS)
1300: {
1301: const struct mdoc_node *n;
1302:
1.11 kristaps 1303: assert(node->child && MDOC_TEXT == node->child->type);
1304: n = node->child;
1305:
1.1 kristaps 1306: term_word(p, n->string);
1307: if (NULL == (n = n->next))
1308: return(0);
1309: p->flags |= TERMP_NOSPACE;
1310: term_word(p, "(");
1311: p->flags |= TERMP_NOSPACE;
1312: term_word(p, n->string);
1313: p->flags |= TERMP_NOSPACE;
1314: term_word(p, ")");
1315: return(0);
1316: }
1317:
1318:
1319: /* ARGSUSED */
1320: static void
1321: termp_vt_post(DECL_ARGS)
1322: {
1323:
1.32 kristaps 1324: if (node->sec != SEC_SYNOPSIS)
1325: return;
1326: if (node->next && MDOC_Vt == node->next->tok)
1327: term_newln(p);
1328: else if (node->next)
1.1 kristaps 1329: term_vspace(p);
1330: }
1331:
1332:
1333: /* ARGSUSED */
1334: static int
1.69 kristaps 1335: termp_bold_pre(DECL_ARGS)
1.1 kristaps 1336: {
1337:
1.69 kristaps 1338: p->bold++;
1.1 kristaps 1339: return(1);
1340: }
1341:
1342:
1343: /* ARGSUSED */
1344: static void
1345: termp_fd_post(DECL_ARGS)
1346: {
1347:
1348: if (node->sec != SEC_SYNOPSIS)
1349: return;
1.29 kristaps 1350:
1.1 kristaps 1351: term_newln(p);
1352: if (node->next && MDOC_Fd != node->next->tok)
1353: term_vspace(p);
1354: }
1355:
1356:
1357: /* ARGSUSED */
1358: static int
1359: termp_sh_pre(DECL_ARGS)
1360: {
1.64 kristaps 1361: /*
1.65 kristaps 1362: * XXX: undocumented: using two `Sh' macros in sequence has no
1363: * vspace between calls, only a newline.
1.64 kristaps 1364: */
1.1 kristaps 1365: switch (node->type) {
1.65 kristaps 1366: case (MDOC_BLOCK):
1367: if (node->prev && MDOC_Sh == node->prev->tok)
1368: if (NULL == node->prev->body->child)
1369: break;
1370: term_vspace(p);
1371: break;
1.1 kristaps 1372: case (MDOC_HEAD):
1.69 kristaps 1373: p->bold++;
1.1 kristaps 1374: break;
1375: case (MDOC_BODY):
1376: p->offset = INDENT;
1377: break;
1378: default:
1379: break;
1380: }
1381: return(1);
1382: }
1383:
1384:
1385: /* ARGSUSED */
1386: static void
1387: termp_sh_post(DECL_ARGS)
1388: {
1389:
1390: switch (node->type) {
1391: case (MDOC_HEAD):
1392: term_newln(p);
1393: break;
1394: case (MDOC_BODY):
1395: term_newln(p);
1396: p->offset = 0;
1397: break;
1398: default:
1399: break;
1400: }
1401: }
1402:
1403:
1404: /* ARGSUSED */
1405: static int
1406: termp_op_pre(DECL_ARGS)
1407: {
1408:
1409: switch (node->type) {
1410: case (MDOC_BODY):
1411: term_word(p, "\\(lB");
1412: p->flags |= TERMP_NOSPACE;
1413: break;
1414: default:
1415: break;
1416: }
1417: return(1);
1418: }
1419:
1420:
1421: /* ARGSUSED */
1422: static int
1423: termp_bt_pre(DECL_ARGS)
1424: {
1425:
1426: term_word(p, "is currently in beta test.");
1427: return(1);
1428: }
1429:
1430:
1431: /* ARGSUSED */
1432: static void
1433: termp_lb_post(DECL_ARGS)
1434: {
1435:
1436: term_newln(p);
1437: }
1438:
1439:
1440: /* ARGSUSED */
1441: static int
1442: termp_ud_pre(DECL_ARGS)
1443: {
1444:
1445: term_word(p, "currently under development.");
1446: return(1);
1447: }
1448:
1449:
1450: /* ARGSUSED */
1451: static int
1452: termp_d1_pre(DECL_ARGS)
1453: {
1454:
1.4 kristaps 1455: if (MDOC_BLOCK != node->type)
1.1 kristaps 1456: return(1);
1457: term_newln(p);
1.29 kristaps 1458: p->offset += (INDENT + 1);
1.1 kristaps 1459: return(1);
1460: }
1461:
1462:
1463: /* ARGSUSED */
1464: static void
1465: termp_d1_post(DECL_ARGS)
1466: {
1467:
1.4 kristaps 1468: if (MDOC_BLOCK != node->type)
1.1 kristaps 1469: return;
1470: term_newln(p);
1471: }
1472:
1473:
1474: /* ARGSUSED */
1475: static int
1476: termp_aq_pre(DECL_ARGS)
1477: {
1478:
1479: if (MDOC_BODY != node->type)
1480: return(1);
1481: term_word(p, "\\(la");
1482: p->flags |= TERMP_NOSPACE;
1483: return(1);
1484: }
1485:
1486:
1487: /* ARGSUSED */
1488: static void
1489: termp_aq_post(DECL_ARGS)
1490: {
1491:
1492: if (MDOC_BODY != node->type)
1493: return;
1494: p->flags |= TERMP_NOSPACE;
1495: term_word(p, "\\(ra");
1496: }
1497:
1498:
1499: /* ARGSUSED */
1500: static int
1501: termp_ft_pre(DECL_ARGS)
1502: {
1503:
1504: if (SEC_SYNOPSIS == node->sec)
1505: if (node->prev && MDOC_Fo == node->prev->tok)
1506: term_vspace(p);
1.69 kristaps 1507: p->under++;
1.1 kristaps 1508: return(1);
1509: }
1510:
1511:
1512: /* ARGSUSED */
1513: static void
1514: termp_ft_post(DECL_ARGS)
1515: {
1516:
1517: if (SEC_SYNOPSIS == node->sec)
1518: term_newln(p);
1519: }
1520:
1521:
1522: /* ARGSUSED */
1523: static int
1524: termp_fn_pre(DECL_ARGS)
1525: {
1526: const struct mdoc_node *n;
1527:
1.11 kristaps 1528: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 1529:
1530: /* FIXME: can be "type funcname" "type varname"... */
1531:
1.69 kristaps 1532: p->bold++;
1.1 kristaps 1533: term_word(p, node->child->string);
1.69 kristaps 1534: p->bold--;
1.1 kristaps 1535:
1536: p->flags |= TERMP_NOSPACE;
1537: term_word(p, "(");
1538:
1539: for (n = node->child->next; n; n = n->next) {
1.69 kristaps 1540: p->under++;
1.1 kristaps 1541: term_word(p, n->string);
1.69 kristaps 1542: p->under--;
1.1 kristaps 1543: if (n->next)
1544: term_word(p, ",");
1545: }
1546:
1547: term_word(p, ")");
1548:
1549: if (SEC_SYNOPSIS == node->sec)
1550: term_word(p, ";");
1551:
1552: return(0);
1553: }
1554:
1555:
1556: /* ARGSUSED */
1557: static void
1558: termp_fn_post(DECL_ARGS)
1559: {
1560:
1561: if (node->sec == SEC_SYNOPSIS && node->next)
1562: term_vspace(p);
1563: }
1564:
1565:
1566: /* ARGSUSED */
1567: static int
1568: termp_fa_pre(DECL_ARGS)
1569: {
1570: struct mdoc_node *n;
1571:
1572: if (node->parent->tok != MDOC_Fo) {
1.69 kristaps 1573: p->under++;
1.1 kristaps 1574: return(1);
1575: }
1576:
1577: for (n = node->child; n; n = n->next) {
1.69 kristaps 1578: p->under++;
1.1 kristaps 1579: term_word(p, n->string);
1.69 kristaps 1580: p->under--;
1.1 kristaps 1581: if (n->next)
1582: term_word(p, ",");
1583: }
1584:
1585: if (node->child && node->next && node->next->tok == MDOC_Fa)
1586: term_word(p, ",");
1587:
1588: return(0);
1589: }
1590:
1591:
1592: /* ARGSUSED */
1593: static int
1594: termp_bd_pre(DECL_ARGS)
1595: {
1.60 kristaps 1596: int i, type;
1.1 kristaps 1597:
1598: /*
1599: * This is fairly tricky due primarily to crappy documentation.
1600: * If -ragged or -filled are specified, the block does nothing
1601: * but change the indentation.
1602: *
1603: * If, on the other hand, -unfilled or -literal are specified,
1604: * then the game changes. Text is printed exactly as entered in
1605: * the display: if a macro line, a newline is appended to the
1606: * line. Blank lines are allowed.
1607: */
1608:
1.54 kristaps 1609: if (MDOC_BLOCK == node->type) {
1610: fmt_block_vspace(p, node, node);
1611: return(1);
1612: } else if (MDOC_BODY != node->type)
1.1 kristaps 1613: return(1);
1614:
1.60 kristaps 1615: assert(node->parent->args);
1.11 kristaps 1616:
1.60 kristaps 1617: for (type = -1, i = 0; -1 == type &&
1.1 kristaps 1618: i < (int)node->parent->args->argc; i++) {
1619: switch (node->parent->args->argv[i].arg) {
1620: case (MDOC_Ragged):
1621: /* FALLTHROUGH */
1622: case (MDOC_Filled):
1623: /* FALLTHROUGH */
1624: case (MDOC_Unfilled):
1625: /* FALLTHROUGH */
1626: case (MDOC_Literal):
1627: type = node->parent->args->argv[i].arg;
1628: break;
1629: default:
1630: break;
1631: }
1632: }
1.60 kristaps 1633:
1634: assert(type > -1);
1.1 kristaps 1635:
1636: i = arg_getattr(MDOC_Offset, node->parent);
1.60 kristaps 1637: if (-1 != i)
1.1 kristaps 1638: p->offset += arg_offset(&node->parent->args->argv[i]);
1639:
1640: switch (type) {
1641: case (MDOC_Literal):
1642: /* FALLTHROUGH */
1643: case (MDOC_Unfilled):
1644: break;
1645: default:
1646: return(1);
1647: }
1648:
1649: for (node = node->child; node; node = node->next) {
1.60 kristaps 1650: p->flags |= TERMP_NOSPACE;
1651: print_node(p, pair, meta, node);
1652: if (node->next)
1.1 kristaps 1653: term_flushln(p);
1654: }
1655:
1656: return(0);
1657: }
1658:
1659:
1660: /* ARGSUSED */
1661: static void
1662: termp_bd_post(DECL_ARGS)
1663: {
1664:
1665: if (MDOC_BODY != node->type)
1666: return;
1.60 kristaps 1667: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1668: term_flushln(p);
1669: }
1670:
1671:
1672: /* ARGSUSED */
1673: static int
1674: termp_qq_pre(DECL_ARGS)
1675: {
1676:
1677: if (MDOC_BODY != node->type)
1678: return(1);
1679: term_word(p, "\"");
1680: p->flags |= TERMP_NOSPACE;
1681: return(1);
1682: }
1683:
1684:
1685: /* ARGSUSED */
1686: static void
1687: termp_qq_post(DECL_ARGS)
1688: {
1689:
1690: if (MDOC_BODY != node->type)
1691: return;
1692: p->flags |= TERMP_NOSPACE;
1693: term_word(p, "\"");
1694: }
1695:
1696:
1697: /* ARGSUSED */
1698: static void
1699: termp_bx_post(DECL_ARGS)
1700: {
1701:
1702: if (node->child)
1703: p->flags |= TERMP_NOSPACE;
1704: term_word(p, "BSD");
1705: }
1706:
1707:
1708: /* ARGSUSED */
1709: static int
1.26 kristaps 1710: termp_xx_pre(DECL_ARGS)
1.1 kristaps 1711: {
1.26 kristaps 1712: const char *pp;
1.1 kristaps 1713:
1.26 kristaps 1714: pp = NULL;
1715: switch (node->tok) {
1716: case (MDOC_Bsx):
1717: pp = "BSDI BSD/OS";
1718: break;
1719: case (MDOC_Dx):
1720: pp = "DragonFlyBSD";
1721: break;
1722: case (MDOC_Fx):
1723: pp = "FreeBSD";
1724: break;
1725: case (MDOC_Nx):
1726: pp = "NetBSD";
1727: break;
1728: case (MDOC_Ox):
1729: pp = "OpenBSD";
1730: break;
1731: case (MDOC_Ux):
1732: pp = "UNIX";
1733: break;
1734: default:
1735: break;
1736: }
1.1 kristaps 1737:
1.26 kristaps 1738: assert(pp);
1739: term_word(p, pp);
1.1 kristaps 1740: return(1);
1741: }
1742:
1743:
1744: /* ARGSUSED */
1745: static int
1746: termp_sq_pre(DECL_ARGS)
1747: {
1748:
1749: if (MDOC_BODY != node->type)
1750: return(1);
1751: term_word(p, "\\(oq");
1752: p->flags |= TERMP_NOSPACE;
1753: return(1);
1754: }
1755:
1756:
1757: /* ARGSUSED */
1758: static void
1759: termp_sq_post(DECL_ARGS)
1760: {
1761:
1762: if (MDOC_BODY != node->type)
1763: return;
1764: p->flags |= TERMP_NOSPACE;
1765: term_word(p, "\\(aq");
1766: }
1767:
1768:
1769: /* ARGSUSED */
1770: static int
1771: termp_pf_pre(DECL_ARGS)
1772: {
1773:
1774: p->flags |= TERMP_IGNDELIM;
1775: return(1);
1776: }
1777:
1778:
1779: /* ARGSUSED */
1780: static void
1781: termp_pf_post(DECL_ARGS)
1782: {
1783:
1784: p->flags &= ~TERMP_IGNDELIM;
1785: p->flags |= TERMP_NOSPACE;
1786: }
1787:
1788:
1789: /* ARGSUSED */
1790: static int
1791: termp_ss_pre(DECL_ARGS)
1792: {
1793:
1794: switch (node->type) {
1795: case (MDOC_BLOCK):
1796: term_newln(p);
1797: if (node->prev)
1798: term_vspace(p);
1799: break;
1800: case (MDOC_HEAD):
1.69 kristaps 1801: p->bold++;
1.5 kristaps 1802: p->offset = HALFINDENT;
1.1 kristaps 1803: break;
1804: default:
1805: break;
1806: }
1807:
1808: return(1);
1809: }
1810:
1811:
1812: /* ARGSUSED */
1813: static void
1814: termp_ss_post(DECL_ARGS)
1815: {
1816:
1.29 kristaps 1817: if (MDOC_HEAD == node->type)
1.1 kristaps 1818: term_newln(p);
1819: }
1820:
1821:
1822: /* ARGSUSED */
1823: static int
1824: termp_cd_pre(DECL_ARGS)
1825: {
1826:
1.69 kristaps 1827: p->bold++;
1.1 kristaps 1828: term_newln(p);
1829: return(1);
1830: }
1831:
1832:
1833: /* ARGSUSED */
1834: static int
1835: termp_in_pre(DECL_ARGS)
1836: {
1837:
1.69 kristaps 1838: p->bold++;
1.22 kristaps 1839: if (SEC_SYNOPSIS == node->sec)
1840: term_word(p, "#include");
1841:
1.1 kristaps 1842: term_word(p, "<");
1843: p->flags |= TERMP_NOSPACE;
1844: return(1);
1845: }
1846:
1847:
1848: /* ARGSUSED */
1849: static void
1850: termp_in_post(DECL_ARGS)
1851: {
1852:
1.69 kristaps 1853: p->bold++;
1.1 kristaps 1854: term_word(p, ">");
1.69 kristaps 1855: p->bold--;
1.1 kristaps 1856:
1857: if (SEC_SYNOPSIS != node->sec)
1858: return;
1.22 kristaps 1859:
1860: term_newln(p);
1861: /*
1862: * XXX Not entirely correct. If `.In foo bar' is specified in
1863: * the SYNOPSIS section, then it produces a single break after
1864: * the <foo>; mandoc asserts a vertical space. Since this
1865: * construction is rarely used, I think it's fine.
1866: */
1.1 kristaps 1867: if (node->next && MDOC_In != node->next->tok)
1868: term_vspace(p);
1869: }
1870:
1871:
1872: /* ARGSUSED */
1873: static int
1.45 kristaps 1874: termp_sp_pre(DECL_ARGS)
1875: {
1876: int i, len;
1877:
1878: if (NULL == node->child) {
1879: term_vspace(p);
1880: return(0);
1881: }
1882:
1883: len = atoi(node->child->string);
1884: if (0 == len)
1885: term_newln(p);
1886: for (i = 0; i < len; i++)
1887: term_vspace(p);
1888:
1889: return(0);
1890: }
1891:
1892:
1893: /* ARGSUSED */
1894: static int
1.44 kristaps 1895: termp_br_pre(DECL_ARGS)
1896: {
1897:
1898: term_newln(p);
1899: return(1);
1900: }
1901:
1902:
1903: /* ARGSUSED */
1904: static int
1.1 kristaps 1905: termp_brq_pre(DECL_ARGS)
1906: {
1907:
1908: if (MDOC_BODY != node->type)
1909: return(1);
1910: term_word(p, "\\(lC");
1911: p->flags |= TERMP_NOSPACE;
1912: return(1);
1913: }
1914:
1915:
1916: /* ARGSUSED */
1917: static void
1918: termp_brq_post(DECL_ARGS)
1919: {
1920:
1921: if (MDOC_BODY != node->type)
1922: return;
1923: p->flags |= TERMP_NOSPACE;
1924: term_word(p, "\\(rC");
1925: }
1926:
1927:
1928: /* ARGSUSED */
1929: static int
1930: termp_bq_pre(DECL_ARGS)
1931: {
1932:
1933: if (MDOC_BODY != node->type)
1934: return(1);
1935: term_word(p, "\\(lB");
1936: p->flags |= TERMP_NOSPACE;
1937: return(1);
1938: }
1939:
1940:
1941: /* ARGSUSED */
1942: static void
1943: termp_bq_post(DECL_ARGS)
1944: {
1945:
1946: if (MDOC_BODY != node->type)
1947: return;
1948: p->flags |= TERMP_NOSPACE;
1949: term_word(p, "\\(rB");
1950: }
1951:
1952:
1953: /* ARGSUSED */
1954: static int
1955: termp_pq_pre(DECL_ARGS)
1956: {
1957:
1958: if (MDOC_BODY != node->type)
1959: return(1);
1960: term_word(p, "\\&(");
1961: p->flags |= TERMP_NOSPACE;
1962: return(1);
1963: }
1964:
1965:
1966: /* ARGSUSED */
1967: static void
1968: termp_pq_post(DECL_ARGS)
1969: {
1970:
1971: if (MDOC_BODY != node->type)
1972: return;
1973: term_word(p, ")");
1974: }
1975:
1976:
1977: /* ARGSUSED */
1978: static int
1979: termp_fo_pre(DECL_ARGS)
1980: {
1981: const struct mdoc_node *n;
1982:
1983: if (MDOC_BODY == node->type) {
1.33 kristaps 1984: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1985: term_word(p, "(");
1986: p->flags |= TERMP_NOSPACE;
1987: return(1);
1988: } else if (MDOC_HEAD != node->type)
1989: return(1);
1990:
1.69 kristaps 1991: p->bold++;
1.1 kristaps 1992: for (n = node->child; n; n = n->next) {
1.11 kristaps 1993: assert(MDOC_TEXT == n->type);
1.1 kristaps 1994: term_word(p, n->string);
1995: }
1.69 kristaps 1996: p->bold--;
1.1 kristaps 1997:
1998: return(0);
1999: }
2000:
2001:
2002: /* ARGSUSED */
2003: static void
2004: termp_fo_post(DECL_ARGS)
2005: {
2006:
2007: if (MDOC_BODY != node->type)
2008: return;
2009: p->flags |= TERMP_NOSPACE;
2010: term_word(p, ")");
2011: p->flags |= TERMP_NOSPACE;
2012: term_word(p, ";");
2013: term_newln(p);
2014: }
2015:
2016:
2017: /* ARGSUSED */
2018: static int
2019: termp_bf_pre(DECL_ARGS)
2020: {
2021: const struct mdoc_node *n;
2022:
1.28 kristaps 2023: if (MDOC_HEAD == node->type)
1.1 kristaps 2024: return(0);
1.28 kristaps 2025: else if (MDOC_BLOCK != node->type)
1.1 kristaps 2026: return(1);
2027:
2028: if (NULL == (n = node->head->child)) {
2029: if (arg_hasattr(MDOC_Emphasis, node))
1.69 kristaps 2030: p->under++;
1.1 kristaps 2031: else if (arg_hasattr(MDOC_Symbolic, node))
1.69 kristaps 2032: p->bold++;
1.1 kristaps 2033:
2034: return(1);
2035: }
2036:
1.11 kristaps 2037: assert(MDOC_TEXT == n->type);
1.1 kristaps 2038: if (0 == strcmp("Em", n->string))
1.69 kristaps 2039: p->under++;
1.1 kristaps 2040: else if (0 == strcmp("Sy", n->string))
1.69 kristaps 2041: p->bold++;
1.1 kristaps 2042:
2043: return(1);
2044: }
2045:
2046:
2047: /* ARGSUSED */
2048: static int
2049: termp_sm_pre(DECL_ARGS)
2050: {
2051:
1.11 kristaps 2052: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 2053: if (0 == strcmp("on", node->child->string)) {
2054: p->flags &= ~TERMP_NONOSPACE;
2055: p->flags &= ~TERMP_NOSPACE;
2056: } else
2057: p->flags |= TERMP_NONOSPACE;
2058:
2059: return(0);
2060: }
2061:
2062:
2063: /* ARGSUSED */
2064: static int
2065: termp_ap_pre(DECL_ARGS)
2066: {
2067:
2068: p->flags |= TERMP_NOSPACE;
2069: term_word(p, "\\(aq");
2070: p->flags |= TERMP_NOSPACE;
2071: return(1);
2072: }
2073:
2074:
2075: /* ARGSUSED */
2076: static void
2077: termp____post(DECL_ARGS)
2078: {
2079:
2080: p->flags |= TERMP_NOSPACE;
2081: term_word(p, node->next ? "," : ".");
2082: }
2083:
2084:
2085: /* ARGSUSED */
2086: static int
2087: termp_lk_pre(DECL_ARGS)
2088: {
2089: const struct mdoc_node *n;
2090:
1.11 kristaps 2091: assert(node->child);
2092: n = node->child;
1.1 kristaps 2093:
1.12 kristaps 2094: if (NULL == n->next) {
1.69 kristaps 2095: p->under++;
1.12 kristaps 2096: return(1);
2097: }
2098:
1.69 kristaps 2099: p->under++;
1.1 kristaps 2100: term_word(p, n->string);
2101: p->flags |= TERMP_NOSPACE;
2102: term_word(p, ":");
1.69 kristaps 2103: p->under--;
1.1 kristaps 2104:
1.69 kristaps 2105: p->bold++;
1.12 kristaps 2106: for (n = n->next; n; n = n->next)
1.1 kristaps 2107: term_word(p, n->string);
1.69 kristaps 2108: p->bold--;
1.12 kristaps 2109:
1.1 kristaps 2110: return(0);
2111: }
2112:
2113:
2114: /* ARGSUSED */
2115: static int
1.69 kristaps 2116: termp_under_pre(DECL_ARGS)
1.1 kristaps 2117: {
2118:
1.69 kristaps 2119: p->under++;
1.1 kristaps 2120: return(1);
2121: }
CVSweb