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