Annotation of mandoc/man_term.c, Revision 1.247
1.247 ! schwarze 1: /* $Id: man_term.c,v 1.246 2025/07/02 20:50:14 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.247 ! schwarze 41: #define MAXMARGINS 64 /* Maximum number of indented scopes. */
1.18 kristaps 42:
1.24 kristaps 43: struct mtermp {
1.247 ! schwarze 44: int lmargin[MAXMARGINS]; /* Margins in basic units. */
! 45: int lmargincur; /* Index of current margin. */
! 46: int lmarginsz; /* Actual number of nested margins. */
! 47: size_t offset; /* Default offset in basic units. */
! 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;
1.247 ! schwarze 377: const char *cp; /* Request argument. */
! 378: size_t v; /* Indentation in basic units. */
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.247 ! schwarze 391: if (*cp == '-') {
1.83 kristaps 392: less = -1;
1.247 ! schwarze 393: cp++;
! 394: } else if (*cp == '+') {
1.83 kristaps 395: less = 1;
1.247 ! schwarze 396: cp++;
! 397: }
1.83 kristaps 398:
1.247 ! schwarze 399: if (a2roffsu(cp, &su, SCALE_EN) == NULL)
1.185 schwarze 400: return 0;
1.83 kristaps 401:
1.247 ! schwarze 402: v = term_hspan(p, &su);
1.83 kristaps 403:
404: if (less < 0)
1.203 schwarze 405: p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
1.83 kristaps 406: else if (less > 0)
1.203 schwarze 407: p->tcol->offset += v;
1.146 schwarze 408: else
1.203 schwarze 409: p->tcol->offset = v;
410: if (p->tcol->offset > SHRT_MAX)
411: p->tcol->offset = term_len(p, p->defindent);
1.19 kristaps 412:
1.199 schwarze 413: return 0;
414: }
415:
416: static int
417: pre_DT(DECL_ARGS)
418: {
419: term_tab_set(p, NULL);
420: term_tab_set(p, "T");
421: term_tab_set(p, ".5i");
1.185 schwarze 422: return 0;
1.15 kristaps 423: }
424:
425: static int
1.19 kristaps 426: pre_HP(DECL_ARGS)
427: {
1.163 schwarze 428: struct roffsu su;
1.172 schwarze 429: const struct roff_node *nn;
1.247 ! schwarze 430: int len; /* Indentation in basic units. */
1.19 kristaps 431:
1.20 kristaps 432: switch (n->type) {
1.171 schwarze 433: case ROFFT_BLOCK:
1.134 schwarze 434: print_bvspace(p, n, mt->pardist);
1.185 schwarze 435: return 1;
1.227 schwarze 436: case ROFFT_HEAD:
437: return 0;
1.171 schwarze 438: case ROFFT_BODY:
1.20 kristaps 439: break;
440: default:
1.227 schwarze 441: abort();
1.20 kristaps 442: }
443:
1.226 schwarze 444: if (n->child == NULL)
445: return 0;
446:
447: if ((n->child->flags & NODE_NOFILL) == 0) {
1.145 schwarze 448: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.139 schwarze 449: p->trailspace = 2;
1.130 schwarze 450: }
451:
1.24 kristaps 452: /* Calculate offset. */
453:
1.163 schwarze 454: if ((nn = n->parent->head->child) != NULL &&
1.204 schwarze 455: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.247 ! schwarze 456: len = term_hspan(p, &su);
1.164 schwarze 457: if (len < 0 && (size_t)(-len) > mt->offset)
458: len = -mt->offset;
459: else if (len > SHRT_MAX)
460: len = term_len(p, p->defindent);
1.163 schwarze 461: mt->lmargin[mt->lmargincur] = len;
462: } else
463: len = mt->lmargin[mt->lmargincur];
1.24 kristaps 464:
1.203 schwarze 465: p->tcol->offset = mt->offset;
466: p->tcol->rmargin = mt->offset + len;
1.185 schwarze 467: return 1;
1.19 kristaps 468: }
469:
1.20 kristaps 470: static void
471: post_HP(DECL_ARGS)
472: {
473: switch (n->type) {
1.227 schwarze 474: case ROFFT_BLOCK:
475: case ROFFT_HEAD:
476: break;
1.171 schwarze 477: case ROFFT_BODY:
1.136 schwarze 478: term_newln(p);
1.174 schwarze 479:
480: /*
481: * Compatibility with a groff bug.
482: * The .HP macro uses the undocumented .tag request
483: * which causes a line break and cancels no-space
484: * mode even if there isn't any output.
485: */
486:
487: if (n->child == NULL)
488: term_vspace(p);
489:
1.145 schwarze 490: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.139 schwarze 491: p->trailspace = 0;
1.203 schwarze 492: p->tcol->offset = mt->offset;
493: p->tcol->rmargin = p->maxrmargin;
1.20 kristaps 494: break;
495: default:
1.227 schwarze 496: abort();
1.20 kristaps 497: }
498: }
499:
1.19 kristaps 500: static int
1.1 kristaps 501: pre_PP(DECL_ARGS)
502: {
1.19 kristaps 503: switch (n->type) {
1.171 schwarze 504: case ROFFT_BLOCK:
1.244 schwarze 505: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.134 schwarze 506: print_bvspace(p, n, mt->pardist);
1.19 kristaps 507: break;
1.227 schwarze 508: case ROFFT_HEAD:
509: return 0;
510: case ROFFT_BODY:
1.203 schwarze 511: p->tcol->offset = mt->offset;
1.19 kristaps 512: break;
1.227 schwarze 513: default:
514: abort();
1.19 kristaps 515: }
1.227 schwarze 516: return 1;
1.1 kristaps 517: }
518:
519: static int
1.4 kristaps 520: pre_IP(DECL_ARGS)
521: {
1.163 schwarze 522: struct roffsu su;
1.172 schwarze 523: const struct roff_node *nn;
1.247 ! schwarze 524: int len; /* Indentation in basic units. */
1.18 kristaps 525:
1.22 kristaps 526: switch (n->type) {
1.227 schwarze 527: case ROFFT_BLOCK:
528: print_bvspace(p, n, mt->pardist);
529: return 1;
1.171 schwarze 530: case ROFFT_HEAD:
1.22 kristaps 531: p->flags |= TERMP_NOBREAK;
1.139 schwarze 532: p->trailspace = 1;
1.22 kristaps 533: break;
1.227 schwarze 534: case ROFFT_BODY:
1.239 schwarze 535: p->flags |= TERMP_NOSPACE | TERMP_NONEWLINE;
1.227 schwarze 536: break;
1.22 kristaps 537: default:
1.227 schwarze 538: abort();
1.22 kristaps 539: }
1.18 kristaps 540:
1.94 schwarze 541: /* Calculate the offset from the optional second argument. */
1.163 schwarze 542: if ((nn = n->parent->head->child) != NULL &&
543: (nn = nn->next) != NULL &&
1.204 schwarze 544: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.247 ! schwarze 545: len = term_hspan(p, &su);
1.163 schwarze 546: if (len < 0 && (size_t)(-len) > mt->offset)
547: len = -mt->offset;
1.164 schwarze 548: else if (len > SHRT_MAX)
549: len = term_len(p, p->defindent);
550: mt->lmargin[mt->lmargincur] = len;
1.163 schwarze 551: } else
552: len = mt->lmargin[mt->lmargincur];
1.4 kristaps 553:
1.22 kristaps 554: switch (n->type) {
1.171 schwarze 555: case ROFFT_HEAD:
1.203 schwarze 556: p->tcol->offset = mt->offset;
557: p->tcol->rmargin = mt->offset + len;
1.235 schwarze 558: if (n->child != NULL)
1.135 schwarze 559: print_man_node(p, mt, n->child, meta);
1.185 schwarze 560: return 0;
1.171 schwarze 561: case ROFFT_BODY:
1.203 schwarze 562: p->tcol->offset = mt->offset + len;
563: p->tcol->rmargin = p->maxrmargin;
1.23 kristaps 564: break;
1.22 kristaps 565: default:
1.227 schwarze 566: abort();
1.18 kristaps 567: }
1.185 schwarze 568: return 1;
1.22 kristaps 569: }
1.18 kristaps 570:
1.22 kristaps 571: static void
572: post_IP(DECL_ARGS)
573: {
574: switch (n->type) {
1.227 schwarze 575: case ROFFT_BLOCK:
576: break;
1.171 schwarze 577: case ROFFT_HEAD:
1.22 kristaps 578: term_flushln(p);
579: p->flags &= ~TERMP_NOBREAK;
1.139 schwarze 580: p->trailspace = 0;
1.203 schwarze 581: p->tcol->rmargin = p->maxrmargin;
1.22 kristaps 582: break;
1.171 schwarze 583: case ROFFT_BODY:
1.94 schwarze 584: term_newln(p);
1.203 schwarze 585: p->tcol->offset = mt->offset;
1.22 kristaps 586: break;
587: default:
1.227 schwarze 588: abort();
1.22 kristaps 589: }
1.4 kristaps 590: }
591:
592: static int
1.1 kristaps 593: pre_TP(DECL_ARGS)
594: {
1.163 schwarze 595: struct roffsu su;
1.172 schwarze 596: struct roff_node *nn;
1.247 ! schwarze 597: int len; /* Indentation in basic units. */
1.1 kristaps 598:
1.21 kristaps 599: switch (n->type) {
1.227 schwarze 600: case ROFFT_BLOCK:
601: if (n->tok == MAN_TP)
602: print_bvspace(p, n, mt->pardist);
603: return 1;
1.171 schwarze 604: case ROFFT_HEAD:
1.184 schwarze 605: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
1.139 schwarze 606: p->trailspace = 1;
1.21 kristaps 607: break;
1.171 schwarze 608: case ROFFT_BODY:
1.239 schwarze 609: p->flags |= TERMP_NOSPACE | TERMP_NONEWLINE;
1.23 kristaps 610: break;
611: default:
1.227 schwarze 612: abort();
1.23 kristaps 613: }
614:
615: /* Calculate offset. */
616:
1.163 schwarze 617: if ((nn = n->parent->head->child) != NULL &&
1.188 schwarze 618: nn->string != NULL && ! (NODE_LINE & nn->flags) &&
1.204 schwarze 619: a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
1.247 ! schwarze 620: len = term_hspan(p, &su);
1.163 schwarze 621: if (len < 0 && (size_t)(-len) > mt->offset)
622: len = -mt->offset;
1.164 schwarze 623: else if (len > SHRT_MAX)
624: len = term_len(p, p->defindent);
625: mt->lmargin[mt->lmargincur] = len;
1.163 schwarze 626: } else
627: len = mt->lmargin[mt->lmargincur];
1.23 kristaps 628:
629: switch (n->type) {
1.171 schwarze 630: case ROFFT_HEAD:
1.203 schwarze 631: p->tcol->offset = mt->offset;
632: p->tcol->rmargin = mt->offset + len;
1.23 kristaps 633:
634: /* Don't print same-line elements. */
1.141 schwarze 635: nn = n->child;
1.227 schwarze 636: while (nn != NULL && (nn->flags & NODE_LINE) == 0)
1.141 schwarze 637: nn = nn->next;
1.232 schwarze 638:
1.227 schwarze 639: while (nn != NULL) {
1.141 schwarze 640: print_man_node(p, mt, nn, meta);
641: nn = nn->next;
642: }
1.185 schwarze 643: return 0;
1.171 schwarze 644: case ROFFT_BODY:
1.203 schwarze 645: p->tcol->offset = mt->offset + len;
646: p->tcol->rmargin = p->maxrmargin;
1.139 schwarze 647: p->trailspace = 0;
1.184 schwarze 648: p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
1.21 kristaps 649: break;
650: default:
1.227 schwarze 651: abort();
1.21 kristaps 652: }
1.185 schwarze 653: return 1;
1.21 kristaps 654: }
1.1 kristaps 655:
1.21 kristaps 656: static void
657: post_TP(DECL_ARGS)
658: {
659: switch (n->type) {
1.227 schwarze 660: case ROFFT_BLOCK:
661: break;
1.171 schwarze 662: case ROFFT_HEAD:
1.21 kristaps 663: term_flushln(p);
664: break;
1.171 schwarze 665: case ROFFT_BODY:
1.94 schwarze 666: term_newln(p);
1.203 schwarze 667: p->tcol->offset = mt->offset;
1.21 kristaps 668: break;
669: default:
1.227 schwarze 670: abort();
1.21 kristaps 671: }
1.1 kristaps 672: }
673:
674: static int
675: pre_SS(DECL_ARGS)
676: {
1.134 schwarze 677: int i;
1.1 kristaps 678:
1.19 kristaps 679: switch (n->type) {
1.171 schwarze 680: case ROFFT_BLOCK:
1.244 schwarze 681: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.122 schwarze 682: mt->offset = term_len(p, p->defindent);
1.158 schwarze 683:
684: /*
685: * No vertical space before the first subsection
686: * and after an empty subsection.
687: */
688:
1.234 schwarze 689: if ((n = roff_node_prev(n)) == NULL ||
690: (n->tok == MAN_SS && roff_node_child(n->body) == NULL))
1.24 kristaps 691: break;
1.158 schwarze 692:
1.134 schwarze 693: for (i = 0; i < mt->pardist; i++)
694: term_vspace(p);
1.19 kristaps 695: break;
1.171 schwarze 696: case ROFFT_HEAD:
1.52 kristaps 697: term_fontrepl(p, TERMFONT_BOLD);
1.247 ! schwarze 698: p->tcol->offset = term_len(p, p->defindent) / 2 + 1;
1.203 schwarze 699: p->tcol->rmargin = mt->offset;
1.247 ! schwarze 700: p->trailspace = mt->offset / term_len(p, 1);
1.176 schwarze 701: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.19 kristaps 702: break;
1.171 schwarze 703: case ROFFT_BODY:
1.203 schwarze 704: p->tcol->offset = mt->offset;
705: p->tcol->rmargin = p->maxrmargin;
1.176 schwarze 706: p->trailspace = 0;
707: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.24 kristaps 708: break;
1.19 kristaps 709: default:
710: break;
711: }
1.185 schwarze 712: return 1;
1.1 kristaps 713: }
714:
715: static int
716: pre_SH(DECL_ARGS)
717: {
1.134 schwarze 718: int i;
1.22 kristaps 719:
1.19 kristaps 720: switch (n->type) {
1.171 schwarze 721: case ROFFT_BLOCK:
1.244 schwarze 722: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.122 schwarze 723: mt->offset = term_len(p, p->defindent);
1.158 schwarze 724:
725: /*
726: * No vertical space before the first section
727: * and after an empty section.
728: */
729:
1.234 schwarze 730: if ((n = roff_node_prev(n)) == NULL ||
731: (n->tok == MAN_SH && roff_node_child(n->body) == NULL))
1.61 kristaps 732: break;
1.158 schwarze 733:
1.134 schwarze 734: for (i = 0; i < mt->pardist; i++)
735: term_vspace(p);
1.19 kristaps 736: break;
1.171 schwarze 737: case ROFFT_HEAD:
1.52 kristaps 738: term_fontrepl(p, TERMFONT_BOLD);
1.203 schwarze 739: p->tcol->offset = 0;
740: p->tcol->rmargin = mt->offset;
1.247 ! schwarze 741: p->trailspace = mt->offset / term_len(p, 1);
1.176 schwarze 742: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.19 kristaps 743: break;
1.171 schwarze 744: case ROFFT_BODY:
1.203 schwarze 745: p->tcol->offset = mt->offset;
746: p->tcol->rmargin = p->maxrmargin;
1.176 schwarze 747: p->trailspace = 0;
748: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.19 kristaps 749: break;
750: default:
1.227 schwarze 751: abort();
1.19 kristaps 752: }
1.185 schwarze 753: return 1;
1.1 kristaps 754: }
755:
756: static void
757: post_SH(DECL_ARGS)
758: {
1.19 kristaps 759: switch (n->type) {
1.227 schwarze 760: case ROFFT_BLOCK:
761: break;
1.171 schwarze 762: case ROFFT_HEAD:
763: case ROFFT_BODY:
1.19 kristaps 764: term_newln(p);
765: break;
766: default:
1.227 schwarze 767: abort();
1.19 kristaps 768: }
1.1 kristaps 769: }
770:
1.26 kristaps 771: static int
772: pre_RS(DECL_ARGS)
773: {
1.163 schwarze 774: struct roffsu su;
1.26 kristaps 775:
776: switch (n->type) {
1.171 schwarze 777: case ROFFT_BLOCK:
1.26 kristaps 778: term_newln(p);
1.185 schwarze 779: return 1;
1.171 schwarze 780: case ROFFT_HEAD:
1.185 schwarze 781: return 0;
1.227 schwarze 782: case ROFFT_BODY:
783: break;
1.26 kristaps 784: default:
1.227 schwarze 785: abort();
1.26 kristaps 786: }
787:
1.165 schwarze 788: n = n->parent->head;
789: n->aux = SHRT_MAX + 1;
1.177 schwarze 790: if (n->child == NULL)
791: n->aux = mt->lmargin[mt->lmargincur];
1.204 schwarze 792: else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
1.247 ! schwarze 793: n->aux = term_hspan(p, &su);
1.165 schwarze 794: if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
795: n->aux = -mt->offset;
796: else if (n->aux > SHRT_MAX)
797: n->aux = term_len(p, p->defindent);
1.26 kristaps 798:
1.165 schwarze 799: mt->offset += n->aux;
1.203 schwarze 800: p->tcol->offset = mt->offset;
801: p->tcol->rmargin = p->maxrmargin;
1.26 kristaps 802:
1.116 schwarze 803: if (++mt->lmarginsz < MAXMARGINS)
804: mt->lmargincur = mt->lmarginsz;
805:
1.244 schwarze 806: mt->lmargin[mt->lmargincur] = term_len(p, 7);
1.185 schwarze 807: return 1;
1.26 kristaps 808: }
809:
810: static void
811: post_RS(DECL_ARGS)
812: {
813: switch (n->type) {
1.171 schwarze 814: case ROFFT_BLOCK:
815: case ROFFT_HEAD:
1.110 kristaps 816: return;
1.227 schwarze 817: case ROFFT_BODY:
818: break;
1.26 kristaps 819: default:
1.227 schwarze 820: abort();
1.26 kristaps 821: }
1.227 schwarze 822: term_newln(p);
1.165 schwarze 823: mt->offset -= n->parent->head->aux;
1.203 schwarze 824: p->tcol->offset = mt->offset;
1.116 schwarze 825: if (--mt->lmarginsz < MAXMARGINS)
826: mt->lmargincur = mt->lmarginsz;
1.215 schwarze 827: }
828:
829: static int
830: pre_SY(DECL_ARGS)
831: {
832: const struct roff_node *nn;
1.247 ! schwarze 833: int len; /* Indentation in basic units. */
1.215 schwarze 834:
835: switch (n->type) {
836: case ROFFT_BLOCK:
1.234 schwarze 837: if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY)
1.217 schwarze 838: print_bvspace(p, n, mt->pardist);
1.215 schwarze 839: return 1;
840: case ROFFT_HEAD:
841: case ROFFT_BODY:
842: break;
843: default:
844: abort();
845: }
846:
847: nn = n->parent->head->child;
1.247 ! schwarze 848: len = term_len(p, 1);
! 849: if (nn != NULL)
! 850: len += term_strlen(p, nn->string);
1.215 schwarze 851:
852: switch (n->type) {
853: case ROFFT_HEAD:
854: p->tcol->offset = mt->offset;
855: p->tcol->rmargin = mt->offset + len;
1.228 schwarze 856: if (n->next->child == NULL ||
857: (n->next->child->flags & NODE_NOFILL) == 0)
858: p->flags |= TERMP_NOBREAK;
1.215 schwarze 859: term_fontrepl(p, TERMFONT_BOLD);
860: break;
861: case ROFFT_BODY:
862: mt->lmargin[mt->lmargincur] = len;
863: p->tcol->offset = mt->offset + len;
864: p->tcol->rmargin = p->maxrmargin;
865: p->flags |= TERMP_NOSPACE;
866: break;
867: default:
868: abort();
869: }
870: return 1;
871: }
872:
873: static void
874: post_SY(DECL_ARGS)
875: {
876: switch (n->type) {
1.227 schwarze 877: case ROFFT_BLOCK:
878: break;
1.215 schwarze 879: case ROFFT_HEAD:
880: term_flushln(p);
881: p->flags &= ~TERMP_NOBREAK;
882: break;
883: case ROFFT_BODY:
884: term_newln(p);
885: p->tcol->offset = mt->offset;
886: break;
887: default:
1.227 schwarze 888: abort();
1.215 schwarze 889: }
1.137 schwarze 890: }
891:
892: static int
893: pre_UR(DECL_ARGS)
894: {
1.185 schwarze 895: return n->type != ROFFT_HEAD;
1.137 schwarze 896: }
897:
898: static void
899: post_UR(DECL_ARGS)
900: {
1.171 schwarze 901: if (n->type != ROFFT_BLOCK)
1.137 schwarze 902: return;
903:
904: term_word(p, "<");
905: p->flags |= TERMP_NOSPACE;
906:
1.227 schwarze 907: if (n->child->child != NULL)
1.137 schwarze 908: print_man_node(p, mt, n->child->child, meta);
909:
910: p->flags |= TERMP_NOSPACE;
911: term_word(p, ">");
1.26 kristaps 912: }
913:
1.1 kristaps 914: static void
1.45 kristaps 915: print_man_node(DECL_ARGS)
1.1 kristaps 916: {
1.214 schwarze 917: const struct man_term_act *act;
918: int c;
1.1 kristaps 919:
1.239 schwarze 920: /*
921: * In no-fill mode, break the output line at the beginning
922: * of new input lines except after \c, and nowhere else.
923: */
924:
925: if (n->flags & NODE_NOFILL) {
926: if (n->flags & NODE_LINE &&
927: (p->flags & TERMP_NONEWLINE) == 0)
928: term_newln(p);
929: p->flags |= TERMP_BRNEVER;
1.240 schwarze 930: } else {
931: if (n->flags & NODE_LINE)
932: term_tab_ref(p);
1.239 schwarze 933: p->flags &= ~TERMP_BRNEVER;
1.240 schwarze 934: }
1.239 schwarze 935:
1.235 schwarze 936: if (n->flags & NODE_ID)
937: term_tag_write(n, p->line);
938:
1.1 kristaps 939: switch (n->type) {
1.171 schwarze 940: case ROFFT_TEXT:
1.97 kristaps 941: /*
942: * If we have a blank line, output a vertical space.
943: * If we have a space as the first character, break
944: * before printing the line's data.
945: */
1.200 schwarze 946: if (*n->string == '\0') {
1.207 schwarze 947: if (p->flags & TERMP_NONEWLINE)
948: term_newln(p);
949: else
950: term_vspace(p);
1.98 schwarze 951: return;
1.200 schwarze 952: } else if (*n->string == ' ' && n->flags & NODE_LINE &&
953: (p->flags & TERMP_NONEWLINE) == 0)
1.96 kristaps 954: term_newln(p);
1.212 schwarze 955: else if (n->flags & NODE_DELIMC)
956: p->flags |= TERMP_NOSPACE;
1.54 kristaps 957:
1.4 kristaps 958: term_word(p, n->string);
1.130 schwarze 959: goto out;
1.210 schwarze 960: case ROFFT_COMMENT:
961: return;
1.171 schwarze 962: case ROFFT_EQN:
1.188 schwarze 963: if ( ! (n->flags & NODE_LINE))
1.152 schwarze 964: p->flags |= TERMP_NOSPACE;
1.115 kristaps 965: term_eqn(p, n->eqn);
1.188 schwarze 966: if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.153 schwarze 967: p->flags |= TERMP_NOSPACE;
1.97 kristaps 968: return;
1.171 schwarze 969: case ROFFT_TBL:
1.169 schwarze 970: if (p->tbl.cols == NULL)
1.241 schwarze 971: term_newln(p);
1.92 kristaps 972: term_tbl(p, n->span);
1.97 kristaps 973: return;
1.1 kristaps 974: default:
975: break;
976: }
1.224 schwarze 977:
1.193 schwarze 978: if (n->tok < ROFF_MAX) {
1.194 schwarze 979: roff_term_pre(p, n);
1.193 schwarze 980: return;
981: }
982:
1.214 schwarze 983: act = man_term_act(n->tok);
1.220 schwarze 984: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.97 kristaps 985: term_fontrepl(p, TERMFONT_NONE);
986:
987: c = 1;
1.214 schwarze 988: if (act->pre != NULL)
989: c = (*act->pre)(p, mt, n, meta);
1.97 kristaps 990:
1.227 schwarze 991: if (c && n->child != NULL)
1.135 schwarze 992: print_man_nodelist(p, mt, n->child, meta);
1.1 kristaps 993:
1.214 schwarze 994: if (act->post != NULL)
995: (*act->post)(p, mt, n, meta);
1.220 schwarze 996: if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
1.97 kristaps 997: term_fontrepl(p, TERMFONT_NONE);
1.63 kristaps 998:
1.130 schwarze 999: out:
1.239 schwarze 1000: if (n->parent->tok == MAN_HP && n->parent->type == ROFFT_BODY &&
1001: n->prev == NULL && n->flags & NODE_NOFILL) {
1002: term_newln(p);
1003: p->tcol->offset = p->tcol->rmargin;
1004: p->tcol->rmargin = p->maxrmargin;
1.130 schwarze 1005: }
1.227 schwarze 1006: if (n->flags & NODE_EOS)
1.63 kristaps 1007: p->flags |= TERMP_SENTENCE;
1.1 kristaps 1008: }
1009:
1010: static void
1.52 kristaps 1011: print_man_nodelist(DECL_ARGS)
1.1 kristaps 1012: {
1.168 schwarze 1013: while (n != NULL) {
1014: print_man_node(p, mt, n, meta);
1015: n = n->next;
1016: }
1.1 kristaps 1017: }
1018:
1.31 kristaps 1019: static void
1.173 schwarze 1020: print_man_foot(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1021: {
1.147 schwarze 1022: char *title;
1.247 ! schwarze 1023: size_t datelen, titlen; /* In basic units. */
1.73 kristaps 1024:
1.245 schwarze 1025: assert(meta->title != NULL);
1026: assert(meta->msec != NULL);
1.1 kristaps 1027:
1.52 kristaps 1028: term_fontrepl(p, TERMFONT_NONE);
1.149 schwarze 1029: if (meta->hasbody)
1030: term_vspace(p);
1.126 schwarze 1031:
1.125 schwarze 1032: datelen = term_strlen(p, meta->date);
1.245 schwarze 1033: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1034: titlen = term_strlen(p, title);
1.1 kristaps 1035:
1.173 schwarze 1036: /* Bottom left corner: operating system. */
1.126 schwarze 1037:
1.203 schwarze 1038: p->tcol->offset = 0;
1039: p->tcol->rmargin = p->maxrmargin > datelen ?
1.156 schwarze 1040: (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1.245 schwarze 1041: p->trailspace = 1;
1042: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.1 kristaps 1043:
1.173 schwarze 1044: if (meta->os)
1045: term_word(p, meta->os);
1.1 kristaps 1046: term_flushln(p);
1047:
1.126 schwarze 1048: /* At the bottom in the middle: manual date. */
1049:
1.203 schwarze 1050: p->tcol->offset = p->tcol->rmargin;
1051: p->tcol->rmargin = p->maxrmargin > titlen ?
1052: p->maxrmargin - titlen : 0;
1.117 schwarze 1053: p->flags |= TERMP_NOSPACE;
1.123 schwarze 1054:
1.125 schwarze 1055: term_word(p, meta->date);
1.123 schwarze 1056: term_flushln(p);
1057:
1.126 schwarze 1058: /* Bottom right corner: manual title and section. */
1059:
1.245 schwarze 1060: p->tcol->offset = p->tcol->rmargin;
1061: p->tcol->rmargin = p->maxrmargin;
1062: p->trailspace = 0;
1.123 schwarze 1063: p->flags &= ~TERMP_NOBREAK;
1064: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1065:
1.123 schwarze 1066: term_word(p, title);
1.1 kristaps 1067: term_flushln(p);
1.211 schwarze 1068:
1069: /*
1070: * Reset the terminal state for more output after the footer:
1071: * Some output modes, in particular PostScript and PDF, print
1072: * the header and the footer into a buffer such that it can be
1073: * reused for multiple output pages, then go on to format the
1074: * main text.
1075: */
1076:
1077: p->tcol->offset = 0;
1078: p->flags = 0;
1.147 schwarze 1079: free(title);
1.1 kristaps 1080: }
1081:
1.31 kristaps 1082: static void
1.173 schwarze 1083: print_man_head(struct termp *p, const struct roff_meta *meta)
1.1 kristaps 1084: {
1.148 schwarze 1085: const char *volume;
1.147 schwarze 1086: char *title;
1.247 ! schwarze 1087: size_t vollen, titlen; /* In basic units. */
1.73 kristaps 1088:
1.135 schwarze 1089: assert(meta->title);
1090: assert(meta->msec);
1.1 kristaps 1091:
1.148 schwarze 1092: volume = NULL == meta->vol ? "" : meta->vol;
1093: vollen = term_strlen(p, volume);
1.1 kristaps 1094:
1.126 schwarze 1095: /* Top left corner: manual title and section. */
1096:
1.147 schwarze 1097: mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.77 kristaps 1098: titlen = term_strlen(p, title);
1.1 kristaps 1099:
1.118 schwarze 1100: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.139 schwarze 1101: p->trailspace = 1;
1.203 schwarze 1102: p->tcol->offset = 0;
1.247 ! schwarze 1103: p->tcol->rmargin =
! 1104: titlen * 2 + term_len(p, 2) + vollen < p->maxrmargin ?
1.148 schwarze 1105: (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.156 schwarze 1106: vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1.1 kristaps 1107:
1108: term_word(p, title);
1109: term_flushln(p);
1110:
1.126 schwarze 1111: /* At the top in the middle: manual volume. */
1112:
1.117 schwarze 1113: p->flags |= TERMP_NOSPACE;
1.203 schwarze 1114: p->tcol->offset = p->tcol->rmargin;
1115: p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1.247 ! schwarze 1116: p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1117:
1.148 schwarze 1118: term_word(p, volume);
1.1 kristaps 1119: term_flushln(p);
1120:
1.126 schwarze 1121: /* Top right corner: title and section, again. */
1122:
1.1 kristaps 1123: p->flags &= ~TERMP_NOBREAK;
1.139 schwarze 1124: p->trailspace = 0;
1.203 schwarze 1125: if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1.117 schwarze 1126: p->flags |= TERMP_NOSPACE;
1.203 schwarze 1127: p->tcol->offset = p->tcol->rmargin;
1128: p->tcol->rmargin = p->maxrmargin;
1.57 kristaps 1129: term_word(p, title);
1130: term_flushln(p);
1131: }
1.1 kristaps 1132:
1.118 schwarze 1133: p->flags &= ~TERMP_NOSPACE;
1.203 schwarze 1134: p->tcol->offset = 0;
1135: p->tcol->rmargin = p->maxrmargin;
1.61 kristaps 1136: term_vspace(p);
1.147 schwarze 1137: free(title);
1.1 kristaps 1138: }
CVSweb