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