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