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