Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.71
1.71 ! schwarze 1: /* $Id: man_term.c,v 1.70 2011/07/07 21:10:48 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.62 schwarze 244: return(0);
1.11 schwarze 245: }
246:
247: /* ARGSUSED */
248: static int
1.51 schwarze 249: pre_alternate(DECL_ARGS)
1.1 kristaps 250: {
1.51 schwarze 251: enum termfont font[2];
252: const struct man_node *nn;
253: int savelit, i;
1.1 kristaps 254:
1.51 schwarze 255: switch (n->tok) {
256: case (MAN_RB):
257: font[0] = TERMFONT_NONE;
258: font[1] = TERMFONT_BOLD;
259: break;
260: case (MAN_RI):
261: font[0] = TERMFONT_NONE;
262: font[1] = TERMFONT_UNDER;
263: break;
264: case (MAN_BR):
265: font[0] = TERMFONT_BOLD;
266: font[1] = TERMFONT_NONE;
267: break;
268: case (MAN_BI):
269: font[0] = TERMFONT_BOLD;
270: font[1] = TERMFONT_UNDER;
271: break;
272: case (MAN_IR):
273: font[0] = TERMFONT_UNDER;
274: font[1] = TERMFONT_NONE;
275: break;
276: case (MAN_IB):
277: font[0] = TERMFONT_UNDER;
278: font[1] = TERMFONT_BOLD;
279: break;
280: default:
281: abort();
282: }
1.17 schwarze 283:
1.51 schwarze 284: savelit = MANT_LITERAL & mt->fl;
285: mt->fl &= ~MANT_LITERAL;
1.17 schwarze 286:
1.51 schwarze 287: for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
288: term_fontrepl(p, font[i]);
289: if (savelit && NULL == nn->next)
290: mt->fl |= MANT_LITERAL;
1.19 schwarze 291: print_man_node(p, mt, nn, m);
1.51 schwarze 292: if (nn->next)
1.1 kristaps 293: p->flags |= TERMP_NOSPACE;
294: }
1.17 schwarze 295:
1.1 kristaps 296: return(0);
297: }
298:
299: /* ARGSUSED */
300: static int
301: pre_B(DECL_ARGS)
302: {
303:
1.21 schwarze 304: term_fontrepl(p, TERMFONT_BOLD);
1.1 kristaps 305: return(1);
1.52 schwarze 306: }
307:
308: /* ARGSUSED */
309: static int
310: pre_ft(DECL_ARGS)
311: {
312: const char *cp;
313:
314: if (NULL == n->child) {
315: term_fontlast(p);
316: return(0);
317: }
318:
319: cp = n->child->string;
320: switch (*cp) {
321: case ('4'):
322: /* FALLTHROUGH */
323: case ('3'):
324: /* FALLTHROUGH */
325: case ('B'):
326: term_fontrepl(p, TERMFONT_BOLD);
327: break;
328: case ('2'):
329: /* FALLTHROUGH */
330: case ('I'):
331: term_fontrepl(p, TERMFONT_UNDER);
332: break;
333: case ('P'):
334: term_fontlast(p);
335: break;
336: case ('1'):
337: /* FALLTHROUGH */
338: case ('C'):
339: /* FALLTHROUGH */
340: case ('R'):
341: term_fontrepl(p, TERMFONT_NONE);
342: break;
343: default:
344: break;
345: }
346: return(0);
1.1 kristaps 347: }
348:
349: /* ARGSUSED */
350: static int
1.45 schwarze 351: pre_in(DECL_ARGS)
1.11 schwarze 352: {
1.45 schwarze 353: int len, less;
354: size_t v;
355: const char *cp;
356:
357: term_newln(p);
358:
359: if (NULL == n->child) {
360: p->offset = mt->offset;
361: return(0);
362: }
1.11 schwarze 363:
1.45 schwarze 364: cp = n->child->string;
365: less = 0;
1.11 schwarze 366:
1.45 schwarze 367: if ('-' == *cp)
368: less = -1;
369: else if ('+' == *cp)
370: less = 1;
371: else
372: cp--;
373:
374: if ((len = a2width(p, ++cp)) < 0)
375: return(0);
376:
377: v = (size_t)len;
378:
379: if (less < 0)
380: p->offset -= p->offset > v ? v : p->offset;
381: else if (less > 0)
382: p->offset += v;
383: else
384: p->offset = v;
1.59 schwarze 385:
386: /* Don't let this creep beyond the right margin. */
387:
388: if (p->offset > p->rmargin)
389: p->offset = p->rmargin;
1.11 schwarze 390:
391: return(0);
392: }
393:
394:
395: /* ARGSUSED */
396: static int
1.45 schwarze 397: pre_sp(DECL_ARGS)
1.8 schwarze 398: {
1.45 schwarze 399: size_t i, len;
400:
1.69 schwarze 401: if ((NULL == n->prev && n->parent)) {
402: if (MAN_SS == n->parent->tok)
403: return(0);
404: if (MAN_SH == n->parent->tok)
405: return(0);
406: }
407:
1.45 schwarze 408: switch (n->tok) {
409: case (MAN_br):
410: len = 0;
411: break;
412: default:
413: len = n->child ? a2height(p, n->child->string) : 1;
414: break;
415: }
416:
417: if (0 == len)
418: term_newln(p);
419: for (i = 0; i < len; i++)
420: term_vspace(p);
1.8 schwarze 421:
422: return(0);
423: }
424:
425:
426: /* ARGSUSED */
427: static int
1.11 schwarze 428: pre_HP(DECL_ARGS)
429: {
430: size_t len;
431: int ival;
432: const struct man_node *nn;
433:
434: switch (n->type) {
435: case (MAN_BLOCK):
1.18 schwarze 436: print_bvspace(p, n);
1.11 schwarze 437: return(1);
438: case (MAN_BODY):
439: p->flags |= TERMP_NOBREAK;
440: p->flags |= TERMP_TWOSPACE;
441: break;
442: default:
443: return(0);
444: }
445:
1.70 schwarze 446: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 447: ival = -1;
448:
449: /* Calculate offset. */
450:
451: if (NULL != (nn = n->parent->head->child))
1.42 schwarze 452: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 453: len = (size_t)ival;
454:
455: if (0 == len)
1.42 schwarze 456: len = term_len(p, 1);
1.11 schwarze 457:
1.13 schwarze 458: p->offset = mt->offset;
459: p->rmargin = mt->offset + len;
1.11 schwarze 460:
461: if (ival >= 0)
1.70 schwarze 462: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 463:
464: return(1);
465: }
466:
467:
468: /* ARGSUSED */
469: static void
470: post_HP(DECL_ARGS)
471: {
472:
473: switch (n->type) {
474: case (MAN_BLOCK):
475: term_flushln(p);
476: break;
477: case (MAN_BODY):
478: term_flushln(p);
479: p->flags &= ~TERMP_NOBREAK;
480: p->flags &= ~TERMP_TWOSPACE;
1.13 schwarze 481: p->offset = mt->offset;
1.11 schwarze 482: p->rmargin = p->maxrmargin;
483: break;
484: default:
485: break;
486: }
487: }
488:
489:
490: /* ARGSUSED */
491: static int
1.1 kristaps 492: pre_PP(DECL_ARGS)
493: {
494:
1.11 schwarze 495: switch (n->type) {
496: case (MAN_BLOCK):
1.70 schwarze 497: mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
1.18 schwarze 498: print_bvspace(p, n);
1.11 schwarze 499: break;
500: default:
1.13 schwarze 501: p->offset = mt->offset;
1.11 schwarze 502: break;
503: }
504:
1.54 schwarze 505: return(MAN_HEAD != n->type);
1.1 kristaps 506: }
507:
508:
509: /* ARGSUSED */
510: static int
511: pre_IP(DECL_ARGS)
512: {
1.11 schwarze 513: const struct man_node *nn;
514: size_t len;
1.57 schwarze 515: int savelit, ival;
1.11 schwarze 516:
517: switch (n->type) {
518: case (MAN_BODY):
519: p->flags |= TERMP_NOLPAD;
520: p->flags |= TERMP_NOSPACE;
521: break;
522: case (MAN_HEAD):
523: p->flags |= TERMP_NOBREAK;
524: break;
525: case (MAN_BLOCK):
1.18 schwarze 526: print_bvspace(p, n);
1.11 schwarze 527: /* FALLTHROUGH */
528: default:
529: return(1);
530: }
531:
1.70 schwarze 532: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 533: ival = -1;
534:
1.57 schwarze 535: /* Calculate the offset from the optional second argument. */
1.11 schwarze 536: if (NULL != (nn = n->parent->head->child))
1.57 schwarze 537: if (NULL != (nn = nn->next))
1.42 schwarze 538: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 539: len = (size_t)ival;
540:
541: switch (n->type) {
542: case (MAN_HEAD):
543: /* Handle zero-width lengths. */
544: if (0 == len)
1.42 schwarze 545: len = term_len(p, 1);
1.11 schwarze 546:
1.13 schwarze 547: p->offset = mt->offset;
548: p->rmargin = mt->offset + len;
1.11 schwarze 549: if (ival < 0)
550: break;
551:
552: /* Set the saved left-margin. */
1.70 schwarze 553: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.1 kristaps 554:
1.57 schwarze 555: savelit = MANT_LITERAL & mt->fl;
556: mt->fl &= ~MANT_LITERAL;
557:
558: if (n->child)
559: print_man_node(p, mt, n->child, m);
560:
561: if (savelit)
562: mt->fl |= MANT_LITERAL;
563:
1.11 schwarze 564: return(0);
565: case (MAN_BODY):
1.13 schwarze 566: p->offset = mt->offset + len;
1.11 schwarze 567: p->rmargin = p->maxrmargin;
568: break;
569: default:
570: break;
571: }
1.1 kristaps 572:
1.11 schwarze 573: return(1);
574: }
1.1 kristaps 575:
576:
1.11 schwarze 577: /* ARGSUSED */
578: static void
579: post_IP(DECL_ARGS)
580: {
1.4 schwarze 581:
1.11 schwarze 582: switch (n->type) {
583: case (MAN_HEAD):
584: term_flushln(p);
585: p->flags &= ~TERMP_NOBREAK;
586: p->rmargin = p->maxrmargin;
587: break;
588: case (MAN_BODY):
1.57 schwarze 589: term_newln(p);
1.11 schwarze 590: p->flags &= ~TERMP_NOLPAD;
591: break;
592: default:
593: break;
594: }
1.1 kristaps 595: }
596:
597:
598: /* ARGSUSED */
599: static int
600: pre_TP(DECL_ARGS)
601: {
1.11 schwarze 602: const struct man_node *nn;
603: size_t len;
1.57 schwarze 604: int savelit, ival;
1.11 schwarze 605:
606: switch (n->type) {
607: case (MAN_HEAD):
608: p->flags |= TERMP_NOBREAK;
609: break;
610: case (MAN_BODY):
611: p->flags |= TERMP_NOLPAD;
612: p->flags |= TERMP_NOSPACE;
613: break;
614: case (MAN_BLOCK):
1.18 schwarze 615: print_bvspace(p, n);
1.11 schwarze 616: /* FALLTHROUGH */
617: default:
618: return(1);
619: }
620:
1.70 schwarze 621: len = (size_t)mt->lmargin[mt->lmargincur];
1.11 schwarze 622: ival = -1;
623:
624: /* Calculate offset. */
1.1 kristaps 625:
1.70 schwarze 626: if (NULL != (nn = n->parent->head->child))
627: if (nn->parent->line == nn->line)
1.42 schwarze 628: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 629: len = (size_t)ival;
1.8 schwarze 630:
1.11 schwarze 631: switch (n->type) {
632: case (MAN_HEAD):
633: /* Handle zero-length properly. */
634: if (0 == len)
1.42 schwarze 635: len = term_len(p, 1);
1.11 schwarze 636:
1.13 schwarze 637: p->offset = mt->offset;
638: p->rmargin = mt->offset + len;
1.11 schwarze 639:
1.57 schwarze 640: savelit = MANT_LITERAL & mt->fl;
641: mt->fl &= ~MANT_LITERAL;
642:
1.11 schwarze 643: /* Don't print same-line elements. */
1.57 schwarze 644: for (nn = n->child; nn; nn = nn->next)
1.11 schwarze 645: if (nn->line > n->line)
1.19 schwarze 646: print_man_node(p, mt, nn, m);
1.11 schwarze 647:
1.57 schwarze 648: if (savelit)
649: mt->fl |= MANT_LITERAL;
1.11 schwarze 650: if (ival >= 0)
1.70 schwarze 651: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 652:
653: return(0);
654: case (MAN_BODY):
1.13 schwarze 655: p->offset = mt->offset + len;
1.11 schwarze 656: p->rmargin = p->maxrmargin;
657: break;
658: default:
659: break;
660: }
1.1 kristaps 661:
1.11 schwarze 662: return(1);
663: }
1.1 kristaps 664:
665:
1.11 schwarze 666: /* ARGSUSED */
667: static void
668: post_TP(DECL_ARGS)
669: {
1.1 kristaps 670:
1.11 schwarze 671: switch (n->type) {
672: case (MAN_HEAD):
673: term_flushln(p);
674: p->flags &= ~TERMP_NOBREAK;
675: p->flags &= ~TERMP_TWOSPACE;
676: p->rmargin = p->maxrmargin;
677: break;
678: case (MAN_BODY):
1.57 schwarze 679: term_newln(p);
1.11 schwarze 680: p->flags &= ~TERMP_NOLPAD;
681: break;
682: default:
683: break;
684: }
1.1 kristaps 685: }
686:
687:
688: /* ARGSUSED */
689: static int
690: pre_SS(DECL_ARGS)
691: {
692:
1.11 schwarze 693: switch (n->type) {
694: case (MAN_BLOCK):
1.69 schwarze 695: mt->fl &= ~MANT_LITERAL;
1.70 schwarze 696: mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
1.42 schwarze 697: mt->offset = term_len(p, INDENT);
1.11 schwarze 698: /* If following a prior empty `SS', no vspace. */
699: if (n->prev && MAN_SS == n->prev->tok)
700: if (NULL == n->prev->body->child)
701: break;
702: if (NULL == n->prev)
703: break;
704: term_vspace(p);
705: break;
706: case (MAN_HEAD):
1.21 schwarze 707: term_fontrepl(p, TERMFONT_BOLD);
1.42 schwarze 708: p->offset = term_len(p, HALFINDENT);
1.11 schwarze 709: break;
710: case (MAN_BODY):
1.13 schwarze 711: p->offset = mt->offset;
1.11 schwarze 712: break;
713: default:
714: break;
715: }
716:
1.1 kristaps 717: return(1);
718: }
719:
720:
721: /* ARGSUSED */
722: static void
723: post_SS(DECL_ARGS)
724: {
725:
1.11 schwarze 726: switch (n->type) {
727: case (MAN_HEAD):
728: term_newln(p);
729: break;
730: case (MAN_BODY):
731: term_newln(p);
732: break;
733: default:
734: break;
735: }
1.1 kristaps 736: }
737:
738:
739: /* ARGSUSED */
740: static int
741: pre_SH(DECL_ARGS)
742: {
743:
1.11 schwarze 744: switch (n->type) {
745: case (MAN_BLOCK):
1.69 schwarze 746: mt->fl &= ~MANT_LITERAL;
1.70 schwarze 747: mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
1.42 schwarze 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: /* ARGSUSED */
791: static int
792: pre_RS(DECL_ARGS)
793: {
1.69 schwarze 794: int ival;
795: size_t sz;
1.13 schwarze 796:
797: switch (n->type) {
798: case (MAN_BLOCK):
799: term_newln(p);
800: return(1);
801: case (MAN_HEAD):
802: return(0);
803: default:
804: break;
805: }
806:
1.69 schwarze 807: sz = term_len(p, INDENT);
1.13 schwarze 808:
1.69 schwarze 809: if (NULL != (n = n->parent->head->child))
810: if ((ival = a2width(p, n->string)) >= 0)
811: sz = (size_t)ival;
1.13 schwarze 812:
1.69 schwarze 813: mt->offset += sz;
1.13 schwarze 814: p->offset = mt->offset;
815:
1.70 schwarze 816: if (++mt->lmarginsz < MAXMARGINS)
817: mt->lmargincur = mt->lmarginsz;
818:
819: mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
1.13 schwarze 820: return(1);
821: }
822:
823: /* ARGSUSED */
824: static void
825: post_RS(DECL_ARGS)
826: {
1.69 schwarze 827: int ival;
828: size_t sz;
1.13 schwarze 829:
830: switch (n->type) {
831: case (MAN_BLOCK):
1.69 schwarze 832: return;
1.27 schwarze 833: case (MAN_HEAD):
1.69 schwarze 834: return;
1.13 schwarze 835: default:
836: term_newln(p);
1.11 schwarze 837: break;
838: }
1.69 schwarze 839:
840: sz = term_len(p, INDENT);
841:
842: if (NULL != (n = n->parent->head->child))
843: if ((ival = a2width(p, n->string)) >= 0)
844: sz = (size_t)ival;
845:
846: mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
847: p->offset = mt->offset;
1.70 schwarze 848:
849: if (--mt->lmarginsz < MAXMARGINS)
850: mt->lmargincur = mt->lmarginsz;
1.47 schwarze 851: }
852:
1.1 kristaps 853: static void
1.19 schwarze 854: print_man_node(DECL_ARGS)
1.1 kristaps 855: {
1.32 schwarze 856: size_t rm, rmax;
1.21 schwarze 857: int c;
1.1 kristaps 858:
859: switch (n->type) {
860: case(MAN_TEXT):
1.61 schwarze 861: /*
862: * If we have a blank line, output a vertical space.
863: * If we have a space as the first character, break
864: * before printing the line's data.
865: */
1.60 schwarze 866: if ('\0' == *n->string) {
1.1 kristaps 867: term_vspace(p);
1.61 schwarze 868: return;
869: } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60 schwarze 870: term_newln(p);
1.21 schwarze 871:
1.1 kristaps 872: term_word(p, n->string);
1.21 schwarze 873:
1.61 schwarze 874: /*
875: * If we're in a literal context, make sure that words
876: * togehter on the same line stay together. This is a
877: * POST-printing call, so we check the NEXT word. Since
878: * -man doesn't have nested macros, we don't need to be
879: * more specific than this.
880: */
881: if (MANT_LITERAL & mt->fl &&
882: (NULL == n->next ||
883: n->next->line > n->line)) {
1.32 schwarze 884: rm = p->rmargin;
885: rmax = p->maxrmargin;
1.29 schwarze 886: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1.11 schwarze 887: p->flags |= TERMP_NOSPACE;
888: term_flushln(p);
1.57 schwarze 889: p->flags &= ~TERMP_NOLPAD;
1.32 schwarze 890: p->rmargin = rm;
891: p->maxrmargin = rmax;
1.11 schwarze 892: }
1.63 schwarze 893:
894: if (MAN_EOS & n->flags)
895: p->flags |= TERMP_SENTENCE;
1.66 schwarze 896: return;
897: case (MAN_EQN):
1.71 ! schwarze 898: term_eqn(p, n->eqn);
1.61 schwarze 899: return;
1.58 schwarze 900: case (MAN_TBL):
1.61 schwarze 901: /*
902: * Tables are preceded by a newline. Then process a
903: * table line, which will cause line termination,
904: */
1.58 schwarze 905: if (TBL_SPAN_FIRST & n->span->flags)
906: term_newln(p);
907: term_tbl(p, n->span);
1.61 schwarze 908: return;
1.1 kristaps 909: default:
910: break;
911: }
912:
1.61 schwarze 913: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
914: term_fontrepl(p, TERMFONT_NONE);
915:
916: c = 1;
917: if (termacts[n->tok].pre)
918: c = (*termacts[n->tok].pre)(p, mt, n, m);
919:
1.1 kristaps 920: if (c && n->child)
1.21 schwarze 921: print_man_nodelist(p, mt, n->child, m);
1.1 kristaps 922:
1.61 schwarze 923: if (termacts[n->tok].post)
924: (*termacts[n->tok].post)(p, mt, n, m);
925: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
926: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 927:
928: if (MAN_EOS & n->flags)
929: p->flags |= TERMP_SENTENCE;
1.1 kristaps 930: }
931:
932:
933: static void
1.21 schwarze 934: print_man_nodelist(DECL_ARGS)
1.1 kristaps 935: {
1.11 schwarze 936:
1.19 schwarze 937: print_man_node(p, mt, n, m);
1.1 kristaps 938: if ( ! n->next)
939: return;
1.21 schwarze 940: print_man_nodelist(p, mt, n->next, m);
1.1 kristaps 941: }
942:
943:
944: static void
1.41 schwarze 945: print_man_foot(struct termp *p, const void *arg)
1.1 kristaps 946: {
1.41 schwarze 947: const struct man_meta *meta;
948:
949: meta = (const struct man_meta *)arg;
1.21 schwarze 950:
951: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 952:
1.38 schwarze 953: term_vspace(p);
954: term_vspace(p);
1.1 kristaps 955: term_vspace(p);
956:
957: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.65 schwarze 958: p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
1.1 kristaps 959: p->offset = 0;
1.46 schwarze 960:
961: /* term_strlen() can return zero. */
962: if (p->rmargin == p->maxrmargin)
963: p->rmargin--;
1.1 kristaps 964:
965: if (meta->source)
966: term_word(p, meta->source);
967: if (meta->source)
968: term_word(p, "");
969: term_flushln(p);
970:
971: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
972: p->offset = p->rmargin;
973: p->rmargin = p->maxrmargin;
974: p->flags &= ~TERMP_NOBREAK;
975:
1.65 schwarze 976: term_word(p, meta->date);
1.1 kristaps 977: term_flushln(p);
978: }
979:
980:
981: static void
1.41 schwarze 982: print_man_head(struct termp *p, const void *arg)
1.1 kristaps 983: {
1.20 schwarze 984: char buf[BUFSIZ], title[BUFSIZ];
1.25 schwarze 985: size_t buflen, titlen;
1.41 schwarze 986: const struct man_meta *m;
987:
988: m = (const struct man_meta *)arg;
1.1 kristaps 989:
1.29 schwarze 990: /*
991: * Note that old groff would spit out some spaces before the
992: * header. We discontinue this strange behaviour, but at one
993: * point we did so here.
994: */
995:
1.1 kristaps 996: p->rmargin = p->maxrmargin;
1.27 schwarze 997:
1.1 kristaps 998: p->offset = 0;
1.20 schwarze 999: buf[0] = title[0] = '\0';
1.1 kristaps 1000:
1.20 schwarze 1001: if (m->vol)
1002: strlcpy(buf, m->vol, BUFSIZ);
1.42 schwarze 1003: buflen = term_strlen(p, buf);
1.1 kristaps 1004:
1.31 schwarze 1005: snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1.42 schwarze 1006: titlen = term_strlen(p, title);
1.1 kristaps 1007:
1008: p->offset = 0;
1.25 schwarze 1009: p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1.42 schwarze 1010: (p->maxrmargin -
1011: term_strlen(p, buf) + term_len(p, 1)) / 2 :
1.25 schwarze 1012: p->maxrmargin - buflen;
1.1 kristaps 1013: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1014:
1015: term_word(p, title);
1016: term_flushln(p);
1017:
1018: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1019: p->offset = p->rmargin;
1.25 schwarze 1020: p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1021: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1022:
1023: term_word(p, buf);
1024: term_flushln(p);
1025:
1026: p->flags &= ~TERMP_NOBREAK;
1.25 schwarze 1027: if (p->rmargin + titlen <= p->maxrmargin) {
1028: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1029: p->offset = p->rmargin;
1030: p->rmargin = p->maxrmargin;
1031: term_word(p, title);
1032: term_flushln(p);
1033: }
1.1 kristaps 1034:
1035: p->rmargin = p->maxrmargin;
1036: p->offset = 0;
1037: p->flags &= ~TERMP_NOSPACE;
1.29 schwarze 1038:
1039: /*
1040: * Groff likes to have some leading spaces before content. Well
1041: * that's fine by me.
1042: */
1043:
1044: term_vspace(p);
1045: term_vspace(p);
1046: term_vspace(p);
1.1 kristaps 1047: }