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