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