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