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