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