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