Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.142
1.142 ! schwarze 1: /* $OpenBSD: man_term.c,v 1.141 2017/01/10 13:46:53 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;
823: } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
824: if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
1.29 schwarze 825: break;
1.111 schwarze 826:
1.88 schwarze 827: for (i = 0; i < mt->pardist; i++)
828: term_vspace(p);
1.11 schwarze 829: break;
1.124 schwarze 830: case ROFFT_HEAD:
1.21 schwarze 831: term_fontrepl(p, TERMFONT_BOLD);
1.11 schwarze 832: p->offset = 0;
1.129 schwarze 833: p->rmargin = mt->offset;
834: p->trailspace = mt->offset;
835: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.11 schwarze 836: break;
1.124 schwarze 837: case ROFFT_BODY:
1.13 schwarze 838: p->offset = mt->offset;
1.129 schwarze 839: p->rmargin = p->maxrmargin;
840: p->trailspace = 0;
841: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.11 schwarze 842: break;
843: default:
844: break;
845: }
846:
1.138 schwarze 847: return 1;
1.1 kristaps 848: }
849:
850: static void
851: post_SH(DECL_ARGS)
852: {
1.100 schwarze 853:
1.11 schwarze 854: switch (n->type) {
1.124 schwarze 855: case ROFFT_HEAD:
1.11 schwarze 856: term_newln(p);
857: break;
1.124 schwarze 858: case ROFFT_BODY:
1.11 schwarze 859: term_newln(p);
860: break;
861: default:
1.13 schwarze 862: break;
863: }
864: }
865:
866: static int
867: pre_RS(DECL_ARGS)
868: {
1.116 schwarze 869: struct roffsu su;
1.13 schwarze 870:
871: switch (n->type) {
1.124 schwarze 872: case ROFFT_BLOCK:
1.13 schwarze 873: term_newln(p);
1.138 schwarze 874: return 1;
1.124 schwarze 875: case ROFFT_HEAD:
1.138 schwarze 876: return 0;
1.13 schwarze 877: default:
878: break;
879: }
880:
1.118 schwarze 881: n = n->parent->head;
882: n->aux = SHRT_MAX + 1;
1.130 schwarze 883: if (n->child == NULL)
884: n->aux = mt->lmargin[mt->lmargincur];
885: else if (a2roffsu(n->child->string, &su, SCALE_EN))
1.128 schwarze 886: n->aux = term_hspan(p, &su) / 24;
1.118 schwarze 887: if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
888: n->aux = -mt->offset;
889: else if (n->aux > SHRT_MAX)
890: n->aux = term_len(p, p->defindent);
1.13 schwarze 891:
1.118 schwarze 892: mt->offset += n->aux;
1.94 schwarze 893: p->offset = mt->offset;
1.109 schwarze 894: p->rmargin = p->maxrmargin;
1.13 schwarze 895:
1.70 schwarze 896: if (++mt->lmarginsz < MAXMARGINS)
897: mt->lmargincur = mt->lmarginsz;
898:
1.131 schwarze 899: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.138 schwarze 900: return 1;
1.13 schwarze 901: }
902:
903: static void
904: post_RS(DECL_ARGS)
905: {
906:
907: switch (n->type) {
1.124 schwarze 908: case ROFFT_BLOCK:
1.69 schwarze 909: return;
1.124 schwarze 910: case ROFFT_HEAD:
1.69 schwarze 911: return;
1.13 schwarze 912: default:
913: term_newln(p);
1.11 schwarze 914: break;
915: }
1.69 schwarze 916:
1.118 schwarze 917: mt->offset -= n->parent->head->aux;
1.69 schwarze 918: p->offset = mt->offset;
1.70 schwarze 919:
920: if (--mt->lmarginsz < MAXMARGINS)
921: mt->lmargincur = mt->lmarginsz;
1.91 schwarze 922: }
923:
924: static int
925: pre_UR(DECL_ARGS)
926: {
927:
1.138 schwarze 928: return n->type != ROFFT_HEAD;
1.91 schwarze 929: }
930:
931: static void
932: post_UR(DECL_ARGS)
933: {
934:
1.124 schwarze 935: if (n->type != ROFFT_BLOCK)
1.91 schwarze 936: return;
937:
938: term_word(p, "<");
939: p->flags |= TERMP_NOSPACE;
940:
941: if (NULL != n->child->child)
942: print_man_node(p, mt, n->child->child, meta);
943:
944: p->flags |= TERMP_NOSPACE;
945: term_word(p, ">");
1.47 schwarze 946: }
947:
1.1 kristaps 948: static void
1.19 schwarze 949: print_man_node(DECL_ARGS)
1.1 kristaps 950: {
1.32 schwarze 951: size_t rm, rmax;
1.21 schwarze 952: int c;
1.1 kristaps 953:
954: switch (n->type) {
1.124 schwarze 955: case ROFFT_TEXT:
1.61 schwarze 956: /*
957: * If we have a blank line, output a vertical space.
958: * If we have a space as the first character, break
959: * before printing the line's data.
960: */
1.60 schwarze 961: if ('\0' == *n->string) {
1.1 kristaps 962: term_vspace(p);
1.61 schwarze 963: return;
1.141 schwarze 964: } else if (' ' == *n->string && NODE_LINE & n->flags)
1.60 schwarze 965: term_newln(p);
1.21 schwarze 966:
1.1 kristaps 967: term_word(p, n->string);
1.84 schwarze 968: goto out;
1.21 schwarze 969:
1.124 schwarze 970: case ROFFT_EQN:
1.141 schwarze 971: if ( ! (n->flags & NODE_LINE))
1.105 schwarze 972: p->flags |= TERMP_NOSPACE;
1.71 schwarze 973: term_eqn(p, n->eqn);
1.141 schwarze 974: if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.106 schwarze 975: p->flags |= TERMP_NOSPACE;
1.61 schwarze 976: return;
1.124 schwarze 977: case ROFFT_TBL:
1.122 schwarze 978: if (p->tbl.cols == NULL)
979: term_vspace(p);
1.58 schwarze 980: term_tbl(p, n->span);
1.61 schwarze 981: return;
1.1 kristaps 982: default:
983: break;
984: }
985:
1.61 schwarze 986: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
987: term_fontrepl(p, TERMFONT_NONE);
988:
989: c = 1;
990: if (termacts[n->tok].pre)
1.89 schwarze 991: c = (*termacts[n->tok].pre)(p, mt, n, meta);
1.61 schwarze 992:
1.1 kristaps 993: if (c && n->child)
1.89 schwarze 994: print_man_nodelist(p, mt, n->child, meta);
1.1 kristaps 995:
1.61 schwarze 996: if (termacts[n->tok].post)
1.89 schwarze 997: (*termacts[n->tok].post)(p, mt, n, meta);
1.61 schwarze 998: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
999: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 1000:
1.84 schwarze 1001: out:
1002: /*
1003: * If we're in a literal context, make sure that words
1004: * together on the same line stay together. This is a
1005: * POST-printing call, so we check the NEXT word. Since
1006: * -man doesn't have nested macros, we don't need to be
1007: * more specific than this.
1008: */
1.110 schwarze 1009: if (mt->fl & MANT_LITERAL &&
1010: ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1.141 schwarze 1011: (n->next == NULL || n->next->flags & NODE_LINE)) {
1.84 schwarze 1012: rm = p->rmargin;
1013: rmax = p->maxrmargin;
1014: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1015: p->flags |= TERMP_NOSPACE;
1.110 schwarze 1016: if (n->string != NULL && *n->string != '\0')
1.84 schwarze 1017: term_flushln(p);
1018: else
1019: term_newln(p);
1020: if (rm < rmax && n->parent->tok == MAN_HP) {
1021: p->offset = rm;
1022: p->rmargin = rmax;
1023: } else
1024: p->rmargin = rm;
1025: p->maxrmargin = rmax;
1026: }
1.141 schwarze 1027: if (NODE_EOS & n->flags)
1.30 schwarze 1028: p->flags |= TERMP_SENTENCE;
1.1 kristaps 1029: }
1030:
1031:
1032: static void
1.21 schwarze 1033: print_man_nodelist(DECL_ARGS)
1.1 kristaps 1034: {
1.11 schwarze 1035:
1.121 schwarze 1036: while (n != NULL) {
1037: print_man_node(p, mt, n, meta);
1038: n = n->next;
1039: }
1.1 kristaps 1040: }
1041:
1042: static void
1.126 schwarze 1043: print_man_foot(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1044: {
1.101 schwarze 1045: char *title;
1.109 schwarze 1046: size_t datelen, titlen;
1.41 schwarze 1047:
1.80 schwarze 1048: assert(meta->title);
1049: assert(meta->msec);
1050: assert(meta->date);
1.21 schwarze 1051:
1052: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 1053:
1.103 schwarze 1054: if (meta->hasbody)
1055: term_vspace(p);
1.81 schwarze 1056:
1057: /*
1058: * Temporary, undocumented option to imitate mdoc(7) output.
1.126 schwarze 1059: * In the bottom right corner, use the operating system
1060: * instead of the title.
1.81 schwarze 1061: */
1062:
1.79 schwarze 1063: if ( ! p->mdocstyle) {
1.103 schwarze 1064: if (meta->hasbody) {
1065: term_vspace(p);
1066: term_vspace(p);
1067: }
1.101 schwarze 1068: mandoc_asprintf(&title, "%s(%s)",
1069: meta->title, meta->msec);
1.126 schwarze 1070: } else if (meta->os) {
1071: title = mandoc_strdup(meta->os);
1.79 schwarze 1072: } else {
1.101 schwarze 1073: title = mandoc_strdup("");
1.79 schwarze 1074: }
1.78 schwarze 1075: datelen = term_strlen(p, meta->date);
1.1 kristaps 1076:
1.126 schwarze 1077: /* Bottom left corner: operating system. */
1.81 schwarze 1078:
1.1 kristaps 1079: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.93 schwarze 1080: p->trailspace = 1;
1.1 kristaps 1081: p->offset = 0;
1.109 schwarze 1082: p->rmargin = p->maxrmargin > datelen ?
1083: (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1.1 kristaps 1084:
1.126 schwarze 1085: if (meta->os)
1086: term_word(p, meta->os);
1.1 kristaps 1087: term_flushln(p);
1088:
1.81 schwarze 1089: /* At the bottom in the middle: manual date. */
1090:
1.109 schwarze 1091: p->offset = p->rmargin;
1092: titlen = term_strlen(p, title);
1093: p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
1.72 schwarze 1094: p->flags |= TERMP_NOSPACE;
1.78 schwarze 1095:
1096: term_word(p, meta->date);
1097: term_flushln(p);
1098:
1.81 schwarze 1099: /* Bottom right corner: manual title and section. */
1100:
1.78 schwarze 1101: p->flags &= ~TERMP_NOBREAK;
1102: p->flags |= TERMP_NOSPACE;
1.93 schwarze 1103: p->trailspace = 0;
1.78 schwarze 1104: p->offset = p->rmargin;
1.1 kristaps 1105: p->rmargin = p->maxrmargin;
1106:
1.78 schwarze 1107: term_word(p, title);
1.1 kristaps 1108: term_flushln(p);
1.101 schwarze 1109: free(title);
1.1 kristaps 1110: }
1111:
1112: static void
1.126 schwarze 1113: print_man_head(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1114: {
1.102 schwarze 1115: const char *volume;
1.101 schwarze 1116: char *title;
1.102 schwarze 1117: size_t vollen, titlen;
1.41 schwarze 1118:
1.89 schwarze 1119: assert(meta->title);
1120: assert(meta->msec);
1.1 kristaps 1121:
1.102 schwarze 1122: volume = NULL == meta->vol ? "" : meta->vol;
1123: vollen = term_strlen(p, volume);
1.1 kristaps 1124:
1.81 schwarze 1125: /* Top left corner: manual title and section. */
1126:
1.101 schwarze 1127: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.42 schwarze 1128: titlen = term_strlen(p, title);
1.1 kristaps 1129:
1.73 schwarze 1130: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.93 schwarze 1131: p->trailspace = 1;
1.1 kristaps 1132: p->offset = 0;
1.102 schwarze 1133: p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1134: (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.109 schwarze 1135: vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1.1 kristaps 1136:
1137: term_word(p, title);
1138: term_flushln(p);
1139:
1.81 schwarze 1140: /* At the top in the middle: manual volume. */
1141:
1.72 schwarze 1142: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1143: p->offset = p->rmargin;
1.102 schwarze 1144: p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1.25 schwarze 1145: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1146:
1.102 schwarze 1147: term_word(p, volume);
1.1 kristaps 1148: term_flushln(p);
1149:
1.81 schwarze 1150: /* Top right corner: title and section, again. */
1151:
1.1 kristaps 1152: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 1153: p->trailspace = 0;
1.25 schwarze 1154: if (p->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1155: p->flags |= TERMP_NOSPACE;
1.25 schwarze 1156: p->offset = p->rmargin;
1157: p->rmargin = p->maxrmargin;
1158: term_word(p, title);
1159: term_flushln(p);
1160: }
1.1 kristaps 1161:
1.73 schwarze 1162: p->flags &= ~TERMP_NOSPACE;
1163: p->offset = 0;
1.1 kristaps 1164: p->rmargin = p->maxrmargin;
1.29 schwarze 1165:
1.100 schwarze 1166: /*
1.81 schwarze 1167: * Groff prints three blank lines before the content.
1168: * Do the same, except in the temporary, undocumented
1169: * mode imitating mdoc(7) output.
1.29 schwarze 1170: */
1171:
1172: term_vspace(p);
1.79 schwarze 1173: if ( ! p->mdocstyle) {
1174: term_vspace(p);
1175: term_vspace(p);
1176: }
1.101 schwarze 1177: free(title);
1.1 kristaps 1178: }