Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.178
1.178 ! schwarze 1: /* $OpenBSD: man_term.c,v 1.177 2018/12/31 07:07:43 schwarze Exp $ */
1.1 kristaps 2: /*
1.84 schwarze 3: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.163 schwarze 4: * Copyright (c) 2010-2015, 2017, 2018 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.178 ! schwarze 420: if ((n->flags & NODE_NOFILL) == 0) {
1.99 schwarze 421: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.93 schwarze 422: p->trailspace = 2;
1.84 schwarze 423: }
424:
1.11 schwarze 425: /* Calculate offset. */
426:
1.116 schwarze 427: if ((nn = n->parent->head->child) != NULL &&
1.157 schwarze 428: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.158 schwarze 429: len = term_hen(p, &su);
1.117 schwarze 430: if (len < 0 && (size_t)(-len) > mt->offset)
431: len = -mt->offset;
432: else if (len > SHRT_MAX)
433: len = term_len(p, p->defindent);
1.116 schwarze 434: mt->lmargin[mt->lmargincur] = len;
435: } else
436: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 437:
1.156 schwarze 438: p->tcol->offset = mt->offset;
439: p->tcol->rmargin = mt->offset + len;
1.138 schwarze 440: return 1;
1.11 schwarze 441: }
442:
443: static void
444: post_HP(DECL_ARGS)
445: {
446:
447: switch (n->type) {
1.124 schwarze 448: case ROFFT_BODY:
1.90 schwarze 449: term_newln(p);
1.127 schwarze 450:
451: /*
452: * Compatibility with a groff bug.
453: * The .HP macro uses the undocumented .tag request
454: * which causes a line break and cancels no-space
455: * mode even if there isn't any output.
456: */
457:
458: if (n->child == NULL)
459: term_vspace(p);
460:
1.99 schwarze 461: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.93 schwarze 462: p->trailspace = 0;
1.156 schwarze 463: p->tcol->offset = mt->offset;
464: p->tcol->rmargin = p->maxrmargin;
1.11 schwarze 465: break;
466: default:
467: break;
468: }
469: }
470:
471: static int
1.1 kristaps 472: pre_PP(DECL_ARGS)
473: {
474:
1.11 schwarze 475: switch (n->type) {
1.124 schwarze 476: case ROFFT_BLOCK:
1.77 schwarze 477: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.88 schwarze 478: print_bvspace(p, n, mt->pardist);
1.11 schwarze 479: break;
480: default:
1.156 schwarze 481: p->tcol->offset = mt->offset;
1.11 schwarze 482: break;
483: }
484:
1.138 schwarze 485: return n->type != ROFFT_HEAD;
1.1 kristaps 486: }
487:
488: static int
489: pre_IP(DECL_ARGS)
490: {
1.116 schwarze 491: struct roffsu su;
1.125 schwarze 492: const struct roff_node *nn;
1.178 ! schwarze 493: int len;
1.11 schwarze 494:
495: switch (n->type) {
1.124 schwarze 496: case ROFFT_BODY:
1.11 schwarze 497: p->flags |= TERMP_NOSPACE;
498: break;
1.124 schwarze 499: case ROFFT_HEAD:
1.11 schwarze 500: p->flags |= TERMP_NOBREAK;
1.93 schwarze 501: p->trailspace = 1;
1.11 schwarze 502: break;
1.124 schwarze 503: case ROFFT_BLOCK:
1.88 schwarze 504: print_bvspace(p, n, mt->pardist);
1.11 schwarze 505: /* FALLTHROUGH */
506: default:
1.138 schwarze 507: return 1;
1.11 schwarze 508: }
509:
1.57 schwarze 510: /* Calculate the offset from the optional second argument. */
1.116 schwarze 511: if ((nn = n->parent->head->child) != NULL &&
512: (nn = nn->next) != NULL &&
1.157 schwarze 513: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.158 schwarze 514: len = term_hen(p, &su);
1.116 schwarze 515: if (len < 0 && (size_t)(-len) > mt->offset)
516: len = -mt->offset;
1.117 schwarze 517: else if (len > SHRT_MAX)
518: len = term_len(p, p->defindent);
519: mt->lmargin[mt->lmargincur] = len;
1.116 schwarze 520: } else
521: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 522:
523: switch (n->type) {
1.124 schwarze 524: case ROFFT_HEAD:
1.156 schwarze 525: p->tcol->offset = mt->offset;
526: p->tcol->rmargin = mt->offset + len;
1.57 schwarze 527: if (n->child)
1.89 schwarze 528: print_man_node(p, mt, n->child, meta);
1.138 schwarze 529: return 0;
1.124 schwarze 530: case ROFFT_BODY:
1.156 schwarze 531: p->tcol->offset = mt->offset + len;
532: p->tcol->rmargin = p->maxrmargin;
1.11 schwarze 533: break;
534: default:
535: break;
536: }
1.138 schwarze 537: return 1;
1.11 schwarze 538: }
1.1 kristaps 539:
1.11 schwarze 540: static void
541: post_IP(DECL_ARGS)
542: {
543: switch (n->type) {
1.124 schwarze 544: case ROFFT_HEAD:
1.11 schwarze 545: term_flushln(p);
546: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 547: p->trailspace = 0;
1.156 schwarze 548: p->tcol->rmargin = p->maxrmargin;
1.11 schwarze 549: break;
1.124 schwarze 550: case ROFFT_BODY:
1.57 schwarze 551: term_newln(p);
1.156 schwarze 552: p->tcol->offset = mt->offset;
1.11 schwarze 553: break;
554: default:
555: break;
556: }
1.1 kristaps 557: }
558:
559: static int
560: pre_TP(DECL_ARGS)
561: {
1.116 schwarze 562: struct roffsu su;
1.125 schwarze 563: struct roff_node *nn;
1.178 ! schwarze 564: int len;
1.11 schwarze 565:
566: switch (n->type) {
1.124 schwarze 567: case ROFFT_HEAD:
1.137 schwarze 568: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
1.93 schwarze 569: p->trailspace = 1;
1.11 schwarze 570: break;
1.124 schwarze 571: case ROFFT_BODY:
1.11 schwarze 572: p->flags |= TERMP_NOSPACE;
573: break;
1.124 schwarze 574: case ROFFT_BLOCK:
1.166 schwarze 575: if (n->tok == MAN_TP)
576: print_bvspace(p, n, mt->pardist);
1.11 schwarze 577: /* FALLTHROUGH */
578: default:
1.138 schwarze 579: return 1;
1.11 schwarze 580: }
581:
582: /* Calculate offset. */
1.1 kristaps 583:
1.116 schwarze 584: if ((nn = n->parent->head->child) != NULL &&
1.141 schwarze 585: nn->string != NULL && ! (NODE_LINE & nn->flags) &&
1.157 schwarze 586: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.158 schwarze 587: len = term_hen(p, &su);
1.116 schwarze 588: if (len < 0 && (size_t)(-len) > mt->offset)
589: len = -mt->offset;
1.117 schwarze 590: else if (len > SHRT_MAX)
591: len = term_len(p, p->defindent);
592: mt->lmargin[mt->lmargincur] = len;
1.116 schwarze 593: } else
594: len = mt->lmargin[mt->lmargincur];
1.8 schwarze 595:
1.11 schwarze 596: switch (n->type) {
1.124 schwarze 597: case ROFFT_HEAD:
1.156 schwarze 598: p->tcol->offset = mt->offset;
599: p->tcol->rmargin = mt->offset + len;
1.11 schwarze 600:
601: /* Don't print same-line elements. */
1.95 schwarze 602: nn = n->child;
1.141 schwarze 603: while (NULL != nn && 0 == (NODE_LINE & nn->flags))
1.95 schwarze 604: nn = nn->next;
605:
606: while (NULL != nn) {
607: print_man_node(p, mt, nn, meta);
608: nn = nn->next;
609: }
1.138 schwarze 610: return 0;
1.124 schwarze 611: case ROFFT_BODY:
1.156 schwarze 612: p->tcol->offset = mt->offset + len;
613: p->tcol->rmargin = p->maxrmargin;
1.93 schwarze 614: p->trailspace = 0;
1.137 schwarze 615: p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
1.11 schwarze 616: break;
617: default:
618: break;
619: }
1.138 schwarze 620: return 1;
1.11 schwarze 621: }
1.1 kristaps 622:
1.11 schwarze 623: static void
624: post_TP(DECL_ARGS)
625: {
626: switch (n->type) {
1.124 schwarze 627: case ROFFT_HEAD:
1.11 schwarze 628: term_flushln(p);
629: break;
1.124 schwarze 630: case ROFFT_BODY:
1.57 schwarze 631: term_newln(p);
1.156 schwarze 632: p->tcol->offset = mt->offset;
1.11 schwarze 633: break;
634: default:
635: break;
636: }
1.1 kristaps 637: }
638:
639: static int
640: pre_SS(DECL_ARGS)
641: {
1.88 schwarze 642: int i;
1.1 kristaps 643:
1.11 schwarze 644: switch (n->type) {
1.124 schwarze 645: case ROFFT_BLOCK:
1.77 schwarze 646: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
647: mt->offset = term_len(p, p->defindent);
1.111 schwarze 648:
649: /*
650: * No vertical space before the first subsection
651: * and after an empty subsection.
652: */
653:
654: do {
655: n = n->prev;
1.162 schwarze 656: } while (n != NULL && n->tok >= MAN_TH &&
1.167 schwarze 657: man_term_act(n->tok)->flags & MAN_NOTEXT);
1.163 schwarze 658: if (n == NULL || n->type == ROFFT_COMMENT ||
659: (n->tok == MAN_SS && n->body->child == NULL))
1.11 schwarze 660: break;
1.111 schwarze 661:
1.88 schwarze 662: for (i = 0; i < mt->pardist; i++)
663: term_vspace(p);
1.11 schwarze 664: break;
1.124 schwarze 665: case ROFFT_HEAD:
1.21 schwarze 666: term_fontrepl(p, TERMFONT_BOLD);
1.156 schwarze 667: p->tcol->offset = term_len(p, 3);
668: p->tcol->rmargin = mt->offset;
1.129 schwarze 669: p->trailspace = mt->offset;
670: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 671: break;
1.124 schwarze 672: case ROFFT_BODY:
1.156 schwarze 673: p->tcol->offset = mt->offset;
674: p->tcol->rmargin = p->maxrmargin;
1.129 schwarze 675: p->trailspace = 0;
676: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 677: break;
678: default:
679: break;
680: }
681:
1.138 schwarze 682: return 1;
1.1 kristaps 683: }
684:
685: static void
686: post_SS(DECL_ARGS)
687: {
1.100 schwarze 688:
1.11 schwarze 689: switch (n->type) {
1.124 schwarze 690: case ROFFT_HEAD:
1.11 schwarze 691: term_newln(p);
692: break;
1.124 schwarze 693: case ROFFT_BODY:
1.11 schwarze 694: term_newln(p);
695: break;
696: default:
697: break;
698: }
1.1 kristaps 699: }
700:
701: static int
702: pre_SH(DECL_ARGS)
703: {
1.88 schwarze 704: int i;
1.1 kristaps 705:
1.11 schwarze 706: switch (n->type) {
1.124 schwarze 707: case ROFFT_BLOCK:
1.77 schwarze 708: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
709: mt->offset = term_len(p, p->defindent);
1.111 schwarze 710:
711: /*
712: * No vertical space before the first section
713: * and after an empty section.
714: */
715:
716: do {
717: n = n->prev;
1.162 schwarze 718: } while (n != NULL && n->tok >= MAN_TH &&
1.167 schwarze 719: man_term_act(n->tok)->flags & MAN_NOTEXT);
1.163 schwarze 720: if (n == NULL || n->type == ROFFT_COMMENT ||
721: (n->tok == MAN_SH && n->body->child == NULL))
1.29 schwarze 722: break;
1.111 schwarze 723:
1.88 schwarze 724: for (i = 0; i < mt->pardist; i++)
725: term_vspace(p);
1.11 schwarze 726: break;
1.124 schwarze 727: case ROFFT_HEAD:
1.21 schwarze 728: term_fontrepl(p, TERMFONT_BOLD);
1.156 schwarze 729: p->tcol->offset = 0;
730: p->tcol->rmargin = mt->offset;
1.129 schwarze 731: p->trailspace = mt->offset;
732: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 733: break;
1.124 schwarze 734: case ROFFT_BODY:
1.156 schwarze 735: p->tcol->offset = mt->offset;
736: p->tcol->rmargin = p->maxrmargin;
1.129 schwarze 737: p->trailspace = 0;
738: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 739: break;
740: default:
741: break;
742: }
743:
1.138 schwarze 744: return 1;
1.1 kristaps 745: }
746:
747: static void
748: post_SH(DECL_ARGS)
749: {
1.100 schwarze 750:
1.11 schwarze 751: switch (n->type) {
1.124 schwarze 752: case ROFFT_HEAD:
1.11 schwarze 753: term_newln(p);
754: break;
1.124 schwarze 755: case ROFFT_BODY:
1.11 schwarze 756: term_newln(p);
757: break;
758: default:
1.13 schwarze 759: break;
760: }
761: }
762:
763: static int
764: pre_RS(DECL_ARGS)
765: {
1.116 schwarze 766: struct roffsu su;
1.13 schwarze 767:
768: switch (n->type) {
1.124 schwarze 769: case ROFFT_BLOCK:
1.13 schwarze 770: term_newln(p);
1.138 schwarze 771: return 1;
1.124 schwarze 772: case ROFFT_HEAD:
1.138 schwarze 773: return 0;
1.13 schwarze 774: default:
775: break;
776: }
777:
1.118 schwarze 778: n = n->parent->head;
779: n->aux = SHRT_MAX + 1;
1.130 schwarze 780: if (n->child == NULL)
781: n->aux = mt->lmargin[mt->lmargincur];
1.157 schwarze 782: else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
1.158 schwarze 783: n->aux = term_hen(p, &su);
1.118 schwarze 784: if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
785: n->aux = -mt->offset;
786: else if (n->aux > SHRT_MAX)
787: n->aux = term_len(p, p->defindent);
1.13 schwarze 788:
1.118 schwarze 789: mt->offset += n->aux;
1.156 schwarze 790: p->tcol->offset = mt->offset;
791: p->tcol->rmargin = p->maxrmargin;
1.13 schwarze 792:
1.70 schwarze 793: if (++mt->lmarginsz < MAXMARGINS)
794: mt->lmargincur = mt->lmarginsz;
795:
1.131 schwarze 796: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.138 schwarze 797: return 1;
1.13 schwarze 798: }
799:
800: static void
801: post_RS(DECL_ARGS)
802: {
803:
804: switch (n->type) {
1.124 schwarze 805: case ROFFT_BLOCK:
1.69 schwarze 806: return;
1.124 schwarze 807: case ROFFT_HEAD:
1.69 schwarze 808: return;
1.13 schwarze 809: default:
810: term_newln(p);
1.11 schwarze 811: break;
812: }
1.69 schwarze 813:
1.118 schwarze 814: mt->offset -= n->parent->head->aux;
1.156 schwarze 815: p->tcol->offset = mt->offset;
1.70 schwarze 816:
817: if (--mt->lmarginsz < MAXMARGINS)
818: mt->lmargincur = mt->lmarginsz;
1.168 schwarze 819: }
820:
821: static int
822: pre_SY(DECL_ARGS)
823: {
824: const struct roff_node *nn;
825: int len;
826:
827: switch (n->type) {
828: case ROFFT_BLOCK:
1.170 schwarze 829: if (n->prev == NULL || n->prev->tok != MAN_SY)
830: print_bvspace(p, n, mt->pardist);
1.168 schwarze 831: return 1;
832: case ROFFT_HEAD:
833: case ROFFT_BODY:
834: break;
835: default:
836: abort();
837: }
838:
839: nn = n->parent->head->child;
1.172 schwarze 840: len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
1.168 schwarze 841:
842: switch (n->type) {
843: case ROFFT_HEAD:
844: p->tcol->offset = mt->offset;
845: p->tcol->rmargin = mt->offset + len;
846: p->flags |= TERMP_NOBREAK;
847: term_fontrepl(p, TERMFONT_BOLD);
848: break;
849: case ROFFT_BODY:
850: mt->lmargin[mt->lmargincur] = len;
851: p->tcol->offset = mt->offset + len;
852: p->tcol->rmargin = p->maxrmargin;
853: p->flags |= TERMP_NOSPACE;
854: break;
855: default:
856: abort();
857: }
858: return 1;
859: }
860:
861: static void
862: post_SY(DECL_ARGS)
863: {
864: switch (n->type) {
865: case ROFFT_HEAD:
866: term_flushln(p);
867: p->flags &= ~TERMP_NOBREAK;
868: break;
869: case ROFFT_BODY:
870: term_newln(p);
871: p->tcol->offset = mt->offset;
872: break;
873: default:
874: break;
875: }
1.91 schwarze 876: }
877:
878: static int
879: pre_UR(DECL_ARGS)
880: {
881:
1.138 schwarze 882: return n->type != ROFFT_HEAD;
1.91 schwarze 883: }
884:
885: static void
886: post_UR(DECL_ARGS)
887: {
888:
1.124 schwarze 889: if (n->type != ROFFT_BLOCK)
1.91 schwarze 890: return;
891:
892: term_word(p, "<");
893: p->flags |= TERMP_NOSPACE;
894:
895: if (NULL != n->child->child)
896: print_man_node(p, mt, n->child->child, meta);
897:
898: p->flags |= TERMP_NOSPACE;
899: term_word(p, ">");
1.47 schwarze 900: }
901:
1.1 kristaps 902: static void
1.19 schwarze 903: print_man_node(DECL_ARGS)
1.1 kristaps 904: {
1.167 schwarze 905: const struct man_term_act *act;
906: int c;
1.1 kristaps 907:
908: switch (n->type) {
1.124 schwarze 909: case ROFFT_TEXT:
1.61 schwarze 910: /*
911: * If we have a blank line, output a vertical space.
912: * If we have a space as the first character, break
913: * before printing the line's data.
914: */
1.153 schwarze 915: if (*n->string == '\0') {
1.160 schwarze 916: if (p->flags & TERMP_NONEWLINE)
917: term_newln(p);
918: else
919: term_vspace(p);
1.61 schwarze 920: return;
1.153 schwarze 921: } else if (*n->string == ' ' && n->flags & NODE_LINE &&
922: (p->flags & TERMP_NONEWLINE) == 0)
1.60 schwarze 923: term_newln(p);
1.165 schwarze 924: else if (n->flags & NODE_DELIMC)
925: p->flags |= TERMP_NOSPACE;
1.21 schwarze 926:
1.1 kristaps 927: term_word(p, n->string);
1.84 schwarze 928: goto out;
1.163 schwarze 929: case ROFFT_COMMENT:
930: return;
1.124 schwarze 931: case ROFFT_EQN:
1.141 schwarze 932: if ( ! (n->flags & NODE_LINE))
1.105 schwarze 933: p->flags |= TERMP_NOSPACE;
1.71 schwarze 934: term_eqn(p, n->eqn);
1.141 schwarze 935: if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.106 schwarze 936: p->flags |= TERMP_NOSPACE;
1.61 schwarze 937: return;
1.124 schwarze 938: case ROFFT_TBL:
1.122 schwarze 939: if (p->tbl.cols == NULL)
940: term_vspace(p);
1.58 schwarze 941: term_tbl(p, n->span);
1.61 schwarze 942: return;
1.1 kristaps 943: default:
944: break;
945: }
1.177 schwarze 946:
1.146 schwarze 947: if (n->tok < ROFF_MAX) {
1.147 schwarze 948: roff_term_pre(p, n);
1.146 schwarze 949: return;
950: }
951:
1.167 schwarze 952: act = man_term_act(n->tok);
1.173 schwarze 953: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.61 schwarze 954: term_fontrepl(p, TERMFONT_NONE);
955:
956: c = 1;
1.167 schwarze 957: if (act->pre != NULL)
958: c = (*act->pre)(p, mt, n, meta);
1.61 schwarze 959:
1.1 kristaps 960: if (c && n->child)
1.89 schwarze 961: print_man_nodelist(p, mt, n->child, meta);
1.1 kristaps 962:
1.167 schwarze 963: if (act->post != NULL)
964: (*act->post)(p, mt, n, meta);
1.173 schwarze 965: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.61 schwarze 966: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 967:
1.84 schwarze 968: out:
969: /*
970: * If we're in a literal context, make sure that words
971: * together on the same line stay together. This is a
972: * POST-printing call, so we check the NEXT word. Since
973: * -man doesn't have nested macros, we don't need to be
974: * more specific than this.
975: */
1.178 ! schwarze 976: if (n->flags & NODE_NOFILL &&
1.110 schwarze 977: ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1.141 schwarze 978: (n->next == NULL || n->next->flags & NODE_LINE)) {
1.155 schwarze 979: p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
1.110 schwarze 980: if (n->string != NULL && *n->string != '\0')
1.84 schwarze 981: term_flushln(p);
982: else
983: term_newln(p);
1.155 schwarze 984: p->flags &= ~TERMP_BRNEVER;
1.156 schwarze 985: if (p->tcol->rmargin < p->maxrmargin &&
986: n->parent->tok == MAN_HP) {
987: p->tcol->offset = p->tcol->rmargin;
988: p->tcol->rmargin = p->maxrmargin;
1.155 schwarze 989: }
1.84 schwarze 990: }
1.141 schwarze 991: if (NODE_EOS & n->flags)
1.30 schwarze 992: p->flags |= TERMP_SENTENCE;
1.1 kristaps 993: }
994:
995:
996: static void
1.21 schwarze 997: print_man_nodelist(DECL_ARGS)
1.1 kristaps 998: {
1.11 schwarze 999:
1.121 schwarze 1000: while (n != NULL) {
1001: print_man_node(p, mt, n, meta);
1002: n = n->next;
1003: }
1.1 kristaps 1004: }
1005:
1006: static void
1.126 schwarze 1007: print_man_foot(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1008: {
1.101 schwarze 1009: char *title;
1.109 schwarze 1010: size_t datelen, titlen;
1.41 schwarze 1011:
1.80 schwarze 1012: assert(meta->title);
1013: assert(meta->msec);
1014: assert(meta->date);
1.21 schwarze 1015:
1016: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 1017:
1.103 schwarze 1018: if (meta->hasbody)
1019: term_vspace(p);
1.81 schwarze 1020:
1021: /*
1022: * Temporary, undocumented option to imitate mdoc(7) output.
1.126 schwarze 1023: * In the bottom right corner, use the operating system
1024: * instead of the title.
1.81 schwarze 1025: */
1026:
1.79 schwarze 1027: if ( ! p->mdocstyle) {
1.103 schwarze 1028: if (meta->hasbody) {
1029: term_vspace(p);
1030: term_vspace(p);
1031: }
1.101 schwarze 1032: mandoc_asprintf(&title, "%s(%s)",
1033: meta->title, meta->msec);
1.126 schwarze 1034: } else if (meta->os) {
1035: title = mandoc_strdup(meta->os);
1.79 schwarze 1036: } else {
1.101 schwarze 1037: title = mandoc_strdup("");
1.79 schwarze 1038: }
1.78 schwarze 1039: datelen = term_strlen(p, meta->date);
1.1 kristaps 1040:
1.126 schwarze 1041: /* Bottom left corner: operating system. */
1.81 schwarze 1042:
1.1 kristaps 1043: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.93 schwarze 1044: p->trailspace = 1;
1.156 schwarze 1045: p->tcol->offset = 0;
1046: p->tcol->rmargin = p->maxrmargin > datelen ?
1.109 schwarze 1047: (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1.1 kristaps 1048:
1.126 schwarze 1049: if (meta->os)
1050: term_word(p, meta->os);
1.1 kristaps 1051: term_flushln(p);
1052:
1.81 schwarze 1053: /* At the bottom in the middle: manual date. */
1054:
1.156 schwarze 1055: p->tcol->offset = p->tcol->rmargin;
1.109 schwarze 1056: titlen = term_strlen(p, title);
1.156 schwarze 1057: p->tcol->rmargin = p->maxrmargin > titlen ?
1058: p->maxrmargin - titlen : 0;
1.72 schwarze 1059: p->flags |= TERMP_NOSPACE;
1.78 schwarze 1060:
1061: term_word(p, meta->date);
1062: term_flushln(p);
1063:
1.81 schwarze 1064: /* Bottom right corner: manual title and section. */
1065:
1.78 schwarze 1066: p->flags &= ~TERMP_NOBREAK;
1067: p->flags |= TERMP_NOSPACE;
1.93 schwarze 1068: p->trailspace = 0;
1.156 schwarze 1069: p->tcol->offset = p->tcol->rmargin;
1070: p->tcol->rmargin = p->maxrmargin;
1.1 kristaps 1071:
1.78 schwarze 1072: term_word(p, title);
1.1 kristaps 1073: term_flushln(p);
1.164 schwarze 1074:
1075: /*
1076: * Reset the terminal state for more output after the footer:
1077: * Some output modes, in particular PostScript and PDF, print
1078: * the header and the footer into a buffer such that it can be
1079: * reused for multiple output pages, then go on to format the
1080: * main text.
1081: */
1082:
1083: p->tcol->offset = 0;
1084: p->flags = 0;
1085:
1.101 schwarze 1086: free(title);
1.1 kristaps 1087: }
1088:
1089: static void
1.126 schwarze 1090: print_man_head(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1091: {
1.102 schwarze 1092: const char *volume;
1.101 schwarze 1093: char *title;
1.102 schwarze 1094: size_t vollen, titlen;
1.41 schwarze 1095:
1.89 schwarze 1096: assert(meta->title);
1097: assert(meta->msec);
1.1 kristaps 1098:
1.102 schwarze 1099: volume = NULL == meta->vol ? "" : meta->vol;
1100: vollen = term_strlen(p, volume);
1.1 kristaps 1101:
1.81 schwarze 1102: /* Top left corner: manual title and section. */
1103:
1.101 schwarze 1104: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.42 schwarze 1105: titlen = term_strlen(p, title);
1.1 kristaps 1106:
1.73 schwarze 1107: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.93 schwarze 1108: p->trailspace = 1;
1.156 schwarze 1109: p->tcol->offset = 0;
1110: p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1.102 schwarze 1111: (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.109 schwarze 1112: vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1.1 kristaps 1113:
1114: term_word(p, title);
1115: term_flushln(p);
1116:
1.81 schwarze 1117: /* At the top in the middle: manual volume. */
1118:
1.72 schwarze 1119: p->flags |= TERMP_NOSPACE;
1.156 schwarze 1120: p->tcol->offset = p->tcol->rmargin;
1121: p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1122: p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1123:
1.102 schwarze 1124: term_word(p, volume);
1.1 kristaps 1125: term_flushln(p);
1126:
1.81 schwarze 1127: /* Top right corner: title and section, again. */
1128:
1.1 kristaps 1129: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 1130: p->trailspace = 0;
1.156 schwarze 1131: if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1132: p->flags |= TERMP_NOSPACE;
1.156 schwarze 1133: p->tcol->offset = p->tcol->rmargin;
1134: p->tcol->rmargin = p->maxrmargin;
1.25 schwarze 1135: term_word(p, title);
1136: term_flushln(p);
1137: }
1.1 kristaps 1138:
1.73 schwarze 1139: p->flags &= ~TERMP_NOSPACE;
1.156 schwarze 1140: p->tcol->offset = 0;
1141: p->tcol->rmargin = p->maxrmargin;
1.29 schwarze 1142:
1.100 schwarze 1143: /*
1.81 schwarze 1144: * Groff prints three blank lines before the content.
1145: * Do the same, except in the temporary, undocumented
1146: * mode imitating mdoc(7) output.
1.29 schwarze 1147: */
1148:
1149: term_vspace(p);
1.79 schwarze 1150: if ( ! p->mdocstyle) {
1151: term_vspace(p);
1152: term_vspace(p);
1153: }
1.101 schwarze 1154: free(title);
1.1 kristaps 1155: }