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