]>
git.cameronkatri.com Git - mandoc.git/blob - man_term.c
1 /* $Id: man_term.c,v 1.28 2009/08/21 08:41:05 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/types.h>
34 #define MANT_LITERAL (1 << 0)
36 * Default amount to indent the left margin after leading text
37 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
38 * indent). This needs to be saved because `HP' and so on, if
39 * not having a specified value, must default.
41 * Note that this is the indentation AFTER the left offset, so
42 * the total offset is usually offset + lmargin.
46 * The default offset, i.e., the amount between any text and the
52 #define DECL_ARGS struct termp *p, \
54 const struct man_node *n, \
55 const struct man_meta *m
58 int (*pre
)(DECL_ARGS
);
59 void (*post
)(DECL_ARGS
);
62 static int pre_B(DECL_ARGS
);
63 static int pre_BI(DECL_ARGS
);
64 static int pre_BR(DECL_ARGS
);
65 static int pre_HP(DECL_ARGS
);
66 static int pre_I(DECL_ARGS
);
67 static int pre_IB(DECL_ARGS
);
68 static int pre_IP(DECL_ARGS
);
69 static int pre_IR(DECL_ARGS
);
70 static int pre_PP(DECL_ARGS
);
71 static int pre_RB(DECL_ARGS
);
72 static int pre_RI(DECL_ARGS
);
73 static int pre_RS(DECL_ARGS
);
74 static int pre_SH(DECL_ARGS
);
75 static int pre_SS(DECL_ARGS
);
76 static int pre_TP(DECL_ARGS
);
77 static int pre_br(DECL_ARGS
);
78 static int pre_fi(DECL_ARGS
);
79 static int pre_nf(DECL_ARGS
);
80 static int pre_r(DECL_ARGS
);
81 static int pre_sp(DECL_ARGS
);
83 static void post_B(DECL_ARGS
);
84 static void post_I(DECL_ARGS
);
85 static void post_IP(DECL_ARGS
);
86 static void post_HP(DECL_ARGS
);
87 static void post_RS(DECL_ARGS
);
88 static void post_SH(DECL_ARGS
);
89 static void post_SS(DECL_ARGS
);
90 static void post_TP(DECL_ARGS
);
91 static void post_i(DECL_ARGS
);
93 static const struct termact termacts
[MAN_MAX
] = {
94 { pre_br
, NULL
}, /* br */
95 { NULL
, NULL
}, /* TH */
96 { pre_SH
, post_SH
}, /* SH */
97 { pre_SS
, post_SS
}, /* SS */
98 { pre_TP
, post_TP
}, /* TP */
99 { pre_PP
, NULL
}, /* LP */
100 { pre_PP
, NULL
}, /* PP */
101 { pre_PP
, NULL
}, /* P */
102 { pre_IP
, post_IP
}, /* IP */
103 { pre_HP
, post_HP
}, /* HP */
104 { NULL
, NULL
}, /* SM */
105 { pre_B
, post_B
}, /* SB */
106 { pre_BI
, NULL
}, /* BI */
107 { pre_IB
, NULL
}, /* IB */
108 { pre_BR
, NULL
}, /* BR */
109 { pre_RB
, NULL
}, /* RB */
110 { NULL
, NULL
}, /* R */
111 { pre_B
, post_B
}, /* B */
112 { pre_I
, post_I
}, /* I */
113 { pre_IR
, NULL
}, /* IR */
114 { pre_RI
, NULL
}, /* RI */
115 { NULL
, NULL
}, /* na */
116 { pre_I
, post_i
}, /* i */
117 { pre_sp
, NULL
}, /* sp */
118 { pre_nf
, NULL
}, /* nf */
119 { pre_fi
, NULL
}, /* fi */
120 { pre_r
, NULL
}, /* r */
121 { NULL
, NULL
}, /* RE */
122 { pre_RS
, post_RS
}, /* RS */
123 { NULL
, NULL
}, /* DT */
127 extern size_t strlcpy(char *, const char *, size_t);
128 extern size_t strlcat(char *, const char *, size_t);
131 static void print_head(struct termp
*,
132 const struct man_meta
*);
133 static void print_body(DECL_ARGS
);
134 static void print_node(DECL_ARGS
);
135 static void print_foot(struct termp
*,
136 const struct man_meta
*);
137 static void fmt_block_vspace(struct termp
*,
138 const struct man_node
*);
139 static int arg_width(const struct man_node
*);
143 man_run(struct termp
*p
, const struct man
*m
)
147 print_head(p
, man_meta(m
));
148 p
->flags
|= TERMP_NOSPACE
;
150 assert(MAN_ROOT
== man_node(m
)->type
);
156 if (man_node(m
)->child
)
157 print_body(p
, &mt
, man_node(m
)->child
, man_meta(m
));
158 print_foot(p
, man_meta(m
));
165 fmt_block_vspace(struct termp
*p
, const struct man_node
*n
)
172 if (MAN_SS
== n
->prev
->tok
)
174 if (MAN_SH
== n
->prev
->tok
)
182 arg_width(const struct man_node
*n
)
187 assert(MAN_TEXT
== n
->type
);
192 if (0 == (len
= (int)strlen(p
)))
195 for (i
= 0; i
< len
; i
++)
196 if ( ! isdigit((u_char
)p
[i
]))
200 if ('n' == p
[len
- 1] || 'm' == p
[len
- 1])
214 p
->flags
|= TERMP_UNDER
;
224 p
->flags
&= ~TERMP_UNDER
;
225 p
->flags
&= ~TERMP_BOLD
;
236 p
->flags
&= ~TERMP_UNDER
;
245 p
->flags
&= ~TERMP_UNDER
;
254 mt
->fl
&= ~MANT_LITERAL
;
265 mt
->fl
|= MANT_LITERAL
;
274 const struct man_node
*nn
;
277 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
279 p
->flags
|= TERMP_UNDER
;
281 p
->flags
|= TERMP_NOSPACE
;
282 print_node(p
, mt
, nn
, m
);
284 p
->flags
&= ~TERMP_UNDER
;
294 const struct man_node
*nn
;
297 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
298 p
->flags
|= i
% 2 ? TERMP_BOLD
: TERMP_UNDER
;
300 p
->flags
|= TERMP_NOSPACE
;
301 print_node(p
, mt
, nn
, m
);
302 p
->flags
&= i
% 2 ? ~TERMP_BOLD
: ~TERMP_UNDER
;
312 const struct man_node
*nn
;
315 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
317 p
->flags
|= TERMP_BOLD
;
319 p
->flags
|= TERMP_NOSPACE
;
320 print_node(p
, mt
, nn
, m
);
322 p
->flags
&= ~TERMP_BOLD
;
332 const struct man_node
*nn
;
335 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
337 p
->flags
|= TERMP_UNDER
;
339 p
->flags
|= TERMP_NOSPACE
;
340 print_node(p
, mt
, nn
, m
);
342 p
->flags
&= ~TERMP_UNDER
;
352 const struct man_node
*nn
;
355 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
357 p
->flags
|= TERMP_BOLD
;
359 p
->flags
|= TERMP_NOSPACE
;
360 print_node(p
, mt
, nn
, m
);
362 p
->flags
&= ~TERMP_BOLD
;
372 const struct man_node
*nn
;
375 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
376 p
->flags
|= i
% 2 ? TERMP_UNDER
: TERMP_BOLD
;
378 p
->flags
|= TERMP_NOSPACE
;
379 print_node(p
, mt
, nn
, m
);
380 p
->flags
&= i
% 2 ? ~TERMP_UNDER
: ~TERMP_BOLD
;
391 p
->flags
|= TERMP_BOLD
;
401 p
->flags
&= ~TERMP_BOLD
;
411 if (NULL
== n
->child
) {
416 len
= atoi(n
->child
->string
);
419 for (i
= 0; i
< len
; i
++)
442 const struct man_node
*nn
;
446 fmt_block_vspace(p
, n
);
449 p
->flags
|= TERMP_NOBREAK
;
450 p
->flags
|= TERMP_TWOSPACE
;
459 /* Calculate offset. */
461 if (NULL
!= (nn
= n
->parent
->head
->child
))
462 if ((ival
= arg_width(nn
)) >= 0)
468 p
->offset
= mt
->offset
;
469 p
->rmargin
= mt
->offset
+ len
;
472 mt
->lmargin
= (size_t)ival
;
489 p
->flags
&= ~TERMP_NOBREAK
;
490 p
->flags
&= ~TERMP_TWOSPACE
;
491 p
->offset
= mt
->offset
;
492 p
->rmargin
= p
->maxrmargin
;
507 mt
->lmargin
= INDENT
;
508 fmt_block_vspace(p
, n
);
511 p
->offset
= mt
->offset
;
523 const struct man_node
*nn
;
529 p
->flags
|= TERMP_NOLPAD
;
530 p
->flags
|= TERMP_NOSPACE
;
533 p
->flags
|= TERMP_NOBREAK
;
534 p
->flags
|= TERMP_TWOSPACE
;
537 fmt_block_vspace(p
, n
);
546 /* Calculate offset. */
548 if (NULL
!= (nn
= n
->parent
->head
->child
))
549 if (NULL
!= (nn
= nn
->next
)) {
550 for ( ; nn
->next
; nn
= nn
->next
)
552 if ((ival
= arg_width(nn
)) >= 0)
558 /* Handle zero-width lengths. */
562 p
->offset
= mt
->offset
;
563 p
->rmargin
= mt
->offset
+ len
;
567 /* Set the saved left-margin. */
568 mt
->lmargin
= (size_t)ival
;
570 /* Don't print the length value. */
571 for (nn
= n
->child
; nn
->next
; nn
= nn
->next
)
572 print_node(p
, mt
, nn
, m
);
575 p
->offset
= mt
->offset
+ len
;
576 p
->rmargin
= p
->maxrmargin
;
594 p
->flags
&= ~TERMP_NOBREAK
;
595 p
->flags
&= ~TERMP_TWOSPACE
;
596 p
->rmargin
= p
->maxrmargin
;
600 p
->flags
&= ~TERMP_NOLPAD
;
612 const struct man_node
*nn
;
618 p
->flags
|= TERMP_NOBREAK
;
619 p
->flags
|= TERMP_TWOSPACE
;
622 p
->flags
|= TERMP_NOLPAD
;
623 p
->flags
|= TERMP_NOSPACE
;
626 fmt_block_vspace(p
, n
);
632 len
= (size_t)mt
->lmargin
;
635 /* Calculate offset. */
637 if (NULL
!= (nn
= n
->parent
->head
->child
))
638 if (NULL
!= nn
->next
)
639 if ((ival
= arg_width(nn
)) >= 0)
644 /* Handle zero-length properly. */
648 p
->offset
= mt
->offset
;
649 p
->rmargin
= mt
->offset
+ len
;
651 /* Don't print same-line elements. */
652 for (nn
= n
->child
; nn
; nn
= nn
->next
)
653 if (nn
->line
> n
->line
)
654 print_node(p
, mt
, nn
, m
);
657 mt
->lmargin
= (size_t)ival
;
661 p
->offset
= mt
->offset
+ len
;
662 p
->rmargin
= p
->maxrmargin
;
680 p
->flags
&= ~TERMP_NOBREAK
;
681 p
->flags
&= ~TERMP_TWOSPACE
;
682 p
->rmargin
= p
->maxrmargin
;
686 p
->flags
&= ~TERMP_NOLPAD
;
701 mt
->lmargin
= INDENT
;
703 /* If following a prior empty `SS', no vspace. */
704 if (n
->prev
&& MAN_SS
== n
->prev
->tok
)
705 if (NULL
== n
->prev
->body
->child
)
712 p
->flags
|= TERMP_BOLD
;
713 p
->offset
= HALFINDENT
;
716 p
->offset
= mt
->offset
;
734 p
->flags
&= ~TERMP_BOLD
;
752 mt
->lmargin
= INDENT
;
754 /* If following a prior empty `SH', no vspace. */
755 if (n
->prev
&& MAN_SH
== n
->prev
->tok
)
756 if (NULL
== n
->prev
->body
->child
)
761 p
->flags
|= TERMP_BOLD
;
765 p
->offset
= mt
->offset
;
783 p
->flags
&= ~TERMP_BOLD
;
798 const struct man_node
*nn
;
811 if (NULL
== (nn
= n
->parent
->head
->child
)) {
812 mt
->offset
= mt
->lmargin
+ INDENT
;
813 p
->offset
= mt
->offset
;
817 if ((ival
= arg_width(nn
)) < 0)
820 mt
->offset
= INDENT
+ (size_t)ival
;
821 p
->offset
= mt
->offset
;
834 mt
->offset
= mt
->lmargin
= INDENT
;
845 print_node(DECL_ARGS
)
853 if (0 == *n
->string
) {
858 * Note! This is hacky. Here, we recognise the `\c'
859 * escape embedded in so many -man pages. It's supposed
860 * to remove the subsequent space, so we mark NOSPACE if
861 * it's encountered in the string.
863 sz
= (int)strlen(n
->string
);
864 term_word(p
, n
->string
);
865 if (sz
>= 2 && n
->string
[sz
- 1] == 'c' &&
866 n
->string
[sz
- 2] == '\\')
867 p
->flags
|= TERMP_NOSPACE
;
868 /* FIXME: this means that macro lines are munged! */
869 if (MANT_LITERAL
& mt
->fl
) {
870 p
->flags
|= TERMP_NOSPACE
;
875 if (termacts
[n
->tok
].pre
)
876 c
= (*termacts
[n
->tok
].pre
)(p
, mt
, n
, m
);
881 print_body(p
, mt
, n
->child
, m
);
883 if (MAN_TEXT
!= n
->type
)
884 if (termacts
[n
->tok
].post
)
885 (*termacts
[n
->tok
].post
)(p
, mt
, n
, m
);
890 print_body(DECL_ARGS
)
893 print_node(p
, mt
, n
, m
);
896 print_body(p
, mt
, n
->next
, m
);
901 print_foot(struct termp
*p
, const struct man_meta
*meta
)
906 if (NULL
== (buf
= malloc(p
->rmargin
)))
909 tm
= localtime(&meta
->date
);
911 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
916 p
->flags
|= TERMP_NOSPACE
| TERMP_NOBREAK
;
917 p
->rmargin
= p
->maxrmargin
- strlen(buf
);
921 term_word(p
, meta
->source
);
926 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
927 p
->offset
= p
->rmargin
;
928 p
->rmargin
= p
->maxrmargin
;
929 p
->flags
&= ~TERMP_NOBREAK
;
939 print_head(struct termp
*p
, const struct man_meta
*meta
)
943 p
->rmargin
= p
->maxrmargin
;
946 if (NULL
== (buf
= malloc(p
->rmargin
)))
948 if (NULL
== (title
= malloc(p
->rmargin
)))
952 (void)strlcpy(buf
, meta
->vol
, p
->rmargin
);
956 (void)snprintf(title
, p
->rmargin
, "%s(%d)",
957 meta
->title
, meta
->msec
);
960 p
->rmargin
= (p
->maxrmargin
- strlen(buf
) + 1) / 2;
961 p
->flags
|= TERMP_NOBREAK
| TERMP_NOSPACE
;
966 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
967 p
->offset
= p
->rmargin
;
968 p
->rmargin
= p
->maxrmargin
- strlen(title
);
973 p
->offset
= p
->rmargin
;
974 p
->rmargin
= p
->maxrmargin
;
975 p
->flags
&= ~TERMP_NOBREAK
;
976 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
981 p
->rmargin
= p
->maxrmargin
;
983 p
->flags
&= ~TERMP_NOSPACE
;