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