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