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