Annotation of mandoc/man_term.c, Revision 1.246
1.246 ! schwarze 1: /* $Id: man_term.c,v 1.245 2025/06/26 17:06:34 schwarze Exp $ */
1.1 kristaps 2: /*
1.245 schwarze 3: * Copyright (c) 2010-2020,2022-23,2025 Ingo Schwarze <schwarze@openbsd.org>
1.128 schwarze 4: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 5: *
6: * Permission to use, copy, modify, and distribute this software for any
1.8 kristaps 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.171 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.8 kristaps 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.171 schwarze 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.8 kristaps 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.235 schwarze 17: *
18: * Plain text formatter for man(7), used by mandoc(1)
19: * for ASCII, UTF-8, PostScript, and PDF output.
1.1 kristaps 20: */
1.55 kristaps 21: #include "config.h"
22:
1.28 kristaps 23: #include <sys/types.h>
24:
1.1 kristaps 25: #include <assert.h>
1.18 kristaps 26: #include <ctype.h>
1.164 schwarze 27: #include <limits.h>
1.1 kristaps 28: #include <stdio.h>
29: #include <stdlib.h>
30: #include <string.h>
31:
1.171 schwarze 32: #include "mandoc_aux.h"
1.231 schwarze 33: #include "mandoc.h"
1.171 schwarze 34: #include "roff.h"
35: #include "man.h"
1.40 kristaps 36: #include "out.h"
1.1 kristaps 37: #include "term.h"
1.235 schwarze 38: #include "term_tag.h"
1.36 kristaps 39: #include "main.h"
1.1 kristaps 40:
1.116 schwarze 41: #define MAXMARGINS 64 /* maximum number of indented scopes */
1.18 kristaps 42:
1.24 kristaps 43: struct mtermp {
1.163 schwarze 44: int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
1.116 schwarze 45: int lmargincur; /* index of current margin */
46: int lmarginsz; /* actual number of nested margins */
47: size_t offset; /* default offset to visible page */
1.134 schwarze 48: int pardist; /* vert. space before par., unit: [v] */
1.24 kristaps 49: };
1.19 kristaps 50:
1.146 schwarze 51: #define DECL_ARGS struct termp *p, \
1.24 kristaps 52: struct mtermp *mt, \
1.172 schwarze 53: struct roff_node *n, \
1.173 schwarze 54: const struct roff_meta *meta
1.1 kristaps 55:
1.214 schwarze 56: struct man_term_act {
1.1 kristaps 57: int (*pre)(DECL_ARGS);
58: void (*post)(DECL_ARGS);
1.56 kristaps 59: int flags;
60: #define MAN_NOTEXT (1 << 0) /* Never has text children. */
1.1 kristaps 61: };
62:
1.52 kristaps 63: static void print_man_nodelist(DECL_ARGS);
1.45 kristaps 64: static void print_man_node(DECL_ARGS);
1.173 schwarze 65: static void print_man_head(struct termp *,
66: const struct roff_meta *);
67: static void print_man_foot(struct termp *,
68: const struct roff_meta *);
1.146 schwarze 69: static void print_bvspace(struct termp *,
1.234 schwarze 70: struct roff_node *, int);
1.39 kristaps 71:
1.1 kristaps 72: static int pre_B(DECL_ARGS);
1.199 schwarze 73: static int pre_DT(DECL_ARGS);
1.19 kristaps 74: static int pre_HP(DECL_ARGS);
1.1 kristaps 75: static int pre_I(DECL_ARGS);
1.4 kristaps 76: static int pre_IP(DECL_ARGS);
1.243 schwarze 77: static int pre_MR(DECL_ARGS);
1.127 kristaps 78: static int pre_OP(DECL_ARGS);
1.134 schwarze 79: static int pre_PD(DECL_ARGS);
1.1 kristaps 80: static int pre_PP(DECL_ARGS);
1.26 kristaps 81: static int pre_RS(DECL_ARGS);
1.1 kristaps 82: static int pre_SH(DECL_ARGS);
83: static int pre_SS(DECL_ARGS);
1.215 schwarze 84: static int pre_SY(DECL_ARGS);
1.1 kristaps 85: static int pre_TP(DECL_ARGS);
1.137 schwarze 86: static int pre_UR(DECL_ARGS);
1.127 kristaps 87: static int pre_alternate(DECL_ARGS);
1.29 kristaps 88: static int pre_ign(DECL_ARGS);
1.83 kristaps 89: static int pre_in(DECL_ARGS);
1.84 kristaps 90: static int pre_literal(DECL_ARGS);
1.1 kristaps 91:
1.22 kristaps 92: static void post_IP(DECL_ARGS);
1.20 kristaps 93: static void post_HP(DECL_ARGS);
1.26 kristaps 94: static void post_RS(DECL_ARGS);
1.1 kristaps 95: static void post_SH(DECL_ARGS);
1.215 schwarze 96: static void post_SY(DECL_ARGS);
1.21 kristaps 97: static void post_TP(DECL_ARGS);
1.137 schwarze 98: static void post_UR(DECL_ARGS);
1.1 kristaps 99:
1.214 schwarze 100: static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = {
1.56 kristaps 101: { NULL, NULL, 0 }, /* TH */
102: { pre_SH, post_SH, 0 }, /* SH */
1.227 schwarze 103: { pre_SS, post_SH, 0 }, /* SS */
1.56 kristaps 104: { pre_TP, post_TP, 0 }, /* TP */
1.213 schwarze 105: { pre_TP, post_TP, 0 }, /* TQ */
1.242 schwarze 106: { pre_PP, NULL, 0 }, /* LP */
1.56 kristaps 107: { pre_PP, NULL, 0 }, /* PP */
1.242 schwarze 108: { pre_PP, NULL, 0 }, /* P */
1.56 kristaps 109: { pre_IP, post_IP, 0 }, /* IP */
1.146 schwarze 110: { pre_HP, post_HP, 0 }, /* HP */
1.56 kristaps 111: { NULL, NULL, 0 }, /* SM */
112: { pre_B, NULL, 0 }, /* SB */
1.88 kristaps 113: { pre_alternate, NULL, 0 }, /* BI */
114: { pre_alternate, NULL, 0 }, /* IB */
115: { pre_alternate, NULL, 0 }, /* BR */
116: { pre_alternate, NULL, 0 }, /* RB */
1.56 kristaps 117: { NULL, NULL, 0 }, /* R */
118: { pre_B, NULL, 0 }, /* B */
119: { pre_I, NULL, 0 }, /* I */
1.88 kristaps 120: { pre_alternate, NULL, 0 }, /* IR */
121: { pre_alternate, NULL, 0 }, /* RI */
1.56 kristaps 122: { NULL, NULL, 0 }, /* RE */
123: { pre_RS, post_RS, 0 }, /* RS */
1.237 schwarze 124: { pre_DT, NULL, MAN_NOTEXT }, /* DT */
1.158 schwarze 125: { pre_ign, NULL, MAN_NOTEXT }, /* UC */
1.134 schwarze 126: { pre_PD, NULL, MAN_NOTEXT }, /* PD */
1.237 schwarze 127: { pre_ign, NULL, MAN_NOTEXT }, /* AT */
1.83 kristaps 128: { pre_in, NULL, MAN_NOTEXT }, /* in */
1.215 schwarze 129: { pre_SY, post_SY, 0 }, /* SY */
130: { NULL, NULL, 0 }, /* YS */
1.127 kristaps 131: { pre_OP, NULL, 0 }, /* OP */
1.129 schwarze 132: { pre_literal, NULL, 0 }, /* EX */
133: { pre_literal, NULL, 0 }, /* EE */
1.137 schwarze 134: { pre_UR, post_UR, 0 }, /* UR */
135: { NULL, NULL, 0 }, /* UE */
1.208 schwarze 136: { pre_UR, post_UR, 0 }, /* MT */
137: { NULL, NULL, 0 }, /* ME */
1.243 schwarze 138: { pre_MR, NULL, 0 }, /* MR */
1.1 kristaps 139: };
1.214 schwarze 140: static const struct man_term_act *man_term_act(enum roff_tok);
1.1 kristaps 141:
142:
1.214 schwarze 143: static const struct man_term_act *
144: man_term_act(enum roff_tok tok)
145: {
146: assert(tok >= MAN_TH && tok <= MAN_MAX);
147: return man_term_acts + (tok - MAN_TH);
148: }
149:
1.31 kristaps 150: void
1.223 schwarze 151: terminal_man(void *arg, const struct roff_meta *man)
1.1 kristaps 152: {
1.227 schwarze 153: struct mtermp mt;
1.36 kristaps 154: struct termp *p;
1.229 schwarze 155: struct roff_node *n, *nc, *nn;
1.36 kristaps 156:
157: p = (struct termp *)arg;
1.203 schwarze 158: p->tcol->rmargin = p->maxrmargin = p->defrmargin;
1.198 schwarze 159: term_tab_set(p, NULL);
160: term_tab_set(p, "T");
161: term_tab_set(p, ".5i");
1.36 kristaps 162:
1.227 schwarze 163: memset(&mt, 0, sizeof(mt));
1.244 schwarze 164: mt.lmargin[mt.lmargincur] = term_len(p, 7);
1.122 schwarze 165: mt.offset = term_len(p, p->defindent);
1.134 schwarze 166: mt.pardist = 1;
1.24 kristaps 167:
1.181 schwarze 168: n = man->first->child;
1.151 schwarze 169: if (p->synopsisonly) {
1.229 schwarze 170: for (nn = NULL; n != NULL; n = n->next) {
171: if (n->tok != MAN_SH)
172: continue;
173: nc = n->child->child;
174: if (nc->type != ROFFT_TEXT)
175: continue;
176: if (strcmp(nc->string, "SYNOPSIS") == 0)
1.151 schwarze 177: break;
1.229 schwarze 178: if (nn == NULL && strcmp(nc->string, "NAME") == 0)
179: nn = n;
1.151 schwarze 180: }
1.229 schwarze 181: if (n == NULL)
182: n = nn;
183: p->flags |= TERMP_NOSPACE;
184: if (n != NULL && (n = n->child->next->child) != NULL)
185: print_man_nodelist(p, &mt, n, man);
186: term_newln(p);
1.151 schwarze 187: } else {
1.223 schwarze 188: term_begin(p, print_man_head, print_man_foot, man);
1.151 schwarze 189: p->flags |= TERMP_NOSPACE;
190: if (n != NULL)
1.223 schwarze 191: print_man_nodelist(p, &mt, n, man);
1.151 schwarze 192: term_end(p);
193: }
1.1 kristaps 194: }
195:
1.111 kristaps 196: /*
1.246 ! schwarze 197: * Print leading vertical space before a paragraph, unless
! 198: * it is the first paragraph in a section or subsection.
! 199: * If it is the first paragraph in an .RS block, consider
! 200: * that .RS block instead of the paragraph, recursively.
1.111 kristaps 201: */
1.39 kristaps 202: static void
1.234 schwarze 203: print_bvspace(struct termp *p, struct roff_node *n, int pardist)
1.18 kristaps 204: {
1.234 schwarze 205: struct roff_node *nch;
206: int i;
1.111 kristaps 207:
1.39 kristaps 208: term_newln(p);
1.101 schwarze 209:
1.234 schwarze 210: if (n->body != NULL &&
211: (nch = roff_node_child(n->body)) != NULL &&
212: nch->type == ROFFT_TBL)
213: return;
214:
1.246 ! schwarze 215: while (roff_node_prev(n) == NULL) {
! 216: n = n->parent;
! 217: if (n->tok != MAN_RS)
! 218: return;
! 219: if (n->type == ROFFT_BODY)
! 220: n = n->parent;
! 221: }
1.134 schwarze 222: for (i = 0; i < pardist; i++)
223: term_vspace(p);
1.221 schwarze 224: }
1.146 schwarze 225:
1.1 kristaps 226: static int
1.29 kristaps 227: pre_ign(DECL_ARGS)
228: {
1.185 schwarze 229: return 0;
1.29 kristaps 230: }
231:
232: static int
1.1 kristaps 233: pre_I(DECL_ARGS)
234: {
1.52 kristaps 235: term_fontrepl(p, TERMFONT_UNDER);
1.185 schwarze 236: return 1;
1.19 kristaps 237: }
238:
1.3 kristaps 239: static int
1.84 kristaps 240: pre_literal(DECL_ARGS)
1.19 kristaps 241: {
1.81 kristaps 242: term_newln(p);
1.88 kristaps 243:
1.117 schwarze 244: /*
245: * Unlike .IP and .TP, .HP does not have a HEAD.
246: * So in case a second call to term_flushln() is needed,
247: * indentation has to be set up explicitly.
248: */
1.203 schwarze 249: if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) {
250: p->tcol->offset = p->tcol->rmargin;
251: p->tcol->rmargin = p->maxrmargin;
1.139 schwarze 252: p->trailspace = 0;
1.145 schwarze 253: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.117 schwarze 254: p->flags |= TERMP_NOSPACE;
255: }
1.185 schwarze 256: return 0;
1.19 kristaps 257: }
258:
259: static int
1.134 schwarze 260: pre_PD(DECL_ARGS)
261: {
1.160 schwarze 262: struct roffsu su;
1.134 schwarze 263:
264: n = n->child;
1.160 schwarze 265: if (n == NULL) {
1.134 schwarze 266: mt->pardist = 1;
1.185 schwarze 267: return 0;
1.134 schwarze 268: }
1.171 schwarze 269: assert(n->type == ROFFT_TEXT);
1.204 schwarze 270: if (a2roffsu(n->string, &su, SCALE_VS) != NULL)
1.160 schwarze 271: mt->pardist = term_vspan(p, &su);
1.185 schwarze 272: return 0;
1.134 schwarze 273: }
274:
275: static int
1.88 kristaps 276: pre_alternate(DECL_ARGS)
1.3 kristaps 277: {
1.88 kristaps 278: enum termfont font[2];
1.172 schwarze 279: struct roff_node *nn;
1.225 schwarze 280: int i;
1.3 kristaps 281:
1.88 kristaps 282: switch (n->tok) {
1.146 schwarze 283: case MAN_RB:
1.88 kristaps 284: font[0] = TERMFONT_NONE;
285: font[1] = TERMFONT_BOLD;
286: break;
1.146 schwarze 287: case MAN_RI:
1.88 kristaps 288: font[0] = TERMFONT_NONE;
289: font[1] = TERMFONT_UNDER;
290: break;
1.146 schwarze 291: case MAN_BR:
1.88 kristaps 292: font[0] = TERMFONT_BOLD;
293: font[1] = TERMFONT_NONE;
294: break;
1.146 schwarze 295: case MAN_BI:
1.88 kristaps 296: font[0] = TERMFONT_BOLD;
297: font[1] = TERMFONT_UNDER;
298: break;
1.146 schwarze 299: case MAN_IR:
1.88 kristaps 300: font[0] = TERMFONT_UNDER;
301: font[1] = TERMFONT_NONE;
302: break;
1.146 schwarze 303: case MAN_IB:
1.88 kristaps 304: font[0] = TERMFONT_UNDER;
305: font[1] = TERMFONT_BOLD;
306: break;
307: default:
308: abort();
309: }
1.225 schwarze 310: for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) {
1.88 kristaps 311: term_fontrepl(p, font[i]);
1.179 schwarze 312: assert(nn->type == ROFFT_TEXT);
313: term_word(p, nn->string);
1.188 schwarze 314: if (nn->flags & NODE_EOS)
1.230 schwarze 315: p->flags |= TERMP_SENTENCE;
1.227 schwarze 316: if (nn->next != NULL)
1.4 kristaps 317: p->flags |= TERMP_NOSPACE;
1.3 kristaps 318: }
1.185 schwarze 319: return 0;
1.3 kristaps 320: }
321:
1.1 kristaps 322: static int
323: pre_B(DECL_ARGS)
324: {
1.52 kristaps 325: term_fontrepl(p, TERMFONT_BOLD);
1.185 schwarze 326: return 1;
1.243 schwarze 327: }
328:
329: static int
330: pre_MR(DECL_ARGS)
331: {
332: term_fontrepl(p, TERMFONT_NONE);
333: n = n->child;
334: if (n != NULL) {
335: term_word(p, n->string); /* name */
336: p->flags |= TERMP_NOSPACE;
337: }
338: term_word(p, "(");
339: p->flags |= TERMP_NOSPACE;
340: if (n != NULL && (n = n->next) != NULL) {
341: term_word(p, n->string); /* section */
342: p->flags |= TERMP_NOSPACE;
343: }
344: term_word(p, ")");
345: if (n != NULL && (n = n->next) != NULL) {
346: p->flags |= TERMP_NOSPACE;
347: term_word(p, n->string); /* suffix */
348: }
349: return 0;
1.127 kristaps 350: }
351:
352: static int
353: pre_OP(DECL_ARGS)
354: {
355: term_word(p, "[");
1.216 schwarze 356: p->flags |= TERMP_KEEP | TERMP_NOSPACE;
1.127 kristaps 357:
1.227 schwarze 358: if ((n = n->child) != NULL) {
1.127 kristaps 359: term_fontrepl(p, TERMFONT_BOLD);
360: term_word(p, n->string);
361: }
1.227 schwarze 362: if (n != NULL && n->next != NULL) {
1.127 kristaps 363: term_fontrepl(p, TERMFONT_UNDER);
364: term_word(p, n->next->string);
365: }
366: term_fontrepl(p, TERMFONT_NONE);
1.216 schwarze 367: p->flags &= ~TERMP_KEEP;
1.127 kristaps 368: p->flags |= TERMP_NOSPACE;
369: term_word(p, "]");
1.185 schwarze 370: return 0;
1.83 kristaps 371: }
372:
373: static int
374: pre_in(DECL_ARGS)
375: {
1.163 schwarze 376: struct roffsu su;
377: const char *cp;
1.83 kristaps 378: size_t v;
1.163 schwarze 379: int less;
1.83 kristaps 380:
381: term_newln(p);
382:
1.203 schwarze 383: if (n->child == NULL) {
384: p->tcol->offset = mt->offset;
1.185 schwarze 385: return 0;
1.83 kristaps 386: }
387:
388: cp = n->child->string;
389: less = 0;
390:
1.227 schwarze 391: if (*cp == '-')
1.83 kristaps 392: less = -1;
1.227 schwarze 393: else if (*cp == '+')
1.83 kristaps 394: less = 1;
395: else
396: cp--;
397:
1.204 schwarze 398: if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
1.185 schwarze 399: return 0;
1.83 kristaps 400:
1.205 schwarze 401: v = term_hen(p, &su);
1.83 kristaps 402:
403: if (less < 0)
1.203 schwarze 404: p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
1.83 kristaps 405: else if (less > 0)
1.203 schwarze 406: p->tcol->offset += v;
1.146 schwarze 407: else
1.203 schwarze 408: p->tcol->offset = v;
409: if (p->tcol->offset > SHRT_MAX)
410: p->tcol->offset = term_len(p, p->defindent);
1.19 kristaps 411:
1.199 schwarze 412: return 0;
413: }
414:
415: static int
416: pre_DT(DECL_ARGS)
417: {
418: term_tab_set(p, NULL);
419: term_tab_set(p, "T");
420: term_tab_set(p, ".5i");
1.185 schwarze 421: return 0;
1.15 kristaps 422: }
423:
424: static int
1.19 kristaps 425: pre_HP(DECL_ARGS)
426: {
1.163 schwarze 427: struct roffsu su;
1.172 schwarze 428: const struct roff_node *nn;
1.163 schwarze 429: int len;
1.19 kristaps 430:
1.20 kristaps 431: switch (n->type) {
1.171 schwarze 432: case ROFFT_BLOCK:
1.134 schwarze 433: print_bvspace(p, n, mt->pardist);
1.185 schwarze 434: return 1;
1.227 schwarze 435: case ROFFT_HEAD:
436: return 0;
1.171 schwarze 437: case ROFFT_BODY:
1.20 kristaps 438: break;
439: default:
1.227 schwarze 440: abort();
1.20 kristaps 441: }
442:
1.226 schwarze 443: if (n->child == NULL)
444: return 0;
445:
446: if ((n->child->flags & NODE_NOFILL) == 0) {
1.145 schwarze 447: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.139 schwarze 448: p->trailspace = 2;
1.130 schwarze 449: }
450:
1.24 kristaps 451: /* Calculate offset. */
452:
1.163 schwarze 453: if ((nn = n->parent->head->child) != NULL &&
1.204 schwarze 454: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.205 schwarze 455: len = term_hen(p, &su);
1.164 schwarze 456: if (len < 0 && (size_t)(-len) > mt->offset)
457: len = -mt->offset;
458: else if (len > SHRT_MAX)
459: len = term_len(p, p->defindent);
1.163 schwarze 460: mt->lmargin[mt->lmargincur] = len;
461: } else
462: len = mt->lmargin[mt->lmargincur];
1.24 kristaps 463:
1.203 schwarze 464: p->tcol->offset = mt->offset;
465: p->tcol->rmargin = mt->offset + len;
1.185 schwarze 466: return 1;
1.19 kristaps 467: }
468:
1.20 kristaps 469: static void
470: post_HP(DECL_ARGS)
471: {
472: switch (n->type) {
1.227 schwarze 473: case ROFFT_BLOCK:
474: case ROFFT_HEAD:
475: break;
1.171 schwarze 476: case ROFFT_BODY:
1.136 schwarze 477: term_newln(p);
1.174 schwarze 478:
479: /*
480: * Compatibility with a groff bug.
481: * The .HP macro uses the undocumented .tag request
482: * which causes a line break and cancels no-space
483: * mode even if there isn't any output.
484: */
485:
486: if (n->child == NULL)
487: term_vspace(p);
488:
1.145 schwarze 489: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.139 schwarze 490: p->trailspace = 0;
1.203 schwarze 491: p->tcol->offset = mt->offset;
492: p->tcol->rmargin = p->maxrmargin;
1.20 kristaps 493: break;
494: default:
1.227 schwarze 495: abort();
1.20 kristaps 496: }
497: }
498:
1.19 kristaps 499: static int
1.1 kristaps 500: pre_PP(DECL_ARGS)
501: {
1.19 kristaps 502: switch (n->type) {
1.171 schwarze 503: case ROFFT_BLOCK:
1.244 schwarze 504: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.134 schwarze 505: print_bvspace(p, n, mt->pardist);
1.19 kristaps 506: break;
1.227 schwarze 507: case ROFFT_HEAD:
508: return 0;
509: case ROFFT_BODY:
1.203 schwarze 510: p->tcol->offset = mt->offset;
1.19 kristaps 511: break;
1.227 schwarze 512: default:
513: abort();
1.19 kristaps 514: }
1.227 schwarze 515: return 1;
1.1 kristaps 516: }
517:
518: static int
1.4 kristaps 519: pre_IP(DECL_ARGS)
520: {
1.163 schwarze 521: struct roffsu su;
1.172 schwarze 522: const struct roff_node *nn;
1.225 schwarze 523: int len;
1.18 kristaps 524:
1.22 kristaps 525: switch (n->type) {
1.227 schwarze 526: case ROFFT_BLOCK:
527: print_bvspace(p, n, mt->pardist);
528: return 1;
1.171 schwarze 529: case ROFFT_HEAD:
1.22 kristaps 530: p->flags |= TERMP_NOBREAK;
1.139 schwarze 531: p->trailspace = 1;
1.22 kristaps 532: break;
1.227 schwarze 533: case ROFFT_BODY:
1.239 schwarze 534: p->flags |= TERMP_NOSPACE | TERMP_NONEWLINE;
1.227 schwarze 535: break;
1.22 kristaps 536: default:
1.227 schwarze 537: abort();
1.22 kristaps 538: }
1.18 kristaps 539:
1.94 schwarze 540: /* Calculate the offset from the optional second argument. */
1.163 schwarze 541: if ((nn = n->parent->head->child) != NULL &&
542: (nn = nn->next) != NULL &&
1.204 schwarze 543: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.205 schwarze 544: len = term_hen(p, &su);
1.163 schwarze 545: if (len < 0 && (size_t)(-len) > mt->offset)
546: len = -mt->offset;
1.164 schwarze 547: else if (len > SHRT_MAX)
548: len = term_len(p, p->defindent);
549: mt->lmargin[mt->lmargincur] = len;
1.163 schwarze 550: } else
551: len = mt->lmargin[mt->lmargincur];
1.4 kristaps 552:
1.22 kristaps 553: switch (n->type) {
1.171 schwarze 554: case ROFFT_HEAD:
1.203 schwarze 555: p->tcol->offset = mt->offset;
556: p->tcol->rmargin = mt->offset + len;
1.235 schwarze 557: if (n->child != NULL)
1.135 schwarze 558: print_man_node(p, mt, n->child, meta);
1.185 schwarze 559: return 0;
1.171 schwarze 560: case ROFFT_BODY:
1.203 schwarze 561: p->tcol->offset = mt->offset + len;
562: p->tcol->rmargin = p->maxrmargin;
1.23 kristaps 563: break;
1.22 kristaps 564: default:
1.227 schwarze 565: abort();
1.18 kristaps 566: }
1.185 schwarze 567: return 1;
1.22 kristaps 568: }
1.18 kristaps 569:
1.22 kristaps 570: static void
571: post_IP(DECL_ARGS)
572: {
573: switch (n->type) {
1.227 schwarze 574: case ROFFT_BLOCK:
575: break;
1.171 schwarze 576: case ROFFT_HEAD:
1.22 kristaps 577: term_flushln(p);
578: p->flags &= ~TERMP_NOBREAK;
1.139 schwarze 579: p->trailspace = 0;
1.203 schwarze 580: p->tcol->rmargin = p->maxrmargin;
1.22 kristaps 581: break;
1.171 schwarze 582: case ROFFT_BODY:
1.94 schwarze 583: term_newln(p);
1.203 schwarze 584: p->tcol->offset = mt->offset;
1.22 kristaps 585: break;
586: default:
1.227 schwarze 587: abort();
1.22 kristaps 588: }
1.4 kristaps 589: }
590:
591: static int
1.1 kristaps 592: pre_TP(DECL_ARGS)
593: {
1.163 schwarze 594: struct roffsu su;
1.172 schwarze 595: struct roff_node *nn;
1.225 schwarze 596: int len;
1.1 kristaps 597:
1.21 kristaps 598: switch (n->type) {
1.227 schwarze 599: case ROFFT_BLOCK:
600: if (n->tok == MAN_TP)
601: print_bvspace(p, n, mt->pardist);
602: return 1;
1.171 schwarze 603: case ROFFT_HEAD:
1.184 schwarze 604: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
1.139 schwarze 605: p->trailspace = 1;
1.21 kristaps 606: break;
1.171 schwarze 607: case ROFFT_BODY:
1.239 schwarze 608: p->flags |= TERMP_NOSPACE | TERMP_NONEWLINE;
1.23 kristaps 609: break;
610: default:
1.227 schwarze 611: abort();
1.23 kristaps 612: }
613:
614: /* Calculate offset. */
615:
1.163 schwarze 616: if ((nn = n->parent->head->child) != NULL &&
1.188 schwarze 617: nn->string != NULL && ! (NODE_LINE & nn->flags) &&
1.204 schwarze 618: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.205 schwarze 619: len = term_hen(p, &su);
1.163 schwarze 620: if (len < 0 && (size_t)(-len) > mt->offset)
621: len = -mt->offset;
1.164 schwarze 622: else if (len > SHRT_MAX)
623: len = term_len(p, p->defindent);
624: mt->lmargin[mt->lmargincur] = len;
1.163 schwarze 625: } else
626: len = mt->lmargin[mt->lmargincur];
1.23 kristaps 627:
628: switch (n->type) {
1.171 schwarze 629: case ROFFT_HEAD:
1.203 schwarze 630: p->tcol->offset = mt->offset;
631: p->tcol->rmargin = mt->offset + len;
1.23 kristaps 632:
633: /* Don't print same-line elements. */
1.141 schwarze 634: nn = n->child;
1.227 schwarze 635: while (nn != NULL && (nn->flags & NODE_LINE) == 0)
1.141 schwarze 636: nn = nn->next;
1.232 schwarze 637:
1.227 schwarze 638: while (nn != NULL) {
1.141 schwarze 639: print_man_node(p, mt, nn, meta);
640: nn = nn->next;
641: }
1.185 schwarze 642: return 0;
1.171 schwarze 643: case ROFFT_BODY:
1.203 schwarze 644: p->tcol->offset = mt->offset + len;
645: p->tcol->rmargin = p->maxrmargin;
1.139 schwarze 646: p->trailspace = 0;
1.184 schwarze 647: p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
1.21 kristaps 648: break;
649: default:
1.227 schwarze 650: abort();
1.21 kristaps 651: }
1.185 schwarze 652: return 1;
1.21 kristaps 653: }
1.1 kristaps 654:
1.21 kristaps 655: static void
656: post_TP(DECL_ARGS)
657: {
658: switch (n->type) {
1.227 schwarze 659: case ROFFT_BLOCK:
660: break;
1.171 schwarze 661: case ROFFT_HEAD:
1.21 kristaps 662: term_flushln(p);
663: break;
1.171 schwarze 664: case ROFFT_BODY:
1.94 schwarze 665: term_newln(p);
1.203 schwarze 666: p->tcol->offset = mt->offset;
1.21 kristaps 667: break;
668: default:
1.227 schwarze 669: abort();
1.21 kristaps 670: }
1.1 kristaps 671: }
672:
673: static int
674: pre_SS(DECL_ARGS)
675: {
1.134 schwarze 676: int i;
1.1 kristaps 677:
1.19 kristaps 678: switch (n->type) {
1.171 schwarze 679: case ROFFT_BLOCK:
1.244 schwarze 680: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.122 schwarze 681: mt->offset = term_len(p, p->defindent);
1.158 schwarze 682:
683: /*
684: * No vertical space before the first subsection
685: * and after an empty subsection.
686: */
687:
1.234 schwarze 688: if ((n = roff_node_prev(n)) == NULL ||
689: (n->tok == MAN_SS && roff_node_child(n->body) == NULL))
1.24 kristaps 690: break;
1.158 schwarze 691:
1.134 schwarze 692: for (i = 0; i < mt->pardist; i++)
693: term_vspace(p);
1.19 kristaps 694: break;
1.171 schwarze 695: case ROFFT_HEAD:
1.52 kristaps 696: term_fontrepl(p, TERMFONT_BOLD);
1.203 schwarze 697: p->tcol->offset = term_len(p, 3);
698: p->tcol->rmargin = mt->offset;
1.176 schwarze 699: p->trailspace = mt->offset;
700: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.19 kristaps 701: break;
1.171 schwarze 702: case ROFFT_BODY:
1.203 schwarze 703: p->tcol->offset = mt->offset;
704: p->tcol->rmargin = p->maxrmargin;
1.176 schwarze 705: p->trailspace = 0;
706: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.24 kristaps 707: break;
1.19 kristaps 708: default:
709: break;
710: }
1.185 schwarze 711: return 1;
1.1 kristaps 712: }
713:
714: static int
715: pre_SH(DECL_ARGS)
716: {
1.134 schwarze 717: int i;
1.22 kristaps 718:
1.19 kristaps 719: switch (n->type) {
1.171 schwarze 720: case ROFFT_BLOCK:
1.244 schwarze 721: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.122 schwarze 722: mt->offset = term_len(p, p->defindent);
1.158 schwarze 723:
724: /*
725: * No vertical space before the first section
726: * and after an empty section.
727: */
728:
1.234 schwarze 729: if ((n = roff_node_prev(n)) == NULL ||
730: (n->tok == MAN_SH && roff_node_child(n->body) == NULL))
1.61 kristaps 731: break;
1.158 schwarze 732:
1.134 schwarze 733: for (i = 0; i < mt->pardist; i++)
734: term_vspace(p);
1.19 kristaps 735: break;
1.171 schwarze 736: case ROFFT_HEAD:
1.52 kristaps 737: term_fontrepl(p, TERMFONT_BOLD);
1.203 schwarze 738: p->tcol->offset = 0;
739: p->tcol->rmargin = mt->offset;
1.176 schwarze 740: p->trailspace = mt->offset;
741: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.19 kristaps 742: break;
1.171 schwarze 743: case ROFFT_BODY:
1.203 schwarze 744: p->tcol->offset = mt->offset;
745: p->tcol->rmargin = p->maxrmargin;
1.176 schwarze 746: p->trailspace = 0;
747: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.19 kristaps 748: break;
749: default:
1.227 schwarze 750: abort();
1.19 kristaps 751: }
1.185 schwarze 752: return 1;
1.1 kristaps 753: }
754:
755: static void
756: post_SH(DECL_ARGS)
757: {
1.19 kristaps 758: switch (n->type) {
1.227 schwarze 759: case ROFFT_BLOCK:
760: break;
1.171 schwarze 761: case ROFFT_HEAD:
762: case ROFFT_BODY:
1.19 kristaps 763: term_newln(p);
764: break;
765: default:
1.227 schwarze 766: abort();
1.19 kristaps 767: }
1.1 kristaps 768: }
769:
1.26 kristaps 770: static int
771: pre_RS(DECL_ARGS)
772: {
1.163 schwarze 773: struct roffsu su;
1.26 kristaps 774:
775: switch (n->type) {
1.171 schwarze 776: case ROFFT_BLOCK:
1.26 kristaps 777: term_newln(p);
1.185 schwarze 778: return 1;
1.171 schwarze 779: case ROFFT_HEAD:
1.185 schwarze 780: return 0;
1.227 schwarze 781: case ROFFT_BODY:
782: break;
1.26 kristaps 783: default:
1.227 schwarze 784: abort();
1.26 kristaps 785: }
786:
1.165 schwarze 787: n = n->parent->head;
788: n->aux = SHRT_MAX + 1;
1.177 schwarze 789: if (n->child == NULL)
790: n->aux = mt->lmargin[mt->lmargincur];
1.204 schwarze 791: else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
1.205 schwarze 792: n->aux = term_hen(p, &su);
1.165 schwarze 793: if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
794: n->aux = -mt->offset;
795: else if (n->aux > SHRT_MAX)
796: n->aux = term_len(p, p->defindent);
1.26 kristaps 797:
1.165 schwarze 798: mt->offset += n->aux;
1.203 schwarze 799: p->tcol->offset = mt->offset;
800: p->tcol->rmargin = p->maxrmargin;
1.26 kristaps 801:
1.116 schwarze 802: if (++mt->lmarginsz < MAXMARGINS)
803: mt->lmargincur = mt->lmarginsz;
804:
1.244 schwarze 805: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.185 schwarze 806: return 1;
1.26 kristaps 807: }
808:
809: static void
810: post_RS(DECL_ARGS)
811: {
812: switch (n->type) {
1.171 schwarze 813: case ROFFT_BLOCK:
814: case ROFFT_HEAD:
1.110 kristaps 815: return;
1.227 schwarze 816: case ROFFT_BODY:
817: break;
1.26 kristaps 818: default:
1.227 schwarze 819: abort();
1.26 kristaps 820: }
1.227 schwarze 821: term_newln(p);
1.165 schwarze 822: mt->offset -= n->parent->head->aux;
1.203 schwarze 823: p->tcol->offset = mt->offset;
1.116 schwarze 824: if (--mt->lmarginsz < MAXMARGINS)
825: mt->lmargincur = mt->lmarginsz;
1.215 schwarze 826: }
827:
828: static int
829: pre_SY(DECL_ARGS)
830: {
831: const struct roff_node *nn;
832: int len;
833:
834: switch (n->type) {
835: case ROFFT_BLOCK:
1.234 schwarze 836: if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY)
1.217 schwarze 837: print_bvspace(p, n, mt->pardist);
1.215 schwarze 838: return 1;
839: case ROFFT_HEAD:
840: case ROFFT_BODY:
841: break;
842: default:
843: abort();
844: }
845:
846: nn = n->parent->head->child;
1.219 schwarze 847: len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
1.215 schwarze 848:
849: switch (n->type) {
850: case ROFFT_HEAD:
851: p->tcol->offset = mt->offset;
852: p->tcol->rmargin = mt->offset + len;
1.228 schwarze 853: if (n->next->child == NULL ||
854: (n->next->child->flags & NODE_NOFILL) == 0)
855: p->flags |= TERMP_NOBREAK;
1.215 schwarze 856: term_fontrepl(p, TERMFONT_BOLD);
857: break;
858: case ROFFT_BODY:
859: mt->lmargin[mt->lmargincur] = len;
860: p->tcol->offset = mt->offset + len;
861: p->tcol->rmargin = p->maxrmargin;
862: p->flags |= TERMP_NOSPACE;
863: break;
864: default:
865: abort();
866: }
867: return 1;
868: }
869:
870: static void
871: post_SY(DECL_ARGS)
872: {
873: switch (n->type) {
1.227 schwarze 874: case ROFFT_BLOCK:
875: break;
1.215 schwarze 876: case ROFFT_HEAD:
877: term_flushln(p);
878: p->flags &= ~TERMP_NOBREAK;
879: break;
880: case ROFFT_BODY:
881: term_newln(p);
882: p->tcol->offset = mt->offset;
883: break;
884: default:
1.227 schwarze 885: abort();
1.215 schwarze 886: }
1.137 schwarze 887: }
888:
889: static int
890: pre_UR(DECL_ARGS)
891: {
1.185 schwarze 892: return n->type != ROFFT_HEAD;
1.137 schwarze 893: }
894:
895: static void
896: post_UR(DECL_ARGS)
897: {
1.171 schwarze 898: if (n->type != ROFFT_BLOCK)
1.137 schwarze 899: return;
900:
901: term_word(p, "<");
902: p->flags |= TERMP_NOSPACE;
903:
1.227 schwarze 904: if (n->child->child != NULL)
1.137 schwarze 905: print_man_node(p, mt, n->child->child, meta);
906:
907: p->flags |= TERMP_NOSPACE;
908: term_word(p, ">");
1.26 kristaps 909: }
910:
1.1 kristaps 911: static void
1.45 kristaps 912: print_man_node(DECL_ARGS)
1.1 kristaps 913: {
1.214 schwarze 914: const struct man_term_act *act;
915: int c;
1.1 kristaps 916:
1.239 schwarze 917: /*
918: * In no-fill mode, break the output line at the beginning
919: * of new input lines except after \c, and nowhere else.
920: */
921:
922: if (n->flags & NODE_NOFILL) {
923: if (n->flags & NODE_LINE &&
924: (p->flags & TERMP_NONEWLINE) == 0)
925: term_newln(p);
926: p->flags |= TERMP_BRNEVER;
1.240 schwarze 927: } else {
928: if (n->flags & NODE_LINE)
929: term_tab_ref(p);
1.239 schwarze 930: p->flags &= ~TERMP_BRNEVER;
1.240 schwarze 931: }
1.239 schwarze 932:
1.235 schwarze 933: if (n->flags & NODE_ID)
934: term_tag_write(n, p->line);
935:
1.1 kristaps 936: switch (n->type) {
1.171 schwarze 937: case ROFFT_TEXT:
1.97 kristaps 938: /*
939: * If we have a blank line, output a vertical space.
940: * If we have a space as the first character, break
941: * before printing the line's data.
942: */
1.200 schwarze 943: if (*n->string == '\0') {
1.207 schwarze 944: if (p->flags & TERMP_NONEWLINE)
945: term_newln(p);
946: else
947: term_vspace(p);
1.98 schwarze 948: return;
1.200 schwarze 949: } else if (*n->string == ' ' && n->flags & NODE_LINE &&
950: (p->flags & TERMP_NONEWLINE) == 0)
1.96 kristaps 951: term_newln(p);
1.212 schwarze 952: else if (n->flags & NODE_DELIMC)
953: p->flags |= TERMP_NOSPACE;
1.54 kristaps 954:
1.4 kristaps 955: term_word(p, n->string);
1.130 schwarze 956: goto out;
1.210 schwarze 957: case ROFFT_COMMENT:
958: return;
1.171 schwarze 959: case ROFFT_EQN:
1.188 schwarze 960: if ( ! (n->flags & NODE_LINE))
1.152 schwarze 961: p->flags |= TERMP_NOSPACE;
1.115 kristaps 962: term_eqn(p, n->eqn);
1.188 schwarze 963: if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.153 schwarze 964: p->flags |= TERMP_NOSPACE;
1.97 kristaps 965: return;
1.171 schwarze 966: case ROFFT_TBL:
1.169 schwarze 967: if (p->tbl.cols == NULL)
1.241 schwarze 968: term_newln(p);
1.92 kristaps 969: term_tbl(p, n->span);
1.97 kristaps 970: return;
1.1 kristaps 971: default:
972: break;
973: }
1.224 schwarze 974:
1.193 schwarze 975: if (n->tok < ROFF_MAX) {
1.194 schwarze 976: roff_term_pre(p, n);
1.193 schwarze 977: return;
978: }
979:
1.214 schwarze 980: act = man_term_act(n->tok);
1.220 schwarze 981: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.97 kristaps 982: term_fontrepl(p, TERMFONT_NONE);
983:
984: c = 1;
1.214 schwarze 985: if (act->pre != NULL)
986: c = (*act->pre)(p, mt, n, meta);
1.97 kristaps 987:
1.227 schwarze 988: if (c && n->child != NULL)
1.135 schwarze 989: print_man_nodelist(p, mt, n->child, meta);
1.1 kristaps 990:
1.214 schwarze 991: if (act->post != NULL)
992: (*act->post)(p, mt, n, meta);
1.220 schwarze 993: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.97 kristaps 994: term_fontrepl(p, TERMFONT_NONE);
1.63 kristaps 995:
1.130 schwarze 996: out:
1.239 schwarze 997: if (n->parent->tok == MAN_HP && n->parent->type == ROFFT_BODY &&
998: n->prev == NULL && n->flags & NODE_NOFILL) {
999: term_newln(p);
1000: p->tcol->offset = p->tcol->rmargin;
1001: p->tcol->rmargin = p->maxrmargin;
1.130 schwarze 1002: }
1.227 schwarze 1003: if (n->flags & NODE_EOS)
1.63 kristaps 1004: p->flags |= TERMP_SENTENCE;
1.1 kristaps 1005: }
1006:
1007: static void
1.52 kristaps 1008: print_man_nodelist(DECL_ARGS)
1.1 kristaps 1009: {
1.168 schwarze 1010: while (n != NULL) {
1011: print_man_node(p, mt, n, meta);
1012: n = n->next;
1013: }
1.1 kristaps 1014: }
1015:
1.31 kristaps 1016: static void
1.173 schwarze 1017: print_man_foot(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1018: {
1.147 schwarze 1019: char *title;
1.156 schwarze 1020: size_t datelen, titlen;
1.73 kristaps 1021:
1.245 schwarze 1022: assert(meta->title != NULL);
1023: assert(meta->msec != NULL);
1.1 kristaps 1024:
1.52 kristaps 1025: term_fontrepl(p, TERMFONT_NONE);
1.149 schwarze 1026: if (meta->hasbody)
1027: term_vspace(p);
1.126 schwarze 1028:
1.125 schwarze 1029: datelen = term_strlen(p, meta->date);
1.245 schwarze 1030: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1031: titlen = term_strlen(p, title);
1.1 kristaps 1032:
1.173 schwarze 1033: /* Bottom left corner: operating system. */
1.126 schwarze 1034:
1.203 schwarze 1035: p->tcol->offset = 0;
1036: p->tcol->rmargin = p->maxrmargin > datelen ?
1.156 schwarze 1037: (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1.245 schwarze 1038: p->trailspace = 1;
1039: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.1 kristaps 1040:
1.173 schwarze 1041: if (meta->os)
1042: term_word(p, meta->os);
1.1 kristaps 1043: term_flushln(p);
1044:
1.126 schwarze 1045: /* At the bottom in the middle: manual date. */
1046:
1.203 schwarze 1047: p->tcol->offset = p->tcol->rmargin;
1048: p->tcol->rmargin = p->maxrmargin > titlen ?
1049: p->maxrmargin - titlen : 0;
1.117 schwarze 1050: p->flags |= TERMP_NOSPACE;
1.123 schwarze 1051:
1.125 schwarze 1052: term_word(p, meta->date);
1.123 schwarze 1053: term_flushln(p);
1054:
1.126 schwarze 1055: /* Bottom right corner: manual title and section. */
1056:
1.245 schwarze 1057: p->tcol->offset = p->tcol->rmargin;
1058: p->tcol->rmargin = p->maxrmargin;
1059: p->trailspace = 0;
1.123 schwarze 1060: p->flags &= ~TERMP_NOBREAK;
1061: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1062:
1.123 schwarze 1063: term_word(p, title);
1.1 kristaps 1064: term_flushln(p);
1.211 schwarze 1065:
1066: /*
1067: * Reset the terminal state for more output after the footer:
1068: * Some output modes, in particular PostScript and PDF, print
1069: * the header and the footer into a buffer such that it can be
1070: * reused for multiple output pages, then go on to format the
1071: * main text.
1072: */
1073:
1074: p->tcol->offset = 0;
1075: p->flags = 0;
1.147 schwarze 1076: free(title);
1.1 kristaps 1077: }
1078:
1.31 kristaps 1079: static void
1.173 schwarze 1080: print_man_head(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1081: {
1.148 schwarze 1082: const char *volume;
1.147 schwarze 1083: char *title;
1.148 schwarze 1084: size_t vollen, titlen;
1.73 kristaps 1085:
1.135 schwarze 1086: assert(meta->title);
1087: assert(meta->msec);
1.1 kristaps 1088:
1.148 schwarze 1089: volume = NULL == meta->vol ? "" : meta->vol;
1090: vollen = term_strlen(p, volume);
1.1 kristaps 1091:
1.126 schwarze 1092: /* Top left corner: manual title and section. */
1093:
1.147 schwarze 1094: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.77 kristaps 1095: titlen = term_strlen(p, title);
1.1 kristaps 1096:
1.118 schwarze 1097: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.139 schwarze 1098: p->trailspace = 1;
1.203 schwarze 1099: p->tcol->offset = 0;
1100: p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1.148 schwarze 1101: (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.156 schwarze 1102: vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1.1 kristaps 1103:
1104: term_word(p, title);
1105: term_flushln(p);
1106:
1.126 schwarze 1107: /* At the top in the middle: manual volume. */
1108:
1.117 schwarze 1109: p->flags |= TERMP_NOSPACE;
1.203 schwarze 1110: p->tcol->offset = p->tcol->rmargin;
1111: p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1112: p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1113:
1.148 schwarze 1114: term_word(p, volume);
1.1 kristaps 1115: term_flushln(p);
1116:
1.126 schwarze 1117: /* Top right corner: title and section, again. */
1118:
1.1 kristaps 1119: p->flags &= ~TERMP_NOBREAK;
1.139 schwarze 1120: p->trailspace = 0;
1.203 schwarze 1121: if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1.117 schwarze 1122: p->flags |= TERMP_NOSPACE;
1.203 schwarze 1123: p->tcol->offset = p->tcol->rmargin;
1124: p->tcol->rmargin = p->maxrmargin;
1.57 kristaps 1125: term_word(p, title);
1126: term_flushln(p);
1127: }
1.1 kristaps 1128:
1.118 schwarze 1129: p->flags &= ~TERMP_NOSPACE;
1.203 schwarze 1130: p->tcol->offset = 0;
1131: p->tcol->rmargin = p->maxrmargin;
1.61 kristaps 1132:
1.146 schwarze 1133: /*
1.126 schwarze 1134: * Groff prints three blank lines before the content.
1135: * Do the same, except in the temporary, undocumented
1136: * mode imitating mdoc(7) output.
1.62 kristaps 1137: */
1138:
1.61 kristaps 1139: term_vspace(p);
1.147 schwarze 1140: free(title);
1.1 kristaps 1141: }
CVSweb