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