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