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