Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.76
1.76 ! schwarze 1: /* $Id: man_term.c,v 1.75 2011/09/20 14:20:47 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) {
1.76 ! schwarze 250: p->offset = p->rmargin;
1.72 schwarze 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);
1.76 ! schwarze 468: if (len < one)
1.72 schwarze 469: len = one;
1.11 schwarze 470:
1.13 schwarze 471: p->offset = mt->offset;
472: p->rmargin = mt->offset + len;
1.11 schwarze 473:
474: if (ival >= 0)
1.70 schwarze 475: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 476:
477: return(1);
478: }
479:
480:
481: /* ARGSUSED */
482: static void
483: post_HP(DECL_ARGS)
484: {
485:
486: switch (n->type) {
487: case (MAN_BLOCK):
488: term_flushln(p);
489: break;
490: case (MAN_BODY):
491: term_flushln(p);
492: p->flags &= ~TERMP_NOBREAK;
493: p->flags &= ~TERMP_TWOSPACE;
1.13 schwarze 494: p->offset = mt->offset;
1.11 schwarze 495: p->rmargin = p->maxrmargin;
496: break;
497: default:
498: break;
499: }
500: }
501:
502:
503: /* ARGSUSED */
504: static int
1.1 kristaps 505: pre_PP(DECL_ARGS)
506: {
507:
1.11 schwarze 508: switch (n->type) {
509: case (MAN_BLOCK):
1.70 schwarze 510: mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
1.18 schwarze 511: print_bvspace(p, n);
1.11 schwarze 512: break;
513: default:
1.13 schwarze 514: p->offset = mt->offset;
1.11 schwarze 515: break;
516: }
517:
1.54 schwarze 518: return(MAN_HEAD != n->type);
1.1 kristaps 519: }
520:
521:
522: /* ARGSUSED */
523: static int
524: pre_IP(DECL_ARGS)
525: {
1.11 schwarze 526: const struct man_node *nn;
527: size_t len;
1.57 schwarze 528: int savelit, ival;
1.11 schwarze 529:
530: switch (n->type) {
531: case (MAN_BODY):
532: p->flags |= TERMP_NOSPACE;
533: break;
534: case (MAN_HEAD):
535: p->flags |= TERMP_NOBREAK;
536: break;
537: case (MAN_BLOCK):
1.18 schwarze 538: print_bvspace(p, n);
1.11 schwarze 539: /* FALLTHROUGH */
540: default:
541: return(1);
542: }
543:
1.70 schwarze 544: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 545: ival = -1;
546:
1.57 schwarze 547: /* Calculate the offset from the optional second argument. */
1.11 schwarze 548: if (NULL != (nn = n->parent->head->child))
1.57 schwarze 549: if (NULL != (nn = nn->next))
1.42 schwarze 550: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 551: len = (size_t)ival;
552:
553: switch (n->type) {
554: case (MAN_HEAD):
555: /* Handle zero-width lengths. */
556: if (0 == len)
1.42 schwarze 557: len = term_len(p, 1);
1.11 schwarze 558:
1.13 schwarze 559: p->offset = mt->offset;
560: p->rmargin = mt->offset + len;
1.11 schwarze 561: if (ival < 0)
562: break;
563:
564: /* Set the saved left-margin. */
1.70 schwarze 565: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.1 kristaps 566:
1.57 schwarze 567: savelit = MANT_LITERAL & mt->fl;
568: mt->fl &= ~MANT_LITERAL;
569:
570: if (n->child)
571: print_man_node(p, mt, n->child, m);
572:
573: if (savelit)
574: mt->fl |= MANT_LITERAL;
575:
1.11 schwarze 576: return(0);
577: case (MAN_BODY):
1.13 schwarze 578: p->offset = mt->offset + len;
1.11 schwarze 579: p->rmargin = p->maxrmargin;
580: break;
581: default:
582: break;
583: }
1.1 kristaps 584:
1.11 schwarze 585: return(1);
586: }
1.1 kristaps 587:
588:
1.11 schwarze 589: /* ARGSUSED */
590: static void
591: post_IP(DECL_ARGS)
592: {
1.4 schwarze 593:
1.11 schwarze 594: switch (n->type) {
595: case (MAN_HEAD):
596: term_flushln(p);
597: p->flags &= ~TERMP_NOBREAK;
598: p->rmargin = p->maxrmargin;
599: break;
600: case (MAN_BODY):
1.57 schwarze 601: term_newln(p);
1.11 schwarze 602: break;
603: default:
604: break;
605: }
1.1 kristaps 606: }
607:
608:
609: /* ARGSUSED */
610: static int
611: pre_TP(DECL_ARGS)
612: {
1.11 schwarze 613: const struct man_node *nn;
614: size_t len;
1.57 schwarze 615: int savelit, ival;
1.11 schwarze 616:
617: switch (n->type) {
618: case (MAN_HEAD):
619: p->flags |= TERMP_NOBREAK;
620: break;
621: case (MAN_BODY):
622: p->flags |= TERMP_NOSPACE;
623: break;
624: case (MAN_BLOCK):
1.18 schwarze 625: print_bvspace(p, n);
1.11 schwarze 626: /* FALLTHROUGH */
627: default:
628: return(1);
629: }
630:
1.70 schwarze 631: len = (size_t)mt->lmargin[mt->lmargincur];
1.11 schwarze 632: ival = -1;
633:
634: /* Calculate offset. */
1.1 kristaps 635:
1.70 schwarze 636: if (NULL != (nn = n->parent->head->child))
1.75 schwarze 637: if (nn->string && nn->parent->line == nn->line)
1.42 schwarze 638: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 639: len = (size_t)ival;
1.8 schwarze 640:
1.11 schwarze 641: switch (n->type) {
642: case (MAN_HEAD):
643: /* Handle zero-length properly. */
644: if (0 == len)
1.42 schwarze 645: len = term_len(p, 1);
1.11 schwarze 646:
1.13 schwarze 647: p->offset = mt->offset;
648: p->rmargin = mt->offset + len;
1.11 schwarze 649:
1.57 schwarze 650: savelit = MANT_LITERAL & mt->fl;
651: mt->fl &= ~MANT_LITERAL;
652:
1.11 schwarze 653: /* Don't print same-line elements. */
1.57 schwarze 654: for (nn = n->child; nn; nn = nn->next)
1.11 schwarze 655: if (nn->line > n->line)
1.19 schwarze 656: print_man_node(p, mt, nn, m);
1.11 schwarze 657:
1.57 schwarze 658: if (savelit)
659: mt->fl |= MANT_LITERAL;
1.11 schwarze 660: if (ival >= 0)
1.70 schwarze 661: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 662:
663: return(0);
664: case (MAN_BODY):
1.13 schwarze 665: p->offset = mt->offset + len;
1.11 schwarze 666: p->rmargin = p->maxrmargin;
667: break;
668: default:
669: break;
670: }
1.1 kristaps 671:
1.11 schwarze 672: return(1);
673: }
1.1 kristaps 674:
675:
1.11 schwarze 676: /* ARGSUSED */
677: static void
678: post_TP(DECL_ARGS)
679: {
1.1 kristaps 680:
1.11 schwarze 681: switch (n->type) {
682: case (MAN_HEAD):
683: term_flushln(p);
684: p->flags &= ~TERMP_NOBREAK;
685: p->flags &= ~TERMP_TWOSPACE;
686: p->rmargin = p->maxrmargin;
687: break;
688: case (MAN_BODY):
1.57 schwarze 689: term_newln(p);
1.11 schwarze 690: break;
691: default:
692: break;
693: }
1.1 kristaps 694: }
695:
696:
697: /* ARGSUSED */
698: static int
699: pre_SS(DECL_ARGS)
700: {
701:
1.11 schwarze 702: switch (n->type) {
703: case (MAN_BLOCK):
1.69 schwarze 704: mt->fl &= ~MANT_LITERAL;
1.70 schwarze 705: mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
1.42 schwarze 706: mt->offset = term_len(p, INDENT);
1.11 schwarze 707: /* If following a prior empty `SS', no vspace. */
708: if (n->prev && MAN_SS == n->prev->tok)
709: if (NULL == n->prev->body->child)
710: break;
711: if (NULL == n->prev)
712: break;
713: term_vspace(p);
714: break;
715: case (MAN_HEAD):
1.21 schwarze 716: term_fontrepl(p, TERMFONT_BOLD);
1.42 schwarze 717: p->offset = term_len(p, HALFINDENT);
1.11 schwarze 718: break;
719: case (MAN_BODY):
1.13 schwarze 720: p->offset = mt->offset;
1.11 schwarze 721: break;
722: default:
723: break;
724: }
725:
1.1 kristaps 726: return(1);
727: }
728:
729:
730: /* ARGSUSED */
731: static void
732: post_SS(DECL_ARGS)
733: {
734:
1.11 schwarze 735: switch (n->type) {
736: case (MAN_HEAD):
737: term_newln(p);
738: break;
739: case (MAN_BODY):
740: term_newln(p);
741: break;
742: default:
743: break;
744: }
1.1 kristaps 745: }
746:
747:
748: /* ARGSUSED */
749: static int
750: pre_SH(DECL_ARGS)
751: {
752:
1.11 schwarze 753: switch (n->type) {
754: case (MAN_BLOCK):
1.69 schwarze 755: mt->fl &= ~MANT_LITERAL;
1.70 schwarze 756: mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
1.42 schwarze 757: mt->offset = term_len(p, INDENT);
1.11 schwarze 758: /* If following a prior empty `SH', no vspace. */
759: if (n->prev && MAN_SH == n->prev->tok)
760: if (NULL == n->prev->body->child)
761: break;
1.29 schwarze 762: /* If the first macro, no vspae. */
763: if (NULL == n->prev)
764: break;
1.11 schwarze 765: term_vspace(p);
766: break;
767: case (MAN_HEAD):
1.21 schwarze 768: term_fontrepl(p, TERMFONT_BOLD);
1.11 schwarze 769: p->offset = 0;
770: break;
771: case (MAN_BODY):
1.13 schwarze 772: p->offset = mt->offset;
1.11 schwarze 773: break;
774: default:
775: break;
776: }
777:
1.1 kristaps 778: return(1);
779: }
780:
781:
782: /* ARGSUSED */
783: static void
784: post_SH(DECL_ARGS)
785: {
786:
1.11 schwarze 787: switch (n->type) {
788: case (MAN_HEAD):
789: term_newln(p);
790: break;
791: case (MAN_BODY):
792: term_newln(p);
793: break;
794: default:
1.13 schwarze 795: break;
796: }
797: }
798:
799: /* ARGSUSED */
800: static int
801: pre_RS(DECL_ARGS)
802: {
1.69 schwarze 803: int ival;
804: size_t sz;
1.13 schwarze 805:
806: switch (n->type) {
807: case (MAN_BLOCK):
808: term_newln(p);
809: return(1);
810: case (MAN_HEAD):
811: return(0);
812: default:
813: break;
814: }
815:
1.69 schwarze 816: sz = term_len(p, INDENT);
1.13 schwarze 817:
1.69 schwarze 818: if (NULL != (n = n->parent->head->child))
819: if ((ival = a2width(p, n->string)) >= 0)
820: sz = (size_t)ival;
1.13 schwarze 821:
1.69 schwarze 822: mt->offset += sz;
1.74 schwarze 823: p->rmargin = p->maxrmargin;
824: p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin;
1.13 schwarze 825:
1.70 schwarze 826: if (++mt->lmarginsz < MAXMARGINS)
827: mt->lmargincur = mt->lmarginsz;
828:
829: mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
1.13 schwarze 830: return(1);
831: }
832:
833: /* ARGSUSED */
834: static void
835: post_RS(DECL_ARGS)
836: {
1.69 schwarze 837: int ival;
838: size_t sz;
1.13 schwarze 839:
840: switch (n->type) {
841: case (MAN_BLOCK):
1.69 schwarze 842: return;
1.27 schwarze 843: case (MAN_HEAD):
1.69 schwarze 844: return;
1.13 schwarze 845: default:
846: term_newln(p);
1.11 schwarze 847: break;
848: }
1.69 schwarze 849:
850: sz = term_len(p, INDENT);
851:
852: if (NULL != (n = n->parent->head->child))
853: if ((ival = a2width(p, n->string)) >= 0)
854: sz = (size_t)ival;
855:
856: mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
857: p->offset = mt->offset;
1.70 schwarze 858:
859: if (--mt->lmarginsz < MAXMARGINS)
860: mt->lmargincur = mt->lmarginsz;
1.47 schwarze 861: }
862:
1.1 kristaps 863: static void
1.19 schwarze 864: print_man_node(DECL_ARGS)
1.1 kristaps 865: {
1.32 schwarze 866: size_t rm, rmax;
1.21 schwarze 867: int c;
1.1 kristaps 868:
869: switch (n->type) {
870: case(MAN_TEXT):
1.61 schwarze 871: /*
872: * If we have a blank line, output a vertical space.
873: * If we have a space as the first character, break
874: * before printing the line's data.
875: */
1.60 schwarze 876: if ('\0' == *n->string) {
1.1 kristaps 877: term_vspace(p);
1.61 schwarze 878: return;
879: } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60 schwarze 880: term_newln(p);
1.21 schwarze 881:
1.1 kristaps 882: term_word(p, n->string);
1.21 schwarze 883:
1.61 schwarze 884: /*
885: * If we're in a literal context, make sure that words
886: * togehter on the same line stay together. This is a
887: * POST-printing call, so we check the NEXT word. Since
888: * -man doesn't have nested macros, we don't need to be
889: * more specific than this.
890: */
1.72 schwarze 891: if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1.61 schwarze 892: (NULL == n->next ||
893: n->next->line > n->line)) {
1.32 schwarze 894: rm = p->rmargin;
895: rmax = p->maxrmargin;
1.29 schwarze 896: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1.11 schwarze 897: p->flags |= TERMP_NOSPACE;
898: term_flushln(p);
1.32 schwarze 899: p->rmargin = rm;
900: p->maxrmargin = rmax;
1.11 schwarze 901: }
1.63 schwarze 902:
903: if (MAN_EOS & n->flags)
904: p->flags |= TERMP_SENTENCE;
1.66 schwarze 905: return;
906: case (MAN_EQN):
1.71 schwarze 907: term_eqn(p, n->eqn);
1.61 schwarze 908: return;
1.58 schwarze 909: case (MAN_TBL):
1.61 schwarze 910: /*
911: * Tables are preceded by a newline. Then process a
912: * table line, which will cause line termination,
913: */
1.58 schwarze 914: if (TBL_SPAN_FIRST & n->span->flags)
915: term_newln(p);
916: term_tbl(p, n->span);
1.61 schwarze 917: return;
1.1 kristaps 918: default:
919: break;
920: }
921:
1.61 schwarze 922: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
923: term_fontrepl(p, TERMFONT_NONE);
924:
925: c = 1;
926: if (termacts[n->tok].pre)
927: c = (*termacts[n->tok].pre)(p, mt, n, m);
928:
1.1 kristaps 929: if (c && n->child)
1.21 schwarze 930: print_man_nodelist(p, mt, n->child, m);
1.1 kristaps 931:
1.61 schwarze 932: if (termacts[n->tok].post)
933: (*termacts[n->tok].post)(p, mt, n, m);
934: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
935: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 936:
937: if (MAN_EOS & n->flags)
938: p->flags |= TERMP_SENTENCE;
1.1 kristaps 939: }
940:
941:
942: static void
1.21 schwarze 943: print_man_nodelist(DECL_ARGS)
1.1 kristaps 944: {
1.11 schwarze 945:
1.19 schwarze 946: print_man_node(p, mt, n, m);
1.1 kristaps 947: if ( ! n->next)
948: return;
1.21 schwarze 949: print_man_nodelist(p, mt, n->next, m);
1.1 kristaps 950: }
951:
952:
953: static void
1.41 schwarze 954: print_man_foot(struct termp *p, const void *arg)
1.1 kristaps 955: {
1.41 schwarze 956: const struct man_meta *meta;
957:
958: meta = (const struct man_meta *)arg;
1.21 schwarze 959:
960: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 961:
1.38 schwarze 962: term_vspace(p);
963: term_vspace(p);
1.1 kristaps 964: term_vspace(p);
965:
966: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.65 schwarze 967: p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
1.1 kristaps 968: p->offset = 0;
1.46 schwarze 969:
970: /* term_strlen() can return zero. */
971: if (p->rmargin == p->maxrmargin)
972: p->rmargin--;
1.1 kristaps 973:
974: if (meta->source)
975: term_word(p, meta->source);
976: if (meta->source)
977: term_word(p, "");
978: term_flushln(p);
979:
1.72 schwarze 980: p->flags |= TERMP_NOSPACE;
1.1 kristaps 981: p->offset = p->rmargin;
982: p->rmargin = p->maxrmargin;
983: p->flags &= ~TERMP_NOBREAK;
984:
1.65 schwarze 985: term_word(p, meta->date);
1.1 kristaps 986: term_flushln(p);
987: }
988:
989:
990: static void
1.41 schwarze 991: print_man_head(struct termp *p, const void *arg)
1.1 kristaps 992: {
1.20 schwarze 993: char buf[BUFSIZ], title[BUFSIZ];
1.25 schwarze 994: size_t buflen, titlen;
1.41 schwarze 995: const struct man_meta *m;
996:
997: m = (const struct man_meta *)arg;
1.1 kristaps 998:
1.29 schwarze 999: /*
1000: * Note that old groff would spit out some spaces before the
1001: * header. We discontinue this strange behaviour, but at one
1002: * point we did so here.
1003: */
1004:
1.73 schwarze 1005: p->offset = 0;
1.1 kristaps 1006: p->rmargin = p->maxrmargin;
1.27 schwarze 1007:
1.20 schwarze 1008: buf[0] = title[0] = '\0';
1.1 kristaps 1009:
1.20 schwarze 1010: if (m->vol)
1011: strlcpy(buf, m->vol, BUFSIZ);
1.42 schwarze 1012: buflen = term_strlen(p, buf);
1.1 kristaps 1013:
1.31 schwarze 1014: snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1.42 schwarze 1015: titlen = term_strlen(p, title);
1.1 kristaps 1016:
1.73 schwarze 1017: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.1 kristaps 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:
1024: term_word(p, title);
1025: term_flushln(p);
1026:
1.72 schwarze 1027: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1028: p->offset = p->rmargin;
1.25 schwarze 1029: p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1030: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1031:
1032: term_word(p, buf);
1033: term_flushln(p);
1034:
1035: p->flags &= ~TERMP_NOBREAK;
1.25 schwarze 1036: if (p->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1037: p->flags |= TERMP_NOSPACE;
1.25 schwarze 1038: p->offset = p->rmargin;
1039: p->rmargin = p->maxrmargin;
1040: term_word(p, title);
1041: term_flushln(p);
1042: }
1.1 kristaps 1043:
1.73 schwarze 1044: p->flags &= ~TERMP_NOSPACE;
1045: p->offset = 0;
1.1 kristaps 1046: p->rmargin = p->maxrmargin;
1.29 schwarze 1047:
1048: /*
1049: * Groff likes to have some leading spaces before content. Well
1050: * that's fine by me.
1051: */
1052:
1053: term_vspace(p);
1054: term_vspace(p);
1055: term_vspace(p);
1.1 kristaps 1056: }