Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.99
1.99 ! schwarze 1: /* $Id: man_term.c,v 1.98 2014/03/30 21:27:59 schwarze Exp $ */
1.1 kristaps 2: /*
1.84 schwarze 3: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.94 schwarze 4: * Copyright (c) 2010-2014 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 MAXMARGINS 64 /* maximum number of indented scopes */
1.1 kristaps 33:
1.11 schwarze 34: struct mtermp {
35: int fl;
36: #define MANT_LITERAL (1 << 0)
1.70 schwarze 37: size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */
38: int lmargincur; /* index of current margin */
39: int lmarginsz; /* actual number of nested margins */
40: size_t offset; /* default offset to visible page */
1.88 schwarze 41: int pardist; /* vert. space before par., unit: [v] */
1.11 schwarze 42: };
43:
1.1 kristaps 44: #define DECL_ARGS struct termp *p, \
1.11 schwarze 45: struct mtermp *mt, \
1.1 kristaps 46: const struct man_node *n, \
1.89 schwarze 47: const struct man_meta *meta
1.1 kristaps 48:
49: struct termact {
50: int (*pre)(DECL_ARGS);
51: void (*post)(DECL_ARGS);
1.26 schwarze 52: int flags;
53: #define MAN_NOTEXT (1 << 0) /* Never has text children. */
1.1 kristaps 54: };
55:
1.42 schwarze 56: static int a2width(const struct termp *, const char *);
57: static size_t a2height(const struct termp *, const char *);
1.18 schwarze 58:
1.21 schwarze 59: static void print_man_nodelist(DECL_ARGS);
1.19 schwarze 60: static void print_man_node(DECL_ARGS);
1.41 schwarze 61: static void print_man_head(struct termp *, const void *);
62: static void print_man_foot(struct termp *, const void *);
1.18 schwarze 63: static void print_bvspace(struct termp *,
1.88 schwarze 64: const struct man_node *, int);
1.18 schwarze 65:
1.1 kristaps 66: static int pre_B(DECL_ARGS);
1.11 schwarze 67: static int pre_HP(DECL_ARGS);
1.1 kristaps 68: static int pre_I(DECL_ARGS);
69: static int pre_IP(DECL_ARGS);
1.82 schwarze 70: static int pre_OP(DECL_ARGS);
1.88 schwarze 71: static int pre_PD(DECL_ARGS);
1.1 kristaps 72: static int pre_PP(DECL_ARGS);
1.13 schwarze 73: static int pre_RS(DECL_ARGS);
1.1 kristaps 74: static int pre_SH(DECL_ARGS);
75: static int pre_SS(DECL_ARGS);
76: static int pre_TP(DECL_ARGS);
1.91 schwarze 77: static int pre_UR(DECL_ARGS);
1.82 schwarze 78: static int pre_alternate(DECL_ARGS);
79: static int pre_ft(DECL_ARGS);
1.14 schwarze 80: static int pre_ign(DECL_ARGS);
1.45 schwarze 81: static int pre_in(DECL_ARGS);
82: static int pre_literal(DECL_ARGS);
1.97 schwarze 83: static int pre_ll(DECL_ARGS);
1.11 schwarze 84: static int pre_sp(DECL_ARGS);
1.1 kristaps 85:
1.11 schwarze 86: static void post_IP(DECL_ARGS);
87: static void post_HP(DECL_ARGS);
1.13 schwarze 88: static void post_RS(DECL_ARGS);
1.1 kristaps 89: static void post_SH(DECL_ARGS);
90: static void post_SS(DECL_ARGS);
1.11 schwarze 91: static void post_TP(DECL_ARGS);
1.91 schwarze 92: static void post_UR(DECL_ARGS);
1.1 kristaps 93:
1.17 schwarze 94: static const struct termact termacts[MAN_MAX] = {
1.45 schwarze 95: { pre_sp, NULL, MAN_NOTEXT }, /* br */
1.26 schwarze 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.51 schwarze 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.26 schwarze 111: { NULL, NULL, 0 }, /* R */
112: { pre_B, NULL, 0 }, /* B */
113: { pre_I, NULL, 0 }, /* I */
1.51 schwarze 114: { pre_alternate, NULL, 0 }, /* IR */
115: { pre_alternate, NULL, 0 }, /* RI */
1.62 schwarze 116: { pre_ign, NULL, MAN_NOTEXT }, /* na */
1.26 schwarze 117: { pre_sp, NULL, MAN_NOTEXT }, /* sp */
1.45 schwarze 118: { pre_literal, NULL, 0 }, /* nf */
119: { pre_literal, NULL, 0 }, /* fi */
1.26 schwarze 120: { NULL, NULL, 0 }, /* RE */
121: { pre_RS, post_RS, 0 }, /* RS */
122: { pre_ign, NULL, 0 }, /* DT */
123: { pre_ign, NULL, 0 }, /* UC */
1.88 schwarze 124: { pre_PD, NULL, MAN_NOTEXT }, /* PD */
1.36 schwarze 125: { pre_ign, NULL, 0 }, /* AT */
1.45 schwarze 126: { pre_in, NULL, MAN_NOTEXT }, /* in */
1.52 schwarze 127: { pre_ft, NULL, MAN_NOTEXT }, /* ft */
1.82 schwarze 128: { pre_OP, NULL, 0 }, /* OP */
1.83 schwarze 129: { pre_literal, NULL, 0 }, /* EX */
130: { pre_literal, NULL, 0 }, /* EE */
1.91 schwarze 131: { pre_UR, post_UR, 0 }, /* UR */
132: { NULL, NULL, 0 }, /* UE */
1.97 schwarze 133: { pre_ll, NULL, MAN_NOTEXT }, /* ll */
1.1 kristaps 134: };
1.11 schwarze 135:
1.1 kristaps 136:
137:
1.16 schwarze 138: void
1.18 schwarze 139: terminal_man(void *arg, const struct man *man)
1.1 kristaps 140: {
1.18 schwarze 141: struct termp *p;
142: const struct man_node *n;
1.89 schwarze 143: const struct man_meta *meta;
1.18 schwarze 144: struct mtermp mt;
145:
146: p = (struct termp *)arg;
147:
1.77 schwarze 148: if (0 == p->defindent)
149: p->defindent = 7;
150:
1.39 schwarze 151: p->overstep = 0;
1.32 schwarze 152: p->maxrmargin = p->defrmargin;
1.42 schwarze 153: p->tabwidth = term_len(p, 5);
1.27 schwarze 154:
1.18 schwarze 155: if (NULL == p->symtab)
1.68 schwarze 156: p->symtab = mchars_alloc();
1.18 schwarze 157:
158: n = man_node(man);
1.89 schwarze 159: meta = man_meta(man);
1.1 kristaps 160:
1.89 schwarze 161: term_begin(p, print_man_head, print_man_foot, meta);
1.1 kristaps 162: p->flags |= TERMP_NOSPACE;
1.11 schwarze 163:
1.70 schwarze 164: memset(&mt, 0, sizeof(struct mtermp));
165:
1.77 schwarze 166: mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
167: mt.offset = term_len(p, p->defindent);
1.88 schwarze 168: mt.pardist = 1;
1.11 schwarze 169:
1.18 schwarze 170: if (n->child)
1.89 schwarze 171: print_man_nodelist(p, &mt, n->child, meta);
1.41 schwarze 172:
173: term_end(p);
1.1 kristaps 174: }
175:
176:
1.42 schwarze 177: static size_t
178: a2height(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_VS))
1.69 schwarze 183: SCALE_VS_INIT(&su, atoi(cp));
1.11 schwarze 184:
1.42 schwarze 185: return(term_vspan(p, &su));
1.11 schwarze 186: }
187:
188:
189: static int
1.42 schwarze 190: a2width(const struct termp *p, const char *cp)
1.11 schwarze 191: {
1.18 schwarze 192: struct roffsu su;
1.11 schwarze 193:
1.42 schwarze 194: if ( ! a2roffsu(cp, &su, SCALE_BU))
1.18 schwarze 195: return(-1);
196:
1.42 schwarze 197: return((int)term_hspan(p, &su));
1.18 schwarze 198: }
1.11 schwarze 199:
1.69 schwarze 200: /*
201: * Printing leading vertical space before a block.
202: * This is used for the paragraph macros.
203: * The rules are pretty simple, since there's very little nesting going
204: * on here. Basically, if we're the first within another block (SS/SH),
205: * then don't emit vertical space. If we are (RS), then do. If not the
206: * first, print it.
207: */
1.18 schwarze 208: static void
1.88 schwarze 209: print_bvspace(struct termp *p, const struct man_node *n, int pardist)
1.18 schwarze 210: {
1.88 schwarze 211: int i;
1.69 schwarze 212:
1.18 schwarze 213: term_newln(p);
1.64 schwarze 214:
1.69 schwarze 215: if (n->body && n->body->child)
216: if (MAN_TBL == n->body->child->type)
217: return;
1.11 schwarze 218:
1.69 schwarze 219: if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
220: if (NULL == n->prev)
221: return;
1.11 schwarze 222:
1.88 schwarze 223: for (i = 0; i < pardist; i++)
224: term_vspace(p);
1.14 schwarze 225: }
226:
227: /* ARGSUSED */
228: static int
229: pre_ign(DECL_ARGS)
230: {
231:
1.97 schwarze 232: return(0);
233: }
234:
235:
236: /* ARGSUSED */
237: static int
238: pre_ll(DECL_ARGS)
239: {
240:
1.98 schwarze 241: term_setwidth(p, n->nchild ? n->child->string : NULL);
1.14 schwarze 242: return(0);
1.11 schwarze 243: }
244:
245:
1.1 kristaps 246: /* ARGSUSED */
247: static int
248: pre_I(DECL_ARGS)
249: {
250:
1.21 schwarze 251: term_fontrepl(p, TERMFONT_UNDER);
1.1 kristaps 252: return(1);
253: }
254:
255:
256: /* ARGSUSED */
1.11 schwarze 257: static int
1.45 schwarze 258: pre_literal(DECL_ARGS)
1.11 schwarze 259: {
260:
1.45 schwarze 261: term_newln(p);
1.53 schwarze 262:
1.83 schwarze 263: if (MAN_nf == n->tok || MAN_EX == n->tok)
1.45 schwarze 264: mt->fl |= MANT_LITERAL;
1.53 schwarze 265: else
1.45 schwarze 266: mt->fl &= ~MANT_LITERAL;
267:
1.72 schwarze 268: /*
269: * Unlike .IP and .TP, .HP does not have a HEAD.
270: * So in case a second call to term_flushln() is needed,
271: * indentation has to be set up explicitly.
272: */
273: if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
1.76 schwarze 274: p->offset = p->rmargin;
1.72 schwarze 275: p->rmargin = p->maxrmargin;
1.93 schwarze 276: p->trailspace = 0;
1.99 ! schwarze 277: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.72 schwarze 278: p->flags |= TERMP_NOSPACE;
279: }
280:
1.62 schwarze 281: return(0);
1.11 schwarze 282: }
283:
284: /* ARGSUSED */
285: static int
1.88 schwarze 286: pre_PD(DECL_ARGS)
287: {
288:
289: n = n->child;
290: if (0 == n) {
291: mt->pardist = 1;
292: return(0);
293: }
294: assert(MAN_TEXT == n->type);
295: mt->pardist = atoi(n->string);
296: return(0);
297: }
298:
299: /* ARGSUSED */
300: static int
1.51 schwarze 301: pre_alternate(DECL_ARGS)
1.1 kristaps 302: {
1.51 schwarze 303: enum termfont font[2];
304: const struct man_node *nn;
305: int savelit, i;
1.1 kristaps 306:
1.51 schwarze 307: switch (n->tok) {
308: case (MAN_RB):
309: font[0] = TERMFONT_NONE;
310: font[1] = TERMFONT_BOLD;
311: break;
312: case (MAN_RI):
313: font[0] = TERMFONT_NONE;
314: font[1] = TERMFONT_UNDER;
315: break;
316: case (MAN_BR):
317: font[0] = TERMFONT_BOLD;
318: font[1] = TERMFONT_NONE;
319: break;
320: case (MAN_BI):
321: font[0] = TERMFONT_BOLD;
322: font[1] = TERMFONT_UNDER;
323: break;
324: case (MAN_IR):
325: font[0] = TERMFONT_UNDER;
326: font[1] = TERMFONT_NONE;
327: break;
328: case (MAN_IB):
329: font[0] = TERMFONT_UNDER;
330: font[1] = TERMFONT_BOLD;
331: break;
332: default:
333: abort();
334: }
1.17 schwarze 335:
1.51 schwarze 336: savelit = MANT_LITERAL & mt->fl;
337: mt->fl &= ~MANT_LITERAL;
1.17 schwarze 338:
1.51 schwarze 339: for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
340: term_fontrepl(p, font[i]);
341: if (savelit && NULL == nn->next)
342: mt->fl |= MANT_LITERAL;
1.89 schwarze 343: print_man_node(p, mt, nn, meta);
1.51 schwarze 344: if (nn->next)
1.1 kristaps 345: p->flags |= TERMP_NOSPACE;
346: }
1.17 schwarze 347:
1.1 kristaps 348: return(0);
349: }
350:
351: /* ARGSUSED */
352: static int
353: pre_B(DECL_ARGS)
354: {
355:
1.21 schwarze 356: term_fontrepl(p, TERMFONT_BOLD);
1.1 kristaps 357: return(1);
1.82 schwarze 358: }
359:
360: /* ARGSUSED */
361: static int
362: pre_OP(DECL_ARGS)
363: {
364:
365: term_word(p, "[");
366: p->flags |= TERMP_NOSPACE;
367:
368: if (NULL != (n = n->child)) {
369: term_fontrepl(p, TERMFONT_BOLD);
370: term_word(p, n->string);
371: }
372: if (NULL != n && NULL != n->next) {
373: term_fontrepl(p, TERMFONT_UNDER);
374: term_word(p, n->next->string);
375: }
376:
377: term_fontrepl(p, TERMFONT_NONE);
378: p->flags |= TERMP_NOSPACE;
379: term_word(p, "]");
380: return(0);
1.52 schwarze 381: }
382:
383: /* ARGSUSED */
384: static int
385: pre_ft(DECL_ARGS)
386: {
387: const char *cp;
388:
389: if (NULL == n->child) {
390: term_fontlast(p);
391: return(0);
392: }
393:
394: cp = n->child->string;
395: switch (*cp) {
396: case ('4'):
397: /* FALLTHROUGH */
398: case ('3'):
399: /* FALLTHROUGH */
400: case ('B'):
401: term_fontrepl(p, TERMFONT_BOLD);
402: break;
403: case ('2'):
404: /* FALLTHROUGH */
405: case ('I'):
406: term_fontrepl(p, TERMFONT_UNDER);
407: break;
408: case ('P'):
409: term_fontlast(p);
410: break;
411: case ('1'):
412: /* FALLTHROUGH */
413: case ('C'):
414: /* FALLTHROUGH */
415: case ('R'):
416: term_fontrepl(p, TERMFONT_NONE);
417: break;
418: default:
419: break;
420: }
421: return(0);
1.1 kristaps 422: }
423:
424: /* ARGSUSED */
425: static int
1.45 schwarze 426: pre_in(DECL_ARGS)
1.11 schwarze 427: {
1.45 schwarze 428: int len, less;
429: size_t v;
430: const char *cp;
431:
432: term_newln(p);
433:
434: if (NULL == n->child) {
435: p->offset = mt->offset;
436: return(0);
437: }
1.11 schwarze 438:
1.45 schwarze 439: cp = n->child->string;
440: less = 0;
1.11 schwarze 441:
1.45 schwarze 442: if ('-' == *cp)
443: less = -1;
444: else if ('+' == *cp)
445: less = 1;
446: else
447: cp--;
448:
449: if ((len = a2width(p, ++cp)) < 0)
450: return(0);
451:
452: v = (size_t)len;
453:
454: if (less < 0)
455: p->offset -= p->offset > v ? v : p->offset;
456: else if (less > 0)
457: p->offset += v;
458: else
459: p->offset = v;
1.59 schwarze 460:
461: /* Don't let this creep beyond the right margin. */
462:
463: if (p->offset > p->rmargin)
464: p->offset = p->rmargin;
1.11 schwarze 465:
466: return(0);
467: }
468:
469:
470: /* ARGSUSED */
471: static int
1.45 schwarze 472: pre_sp(DECL_ARGS)
1.8 schwarze 473: {
1.85 schwarze 474: char *s;
1.45 schwarze 475: size_t i, len;
1.85 schwarze 476: int neg;
1.45 schwarze 477:
1.69 schwarze 478: if ((NULL == n->prev && n->parent)) {
1.86 schwarze 479: switch (n->parent->tok) {
480: case (MAN_SH):
481: /* FALLTHROUGH */
482: case (MAN_SS):
483: /* FALLTHROUGH */
484: case (MAN_PP):
485: /* FALLTHROUGH */
486: case (MAN_LP):
487: /* FALLTHROUGH */
488: case (MAN_P):
489: /* FALLTHROUGH */
1.69 schwarze 490: return(0);
1.86 schwarze 491: default:
492: break;
493: }
1.69 schwarze 494: }
495:
1.85 schwarze 496: neg = 0;
1.45 schwarze 497: switch (n->tok) {
498: case (MAN_br):
499: len = 0;
500: break;
501: default:
1.85 schwarze 502: if (NULL == n->child) {
503: len = 1;
504: break;
505: }
506: s = n->child->string;
507: if ('-' == *s) {
508: neg = 1;
509: s++;
510: }
511: len = a2height(p, s);
1.45 schwarze 512: break;
513: }
514:
515: if (0 == len)
516: term_newln(p);
1.85 schwarze 517: else if (neg)
518: p->skipvsp += len;
519: else
520: for (i = 0; i < len; i++)
521: term_vspace(p);
1.8 schwarze 522:
523: return(0);
524: }
525:
526:
527: /* ARGSUSED */
528: static int
1.11 schwarze 529: pre_HP(DECL_ARGS)
530: {
1.72 schwarze 531: size_t len, one;
1.11 schwarze 532: int ival;
533: const struct man_node *nn;
534:
535: switch (n->type) {
536: case (MAN_BLOCK):
1.88 schwarze 537: print_bvspace(p, n, mt->pardist);
1.11 schwarze 538: return(1);
539: case (MAN_BODY):
540: break;
541: default:
542: return(0);
543: }
544:
1.84 schwarze 545: if ( ! (MANT_LITERAL & mt->fl)) {
1.99 ! schwarze 546: p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.93 schwarze 547: p->trailspace = 2;
1.84 schwarze 548: }
549:
1.70 schwarze 550: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 551: ival = -1;
552:
553: /* Calculate offset. */
554:
555: if (NULL != (nn = n->parent->head->child))
1.42 schwarze 556: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 557: len = (size_t)ival;
558:
1.72 schwarze 559: one = term_len(p, 1);
1.76 schwarze 560: if (len < one)
1.72 schwarze 561: len = one;
1.11 schwarze 562:
1.13 schwarze 563: p->offset = mt->offset;
564: p->rmargin = mt->offset + len;
1.11 schwarze 565:
566: if (ival >= 0)
1.70 schwarze 567: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 568:
569: return(1);
570: }
571:
572:
573: /* ARGSUSED */
574: static void
575: post_HP(DECL_ARGS)
576: {
577:
578: switch (n->type) {
579: case (MAN_BODY):
1.90 schwarze 580: term_newln(p);
1.99 ! schwarze 581: p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.93 schwarze 582: p->trailspace = 0;
1.13 schwarze 583: p->offset = mt->offset;
1.11 schwarze 584: p->rmargin = p->maxrmargin;
585: break;
586: default:
587: break;
588: }
589: }
590:
591:
592: /* ARGSUSED */
593: static int
1.1 kristaps 594: pre_PP(DECL_ARGS)
595: {
596:
1.11 schwarze 597: switch (n->type) {
598: case (MAN_BLOCK):
1.77 schwarze 599: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.88 schwarze 600: print_bvspace(p, n, mt->pardist);
1.11 schwarze 601: break;
602: default:
1.13 schwarze 603: p->offset = mt->offset;
1.11 schwarze 604: break;
605: }
606:
1.54 schwarze 607: return(MAN_HEAD != n->type);
1.1 kristaps 608: }
609:
610:
611: /* ARGSUSED */
612: static int
613: pre_IP(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_BODY):
621: p->flags |= TERMP_NOSPACE;
622: break;
623: case (MAN_HEAD):
624: p->flags |= TERMP_NOBREAK;
1.93 schwarze 625: p->trailspace = 1;
1.11 schwarze 626: break;
627: case (MAN_BLOCK):
1.88 schwarze 628: print_bvspace(p, n, mt->pardist);
1.11 schwarze 629: /* FALLTHROUGH */
630: default:
631: return(1);
632: }
633:
1.70 schwarze 634: len = mt->lmargin[mt->lmargincur];
1.11 schwarze 635: ival = -1;
636:
1.57 schwarze 637: /* Calculate the offset from the optional second argument. */
1.11 schwarze 638: if (NULL != (nn = n->parent->head->child))
1.57 schwarze 639: if (NULL != (nn = nn->next))
1.42 schwarze 640: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 641: len = (size_t)ival;
642:
643: switch (n->type) {
644: case (MAN_HEAD):
645: /* Handle zero-width lengths. */
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: if (ival < 0)
652: break;
653:
654: /* Set the saved left-margin. */
1.70 schwarze 655: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.1 kristaps 656:
1.57 schwarze 657: savelit = MANT_LITERAL & mt->fl;
658: mt->fl &= ~MANT_LITERAL;
659:
660: if (n->child)
1.89 schwarze 661: print_man_node(p, mt, n->child, meta);
1.57 schwarze 662:
663: if (savelit)
664: mt->fl |= MANT_LITERAL;
665:
1.11 schwarze 666: return(0);
667: case (MAN_BODY):
1.13 schwarze 668: p->offset = mt->offset + len;
1.94 schwarze 669: p->rmargin = p->maxrmargin > p->offset ?
670: p->maxrmargin : p->offset;
1.11 schwarze 671: break;
672: default:
673: break;
674: }
1.1 kristaps 675:
1.11 schwarze 676: return(1);
677: }
1.1 kristaps 678:
679:
1.11 schwarze 680: /* ARGSUSED */
681: static void
682: post_IP(DECL_ARGS)
683: {
1.4 schwarze 684:
1.11 schwarze 685: switch (n->type) {
686: case (MAN_HEAD):
687: term_flushln(p);
688: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 689: p->trailspace = 0;
1.11 schwarze 690: p->rmargin = p->maxrmargin;
691: break;
692: case (MAN_BODY):
1.57 schwarze 693: term_newln(p);
1.92 schwarze 694: p->offset = mt->offset;
1.11 schwarze 695: break;
696: default:
697: break;
698: }
1.1 kristaps 699: }
700:
701:
702: /* ARGSUSED */
703: static int
704: pre_TP(DECL_ARGS)
705: {
1.11 schwarze 706: const struct man_node *nn;
707: size_t len;
1.57 schwarze 708: int savelit, ival;
1.11 schwarze 709:
710: switch (n->type) {
711: case (MAN_HEAD):
712: p->flags |= TERMP_NOBREAK;
1.93 schwarze 713: p->trailspace = 1;
1.11 schwarze 714: break;
715: case (MAN_BODY):
716: p->flags |= TERMP_NOSPACE;
717: break;
718: case (MAN_BLOCK):
1.88 schwarze 719: print_bvspace(p, n, mt->pardist);
1.11 schwarze 720: /* FALLTHROUGH */
721: default:
722: return(1);
723: }
724:
1.70 schwarze 725: len = (size_t)mt->lmargin[mt->lmargincur];
1.11 schwarze 726: ival = -1;
727:
728: /* Calculate offset. */
1.1 kristaps 729:
1.70 schwarze 730: if (NULL != (nn = n->parent->head->child))
1.95 schwarze 731: if (nn->string && 0 == (MAN_LINE & nn->flags))
1.42 schwarze 732: if ((ival = a2width(p, nn->string)) >= 0)
1.11 schwarze 733: len = (size_t)ival;
1.8 schwarze 734:
1.11 schwarze 735: switch (n->type) {
736: case (MAN_HEAD):
737: /* Handle zero-length properly. */
738: if (0 == len)
1.42 schwarze 739: len = term_len(p, 1);
1.11 schwarze 740:
1.13 schwarze 741: p->offset = mt->offset;
742: p->rmargin = mt->offset + len;
1.11 schwarze 743:
1.57 schwarze 744: savelit = MANT_LITERAL & mt->fl;
745: mt->fl &= ~MANT_LITERAL;
746:
1.11 schwarze 747: /* Don't print same-line elements. */
1.95 schwarze 748: nn = n->child;
749: while (NULL != nn && 0 == (MAN_LINE & nn->flags))
750: nn = nn->next;
751:
752: while (NULL != nn) {
753: print_man_node(p, mt, nn, meta);
754: nn = nn->next;
755: }
1.11 schwarze 756:
1.57 schwarze 757: if (savelit)
758: mt->fl |= MANT_LITERAL;
1.11 schwarze 759: if (ival >= 0)
1.70 schwarze 760: mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11 schwarze 761:
762: return(0);
763: case (MAN_BODY):
1.13 schwarze 764: p->offset = mt->offset + len;
1.94 schwarze 765: p->rmargin = p->maxrmargin > p->offset ?
766: p->maxrmargin : p->offset;
1.93 schwarze 767: p->trailspace = 0;
1.84 schwarze 768: p->flags &= ~TERMP_NOBREAK;
1.11 schwarze 769: break;
770: default:
771: break;
772: }
1.1 kristaps 773:
1.11 schwarze 774: return(1);
775: }
1.1 kristaps 776:
777:
1.11 schwarze 778: /* ARGSUSED */
779: static void
780: post_TP(DECL_ARGS)
781: {
1.1 kristaps 782:
1.11 schwarze 783: switch (n->type) {
784: case (MAN_HEAD):
785: term_flushln(p);
786: break;
787: case (MAN_BODY):
1.57 schwarze 788: term_newln(p);
1.92 schwarze 789: p->offset = mt->offset;
1.11 schwarze 790: break;
791: default:
792: break;
793: }
1.1 kristaps 794: }
795:
796:
797: /* ARGSUSED */
798: static int
799: pre_SS(DECL_ARGS)
800: {
1.88 schwarze 801: int i;
1.1 kristaps 802:
1.11 schwarze 803: switch (n->type) {
804: case (MAN_BLOCK):
1.69 schwarze 805: mt->fl &= ~MANT_LITERAL;
1.77 schwarze 806: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
807: mt->offset = term_len(p, p->defindent);
1.11 schwarze 808: /* If following a prior empty `SS', no vspace. */
809: if (n->prev && MAN_SS == n->prev->tok)
810: if (NULL == n->prev->body->child)
811: break;
812: if (NULL == n->prev)
813: break;
1.88 schwarze 814: for (i = 0; i < mt->pardist; i++)
815: term_vspace(p);
1.11 schwarze 816: break;
817: case (MAN_HEAD):
1.21 schwarze 818: term_fontrepl(p, TERMFONT_BOLD);
1.87 schwarze 819: p->offset = term_len(p, 3);
1.11 schwarze 820: break;
821: case (MAN_BODY):
1.13 schwarze 822: p->offset = mt->offset;
1.11 schwarze 823: break;
824: default:
825: break;
826: }
827:
1.1 kristaps 828: return(1);
829: }
830:
831:
832: /* ARGSUSED */
833: static void
834: post_SS(DECL_ARGS)
835: {
836:
1.11 schwarze 837: switch (n->type) {
838: case (MAN_HEAD):
839: term_newln(p);
840: break;
841: case (MAN_BODY):
842: term_newln(p);
843: break;
844: default:
845: break;
846: }
1.1 kristaps 847: }
848:
849:
850: /* ARGSUSED */
851: static int
852: pre_SH(DECL_ARGS)
853: {
1.88 schwarze 854: int i;
1.1 kristaps 855:
1.11 schwarze 856: switch (n->type) {
857: case (MAN_BLOCK):
1.69 schwarze 858: mt->fl &= ~MANT_LITERAL;
1.77 schwarze 859: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
860: mt->offset = term_len(p, p->defindent);
1.11 schwarze 861: /* If following a prior empty `SH', no vspace. */
862: if (n->prev && MAN_SH == n->prev->tok)
863: if (NULL == n->prev->body->child)
864: break;
1.29 schwarze 865: /* If the first macro, no vspae. */
866: if (NULL == n->prev)
867: break;
1.88 schwarze 868: for (i = 0; i < mt->pardist; i++)
869: term_vspace(p);
1.11 schwarze 870: break;
871: case (MAN_HEAD):
1.21 schwarze 872: term_fontrepl(p, TERMFONT_BOLD);
1.11 schwarze 873: p->offset = 0;
874: break;
875: case (MAN_BODY):
1.13 schwarze 876: p->offset = mt->offset;
1.11 schwarze 877: break;
878: default:
879: break;
880: }
881:
1.1 kristaps 882: return(1);
883: }
884:
885:
886: /* ARGSUSED */
887: static void
888: post_SH(DECL_ARGS)
889: {
890:
1.11 schwarze 891: switch (n->type) {
892: case (MAN_HEAD):
893: term_newln(p);
894: break;
895: case (MAN_BODY):
896: term_newln(p);
897: break;
898: default:
1.13 schwarze 899: break;
900: }
901: }
902:
903: /* ARGSUSED */
904: static int
905: pre_RS(DECL_ARGS)
906: {
1.69 schwarze 907: int ival;
908: size_t sz;
1.13 schwarze 909:
910: switch (n->type) {
911: case (MAN_BLOCK):
912: term_newln(p);
913: return(1);
914: case (MAN_HEAD):
915: return(0);
916: default:
917: break;
918: }
919:
1.77 schwarze 920: sz = term_len(p, p->defindent);
1.13 schwarze 921:
1.69 schwarze 922: if (NULL != (n = n->parent->head->child))
923: if ((ival = a2width(p, n->string)) >= 0)
924: sz = (size_t)ival;
1.13 schwarze 925:
1.69 schwarze 926: mt->offset += sz;
1.94 schwarze 927: p->offset = mt->offset;
928: p->rmargin = p->maxrmargin > p->offset ?
929: p->maxrmargin : p->offset;
1.13 schwarze 930:
1.70 schwarze 931: if (++mt->lmarginsz < MAXMARGINS)
932: mt->lmargincur = mt->lmarginsz;
933:
934: mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
1.13 schwarze 935: return(1);
936: }
937:
938: /* ARGSUSED */
939: static void
940: post_RS(DECL_ARGS)
941: {
1.69 schwarze 942: int ival;
943: size_t sz;
1.13 schwarze 944:
945: switch (n->type) {
946: case (MAN_BLOCK):
1.69 schwarze 947: return;
1.27 schwarze 948: case (MAN_HEAD):
1.69 schwarze 949: return;
1.13 schwarze 950: default:
951: term_newln(p);
1.11 schwarze 952: break;
953: }
1.69 schwarze 954:
1.77 schwarze 955: sz = term_len(p, p->defindent);
1.69 schwarze 956:
957: if (NULL != (n = n->parent->head->child))
958: if ((ival = a2width(p, n->string)) >= 0)
959: sz = (size_t)ival;
960:
961: mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
962: p->offset = mt->offset;
1.70 schwarze 963:
964: if (--mt->lmarginsz < MAXMARGINS)
965: mt->lmargincur = mt->lmarginsz;
1.91 schwarze 966: }
967:
968: /* ARGSUSED */
969: static int
970: pre_UR(DECL_ARGS)
971: {
972:
973: return (MAN_HEAD != n->type);
974: }
975:
976: /* ARGSUSED */
977: static void
978: post_UR(DECL_ARGS)
979: {
980:
981: if (MAN_BLOCK != n->type)
982: return;
983:
984: term_word(p, "<");
985: p->flags |= TERMP_NOSPACE;
986:
987: if (NULL != n->child->child)
988: print_man_node(p, mt, n->child->child, meta);
989:
990: p->flags |= TERMP_NOSPACE;
991: term_word(p, ">");
1.47 schwarze 992: }
993:
1.1 kristaps 994: static void
1.19 schwarze 995: print_man_node(DECL_ARGS)
1.1 kristaps 996: {
1.32 schwarze 997: size_t rm, rmax;
1.21 schwarze 998: int c;
1.1 kristaps 999:
1000: switch (n->type) {
1001: case(MAN_TEXT):
1.61 schwarze 1002: /*
1003: * If we have a blank line, output a vertical space.
1004: * If we have a space as the first character, break
1005: * before printing the line's data.
1006: */
1.60 schwarze 1007: if ('\0' == *n->string) {
1.1 kristaps 1008: term_vspace(p);
1.61 schwarze 1009: return;
1010: } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60 schwarze 1011: term_newln(p);
1.21 schwarze 1012:
1.1 kristaps 1013: term_word(p, n->string);
1.84 schwarze 1014: goto out;
1.21 schwarze 1015:
1.66 schwarze 1016: case (MAN_EQN):
1.71 schwarze 1017: term_eqn(p, n->eqn);
1.61 schwarze 1018: return;
1.58 schwarze 1019: case (MAN_TBL):
1.61 schwarze 1020: /*
1021: * Tables are preceded by a newline. Then process a
1022: * table line, which will cause line termination,
1023: */
1.58 schwarze 1024: if (TBL_SPAN_FIRST & n->span->flags)
1025: term_newln(p);
1026: term_tbl(p, n->span);
1.61 schwarze 1027: return;
1.1 kristaps 1028: default:
1029: break;
1030: }
1031:
1.61 schwarze 1032: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1033: term_fontrepl(p, TERMFONT_NONE);
1034:
1035: c = 1;
1036: if (termacts[n->tok].pre)
1.89 schwarze 1037: c = (*termacts[n->tok].pre)(p, mt, n, meta);
1.61 schwarze 1038:
1.1 kristaps 1039: if (c && n->child)
1.89 schwarze 1040: print_man_nodelist(p, mt, n->child, meta);
1.1 kristaps 1041:
1.61 schwarze 1042: if (termacts[n->tok].post)
1.89 schwarze 1043: (*termacts[n->tok].post)(p, mt, n, meta);
1.61 schwarze 1044: if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1045: term_fontrepl(p, TERMFONT_NONE);
1.30 schwarze 1046:
1.84 schwarze 1047: out:
1048: /*
1049: * If we're in a literal context, make sure that words
1050: * together on the same line stay together. This is a
1051: * POST-printing call, so we check the NEXT word. Since
1052: * -man doesn't have nested macros, we don't need to be
1053: * more specific than this.
1054: */
1055: if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1.96 schwarze 1056: (NULL == n->next || MAN_LINE & n->next->flags)) {
1.84 schwarze 1057: rm = p->rmargin;
1058: rmax = p->maxrmargin;
1059: p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1060: p->flags |= TERMP_NOSPACE;
1061: if (NULL != n->string && '\0' != *n->string)
1062: term_flushln(p);
1063: else
1064: term_newln(p);
1065: if (rm < rmax && n->parent->tok == MAN_HP) {
1066: p->offset = rm;
1067: p->rmargin = rmax;
1068: } else
1069: p->rmargin = rm;
1070: p->maxrmargin = rmax;
1071: }
1.30 schwarze 1072: if (MAN_EOS & n->flags)
1073: p->flags |= TERMP_SENTENCE;
1.1 kristaps 1074: }
1075:
1076:
1077: static void
1.21 schwarze 1078: print_man_nodelist(DECL_ARGS)
1.1 kristaps 1079: {
1.11 schwarze 1080:
1.89 schwarze 1081: print_man_node(p, mt, n, meta);
1.1 kristaps 1082: if ( ! n->next)
1083: return;
1.89 schwarze 1084: print_man_nodelist(p, mt, n->next, meta);
1.1 kristaps 1085: }
1086:
1087:
1088: static void
1.41 schwarze 1089: print_man_foot(struct termp *p, const void *arg)
1.1 kristaps 1090: {
1.78 schwarze 1091: char title[BUFSIZ];
1092: size_t datelen;
1.41 schwarze 1093: const struct man_meta *meta;
1094:
1095: meta = (const struct man_meta *)arg;
1.80 schwarze 1096: assert(meta->title);
1097: assert(meta->msec);
1098: assert(meta->date);
1.21 schwarze 1099:
1100: term_fontrepl(p, TERMFONT_NONE);
1.1 kristaps 1101:
1.38 schwarze 1102: term_vspace(p);
1.81 schwarze 1103:
1104: /*
1105: * Temporary, undocumented option to imitate mdoc(7) output.
1106: * In the bottom right corner, use the source instead of
1107: * the title.
1108: */
1109:
1.79 schwarze 1110: if ( ! p->mdocstyle) {
1111: term_vspace(p);
1112: term_vspace(p);
1113: snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1114: } else if (meta->source) {
1115: strlcpy(title, meta->source, BUFSIZ);
1116: } else {
1117: title[0] = '\0';
1118: }
1.78 schwarze 1119: datelen = term_strlen(p, meta->date);
1.1 kristaps 1120:
1.81 schwarze 1121: /* Bottom left corner: manual source. */
1122:
1.1 kristaps 1123: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.93 schwarze 1124: p->trailspace = 1;
1.1 kristaps 1125: p->offset = 0;
1.78 schwarze 1126: p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1.1 kristaps 1127:
1128: if (meta->source)
1129: term_word(p, meta->source);
1130: term_flushln(p);
1131:
1.81 schwarze 1132: /* At the bottom in the middle: manual date. */
1133:
1.72 schwarze 1134: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1135: p->offset = p->rmargin;
1.78 schwarze 1136: p->rmargin = p->maxrmargin - term_strlen(p, title);
1137: if (p->offset + datelen >= p->rmargin)
1138: p->rmargin = p->offset + datelen;
1139:
1140: term_word(p, meta->date);
1141: term_flushln(p);
1142:
1.81 schwarze 1143: /* Bottom right corner: manual title and section. */
1144:
1.78 schwarze 1145: p->flags &= ~TERMP_NOBREAK;
1146: p->flags |= TERMP_NOSPACE;
1.93 schwarze 1147: p->trailspace = 0;
1.78 schwarze 1148: p->offset = p->rmargin;
1.1 kristaps 1149: p->rmargin = p->maxrmargin;
1150:
1.78 schwarze 1151: term_word(p, title);
1.1 kristaps 1152: term_flushln(p);
1153: }
1154:
1155:
1156: static void
1.41 schwarze 1157: print_man_head(struct termp *p, const void *arg)
1.1 kristaps 1158: {
1.20 schwarze 1159: char buf[BUFSIZ], title[BUFSIZ];
1.25 schwarze 1160: size_t buflen, titlen;
1.89 schwarze 1161: const struct man_meta *meta;
1.41 schwarze 1162:
1.89 schwarze 1163: meta = (const struct man_meta *)arg;
1164: assert(meta->title);
1165: assert(meta->msec);
1.1 kristaps 1166:
1.89 schwarze 1167: if (meta->vol)
1168: strlcpy(buf, meta->vol, BUFSIZ);
1.81 schwarze 1169: else
1170: buf[0] = '\0';
1.42 schwarze 1171: buflen = term_strlen(p, buf);
1.1 kristaps 1172:
1.81 schwarze 1173: /* Top left corner: manual title and section. */
1174:
1.89 schwarze 1175: snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1.42 schwarze 1176: titlen = term_strlen(p, title);
1.1 kristaps 1177:
1.73 schwarze 1178: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.93 schwarze 1179: p->trailspace = 1;
1.1 kristaps 1180: p->offset = 0;
1.25 schwarze 1181: p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1.42 schwarze 1182: (p->maxrmargin -
1183: term_strlen(p, buf) + term_len(p, 1)) / 2 :
1.25 schwarze 1184: p->maxrmargin - buflen;
1.1 kristaps 1185:
1186: term_word(p, title);
1187: term_flushln(p);
1188:
1.81 schwarze 1189: /* At the top in the middle: manual volume. */
1190:
1.72 schwarze 1191: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1192: p->offset = p->rmargin;
1.25 schwarze 1193: p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1194: p->maxrmargin - titlen : p->maxrmargin;
1.1 kristaps 1195:
1196: term_word(p, buf);
1197: term_flushln(p);
1198:
1.81 schwarze 1199: /* Top right corner: title and section, again. */
1200:
1.1 kristaps 1201: p->flags &= ~TERMP_NOBREAK;
1.93 schwarze 1202: p->trailspace = 0;
1.25 schwarze 1203: if (p->rmargin + titlen <= p->maxrmargin) {
1.72 schwarze 1204: p->flags |= TERMP_NOSPACE;
1.25 schwarze 1205: p->offset = p->rmargin;
1206: p->rmargin = p->maxrmargin;
1207: term_word(p, title);
1208: term_flushln(p);
1209: }
1.1 kristaps 1210:
1.73 schwarze 1211: p->flags &= ~TERMP_NOSPACE;
1212: p->offset = 0;
1.1 kristaps 1213: p->rmargin = p->maxrmargin;
1.29 schwarze 1214:
1215: /*
1.81 schwarze 1216: * Groff prints three blank lines before the content.
1217: * Do the same, except in the temporary, undocumented
1218: * mode imitating mdoc(7) output.
1.29 schwarze 1219: */
1220:
1221: term_vspace(p);
1.79 schwarze 1222: if ( ! p->mdocstyle) {
1223: term_vspace(p);
1224: term_vspace(p);
1225: }
1.1 kristaps 1226: }