Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.179
1.179 ! schwarze 1: /* $OpenBSD: man_term.c,v 1.178 2018/12/31 11:01:34 schwarze Exp $ */
1.1 kristaps 2: /*
1.84 schwarze 3: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.179 ! schwarze 4: * Copyright (c) 2010-2015, 2017-2019 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"
28: #include "roff.h"
29: #include "man.h"
1.18 schwarze 30: #include "out.h"
1.1 kristaps 31: #include "term.h"
1.18 schwarze 32: #include "main.h"
1.10 schwarze 33:
1.70 schwarze 34: #define MAXMARGINS 64 /* maximum number of indented scopes */
1.1 kristaps 35:
1.11 schwarze 36: struct mtermp {
1.116 schwarze 37: int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
1.70 schwarze 38: int lmargincur; /* index of current margin */
39: int lmarginsz; /* actual number of nested margins */
40: size_t offset; /* default offset to visible page */
1.88 schwarze 41: int pardist; /* vert. space before par., unit: [v] */
1.11 schwarze 42: };
43:
1.100 schwarze 44: #define DECL_ARGS struct termp *p, \
1.11 schwarze 45: struct mtermp *mt, \
1.125 schwarze 46: struct roff_node *n, \
1.126 schwarze 47: const struct roff_meta *meta
1.1 kristaps 48:
1.167 schwarze 49: struct man_term_act {
1.1 kristaps 50: int (*pre)(DECL_ARGS);
51: void (*post)(DECL_ARGS);
1.26 schwarze 52: int flags;
53: #define MAN_NOTEXT (1 << 0) /* Never has text children. */
1.1 kristaps 54: };
55:
1.21 schwarze 56: static void print_man_nodelist(DECL_ARGS);
1.19 schwarze 57: static void print_man_node(DECL_ARGS);
1.126 schwarze 58: static void print_man_head(struct termp *,
59: const struct roff_meta *);
60: static void print_man_foot(struct termp *,
61: const struct roff_meta *);
1.100 schwarze 62: static void print_bvspace(struct termp *,
1.125 schwarze 63: const struct roff_node *, int);
1.18 schwarze 64:
1.1 kristaps 65: static int pre_B(DECL_ARGS);
1.152 schwarze 66: static int pre_DT(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);
1.168 schwarze 76: static int pre_SY(DECL_ARGS);
1.1 kristaps 77: static int pre_TP(DECL_ARGS);
1.91 schwarze 78: static int pre_UR(DECL_ARGS);
1.174 schwarze 79: static int pre_abort(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.168 schwarze 90: static void post_SY(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.167 schwarze 94: static const struct man_term_act man_term_acts[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 */
1.166 schwarze 99: { pre_TP, post_TP, 0 }, /* TQ */
1.174 schwarze 100: { pre_abort, NULL, 0 }, /* LP */
1.26 schwarze 101: { pre_PP, NULL, 0 }, /* PP */
1.174 schwarze 102: { pre_abort, NULL, 0 }, /* P */
1.26 schwarze 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: { NULL, NULL, 0 }, /* RE */
117: { pre_RS, post_RS, 0 }, /* RS */
1.152 schwarze 118: { pre_DT, 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.168 schwarze 123: { pre_SY, post_SY, 0 }, /* SY */
124: { NULL, NULL, 0 }, /* YS */
1.82 schwarze 125: { pre_OP, NULL, 0 }, /* OP */
1.83 schwarze 126: { pre_literal, NULL, 0 }, /* EX */
127: { pre_literal, NULL, 0 }, /* EE */
1.91 schwarze 128: { pre_UR, post_UR, 0 }, /* UR */
129: { NULL, NULL, 0 }, /* UE */
1.161 bentley 130: { pre_UR, post_UR, 0 }, /* MT */
131: { NULL, NULL, 0 }, /* ME */
1.1 kristaps 132: };
1.167 schwarze 133: static const struct man_term_act *man_term_act(enum roff_tok);
1.11 schwarze 134:
1.1 kristaps 135:
1.167 schwarze 136: static const struct man_term_act *
137: man_term_act(enum roff_tok tok)
138: {
139: assert(tok >= MAN_TH && tok <= MAN_MAX);
140: return man_term_acts + (tok - MAN_TH);
141: }
142:
1.16 schwarze 143: void
1.176 schwarze 144: terminal_man(void *arg, const struct roff_meta *man)
1.1 kristaps 145: {
1.18 schwarze 146: struct termp *p;
1.125 schwarze 147: struct roff_node *n;
1.18 schwarze 148: struct mtermp mt;
1.142 schwarze 149: size_t save_defindent;
1.18 schwarze 150:
151: p = (struct termp *)arg;
1.159 schwarze 152: save_defindent = p->defindent;
153: if (p->synopsisonly == 0 && p->defindent == 0)
154: p->defindent = 7;
1.156 schwarze 155: p->tcol->rmargin = p->maxrmargin = p->defrmargin;
1.151 schwarze 156: term_tab_set(p, NULL);
157: term_tab_set(p, "T");
158: term_tab_set(p, ".5i");
1.18 schwarze 159:
1.70 schwarze 160: memset(&mt, 0, sizeof(struct mtermp));
1.77 schwarze 161: mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
162: mt.offset = term_len(p, p->defindent);
1.88 schwarze 163: mt.pardist = 1;
1.11 schwarze 164:
1.134 schwarze 165: n = man->first->child;
1.104 schwarze 166: if (p->synopsisonly) {
167: while (n != NULL) {
168: if (n->tok == MAN_SH &&
1.124 schwarze 169: n->child->child->type == ROFFT_TEXT &&
1.104 schwarze 170: !strcmp(n->child->child->string, "SYNOPSIS")) {
171: if (n->child->next->child != NULL)
172: print_man_nodelist(p, &mt,
1.176 schwarze 173: n->child->next->child, man);
1.104 schwarze 174: term_newln(p);
175: break;
176: }
177: n = n->next;
178: }
179: } else {
1.176 schwarze 180: term_begin(p, print_man_head, print_man_foot, man);
1.104 schwarze 181: p->flags |= TERMP_NOSPACE;
182: if (n != NULL)
1.176 schwarze 183: print_man_nodelist(p, &mt, n, man);
1.104 schwarze 184: term_end(p);
185: }
1.159 schwarze 186: p->defindent = save_defindent;
1.1 kristaps 187: }
188:
1.69 schwarze 189: /*
190: * Printing leading vertical space before a block.
191: * This is used for the paragraph macros.
192: * The rules are pretty simple, since there's very little nesting going
193: * on here. Basically, if we're the first within another block (SS/SH),
194: * then don't emit vertical space. If we are (RS), then do. If not the
195: * first, print it.
196: */
1.18 schwarze 197: static void
1.125 schwarze 198: print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
1.18 schwarze 199: {
1.88 schwarze 200: int i;
1.69 schwarze 201:
1.18 schwarze 202: term_newln(p);
1.64 schwarze 203:
1.69 schwarze 204: if (n->body && n->body->child)
1.124 schwarze 205: if (n->body->child->type == ROFFT_TBL)
1.69 schwarze 206: return;
1.11 schwarze 207:
1.124 schwarze 208: if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
1.69 schwarze 209: if (NULL == n->prev)
210: return;
1.11 schwarze 211:
1.88 schwarze 212: for (i = 0; i < pardist; i++)
213: term_vspace(p);
1.14 schwarze 214: }
215:
1.174 schwarze 216:
217: static int
218: pre_abort(DECL_ARGS)
219: {
220: abort();
221: }
1.100 schwarze 222:
1.14 schwarze 223: static int
224: pre_ign(DECL_ARGS)
225: {
226:
1.138 schwarze 227: return 0;
1.11 schwarze 228: }
229:
1.1 kristaps 230: static int
231: pre_I(DECL_ARGS)
232: {
233:
1.21 schwarze 234: term_fontrepl(p, TERMFONT_UNDER);
1.138 schwarze 235: return 1;
1.1 kristaps 236: }
237:
1.11 schwarze 238: static int
1.45 schwarze 239: pre_literal(DECL_ARGS)
1.11 schwarze 240: {
1.45 schwarze 241: term_newln(p);
1.53 schwarze 242:
1.72 schwarze 243: /*
244: * Unlike .IP and .TP, .HP does not have a HEAD.
245: * So in case a second call to term_flushln() is needed,
246: * indentation has to be set up explicitly.
247: */
1.156 schwarze 248: if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) {
249: p->tcol->offset = p->tcol->rmargin;
250: p->tcol->rmargin = p->maxrmargin;
1.93 schwarze 251: p->trailspace = 0;
1.99 schwarze 252: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.72 schwarze 253: p->flags |= TERMP_NOSPACE;
254: }
1.138 schwarze 255: return 0;
1.11 schwarze 256: }
257:
258: static int
1.88 schwarze 259: pre_PD(DECL_ARGS)
260: {
1.113 schwarze 261: struct roffsu su;
1.88 schwarze 262:
263: n = n->child;
1.113 schwarze 264: if (n == NULL) {
1.88 schwarze 265: mt->pardist = 1;
1.138 schwarze 266: return 0;
1.88 schwarze 267: }
1.124 schwarze 268: assert(n->type == ROFFT_TEXT);
1.157 schwarze 269: if (a2roffsu(n->string, &su, SCALE_VS) != NULL)
1.113 schwarze 270: mt->pardist = term_vspan(p, &su);
1.138 schwarze 271: return 0;
1.88 schwarze 272: }
273:
274: static int
1.51 schwarze 275: pre_alternate(DECL_ARGS)
1.1 kristaps 276: {
1.51 schwarze 277: enum termfont font[2];
1.125 schwarze 278: struct roff_node *nn;
1.178 schwarze 279: int i;
1.1 kristaps 280:
1.51 schwarze 281: switch (n->tok) {
1.100 schwarze 282: case MAN_RB:
1.51 schwarze 283: font[0] = TERMFONT_NONE;
284: font[1] = TERMFONT_BOLD;
285: break;
1.100 schwarze 286: case MAN_RI:
1.51 schwarze 287: font[0] = TERMFONT_NONE;
288: font[1] = TERMFONT_UNDER;
289: break;
1.100 schwarze 290: case MAN_BR:
1.51 schwarze 291: font[0] = TERMFONT_BOLD;
292: font[1] = TERMFONT_NONE;
293: break;
1.100 schwarze 294: case MAN_BI:
1.51 schwarze 295: font[0] = TERMFONT_BOLD;
296: font[1] = TERMFONT_UNDER;
297: break;
1.100 schwarze 298: case MAN_IR:
1.51 schwarze 299: font[0] = TERMFONT_UNDER;
300: font[1] = TERMFONT_NONE;
301: break;
1.100 schwarze 302: case MAN_IB:
1.51 schwarze 303: font[0] = TERMFONT_UNDER;
304: font[1] = TERMFONT_BOLD;
305: break;
306: default:
307: abort();
308: }
1.178 schwarze 309: for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) {
1.51 schwarze 310: term_fontrepl(p, font[i]);
1.132 schwarze 311: assert(nn->type == ROFFT_TEXT);
312: term_word(p, nn->string);
1.141 schwarze 313: if (nn->flags & NODE_EOS)
1.132 schwarze 314: p->flags |= TERMP_SENTENCE;
1.51 schwarze 315: if (nn->next)
1.1 kristaps 316: p->flags |= TERMP_NOSPACE;
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, "[");
1.169 schwarze 334: p->flags |= TERMP_KEEP | TERMP_NOSPACE;
1.82 schwarze 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);
1.169 schwarze 346: p->flags &= ~TERMP_KEEP;
1.82 schwarze 347: p->flags |= TERMP_NOSPACE;
348: term_word(p, "]");
1.138 schwarze 349: return 0;
1.1 kristaps 350: }
351:
352: static int
1.45 schwarze 353: pre_in(DECL_ARGS)
1.11 schwarze 354: {
1.116 schwarze 355: struct roffsu su;
356: const char *cp;
1.45 schwarze 357: size_t v;
1.116 schwarze 358: int less;
1.45 schwarze 359:
360: term_newln(p);
361:
1.156 schwarze 362: if (n->child == NULL) {
363: p->tcol->offset = mt->offset;
1.138 schwarze 364: return 0;
1.45 schwarze 365: }
1.11 schwarze 366:
1.45 schwarze 367: cp = n->child->string;
368: less = 0;
1.11 schwarze 369:
1.45 schwarze 370: if ('-' == *cp)
371: less = -1;
372: else if ('+' == *cp)
373: less = 1;
374: else
375: cp--;
376:
1.157 schwarze 377: if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
1.138 schwarze 378: return 0;
1.45 schwarze 379:
1.158 schwarze 380: v = term_hen(p, &su);
1.45 schwarze 381:
382: if (less < 0)
1.156 schwarze 383: p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
1.45 schwarze 384: else if (less > 0)
1.156 schwarze 385: p->tcol->offset += v;
1.100 schwarze 386: else
1.156 schwarze 387: p->tcol->offset = v;
388: if (p->tcol->offset > SHRT_MAX)
389: p->tcol->offset = term_len(p, p->defindent);
1.8 schwarze 390:
1.152 schwarze 391: return 0;
392: }
393:
394: static int
395: pre_DT(DECL_ARGS)
396: {
397: term_tab_set(p, NULL);
398: term_tab_set(p, "T");
399: term_tab_set(p, ".5i");
1.138 schwarze 400: return 0;
1.8 schwarze 401: }
402:
403: static int
1.11 schwarze 404: pre_HP(DECL_ARGS)
405: {
1.116 schwarze 406: struct roffsu su;
1.125 schwarze 407: const struct roff_node *nn;
1.116 schwarze 408: int len;
1.11 schwarze 409:
410: switch (n->type) {
1.124 schwarze 411: case ROFFT_BLOCK:
1.88 schwarze 412: print_bvspace(p, n, mt->pardist);
1.138 schwarze 413: return 1;
1.124 schwarze 414: case ROFFT_BODY:
1.11 schwarze 415: break;
416: default:
1.138 schwarze 417: return 0;
1.11 schwarze 418: }
419:
1.179 ! schwarze 420: if (n->child == NULL)
! 421: return 0;
! 422:
! 423: if ((n->child->flags & NODE_NOFILL) == 0) {
1.99 schwarze 424: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.93 schwarze 425: p->trailspace = 2;
1.84 schwarze 426: }
427:
1.11 schwarze 428: /* Calculate offset. */
429:
1.116 schwarze 430: if ((nn = n->parent->head->child) != NULL &&
1.157 schwarze 431: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.158 schwarze 432: len = term_hen(p, &su);
1.117 schwarze 433: if (len < 0 && (size_t)(-len) > mt->offset)
434: len = -mt->offset;
435: else if (len > SHRT_MAX)
436: len = term_len(p, p->defindent);
1.116 schwarze 437: mt->lmargin[mt->lmargincur] = len;
438: } else
439: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 440:
1.156 schwarze 441: p->tcol->offset = mt->offset;
442: p->tcol->rmargin = mt->offset + len;
1.138 schwarze 443: return 1;
1.11 schwarze 444: }
445:
446: static void
447: post_HP(DECL_ARGS)
448: {
449:
450: switch (n->type) {
1.124 schwarze 451: case ROFFT_BODY:
1.90 schwarze 452: term_newln(p);
1.127 schwarze 453:
454: /*
455: * Compatibility with a groff bug.
456: * The .HP macro uses the undocumented .tag request
457: * which causes a line break and cancels no-space
458: * mode even if there isn't any output.
459: */
460:
461: if (n->child == NULL)
462: term_vspace(p);
463:
1.99 schwarze 464: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.93 schwarze 465: p->trailspace = 0;
1.156 schwarze 466: p->tcol->offset = mt->offset;
467: p->tcol->rmargin = p->maxrmargin;
1.11 schwarze 468: break;
469: default:
470: break;
471: }
472: }
473:
474: static int
1.1 kristaps 475: pre_PP(DECL_ARGS)
476: {
477:
1.11 schwarze 478: switch (n->type) {
1.124 schwarze 479: case ROFFT_BLOCK:
1.77 schwarze 480: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.88 schwarze 481: print_bvspace(p, n, mt->pardist);
1.11 schwarze 482: break;
483: default:
1.156 schwarze 484: p->tcol->offset = mt->offset;
1.11 schwarze 485: break;
486: }
487:
1.138 schwarze 488: return n->type != ROFFT_HEAD;
1.1 kristaps 489: }
490:
491: static int
492: pre_IP(DECL_ARGS)
493: {
1.116 schwarze 494: struct roffsu su;
1.125 schwarze 495: const struct roff_node *nn;
1.178 schwarze 496: int len;
1.11 schwarze 497:
498: switch (n->type) {
1.124 schwarze 499: case ROFFT_BODY:
1.11 schwarze 500: p->flags |= TERMP_NOSPACE;
501: break;
1.124 schwarze 502: case ROFFT_HEAD:
1.11 schwarze 503: p->flags |= TERMP_NOBREAK;
1.93 schwarze 504: p->trailspace = 1;
1.11 schwarze 505: break;
1.124 schwarze 506: case ROFFT_BLOCK:
1.88 schwarze 507: print_bvspace(p, n, mt->pardist);
1.11 schwarze 508: /* FALLTHROUGH */
509: default:
1.138 schwarze 510: return 1;
1.11 schwarze 511: }
512:
1.57 schwarze 513: /* Calculate the offset from the optional second argument. */
1.116 schwarze 514: if ((nn = n->parent->head->child) != NULL &&
515: (nn = nn->next) != NULL &&
1.157 schwarze 516: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.158 schwarze 517: len = term_hen(p, &su);
1.116 schwarze 518: if (len < 0 && (size_t)(-len) > mt->offset)
519: len = -mt->offset;
1.117 schwarze 520: else if (len > SHRT_MAX)
521: len = term_len(p, p->defindent);
522: mt->lmargin[mt->lmargincur] = len;
1.116 schwarze 523: } else
524: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 525:
526: switch (n->type) {
1.124 schwarze 527: case ROFFT_HEAD:
1.156 schwarze 528: p->tcol->offset = mt->offset;
529: p->tcol->rmargin = mt->offset + len;
1.57 schwarze 530: if (n->child)
1.89 schwarze 531: print_man_node(p, mt, n->child, meta);
1.138 schwarze 532: return 0;
1.124 schwarze 533: case ROFFT_BODY:
1.156 schwarze 534: p->tcol->offset = mt->offset + len;
535: p->tcol->rmargin = p->maxrmargin;
1.11 schwarze 536: break;
537: default:
538: break;
539: }
1.138 schwarze 540: return 1;
1.11 schwarze 541: }
1.1 kristaps 542:
1.11 schwarze 543: static void
544: post_IP(DECL_ARGS)
545: {
546: switch (n->type) {
1.124 schwarze 547: case ROFFT_HEAD:
1.11 schwarze 548: term_flushln(p);
549: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 550: p->trailspace = 0;
1.156 schwarze 551: p->tcol->rmargin = p->maxrmargin;
1.11 schwarze 552: break;
1.124 schwarze 553: case ROFFT_BODY:
1.57 schwarze 554: term_newln(p);
1.156 schwarze 555: p->tcol->offset = mt->offset;
1.11 schwarze 556: break;
557: default:
558: break;
559: }
1.1 kristaps 560: }
561:
562: static int
563: pre_TP(DECL_ARGS)
564: {
1.116 schwarze 565: struct roffsu su;
1.125 schwarze 566: struct roff_node *nn;
1.178 schwarze 567: int len;
1.11 schwarze 568:
569: switch (n->type) {
1.124 schwarze 570: case ROFFT_HEAD:
1.137 schwarze 571: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
1.93 schwarze 572: p->trailspace = 1;
1.11 schwarze 573: break;
1.124 schwarze 574: case ROFFT_BODY:
1.11 schwarze 575: p->flags |= TERMP_NOSPACE;
576: break;
1.124 schwarze 577: case ROFFT_BLOCK:
1.166 schwarze 578: if (n->tok == MAN_TP)
579: print_bvspace(p, n, mt->pardist);
1.11 schwarze 580: /* FALLTHROUGH */
581: default:
1.138 schwarze 582: return 1;
1.11 schwarze 583: }
584:
585: /* Calculate offset. */
1.1 kristaps 586:
1.116 schwarze 587: if ((nn = n->parent->head->child) != NULL &&
1.141 schwarze 588: nn->string != NULL && ! (NODE_LINE & nn->flags) &&
1.157 schwarze 589: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.158 schwarze 590: len = term_hen(p, &su);
1.116 schwarze 591: if (len < 0 && (size_t)(-len) > mt->offset)
592: len = -mt->offset;
1.117 schwarze 593: else if (len > SHRT_MAX)
594: len = term_len(p, p->defindent);
595: mt->lmargin[mt->lmargincur] = len;
1.116 schwarze 596: } else
597: len = mt->lmargin[mt->lmargincur];
1.8 schwarze 598:
1.11 schwarze 599: switch (n->type) {
1.124 schwarze 600: case ROFFT_HEAD:
1.156 schwarze 601: p->tcol->offset = mt->offset;
602: p->tcol->rmargin = mt->offset + len;
1.11 schwarze 603:
604: /* Don't print same-line elements. */
1.95 schwarze 605: nn = n->child;
1.141 schwarze 606: while (NULL != nn && 0 == (NODE_LINE & nn->flags))
1.95 schwarze 607: nn = nn->next;
608:
609: while (NULL != nn) {
610: print_man_node(p, mt, nn, meta);
611: nn = nn->next;
612: }
1.138 schwarze 613: return 0;
1.124 schwarze 614: case ROFFT_BODY:
1.156 schwarze 615: p->tcol->offset = mt->offset + len;
616: p->tcol->rmargin = p->maxrmargin;
1.93 schwarze 617: p->trailspace = 0;
1.137 schwarze 618: p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
1.11 schwarze 619: break;
620: default:
621: break;
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: {
629: switch (n->type) {
1.124 schwarze 630: case ROFFT_HEAD:
1.11 schwarze 631: term_flushln(p);
632: break;
1.124 schwarze 633: case ROFFT_BODY:
1.57 schwarze 634: term_newln(p);
1.156 schwarze 635: p->tcol->offset = mt->offset;
1.11 schwarze 636: break;
637: default:
638: break;
639: }
1.1 kristaps 640: }
641:
642: static int
643: pre_SS(DECL_ARGS)
644: {
1.88 schwarze 645: int i;
1.1 kristaps 646:
1.11 schwarze 647: switch (n->type) {
1.124 schwarze 648: case ROFFT_BLOCK:
1.77 schwarze 649: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
650: mt->offset = term_len(p, p->defindent);
1.111 schwarze 651:
652: /*
653: * No vertical space before the first subsection
654: * and after an empty subsection.
655: */
656:
657: do {
658: n = n->prev;
1.162 schwarze 659: } while (n != NULL && n->tok >= MAN_TH &&
1.167 schwarze 660: man_term_act(n->tok)->flags & MAN_NOTEXT);
1.163 schwarze 661: if (n == NULL || n->type == ROFFT_COMMENT ||
662: (n->tok == MAN_SS && n->body->child == NULL))
1.11 schwarze 663: break;
1.111 schwarze 664:
1.88 schwarze 665: for (i = 0; i < mt->pardist; i++)
666: term_vspace(p);
1.11 schwarze 667: break;
1.124 schwarze 668: case ROFFT_HEAD:
1.21 schwarze 669: term_fontrepl(p, TERMFONT_BOLD);
1.156 schwarze 670: p->tcol->offset = term_len(p, 3);
671: p->tcol->rmargin = mt->offset;
1.129 schwarze 672: p->trailspace = mt->offset;
673: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 674: break;
1.124 schwarze 675: case ROFFT_BODY:
1.156 schwarze 676: p->tcol->offset = mt->offset;
677: p->tcol->rmargin = p->maxrmargin;
1.129 schwarze 678: p->trailspace = 0;
679: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 680: break;
681: default:
682: break;
683: }
684:
1.138 schwarze 685: return 1;
1.1 kristaps 686: }
687:
688: static void
689: post_SS(DECL_ARGS)
690: {
1.100 schwarze 691:
1.11 schwarze 692: switch (n->type) {
1.124 schwarze 693: case ROFFT_HEAD:
1.11 schwarze 694: term_newln(p);
695: break;
1.124 schwarze 696: case ROFFT_BODY:
1.11 schwarze 697: term_newln(p);
698: break;
699: default:
700: break;
701: }
1.1 kristaps 702: }
703:
704: static int
705: pre_SH(DECL_ARGS)
706: {
1.88 schwarze 707: int i;
1.1 kristaps 708:
1.11 schwarze 709: switch (n->type) {
1.124 schwarze 710: case ROFFT_BLOCK:
1.77 schwarze 711: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
712: mt->offset = term_len(p, p->defindent);
1.111 schwarze 713:
714: /*
715: * No vertical space before the first section
716: * and after an empty section.
717: */
718:
719: do {
720: n = n->prev;
1.162 schwarze 721: } while (n != NULL && n->tok >= MAN_TH &&
1.167 schwarze 722: man_term_act(n->tok)->flags & MAN_NOTEXT);
1.163 schwarze 723: if (n == NULL || n->type == ROFFT_COMMENT ||
724: (n->tok == MAN_SH && n->body->child == NULL))
1.29 schwarze 725: break;
1.111 schwarze 726:
1.88 schwarze 727: for (i = 0; i < mt->pardist; i++)
728: term_vspace(p);
1.11 schwarze 729: break;
1.124 schwarze 730: case ROFFT_HEAD:
1.21 schwarze 731: term_fontrepl(p, TERMFONT_BOLD);
1.156 schwarze 732: p->tcol->offset = 0;
733: p->tcol->rmargin = mt->offset;
1.129 schwarze 734: p->trailspace = mt->offset;
735: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 736: break;
1.124 schwarze 737: case ROFFT_BODY:
1.156 schwarze 738: p->tcol->offset = mt->offset;
739: p->tcol->rmargin = p->maxrmargin;
1.129 schwarze 740: p->trailspace = 0;
741: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 742: break;
743: default:
744: break;
745: }
746:
1.138 schwarze 747: return 1;
1.1 kristaps 748: }
749:
750: static void
751: post_SH(DECL_ARGS)
752: {
1.100 schwarze 753:
1.11 schwarze 754: switch (n->type) {
1.124 schwarze 755: case ROFFT_HEAD:
1.11 schwarze 756: term_newln(p);
757: break;
1.124 schwarze 758: case ROFFT_BODY:
1.11 schwarze 759: term_newln(p);
760: break;
761: default:
1.13 schwarze 762: break;
763: }
764: }
765:
766: static int
767: pre_RS(DECL_ARGS)
768: {
1.116 schwarze 769: struct roffsu su;
1.13 schwarze 770:
771: switch (n->type) {
1.124 schwarze 772: case ROFFT_BLOCK:
1.13 schwarze 773: term_newln(p);
1.138 schwarze 774: return 1;
1.124 schwarze 775: case ROFFT_HEAD:
1.138 schwarze 776: return 0;
1.13 schwarze 777: default:
778: break;
779: }
780:
1.118 schwarze 781: n = n->parent->head;
782: n->aux = SHRT_MAX + 1;
1.130 schwarze 783: if (n->child == NULL)
784: n->aux = mt->lmargin[mt->lmargincur];
1.157 schwarze 785: else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
1.158 schwarze 786: n->aux = term_hen(p, &su);
1.118 schwarze 787: if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
788: n->aux = -mt->offset;
789: else if (n->aux > SHRT_MAX)
790: n->aux = term_len(p, p->defindent);
1.13 schwarze 791:
1.118 schwarze 792: mt->offset += n->aux;
1.156 schwarze 793: p->tcol->offset = mt->offset;
794: p->tcol->rmargin = p->maxrmargin;
1.13 schwarze 795:
1.70 schwarze 796: if (++mt->lmarginsz < MAXMARGINS)
797: mt->lmargincur = mt->lmarginsz;
798:
1.131 schwarze 799: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.138 schwarze 800: return 1;
1.13 schwarze 801: }
802:
803: static void
804: post_RS(DECL_ARGS)
805: {
806:
807: switch (n->type) {
1.124 schwarze 808: case ROFFT_BLOCK:
1.69 schwarze 809: return;
1.124 schwarze 810: case ROFFT_HEAD:
1.69 schwarze 811: return;
1.13 schwarze 812: default:
813: term_newln(p);
1.11 schwarze 814: break;
815: }
1.69 schwarze 816:
1.118 schwarze 817: mt->offset -= n->parent->head->aux;
1.156 schwarze 818: p->tcol->offset = mt->offset;
1.70 schwarze 819:
820: if (--mt->lmarginsz < MAXMARGINS)
821: mt->lmargincur = mt->lmarginsz;
1.168 schwarze 822: }
823:
824: static int
825: pre_SY(DECL_ARGS)
826: {
827: const struct roff_node *nn;
828: int len;
829:
830: switch (n->type) {
831: case ROFFT_BLOCK:
1.170 schwarze 832: if (n->prev == NULL || n->prev->tok != MAN_SY)
833: print_bvspace(p, n, mt->pardist);
1.168 schwarze 834: return 1;
835: case ROFFT_HEAD:
836: case ROFFT_BODY:
837: break;
838: default:
839: abort();
840: }
841:
842: nn = n->parent->head->child;
1.172 schwarze 843: len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
1.168 schwarze 844:
845: switch (n->type) {
846: case ROFFT_HEAD:
847: p->tcol->offset = mt->offset;
848: p->tcol->rmargin = mt->offset + len;
849: p->flags |= TERMP_NOBREAK;
850: term_fontrepl(p, TERMFONT_BOLD);
851: break;
852: case ROFFT_BODY:
853: mt->lmargin[mt->lmargincur] = len;
854: p->tcol->offset = mt->offset + len;
855: p->tcol->rmargin = p->maxrmargin;
856: p->flags |= TERMP_NOSPACE;
857: break;
858: default:
859: abort();
860: }
861: return 1;
862: }
863:
864: static void
865: post_SY(DECL_ARGS)
866: {
867: switch (n->type) {
868: case ROFFT_HEAD:
869: term_flushln(p);
870: p->flags &= ~TERMP_NOBREAK;
871: break;
872: case ROFFT_BODY:
873: term_newln(p);
874: p->tcol->offset = mt->offset;
875: break;
876: default:
877: break;
878: }
1.91 schwarze 879: }
880:
881: static int
882: pre_UR(DECL_ARGS)
883: {
884:
1.138 schwarze 885: return n->type != ROFFT_HEAD;
1.91 schwarze 886: }
887:
888: static void
889: post_UR(DECL_ARGS)
890: {
891:
1.124 schwarze 892: if (n->type != ROFFT_BLOCK)
1.91 schwarze 893: return;
894:
895: term_word(p, "<");
896: p->flags |= TERMP_NOSPACE;
897:
898: if (NULL != n->child->child)
899: print_man_node(p, mt, n->child->child, meta);
900:
901: p->flags |= TERMP_NOSPACE;
902: term_word(p, ">");
1.47 schwarze 903: }
904:
1.1 kristaps 905: static void
1.19 schwarze 906: print_man_node(DECL_ARGS)
1.1 kristaps 907: {
1.167 schwarze 908: const struct man_term_act *act;
909: int c;
1.1 kristaps 910:
911: switch (n->type) {
1.124 schwarze 912: case ROFFT_TEXT:
1.61 schwarze 913: /*
914: * If we have a blank line, output a vertical space.
915: * If we have a space as the first character, break
916: * before printing the line's data.
917: */
1.153 schwarze 918: if (*n->string == '\0') {
1.160 schwarze 919: if (p->flags & TERMP_NONEWLINE)
920: term_newln(p);
921: else
922: term_vspace(p);
1.61 schwarze 923: return;
1.153 schwarze 924: } else if (*n->string == ' ' && n->flags & NODE_LINE &&
925: (p->flags & TERMP_NONEWLINE) == 0)
1.60 schwarze 926: term_newln(p);
1.165 schwarze 927: else if (n->flags & NODE_DELIMC)
928: p->flags |= TERMP_NOSPACE;
1.21 schwarze 929:
1.1 kristaps 930: term_word(p, n->string);
1.84 schwarze 931: goto out;
1.163 schwarze 932: case ROFFT_COMMENT:
933: return;
1.124 schwarze 934: case ROFFT_EQN:
1.141 schwarze 935: if ( ! (n->flags & NODE_LINE))
1.105 schwarze 936: p->flags |= TERMP_NOSPACE;
1.71 schwarze 937: term_eqn(p, n->eqn);
1.141 schwarze 938: if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.106 schwarze 939: p->flags |= TERMP_NOSPACE;
1.61 schwarze 940: return;
1.124 schwarze 941: case ROFFT_TBL:
1.122 schwarze 942: if (p->tbl.cols == NULL)
943: term_vspace(p);
1.58 schwarze 944: term_tbl(p, n->span);
1.61 schwarze 945: return;
1.1 kristaps 946: default:
947: break;
948: }
1.177 schwarze 949:
1.146 schwarze 950: if (n->tok < ROFF_MAX) {
1.147 schwarze 951: roff_term_pre(p, n);
1.146 schwarze 952: return;
953: }
954:
1.167 schwarze 955: act = man_term_act(n->tok);
1.173 schwarze 956: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.61 schwarze 957: term_fontrepl(p, TERMFONT_NONE);
958:
959: c = 1;
1.167 schwarze 960: if (act->pre != NULL)
961: c = (*act->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.167 schwarze 966: if (act->post != NULL)
967: (*act->post)(p, mt, n, meta);
1.173 schwarze 968: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.61 schwarze 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.178 schwarze 979: if (n->flags & NODE_NOFILL &&
1.110 schwarze 980: ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1.141 schwarze 981: (n->next == NULL || n->next->flags & NODE_LINE)) {
1.155 schwarze 982: p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
1.110 schwarze 983: if (n->string != NULL && *n->string != '\0')
1.84 schwarze 984: term_flushln(p);
985: else
986: term_newln(p);
1.155 schwarze 987: p->flags &= ~TERMP_BRNEVER;
1.156 schwarze 988: if (p->tcol->rmargin < p->maxrmargin &&
989: n->parent->tok == MAN_HP) {
990: p->tcol->offset = p->tcol->rmargin;
991: p->tcol->rmargin = p->maxrmargin;
1.155 schwarze 992: }
1.84 schwarze 993: }
1.141 schwarze 994: if (NODE_EOS & n->flags)
1.30 schwarze 995: p->flags |= TERMP_SENTENCE;
1.1 kristaps 996: }
997:
998:
999: static void
1.21 schwarze 1000: print_man_nodelist(DECL_ARGS)
1.1 kristaps 1001: {
1.11 schwarze 1002:
1.121 schwarze 1003: while (n != NULL) {
1004: print_man_node(p, mt, n, meta);
1005: n = n->next;
1006: }
1.1 kristaps 1007: }
1008:
1009: static void
1.126 schwarze 1010: print_man_foot(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1011: {
1.101 schwarze 1012: char *title;
1.109 schwarze 1013: size_t datelen, titlen;
1.41 schwarze 1014:
1.80 schwarze 1015: assert(meta->title);
1016: assert(meta->msec);
1017: assert(meta->date);
1.21 schwarze 1018:
1019: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 1020:
1.103 schwarze 1021: if (meta->hasbody)
1022: term_vspace(p);
1.81 schwarze 1023:
1024: /*
1025: * Temporary, undocumented option to imitate mdoc(7) output.
1.126 schwarze 1026: * In the bottom right corner, use the operating system
1027: * instead of the title.
1.81 schwarze 1028: */
1029:
1.79 schwarze 1030: if ( ! p->mdocstyle) {
1.103 schwarze 1031: if (meta->hasbody) {
1032: term_vspace(p);
1033: term_vspace(p);
1034: }
1.101 schwarze 1035: mandoc_asprintf(&title, "%s(%s)",
1036: meta->title, meta->msec);
1.126 schwarze 1037: } else if (meta->os) {
1038: title = mandoc_strdup(meta->os);
1.79 schwarze 1039: } else {
1.101 schwarze 1040: title = mandoc_strdup("");
1.79 schwarze 1041: }
1.78 schwarze 1042: datelen = term_strlen(p, meta->date);
1.1 kristaps 1043:
1.126 schwarze 1044: /* Bottom left corner: operating system. */
1.81 schwarze 1045:
1.1 kristaps 1046: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.93 schwarze 1047: p->trailspace = 1;
1.156 schwarze 1048: p->tcol->offset = 0;
1049: p->tcol->rmargin = p->maxrmargin > datelen ?
1.109 schwarze 1050: (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1.1 kristaps 1051:
1.126 schwarze 1052: if (meta->os)
1053: term_word(p, meta->os);
1.1 kristaps 1054: term_flushln(p);
1055:
1.81 schwarze 1056: /* At the bottom in the middle: manual date. */
1057:
1.156 schwarze 1058: p->tcol->offset = p->tcol->rmargin;
1.109 schwarze 1059: titlen = term_strlen(p, title);
1.156 schwarze 1060: p->tcol->rmargin = p->maxrmargin > titlen ?
1061: p->maxrmargin - titlen : 0;
1.72 schwarze 1062: p->flags |= TERMP_NOSPACE;
1.78 schwarze 1063:
1064: term_word(p, meta->date);
1065: term_flushln(p);
1066:
1.81 schwarze 1067: /* Bottom right corner: manual title and section. */
1068:
1.78 schwarze 1069: p->flags &= ~TERMP_NOBREAK;
1070: p->flags |= TERMP_NOSPACE;
1.93 schwarze 1071: p->trailspace = 0;
1.156 schwarze 1072: p->tcol->offset = p->tcol->rmargin;
1073: p->tcol->rmargin = p->maxrmargin;
1.1 kristaps 1074:
1.78 schwarze 1075: term_word(p, title);
1.1 kristaps 1076: term_flushln(p);
1.164 schwarze 1077:
1078: /*
1079: * Reset the terminal state for more output after the footer:
1080: * Some output modes, in particular PostScript and PDF, print
1081: * the header and the footer into a buffer such that it can be
1082: * reused for multiple output pages, then go on to format the
1083: * main text.
1084: */
1085:
1086: p->tcol->offset = 0;
1087: p->flags = 0;
1088:
1.101 schwarze 1089: free(title);
1.1 kristaps 1090: }
1091:
1092: static void
1.126 schwarze 1093: print_man_head(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1094: {
1.102 schwarze 1095: const char *volume;
1.101 schwarze 1096: char *title;
1.102 schwarze 1097: size_t vollen, titlen;
1.41 schwarze 1098:
1.89 schwarze 1099: assert(meta->title);
1100: assert(meta->msec);
1.1 kristaps 1101:
1.102 schwarze 1102: volume = NULL == meta->vol ? "" : meta->vol;
1103: vollen = term_strlen(p, volume);
1.1 kristaps 1104:
1.81 schwarze 1105: /* Top left corner: manual title and section. */
1106:
1.101 schwarze 1107: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.42 schwarze 1108: titlen = term_strlen(p, title);
1.1 kristaps 1109:
1.73 schwarze 1110: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.93 schwarze 1111: p->trailspace = 1;
1.156 schwarze 1112: p->tcol->offset = 0;
1113: p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1.102 schwarze 1114: (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.109 schwarze 1115: vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1.1 kristaps 1116:
1117: term_word(p, title);
1118: term_flushln(p);
1119:
1.81 schwarze 1120: /* At the top in the middle: manual volume. */
1121:
1.72 schwarze 1122: p->flags |= TERMP_NOSPACE;
1.156 schwarze 1123: p->tcol->offset = p->tcol->rmargin;
1124: p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1125: p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1126:
1.102 schwarze 1127: term_word(p, volume);
1.1 kristaps 1128: term_flushln(p);
1129:
1.81 schwarze 1130: /* Top right corner: title and section, again. */
1131:
1.1 kristaps 1132: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 1133: p->trailspace = 0;
1.156 schwarze 1134: if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1135: p->flags |= TERMP_NOSPACE;
1.156 schwarze 1136: p->tcol->offset = p->tcol->rmargin;
1137: p->tcol->rmargin = p->maxrmargin;
1.25 schwarze 1138: term_word(p, title);
1139: term_flushln(p);
1140: }
1.1 kristaps 1141:
1.73 schwarze 1142: p->flags &= ~TERMP_NOSPACE;
1.156 schwarze 1143: p->tcol->offset = 0;
1144: p->tcol->rmargin = p->maxrmargin;
1.29 schwarze 1145:
1.100 schwarze 1146: /*
1.81 schwarze 1147: * Groff prints three blank lines before the content.
1148: * Do the same, except in the temporary, undocumented
1149: * mode imitating mdoc(7) output.
1.29 schwarze 1150: */
1151:
1152: term_vspace(p);
1.79 schwarze 1153: if ( ! p->mdocstyle) {
1154: term_vspace(p);
1155: term_vspace(p);
1156: }
1.101 schwarze 1157: free(title);
1.1 kristaps 1158: }