Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.85
1.85 ! schwarze 1: /* $Id: man_term.c,v 1.84 2012/07/10 19:53:11 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)) {
444: if (MAN_SS == n->parent->tok)
445: return(0);
446: if (MAN_SH == n->parent->tok)
447: return(0);
448: }
449:
1.85 ! schwarze 450: neg = 0;
1.45 schwarze 451: switch (n->tok) {
452: case (MAN_br):
453: len = 0;
454: break;
455: default:
1.85 ! schwarze 456: if (NULL == n->child) {
! 457: len = 1;
! 458: break;
! 459: }
! 460: s = n->child->string;
! 461: if ('-' == *s) {
! 462: neg = 1;
! 463: s++;
! 464: }
! 465: len = a2height(p, s);
1.45 schwarze 466: break;
467: }
468:
469: if (0 == len)
470: term_newln(p);
1.85 ! schwarze 471: else if (neg)
! 472: p->skipvsp += len;
! 473: else
! 474: for (i = 0; i < len; i++)
! 475: term_vspace(p);
1.8 schwarze 476:
477: return(0);
478: }
479:
480:
481: /* ARGSUSED */
482: static int
1.11 schwarze 483: pre_HP(DECL_ARGS)
484: {
1.72 schwarze 485: size_t len, one;
1.11 schwarze 486: int ival;
487: const struct man_node *nn;
488:
489: switch (n->type) {
490: case (MAN_BLOCK):
1.18 schwarze 491: print_bvspace(p, n);
1.11 schwarze 492: return(1);
493: case (MAN_BODY):
494: break;
495: default:
496: return(0);
497: }
498:
1.84 schwarze 499: if ( ! (MANT_LITERAL & mt->fl)) {
500: p->flags |= TERMP_NOBREAK;
501: p->flags |= TERMP_TWOSPACE;
502: }
503:
1.70 schwarze 504: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 505: ival = -1;
506:
507: /* Calculate offset. */
508:
509: if (NULL != (nn = n->parent->head->child))
1.42 schwarze 510: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 511: len = (size_t)ival;
512:
1.72 schwarze 513: one = term_len(p, 1);
1.76 schwarze 514: if (len < one)
1.72 schwarze 515: len = one;
1.11 schwarze 516:
1.13 schwarze 517: p->offset = mt->offset;
518: p->rmargin = mt->offset + len;
1.11 schwarze 519:
520: if (ival >= 0)
1.70 schwarze 521: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 522:
523: return(1);
524: }
525:
526:
527: /* ARGSUSED */
528: static void
529: post_HP(DECL_ARGS)
530: {
531:
532: switch (n->type) {
533: case (MAN_BODY):
534: term_flushln(p);
535: p->flags &= ~TERMP_NOBREAK;
536: p->flags &= ~TERMP_TWOSPACE;
1.13 schwarze 537: p->offset = mt->offset;
1.11 schwarze 538: p->rmargin = p->maxrmargin;
539: break;
540: default:
541: break;
542: }
543: }
544:
545:
546: /* ARGSUSED */
547: static int
1.1 kristaps 548: pre_PP(DECL_ARGS)
549: {
550:
1.11 schwarze 551: switch (n->type) {
552: case (MAN_BLOCK):
1.77 schwarze 553: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.18 schwarze 554: print_bvspace(p, n);
1.11 schwarze 555: break;
556: default:
1.13 schwarze 557: p->offset = mt->offset;
1.11 schwarze 558: break;
559: }
560:
1.54 schwarze 561: return(MAN_HEAD != n->type);
1.1 kristaps 562: }
563:
564:
565: /* ARGSUSED */
566: static int
567: pre_IP(DECL_ARGS)
568: {
1.11 schwarze 569: const struct man_node *nn;
570: size_t len;
1.57 schwarze 571: int savelit, ival;
1.11 schwarze 572:
573: switch (n->type) {
574: case (MAN_BODY):
575: p->flags |= TERMP_NOSPACE;
576: break;
577: case (MAN_HEAD):
578: p->flags |= TERMP_NOBREAK;
579: break;
580: case (MAN_BLOCK):
1.18 schwarze 581: print_bvspace(p, n);
1.11 schwarze 582: /* FALLTHROUGH */
583: default:
584: return(1);
585: }
586:
1.70 schwarze 587: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 588: ival = -1;
589:
1.57 schwarze 590: /* Calculate the offset from the optional second argument. */
1.11 schwarze 591: if (NULL != (nn = n->parent->head->child))
1.57 schwarze 592: if (NULL != (nn = nn->next))
1.42 schwarze 593: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 594: len = (size_t)ival;
595:
596: switch (n->type) {
597: case (MAN_HEAD):
598: /* Handle zero-width lengths. */
599: if (0 == len)
1.42 schwarze 600: len = term_len(p, 1);
1.11 schwarze 601:
1.13 schwarze 602: p->offset = mt->offset;
603: p->rmargin = mt->offset + len;
1.11 schwarze 604: if (ival < 0)
605: break;
606:
607: /* Set the saved left-margin. */
1.70 schwarze 608: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.1 kristaps 609:
1.57 schwarze 610: savelit = MANT_LITERAL & mt->fl;
611: mt->fl &= ~MANT_LITERAL;
612:
613: if (n->child)
614: print_man_node(p, mt, n->child, m);
615:
616: if (savelit)
617: mt->fl |= MANT_LITERAL;
618:
1.11 schwarze 619: return(0);
620: case (MAN_BODY):
1.13 schwarze 621: p->offset = mt->offset + len;
1.11 schwarze 622: p->rmargin = p->maxrmargin;
623: break;
624: default:
625: break;
626: }
1.1 kristaps 627:
1.11 schwarze 628: return(1);
629: }
1.1 kristaps 630:
631:
1.11 schwarze 632: /* ARGSUSED */
633: static void
634: post_IP(DECL_ARGS)
635: {
1.4 schwarze 636:
1.11 schwarze 637: switch (n->type) {
638: case (MAN_HEAD):
639: term_flushln(p);
640: p->flags &= ~TERMP_NOBREAK;
641: p->rmargin = p->maxrmargin;
642: break;
643: case (MAN_BODY):
1.57 schwarze 644: term_newln(p);
1.11 schwarze 645: break;
646: default:
647: break;
648: }
1.1 kristaps 649: }
650:
651:
652: /* ARGSUSED */
653: static int
654: pre_TP(DECL_ARGS)
655: {
1.11 schwarze 656: const struct man_node *nn;
657: size_t len;
1.57 schwarze 658: int savelit, ival;
1.11 schwarze 659:
660: switch (n->type) {
661: case (MAN_HEAD):
662: p->flags |= TERMP_NOBREAK;
663: break;
664: case (MAN_BODY):
665: p->flags |= TERMP_NOSPACE;
666: break;
667: case (MAN_BLOCK):
1.18 schwarze 668: print_bvspace(p, n);
1.11 schwarze 669: /* FALLTHROUGH */
670: default:
671: return(1);
672: }
673:
1.70 schwarze 674: len = (size_t)mt->lmargin[mt->lmargincur];
1.11 schwarze 675: ival = -1;
676:
677: /* Calculate offset. */
1.1 kristaps 678:
1.70 schwarze 679: if (NULL != (nn = n->parent->head->child))
1.75 schwarze 680: if (nn->string && nn->parent->line == nn->line)
1.42 schwarze 681: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 682: len = (size_t)ival;
1.8 schwarze 683:
1.11 schwarze 684: switch (n->type) {
685: case (MAN_HEAD):
686: /* Handle zero-length properly. */
687: if (0 == len)
1.42 schwarze 688: len = term_len(p, 1);
1.11 schwarze 689:
1.13 schwarze 690: p->offset = mt->offset;
691: p->rmargin = mt->offset + len;
1.11 schwarze 692:
1.57 schwarze 693: savelit = MANT_LITERAL & mt->fl;
694: mt->fl &= ~MANT_LITERAL;
695:
1.11 schwarze 696: /* Don't print same-line elements. */
1.57 schwarze 697: for (nn = n->child; nn; nn = nn->next)
1.11 schwarze 698: if (nn->line > n->line)
1.19 schwarze 699: print_man_node(p, mt, nn, m);
1.11 schwarze 700:
1.57 schwarze 701: if (savelit)
702: mt->fl |= MANT_LITERAL;
1.11 schwarze 703: if (ival >= 0)
1.70 schwarze 704: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 705:
706: return(0);
707: case (MAN_BODY):
1.13 schwarze 708: p->offset = mt->offset + len;
1.11 schwarze 709: p->rmargin = p->maxrmargin;
1.84 schwarze 710: p->flags &= ~TERMP_NOBREAK;
711: p->flags &= ~TERMP_TWOSPACE;
1.11 schwarze 712: break;
713: default:
714: break;
715: }
1.1 kristaps 716:
1.11 schwarze 717: return(1);
718: }
1.1 kristaps 719:
720:
1.11 schwarze 721: /* ARGSUSED */
722: static void
723: post_TP(DECL_ARGS)
724: {
1.1 kristaps 725:
1.11 schwarze 726: switch (n->type) {
727: case (MAN_HEAD):
728: term_flushln(p);
729: break;
730: case (MAN_BODY):
1.57 schwarze 731: term_newln(p);
1.11 schwarze 732: break;
733: default:
734: break;
735: }
1.1 kristaps 736: }
737:
738:
739: /* ARGSUSED */
740: static int
741: pre_SS(DECL_ARGS)
742: {
743:
1.11 schwarze 744: switch (n->type) {
745: case (MAN_BLOCK):
1.69 schwarze 746: mt->fl &= ~MANT_LITERAL;
1.77 schwarze 747: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
748: mt->offset = term_len(p, p->defindent);
1.11 schwarze 749: /* If following a prior empty `SS', no vspace. */
750: if (n->prev && MAN_SS == n->prev->tok)
751: if (NULL == n->prev->body->child)
752: break;
753: if (NULL == n->prev)
754: break;
755: term_vspace(p);
756: break;
757: case (MAN_HEAD):
1.21 schwarze 758: term_fontrepl(p, TERMFONT_BOLD);
1.77 schwarze 759: p->offset = term_len(p, p->defindent/2);
1.11 schwarze 760: break;
761: case (MAN_BODY):
1.13 schwarze 762: p->offset = mt->offset;
1.11 schwarze 763: break;
764: default:
765: break;
766: }
767:
1.1 kristaps 768: return(1);
769: }
770:
771:
772: /* ARGSUSED */
773: static void
774: post_SS(DECL_ARGS)
775: {
776:
1.11 schwarze 777: switch (n->type) {
778: case (MAN_HEAD):
779: term_newln(p);
780: break;
781: case (MAN_BODY):
782: term_newln(p);
783: break;
784: default:
785: break;
786: }
1.1 kristaps 787: }
788:
789:
790: /* ARGSUSED */
791: static int
792: pre_SH(DECL_ARGS)
793: {
794:
1.11 schwarze 795: switch (n->type) {
796: case (MAN_BLOCK):
1.69 schwarze 797: mt->fl &= ~MANT_LITERAL;
1.77 schwarze 798: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
799: mt->offset = term_len(p, p->defindent);
1.11 schwarze 800: /* If following a prior empty `SH', no vspace. */
801: if (n->prev && MAN_SH == n->prev->tok)
802: if (NULL == n->prev->body->child)
803: break;
1.29 schwarze 804: /* If the first macro, no vspae. */
805: if (NULL == n->prev)
806: break;
1.11 schwarze 807: term_vspace(p);
808: break;
809: case (MAN_HEAD):
1.21 schwarze 810: term_fontrepl(p, TERMFONT_BOLD);
1.11 schwarze 811: p->offset = 0;
812: break;
813: case (MAN_BODY):
1.13 schwarze 814: p->offset = mt->offset;
1.11 schwarze 815: break;
816: default:
817: break;
818: }
819:
1.1 kristaps 820: return(1);
821: }
822:
823:
824: /* ARGSUSED */
825: static void
826: post_SH(DECL_ARGS)
827: {
828:
1.11 schwarze 829: switch (n->type) {
830: case (MAN_HEAD):
831: term_newln(p);
832: break;
833: case (MAN_BODY):
834: term_newln(p);
835: break;
836: default:
1.13 schwarze 837: break;
838: }
839: }
840:
841: /* ARGSUSED */
842: static int
843: pre_RS(DECL_ARGS)
844: {
1.69 schwarze 845: int ival;
846: size_t sz;
1.13 schwarze 847:
848: switch (n->type) {
849: case (MAN_BLOCK):
850: term_newln(p);
851: return(1);
852: case (MAN_HEAD):
853: return(0);
854: default:
855: break;
856: }
857:
1.77 schwarze 858: sz = term_len(p, p->defindent);
1.13 schwarze 859:
1.69 schwarze 860: if (NULL != (n = n->parent->head->child))
861: if ((ival = a2width(p, n->string)) >= 0)
862: sz = (size_t)ival;
1.13 schwarze 863:
1.69 schwarze 864: mt->offset += sz;
1.74 schwarze 865: p->rmargin = p->maxrmargin;
866: p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin;
1.13 schwarze 867:
1.70 schwarze 868: if (++mt->lmarginsz < MAXMARGINS)
869: mt->lmargincur = mt->lmarginsz;
870:
871: mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
1.13 schwarze 872: return(1);
873: }
874:
875: /* ARGSUSED */
876: static void
877: post_RS(DECL_ARGS)
878: {
1.69 schwarze 879: int ival;
880: size_t sz;
1.13 schwarze 881:
882: switch (n->type) {
883: case (MAN_BLOCK):
1.69 schwarze 884: return;
1.27 schwarze 885: case (MAN_HEAD):
1.69 schwarze 886: return;
1.13 schwarze 887: default:
888: term_newln(p);
1.11 schwarze 889: break;
890: }
1.69 schwarze 891:
1.77 schwarze 892: sz = term_len(p, p->defindent);
1.69 schwarze 893:
894: if (NULL != (n = n->parent->head->child))
895: if ((ival = a2width(p, n->string)) >= 0)
896: sz = (size_t)ival;
897:
898: mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
899: p->offset = mt->offset;
1.70 schwarze 900:
901: if (--mt->lmarginsz < MAXMARGINS)
902: mt->lmargincur = mt->lmarginsz;
1.47 schwarze 903: }
904:
1.1 kristaps 905: static void
1.19 schwarze 906: print_man_node(DECL_ARGS)
1.1 kristaps 907: {
1.32 schwarze 908: size_t rm, rmax;
1.21 schwarze 909: int c;
1.1 kristaps 910:
911: switch (n->type) {
912: case(MAN_TEXT):
1.61 schwarze 913: /*
914: * If we have a blank line, output a vertical space.
915: * If we have a space as the first character, break
916: * before printing the line's data.
917: */
1.60 schwarze 918: if ('\0' == *n->string) {
1.1 kristaps 919: term_vspace(p);
1.61 schwarze 920: return;
921: } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60 schwarze 922: term_newln(p);
1.21 schwarze 923:
1.1 kristaps 924: term_word(p, n->string);
1.84 schwarze 925: goto out;
1.21 schwarze 926:
1.66 schwarze 927: case (MAN_EQN):
1.71 schwarze 928: term_eqn(p, n->eqn);
1.61 schwarze 929: return;
1.58 schwarze 930: case (MAN_TBL):
1.61 schwarze 931: /*
932: * Tables are preceded by a newline. Then process a
933: * table line, which will cause line termination,
934: */
1.58 schwarze 935: if (TBL_SPAN_FIRST & n->span->flags)
936: term_newln(p);
937: term_tbl(p, n->span);
1.61 schwarze 938: return;
1.1 kristaps 939: default:
940: break;
941: }
942:
1.61 schwarze 943: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
944: term_fontrepl(p, TERMFONT_NONE);
945:
946: c = 1;
947: if (termacts[n->tok].pre)
948: c = (*termacts[n->tok].pre)(p, mt, n, m);
949:
1.1 kristaps 950: if (c && n->child)
1.21 schwarze 951: print_man_nodelist(p, mt, n->child, m);
1.1 kristaps 952:
1.61 schwarze 953: if (termacts[n->tok].post)
954: (*termacts[n->tok].post)(p, mt, n, m);
955: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
956: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 957:
1.84 schwarze 958: out:
959: /*
960: * If we're in a literal context, make sure that words
961: * together on the same line stay together. This is a
962: * POST-printing call, so we check the NEXT word. Since
963: * -man doesn't have nested macros, we don't need to be
964: * more specific than this.
965: */
966: if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
967: NULL != n->next && n->next->line > n->line) {
968: rm = p->rmargin;
969: rmax = p->maxrmargin;
970: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
971: p->flags |= TERMP_NOSPACE;
972: if (NULL != n->string && '\0' != *n->string)
973: term_flushln(p);
974: else
975: term_newln(p);
976: if (rm < rmax && n->parent->tok == MAN_HP) {
977: p->offset = rm;
978: p->rmargin = rmax;
979: } else
980: p->rmargin = rm;
981: p->maxrmargin = rmax;
982: }
1.30 schwarze 983: if (MAN_EOS & n->flags)
984: p->flags |= TERMP_SENTENCE;
1.1 kristaps 985: }
986:
987:
988: static void
1.21 schwarze 989: print_man_nodelist(DECL_ARGS)
1.1 kristaps 990: {
1.11 schwarze 991:
1.19 schwarze 992: print_man_node(p, mt, n, m);
1.1 kristaps 993: if ( ! n->next)
994: return;
1.21 schwarze 995: print_man_nodelist(p, mt, n->next, m);
1.1 kristaps 996: }
997:
998:
999: static void
1.41 schwarze 1000: print_man_foot(struct termp *p, const void *arg)
1.1 kristaps 1001: {
1.78 schwarze 1002: char title[BUFSIZ];
1003: size_t datelen;
1.41 schwarze 1004: const struct man_meta *meta;
1005:
1006: meta = (const struct man_meta *)arg;
1.80 schwarze 1007: assert(meta->title);
1008: assert(meta->msec);
1009: assert(meta->date);
1.21 schwarze 1010:
1011: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 1012:
1.38 schwarze 1013: term_vspace(p);
1.81 schwarze 1014:
1015: /*
1016: * Temporary, undocumented option to imitate mdoc(7) output.
1017: * In the bottom right corner, use the source instead of
1018: * the title.
1019: */
1020:
1.79 schwarze 1021: if ( ! p->mdocstyle) {
1022: term_vspace(p);
1023: term_vspace(p);
1024: snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1025: } else if (meta->source) {
1026: strlcpy(title, meta->source, BUFSIZ);
1027: } else {
1028: title[0] = '\0';
1029: }
1.78 schwarze 1030: datelen = term_strlen(p, meta->date);
1.1 kristaps 1031:
1.81 schwarze 1032: /* Bottom left corner: manual source. */
1033:
1.1 kristaps 1034: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1035: p->offset = 0;
1.78 schwarze 1036: p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1.1 kristaps 1037:
1038: if (meta->source)
1039: term_word(p, meta->source);
1040: term_flushln(p);
1041:
1.81 schwarze 1042: /* At the bottom in the middle: manual date. */
1043:
1.72 schwarze 1044: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1045: p->offset = p->rmargin;
1.78 schwarze 1046: p->rmargin = p->maxrmargin - term_strlen(p, title);
1047: if (p->offset + datelen >= p->rmargin)
1048: p->rmargin = p->offset + datelen;
1049:
1050: term_word(p, meta->date);
1051: term_flushln(p);
1052:
1.81 schwarze 1053: /* Bottom right corner: manual title and section. */
1054:
1.78 schwarze 1055: p->flags &= ~TERMP_NOBREAK;
1056: p->flags |= TERMP_NOSPACE;
1057: p->offset = p->rmargin;
1.1 kristaps 1058: p->rmargin = p->maxrmargin;
1059:
1.78 schwarze 1060: term_word(p, title);
1.1 kristaps 1061: term_flushln(p);
1062: }
1063:
1064:
1065: static void
1.41 schwarze 1066: print_man_head(struct termp *p, const void *arg)
1.1 kristaps 1067: {
1.20 schwarze 1068: char buf[BUFSIZ], title[BUFSIZ];
1.25 schwarze 1069: size_t buflen, titlen;
1.41 schwarze 1070: const struct man_meta *m;
1071:
1072: m = (const struct man_meta *)arg;
1.80 schwarze 1073: assert(m->title);
1074: assert(m->msec);
1.1 kristaps 1075:
1.20 schwarze 1076: if (m->vol)
1077: strlcpy(buf, m->vol, BUFSIZ);
1.81 schwarze 1078: else
1079: buf[0] = '\0';
1.42 schwarze 1080: buflen = term_strlen(p, buf);
1.1 kristaps 1081:
1.81 schwarze 1082: /* Top left corner: manual title and section. */
1083:
1.31 schwarze 1084: snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1.42 schwarze 1085: titlen = term_strlen(p, title);
1.1 kristaps 1086:
1.73 schwarze 1087: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.1 kristaps 1088: p->offset = 0;
1.25 schwarze 1089: p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1.42 schwarze 1090: (p->maxrmargin -
1091: term_strlen(p, buf) + term_len(p, 1)) / 2 :
1.25 schwarze 1092: p->maxrmargin - buflen;
1.1 kristaps 1093:
1094: term_word(p, title);
1095: term_flushln(p);
1096:
1.81 schwarze 1097: /* At the top in the middle: manual volume. */
1098:
1.72 schwarze 1099: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1100: p->offset = p->rmargin;
1.25 schwarze 1101: p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1102: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1103:
1104: term_word(p, buf);
1105: term_flushln(p);
1106:
1.81 schwarze 1107: /* Top right corner: title and section, again. */
1108:
1.1 kristaps 1109: p->flags &= ~TERMP_NOBREAK;
1.25 schwarze 1110: if (p->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1111: p->flags |= TERMP_NOSPACE;
1.25 schwarze 1112: p->offset = p->rmargin;
1113: p->rmargin = p->maxrmargin;
1114: term_word(p, title);
1115: term_flushln(p);
1116: }
1.1 kristaps 1117:
1.73 schwarze 1118: p->flags &= ~TERMP_NOSPACE;
1119: p->offset = 0;
1.1 kristaps 1120: p->rmargin = p->maxrmargin;
1.29 schwarze 1121:
1122: /*
1.81 schwarze 1123: * Groff prints three blank lines before the content.
1124: * Do the same, except in the temporary, undocumented
1125: * mode imitating mdoc(7) output.
1.29 schwarze 1126: */
1127:
1128: term_vspace(p);
1.79 schwarze 1129: if ( ! p->mdocstyle) {
1130: term_vspace(p);
1131: term_vspace(p);
1132: }
1.1 kristaps 1133: }