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