]>
git.cameronkatri.com Git - mandoc.git/blob - man_term.c
1 /* $Id: man_term.c,v 1.30 2009/09/15 08:16:20 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>
33 #define MANT_LITERAL (1 << 0)
35 * Default amount to indent the left margin after leading text
36 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
37 * indent). This needs to be saved because `HP' and so on, if
38 * not having a specified value, must default.
40 * Note that this is the indentation AFTER the left offset, so
41 * the total offset is usually offset + lmargin.
45 * The default offset, i.e., the amount between any text and the
51 #define DECL_ARGS struct termp *p, \
53 const struct man_node *n, \
54 const struct man_meta *m
57 int (*pre
)(DECL_ARGS
);
58 void (*post
)(DECL_ARGS
);
61 static int pre_B(DECL_ARGS
);
62 static int pre_BI(DECL_ARGS
);
63 static int pre_BR(DECL_ARGS
);
64 static int pre_HP(DECL_ARGS
);
65 static int pre_I(DECL_ARGS
);
66 static int pre_IB(DECL_ARGS
);
67 static int pre_IP(DECL_ARGS
);
68 static int pre_IR(DECL_ARGS
);
69 static int pre_PP(DECL_ARGS
);
70 static int pre_RB(DECL_ARGS
);
71 static int pre_RI(DECL_ARGS
);
72 static int pre_RS(DECL_ARGS
);
73 static int pre_SH(DECL_ARGS
);
74 static int pre_SS(DECL_ARGS
);
75 static int pre_TP(DECL_ARGS
);
76 static int pre_br(DECL_ARGS
);
77 static int pre_fi(DECL_ARGS
);
78 static int pre_ign(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 { pre_ign
, NULL
}, /* DT */
124 { pre_ign
, NULL
}, /* UC */
128 extern size_t strlcpy(char *, const char *, size_t);
129 extern size_t strlcat(char *, const char *, size_t);
132 static int print_head(struct termp
*,
133 const struct man_meta
*);
134 static void print_body(DECL_ARGS
);
135 static void print_node(DECL_ARGS
);
136 static int print_foot(struct termp
*,
137 const struct man_meta
*);
138 static void fmt_block_vspace(struct termp
*,
139 const struct man_node
*);
140 static int arg_width(const struct man_node
*);
144 man_run(struct termp
*p
, const struct man
*m
)
148 if ( ! 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 if ( ! print_foot(p
, man_meta(m
)))
168 fmt_block_vspace(struct termp
*p
, const struct man_node
*n
)
175 if (MAN_SS
== n
->prev
->tok
)
177 if (MAN_SH
== n
->prev
->tok
)
185 arg_width(const struct man_node
*n
)
190 assert(MAN_TEXT
== n
->type
);
195 if (0 == (len
= (int)strlen(p
)))
198 for (i
= 0; i
< len
; i
++)
199 if ( ! isdigit((u_char
)p
[i
]))
203 if ('n' == p
[len
- 1] || 'm' == p
[len
- 1])
236 p
->bold
= p
->under
= 0;
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
++) {
292 p
->flags
|= TERMP_NOSPACE
;
293 print_node(p
, mt
, nn
, m
);
305 const struct man_node
*nn
;
308 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
314 p
->flags
|= TERMP_NOSPACE
;
315 print_node(p
, mt
, nn
, m
);
329 const struct man_node
*nn
;
332 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
336 p
->flags
|= TERMP_NOSPACE
;
337 print_node(p
, mt
, nn
, m
);
349 const struct man_node
*nn
;
352 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
356 p
->flags
|= TERMP_NOSPACE
;
357 print_node(p
, mt
, nn
, m
);
369 const struct man_node
*nn
;
372 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
376 p
->flags
|= TERMP_NOSPACE
;
377 print_node(p
, mt
, nn
, m
);
389 const struct man_node
*nn
;
392 for (i
= 0, nn
= n
->child
; nn
; nn
= nn
->next
, i
++) {
398 p
->flags
|= TERMP_NOSPACE
;
399 print_node(p
, mt
, nn
, m
);
434 if (NULL
== n
->child
) {
439 len
= atoi(n
->child
->string
);
442 for (i
= 0; i
< len
; i
++)
465 const struct man_node
*nn
;
469 fmt_block_vspace(p
, n
);
472 p
->flags
|= TERMP_NOBREAK
;
473 p
->flags
|= TERMP_TWOSPACE
;
482 /* Calculate offset. */
484 if (NULL
!= (nn
= n
->parent
->head
->child
))
485 if ((ival
= arg_width(nn
)) >= 0)
491 p
->offset
= mt
->offset
;
492 p
->rmargin
= mt
->offset
+ len
;
495 mt
->lmargin
= (size_t)ival
;
512 p
->flags
&= ~TERMP_NOBREAK
;
513 p
->flags
&= ~TERMP_TWOSPACE
;
514 p
->offset
= mt
->offset
;
515 p
->rmargin
= p
->maxrmargin
;
530 mt
->lmargin
= INDENT
;
531 fmt_block_vspace(p
, n
);
534 p
->offset
= mt
->offset
;
546 const struct man_node
*nn
;
552 p
->flags
|= TERMP_NOLPAD
;
553 p
->flags
|= TERMP_NOSPACE
;
556 p
->flags
|= TERMP_NOBREAK
;
557 p
->flags
|= TERMP_TWOSPACE
;
560 fmt_block_vspace(p
, n
);
569 /* Calculate offset. */
571 if (NULL
!= (nn
= n
->parent
->head
->child
))
572 if (NULL
!= (nn
= nn
->next
)) {
573 for ( ; nn
->next
; nn
= nn
->next
)
575 if ((ival
= arg_width(nn
)) >= 0)
581 /* Handle zero-width lengths. */
585 p
->offset
= mt
->offset
;
586 p
->rmargin
= mt
->offset
+ len
;
590 /* Set the saved left-margin. */
591 mt
->lmargin
= (size_t)ival
;
593 /* Don't print the length value. */
594 for (nn
= n
->child
; nn
->next
; nn
= nn
->next
)
595 print_node(p
, mt
, nn
, m
);
598 p
->offset
= mt
->offset
+ len
;
599 p
->rmargin
= p
->maxrmargin
;
617 p
->flags
&= ~TERMP_NOBREAK
;
618 p
->flags
&= ~TERMP_TWOSPACE
;
619 p
->rmargin
= p
->maxrmargin
;
623 p
->flags
&= ~TERMP_NOLPAD
;
635 const struct man_node
*nn
;
641 p
->flags
|= TERMP_NOBREAK
;
642 p
->flags
|= TERMP_TWOSPACE
;
645 p
->flags
|= TERMP_NOLPAD
;
646 p
->flags
|= TERMP_NOSPACE
;
649 fmt_block_vspace(p
, n
);
655 len
= (size_t)mt
->lmargin
;
658 /* Calculate offset. */
660 if (NULL
!= (nn
= n
->parent
->head
->child
))
661 if (NULL
!= nn
->next
)
662 if ((ival
= arg_width(nn
)) >= 0)
667 /* Handle zero-length properly. */
671 p
->offset
= mt
->offset
;
672 p
->rmargin
= mt
->offset
+ len
;
674 /* Don't print same-line elements. */
675 for (nn
= n
->child
; nn
; nn
= nn
->next
)
676 if (nn
->line
> n
->line
)
677 print_node(p
, mt
, nn
, m
);
680 mt
->lmargin
= (size_t)ival
;
684 p
->offset
= mt
->offset
+ len
;
685 p
->rmargin
= p
->maxrmargin
;
703 p
->flags
&= ~TERMP_NOBREAK
;
704 p
->flags
&= ~TERMP_TWOSPACE
;
705 p
->rmargin
= p
->maxrmargin
;
709 p
->flags
&= ~TERMP_NOLPAD
;
724 mt
->lmargin
= INDENT
;
726 /* If following a prior empty `SS', no vspace. */
727 if (n
->prev
&& MAN_SS
== n
->prev
->tok
)
728 if (NULL
== n
->prev
->body
->child
)
736 p
->offset
= HALFINDENT
;
739 p
->offset
= mt
->offset
;
775 mt
->lmargin
= INDENT
;
777 /* If following a prior empty `SH', no vspace. */
778 if (n
->prev
&& MAN_SH
== n
->prev
->tok
)
779 if (NULL
== n
->prev
->body
->child
)
788 p
->offset
= mt
->offset
;
821 const struct man_node
*nn
;
834 if (NULL
== (nn
= n
->parent
->head
->child
)) {
835 mt
->offset
= mt
->lmargin
+ INDENT
;
836 p
->offset
= mt
->offset
;
840 if ((ival
= arg_width(nn
)) < 0)
843 mt
->offset
= INDENT
+ (size_t)ival
;
844 p
->offset
= mt
->offset
;
857 mt
->offset
= mt
->lmargin
= INDENT
;
868 print_node(DECL_ARGS
)
876 if (0 == *n
->string
) {
881 * Note! This is hacky. Here, we recognise the `\c'
882 * escape embedded in so many -man pages. It's supposed
883 * to remove the subsequent space, so we mark NOSPACE if
884 * it's encountered in the string.
886 sz
= (int)strlen(n
->string
);
887 term_word(p
, n
->string
);
888 if (sz
>= 2 && n
->string
[sz
- 1] == 'c' &&
889 n
->string
[sz
- 2] == '\\')
890 p
->flags
|= TERMP_NOSPACE
;
891 /* FIXME: this means that macro lines are munged! */
892 if (MANT_LITERAL
& mt
->fl
) {
893 p
->flags
|= TERMP_NOSPACE
;
898 if (termacts
[n
->tok
].pre
)
899 c
= (*termacts
[n
->tok
].pre
)(p
, mt
, n
, m
);
904 print_body(p
, mt
, n
->child
, m
);
906 if (MAN_TEXT
!= n
->type
)
907 if (termacts
[n
->tok
].post
)
908 (*termacts
[n
->tok
].post
)(p
, mt
, n
, m
);
913 print_body(DECL_ARGS
)
916 print_node(p
, mt
, n
, m
);
919 print_body(p
, mt
, n
->next
, m
);
924 print_foot(struct termp
*p
, const struct man_meta
*meta
)
929 if (NULL
== (buf
= malloc(p
->rmargin
)))
932 tm
= localtime(&meta
->date
);
934 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
939 p
->flags
|= TERMP_NOSPACE
| TERMP_NOBREAK
;
940 p
->rmargin
= p
->maxrmargin
- strlen(buf
);
944 term_word(p
, meta
->source
);
949 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
950 p
->offset
= p
->rmargin
;
951 p
->rmargin
= p
->maxrmargin
;
952 p
->flags
&= ~TERMP_NOBREAK
;
963 print_head(struct termp
*p
, const struct man_meta
*meta
)
967 p
->rmargin
= p
->maxrmargin
;
970 if (NULL
== (buf
= malloc(p
->rmargin
)))
972 if (NULL
== (title
= malloc(p
->rmargin
)))
976 (void)strlcpy(buf
, meta
->vol
, p
->rmargin
);
980 (void)snprintf(title
, p
->rmargin
, "%s(%d)",
981 meta
->title
, meta
->msec
);
984 p
->rmargin
= (p
->maxrmargin
- strlen(buf
) + 1) / 2;
985 p
->flags
|= TERMP_NOBREAK
| TERMP_NOSPACE
;
990 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
991 p
->offset
= p
->rmargin
;
992 p
->rmargin
= p
->maxrmargin
- strlen(title
);
997 p
->offset
= p
->rmargin
;
998 p
->rmargin
= p
->maxrmargin
;
999 p
->flags
&= ~TERMP_NOBREAK
;
1000 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
1002 term_word(p
, title
);
1005 p
->rmargin
= p
->maxrmargin
;
1007 p
->flags
&= ~TERMP_NOSPACE
;