]>
git.cameronkatri.com Git - mandoc.git/blob - term.c
1 /* $Id: term.c,v 1.10 2009/02/23 09:33:34 kristaps Exp $ */
3 * Copyright (c) 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
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
28 * Performs actions on nodes of the abstract syntax tree. Both pre- and
29 * post-fix operations are defined here.
32 /* FIXME: indent/tab. */
35 #define TTYPE_CMD_FLAG 1
36 #define TTYPE_CMD_ARG 2
37 #define TTYPE_SECTION 3
38 #define TTYPE_FUNC_DECL 4
39 #define TTYPE_VAR_DECL 5
40 #define TTYPE_FUNC_TYPE 6
41 #define TTYPE_FUNC_NAME 7
42 #define TTYPE_FUNC_ARG 8
44 #define TTYPE_SSECTION 10
49 * These define "styles" for element types, like command arguments or
50 * executable names. This is useful when multiple macros must decorate
51 * the same thing (like .Ex -std cmd and .Nm cmd).
54 const int ttypes
[TTYPE_NMAX
] = {
55 TERMP_BOLD
, /* TTYPE_PROG */
56 TERMP_BOLD
, /* TTYPE_CMD_FLAG */
57 TERMP_UNDERLINE
, /* TTYPE_CMD_ARG */
58 TERMP_BOLD
, /* TTYPE_SECTION */
59 TERMP_BOLD
, /* TTYPE_FUNC_DECL */
60 TERMP_UNDERLINE
, /* TTYPE_VAR_DECL */
61 TERMP_UNDERLINE
, /* TTYPE_FUNC_TYPE */
62 TERMP_BOLD
, /* TTYPE_FUNC_NAME */
63 TERMP_UNDERLINE
, /* TTYPE_FUNC_ARG */
64 TERMP_UNDERLINE
, /* TTYPE_LINK */
65 TERMP_BOLD
, /* TTYPE_SSECTION */
66 TERMP_UNDERLINE
/* TTYPE_FILE */
69 static int arg_hasattr(int, size_t,
70 const struct mdoc_arg
*);
71 static int arg_getattr(int, size_t,
72 const struct mdoc_arg
*);
73 static size_t arg_offset(const char *);
76 * What follows describes prefix and postfix operations for the abstract
77 * syntax tree descent.
82 const struct mdoc_meta *meta, \
83 const struct mdoc_node *node
85 #define DECL_PRE(name) \
86 static int name##_pre(DECL_ARGS)
87 #define DECL_POST(name) \
88 static void name##_post(DECL_ARGS)
89 #define DECL_PREPOST(name) \
93 DECL_PREPOST(termp_aq
);
94 DECL_PREPOST(termp_ar
);
95 DECL_PREPOST(termp_d1
);
96 DECL_PREPOST(termp_dq
);
97 DECL_PREPOST(termp_fa
);
98 DECL_PREPOST(termp_fd
);
99 DECL_PREPOST(termp_fl
);
100 DECL_PREPOST(termp_fn
);
101 DECL_PREPOST(termp_ft
);
102 DECL_PREPOST(termp_it
);
103 DECL_PREPOST(termp_nm
);
104 DECL_PREPOST(termp_op
);
105 DECL_PREPOST(termp_pa
);
106 DECL_PREPOST(termp_pf
);
107 DECL_PREPOST(termp_qq
);
108 DECL_PREPOST(termp_sh
);
109 DECL_PREPOST(termp_ss
);
110 DECL_PREPOST(termp_sq
);
111 DECL_PREPOST(termp_sx
);
112 DECL_PREPOST(termp_va
);
113 DECL_PREPOST(termp_vt
);
128 const struct termact __termacts
[MDOC_MAX
] = {
129 { NULL
, NULL
}, /* \" */
130 { NULL
, NULL
}, /* Dd */
131 { NULL
, NULL
}, /* Dt */
132 { NULL
, NULL
}, /* Os */
133 { termp_sh_pre
, termp_sh_post
}, /* Sh */
134 { termp_ss_pre
, termp_ss_post
}, /* Ss */
135 { termp_pp_pre
, NULL
}, /* Pp */
136 { termp_d1_pre
, termp_d1_post
}, /* D1 */
137 { NULL
, NULL
}, /* Dl */
138 { termp_bd_pre
, NULL
}, /* Bd */
139 { NULL
, NULL
}, /* Ed */
140 { NULL
, termp_bl_post
}, /* Bl */
141 { NULL
, NULL
}, /* El */
142 { termp_it_pre
, termp_it_post
}, /* It */
143 { NULL
, NULL
}, /* Ad */
144 { NULL
, NULL
}, /* An */
145 { termp_ar_pre
, termp_ar_post
}, /* Ar */
146 { NULL
, NULL
}, /* Cd */
147 { NULL
, NULL
}, /* Cm */
148 { NULL
, NULL
}, /* Dv */
149 { NULL
, NULL
}, /* Er */
150 { NULL
, NULL
}, /* Ev */
151 { termp_ex_pre
, NULL
}, /* Ex */
152 { termp_fa_pre
, termp_fa_post
}, /* Fa */
153 { termp_fd_pre
, termp_fd_post
}, /* Fd */
154 { termp_fl_pre
, termp_fl_post
}, /* Fl */
155 { termp_fn_pre
, termp_fn_post
}, /* Fn */
156 { termp_ft_pre
, termp_ft_post
}, /* Ft */
157 { NULL
, NULL
}, /* Ic */
158 { NULL
, NULL
}, /* In */
159 { NULL
, NULL
}, /* Li */
160 { termp_nd_pre
, NULL
}, /* Nd */
161 { termp_nm_pre
, termp_nm_post
}, /* Nm */
162 { termp_op_pre
, termp_op_post
}, /* Op */
163 { NULL
, NULL
}, /* Ot */
164 { termp_pa_pre
, termp_pa_post
}, /* Pa */
165 { NULL
, NULL
}, /* Rv */
166 { NULL
, NULL
}, /* St */
167 { termp_va_pre
, termp_va_post
}, /* Va */
168 { termp_vt_pre
, termp_vt_post
}, /* Vt */
169 { termp_xr_pre
, NULL
}, /* Xr */
170 { NULL
, NULL
}, /* %A */
171 { NULL
, NULL
}, /* %B */
172 { NULL
, NULL
}, /* %D */
173 { NULL
, NULL
}, /* %I */
174 { NULL
, NULL
}, /* %J */
175 { NULL
, NULL
}, /* %N */
176 { NULL
, NULL
}, /* %O */
177 { NULL
, NULL
}, /* %P */
178 { NULL
, NULL
}, /* %R */
179 { NULL
, NULL
}, /* %T */
180 { NULL
, NULL
}, /* %V */
181 { NULL
, NULL
}, /* Ac */
182 { NULL
, NULL
}, /* Ao */
183 { termp_aq_pre
, termp_aq_post
}, /* Aq */
184 { NULL
, NULL
}, /* At */
185 { NULL
, NULL
}, /* Bc */
186 { NULL
, NULL
}, /* Bf */
187 { NULL
, NULL
}, /* Bo */
188 { NULL
, NULL
}, /* Bq */
189 { NULL
, NULL
}, /* Bsx */
190 { termp_bx_pre
, NULL
}, /* Bx */
191 { NULL
, NULL
}, /* Db */
192 { NULL
, NULL
}, /* Dc */
193 { NULL
, NULL
}, /* Do */
194 { termp_dq_pre
, termp_dq_post
}, /* Dq */
195 { NULL
, NULL
}, /* Ec */
196 { NULL
, NULL
}, /* Ef */
197 { NULL
, NULL
}, /* Em */
198 { NULL
, NULL
}, /* Eo */
199 { NULL
, NULL
}, /* Fx */
200 { NULL
, NULL
}, /* Ms */
201 { NULL
, NULL
}, /* No */
202 { termp_ns_pre
, NULL
}, /* Ns */
203 { termp_nx_pre
, NULL
}, /* Nx */
204 { termp_ox_pre
, NULL
}, /* Ox */
205 { NULL
, NULL
}, /* Pc */
206 { termp_pf_pre
, termp_pf_post
}, /* Pf */
207 { NULL
, NULL
}, /* Po */
208 { NULL
, NULL
}, /* Pq */
209 { NULL
, NULL
}, /* Qc */
210 { NULL
, NULL
}, /* Ql */
211 { NULL
, NULL
}, /* Qo */
212 { termp_qq_pre
, termp_qq_post
}, /* Qq */
213 { NULL
, NULL
}, /* Re */
214 { NULL
, NULL
}, /* Rs */
215 { NULL
, NULL
}, /* Sc */
216 { NULL
, NULL
}, /* So */
217 { termp_sq_pre
, termp_sq_post
}, /* Sq */
218 { NULL
, NULL
}, /* Sm */
219 { termp_sx_pre
, termp_sx_post
}, /* Sx */
220 { NULL
, NULL
}, /* Sy */
221 { NULL
, NULL
}, /* Tn */
222 { NULL
, NULL
}, /* Ux */
223 { NULL
, NULL
}, /* Xc */
224 { NULL
, NULL
}, /* Xo */
225 { NULL
, NULL
}, /* Fo */
226 { NULL
, NULL
}, /* Fc */
227 { NULL
, NULL
}, /* Oo */
228 { NULL
, NULL
}, /* Oc */
229 { NULL
, NULL
}, /* Bk */
230 { NULL
, NULL
}, /* Ek */
231 { NULL
, NULL
}, /* Bt */
232 { NULL
, NULL
}, /* Hf */
233 { NULL
, NULL
}, /* Fr */
234 { termp_ud_pre
, NULL
}, /* Ud */
237 const struct termact
*termacts
= __termacts
;
241 arg_offset(const char *v
)
243 if (0 == strcmp(v
, "indent"))
245 if (0 == strcmp(v
, "indent-two"))
254 arg_hasattr(int arg
, size_t argc
, const struct mdoc_arg
*argv
)
257 return(-1 != arg_getattr(arg
, argc
, argv
));
262 arg_getattr(int arg
, size_t argc
, const struct mdoc_arg
*argv
)
266 for (i
= 0; i
< (int)argc
; i
++)
267 if (argv
[i
].arg
== arg
)
275 termp_dq_pre(DECL_ARGS
)
278 if (MDOC_BODY
!= node
->type
)
282 p
->flags
|= TERMP_NOSPACE
;
289 termp_dq_post(DECL_ARGS
)
292 if (MDOC_BODY
!= node
->type
)
295 p
->flags
|= TERMP_NOSPACE
;
302 termp_it_post(DECL_ARGS
)
304 const struct mdoc_node
*n
, *it
;
305 const struct mdoc_block
*bl
;
310 * This (and termp_it_pre()) are the most complicated functions
311 * here. They must account for a considerable number of
312 * switches that completely change the output behaviour, like
313 * -tag versus -column. Yech.
316 switch (node
->type
) {
326 assert(MDOC_BLOCK
== it
->type
);
327 assert(MDOC_It
== it
->tok
);
330 assert(MDOC_BODY
== n
->type
);
331 assert(MDOC_Bl
== n
->tok
);
335 /* If `-tag', adjust our margins accordingly. */
337 if (arg_hasattr(MDOC_Tag
, bl
->argc
, bl
->argv
)) {
338 i
= arg_getattr(MDOC_Width
, bl
->argc
, bl
->argv
);
340 assert(1 == bl
->argv
[i
].sz
);
341 width
= strlen(*bl
->argv
[i
].value
); /* XXX */
343 if (MDOC_HEAD
== node
->type
) {
345 /* FIXME: nested lists. */
346 p
->rmargin
= p
->maxrmargin
;
347 p
->flags
&= ~TERMP_NOBREAK
;
350 p
->offset
-= width
+ 1;
351 p
->flags
&= ~TERMP_NOLPAD
;
356 if (arg_hasattr(MDOC_Ohang
, bl
->argc
, bl
->argv
)) {
357 i
= arg_getattr(MDOC_Offset
, bl
->argc
, bl
->argv
);
360 assert(1 == bl
->argv
[i
].sz
);
361 width
= arg_offset(*bl
->argv
[i
].value
);
373 termp_it_pre(DECL_ARGS
)
375 const struct mdoc_node
*n
, *it
;
376 const struct mdoc_block
*bl
;
381 * Also see termp_it_post() for general comments.
384 switch (node
->type
) {
397 assert(MDOC_BLOCK
== it
->type
);
398 assert(MDOC_It
== it
->tok
);
401 assert(MDOC_BODY
== n
->type
);
402 assert(MDOC_Bl
== n
->tok
);
406 /* If `-compact', don't assert vertical space. */
408 if (MDOC_BLOCK
== node
->type
) {
409 if (arg_hasattr(MDOC_Compact
, bl
->argc
, bl
->argv
))
416 assert(MDOC_HEAD
== node
->type
417 || MDOC_BODY
== node
->type
);
419 /* If `-tag', adjust our margins accordingly. */
421 if (arg_hasattr(MDOC_Tag
, bl
->argc
, bl
->argv
)) {
422 i
= arg_getattr(MDOC_Width
, bl
->argc
, bl
->argv
);
423 assert(i
>= 0); /* XXX */
424 assert(1 == bl
->argv
[i
].sz
);
425 width
= strlen(*bl
->argv
[i
].value
); /* XXX */
427 /* FIXME: nested lists. */
429 if (MDOC_HEAD
== node
->type
) {
430 p
->flags
|= TERMP_NOBREAK
;
431 p
->flags
|= TERMP_NOSPACE
;
432 p
->rmargin
= p
->offset
+ width
;
434 p
->flags
|= TERMP_NOSPACE
;
435 p
->flags
|= TERMP_NOLPAD
;
436 p
->offset
+= width
+ 1;
441 /* If `-ohang', adjust left-margin. */
443 if (arg_hasattr(MDOC_Ohang
, bl
->argc
, bl
->argv
)) {
445 i
= arg_getattr(MDOC_Offset
, bl
->argc
, bl
->argv
);
447 assert(1 == bl
->argv
[i
].sz
);
448 width
= arg_offset(*bl
->argv
[i
].value
);
451 p
->flags
|= TERMP_NOSPACE
;
462 termp_nm_post(DECL_ARGS
)
465 p
->flags
&= ~ttypes
[TTYPE_PROG
];
471 termp_fl_post(DECL_ARGS
)
474 p
->flags
&= ~ttypes
[TTYPE_CMD_FLAG
];
480 termp_ar_pre(DECL_ARGS
)
483 p
->flags
|= ttypes
[TTYPE_CMD_ARG
];
484 if (NULL
== node
->child
)
492 termp_nm_pre(DECL_ARGS
)
495 p
->flags
|= ttypes
[TTYPE_PROG
];
496 if (NULL
== node
->child
)
504 termp_ns_pre(DECL_ARGS
)
507 p
->flags
|= TERMP_NOSPACE
;
514 termp_pp_pre(DECL_ARGS
)
524 termp_ar_post(DECL_ARGS
)
527 p
->flags
&= ~ttypes
[TTYPE_CMD_ARG
];
533 termp_ex_pre(DECL_ARGS
)
537 i
= arg_getattr(MDOC_Std
, node
->data
.elem
.argc
,
538 node
->data
.elem
.argv
);
542 p
->flags
|= ttypes
[TTYPE_PROG
];
543 word(p
, *node
->data
.elem
.argv
[i
].value
);
544 p
->flags
&= ~ttypes
[TTYPE_PROG
];
545 word(p
, "utility exits 0 on success, and >0 if an error occurs.");
553 termp_nd_pre(DECL_ARGS
)
563 termp_bl_post(DECL_ARGS
)
566 if (MDOC_BLOCK
== node
->type
)
573 termp_op_post(DECL_ARGS
)
576 if (MDOC_BODY
!= node
->type
)
578 p
->flags
|= TERMP_NOSPACE
;
585 termp_sh_post(DECL_ARGS
)
588 switch (node
->type
) {
590 p
->flags
&= ~ttypes
[TTYPE_SECTION
];
605 termp_xr_pre(DECL_ARGS
)
607 const struct mdoc_node
*n
;
612 assert(MDOC_TEXT
== n
->type
);
613 word(p
, n
->data
.text
.string
);
615 if (NULL
== (n
= n
->next
))
618 assert(MDOC_TEXT
== n
->type
);
619 p
->flags
|= TERMP_NOSPACE
;
621 p
->flags
|= TERMP_NOSPACE
;
622 word(p
, n
->data
.text
.string
);
623 p
->flags
|= TERMP_NOSPACE
;
632 termp_vt_pre(DECL_ARGS
)
635 /* FIXME: this can be "type name". */
636 p
->flags
|= ttypes
[TTYPE_VAR_DECL
];
643 termp_vt_post(DECL_ARGS
)
646 p
->flags
&= ~ttypes
[TTYPE_VAR_DECL
];
647 if (node
->sec
== SEC_SYNOPSIS
)
654 termp_fd_pre(DECL_ARGS
)
658 * FIXME: this naming is bad. This value is used, in general,
659 * for the #include header or other preprocessor statement.
661 p
->flags
|= ttypes
[TTYPE_FUNC_DECL
];
668 termp_fd_post(DECL_ARGS
)
671 p
->flags
&= ~ttypes
[TTYPE_FUNC_DECL
];
672 if (node
->sec
== SEC_SYNOPSIS
)
680 termp_sh_pre(DECL_ARGS
)
683 switch (node
->type
) {
686 p
->flags
|= ttypes
[TTYPE_SECTION
];
700 termp_op_pre(DECL_ARGS
)
703 switch (node
->type
) {
706 p
->flags
|= TERMP_NOSPACE
;
717 termp_ud_pre(DECL_ARGS
)
720 word(p
, "currently under development.");
727 termp_fl_pre(DECL_ARGS
)
730 p
->flags
|= ttypes
[TTYPE_CMD_FLAG
];
732 p
->flags
|= TERMP_NOSPACE
;
739 termp_d1_pre(DECL_ARGS
)
742 if (MDOC_BODY
!= node
->type
)
752 termp_d1_post(DECL_ARGS
)
755 if (MDOC_BODY
!= node
->type
)
764 termp_aq_pre(DECL_ARGS
)
767 if (MDOC_BODY
!= node
->type
)
770 p
->flags
|= TERMP_NOSPACE
;
777 termp_aq_post(DECL_ARGS
)
780 if (MDOC_BODY
!= node
->type
)
782 p
->flags
|= TERMP_NOSPACE
;
789 termp_ft_pre(DECL_ARGS
)
792 p
->flags
|= ttypes
[TTYPE_FUNC_TYPE
];
799 termp_ft_post(DECL_ARGS
)
802 p
->flags
&= ~ttypes
[TTYPE_FUNC_TYPE
];
803 if (node
->sec
== SEC_SYNOPSIS
)
811 termp_fn_pre(DECL_ARGS
)
813 const struct mdoc_node
*n
;
816 assert(MDOC_TEXT
== node
->child
->type
);
818 /* FIXME: can be "type funcname" "type varname"... */
820 p
->flags
|= ttypes
[TTYPE_FUNC_NAME
];
821 word(p
, node
->child
->data
.text
.string
);
822 p
->flags
&= ~ttypes
[TTYPE_FUNC_NAME
];
824 p
->flags
|= TERMP_NOSPACE
;
827 p
->flags
|= TERMP_NOSPACE
;
828 for (n
= node
->child
->next
; n
; n
= n
->next
) {
829 assert(MDOC_TEXT
== n
->type
);
830 p
->flags
|= ttypes
[TTYPE_FUNC_ARG
];
831 word(p
, n
->data
.text
.string
);
832 p
->flags
&= ~ttypes
[TTYPE_FUNC_ARG
];
837 p
->flags
|= TERMP_NOSPACE
;
840 if (SEC_SYNOPSIS
== node
->sec
)
849 termp_fn_post(DECL_ARGS
)
852 if (node
->sec
== SEC_SYNOPSIS
)
860 termp_sx_pre(DECL_ARGS
)
863 p
->flags
|= ttypes
[TTYPE_LINK
];
870 termp_sx_post(DECL_ARGS
)
873 p
->flags
&= ~ttypes
[TTYPE_LINK
];
879 termp_fa_pre(DECL_ARGS
)
882 p
->flags
|= ttypes
[TTYPE_FUNC_ARG
];
889 termp_fa_post(DECL_ARGS
)
892 p
->flags
&= ~ttypes
[TTYPE_FUNC_ARG
];
898 termp_va_pre(DECL_ARGS
)
901 p
->flags
|= ttypes
[TTYPE_VAR_DECL
];
908 termp_va_post(DECL_ARGS
)
911 p
->flags
&= ~ttypes
[TTYPE_VAR_DECL
];
917 termp_bd_pre(DECL_ARGS
)
919 const struct mdoc_block
*bl
;
920 const struct mdoc_node
*n
;
922 if (MDOC_BLOCK
== node
->type
) {
925 } else if (MDOC_BODY
!= node
->type
)
928 assert(MDOC_BLOCK
== node
->parent
->type
);
930 bl
= &node
->parent
->data
.block
;
931 if ( ! arg_hasattr(MDOC_Literal
, bl
->argc
, bl
->argv
))
934 p
->flags
|= TERMP_LITERAL
;
936 for (n
= node
->child
; n
; n
= n
->next
) {
937 assert(MDOC_TEXT
== n
->type
); /* FIXME */
938 if ((*n
->data
.text
.string
)) {
939 word(p
, n
->data
.text
.string
);
946 p
->flags
&= ~TERMP_LITERAL
;
953 termp_qq_pre(DECL_ARGS
)
956 if (MDOC_BODY
!= node
->type
)
959 p
->flags
|= TERMP_NOSPACE
;
966 termp_qq_post(DECL_ARGS
)
969 if (MDOC_BODY
!= node
->type
)
971 p
->flags
|= TERMP_NOSPACE
;
978 termp_bx_pre(DECL_ARGS
)
988 termp_ox_pre(DECL_ARGS
)
998 termp_nx_pre(DECL_ARGS
)
1008 termp_sq_pre(DECL_ARGS
)
1011 if (MDOC_BODY
!= node
->type
)
1014 p
->flags
|= TERMP_NOSPACE
;
1021 termp_sq_post(DECL_ARGS
)
1024 if (MDOC_BODY
!= node
->type
)
1026 p
->flags
|= TERMP_NOSPACE
;
1033 termp_pf_pre(DECL_ARGS
)
1036 p
->flags
|= TERMP_IGNDELIM
;
1043 termp_pf_post(DECL_ARGS
)
1046 p
->flags
&= ~TERMP_IGNDELIM
;
1047 p
->flags
|= TERMP_NOSPACE
;
1053 termp_ss_pre(DECL_ARGS
)
1056 switch (node
->type
) {
1059 p
->flags
|= ttypes
[TTYPE_SSECTION
];
1060 p
->offset
= INDENT
/ 2;
1072 termp_ss_post(DECL_ARGS
)
1075 switch (node
->type
) {
1077 p
->flags
&= ~ttypes
[TTYPE_SSECTION
];
1089 termp_pa_pre(DECL_ARGS
)
1092 p
->flags
|= ttypes
[TTYPE_FILE
];
1099 termp_pa_post(DECL_ARGS
)
1102 p
->flags
&= ~ttypes
[TTYPE_FILE
];