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