Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.153
1.153 ! schwarze 1: /* $OpenBSD: man_term.c,v 1.152 2017/05/07 21:44:33 schwarze Exp $ */
1.1 kristaps 2: /*
1.84 schwarze 3: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.142 schwarze 4: * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 5: *
6: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 9: *
1.124 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.2 schwarze 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.124 schwarze 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.2 schwarze 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 17: */
1.13 schwarze 18: #include <sys/types.h>
19:
1.1 kristaps 20: #include <assert.h>
1.11 schwarze 21: #include <ctype.h>
1.117 schwarze 22: #include <limits.h>
1.1 kristaps 23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
26:
1.124 schwarze 27: #include "mandoc_aux.h"
1.37 schwarze 28: #include "mandoc.h"
1.124 schwarze 29: #include "roff.h"
30: #include "man.h"
1.18 schwarze 31: #include "out.h"
1.1 kristaps 32: #include "term.h"
1.18 schwarze 33: #include "main.h"
1.10 schwarze 34:
1.70 schwarze 35: #define MAXMARGINS 64 /* maximum number of indented scopes */
1.1 kristaps 36:
1.11 schwarze 37: struct mtermp {
38: int fl;
39: #define MANT_LITERAL (1 << 0)
1.116 schwarze 40: int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
1.70 schwarze 41: int lmargincur; /* index of current margin */
42: int lmarginsz; /* actual number of nested margins */
43: size_t offset; /* default offset to visible page */
1.88 schwarze 44: int pardist; /* vert. space before par., unit: [v] */
1.11 schwarze 45: };
46:
1.100 schwarze 47: #define DECL_ARGS struct termp *p, \
1.11 schwarze 48: struct mtermp *mt, \
1.125 schwarze 49: struct roff_node *n, \
1.126 schwarze 50: const struct roff_meta *meta
1.1 kristaps 51:
52: struct termact {
53: int (*pre)(DECL_ARGS);
54: void (*post)(DECL_ARGS);
1.26 schwarze 55: int flags;
56: #define MAN_NOTEXT (1 << 0) /* Never has text children. */
1.1 kristaps 57: };
58:
1.21 schwarze 59: static void print_man_nodelist(DECL_ARGS);
1.19 schwarze 60: static void print_man_node(DECL_ARGS);
1.126 schwarze 61: static void print_man_head(struct termp *,
62: const struct roff_meta *);
63: static void print_man_foot(struct termp *,
64: const struct roff_meta *);
1.100 schwarze 65: static void print_bvspace(struct termp *,
1.125 schwarze 66: const struct roff_node *, int);
1.18 schwarze 67:
1.1 kristaps 68: static int pre_B(DECL_ARGS);
1.152 schwarze 69: static int pre_DT(DECL_ARGS);
1.11 schwarze 70: static int pre_HP(DECL_ARGS);
1.1 kristaps 71: static int pre_I(DECL_ARGS);
72: static int pre_IP(DECL_ARGS);
1.82 schwarze 73: static int pre_OP(DECL_ARGS);
1.88 schwarze 74: static int pre_PD(DECL_ARGS);
1.1 kristaps 75: static int pre_PP(DECL_ARGS);
1.13 schwarze 76: static int pre_RS(DECL_ARGS);
1.1 kristaps 77: static int pre_SH(DECL_ARGS);
78: static int pre_SS(DECL_ARGS);
79: static int pre_TP(DECL_ARGS);
1.91 schwarze 80: static int pre_UR(DECL_ARGS);
1.82 schwarze 81: static int pre_alternate(DECL_ARGS);
1.14 schwarze 82: static int pre_ign(DECL_ARGS);
1.45 schwarze 83: static int pre_in(DECL_ARGS);
84: static int pre_literal(DECL_ARGS);
1.1 kristaps 85:
1.11 schwarze 86: static void post_IP(DECL_ARGS);
87: static void post_HP(DECL_ARGS);
1.13 schwarze 88: static void post_RS(DECL_ARGS);
1.1 kristaps 89: static void post_SH(DECL_ARGS);
90: static void post_SS(DECL_ARGS);
1.11 schwarze 91: static void post_TP(DECL_ARGS);
1.91 schwarze 92: static void post_UR(DECL_ARGS);
1.1 kristaps 93:
1.145 schwarze 94: static const struct termact __termacts[MAN_MAX - MAN_TH] = {
1.26 schwarze 95: { NULL, NULL, 0 }, /* TH */
96: { pre_SH, post_SH, 0 }, /* SH */
97: { pre_SS, post_SS, 0 }, /* SS */
98: { pre_TP, post_TP, 0 }, /* TP */
99: { pre_PP, NULL, 0 }, /* LP */
100: { pre_PP, NULL, 0 }, /* PP */
101: { pre_PP, NULL, 0 }, /* P */
102: { pre_IP, post_IP, 0 }, /* IP */
1.100 schwarze 103: { pre_HP, post_HP, 0 }, /* HP */
1.26 schwarze 104: { NULL, NULL, 0 }, /* SM */
105: { pre_B, NULL, 0 }, /* SB */
1.51 schwarze 106: { pre_alternate, NULL, 0 }, /* BI */
107: { pre_alternate, NULL, 0 }, /* IB */
108: { pre_alternate, NULL, 0 }, /* BR */
109: { pre_alternate, NULL, 0 }, /* RB */
1.26 schwarze 110: { NULL, NULL, 0 }, /* R */
111: { pre_B, NULL, 0 }, /* B */
112: { pre_I, NULL, 0 }, /* I */
1.51 schwarze 113: { pre_alternate, NULL, 0 }, /* IR */
114: { pre_alternate, NULL, 0 }, /* RI */
1.45 schwarze 115: { pre_literal, NULL, 0 }, /* nf */
116: { pre_literal, NULL, 0 }, /* fi */
1.26 schwarze 117: { NULL, NULL, 0 }, /* RE */
118: { pre_RS, post_RS, 0 }, /* RS */
1.152 schwarze 119: { pre_DT, NULL, 0 }, /* DT */
1.111 schwarze 120: { pre_ign, NULL, MAN_NOTEXT }, /* UC */
1.88 schwarze 121: { pre_PD, NULL, MAN_NOTEXT }, /* PD */
1.36 schwarze 122: { pre_ign, NULL, 0 }, /* AT */
1.45 schwarze 123: { pre_in, NULL, MAN_NOTEXT }, /* in */
1.82 schwarze 124: { pre_OP, NULL, 0 }, /* OP */
1.83 schwarze 125: { pre_literal, NULL, 0 }, /* EX */
126: { pre_literal, NULL, 0 }, /* EE */
1.91 schwarze 127: { pre_UR, post_UR, 0 }, /* UR */
128: { NULL, NULL, 0 }, /* UE */
1.1 kristaps 129: };
1.145 schwarze 130: static const struct termact *termacts = __termacts - MAN_TH;
1.11 schwarze 131:
1.1 kristaps 132:
1.16 schwarze 133: void
1.133 schwarze 134: terminal_man(void *arg, const struct roff_man *man)
1.1 kristaps 135: {
1.18 schwarze 136: struct termp *p;
1.125 schwarze 137: struct roff_node *n;
1.18 schwarze 138: struct mtermp mt;
1.142 schwarze 139: size_t save_defindent;
1.18 schwarze 140:
141: p = (struct termp *)arg;
1.39 schwarze 142: p->overstep = 0;
1.104 schwarze 143: p->rmargin = p->maxrmargin = p->defrmargin;
1.151 schwarze 144: term_tab_set(p, NULL);
145: term_tab_set(p, "T");
146: term_tab_set(p, ".5i");
1.18 schwarze 147:
1.70 schwarze 148: memset(&mt, 0, sizeof(struct mtermp));
1.77 schwarze 149: mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
150: mt.offset = term_len(p, p->defindent);
1.88 schwarze 151: mt.pardist = 1;
1.11 schwarze 152:
1.134 schwarze 153: n = man->first->child;
1.104 schwarze 154: if (p->synopsisonly) {
155: while (n != NULL) {
156: if (n->tok == MAN_SH &&
1.124 schwarze 157: n->child->child->type == ROFFT_TEXT &&
1.104 schwarze 158: !strcmp(n->child->child->string, "SYNOPSIS")) {
159: if (n->child->next->child != NULL)
160: print_man_nodelist(p, &mt,
1.134 schwarze 161: n->child->next->child,
162: &man->meta);
1.104 schwarze 163: term_newln(p);
164: break;
165: }
166: n = n->next;
167: }
168: } else {
1.142 schwarze 169: save_defindent = p->defindent;
1.104 schwarze 170: if (p->defindent == 0)
171: p->defindent = 7;
1.134 schwarze 172: term_begin(p, print_man_head, print_man_foot, &man->meta);
1.104 schwarze 173: p->flags |= TERMP_NOSPACE;
174: if (n != NULL)
1.134 schwarze 175: print_man_nodelist(p, &mt, n, &man->meta);
1.104 schwarze 176: term_end(p);
1.142 schwarze 177: p->defindent = save_defindent;
1.104 schwarze 178: }
1.1 kristaps 179: }
180:
1.69 schwarze 181: /*
182: * Printing leading vertical space before a block.
183: * This is used for the paragraph macros.
184: * The rules are pretty simple, since there's very little nesting going
185: * on here. Basically, if we're the first within another block (SS/SH),
186: * then don't emit vertical space. If we are (RS), then do. If not the
187: * first, print it.
188: */
1.18 schwarze 189: static void
1.125 schwarze 190: print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
1.18 schwarze 191: {
1.88 schwarze 192: int i;
1.69 schwarze 193:
1.18 schwarze 194: term_newln(p);
1.64 schwarze 195:
1.69 schwarze 196: if (n->body && n->body->child)
1.124 schwarze 197: if (n->body->child->type == ROFFT_TBL)
1.69 schwarze 198: return;
1.11 schwarze 199:
1.124 schwarze 200: if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
1.69 schwarze 201: if (NULL == n->prev)
202: return;
1.11 schwarze 203:
1.88 schwarze 204: for (i = 0; i < pardist; i++)
205: term_vspace(p);
1.14 schwarze 206: }
207:
1.100 schwarze 208:
1.14 schwarze 209: static int
210: pre_ign(DECL_ARGS)
211: {
212:
1.138 schwarze 213: return 0;
1.11 schwarze 214: }
215:
1.1 kristaps 216: static int
217: pre_I(DECL_ARGS)
218: {
219:
1.21 schwarze 220: term_fontrepl(p, TERMFONT_UNDER);
1.138 schwarze 221: return 1;
1.1 kristaps 222: }
223:
1.11 schwarze 224: static int
1.45 schwarze 225: pre_literal(DECL_ARGS)
1.11 schwarze 226: {
227:
1.45 schwarze 228: term_newln(p);
1.53 schwarze 229:
1.83 schwarze 230: if (MAN_nf == n->tok || MAN_EX == n->tok)
1.45 schwarze 231: mt->fl |= MANT_LITERAL;
1.53 schwarze 232: else
1.45 schwarze 233: mt->fl &= ~MANT_LITERAL;
234:
1.72 schwarze 235: /*
236: * Unlike .IP and .TP, .HP does not have a HEAD.
237: * So in case a second call to term_flushln() is needed,
238: * indentation has to be set up explicitly.
239: */
240: if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
1.76 schwarze 241: p->offset = p->rmargin;
1.72 schwarze 242: p->rmargin = p->maxrmargin;
1.93 schwarze 243: p->trailspace = 0;
1.99 schwarze 244: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.72 schwarze 245: p->flags |= TERMP_NOSPACE;
246: }
247:
1.138 schwarze 248: return 0;
1.11 schwarze 249: }
250:
251: static int
1.88 schwarze 252: pre_PD(DECL_ARGS)
253: {
1.113 schwarze 254: struct roffsu su;
1.88 schwarze 255:
256: n = n->child;
1.113 schwarze 257: if (n == NULL) {
1.88 schwarze 258: mt->pardist = 1;
1.138 schwarze 259: return 0;
1.88 schwarze 260: }
1.124 schwarze 261: assert(n->type == ROFFT_TEXT);
1.113 schwarze 262: if (a2roffsu(n->string, &su, SCALE_VS))
263: mt->pardist = term_vspan(p, &su);
1.138 schwarze 264: return 0;
1.88 schwarze 265: }
266:
267: static int
1.51 schwarze 268: pre_alternate(DECL_ARGS)
1.1 kristaps 269: {
1.51 schwarze 270: enum termfont font[2];
1.125 schwarze 271: struct roff_node *nn;
1.51 schwarze 272: int savelit, i;
1.1 kristaps 273:
1.51 schwarze 274: switch (n->tok) {
1.100 schwarze 275: case MAN_RB:
1.51 schwarze 276: font[0] = TERMFONT_NONE;
277: font[1] = TERMFONT_BOLD;
278: break;
1.100 schwarze 279: case MAN_RI:
1.51 schwarze 280: font[0] = TERMFONT_NONE;
281: font[1] = TERMFONT_UNDER;
282: break;
1.100 schwarze 283: case MAN_BR:
1.51 schwarze 284: font[0] = TERMFONT_BOLD;
285: font[1] = TERMFONT_NONE;
286: break;
1.100 schwarze 287: case MAN_BI:
1.51 schwarze 288: font[0] = TERMFONT_BOLD;
289: font[1] = TERMFONT_UNDER;
290: break;
1.100 schwarze 291: case MAN_IR:
1.51 schwarze 292: font[0] = TERMFONT_UNDER;
293: font[1] = TERMFONT_NONE;
294: break;
1.100 schwarze 295: case MAN_IB:
1.51 schwarze 296: font[0] = TERMFONT_UNDER;
297: font[1] = TERMFONT_BOLD;
298: break;
299: default:
300: abort();
301: }
1.17 schwarze 302:
1.51 schwarze 303: savelit = MANT_LITERAL & mt->fl;
304: mt->fl &= ~MANT_LITERAL;
1.17 schwarze 305:
1.51 schwarze 306: for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
307: term_fontrepl(p, font[i]);
308: if (savelit && NULL == nn->next)
309: mt->fl |= MANT_LITERAL;
1.132 schwarze 310: assert(nn->type == ROFFT_TEXT);
311: term_word(p, nn->string);
1.141 schwarze 312: if (nn->flags & NODE_EOS)
1.132 schwarze 313: p->flags |= TERMP_SENTENCE;
1.51 schwarze 314: if (nn->next)
1.1 kristaps 315: p->flags |= TERMP_NOSPACE;
316: }
1.17 schwarze 317:
1.138 schwarze 318: return 0;
1.1 kristaps 319: }
320:
321: static int
322: pre_B(DECL_ARGS)
323: {
324:
1.21 schwarze 325: term_fontrepl(p, TERMFONT_BOLD);
1.138 schwarze 326: return 1;
1.82 schwarze 327: }
328:
329: static int
330: pre_OP(DECL_ARGS)
331: {
332:
333: term_word(p, "[");
334: p->flags |= TERMP_NOSPACE;
335:
336: if (NULL != (n = n->child)) {
337: term_fontrepl(p, TERMFONT_BOLD);
338: term_word(p, n->string);
339: }
340: if (NULL != n && NULL != n->next) {
341: term_fontrepl(p, TERMFONT_UNDER);
342: term_word(p, n->next->string);
343: }
344:
345: term_fontrepl(p, TERMFONT_NONE);
346: p->flags |= TERMP_NOSPACE;
347: term_word(p, "]");
1.138 schwarze 348: return 0;
1.1 kristaps 349: }
350:
351: static int
1.45 schwarze 352: pre_in(DECL_ARGS)
1.11 schwarze 353: {
1.116 schwarze 354: struct roffsu su;
355: const char *cp;
1.45 schwarze 356: size_t v;
1.116 schwarze 357: int less;
1.45 schwarze 358:
359: term_newln(p);
360:
361: if (NULL == n->child) {
362: p->offset = mt->offset;
1.138 schwarze 363: return 0;
1.45 schwarze 364: }
1.11 schwarze 365:
1.45 schwarze 366: cp = n->child->string;
367: less = 0;
1.11 schwarze 368:
1.45 schwarze 369: if ('-' == *cp)
370: less = -1;
371: else if ('+' == *cp)
372: less = 1;
373: else
374: cp--;
375:
1.116 schwarze 376: if ( ! a2roffsu(++cp, &su, SCALE_EN))
1.138 schwarze 377: return 0;
1.45 schwarze 378:
1.128 schwarze 379: v = (term_hspan(p, &su) + 11) / 24;
1.45 schwarze 380:
381: if (less < 0)
382: p->offset -= p->offset > v ? v : p->offset;
383: else if (less > 0)
384: p->offset += v;
1.100 schwarze 385: else
1.45 schwarze 386: p->offset = v;
1.117 schwarze 387: if (p->offset > SHRT_MAX)
388: p->offset = term_len(p, p->defindent);
1.8 schwarze 389:
1.152 schwarze 390: return 0;
391: }
392:
393: static int
394: pre_DT(DECL_ARGS)
395: {
396: term_tab_set(p, NULL);
397: term_tab_set(p, "T");
398: term_tab_set(p, ".5i");
1.138 schwarze 399: return 0;
1.8 schwarze 400: }
401:
402: static int
1.11 schwarze 403: pre_HP(DECL_ARGS)
404: {
1.116 schwarze 405: struct roffsu su;
1.125 schwarze 406: const struct roff_node *nn;
1.116 schwarze 407: int len;
1.11 schwarze 408:
409: switch (n->type) {
1.124 schwarze 410: case ROFFT_BLOCK:
1.88 schwarze 411: print_bvspace(p, n, mt->pardist);
1.138 schwarze 412: return 1;
1.124 schwarze 413: case ROFFT_BODY:
1.11 schwarze 414: break;
415: default:
1.138 schwarze 416: return 0;
1.11 schwarze 417: }
418:
1.84 schwarze 419: if ( ! (MANT_LITERAL & mt->fl)) {
1.99 schwarze 420: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.93 schwarze 421: p->trailspace = 2;
1.84 schwarze 422: }
423:
1.11 schwarze 424: /* Calculate offset. */
425:
1.116 schwarze 426: if ((nn = n->parent->head->child) != NULL &&
427: a2roffsu(nn->string, &su, SCALE_EN)) {
1.128 schwarze 428: len = term_hspan(p, &su) / 24;
1.117 schwarze 429: if (len < 0 && (size_t)(-len) > mt->offset)
430: len = -mt->offset;
431: else if (len > SHRT_MAX)
432: len = term_len(p, p->defindent);
1.116 schwarze 433: mt->lmargin[mt->lmargincur] = len;
434: } else
435: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 436:
1.13 schwarze 437: p->offset = mt->offset;
1.117 schwarze 438: p->rmargin = mt->offset + len;
1.138 schwarze 439: return 1;
1.11 schwarze 440: }
441:
442: static void
443: post_HP(DECL_ARGS)
444: {
445:
446: switch (n->type) {
1.124 schwarze 447: case ROFFT_BODY:
1.90 schwarze 448: term_newln(p);
1.127 schwarze 449:
450: /*
451: * Compatibility with a groff bug.
452: * The .HP macro uses the undocumented .tag request
453: * which causes a line break and cancels no-space
454: * mode even if there isn't any output.
455: */
456:
457: if (n->child == NULL)
458: term_vspace(p);
459:
1.99 schwarze 460: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.93 schwarze 461: p->trailspace = 0;
1.13 schwarze 462: p->offset = mt->offset;
1.11 schwarze 463: p->rmargin = p->maxrmargin;
464: break;
465: default:
466: break;
467: }
468: }
469:
470: static int
1.1 kristaps 471: pre_PP(DECL_ARGS)
472: {
473:
1.11 schwarze 474: switch (n->type) {
1.124 schwarze 475: case ROFFT_BLOCK:
1.77 schwarze 476: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.88 schwarze 477: print_bvspace(p, n, mt->pardist);
1.11 schwarze 478: break;
479: default:
1.13 schwarze 480: p->offset = mt->offset;
1.11 schwarze 481: break;
482: }
483:
1.138 schwarze 484: return n->type != ROFFT_HEAD;
1.1 kristaps 485: }
486:
487: static int
488: pre_IP(DECL_ARGS)
489: {
1.116 schwarze 490: struct roffsu su;
1.125 schwarze 491: const struct roff_node *nn;
1.116 schwarze 492: int len, savelit;
1.11 schwarze 493:
494: switch (n->type) {
1.124 schwarze 495: case ROFFT_BODY:
1.11 schwarze 496: p->flags |= TERMP_NOSPACE;
497: break;
1.124 schwarze 498: case ROFFT_HEAD:
1.11 schwarze 499: p->flags |= TERMP_NOBREAK;
1.93 schwarze 500: p->trailspace = 1;
1.11 schwarze 501: break;
1.124 schwarze 502: case ROFFT_BLOCK:
1.88 schwarze 503: print_bvspace(p, n, mt->pardist);
1.11 schwarze 504: /* FALLTHROUGH */
505: default:
1.138 schwarze 506: return 1;
1.11 schwarze 507: }
508:
1.57 schwarze 509: /* Calculate the offset from the optional second argument. */
1.116 schwarze 510: if ((nn = n->parent->head->child) != NULL &&
511: (nn = nn->next) != NULL &&
512: a2roffsu(nn->string, &su, SCALE_EN)) {
1.128 schwarze 513: len = term_hspan(p, &su) / 24;
1.116 schwarze 514: if (len < 0 && (size_t)(-len) > mt->offset)
515: len = -mt->offset;
1.117 schwarze 516: else if (len > SHRT_MAX)
517: len = term_len(p, p->defindent);
518: mt->lmargin[mt->lmargincur] = len;
1.116 schwarze 519: } else
520: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 521:
522: switch (n->type) {
1.124 schwarze 523: case ROFFT_HEAD:
1.13 schwarze 524: p->offset = mt->offset;
525: p->rmargin = mt->offset + len;
1.11 schwarze 526:
1.57 schwarze 527: savelit = MANT_LITERAL & mt->fl;
528: mt->fl &= ~MANT_LITERAL;
529:
530: if (n->child)
1.89 schwarze 531: print_man_node(p, mt, n->child, meta);
1.57 schwarze 532:
533: if (savelit)
534: mt->fl |= MANT_LITERAL;
535:
1.138 schwarze 536: return 0;
1.124 schwarze 537: case ROFFT_BODY:
1.13 schwarze 538: p->offset = mt->offset + len;
1.109 schwarze 539: p->rmargin = p->maxrmargin;
1.11 schwarze 540: break;
541: default:
542: break;
543: }
1.1 kristaps 544:
1.138 schwarze 545: return 1;
1.11 schwarze 546: }
1.1 kristaps 547:
1.11 schwarze 548: static void
549: post_IP(DECL_ARGS)
550: {
1.4 schwarze 551:
1.11 schwarze 552: switch (n->type) {
1.124 schwarze 553: case ROFFT_HEAD:
1.11 schwarze 554: term_flushln(p);
555: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 556: p->trailspace = 0;
1.11 schwarze 557: p->rmargin = p->maxrmargin;
558: break;
1.124 schwarze 559: case ROFFT_BODY:
1.57 schwarze 560: term_newln(p);
1.92 schwarze 561: p->offset = mt->offset;
1.11 schwarze 562: break;
563: default:
564: break;
565: }
1.1 kristaps 566: }
567:
568: static int
569: pre_TP(DECL_ARGS)
570: {
1.116 schwarze 571: struct roffsu su;
1.125 schwarze 572: struct roff_node *nn;
1.116 schwarze 573: int len, savelit;
1.11 schwarze 574:
575: switch (n->type) {
1.124 schwarze 576: case ROFFT_HEAD:
1.137 schwarze 577: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
1.93 schwarze 578: p->trailspace = 1;
1.11 schwarze 579: break;
1.124 schwarze 580: case ROFFT_BODY:
1.11 schwarze 581: p->flags |= TERMP_NOSPACE;
582: break;
1.124 schwarze 583: case ROFFT_BLOCK:
1.88 schwarze 584: print_bvspace(p, n, mt->pardist);
1.11 schwarze 585: /* FALLTHROUGH */
586: default:
1.138 schwarze 587: return 1;
1.11 schwarze 588: }
589:
590: /* Calculate offset. */
1.1 kristaps 591:
1.116 schwarze 592: if ((nn = n->parent->head->child) != NULL &&
1.141 schwarze 593: nn->string != NULL && ! (NODE_LINE & nn->flags) &&
1.116 schwarze 594: a2roffsu(nn->string, &su, SCALE_EN)) {
1.128 schwarze 595: len = term_hspan(p, &su) / 24;
1.116 schwarze 596: if (len < 0 && (size_t)(-len) > mt->offset)
597: len = -mt->offset;
1.117 schwarze 598: else if (len > SHRT_MAX)
599: len = term_len(p, p->defindent);
600: mt->lmargin[mt->lmargincur] = len;
1.116 schwarze 601: } else
602: len = mt->lmargin[mt->lmargincur];
1.8 schwarze 603:
1.11 schwarze 604: switch (n->type) {
1.124 schwarze 605: case ROFFT_HEAD:
1.13 schwarze 606: p->offset = mt->offset;
607: p->rmargin = mt->offset + len;
1.11 schwarze 608:
1.57 schwarze 609: savelit = MANT_LITERAL & mt->fl;
610: mt->fl &= ~MANT_LITERAL;
611:
1.11 schwarze 612: /* Don't print same-line elements. */
1.95 schwarze 613: nn = n->child;
1.141 schwarze 614: while (NULL != nn && 0 == (NODE_LINE & nn->flags))
1.95 schwarze 615: nn = nn->next;
616:
617: while (NULL != nn) {
618: print_man_node(p, mt, nn, meta);
619: nn = nn->next;
620: }
1.11 schwarze 621:
1.57 schwarze 622: if (savelit)
623: mt->fl |= MANT_LITERAL;
1.138 schwarze 624: return 0;
1.124 schwarze 625: case ROFFT_BODY:
1.13 schwarze 626: p->offset = mt->offset + len;
1.109 schwarze 627: p->rmargin = p->maxrmargin;
1.93 schwarze 628: p->trailspace = 0;
1.137 schwarze 629: p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
1.11 schwarze 630: break;
631: default:
632: break;
633: }
1.1 kristaps 634:
1.138 schwarze 635: return 1;
1.11 schwarze 636: }
1.1 kristaps 637:
1.11 schwarze 638: static void
639: post_TP(DECL_ARGS)
640: {
1.1 kristaps 641:
1.11 schwarze 642: switch (n->type) {
1.124 schwarze 643: case ROFFT_HEAD:
1.11 schwarze 644: term_flushln(p);
645: break;
1.124 schwarze 646: case ROFFT_BODY:
1.57 schwarze 647: term_newln(p);
1.92 schwarze 648: p->offset = mt->offset;
1.11 schwarze 649: break;
650: default:
651: break;
652: }
1.1 kristaps 653: }
654:
655: static int
656: pre_SS(DECL_ARGS)
657: {
1.88 schwarze 658: int i;
1.1 kristaps 659:
1.11 schwarze 660: switch (n->type) {
1.124 schwarze 661: case ROFFT_BLOCK:
1.69 schwarze 662: mt->fl &= ~MANT_LITERAL;
1.77 schwarze 663: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
664: mt->offset = term_len(p, p->defindent);
1.111 schwarze 665:
666: /*
667: * No vertical space before the first subsection
668: * and after an empty subsection.
669: */
670:
671: do {
672: n = n->prev;
1.135 schwarze 673: } while (n != NULL && n->tok != TOKEN_NONE &&
1.123 schwarze 674: termacts[n->tok].flags & MAN_NOTEXT);
1.111 schwarze 675: if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
1.11 schwarze 676: break;
1.111 schwarze 677:
1.88 schwarze 678: for (i = 0; i < mt->pardist; i++)
679: term_vspace(p);
1.11 schwarze 680: break;
1.124 schwarze 681: case ROFFT_HEAD:
1.21 schwarze 682: term_fontrepl(p, TERMFONT_BOLD);
1.87 schwarze 683: p->offset = term_len(p, 3);
1.129 schwarze 684: p->rmargin = mt->offset;
685: p->trailspace = mt->offset;
686: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 687: break;
1.124 schwarze 688: case ROFFT_BODY:
1.13 schwarze 689: p->offset = mt->offset;
1.129 schwarze 690: p->rmargin = p->maxrmargin;
691: p->trailspace = 0;
692: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 693: break;
694: default:
695: break;
696: }
697:
1.138 schwarze 698: return 1;
1.1 kristaps 699: }
700:
701: static void
702: post_SS(DECL_ARGS)
703: {
1.100 schwarze 704:
1.11 schwarze 705: switch (n->type) {
1.124 schwarze 706: case ROFFT_HEAD:
1.11 schwarze 707: term_newln(p);
708: break;
1.124 schwarze 709: case ROFFT_BODY:
1.11 schwarze 710: term_newln(p);
711: break;
712: default:
713: break;
714: }
1.1 kristaps 715: }
716:
717: static int
718: pre_SH(DECL_ARGS)
719: {
1.88 schwarze 720: int i;
1.1 kristaps 721:
1.11 schwarze 722: switch (n->type) {
1.124 schwarze 723: case ROFFT_BLOCK:
1.69 schwarze 724: mt->fl &= ~MANT_LITERAL;
1.77 schwarze 725: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
726: mt->offset = term_len(p, p->defindent);
1.111 schwarze 727:
728: /*
729: * No vertical space before the first section
730: * and after an empty section.
731: */
732:
733: do {
734: n = n->prev;
1.144 schwarze 735: } while (n != NULL && n->tok != TOKEN_NONE &&
1.143 schwarze 736: termacts[n->tok].flags & MAN_NOTEXT);
1.111 schwarze 737: if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
1.29 schwarze 738: break;
1.111 schwarze 739:
1.88 schwarze 740: for (i = 0; i < mt->pardist; i++)
741: term_vspace(p);
1.11 schwarze 742: break;
1.124 schwarze 743: case ROFFT_HEAD:
1.21 schwarze 744: term_fontrepl(p, TERMFONT_BOLD);
1.11 schwarze 745: p->offset = 0;
1.129 schwarze 746: p->rmargin = mt->offset;
747: p->trailspace = mt->offset;
748: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 749: break;
1.124 schwarze 750: case ROFFT_BODY:
1.13 schwarze 751: p->offset = mt->offset;
1.129 schwarze 752: p->rmargin = p->maxrmargin;
753: p->trailspace = 0;
754: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 755: break;
756: default:
757: break;
758: }
759:
1.138 schwarze 760: return 1;
1.1 kristaps 761: }
762:
763: static void
764: post_SH(DECL_ARGS)
765: {
1.100 schwarze 766:
1.11 schwarze 767: switch (n->type) {
1.124 schwarze 768: case ROFFT_HEAD:
1.11 schwarze 769: term_newln(p);
770: break;
1.124 schwarze 771: case ROFFT_BODY:
1.11 schwarze 772: term_newln(p);
773: break;
774: default:
1.13 schwarze 775: break;
776: }
777: }
778:
779: static int
780: pre_RS(DECL_ARGS)
781: {
1.116 schwarze 782: struct roffsu su;
1.13 schwarze 783:
784: switch (n->type) {
1.124 schwarze 785: case ROFFT_BLOCK:
1.13 schwarze 786: term_newln(p);
1.138 schwarze 787: return 1;
1.124 schwarze 788: case ROFFT_HEAD:
1.138 schwarze 789: return 0;
1.13 schwarze 790: default:
791: break;
792: }
793:
1.118 schwarze 794: n = n->parent->head;
795: n->aux = SHRT_MAX + 1;
1.130 schwarze 796: if (n->child == NULL)
797: n->aux = mt->lmargin[mt->lmargincur];
798: else if (a2roffsu(n->child->string, &su, SCALE_EN))
1.128 schwarze 799: n->aux = term_hspan(p, &su) / 24;
1.118 schwarze 800: if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
801: n->aux = -mt->offset;
802: else if (n->aux > SHRT_MAX)
803: n->aux = term_len(p, p->defindent);
1.13 schwarze 804:
1.118 schwarze 805: mt->offset += n->aux;
1.94 schwarze 806: p->offset = mt->offset;
1.109 schwarze 807: p->rmargin = p->maxrmargin;
1.13 schwarze 808:
1.70 schwarze 809: if (++mt->lmarginsz < MAXMARGINS)
810: mt->lmargincur = mt->lmarginsz;
811:
1.131 schwarze 812: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.138 schwarze 813: return 1;
1.13 schwarze 814: }
815:
816: static void
817: post_RS(DECL_ARGS)
818: {
819:
820: switch (n->type) {
1.124 schwarze 821: case ROFFT_BLOCK:
1.69 schwarze 822: return;
1.124 schwarze 823: case ROFFT_HEAD:
1.69 schwarze 824: return;
1.13 schwarze 825: default:
826: term_newln(p);
1.11 schwarze 827: break;
828: }
1.69 schwarze 829:
1.118 schwarze 830: mt->offset -= n->parent->head->aux;
1.69 schwarze 831: p->offset = mt->offset;
1.70 schwarze 832:
833: if (--mt->lmarginsz < MAXMARGINS)
834: mt->lmargincur = mt->lmarginsz;
1.91 schwarze 835: }
836:
837: static int
838: pre_UR(DECL_ARGS)
839: {
840:
1.138 schwarze 841: return n->type != ROFFT_HEAD;
1.91 schwarze 842: }
843:
844: static void
845: post_UR(DECL_ARGS)
846: {
847:
1.124 schwarze 848: if (n->type != ROFFT_BLOCK)
1.91 schwarze 849: return;
850:
851: term_word(p, "<");
852: p->flags |= TERMP_NOSPACE;
853:
854: if (NULL != n->child->child)
855: print_man_node(p, mt, n->child->child, meta);
856:
857: p->flags |= TERMP_NOSPACE;
858: term_word(p, ">");
1.47 schwarze 859: }
860:
1.1 kristaps 861: static void
1.19 schwarze 862: print_man_node(DECL_ARGS)
1.1 kristaps 863: {
1.32 schwarze 864: size_t rm, rmax;
1.21 schwarze 865: int c;
1.1 kristaps 866:
867: switch (n->type) {
1.124 schwarze 868: case ROFFT_TEXT:
1.61 schwarze 869: /*
870: * If we have a blank line, output a vertical space.
871: * If we have a space as the first character, break
872: * before printing the line's data.
873: */
1.153 ! schwarze 874: if (*n->string == '\0') {
1.1 kristaps 875: term_vspace(p);
1.61 schwarze 876: return;
1.153 ! schwarze 877: } else if (*n->string == ' ' && n->flags & NODE_LINE &&
! 878: (p->flags & TERMP_NONEWLINE) == 0)
1.60 schwarze 879: term_newln(p);
1.21 schwarze 880:
1.1 kristaps 881: term_word(p, n->string);
1.84 schwarze 882: goto out;
1.21 schwarze 883:
1.124 schwarze 884: case ROFFT_EQN:
1.141 schwarze 885: if ( ! (n->flags & NODE_LINE))
1.105 schwarze 886: p->flags |= TERMP_NOSPACE;
1.71 schwarze 887: term_eqn(p, n->eqn);
1.141 schwarze 888: if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.106 schwarze 889: p->flags |= TERMP_NOSPACE;
1.61 schwarze 890: return;
1.124 schwarze 891: case ROFFT_TBL:
1.122 schwarze 892: if (p->tbl.cols == NULL)
893: term_vspace(p);
1.58 schwarze 894: term_tbl(p, n->span);
1.61 schwarze 895: return;
1.1 kristaps 896: default:
897: break;
898: }
899:
1.146 schwarze 900: if (n->tok < ROFF_MAX) {
1.147 schwarze 901: roff_term_pre(p, n);
1.146 schwarze 902: return;
903: }
904:
905: assert(n->tok >= MAN_TH && n->tok <= MAN_MAX);
1.61 schwarze 906: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
907: term_fontrepl(p, TERMFONT_NONE);
908:
909: c = 1;
910: if (termacts[n->tok].pre)
1.89 schwarze 911: c = (*termacts[n->tok].pre)(p, mt, n, meta);
1.61 schwarze 912:
1.1 kristaps 913: if (c && n->child)
1.89 schwarze 914: print_man_nodelist(p, mt, n->child, meta);
1.1 kristaps 915:
1.61 schwarze 916: if (termacts[n->tok].post)
1.89 schwarze 917: (*termacts[n->tok].post)(p, mt, n, meta);
1.61 schwarze 918: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
919: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 920:
1.84 schwarze 921: out:
922: /*
923: * If we're in a literal context, make sure that words
924: * together on the same line stay together. This is a
925: * POST-printing call, so we check the NEXT word. Since
926: * -man doesn't have nested macros, we don't need to be
927: * more specific than this.
928: */
1.110 schwarze 929: if (mt->fl & MANT_LITERAL &&
930: ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1.141 schwarze 931: (n->next == NULL || n->next->flags & NODE_LINE)) {
1.84 schwarze 932: rm = p->rmargin;
933: rmax = p->maxrmargin;
934: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
935: p->flags |= TERMP_NOSPACE;
1.110 schwarze 936: if (n->string != NULL && *n->string != '\0')
1.84 schwarze 937: term_flushln(p);
938: else
939: term_newln(p);
940: if (rm < rmax && n->parent->tok == MAN_HP) {
941: p->offset = rm;
942: p->rmargin = rmax;
943: } else
944: p->rmargin = rm;
945: p->maxrmargin = rmax;
946: }
1.141 schwarze 947: if (NODE_EOS & n->flags)
1.30 schwarze 948: p->flags |= TERMP_SENTENCE;
1.1 kristaps 949: }
950:
951:
952: static void
1.21 schwarze 953: print_man_nodelist(DECL_ARGS)
1.1 kristaps 954: {
1.11 schwarze 955:
1.121 schwarze 956: while (n != NULL) {
957: print_man_node(p, mt, n, meta);
958: n = n->next;
959: }
1.1 kristaps 960: }
961:
962: static void
1.126 schwarze 963: print_man_foot(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 964: {
1.101 schwarze 965: char *title;
1.109 schwarze 966: size_t datelen, titlen;
1.41 schwarze 967:
1.80 schwarze 968: assert(meta->title);
969: assert(meta->msec);
970: assert(meta->date);
1.21 schwarze 971:
972: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 973:
1.103 schwarze 974: if (meta->hasbody)
975: term_vspace(p);
1.81 schwarze 976:
977: /*
978: * Temporary, undocumented option to imitate mdoc(7) output.
1.126 schwarze 979: * In the bottom right corner, use the operating system
980: * instead of the title.
1.81 schwarze 981: */
982:
1.79 schwarze 983: if ( ! p->mdocstyle) {
1.103 schwarze 984: if (meta->hasbody) {
985: term_vspace(p);
986: term_vspace(p);
987: }
1.101 schwarze 988: mandoc_asprintf(&title, "%s(%s)",
989: meta->title, meta->msec);
1.126 schwarze 990: } else if (meta->os) {
991: title = mandoc_strdup(meta->os);
1.79 schwarze 992: } else {
1.101 schwarze 993: title = mandoc_strdup("");
1.79 schwarze 994: }
1.78 schwarze 995: datelen = term_strlen(p, meta->date);
1.1 kristaps 996:
1.126 schwarze 997: /* Bottom left corner: operating system. */
1.81 schwarze 998:
1.1 kristaps 999: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.93 schwarze 1000: p->trailspace = 1;
1.1 kristaps 1001: p->offset = 0;
1.109 schwarze 1002: p->rmargin = p->maxrmargin > datelen ?
1003: (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1.1 kristaps 1004:
1.126 schwarze 1005: if (meta->os)
1006: term_word(p, meta->os);
1.1 kristaps 1007: term_flushln(p);
1008:
1.81 schwarze 1009: /* At the bottom in the middle: manual date. */
1010:
1.109 schwarze 1011: p->offset = p->rmargin;
1012: titlen = term_strlen(p, title);
1013: p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
1.72 schwarze 1014: p->flags |= TERMP_NOSPACE;
1.78 schwarze 1015:
1016: term_word(p, meta->date);
1017: term_flushln(p);
1018:
1.81 schwarze 1019: /* Bottom right corner: manual title and section. */
1020:
1.78 schwarze 1021: p->flags &= ~TERMP_NOBREAK;
1022: p->flags |= TERMP_NOSPACE;
1.93 schwarze 1023: p->trailspace = 0;
1.78 schwarze 1024: p->offset = p->rmargin;
1.1 kristaps 1025: p->rmargin = p->maxrmargin;
1026:
1.78 schwarze 1027: term_word(p, title);
1.1 kristaps 1028: term_flushln(p);
1.101 schwarze 1029: free(title);
1.1 kristaps 1030: }
1031:
1032: static void
1.126 schwarze 1033: print_man_head(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1034: {
1.102 schwarze 1035: const char *volume;
1.101 schwarze 1036: char *title;
1.102 schwarze 1037: size_t vollen, titlen;
1.41 schwarze 1038:
1.89 schwarze 1039: assert(meta->title);
1040: assert(meta->msec);
1.1 kristaps 1041:
1.102 schwarze 1042: volume = NULL == meta->vol ? "" : meta->vol;
1043: vollen = term_strlen(p, volume);
1.1 kristaps 1044:
1.81 schwarze 1045: /* Top left corner: manual title and section. */
1046:
1.101 schwarze 1047: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.42 schwarze 1048: titlen = term_strlen(p, title);
1.1 kristaps 1049:
1.73 schwarze 1050: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.93 schwarze 1051: p->trailspace = 1;
1.1 kristaps 1052: p->offset = 0;
1.102 schwarze 1053: p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1054: (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.109 schwarze 1055: vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1.1 kristaps 1056:
1057: term_word(p, title);
1058: term_flushln(p);
1059:
1.81 schwarze 1060: /* At the top in the middle: manual volume. */
1061:
1.72 schwarze 1062: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1063: p->offset = p->rmargin;
1.102 schwarze 1064: p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1.25 schwarze 1065: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1066:
1.102 schwarze 1067: term_word(p, volume);
1.1 kristaps 1068: term_flushln(p);
1069:
1.81 schwarze 1070: /* Top right corner: title and section, again. */
1071:
1.1 kristaps 1072: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 1073: p->trailspace = 0;
1.25 schwarze 1074: if (p->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1075: p->flags |= TERMP_NOSPACE;
1.25 schwarze 1076: p->offset = p->rmargin;
1077: p->rmargin = p->maxrmargin;
1078: term_word(p, title);
1079: term_flushln(p);
1080: }
1.1 kristaps 1081:
1.73 schwarze 1082: p->flags &= ~TERMP_NOSPACE;
1083: p->offset = 0;
1.1 kristaps 1084: p->rmargin = p->maxrmargin;
1.29 schwarze 1085:
1.100 schwarze 1086: /*
1.81 schwarze 1087: * Groff prints three blank lines before the content.
1088: * Do the same, except in the temporary, undocumented
1089: * mode imitating mdoc(7) output.
1.29 schwarze 1090: */
1091:
1092: term_vspace(p);
1.79 schwarze 1093: if ( ! p->mdocstyle) {
1094: term_vspace(p);
1095: term_vspace(p);
1096: }
1.101 schwarze 1097: free(title);
1.1 kristaps 1098: }