]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Vastly simplify man(7) block unwinding, similar to mdoc_macro.c 1.171.
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.173 2015/04/02 23:48:19 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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.
17 */
18 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "mandoc_aux.h"
30 #include "mandoc.h"
31 #include "roff.h"
32 #include "man.h"
33 #include "out.h"
34 #include "term.h"
35 #include "main.h"
36
37 #define MAXMARGINS 64 /* maximum number of indented scopes */
38
39 struct mtermp {
40 int fl;
41 #define MANT_LITERAL (1 << 0)
42 int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
43 int lmargincur; /* index of current margin */
44 int lmarginsz; /* actual number of nested margins */
45 size_t offset; /* default offset to visible page */
46 int pardist; /* vert. space before par., unit: [v] */
47 };
48
49 #define DECL_ARGS struct termp *p, \
50 struct mtermp *mt, \
51 struct roff_node *n, \
52 const struct roff_meta *meta
53
54 struct termact {
55 int (*pre)(DECL_ARGS);
56 void (*post)(DECL_ARGS);
57 int flags;
58 #define MAN_NOTEXT (1 << 0) /* Never has text children. */
59 };
60
61 static void print_man_nodelist(DECL_ARGS);
62 static void print_man_node(DECL_ARGS);
63 static void print_man_head(struct termp *,
64 const struct roff_meta *);
65 static void print_man_foot(struct termp *,
66 const struct roff_meta *);
67 static void print_bvspace(struct termp *,
68 const struct roff_node *, int);
69
70 static int pre_B(DECL_ARGS);
71 static int pre_HP(DECL_ARGS);
72 static int pre_I(DECL_ARGS);
73 static int pre_IP(DECL_ARGS);
74 static int pre_OP(DECL_ARGS);
75 static int pre_PD(DECL_ARGS);
76 static int pre_PP(DECL_ARGS);
77 static int pre_RS(DECL_ARGS);
78 static int pre_SH(DECL_ARGS);
79 static int pre_SS(DECL_ARGS);
80 static int pre_TP(DECL_ARGS);
81 static int pre_UR(DECL_ARGS);
82 static int pre_alternate(DECL_ARGS);
83 static int pre_ft(DECL_ARGS);
84 static int pre_ign(DECL_ARGS);
85 static int pre_in(DECL_ARGS);
86 static int pre_literal(DECL_ARGS);
87 static int pre_ll(DECL_ARGS);
88 static int pre_sp(DECL_ARGS);
89
90 static void post_IP(DECL_ARGS);
91 static void post_HP(DECL_ARGS);
92 static void post_RS(DECL_ARGS);
93 static void post_SH(DECL_ARGS);
94 static void post_SS(DECL_ARGS);
95 static void post_TP(DECL_ARGS);
96 static void post_UR(DECL_ARGS);
97
98 static const struct termact termacts[MAN_MAX] = {
99 { pre_sp, NULL, MAN_NOTEXT }, /* br */
100 { NULL, NULL, 0 }, /* TH */
101 { pre_SH, post_SH, 0 }, /* SH */
102 { pre_SS, post_SS, 0 }, /* SS */
103 { pre_TP, post_TP, 0 }, /* TP */
104 { pre_PP, NULL, 0 }, /* LP */
105 { pre_PP, NULL, 0 }, /* PP */
106 { pre_PP, NULL, 0 }, /* P */
107 { pre_IP, post_IP, 0 }, /* IP */
108 { pre_HP, post_HP, 0 }, /* HP */
109 { NULL, NULL, 0 }, /* SM */
110 { pre_B, NULL, 0 }, /* SB */
111 { pre_alternate, NULL, 0 }, /* BI */
112 { pre_alternate, NULL, 0 }, /* IB */
113 { pre_alternate, NULL, 0 }, /* BR */
114 { pre_alternate, NULL, 0 }, /* RB */
115 { NULL, NULL, 0 }, /* R */
116 { pre_B, NULL, 0 }, /* B */
117 { pre_I, NULL, 0 }, /* I */
118 { pre_alternate, NULL, 0 }, /* IR */
119 { pre_alternate, NULL, 0 }, /* RI */
120 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
121 { pre_literal, NULL, 0 }, /* nf */
122 { pre_literal, NULL, 0 }, /* fi */
123 { NULL, NULL, 0 }, /* RE */
124 { pre_RS, post_RS, 0 }, /* RS */
125 { pre_ign, NULL, 0 }, /* DT */
126 { pre_ign, NULL, MAN_NOTEXT }, /* UC */
127 { pre_PD, NULL, MAN_NOTEXT }, /* PD */
128 { pre_ign, NULL, 0 }, /* AT */
129 { pre_in, NULL, MAN_NOTEXT }, /* in */
130 { pre_ft, NULL, MAN_NOTEXT }, /* ft */
131 { pre_OP, NULL, 0 }, /* OP */
132 { pre_literal, NULL, 0 }, /* EX */
133 { pre_literal, NULL, 0 }, /* EE */
134 { pre_UR, post_UR, 0 }, /* UR */
135 { NULL, NULL, 0 }, /* UE */
136 { pre_ll, NULL, MAN_NOTEXT }, /* ll */
137 };
138
139
140 void
141 terminal_man(void *arg, const struct man *man)
142 {
143 struct termp *p;
144 const struct roff_meta *meta;
145 struct roff_node *n;
146 struct mtermp mt;
147
148 p = (struct termp *)arg;
149
150 p->overstep = 0;
151 p->rmargin = p->maxrmargin = p->defrmargin;
152 p->tabwidth = term_len(p, 5);
153
154 n = man_node(man)->child;
155 meta = man_meta(man);
156
157 memset(&mt, 0, sizeof(struct mtermp));
158
159 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
160 mt.offset = term_len(p, p->defindent);
161 mt.pardist = 1;
162
163 if (p->synopsisonly) {
164 while (n != NULL) {
165 if (n->tok == MAN_SH &&
166 n->child->child->type == ROFFT_TEXT &&
167 !strcmp(n->child->child->string, "SYNOPSIS")) {
168 if (n->child->next->child != NULL)
169 print_man_nodelist(p, &mt,
170 n->child->next->child, meta);
171 term_newln(p);
172 break;
173 }
174 n = n->next;
175 }
176 } else {
177 if (p->defindent == 0)
178 p->defindent = 7;
179 term_begin(p, print_man_head, print_man_foot, meta);
180 p->flags |= TERMP_NOSPACE;
181 if (n != NULL)
182 print_man_nodelist(p, &mt, n, meta);
183 term_end(p);
184 }
185 }
186
187 /*
188 * Printing leading vertical space before a block.
189 * This is used for the paragraph macros.
190 * The rules are pretty simple, since there's very little nesting going
191 * on here. Basically, if we're the first within another block (SS/SH),
192 * then don't emit vertical space. If we are (RS), then do. If not the
193 * first, print it.
194 */
195 static void
196 print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
197 {
198 int i;
199
200 term_newln(p);
201
202 if (n->body && n->body->child)
203 if (n->body->child->type == ROFFT_TBL)
204 return;
205
206 if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
207 if (NULL == n->prev)
208 return;
209
210 for (i = 0; i < pardist; i++)
211 term_vspace(p);
212 }
213
214
215 static int
216 pre_ign(DECL_ARGS)
217 {
218
219 return(0);
220 }
221
222 static int
223 pre_ll(DECL_ARGS)
224 {
225
226 term_setwidth(p, n->nchild ? n->child->string : NULL);
227 return(0);
228 }
229
230 static int
231 pre_I(DECL_ARGS)
232 {
233
234 term_fontrepl(p, TERMFONT_UNDER);
235 return(1);
236 }
237
238 static int
239 pre_literal(DECL_ARGS)
240 {
241
242 term_newln(p);
243
244 if (MAN_nf == n->tok || MAN_EX == n->tok)
245 mt->fl |= MANT_LITERAL;
246 else
247 mt->fl &= ~MANT_LITERAL;
248
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) {
255 p->offset = p->rmargin;
256 p->rmargin = p->maxrmargin;
257 p->trailspace = 0;
258 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
259 p->flags |= TERMP_NOSPACE;
260 }
261
262 return(0);
263 }
264
265 static int
266 pre_PD(DECL_ARGS)
267 {
268 struct roffsu su;
269
270 n = n->child;
271 if (n == NULL) {
272 mt->pardist = 1;
273 return(0);
274 }
275 assert(n->type == ROFFT_TEXT);
276 if (a2roffsu(n->string, &su, SCALE_VS))
277 mt->pardist = term_vspan(p, &su);
278 return(0);
279 }
280
281 static int
282 pre_alternate(DECL_ARGS)
283 {
284 enum termfont font[2];
285 struct roff_node *nn;
286 int savelit, i;
287
288 switch (n->tok) {
289 case MAN_RB:
290 font[0] = TERMFONT_NONE;
291 font[1] = TERMFONT_BOLD;
292 break;
293 case MAN_RI:
294 font[0] = TERMFONT_NONE;
295 font[1] = TERMFONT_UNDER;
296 break;
297 case MAN_BR:
298 font[0] = TERMFONT_BOLD;
299 font[1] = TERMFONT_NONE;
300 break;
301 case MAN_BI:
302 font[0] = TERMFONT_BOLD;
303 font[1] = TERMFONT_UNDER;
304 break;
305 case MAN_IR:
306 font[0] = TERMFONT_UNDER;
307 font[1] = TERMFONT_NONE;
308 break;
309 case MAN_IB:
310 font[0] = TERMFONT_UNDER;
311 font[1] = TERMFONT_BOLD;
312 break;
313 default:
314 abort();
315 }
316
317 savelit = MANT_LITERAL & mt->fl;
318 mt->fl &= ~MANT_LITERAL;
319
320 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
321 term_fontrepl(p, font[i]);
322 if (savelit && NULL == nn->next)
323 mt->fl |= MANT_LITERAL;
324 print_man_node(p, mt, nn, meta);
325 if (nn->next)
326 p->flags |= TERMP_NOSPACE;
327 }
328
329 return(0);
330 }
331
332 static int
333 pre_B(DECL_ARGS)
334 {
335
336 term_fontrepl(p, TERMFONT_BOLD);
337 return(1);
338 }
339
340 static int
341 pre_OP(DECL_ARGS)
342 {
343
344 term_word(p, "[");
345 p->flags |= TERMP_NOSPACE;
346
347 if (NULL != (n = n->child)) {
348 term_fontrepl(p, TERMFONT_BOLD);
349 term_word(p, n->string);
350 }
351 if (NULL != n && NULL != n->next) {
352 term_fontrepl(p, TERMFONT_UNDER);
353 term_word(p, n->next->string);
354 }
355
356 term_fontrepl(p, TERMFONT_NONE);
357 p->flags |= TERMP_NOSPACE;
358 term_word(p, "]");
359 return(0);
360 }
361
362 static int
363 pre_ft(DECL_ARGS)
364 {
365 const char *cp;
366
367 if (NULL == n->child) {
368 term_fontlast(p);
369 return(0);
370 }
371
372 cp = n->child->string;
373 switch (*cp) {
374 case '4':
375 /* FALLTHROUGH */
376 case '3':
377 /* FALLTHROUGH */
378 case 'B':
379 term_fontrepl(p, TERMFONT_BOLD);
380 break;
381 case '2':
382 /* FALLTHROUGH */
383 case 'I':
384 term_fontrepl(p, TERMFONT_UNDER);
385 break;
386 case 'P':
387 term_fontlast(p);
388 break;
389 case '1':
390 /* FALLTHROUGH */
391 case 'C':
392 /* FALLTHROUGH */
393 case 'R':
394 term_fontrepl(p, TERMFONT_NONE);
395 break;
396 default:
397 break;
398 }
399 return(0);
400 }
401
402 static int
403 pre_in(DECL_ARGS)
404 {
405 struct roffsu su;
406 const char *cp;
407 size_t v;
408 int less;
409
410 term_newln(p);
411
412 if (NULL == n->child) {
413 p->offset = mt->offset;
414 return(0);
415 }
416
417 cp = n->child->string;
418 less = 0;
419
420 if ('-' == *cp)
421 less = -1;
422 else if ('+' == *cp)
423 less = 1;
424 else
425 cp--;
426
427 if ( ! a2roffsu(++cp, &su, SCALE_EN))
428 return(0);
429
430 v = term_hspan(p, &su);
431
432 if (less < 0)
433 p->offset -= p->offset > v ? v : p->offset;
434 else if (less > 0)
435 p->offset += v;
436 else
437 p->offset = v;
438 if (p->offset > SHRT_MAX)
439 p->offset = term_len(p, p->defindent);
440
441 return(0);
442 }
443
444 static int
445 pre_sp(DECL_ARGS)
446 {
447 struct roffsu su;
448 int i, len;
449
450 if ((NULL == n->prev && n->parent)) {
451 switch (n->parent->tok) {
452 case MAN_SH:
453 /* FALLTHROUGH */
454 case MAN_SS:
455 /* FALLTHROUGH */
456 case MAN_PP:
457 /* FALLTHROUGH */
458 case MAN_LP:
459 /* FALLTHROUGH */
460 case MAN_P:
461 /* FALLTHROUGH */
462 return(0);
463 default:
464 break;
465 }
466 }
467
468 if (n->tok == MAN_br)
469 len = 0;
470 else if (n->child == NULL)
471 len = 1;
472 else {
473 if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
474 su.scale = 1.0;
475 len = term_vspan(p, &su);
476 }
477
478 if (len == 0)
479 term_newln(p);
480 else if (len < 0)
481 p->skipvsp -= len;
482 else
483 for (i = 0; i < len; i++)
484 term_vspace(p);
485
486 return(0);
487 }
488
489 static int
490 pre_HP(DECL_ARGS)
491 {
492 struct roffsu su;
493 const struct roff_node *nn;
494 int len;
495
496 switch (n->type) {
497 case ROFFT_BLOCK:
498 print_bvspace(p, n, mt->pardist);
499 return(1);
500 case ROFFT_BODY:
501 break;
502 default:
503 return(0);
504 }
505
506 if ( ! (MANT_LITERAL & mt->fl)) {
507 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
508 p->trailspace = 2;
509 }
510
511 /* Calculate offset. */
512
513 if ((nn = n->parent->head->child) != NULL &&
514 a2roffsu(nn->string, &su, SCALE_EN)) {
515 len = term_hspan(p, &su);
516 if (len < 0 && (size_t)(-len) > mt->offset)
517 len = -mt->offset;
518 else if (len > SHRT_MAX)
519 len = term_len(p, p->defindent);
520 mt->lmargin[mt->lmargincur] = len;
521 } else
522 len = mt->lmargin[mt->lmargincur];
523
524 p->offset = mt->offset;
525 p->rmargin = mt->offset + len;
526 return(1);
527 }
528
529 static void
530 post_HP(DECL_ARGS)
531 {
532
533 switch (n->type) {
534 case ROFFT_BODY:
535 term_newln(p);
536 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
537 p->trailspace = 0;
538 p->offset = mt->offset;
539 p->rmargin = p->maxrmargin;
540 break;
541 default:
542 break;
543 }
544 }
545
546 static int
547 pre_PP(DECL_ARGS)
548 {
549
550 switch (n->type) {
551 case ROFFT_BLOCK:
552 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
553 print_bvspace(p, n, mt->pardist);
554 break;
555 default:
556 p->offset = mt->offset;
557 break;
558 }
559
560 return(n->type != ROFFT_HEAD);
561 }
562
563 static int
564 pre_IP(DECL_ARGS)
565 {
566 struct roffsu su;
567 const struct roff_node *nn;
568 int len, savelit;
569
570 switch (n->type) {
571 case ROFFT_BODY:
572 p->flags |= TERMP_NOSPACE;
573 break;
574 case ROFFT_HEAD:
575 p->flags |= TERMP_NOBREAK;
576 p->trailspace = 1;
577 break;
578 case ROFFT_BLOCK:
579 print_bvspace(p, n, mt->pardist);
580 /* FALLTHROUGH */
581 default:
582 return(1);
583 }
584
585 /* Calculate the offset from the optional second argument. */
586 if ((nn = n->parent->head->child) != NULL &&
587 (nn = nn->next) != NULL &&
588 a2roffsu(nn->string, &su, SCALE_EN)) {
589 len = term_hspan(p, &su);
590 if (len < 0 && (size_t)(-len) > mt->offset)
591 len = -mt->offset;
592 else if (len > SHRT_MAX)
593 len = term_len(p, p->defindent);
594 mt->lmargin[mt->lmargincur] = len;
595 } else
596 len = mt->lmargin[mt->lmargincur];
597
598 switch (n->type) {
599 case ROFFT_HEAD:
600 p->offset = mt->offset;
601 p->rmargin = mt->offset + len;
602
603 savelit = MANT_LITERAL & mt->fl;
604 mt->fl &= ~MANT_LITERAL;
605
606 if (n->child)
607 print_man_node(p, mt, n->child, meta);
608
609 if (savelit)
610 mt->fl |= MANT_LITERAL;
611
612 return(0);
613 case ROFFT_BODY:
614 p->offset = mt->offset + len;
615 p->rmargin = p->maxrmargin;
616 break;
617 default:
618 break;
619 }
620
621 return(1);
622 }
623
624 static void
625 post_IP(DECL_ARGS)
626 {
627
628 switch (n->type) {
629 case ROFFT_HEAD:
630 term_flushln(p);
631 p->flags &= ~TERMP_NOBREAK;
632 p->trailspace = 0;
633 p->rmargin = p->maxrmargin;
634 break;
635 case ROFFT_BODY:
636 term_newln(p);
637 p->offset = mt->offset;
638 break;
639 default:
640 break;
641 }
642 }
643
644 static int
645 pre_TP(DECL_ARGS)
646 {
647 struct roffsu su;
648 struct roff_node *nn;
649 int len, savelit;
650
651 switch (n->type) {
652 case ROFFT_HEAD:
653 p->flags |= TERMP_NOBREAK;
654 p->trailspace = 1;
655 break;
656 case ROFFT_BODY:
657 p->flags |= TERMP_NOSPACE;
658 break;
659 case ROFFT_BLOCK:
660 print_bvspace(p, n, mt->pardist);
661 /* FALLTHROUGH */
662 default:
663 return(1);
664 }
665
666 /* Calculate offset. */
667
668 if ((nn = n->parent->head->child) != NULL &&
669 nn->string != NULL && ! (MAN_LINE & nn->flags) &&
670 a2roffsu(nn->string, &su, SCALE_EN)) {
671 len = term_hspan(p, &su);
672 if (len < 0 && (size_t)(-len) > mt->offset)
673 len = -mt->offset;
674 else if (len > SHRT_MAX)
675 len = term_len(p, p->defindent);
676 mt->lmargin[mt->lmargincur] = len;
677 } else
678 len = mt->lmargin[mt->lmargincur];
679
680 switch (n->type) {
681 case ROFFT_HEAD:
682 p->offset = mt->offset;
683 p->rmargin = mt->offset + len;
684
685 savelit = MANT_LITERAL & mt->fl;
686 mt->fl &= ~MANT_LITERAL;
687
688 /* Don't print same-line elements. */
689 nn = n->child;
690 while (NULL != nn && 0 == (MAN_LINE & nn->flags))
691 nn = nn->next;
692
693 while (NULL != nn) {
694 print_man_node(p, mt, nn, meta);
695 nn = nn->next;
696 }
697
698 if (savelit)
699 mt->fl |= MANT_LITERAL;
700 return(0);
701 case ROFFT_BODY:
702 p->offset = mt->offset + len;
703 p->rmargin = p->maxrmargin;
704 p->trailspace = 0;
705 p->flags &= ~TERMP_NOBREAK;
706 break;
707 default:
708 break;
709 }
710
711 return(1);
712 }
713
714 static void
715 post_TP(DECL_ARGS)
716 {
717
718 switch (n->type) {
719 case ROFFT_HEAD:
720 term_flushln(p);
721 break;
722 case ROFFT_BODY:
723 term_newln(p);
724 p->offset = mt->offset;
725 break;
726 default:
727 break;
728 }
729 }
730
731 static int
732 pre_SS(DECL_ARGS)
733 {
734 int i;
735
736 switch (n->type) {
737 case ROFFT_BLOCK:
738 mt->fl &= ~MANT_LITERAL;
739 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
740 mt->offset = term_len(p, p->defindent);
741
742 /*
743 * No vertical space before the first subsection
744 * and after an empty subsection.
745 */
746
747 do {
748 n = n->prev;
749 } while (n != NULL && n->tok != MAN_MAX &&
750 termacts[n->tok].flags & MAN_NOTEXT);
751 if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
752 break;
753
754 for (i = 0; i < mt->pardist; i++)
755 term_vspace(p);
756 break;
757 case ROFFT_HEAD:
758 term_fontrepl(p, TERMFONT_BOLD);
759 p->offset = term_len(p, 3);
760 break;
761 case ROFFT_BODY:
762 p->offset = mt->offset;
763 break;
764 default:
765 break;
766 }
767
768 return(1);
769 }
770
771 static void
772 post_SS(DECL_ARGS)
773 {
774
775 switch (n->type) {
776 case ROFFT_HEAD:
777 term_newln(p);
778 break;
779 case ROFFT_BODY:
780 term_newln(p);
781 break;
782 default:
783 break;
784 }
785 }
786
787 static int
788 pre_SH(DECL_ARGS)
789 {
790 int i;
791
792 switch (n->type) {
793 case ROFFT_BLOCK:
794 mt->fl &= ~MANT_LITERAL;
795 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
796 mt->offset = term_len(p, p->defindent);
797
798 /*
799 * No vertical space before the first section
800 * and after an empty section.
801 */
802
803 do {
804 n = n->prev;
805 } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
806 if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
807 break;
808
809 for (i = 0; i < mt->pardist; i++)
810 term_vspace(p);
811 break;
812 case ROFFT_HEAD:
813 term_fontrepl(p, TERMFONT_BOLD);
814 p->offset = 0;
815 break;
816 case ROFFT_BODY:
817 p->offset = mt->offset;
818 break;
819 default:
820 break;
821 }
822
823 return(1);
824 }
825
826 static void
827 post_SH(DECL_ARGS)
828 {
829
830 switch (n->type) {
831 case ROFFT_HEAD:
832 term_newln(p);
833 break;
834 case ROFFT_BODY:
835 term_newln(p);
836 break;
837 default:
838 break;
839 }
840 }
841
842 static int
843 pre_RS(DECL_ARGS)
844 {
845 struct roffsu su;
846
847 switch (n->type) {
848 case ROFFT_BLOCK:
849 term_newln(p);
850 return(1);
851 case ROFFT_HEAD:
852 return(0);
853 default:
854 break;
855 }
856
857 n = n->parent->head;
858 n->aux = SHRT_MAX + 1;
859 if (n->child != NULL && a2roffsu(n->child->string, &su, SCALE_EN))
860 n->aux = term_hspan(p, &su);
861 if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
862 n->aux = -mt->offset;
863 else if (n->aux > SHRT_MAX)
864 n->aux = term_len(p, p->defindent);
865
866 mt->offset += n->aux;
867 p->offset = mt->offset;
868 p->rmargin = p->maxrmargin;
869
870 if (++mt->lmarginsz < MAXMARGINS)
871 mt->lmargincur = mt->lmarginsz;
872
873 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
874 return(1);
875 }
876
877 static void
878 post_RS(DECL_ARGS)
879 {
880
881 switch (n->type) {
882 case ROFFT_BLOCK:
883 return;
884 case ROFFT_HEAD:
885 return;
886 default:
887 term_newln(p);
888 break;
889 }
890
891 mt->offset -= n->parent->head->aux;
892 p->offset = mt->offset;
893
894 if (--mt->lmarginsz < MAXMARGINS)
895 mt->lmargincur = mt->lmarginsz;
896 }
897
898 static int
899 pre_UR(DECL_ARGS)
900 {
901
902 return (n->type != ROFFT_HEAD);
903 }
904
905 static void
906 post_UR(DECL_ARGS)
907 {
908
909 if (n->type != ROFFT_BLOCK)
910 return;
911
912 term_word(p, "<");
913 p->flags |= TERMP_NOSPACE;
914
915 if (NULL != n->child->child)
916 print_man_node(p, mt, n->child->child, meta);
917
918 p->flags |= TERMP_NOSPACE;
919 term_word(p, ">");
920 }
921
922 static void
923 print_man_node(DECL_ARGS)
924 {
925 size_t rm, rmax;
926 int c;
927
928 switch (n->type) {
929 case ROFFT_TEXT:
930 /*
931 * If we have a blank line, output a vertical space.
932 * If we have a space as the first character, break
933 * before printing the line's data.
934 */
935 if ('\0' == *n->string) {
936 term_vspace(p);
937 return;
938 } else if (' ' == *n->string && MAN_LINE & n->flags)
939 term_newln(p);
940
941 term_word(p, n->string);
942 goto out;
943
944 case ROFFT_EQN:
945 if ( ! (n->flags & MAN_LINE))
946 p->flags |= TERMP_NOSPACE;
947 term_eqn(p, n->eqn);
948 if (n->next != NULL && ! (n->next->flags & MAN_LINE))
949 p->flags |= TERMP_NOSPACE;
950 return;
951 case ROFFT_TBL:
952 if (p->tbl.cols == NULL)
953 term_vspace(p);
954 term_tbl(p, n->span);
955 return;
956 default:
957 break;
958 }
959
960 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
961 term_fontrepl(p, TERMFONT_NONE);
962
963 c = 1;
964 if (termacts[n->tok].pre)
965 c = (*termacts[n->tok].pre)(p, mt, n, meta);
966
967 if (c && n->child)
968 print_man_nodelist(p, mt, n->child, meta);
969
970 if (termacts[n->tok].post)
971 (*termacts[n->tok].post)(p, mt, n, meta);
972 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
973 term_fontrepl(p, TERMFONT_NONE);
974
975 out:
976 /*
977 * If we're in a literal context, make sure that words
978 * together on the same line stay together. This is a
979 * POST-printing call, so we check the NEXT word. Since
980 * -man doesn't have nested macros, we don't need to be
981 * more specific than this.
982 */
983 if (mt->fl & MANT_LITERAL &&
984 ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
985 (n->next == NULL || n->next->flags & MAN_LINE)) {
986 rm = p->rmargin;
987 rmax = p->maxrmargin;
988 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
989 p->flags |= TERMP_NOSPACE;
990 if (n->string != NULL && *n->string != '\0')
991 term_flushln(p);
992 else
993 term_newln(p);
994 if (rm < rmax && n->parent->tok == MAN_HP) {
995 p->offset = rm;
996 p->rmargin = rmax;
997 } else
998 p->rmargin = rm;
999 p->maxrmargin = rmax;
1000 }
1001 if (MAN_EOS & n->flags)
1002 p->flags |= TERMP_SENTENCE;
1003 }
1004
1005
1006 static void
1007 print_man_nodelist(DECL_ARGS)
1008 {
1009
1010 while (n != NULL) {
1011 print_man_node(p, mt, n, meta);
1012 n = n->next;
1013 }
1014 }
1015
1016 static void
1017 print_man_foot(struct termp *p, const struct roff_meta *meta)
1018 {
1019 char *title;
1020 size_t datelen, titlen;
1021
1022 assert(meta->title);
1023 assert(meta->msec);
1024 assert(meta->date);
1025
1026 term_fontrepl(p, TERMFONT_NONE);
1027
1028 if (meta->hasbody)
1029 term_vspace(p);
1030
1031 /*
1032 * Temporary, undocumented option to imitate mdoc(7) output.
1033 * In the bottom right corner, use the operating system
1034 * instead of the title.
1035 */
1036
1037 if ( ! p->mdocstyle) {
1038 if (meta->hasbody) {
1039 term_vspace(p);
1040 term_vspace(p);
1041 }
1042 mandoc_asprintf(&title, "%s(%s)",
1043 meta->title, meta->msec);
1044 } else if (meta->os) {
1045 title = mandoc_strdup(meta->os);
1046 } else {
1047 title = mandoc_strdup("");
1048 }
1049 datelen = term_strlen(p, meta->date);
1050
1051 /* Bottom left corner: operating system. */
1052
1053 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1054 p->trailspace = 1;
1055 p->offset = 0;
1056 p->rmargin = p->maxrmargin > datelen ?
1057 (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1058
1059 if (meta->os)
1060 term_word(p, meta->os);
1061 term_flushln(p);
1062
1063 /* At the bottom in the middle: manual date. */
1064
1065 p->offset = p->rmargin;
1066 titlen = term_strlen(p, title);
1067 p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
1068 p->flags |= TERMP_NOSPACE;
1069
1070 term_word(p, meta->date);
1071 term_flushln(p);
1072
1073 /* Bottom right corner: manual title and section. */
1074
1075 p->flags &= ~TERMP_NOBREAK;
1076 p->flags |= TERMP_NOSPACE;
1077 p->trailspace = 0;
1078 p->offset = p->rmargin;
1079 p->rmargin = p->maxrmargin;
1080
1081 term_word(p, title);
1082 term_flushln(p);
1083 free(title);
1084 }
1085
1086 static void
1087 print_man_head(struct termp *p, const struct roff_meta *meta)
1088 {
1089 const char *volume;
1090 char *title;
1091 size_t vollen, titlen;
1092
1093 assert(meta->title);
1094 assert(meta->msec);
1095
1096 volume = NULL == meta->vol ? "" : meta->vol;
1097 vollen = term_strlen(p, volume);
1098
1099 /* Top left corner: manual title and section. */
1100
1101 mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1102 titlen = term_strlen(p, title);
1103
1104 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1105 p->trailspace = 1;
1106 p->offset = 0;
1107 p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1108 (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1109 vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1110
1111 term_word(p, title);
1112 term_flushln(p);
1113
1114 /* At the top in the middle: manual volume. */
1115
1116 p->flags |= TERMP_NOSPACE;
1117 p->offset = p->rmargin;
1118 p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1119 p->maxrmargin - titlen : p->maxrmargin;
1120
1121 term_word(p, volume);
1122 term_flushln(p);
1123
1124 /* Top right corner: title and section, again. */
1125
1126 p->flags &= ~TERMP_NOBREAK;
1127 p->trailspace = 0;
1128 if (p->rmargin + titlen <= p->maxrmargin) {
1129 p->flags |= TERMP_NOSPACE;
1130 p->offset = p->rmargin;
1131 p->rmargin = p->maxrmargin;
1132 term_word(p, title);
1133 term_flushln(p);
1134 }
1135
1136 p->flags &= ~TERMP_NOSPACE;
1137 p->offset = 0;
1138 p->rmargin = p->maxrmargin;
1139
1140 /*
1141 * Groff prints three blank lines before the content.
1142 * Do the same, except in the temporary, undocumented
1143 * mode imitating mdoc(7) output.
1144 */
1145
1146 term_vspace(p);
1147 if ( ! p->mdocstyle) {
1148 term_vspace(p);
1149 term_vspace(p);
1150 }
1151 free(title);
1152 }