Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.68
1.68 ! schwarze 1: /* $Id: man_term.c,v 1.67 2011/04/24 16:22:02 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 "main.h"
1.10 schwarze 31:
32: #define INDENT 7
33: #define HALFINDENT 3
1.1 kristaps 34:
1.19 schwarze 35: /* FIXME: have PD set the default vspace width. */
36:
1.11 schwarze 37: struct mtermp {
38: int fl;
39: #define MANT_LITERAL (1 << 0)
1.13 schwarze 40: /*
41: * Default amount to indent the left margin after leading text
42: * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
43: * indent). This needs to be saved because `HP' and so on, if
44: * not having a specified value, must default.
45: *
46: * Note that this is the indentation AFTER the left offset, so
47: * the total offset is usually offset + lmargin.
48: */
49: size_t lmargin;
50: /*
51: * The default offset, i.e., the amount between any text and the
52: * page boundary.
53: */
54: size_t offset;
1.11 schwarze 55: };
56:
1.1 kristaps 57: #define DECL_ARGS struct termp *p, \
1.11 schwarze 58: struct mtermp *mt, \
1.1 kristaps 59: const struct man_node *n, \
60: const struct man_meta *m
61:
62: struct termact {
63: int (*pre)(DECL_ARGS);
64: void (*post)(DECL_ARGS);
1.26 schwarze 65: int flags;
66: #define MAN_NOTEXT (1 << 0) /* Never has text children. */
1.1 kristaps 67: };
68:
1.42 schwarze 69: static int a2width(const struct termp *, const char *);
70: static size_t a2height(const struct termp *, const char *);
1.18 schwarze 71:
1.21 schwarze 72: static void print_man_nodelist(DECL_ARGS);
1.19 schwarze 73: static void print_man_node(DECL_ARGS);
1.41 schwarze 74: static void print_man_head(struct termp *, const void *);
75: static void print_man_foot(struct termp *, const void *);
1.18 schwarze 76: static void print_bvspace(struct termp *,
77: const struct man_node *);
78:
1.51 schwarze 79: static int pre_alternate(DECL_ARGS);
1.1 kristaps 80: static int pre_B(DECL_ARGS);
1.11 schwarze 81: static int pre_HP(DECL_ARGS);
1.1 kristaps 82: static int pre_I(DECL_ARGS);
83: static int pre_IP(DECL_ARGS);
84: static int pre_PP(DECL_ARGS);
1.13 schwarze 85: static int pre_RS(DECL_ARGS);
1.1 kristaps 86: static int pre_SH(DECL_ARGS);
87: static int pre_SS(DECL_ARGS);
88: static int pre_TP(DECL_ARGS);
1.14 schwarze 89: static int pre_ign(DECL_ARGS);
1.45 schwarze 90: static int pre_in(DECL_ARGS);
91: static int pre_literal(DECL_ARGS);
1.11 schwarze 92: static int pre_sp(DECL_ARGS);
1.52 schwarze 93: static int pre_ft(DECL_ARGS);
1.1 kristaps 94:
1.11 schwarze 95: static void post_IP(DECL_ARGS);
96: static void post_HP(DECL_ARGS);
1.13 schwarze 97: static void post_RS(DECL_ARGS);
1.1 kristaps 98: static void post_SH(DECL_ARGS);
99: static void post_SS(DECL_ARGS);
1.11 schwarze 100: static void post_TP(DECL_ARGS);
1.1 kristaps 101:
1.17 schwarze 102: static const struct termact termacts[MAN_MAX] = {
1.45 schwarze 103: { pre_sp, NULL, MAN_NOTEXT }, /* br */
1.26 schwarze 104: { NULL, NULL, 0 }, /* TH */
105: { pre_SH, post_SH, 0 }, /* SH */
106: { pre_SS, post_SS, 0 }, /* SS */
107: { pre_TP, post_TP, 0 }, /* TP */
108: { pre_PP, NULL, 0 }, /* LP */
109: { pre_PP, NULL, 0 }, /* PP */
110: { pre_PP, NULL, 0 }, /* P */
111: { pre_IP, post_IP, 0 }, /* IP */
112: { pre_HP, post_HP, 0 }, /* HP */
113: { NULL, NULL, 0 }, /* SM */
114: { pre_B, NULL, 0 }, /* SB */
1.51 schwarze 115: { pre_alternate, NULL, 0 }, /* BI */
116: { pre_alternate, NULL, 0 }, /* IB */
117: { pre_alternate, NULL, 0 }, /* BR */
118: { pre_alternate, NULL, 0 }, /* RB */
1.26 schwarze 119: { NULL, NULL, 0 }, /* R */
120: { pre_B, NULL, 0 }, /* B */
121: { pre_I, NULL, 0 }, /* I */
1.51 schwarze 122: { pre_alternate, NULL, 0 }, /* IR */
123: { pre_alternate, NULL, 0 }, /* RI */
1.62 schwarze 124: { pre_ign, NULL, MAN_NOTEXT }, /* na */
1.26 schwarze 125: { pre_sp, NULL, MAN_NOTEXT }, /* sp */
1.45 schwarze 126: { pre_literal, NULL, 0 }, /* nf */
127: { pre_literal, NULL, 0 }, /* fi */
1.26 schwarze 128: { NULL, NULL, 0 }, /* RE */
129: { pre_RS, post_RS, 0 }, /* RS */
130: { pre_ign, NULL, 0 }, /* DT */
131: { pre_ign, NULL, 0 }, /* UC */
132: { pre_ign, NULL, 0 }, /* PD */
1.36 schwarze 133: { pre_ign, NULL, 0 }, /* AT */
1.45 schwarze 134: { pre_in, NULL, MAN_NOTEXT }, /* in */
1.52 schwarze 135: { pre_ft, NULL, MAN_NOTEXT }, /* ft */
1.1 kristaps 136: };
1.11 schwarze 137:
1.1 kristaps 138:
139:
1.16 schwarze 140: void
1.18 schwarze 141: terminal_man(void *arg, const struct man *man)
1.1 kristaps 142: {
1.18 schwarze 143: struct termp *p;
144: const struct man_node *n;
145: const struct man_meta *m;
146: struct mtermp mt;
147:
148: p = (struct termp *)arg;
149:
1.39 schwarze 150: p->overstep = 0;
1.32 schwarze 151: p->maxrmargin = p->defrmargin;
1.42 schwarze 152: p->tabwidth = term_len(p, 5);
1.27 schwarze 153:
1.18 schwarze 154: if (NULL == p->symtab)
1.68 ! schwarze 155: p->symtab = mchars_alloc();
1.18 schwarze 156:
157: n = man_node(man);
158: m = man_meta(man);
1.1 kristaps 159:
1.41 schwarze 160: term_begin(p, print_man_head, print_man_foot, m);
1.1 kristaps 161: p->flags |= TERMP_NOSPACE;
1.11 schwarze 162:
163: mt.fl = 0;
1.42 schwarze 164: mt.lmargin = term_len(p, INDENT);
165: mt.offset = term_len(p, INDENT);
1.11 schwarze 166:
1.18 schwarze 167: if (n->child)
1.21 schwarze 168: print_man_nodelist(p, &mt, n->child, m);
1.41 schwarze 169:
170: term_end(p);
1.1 kristaps 171: }
172:
173:
1.42 schwarze 174: static size_t
175: a2height(const struct termp *p, const char *cp)
1.11 schwarze 176: {
1.18 schwarze 177: struct roffsu su;
1.11 schwarze 178:
1.42 schwarze 179: if ( ! a2roffsu(cp, &su, SCALE_VS))
180: SCALE_VS_INIT(&su, term_strlen(p, cp));
1.11 schwarze 181:
1.42 schwarze 182: return(term_vspan(p, &su));
1.11 schwarze 183: }
184:
185:
186: static int
1.42 schwarze 187: a2width(const struct termp *p, const char *cp)
1.11 schwarze 188: {
1.18 schwarze 189: struct roffsu su;
1.11 schwarze 190:
1.42 schwarze 191: if ( ! a2roffsu(cp, &su, SCALE_BU))
1.18 schwarze 192: return(-1);
193:
1.42 schwarze 194: return((int)term_hspan(p, &su));
1.18 schwarze 195: }
1.11 schwarze 196:
197:
1.18 schwarze 198: static void
199: print_bvspace(struct termp *p, const struct man_node *n)
200: {
201: term_newln(p);
1.64 schwarze 202:
203: if (n->body && n->body->child && MAN_TBL == n->body->child->type)
204: return;
1.11 schwarze 205:
1.18 schwarze 206: if (NULL == n->prev)
207: return;
1.11 schwarze 208:
1.18 schwarze 209: if (MAN_SS == n->prev->tok)
210: return;
211: if (MAN_SH == n->prev->tok)
212: return;
1.11 schwarze 213:
1.18 schwarze 214: term_vspace(p);
1.14 schwarze 215: }
216:
217:
218: /* ARGSUSED */
219: static int
220: pre_ign(DECL_ARGS)
221: {
222:
223: return(0);
1.11 schwarze 224: }
225:
226:
1.1 kristaps 227: /* ARGSUSED */
228: static int
229: pre_I(DECL_ARGS)
230: {
231:
1.21 schwarze 232: term_fontrepl(p, TERMFONT_UNDER);
1.1 kristaps 233: return(1);
234: }
235:
236:
237: /* ARGSUSED */
1.11 schwarze 238: static int
1.45 schwarze 239: pre_literal(DECL_ARGS)
1.11 schwarze 240: {
241:
1.45 schwarze 242: term_newln(p);
1.53 schwarze 243:
244: if (MAN_nf == n->tok)
1.45 schwarze 245: mt->fl |= MANT_LITERAL;
1.53 schwarze 246: else
1.45 schwarze 247: mt->fl &= ~MANT_LITERAL;
248:
1.62 schwarze 249: return(0);
1.11 schwarze 250: }
251:
252: /* ARGSUSED */
253: static int
1.51 schwarze 254: pre_alternate(DECL_ARGS)
1.1 kristaps 255: {
1.51 schwarze 256: enum termfont font[2];
257: const struct man_node *nn;
258: int savelit, i;
1.1 kristaps 259:
1.51 schwarze 260: switch (n->tok) {
261: case (MAN_RB):
262: font[0] = TERMFONT_NONE;
263: font[1] = TERMFONT_BOLD;
264: break;
265: case (MAN_RI):
266: font[0] = TERMFONT_NONE;
267: font[1] = TERMFONT_UNDER;
268: break;
269: case (MAN_BR):
270: font[0] = TERMFONT_BOLD;
271: font[1] = TERMFONT_NONE;
272: break;
273: case (MAN_BI):
274: font[0] = TERMFONT_BOLD;
275: font[1] = TERMFONT_UNDER;
276: break;
277: case (MAN_IR):
278: font[0] = TERMFONT_UNDER;
279: font[1] = TERMFONT_NONE;
280: break;
281: case (MAN_IB):
282: font[0] = TERMFONT_UNDER;
283: font[1] = TERMFONT_BOLD;
284: break;
285: default:
286: abort();
287: }
1.17 schwarze 288:
1.51 schwarze 289: savelit = MANT_LITERAL & mt->fl;
290: mt->fl &= ~MANT_LITERAL;
1.17 schwarze 291:
1.51 schwarze 292: for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
293: term_fontrepl(p, font[i]);
294: if (savelit && NULL == nn->next)
295: mt->fl |= MANT_LITERAL;
1.19 schwarze 296: print_man_node(p, mt, nn, m);
1.51 schwarze 297: if (nn->next)
1.1 kristaps 298: p->flags |= TERMP_NOSPACE;
299: }
1.17 schwarze 300:
1.1 kristaps 301: return(0);
302: }
303:
304: /* ARGSUSED */
305: static int
306: pre_B(DECL_ARGS)
307: {
308:
1.21 schwarze 309: term_fontrepl(p, TERMFONT_BOLD);
1.1 kristaps 310: return(1);
1.52 schwarze 311: }
312:
313: /* ARGSUSED */
314: static int
315: pre_ft(DECL_ARGS)
316: {
317: const char *cp;
318:
319: if (NULL == n->child) {
320: term_fontlast(p);
321: return(0);
322: }
323:
324: cp = n->child->string;
325: switch (*cp) {
326: case ('4'):
327: /* FALLTHROUGH */
328: case ('3'):
329: /* FALLTHROUGH */
330: case ('B'):
331: term_fontrepl(p, TERMFONT_BOLD);
332: break;
333: case ('2'):
334: /* FALLTHROUGH */
335: case ('I'):
336: term_fontrepl(p, TERMFONT_UNDER);
337: break;
338: case ('P'):
339: term_fontlast(p);
340: break;
341: case ('1'):
342: /* FALLTHROUGH */
343: case ('C'):
344: /* FALLTHROUGH */
345: case ('R'):
346: term_fontrepl(p, TERMFONT_NONE);
347: break;
348: default:
349: break;
350: }
351: return(0);
1.1 kristaps 352: }
353:
354: /* ARGSUSED */
355: static int
1.45 schwarze 356: pre_in(DECL_ARGS)
1.11 schwarze 357: {
1.45 schwarze 358: int len, less;
359: size_t v;
360: const char *cp;
361:
362: term_newln(p);
363:
364: if (NULL == n->child) {
365: p->offset = mt->offset;
366: return(0);
367: }
1.11 schwarze 368:
1.45 schwarze 369: cp = n->child->string;
370: less = 0;
1.11 schwarze 371:
1.45 schwarze 372: if ('-' == *cp)
373: less = -1;
374: else if ('+' == *cp)
375: less = 1;
376: else
377: cp--;
378:
379: if ((len = a2width(p, ++cp)) < 0)
380: return(0);
381:
382: v = (size_t)len;
383:
384: if (less < 0)
385: p->offset -= p->offset > v ? v : p->offset;
386: else if (less > 0)
387: p->offset += v;
388: else
389: p->offset = v;
1.59 schwarze 390:
391: /* Don't let this creep beyond the right margin. */
392:
393: if (p->offset > p->rmargin)
394: p->offset = p->rmargin;
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: switch (n->type) {
850: case(MAN_TEXT):
1.61 schwarze 851: /*
852: * If we have a blank line, output a vertical space.
853: * If we have a space as the first character, break
854: * before printing the line's data.
855: */
1.60 schwarze 856: if ('\0' == *n->string) {
1.1 kristaps 857: term_vspace(p);
1.61 schwarze 858: return;
859: } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60 schwarze 860: term_newln(p);
1.21 schwarze 861:
1.1 kristaps 862: term_word(p, n->string);
1.21 schwarze 863:
1.61 schwarze 864: /*
865: * If we're in a literal context, make sure that words
866: * togehter on the same line stay together. This is a
867: * POST-printing call, so we check the NEXT word. Since
868: * -man doesn't have nested macros, we don't need to be
869: * more specific than this.
870: */
871: if (MANT_LITERAL & mt->fl &&
872: (NULL == n->next ||
873: n->next->line > n->line)) {
1.32 schwarze 874: rm = p->rmargin;
875: rmax = p->maxrmargin;
1.29 schwarze 876: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1.11 schwarze 877: p->flags |= TERMP_NOSPACE;
878: term_flushln(p);
1.57 schwarze 879: p->flags &= ~TERMP_NOLPAD;
1.32 schwarze 880: p->rmargin = rm;
881: p->maxrmargin = rmax;
1.11 schwarze 882: }
1.63 schwarze 883:
884: if (MAN_EOS & n->flags)
885: p->flags |= TERMP_SENTENCE;
1.66 schwarze 886: return;
887: case (MAN_EQN):
888: term_word(p, n->eqn->data);
1.61 schwarze 889: return;
1.58 schwarze 890: case (MAN_TBL):
1.61 schwarze 891: /*
892: * Tables are preceded by a newline. Then process a
893: * table line, which will cause line termination,
894: */
1.58 schwarze 895: if (TBL_SPAN_FIRST & n->span->flags)
896: term_newln(p);
897: term_tbl(p, n->span);
1.61 schwarze 898: return;
1.1 kristaps 899: default:
900: break;
901: }
902:
1.61 schwarze 903: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
904: term_fontrepl(p, TERMFONT_NONE);
905:
906: c = 1;
907: if (termacts[n->tok].pre)
908: c = (*termacts[n->tok].pre)(p, mt, n, m);
909:
1.1 kristaps 910: if (c && n->child)
1.21 schwarze 911: print_man_nodelist(p, mt, n->child, m);
1.1 kristaps 912:
1.61 schwarze 913: if (termacts[n->tok].post)
914: (*termacts[n->tok].post)(p, mt, n, m);
915: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
916: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 917:
918: if (MAN_EOS & n->flags)
919: p->flags |= TERMP_SENTENCE;
1.1 kristaps 920: }
921:
922:
923: static void
1.21 schwarze 924: print_man_nodelist(DECL_ARGS)
1.1 kristaps 925: {
1.11 schwarze 926:
1.19 schwarze 927: print_man_node(p, mt, n, m);
1.1 kristaps 928: if ( ! n->next)
929: return;
1.21 schwarze 930: print_man_nodelist(p, mt, n->next, m);
1.1 kristaps 931: }
932:
933:
934: static void
1.41 schwarze 935: print_man_foot(struct termp *p, const void *arg)
1.1 kristaps 936: {
1.41 schwarze 937: const struct man_meta *meta;
938:
939: meta = (const struct man_meta *)arg;
1.21 schwarze 940:
941: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 942:
1.38 schwarze 943: term_vspace(p);
944: term_vspace(p);
1.1 kristaps 945: term_vspace(p);
946:
947: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.65 schwarze 948: p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
1.1 kristaps 949: p->offset = 0;
1.46 schwarze 950:
951: /* term_strlen() can return zero. */
952: if (p->rmargin == p->maxrmargin)
953: p->rmargin--;
1.1 kristaps 954:
955: if (meta->source)
956: term_word(p, meta->source);
957: if (meta->source)
958: term_word(p, "");
959: term_flushln(p);
960:
961: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
962: p->offset = p->rmargin;
963: p->rmargin = p->maxrmargin;
964: p->flags &= ~TERMP_NOBREAK;
965:
1.65 schwarze 966: term_word(p, meta->date);
1.1 kristaps 967: term_flushln(p);
968: }
969:
970:
971: static void
1.41 schwarze 972: print_man_head(struct termp *p, const void *arg)
1.1 kristaps 973: {
1.20 schwarze 974: char buf[BUFSIZ], title[BUFSIZ];
1.25 schwarze 975: size_t buflen, titlen;
1.41 schwarze 976: const struct man_meta *m;
977:
978: m = (const struct man_meta *)arg;
1.1 kristaps 979:
1.29 schwarze 980: /*
981: * Note that old groff would spit out some spaces before the
982: * header. We discontinue this strange behaviour, but at one
983: * point we did so here.
984: */
985:
1.1 kristaps 986: p->rmargin = p->maxrmargin;
1.27 schwarze 987:
1.1 kristaps 988: p->offset = 0;
1.20 schwarze 989: buf[0] = title[0] = '\0';
1.1 kristaps 990:
1.20 schwarze 991: if (m->vol)
992: strlcpy(buf, m->vol, BUFSIZ);
1.42 schwarze 993: buflen = term_strlen(p, buf);
1.1 kristaps 994:
1.31 schwarze 995: snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1.42 schwarze 996: titlen = term_strlen(p, title);
1.1 kristaps 997:
998: p->offset = 0;
1.25 schwarze 999: p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1.42 schwarze 1000: (p->maxrmargin -
1001: term_strlen(p, buf) + term_len(p, 1)) / 2 :
1.25 schwarze 1002: p->maxrmargin - buflen;
1.1 kristaps 1003: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1004:
1005: term_word(p, title);
1006: term_flushln(p);
1007:
1008: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1009: p->offset = p->rmargin;
1.25 schwarze 1010: p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1011: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1012:
1013: term_word(p, buf);
1014: term_flushln(p);
1015:
1016: p->flags &= ~TERMP_NOBREAK;
1.25 schwarze 1017: if (p->rmargin + titlen <= p->maxrmargin) {
1018: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1019: p->offset = p->rmargin;
1020: p->rmargin = p->maxrmargin;
1021: term_word(p, title);
1022: term_flushln(p);
1023: }
1.1 kristaps 1024:
1025: p->rmargin = p->maxrmargin;
1026: p->offset = 0;
1027: p->flags &= ~TERMP_NOSPACE;
1.29 schwarze 1028:
1029: /*
1030: * Groff likes to have some leading spaces before content. Well
1031: * that's fine by me.
1032: */
1033:
1034: term_vspace(p);
1035: term_vspace(p);
1036: term_vspace(p);
1.1 kristaps 1037: }