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