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