exports.5: network prefixlength (CIDR) format is supported
[dragonfly.git] / contrib / mdocml / man_term.c
blobc91c0746201a3d7ccab77e49ce98a9f6b88d3d97
1 /* $Id: man_term.c,v 1.149 2014/06/20 23:02:31 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #include <sys/types.h>
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
30 #include "mandoc.h"
31 #include "mandoc_aux.h"
32 #include "out.h"
33 #include "man.h"
34 #include "term.h"
35 #include "main.h"
37 #define MAXMARGINS 64 /* maximum number of indented scopes */
39 struct mtermp {
40 int fl;
41 #define MANT_LITERAL (1 << 0)
42 size_t lmargin[MAXMARGINS]; /* margins (incl. visible 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] */
49 #define DECL_ARGS struct termp *p, \
50 struct mtermp *mt, \
51 const struct man_node *n, \
52 const struct man_meta *meta
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. */
61 static int a2width(const struct termp *, const char *);
62 static size_t a2height(const struct termp *, const char *);
64 static void print_man_nodelist(DECL_ARGS);
65 static void print_man_node(DECL_ARGS);
66 static void print_man_head(struct termp *, const void *);
67 static void print_man_foot(struct termp *, const void *);
68 static void print_bvspace(struct termp *,
69 const struct man_node *, int);
71 static int pre_B(DECL_ARGS);
72 static int pre_HP(DECL_ARGS);
73 static int pre_I(DECL_ARGS);
74 static int pre_IP(DECL_ARGS);
75 static int pre_OP(DECL_ARGS);
76 static int pre_PD(DECL_ARGS);
77 static int pre_PP(DECL_ARGS);
78 static int pre_RS(DECL_ARGS);
79 static int pre_SH(DECL_ARGS);
80 static int pre_SS(DECL_ARGS);
81 static int pre_TP(DECL_ARGS);
82 static int pre_UR(DECL_ARGS);
83 static int pre_alternate(DECL_ARGS);
84 static int pre_ft(DECL_ARGS);
85 static int pre_ign(DECL_ARGS);
86 static int pre_in(DECL_ARGS);
87 static int pre_literal(DECL_ARGS);
88 static int pre_ll(DECL_ARGS);
89 static int pre_sp(DECL_ARGS);
91 static void post_IP(DECL_ARGS);
92 static void post_HP(DECL_ARGS);
93 static void post_RS(DECL_ARGS);
94 static void post_SH(DECL_ARGS);
95 static void post_SS(DECL_ARGS);
96 static void post_TP(DECL_ARGS);
97 static void post_UR(DECL_ARGS);
99 static const struct termact termacts[MAN_MAX] = {
100 { pre_sp, NULL, MAN_NOTEXT }, /* br */
101 { NULL, NULL, 0 }, /* TH */
102 { pre_SH, post_SH, 0 }, /* SH */
103 { pre_SS, post_SS, 0 }, /* SS */
104 { pre_TP, post_TP, 0 }, /* TP */
105 { pre_PP, NULL, 0 }, /* LP */
106 { pre_PP, NULL, 0 }, /* PP */
107 { pre_PP, NULL, 0 }, /* P */
108 { pre_IP, post_IP, 0 }, /* IP */
109 { pre_HP, post_HP, 0 }, /* HP */
110 { NULL, NULL, 0 }, /* SM */
111 { pre_B, NULL, 0 }, /* SB */
112 { pre_alternate, NULL, 0 }, /* BI */
113 { pre_alternate, NULL, 0 }, /* IB */
114 { pre_alternate, NULL, 0 }, /* BR */
115 { pre_alternate, NULL, 0 }, /* RB */
116 { NULL, NULL, 0 }, /* R */
117 { pre_B, NULL, 0 }, /* B */
118 { pre_I, NULL, 0 }, /* I */
119 { pre_alternate, NULL, 0 }, /* IR */
120 { pre_alternate, NULL, 0 }, /* RI */
121 { pre_ign, NULL, MAN_NOTEXT }, /* na */
122 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
123 { pre_literal, NULL, 0 }, /* nf */
124 { pre_literal, NULL, 0 }, /* fi */
125 { NULL, NULL, 0 }, /* RE */
126 { pre_RS, post_RS, 0 }, /* RS */
127 { pre_ign, NULL, 0 }, /* DT */
128 { pre_ign, NULL, 0 }, /* UC */
129 { pre_PD, NULL, MAN_NOTEXT }, /* PD */
130 { pre_ign, NULL, 0 }, /* AT */
131 { pre_in, NULL, MAN_NOTEXT }, /* in */
132 { pre_ft, NULL, MAN_NOTEXT }, /* ft */
133 { pre_OP, NULL, 0 }, /* OP */
134 { pre_literal, NULL, 0 }, /* EX */
135 { pre_literal, NULL, 0 }, /* EE */
136 { pre_UR, post_UR, 0 }, /* UR */
137 { NULL, NULL, 0 }, /* UE */
138 { pre_ll, NULL, MAN_NOTEXT }, /* ll */
142 void
143 terminal_man(void *arg, const struct man *man)
145 struct termp *p;
146 const struct man_node *n;
147 const struct man_meta *meta;
148 struct mtermp mt;
150 p = (struct termp *)arg;
152 if (0 == p->defindent)
153 p->defindent = 7;
155 p->overstep = 0;
156 p->maxrmargin = p->defrmargin;
157 p->tabwidth = term_len(p, 5);
159 if (NULL == p->symtab)
160 p->symtab = mchars_alloc();
162 n = man_node(man);
163 meta = man_meta(man);
165 term_begin(p, print_man_head, print_man_foot, meta);
166 p->flags |= TERMP_NOSPACE;
168 memset(&mt, 0, sizeof(struct mtermp));
170 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
171 mt.offset = term_len(p, p->defindent);
172 mt.pardist = 1;
174 if (n->child)
175 print_man_nodelist(p, &mt, n->child, meta);
177 term_end(p);
181 static size_t
182 a2height(const struct termp *p, const char *cp)
184 struct roffsu su;
186 if ( ! a2roffsu(cp, &su, SCALE_VS))
187 SCALE_VS_INIT(&su, atoi(cp));
189 return(term_vspan(p, &su));
192 static int
193 a2width(const struct termp *p, const char *cp)
195 struct roffsu su;
197 if ( ! a2roffsu(cp, &su, SCALE_BU))
198 return(-1);
200 return((int)term_hspan(p, &su));
204 * Printing leading vertical space before a block.
205 * This is used for the paragraph macros.
206 * The rules are pretty simple, since there's very little nesting going
207 * on here. Basically, if we're the first within another block (SS/SH),
208 * then don't emit vertical space. If we are (RS), then do. If not the
209 * first, print it.
211 static void
212 print_bvspace(struct termp *p, const struct man_node *n, int pardist)
214 int i;
216 term_newln(p);
218 if (n->body && n->body->child)
219 if (MAN_TBL == n->body->child->type)
220 return;
222 if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
223 if (NULL == n->prev)
224 return;
226 for (i = 0; i < pardist; i++)
227 term_vspace(p);
231 static int
232 pre_ign(DECL_ARGS)
235 return(0);
238 static int
239 pre_ll(DECL_ARGS)
242 term_setwidth(p, n->nchild ? n->child->string : NULL);
243 return(0);
246 static int
247 pre_I(DECL_ARGS)
250 term_fontrepl(p, TERMFONT_UNDER);
251 return(1);
254 static int
255 pre_literal(DECL_ARGS)
258 term_newln(p);
260 if (MAN_nf == n->tok || MAN_EX == n->tok)
261 mt->fl |= MANT_LITERAL;
262 else
263 mt->fl &= ~MANT_LITERAL;
266 * Unlike .IP and .TP, .HP does not have a HEAD.
267 * So in case a second call to term_flushln() is needed,
268 * indentation has to be set up explicitly.
270 if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
271 p->offset = p->rmargin;
272 p->rmargin = p->maxrmargin;
273 p->trailspace = 0;
274 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
275 p->flags |= TERMP_NOSPACE;
278 return(0);
281 static int
282 pre_PD(DECL_ARGS)
285 n = n->child;
286 if (0 == n) {
287 mt->pardist = 1;
288 return(0);
290 assert(MAN_TEXT == n->type);
291 mt->pardist = atoi(n->string);
292 return(0);
295 static int
296 pre_alternate(DECL_ARGS)
298 enum termfont font[2];
299 const struct man_node *nn;
300 int savelit, i;
302 switch (n->tok) {
303 case MAN_RB:
304 font[0] = TERMFONT_NONE;
305 font[1] = TERMFONT_BOLD;
306 break;
307 case MAN_RI:
308 font[0] = TERMFONT_NONE;
309 font[1] = TERMFONT_UNDER;
310 break;
311 case MAN_BR:
312 font[0] = TERMFONT_BOLD;
313 font[1] = TERMFONT_NONE;
314 break;
315 case MAN_BI:
316 font[0] = TERMFONT_BOLD;
317 font[1] = TERMFONT_UNDER;
318 break;
319 case MAN_IR:
320 font[0] = TERMFONT_UNDER;
321 font[1] = TERMFONT_NONE;
322 break;
323 case MAN_IB:
324 font[0] = TERMFONT_UNDER;
325 font[1] = TERMFONT_BOLD;
326 break;
327 default:
328 abort();
331 savelit = MANT_LITERAL & mt->fl;
332 mt->fl &= ~MANT_LITERAL;
334 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
335 term_fontrepl(p, font[i]);
336 if (savelit && NULL == nn->next)
337 mt->fl |= MANT_LITERAL;
338 print_man_node(p, mt, nn, meta);
339 if (nn->next)
340 p->flags |= TERMP_NOSPACE;
343 return(0);
346 static int
347 pre_B(DECL_ARGS)
350 term_fontrepl(p, TERMFONT_BOLD);
351 return(1);
354 static int
355 pre_OP(DECL_ARGS)
358 term_word(p, "[");
359 p->flags |= TERMP_NOSPACE;
361 if (NULL != (n = n->child)) {
362 term_fontrepl(p, TERMFONT_BOLD);
363 term_word(p, n->string);
365 if (NULL != n && NULL != n->next) {
366 term_fontrepl(p, TERMFONT_UNDER);
367 term_word(p, n->next->string);
370 term_fontrepl(p, TERMFONT_NONE);
371 p->flags |= TERMP_NOSPACE;
372 term_word(p, "]");
373 return(0);
376 static int
377 pre_ft(DECL_ARGS)
379 const char *cp;
381 if (NULL == n->child) {
382 term_fontlast(p);
383 return(0);
386 cp = n->child->string;
387 switch (*cp) {
388 case '4':
389 /* FALLTHROUGH */
390 case '3':
391 /* FALLTHROUGH */
392 case 'B':
393 term_fontrepl(p, TERMFONT_BOLD);
394 break;
395 case '2':
396 /* FALLTHROUGH */
397 case 'I':
398 term_fontrepl(p, TERMFONT_UNDER);
399 break;
400 case 'P':
401 term_fontlast(p);
402 break;
403 case '1':
404 /* FALLTHROUGH */
405 case 'C':
406 /* FALLTHROUGH */
407 case 'R':
408 term_fontrepl(p, TERMFONT_NONE);
409 break;
410 default:
411 break;
413 return(0);
416 static int
417 pre_in(DECL_ARGS)
419 int len, less;
420 size_t v;
421 const char *cp;
423 term_newln(p);
425 if (NULL == n->child) {
426 p->offset = mt->offset;
427 return(0);
430 cp = n->child->string;
431 less = 0;
433 if ('-' == *cp)
434 less = -1;
435 else if ('+' == *cp)
436 less = 1;
437 else
438 cp--;
440 if ((len = a2width(p, ++cp)) < 0)
441 return(0);
443 v = (size_t)len;
445 if (less < 0)
446 p->offset -= p->offset > v ? v : p->offset;
447 else if (less > 0)
448 p->offset += v;
449 else
450 p->offset = v;
452 /* Don't let this creep beyond the right margin. */
454 if (p->offset > p->rmargin)
455 p->offset = p->rmargin;
457 return(0);
460 static int
461 pre_sp(DECL_ARGS)
463 char *s;
464 size_t i, len;
465 int neg;
467 if ((NULL == n->prev && n->parent)) {
468 switch (n->parent->tok) {
469 case MAN_SH:
470 /* FALLTHROUGH */
471 case MAN_SS:
472 /* FALLTHROUGH */
473 case MAN_PP:
474 /* FALLTHROUGH */
475 case MAN_LP:
476 /* FALLTHROUGH */
477 case MAN_P:
478 /* FALLTHROUGH */
479 return(0);
480 default:
481 break;
485 neg = 0;
486 switch (n->tok) {
487 case MAN_br:
488 len = 0;
489 break;
490 default:
491 if (NULL == n->child) {
492 len = 1;
493 break;
495 s = n->child->string;
496 if ('-' == *s) {
497 neg = 1;
498 s++;
500 len = a2height(p, s);
501 break;
504 if (0 == len)
505 term_newln(p);
506 else if (neg)
507 p->skipvsp += len;
508 else
509 for (i = 0; i < len; i++)
510 term_vspace(p);
512 return(0);
515 static int
516 pre_HP(DECL_ARGS)
518 size_t len, one;
519 int ival;
520 const struct man_node *nn;
522 switch (n->type) {
523 case MAN_BLOCK:
524 print_bvspace(p, n, mt->pardist);
525 return(1);
526 case MAN_BODY:
527 break;
528 default:
529 return(0);
532 if ( ! (MANT_LITERAL & mt->fl)) {
533 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
534 p->trailspace = 2;
537 len = mt->lmargin[mt->lmargincur];
538 ival = -1;
540 /* Calculate offset. */
542 if (NULL != (nn = n->parent->head->child))
543 if ((ival = a2width(p, nn->string)) >= 0)
544 len = (size_t)ival;
546 one = term_len(p, 1);
547 if (len < one)
548 len = one;
550 p->offset = mt->offset;
551 p->rmargin = mt->offset + len;
553 if (ival >= 0)
554 mt->lmargin[mt->lmargincur] = (size_t)ival;
556 return(1);
559 static void
560 post_HP(DECL_ARGS)
563 switch (n->type) {
564 case MAN_BODY:
565 term_newln(p);
566 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
567 p->trailspace = 0;
568 p->offset = mt->offset;
569 p->rmargin = p->maxrmargin;
570 break;
571 default:
572 break;
576 static int
577 pre_PP(DECL_ARGS)
580 switch (n->type) {
581 case MAN_BLOCK:
582 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
583 print_bvspace(p, n, mt->pardist);
584 break;
585 default:
586 p->offset = mt->offset;
587 break;
590 return(MAN_HEAD != n->type);
593 static int
594 pre_IP(DECL_ARGS)
596 const struct man_node *nn;
597 size_t len;
598 int savelit, ival;
600 switch (n->type) {
601 case MAN_BODY:
602 p->flags |= TERMP_NOSPACE;
603 break;
604 case MAN_HEAD:
605 p->flags |= TERMP_NOBREAK;
606 p->trailspace = 1;
607 break;
608 case MAN_BLOCK:
609 print_bvspace(p, n, mt->pardist);
610 /* FALLTHROUGH */
611 default:
612 return(1);
615 len = mt->lmargin[mt->lmargincur];
616 ival = -1;
618 /* Calculate the offset from the optional second argument. */
619 if (NULL != (nn = n->parent->head->child))
620 if (NULL != (nn = nn->next))
621 if ((ival = a2width(p, nn->string)) >= 0)
622 len = (size_t)ival;
624 switch (n->type) {
625 case MAN_HEAD:
626 /* Handle zero-width lengths. */
627 if (0 == len)
628 len = term_len(p, 1);
630 p->offset = mt->offset;
631 p->rmargin = mt->offset + len;
632 if (ival < 0)
633 break;
635 /* Set the saved left-margin. */
636 mt->lmargin[mt->lmargincur] = (size_t)ival;
638 savelit = MANT_LITERAL & mt->fl;
639 mt->fl &= ~MANT_LITERAL;
641 if (n->child)
642 print_man_node(p, mt, n->child, meta);
644 if (savelit)
645 mt->fl |= MANT_LITERAL;
647 return(0);
648 case MAN_BODY:
649 p->offset = mt->offset + len;
650 p->rmargin = p->maxrmargin > p->offset ?
651 p->maxrmargin : p->offset;
652 break;
653 default:
654 break;
657 return(1);
660 static void
661 post_IP(DECL_ARGS)
664 switch (n->type) {
665 case MAN_HEAD:
666 term_flushln(p);
667 p->flags &= ~TERMP_NOBREAK;
668 p->trailspace = 0;
669 p->rmargin = p->maxrmargin;
670 break;
671 case MAN_BODY:
672 term_newln(p);
673 p->offset = mt->offset;
674 break;
675 default:
676 break;
680 static int
681 pre_TP(DECL_ARGS)
683 const struct man_node *nn;
684 size_t len;
685 int savelit, ival;
687 switch (n->type) {
688 case MAN_HEAD:
689 p->flags |= TERMP_NOBREAK;
690 p->trailspace = 1;
691 break;
692 case MAN_BODY:
693 p->flags |= TERMP_NOSPACE;
694 break;
695 case MAN_BLOCK:
696 print_bvspace(p, n, mt->pardist);
697 /* FALLTHROUGH */
698 default:
699 return(1);
702 len = (size_t)mt->lmargin[mt->lmargincur];
703 ival = -1;
705 /* Calculate offset. */
707 if (NULL != (nn = n->parent->head->child))
708 if (nn->string && 0 == (MAN_LINE & nn->flags))
709 if ((ival = a2width(p, nn->string)) >= 0)
710 len = (size_t)ival;
712 switch (n->type) {
713 case MAN_HEAD:
714 /* Handle zero-length properly. */
715 if (0 == len)
716 len = term_len(p, 1);
718 p->offset = mt->offset;
719 p->rmargin = mt->offset + len;
721 savelit = MANT_LITERAL & mt->fl;
722 mt->fl &= ~MANT_LITERAL;
724 /* Don't print same-line elements. */
725 nn = n->child;
726 while (NULL != nn && 0 == (MAN_LINE & nn->flags))
727 nn = nn->next;
729 while (NULL != nn) {
730 print_man_node(p, mt, nn, meta);
731 nn = nn->next;
734 if (savelit)
735 mt->fl |= MANT_LITERAL;
736 if (ival >= 0)
737 mt->lmargin[mt->lmargincur] = (size_t)ival;
739 return(0);
740 case MAN_BODY:
741 p->offset = mt->offset + len;
742 p->rmargin = p->maxrmargin > p->offset ?
743 p->maxrmargin : p->offset;
744 p->trailspace = 0;
745 p->flags &= ~TERMP_NOBREAK;
746 break;
747 default:
748 break;
751 return(1);
754 static void
755 post_TP(DECL_ARGS)
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;
771 static int
772 pre_SS(DECL_ARGS)
774 int i;
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;
801 return(1);
804 static void
805 post_SS(DECL_ARGS)
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;
820 static int
821 pre_SH(DECL_ARGS)
823 int i;
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;
851 return(1);
854 static void
855 post_SH(DECL_ARGS)
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;
870 static int
871 pre_RS(DECL_ARGS)
873 int ival;
874 size_t sz;
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;
886 sz = term_len(p, p->defindent);
888 if (NULL != (n = n->parent->head->child))
889 if ((ival = a2width(p, n->string)) >= 0)
890 sz = (size_t)ival;
892 mt->offset += sz;
893 p->offset = mt->offset;
894 p->rmargin = p->maxrmargin > p->offset ?
895 p->maxrmargin : p->offset;
897 if (++mt->lmarginsz < MAXMARGINS)
898 mt->lmargincur = mt->lmarginsz;
900 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
901 return(1);
904 static void
905 post_RS(DECL_ARGS)
907 int ival;
908 size_t sz;
910 switch (n->type) {
911 case MAN_BLOCK:
912 return;
913 case MAN_HEAD:
914 return;
915 default:
916 term_newln(p);
917 break;
920 sz = term_len(p, p->defindent);
922 if (NULL != (n = n->parent->head->child))
923 if ((ival = a2width(p, n->string)) >= 0)
924 sz = (size_t)ival;
926 mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
927 p->offset = mt->offset;
929 if (--mt->lmarginsz < MAXMARGINS)
930 mt->lmargincur = mt->lmarginsz;
933 static int
934 pre_UR(DECL_ARGS)
937 return (MAN_HEAD != n->type);
940 static void
941 post_UR(DECL_ARGS)
944 if (MAN_BLOCK != n->type)
945 return;
947 term_word(p, "<");
948 p->flags |= TERMP_NOSPACE;
950 if (NULL != n->child->child)
951 print_man_node(p, mt, n->child->child, meta);
953 p->flags |= TERMP_NOSPACE;
954 term_word(p, ">");
957 static void
958 print_man_node(DECL_ARGS)
960 size_t rm, rmax;
961 int c;
963 switch (n->type) {
964 case MAN_TEXT:
966 * If we have a blank line, output a vertical space.
967 * If we have a space as the first character, break
968 * before printing the line's data.
970 if ('\0' == *n->string) {
971 term_vspace(p);
972 return;
973 } else if (' ' == *n->string && MAN_LINE & n->flags)
974 term_newln(p);
976 term_word(p, n->string);
977 goto out;
979 case MAN_EQN:
980 term_eqn(p, n->eqn);
981 return;
982 case MAN_TBL:
984 * Tables are preceded by a newline. Then process a
985 * table line, which will cause line termination,
987 if (TBL_SPAN_FIRST & n->span->flags)
988 term_newln(p);
989 term_tbl(p, n->span);
990 return;
991 default:
992 break;
995 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
996 term_fontrepl(p, TERMFONT_NONE);
998 c = 1;
999 if (termacts[n->tok].pre)
1000 c = (*termacts[n->tok].pre)(p, mt, n, meta);
1002 if (c && n->child)
1003 print_man_nodelist(p, mt, n->child, meta);
1005 if (termacts[n->tok].post)
1006 (*termacts[n->tok].post)(p, mt, n, meta);
1007 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1008 term_fontrepl(p, TERMFONT_NONE);
1010 out:
1012 * If we're in a literal context, make sure that words
1013 * together on the same line stay together. This is a
1014 * POST-printing call, so we check the NEXT word. Since
1015 * -man doesn't have nested macros, we don't need to be
1016 * more specific than this.
1018 if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1019 (NULL == n->next || MAN_LINE & n->next->flags)) {
1020 rm = p->rmargin;
1021 rmax = p->maxrmargin;
1022 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1023 p->flags |= TERMP_NOSPACE;
1024 if (NULL != n->string && '\0' != *n->string)
1025 term_flushln(p);
1026 else
1027 term_newln(p);
1028 if (rm < rmax && n->parent->tok == MAN_HP) {
1029 p->offset = rm;
1030 p->rmargin = rmax;
1031 } else
1032 p->rmargin = rm;
1033 p->maxrmargin = rmax;
1035 if (MAN_EOS & n->flags)
1036 p->flags |= TERMP_SENTENCE;
1040 static void
1041 print_man_nodelist(DECL_ARGS)
1044 print_man_node(p, mt, n, meta);
1045 if ( ! n->next)
1046 return;
1047 print_man_nodelist(p, mt, n->next, meta);
1050 static void
1051 print_man_foot(struct termp *p, const void *arg)
1053 const struct man_meta *meta;
1054 char *title;
1055 size_t datelen;
1057 meta = (const struct man_meta *)arg;
1058 assert(meta->title);
1059 assert(meta->msec);
1060 assert(meta->date);
1062 term_fontrepl(p, TERMFONT_NONE);
1064 if (meta->hasbody)
1065 term_vspace(p);
1068 * Temporary, undocumented option to imitate mdoc(7) output.
1069 * In the bottom right corner, use the source instead of
1070 * the title.
1073 if ( ! p->mdocstyle) {
1074 if (meta->hasbody) {
1075 term_vspace(p);
1076 term_vspace(p);
1078 mandoc_asprintf(&title, "%s(%s)",
1079 meta->title, meta->msec);
1080 } else if (meta->source) {
1081 title = mandoc_strdup(meta->source);
1082 } else {
1083 title = mandoc_strdup("");
1085 datelen = term_strlen(p, meta->date);
1087 /* Bottom left corner: manual source. */
1089 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1090 p->trailspace = 1;
1091 p->offset = 0;
1092 p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1094 if (meta->source)
1095 term_word(p, meta->source);
1096 term_flushln(p);
1098 /* At the bottom in the middle: manual date. */
1100 p->flags |= TERMP_NOSPACE;
1101 p->offset = p->rmargin;
1102 p->rmargin = p->maxrmargin - term_strlen(p, title);
1103 if (p->offset + datelen >= p->rmargin)
1104 p->rmargin = p->offset + datelen;
1106 term_word(p, meta->date);
1107 term_flushln(p);
1109 /* Bottom right corner: manual title and section. */
1111 p->flags &= ~TERMP_NOBREAK;
1112 p->flags |= TERMP_NOSPACE;
1113 p->trailspace = 0;
1114 p->offset = p->rmargin;
1115 p->rmargin = p->maxrmargin;
1117 term_word(p, title);
1118 term_flushln(p);
1119 free(title);
1122 static void
1123 print_man_head(struct termp *p, const void *arg)
1125 const struct man_meta *meta;
1126 const char *volume;
1127 char *title;
1128 size_t vollen, titlen;
1130 meta = (const struct man_meta *)arg;
1131 assert(meta->title);
1132 assert(meta->msec);
1134 volume = NULL == meta->vol ? "" : meta->vol;
1135 vollen = term_strlen(p, volume);
1137 /* Top left corner: manual title and section. */
1139 mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1140 titlen = term_strlen(p, title);
1142 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1143 p->trailspace = 1;
1144 p->offset = 0;
1145 p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1146 (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1147 p->maxrmargin - vollen;
1149 term_word(p, title);
1150 term_flushln(p);
1152 /* At the top in the middle: manual volume. */
1154 p->flags |= TERMP_NOSPACE;
1155 p->offset = p->rmargin;
1156 p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1157 p->maxrmargin - titlen : p->maxrmargin;
1159 term_word(p, volume);
1160 term_flushln(p);
1162 /* Top right corner: title and section, again. */
1164 p->flags &= ~TERMP_NOBREAK;
1165 p->trailspace = 0;
1166 if (p->rmargin + titlen <= p->maxrmargin) {
1167 p->flags |= TERMP_NOSPACE;
1168 p->offset = p->rmargin;
1169 p->rmargin = p->maxrmargin;
1170 term_word(p, title);
1171 term_flushln(p);
1174 p->flags &= ~TERMP_NOSPACE;
1175 p->offset = 0;
1176 p->rmargin = p->maxrmargin;
1179 * Groff prints three blank lines before the content.
1180 * Do the same, except in the temporary, undocumented
1181 * mode imitating mdoc(7) output.
1184 term_vspace(p);
1185 if ( ! p->mdocstyle) {
1186 term_vspace(p);
1187 term_vspace(p);
1189 free(title);