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