]>
git.cameronkatri.com Git - mandoc.git/blob - man_term.c
3e10d34c1426030e714bb272d63a44863646806f
1 /* $Id: man_term.c,v 1.29 2009/08/22 09:10:38 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_ign(DECL_ARGS
);
80 static int pre_nf(DECL_ARGS
);
81 static int pre_r(DECL_ARGS
);
82 static int pre_sp(DECL_ARGS
);
84 static void post_B(DECL_ARGS
);
85 static void post_I(DECL_ARGS
);
86 static void post_IP(DECL_ARGS
);
87 static void post_HP(DECL_ARGS
);
88 static void post_RS(DECL_ARGS
);
89 static void post_SH(DECL_ARGS
);
90 static void post_SS(DECL_ARGS
);
91 static void post_TP(DECL_ARGS
);
92 static void post_i(DECL_ARGS
);
94 static const struct termact termacts
[MAN_MAX
] = {
95 { pre_br
, NULL
}, /* br */
96 { NULL
, NULL
}, /* TH */
97 { pre_SH
, post_SH
}, /* SH */
98 { pre_SS
, post_SS
}, /* SS */
99 { pre_TP
, post_TP
}, /* TP */
100 { pre_PP
, NULL
}, /* LP */
101 { pre_PP
, NULL
}, /* PP */
102 { pre_PP
, NULL
}, /* P */
103 { pre_IP
, post_IP
}, /* IP */
104 { pre_HP
, post_HP
}, /* HP */
105 { NULL
, NULL
}, /* SM */
106 { pre_B
, post_B
}, /* SB */
107 { pre_BI
, NULL
}, /* BI */
108 { pre_IB
, NULL
}, /* IB */
109 { pre_BR
, NULL
}, /* BR */
110 { pre_RB
, NULL
}, /* RB */
111 { NULL
, NULL
}, /* R */
112 { pre_B
, post_B
}, /* B */
113 { pre_I
, post_I
}, /* I */
114 { pre_IR
, NULL
}, /* IR */
115 { pre_RI
, NULL
}, /* RI */
116 { NULL
, NULL
}, /* na */
117 { pre_I
, post_i
}, /* i */
118 { pre_sp
, NULL
}, /* sp */
119 { pre_nf
, NULL
}, /* nf */
120 { pre_fi
, NULL
}, /* fi */
121 { pre_r
, NULL
}, /* r */
122 { NULL
, NULL
}, /* RE */
123 { pre_RS
, post_RS
}, /* RS */
124 { pre_ign
, NULL
}, /* DT */
125 { pre_ign
, NULL
}, /* UC */
129 extern size_t strlcpy(char *, const char *, size_t);
130 extern size_t strlcat(char *, const char *, size_t);
133 static void print_head(struct termp
*,
134 const struct man_meta
*);
135 static void print_body(DECL_ARGS
);
136 static void print_node(DECL_ARGS
);
137 static void print_foot(struct termp
*,
138 const struct man_meta
*);
139 static void fmt_block_vspace(struct termp
*,
140 const struct man_node
*);
141 static int arg_width(const struct man_node
*);
145 man_run(struct termp
*p
, const struct man
*m
)
149 print_head(p
, man_meta(m
));
150 p
->flags
|= TERMP_NOSPACE
;
152 assert(MAN_ROOT
== man_node(m
)->type
);
158 if (man_node(m
)->child
)
159 print_body(p
, &mt
, man_node(m
)->child
, man_meta(m
));
160 print_foot(p
, man_meta(m
));
167 fmt_block_vspace(struct termp
*p
, const struct man_node
*n
)
174 if (MAN_SS
== n
->prev
->tok
)
176 if (MAN_SH
== n
->prev
->tok
)
184 arg_width(const struct man_node
*n
)
189 assert(MAN_TEXT
== n
->type
);
194 if (0 == (len
= (int)strlen(p
)))
197 for (i
= 0; i
< len
; i
++)
198 if ( ! isdigit((u_char
)p
[i
]))
202 if ('n' == p
[len
- 1] || 'm' == p
[len
- 1])
225 p
->flags
|= TERMP_UNDER
;
235 p
->flags
&= ~TERMP_UNDER
;
236 p
->flags
&= ~TERMP_BOLD
;
247 p
->flags
&= ~TERMP_UNDER
;
256 p
->flags
&= ~TERMP_UNDER
;
265 mt
->fl
&= ~MANT_LITERAL
;
276 mt
->fl
|= MANT_LITERAL
;
285 const struct man_node
*nn
;
288 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
290 p
->flags
|= TERMP_UNDER
;
292 p
->flags
|= TERMP_NOSPACE
;
293 print_node(p
, mt
, nn
, m
);
295 p
->flags
&= ~TERMP_UNDER
;
305 const struct man_node
*nn
;
308 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
309 p
->flags
|= i
% 2 ? TERMP_BOLD
: TERMP_UNDER
;
311 p
->flags
|= TERMP_NOSPACE
;
312 print_node(p
, mt
, nn
, m
);
313 p
->flags
&= i
% 2 ? ~TERMP_BOLD
: ~TERMP_UNDER
;
323 const struct man_node
*nn
;
326 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
328 p
->flags
|= TERMP_BOLD
;
330 p
->flags
|= TERMP_NOSPACE
;
331 print_node(p
, mt
, nn
, m
);
333 p
->flags
&= ~TERMP_BOLD
;
343 const struct man_node
*nn
;
346 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
348 p
->flags
|= TERMP_UNDER
;
350 p
->flags
|= TERMP_NOSPACE
;
351 print_node(p
, mt
, nn
, m
);
353 p
->flags
&= ~TERMP_UNDER
;
363 const struct man_node
*nn
;
366 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
368 p
->flags
|= TERMP_BOLD
;
370 p
->flags
|= TERMP_NOSPACE
;
371 print_node(p
, mt
, nn
, m
);
373 p
->flags
&= ~TERMP_BOLD
;
383 const struct man_node
*nn
;
386 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
387 p
->flags
|= i
% 2 ? TERMP_UNDER
: TERMP_BOLD
;
389 p
->flags
|= TERMP_NOSPACE
;
390 print_node(p
, mt
, nn
, m
);
391 p
->flags
&= i
% 2 ? ~TERMP_UNDER
: ~TERMP_BOLD
;
402 p
->flags
|= TERMP_BOLD
;
412 p
->flags
&= ~TERMP_BOLD
;
422 if (NULL
== n
->child
) {
427 len
= atoi(n
->child
->string
);
430 for (i
= 0; i
< len
; i
++)
453 const struct man_node
*nn
;
457 fmt_block_vspace(p
, n
);
460 p
->flags
|= TERMP_NOBREAK
;
461 p
->flags
|= TERMP_TWOSPACE
;
470 /* Calculate offset. */
472 if (NULL
!= (nn
= n
->parent
->head
->child
))
473 if ((ival
= arg_width(nn
)) >= 0)
479 p
->offset
= mt
->offset
;
480 p
->rmargin
= mt
->offset
+ len
;
483 mt
->lmargin
= (size_t)ival
;
500 p
->flags
&= ~TERMP_NOBREAK
;
501 p
->flags
&= ~TERMP_TWOSPACE
;
502 p
->offset
= mt
->offset
;
503 p
->rmargin
= p
->maxrmargin
;
518 mt
->lmargin
= INDENT
;
519 fmt_block_vspace(p
, n
);
522 p
->offset
= mt
->offset
;
534 const struct man_node
*nn
;
540 p
->flags
|= TERMP_NOLPAD
;
541 p
->flags
|= TERMP_NOSPACE
;
544 p
->flags
|= TERMP_NOBREAK
;
545 p
->flags
|= TERMP_TWOSPACE
;
548 fmt_block_vspace(p
, n
);
557 /* Calculate offset. */
559 if (NULL
!= (nn
= n
->parent
->head
->child
))
560 if (NULL
!= (nn
= nn
->next
)) {
561 for ( ; nn
->next
; nn
= nn
->next
)
563 if ((ival
= arg_width(nn
)) >= 0)
569 /* Handle zero-width lengths. */
573 p
->offset
= mt
->offset
;
574 p
->rmargin
= mt
->offset
+ len
;
578 /* Set the saved left-margin. */
579 mt
->lmargin
= (size_t)ival
;
581 /* Don't print the length value. */
582 for (nn
= n
->child
; nn
->next
; nn
= nn
->next
)
583 print_node(p
, mt
, nn
, m
);
586 p
->offset
= mt
->offset
+ len
;
587 p
->rmargin
= p
->maxrmargin
;
605 p
->flags
&= ~TERMP_NOBREAK
;
606 p
->flags
&= ~TERMP_TWOSPACE
;
607 p
->rmargin
= p
->maxrmargin
;
611 p
->flags
&= ~TERMP_NOLPAD
;
623 const struct man_node
*nn
;
629 p
->flags
|= TERMP_NOBREAK
;
630 p
->flags
|= TERMP_TWOSPACE
;
633 p
->flags
|= TERMP_NOLPAD
;
634 p
->flags
|= TERMP_NOSPACE
;
637 fmt_block_vspace(p
, n
);
643 len
= (size_t)mt
->lmargin
;
646 /* Calculate offset. */
648 if (NULL
!= (nn
= n
->parent
->head
->child
))
649 if (NULL
!= nn
->next
)
650 if ((ival
= arg_width(nn
)) >= 0)
655 /* Handle zero-length properly. */
659 p
->offset
= mt
->offset
;
660 p
->rmargin
= mt
->offset
+ len
;
662 /* Don't print same-line elements. */
663 for (nn
= n
->child
; nn
; nn
= nn
->next
)
664 if (nn
->line
> n
->line
)
665 print_node(p
, mt
, nn
, m
);
668 mt
->lmargin
= (size_t)ival
;
672 p
->offset
= mt
->offset
+ len
;
673 p
->rmargin
= p
->maxrmargin
;
691 p
->flags
&= ~TERMP_NOBREAK
;
692 p
->flags
&= ~TERMP_TWOSPACE
;
693 p
->rmargin
= p
->maxrmargin
;
697 p
->flags
&= ~TERMP_NOLPAD
;
712 mt
->lmargin
= INDENT
;
714 /* If following a prior empty `SS', no vspace. */
715 if (n
->prev
&& MAN_SS
== n
->prev
->tok
)
716 if (NULL
== n
->prev
->body
->child
)
723 p
->flags
|= TERMP_BOLD
;
724 p
->offset
= HALFINDENT
;
727 p
->offset
= mt
->offset
;
745 p
->flags
&= ~TERMP_BOLD
;
763 mt
->lmargin
= INDENT
;
765 /* If following a prior empty `SH', no vspace. */
766 if (n
->prev
&& MAN_SH
== n
->prev
->tok
)
767 if (NULL
== n
->prev
->body
->child
)
772 p
->flags
|= TERMP_BOLD
;
776 p
->offset
= mt
->offset
;
794 p
->flags
&= ~TERMP_BOLD
;
809 const struct man_node
*nn
;
822 if (NULL
== (nn
= n
->parent
->head
->child
)) {
823 mt
->offset
= mt
->lmargin
+ INDENT
;
824 p
->offset
= mt
->offset
;
828 if ((ival
= arg_width(nn
)) < 0)
831 mt
->offset
= INDENT
+ (size_t)ival
;
832 p
->offset
= mt
->offset
;
845 mt
->offset
= mt
->lmargin
= INDENT
;
856 print_node(DECL_ARGS
)
864 if (0 == *n
->string
) {
869 * Note! This is hacky. Here, we recognise the `\c'
870 * escape embedded in so many -man pages. It's supposed
871 * to remove the subsequent space, so we mark NOSPACE if
872 * it's encountered in the string.
874 sz
= (int)strlen(n
->string
);
875 term_word(p
, n
->string
);
876 if (sz
>= 2 && n
->string
[sz
- 1] == 'c' &&
877 n
->string
[sz
- 2] == '\\')
878 p
->flags
|= TERMP_NOSPACE
;
879 /* FIXME: this means that macro lines are munged! */
880 if (MANT_LITERAL
& mt
->fl
) {
881 p
->flags
|= TERMP_NOSPACE
;
886 if (termacts
[n
->tok
].pre
)
887 c
= (*termacts
[n
->tok
].pre
)(p
, mt
, n
, m
);
892 print_body(p
, mt
, n
->child
, m
);
894 if (MAN_TEXT
!= n
->type
)
895 if (termacts
[n
->tok
].post
)
896 (*termacts
[n
->tok
].post
)(p
, mt
, n
, m
);
901 print_body(DECL_ARGS
)
904 print_node(p
, mt
, n
, m
);
907 print_body(p
, mt
, n
->next
, m
);
912 print_foot(struct termp
*p
, const struct man_meta
*meta
)
917 if (NULL
== (buf
= malloc(p
->rmargin
)))
920 tm
= localtime(&meta
->date
);
922 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
927 p
->flags
|= TERMP_NOSPACE
| TERMP_NOBREAK
;
928 p
->rmargin
= p
->maxrmargin
- strlen(buf
);
932 term_word(p
, meta
->source
);
937 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
938 p
->offset
= p
->rmargin
;
939 p
->rmargin
= p
->maxrmargin
;
940 p
->flags
&= ~TERMP_NOBREAK
;
950 print_head(struct termp
*p
, const struct man_meta
*meta
)
954 p
->rmargin
= p
->maxrmargin
;
957 if (NULL
== (buf
= malloc(p
->rmargin
)))
959 if (NULL
== (title
= malloc(p
->rmargin
)))
963 (void)strlcpy(buf
, meta
->vol
, p
->rmargin
);
967 (void)snprintf(title
, p
->rmargin
, "%s(%d)",
968 meta
->title
, meta
->msec
);
971 p
->rmargin
= (p
->maxrmargin
- strlen(buf
) + 1) / 2;
972 p
->flags
|= TERMP_NOBREAK
| TERMP_NOSPACE
;
977 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
978 p
->offset
= p
->rmargin
;
979 p
->rmargin
= p
->maxrmargin
- strlen(title
);
984 p
->offset
= p
->rmargin
;
985 p
->rmargin
= p
->maxrmargin
;
986 p
->flags
&= ~TERMP_NOBREAK
;
987 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
992 p
->rmargin
= p
->maxrmargin
;
994 p
->flags
&= ~TERMP_NOSPACE
;