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