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