Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.58
1.58 ! schwarze 1: /* $Id: man_term.c,v 1.57 2011/01/04 01:15:39 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.26 schwarze 125: { NULL, NULL, MAN_NOTEXT }, /* na */
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.11 schwarze 254: return(1);
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.11 schwarze 395:
396: return(0);
397: }
398:
399:
400: /* ARGSUSED */
401: static int
1.45 schwarze 402: pre_sp(DECL_ARGS)
1.8 schwarze 403: {
1.45 schwarze 404: size_t i, len;
405:
406: switch (n->tok) {
407: case (MAN_br):
408: len = 0;
409: break;
410: default:
411: len = n->child ? a2height(p, n->child->string) : 1;
412: break;
413: }
414:
415: if (0 == len)
416: term_newln(p);
417: for (i = 0; i < len; i++)
418: term_vspace(p);
1.8 schwarze 419:
420: return(0);
421: }
422:
423:
424: /* ARGSUSED */
425: static int
1.11 schwarze 426: pre_HP(DECL_ARGS)
427: {
428: size_t len;
429: int ival;
430: const struct man_node *nn;
431:
432: switch (n->type) {
433: case (MAN_BLOCK):
1.18 schwarze 434: print_bvspace(p, n);
1.11 schwarze 435: return(1);
436: case (MAN_BODY):
437: p->flags |= TERMP_NOBREAK;
438: p->flags |= TERMP_TWOSPACE;
439: break;
440: default:
441: return(0);
442: }
443:
1.13 schwarze 444: len = mt->lmargin;
1.11 schwarze 445: ival = -1;
446:
447: /* Calculate offset. */
448:
449: if (NULL != (nn = n->parent->head->child))
1.42 schwarze 450: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 451: len = (size_t)ival;
452:
453: if (0 == len)
1.42 schwarze 454: len = term_len(p, 1);
1.11 schwarze 455:
1.13 schwarze 456: p->offset = mt->offset;
457: p->rmargin = mt->offset + len;
1.11 schwarze 458:
459: if (ival >= 0)
1.13 schwarze 460: mt->lmargin = (size_t)ival;
1.11 schwarze 461:
462: return(1);
463: }
464:
465:
466: /* ARGSUSED */
467: static void
468: post_HP(DECL_ARGS)
469: {
470:
471: switch (n->type) {
472: case (MAN_BLOCK):
473: term_flushln(p);
474: break;
475: case (MAN_BODY):
476: term_flushln(p);
477: p->flags &= ~TERMP_NOBREAK;
478: p->flags &= ~TERMP_TWOSPACE;
1.13 schwarze 479: p->offset = mt->offset;
1.11 schwarze 480: p->rmargin = p->maxrmargin;
481: break;
482: default:
483: break;
484: }
485: }
486:
487:
488: /* ARGSUSED */
489: static int
1.1 kristaps 490: pre_PP(DECL_ARGS)
491: {
492:
1.11 schwarze 493: switch (n->type) {
494: case (MAN_BLOCK):
1.42 schwarze 495: mt->lmargin = term_len(p, INDENT);
1.18 schwarze 496: print_bvspace(p, n);
1.11 schwarze 497: break;
498: default:
1.13 schwarze 499: p->offset = mt->offset;
1.11 schwarze 500: break;
501: }
502:
1.54 schwarze 503: return(MAN_HEAD != n->type);
1.1 kristaps 504: }
505:
506:
507: /* ARGSUSED */
508: static int
509: pre_IP(DECL_ARGS)
510: {
1.11 schwarze 511: const struct man_node *nn;
512: size_t len;
1.57 schwarze 513: int savelit, ival;
1.11 schwarze 514:
515: switch (n->type) {
516: case (MAN_BODY):
517: p->flags |= TERMP_NOLPAD;
518: p->flags |= TERMP_NOSPACE;
519: break;
520: case (MAN_HEAD):
521: p->flags |= TERMP_NOBREAK;
522: break;
523: case (MAN_BLOCK):
1.18 schwarze 524: print_bvspace(p, n);
1.11 schwarze 525: /* FALLTHROUGH */
526: default:
527: return(1);
528: }
529:
1.13 schwarze 530: len = mt->lmargin;
1.11 schwarze 531: ival = -1;
532:
1.57 schwarze 533: /* Calculate the offset from the optional second argument. */
1.11 schwarze 534: if (NULL != (nn = n->parent->head->child))
1.57 schwarze 535: if (NULL != (nn = nn->next))
1.42 schwarze 536: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 537: len = (size_t)ival;
538:
539: switch (n->type) {
540: case (MAN_HEAD):
541: /* Handle zero-width lengths. */
542: if (0 == len)
1.42 schwarze 543: len = term_len(p, 1);
1.11 schwarze 544:
1.13 schwarze 545: p->offset = mt->offset;
546: p->rmargin = mt->offset + len;
1.11 schwarze 547: if (ival < 0)
548: break;
549:
550: /* Set the saved left-margin. */
1.13 schwarze 551: mt->lmargin = (size_t)ival;
1.1 kristaps 552:
1.57 schwarze 553: savelit = MANT_LITERAL & mt->fl;
554: mt->fl &= ~MANT_LITERAL;
555:
556: if (n->child)
557: print_man_node(p, mt, n->child, m);
558:
559: if (savelit)
560: mt->fl |= MANT_LITERAL;
561:
1.11 schwarze 562: return(0);
563: case (MAN_BODY):
1.13 schwarze 564: p->offset = mt->offset + len;
1.11 schwarze 565: p->rmargin = p->maxrmargin;
566: break;
567: default:
568: break;
569: }
1.1 kristaps 570:
1.11 schwarze 571: return(1);
572: }
1.1 kristaps 573:
574:
1.11 schwarze 575: /* ARGSUSED */
576: static void
577: post_IP(DECL_ARGS)
578: {
1.4 schwarze 579:
1.11 schwarze 580: switch (n->type) {
581: case (MAN_HEAD):
582: term_flushln(p);
583: p->flags &= ~TERMP_NOBREAK;
584: p->rmargin = p->maxrmargin;
585: break;
586: case (MAN_BODY):
1.57 schwarze 587: term_newln(p);
1.11 schwarze 588: p->flags &= ~TERMP_NOLPAD;
589: break;
590: default:
591: break;
592: }
1.1 kristaps 593: }
594:
595:
596: /* ARGSUSED */
597: static int
598: pre_TP(DECL_ARGS)
599: {
1.11 schwarze 600: const struct man_node *nn;
601: size_t len;
1.57 schwarze 602: int savelit, ival;
1.11 schwarze 603:
604: switch (n->type) {
605: case (MAN_HEAD):
606: p->flags |= TERMP_NOBREAK;
607: break;
608: case (MAN_BODY):
609: p->flags |= TERMP_NOLPAD;
610: p->flags |= TERMP_NOSPACE;
611: break;
612: case (MAN_BLOCK):
1.18 schwarze 613: print_bvspace(p, n);
1.11 schwarze 614: /* FALLTHROUGH */
615: default:
616: return(1);
617: }
618:
619: len = (size_t)mt->lmargin;
620: ival = -1;
621:
622: /* Calculate offset. */
1.1 kristaps 623:
1.22 schwarze 624: if (NULL != (nn = n->parent->head->child)) {
625: while (nn && MAN_TEXT != nn->type)
626: nn = nn->next;
627: if (nn && nn->next)
1.42 schwarze 628: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 629: len = (size_t)ival;
1.22 schwarze 630: }
1.8 schwarze 631:
1.11 schwarze 632: switch (n->type) {
633: case (MAN_HEAD):
634: /* Handle zero-length properly. */
635: if (0 == len)
1.42 schwarze 636: len = term_len(p, 1);
1.11 schwarze 637:
1.13 schwarze 638: p->offset = mt->offset;
639: p->rmargin = mt->offset + len;
1.11 schwarze 640:
1.57 schwarze 641: savelit = MANT_LITERAL & mt->fl;
642: mt->fl &= ~MANT_LITERAL;
643:
1.11 schwarze 644: /* Don't print same-line elements. */
1.57 schwarze 645: for (nn = n->child; nn; nn = nn->next)
1.11 schwarze 646: if (nn->line > n->line)
1.19 schwarze 647: print_man_node(p, mt, nn, m);
1.11 schwarze 648:
1.57 schwarze 649: if (savelit)
650: mt->fl |= MANT_LITERAL;
651:
1.11 schwarze 652: if (ival >= 0)
1.13 schwarze 653: mt->lmargin = (size_t)ival;
1.11 schwarze 654:
655: return(0);
656: case (MAN_BODY):
1.13 schwarze 657: p->offset = mt->offset + len;
1.11 schwarze 658: p->rmargin = p->maxrmargin;
659: break;
660: default:
661: break;
662: }
1.1 kristaps 663:
1.11 schwarze 664: return(1);
665: }
1.1 kristaps 666:
667:
1.11 schwarze 668: /* ARGSUSED */
669: static void
670: post_TP(DECL_ARGS)
671: {
1.1 kristaps 672:
1.11 schwarze 673: switch (n->type) {
674: case (MAN_HEAD):
675: term_flushln(p);
676: p->flags &= ~TERMP_NOBREAK;
677: p->flags &= ~TERMP_TWOSPACE;
678: p->rmargin = p->maxrmargin;
679: break;
680: case (MAN_BODY):
1.57 schwarze 681: term_newln(p);
1.11 schwarze 682: p->flags &= ~TERMP_NOLPAD;
683: break;
684: default:
685: break;
686: }
1.1 kristaps 687: }
688:
689:
690: /* ARGSUSED */
691: static int
692: pre_SS(DECL_ARGS)
693: {
694:
1.11 schwarze 695: switch (n->type) {
696: case (MAN_BLOCK):
1.42 schwarze 697: mt->lmargin = term_len(p, INDENT);
698: mt->offset = term_len(p, INDENT);
1.11 schwarze 699: /* If following a prior empty `SS', no vspace. */
700: if (n->prev && MAN_SS == n->prev->tok)
701: if (NULL == n->prev->body->child)
702: break;
703: if (NULL == n->prev)
704: break;
705: term_vspace(p);
706: break;
707: case (MAN_HEAD):
1.21 schwarze 708: term_fontrepl(p, TERMFONT_BOLD);
1.42 schwarze 709: p->offset = term_len(p, HALFINDENT);
1.11 schwarze 710: break;
711: case (MAN_BODY):
1.13 schwarze 712: p->offset = mt->offset;
1.11 schwarze 713: break;
714: default:
715: break;
716: }
717:
1.1 kristaps 718: return(1);
719: }
720:
721:
722: /* ARGSUSED */
723: static void
724: post_SS(DECL_ARGS)
725: {
726:
1.11 schwarze 727: switch (n->type) {
728: case (MAN_HEAD):
729: term_newln(p);
730: break;
731: case (MAN_BODY):
732: term_newln(p);
733: break;
734: default:
735: break;
736: }
1.1 kristaps 737: }
738:
739:
740: /* ARGSUSED */
741: static int
742: pre_SH(DECL_ARGS)
743: {
744:
1.11 schwarze 745: switch (n->type) {
746: case (MAN_BLOCK):
1.42 schwarze 747: mt->lmargin = term_len(p, INDENT);
748: mt->offset = term_len(p, INDENT);
1.11 schwarze 749: /* If following a prior empty `SH', no vspace. */
750: if (n->prev && MAN_SH == n->prev->tok)
751: if (NULL == n->prev->body->child)
752: break;
1.29 schwarze 753: /* If the first macro, no vspae. */
754: if (NULL == n->prev)
755: break;
1.11 schwarze 756: term_vspace(p);
757: break;
758: case (MAN_HEAD):
1.21 schwarze 759: term_fontrepl(p, TERMFONT_BOLD);
1.11 schwarze 760: p->offset = 0;
761: break;
762: case (MAN_BODY):
1.13 schwarze 763: p->offset = mt->offset;
1.11 schwarze 764: break;
765: default:
766: break;
767: }
768:
1.1 kristaps 769: return(1);
770: }
771:
772:
773: /* ARGSUSED */
774: static void
775: post_SH(DECL_ARGS)
776: {
777:
1.11 schwarze 778: switch (n->type) {
779: case (MAN_HEAD):
780: term_newln(p);
781: break;
782: case (MAN_BODY):
783: term_newln(p);
784: break;
785: default:
1.13 schwarze 786: break;
787: }
788: }
789:
790:
791: /* ARGSUSED */
792: static int
793: pre_RS(DECL_ARGS)
794: {
795: const struct man_node *nn;
796: int ival;
797:
798: switch (n->type) {
799: case (MAN_BLOCK):
800: term_newln(p);
801: return(1);
802: case (MAN_HEAD):
803: return(0);
804: default:
805: break;
806: }
807:
808: if (NULL == (nn = n->parent->head->child)) {
1.42 schwarze 809: mt->offset = mt->lmargin + term_len(p, INDENT);
1.13 schwarze 810: p->offset = mt->offset;
811: return(1);
812: }
813:
1.42 schwarze 814: if ((ival = a2width(p, nn->string)) < 0)
1.13 schwarze 815: return(1);
816:
1.42 schwarze 817: mt->offset = term_len(p, INDENT) + (size_t)ival;
1.13 schwarze 818: p->offset = mt->offset;
819:
820: return(1);
821: }
822:
823:
824: /* ARGSUSED */
825: static void
826: post_RS(DECL_ARGS)
827: {
828:
829: switch (n->type) {
830: case (MAN_BLOCK):
1.42 schwarze 831: mt->offset = mt->lmargin = term_len(p, INDENT);
1.13 schwarze 832: break;
1.27 schwarze 833: case (MAN_HEAD):
834: break;
1.13 schwarze 835: default:
836: term_newln(p);
1.42 schwarze 837: p->offset = term_len(p, INDENT);
1.11 schwarze 838: break;
839: }
1.47 schwarze 840: }
841:
842:
1.1 kristaps 843: static void
1.19 schwarze 844: print_man_node(DECL_ARGS)
1.1 kristaps 845: {
1.32 schwarze 846: size_t rm, rmax;
1.21 schwarze 847: int c;
1.1 kristaps 848:
849: c = 1;
850:
851: switch (n->type) {
852: case(MAN_TEXT):
853: if (0 == *n->string) {
854: term_vspace(p);
855: break;
856: }
1.21 schwarze 857:
1.1 kristaps 858: term_word(p, n->string);
1.21 schwarze 859:
1.11 schwarze 860: /* FIXME: this means that macro lines are munged! */
1.21 schwarze 861:
1.11 schwarze 862: if (MANT_LITERAL & mt->fl) {
1.32 schwarze 863: rm = p->rmargin;
864: rmax = p->maxrmargin;
1.29 schwarze 865: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1.11 schwarze 866: p->flags |= TERMP_NOSPACE;
867: term_flushln(p);
1.57 schwarze 868: p->flags &= ~TERMP_NOLPAD;
1.32 schwarze 869: p->rmargin = rm;
870: p->maxrmargin = rmax;
1.11 schwarze 871: }
1.1 kristaps 872: break;
1.58 ! schwarze 873: case (MAN_TBL):
! 874: if (TBL_SPAN_FIRST & n->span->flags)
! 875: term_newln(p);
! 876: term_tbl(p, n->span);
! 877: break;
1.1 kristaps 878: default:
1.26 schwarze 879: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
880: term_fontrepl(p, TERMFONT_NONE);
1.11 schwarze 881: if (termacts[n->tok].pre)
882: c = (*termacts[n->tok].pre)(p, mt, n, m);
1.1 kristaps 883: break;
884: }
885:
886: if (c && n->child)
1.21 schwarze 887: print_man_nodelist(p, mt, n->child, m);
1.1 kristaps 888:
1.58 ! schwarze 889: switch (n->type) {
! 890: case (MAN_TEXT):
! 891: /* FALLTHROUGH */
! 892: case (MAN_TBL):
! 893: break;
! 894: default:
1.1 kristaps 895: if (termacts[n->tok].post)
1.11 schwarze 896: (*termacts[n->tok].post)(p, mt, n, m);
1.26 schwarze 897: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
898: term_fontrepl(p, TERMFONT_NONE);
1.58 ! schwarze 899: break;
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: }