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