/src/postfix/postfix/src/util/mac_parse.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*++ |
2 | | /* NAME |
3 | | /* mac_parse 3 |
4 | | /* SUMMARY |
5 | | /* locate macro references in string |
6 | | /* SYNOPSIS |
7 | | /* #include <mac_parse.h> |
8 | | /* |
9 | | /* int mac_parse(string, action, context) |
10 | | /* const char *string; |
11 | | /* int (*action)(int type, VSTRING *buf, void *context); |
12 | | /* DESCRIPTION |
13 | | /* This module recognizes macro expressions in null-terminated |
14 | | /* strings. Macro expressions have the form $name, $(text) or |
15 | | /* ${text}. A macro name consists of alphanumerics and/or |
16 | | /* underscore. Text other than macro expressions is treated |
17 | | /* as literal text. |
18 | | /* |
19 | | /* mac_parse() breaks up its string argument into macro references |
20 | | /* and other text, and invokes the \fIaction\fR routine for each item |
21 | | /* found. With each action routine call, the \fItype\fR argument |
22 | | /* indicates what was found, \fIbuf\fR contains a copy of the text |
23 | | /* found, and \fIcontext\fR is passed on unmodified from the caller. |
24 | | /* The application is at liberty to clobber \fIbuf\fR. |
25 | | /* .IP MAC_PARSE_LITERAL |
26 | | /* The content of \fIbuf\fR is literal text. |
27 | | /* .IP MAC_PARSE_EXPR |
28 | | /* The content of \fIbuf\fR is a macro expression: either a |
29 | | /* bare macro name without the preceding "$", or all the text |
30 | | /* inside $() or ${}. |
31 | | /* .PP |
32 | | /* The action routine result value is the bit-wise OR of zero or more |
33 | | /* of the following: |
34 | | /* .IP MAC_PARSE_ERROR |
35 | | /* A parsing error was detected. |
36 | | /* .IP MAC_PARSE_UNDEF |
37 | | /* A macro was expanded but not defined. |
38 | | /* .PP |
39 | | /* Use the constant MAC_PARSE_OK when no error was detected. |
40 | | /* SEE ALSO |
41 | | /* dict(3) dictionary interface. |
42 | | /* DIAGNOSTICS |
43 | | /* Fatal errors: out of memory. malformed macro name. |
44 | | /* |
45 | | /* The result value is the bit-wise OR of zero or more of the |
46 | | /* following: |
47 | | /* .IP MAC_PARSE_ERROR |
48 | | /* A parsing error was detected. |
49 | | /* .IP MAC_PARSE_UNDEF |
50 | | /* A macro was expanded but not defined. |
51 | | /* LICENSE |
52 | | /* .ad |
53 | | /* .fi |
54 | | /* The Secure Mailer license must be distributed with this software. |
55 | | /* AUTHOR(S) |
56 | | /* Wietse Venema |
57 | | /* IBM T.J. Watson Research |
58 | | /* P.O. Box 704 |
59 | | /* Yorktown Heights, NY 10598, USA |
60 | | /*--*/ |
61 | | |
62 | | /* System library. */ |
63 | | |
64 | | #include <sys_defs.h> |
65 | | #include <ctype.h> |
66 | | |
67 | | /* Utility library. */ |
68 | | |
69 | | #include <msg.h> |
70 | | #include <mac_parse.h> |
71 | | |
72 | | /* |
73 | | * Helper macro for consistency. Null-terminate the temporary buffer, |
74 | | * execute the action, and reset the temporary buffer for re-use. |
75 | | */ |
76 | | #define MAC_PARSE_ACTION(status, type, buf, context) \ |
77 | 0 | do { \ |
78 | 0 | VSTRING_TERMINATE(buf); \ |
79 | 0 | status |= action((type), (buf), (context)); \ |
80 | 0 | VSTRING_RESET(buf); \ |
81 | 0 | } while(0) |
82 | | |
83 | | /* mac_parse - split string into literal text and macro references */ |
84 | | |
85 | | int mac_parse(const char *value, MAC_PARSE_FN action, void *context) |
86 | 0 | { |
87 | 0 | const char *myname = "mac_parse"; |
88 | 0 | VSTRING *buf = vstring_alloc(1); /* result buffer */ |
89 | 0 | const char *vp; /* value pointer */ |
90 | 0 | const char *pp; /* open_paren pointer */ |
91 | 0 | const char *ep; /* string end pointer */ |
92 | 0 | static char open_paren[] = "({"; |
93 | 0 | static char close_paren[] = ")}"; |
94 | 0 | int level; |
95 | 0 | int status = 0; |
96 | |
|
97 | 0 | #define SKIP(start, var, cond) do { \ |
98 | 0 | for (var = start; *var && (cond); var++) \ |
99 | 0 | /* void */; \ |
100 | 0 | } while (0) |
101 | |
|
102 | 0 | if (msg_verbose > 1) |
103 | 0 | msg_info("%s: %s", myname, value); |
104 | |
|
105 | 0 | for (vp = value; *vp;) { |
106 | 0 | if (*vp != '$') { /* ordinary character */ |
107 | 0 | VSTRING_ADDCH(buf, *vp); |
108 | 0 | vp += 1; |
109 | 0 | } else if (vp[1] == '$') { /* $$ becomes $ */ |
110 | 0 | VSTRING_ADDCH(buf, *vp); |
111 | 0 | vp += 2; |
112 | 0 | } else { /* found bare $ */ |
113 | 0 | if (VSTRING_LEN(buf) > 0) |
114 | 0 | MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); |
115 | 0 | vp += 1; |
116 | 0 | pp = open_paren; |
117 | 0 | if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */ |
118 | 0 | level = 1; |
119 | 0 | vp += 1; |
120 | 0 | for (ep = vp; level > 0; ep++) { |
121 | 0 | if (*ep == 0) { |
122 | 0 | msg_warn("truncated macro reference: \"%s\"", value); |
123 | 0 | status |= MAC_PARSE_ERROR; |
124 | 0 | break; |
125 | 0 | } |
126 | 0 | if (*ep == *pp) |
127 | 0 | level++; |
128 | 0 | if (*ep == close_paren[pp - open_paren]) |
129 | 0 | level--; |
130 | 0 | } |
131 | 0 | if (status & MAC_PARSE_ERROR) |
132 | 0 | break; |
133 | 0 | vstring_strncat(buf, vp, level > 0 ? ep - vp : ep - vp - 1); |
134 | 0 | vp = ep; |
135 | 0 | } else { /* plain $x */ |
136 | 0 | SKIP(vp, ep, ISALNUM(*ep) || *ep == '_'); |
137 | 0 | vstring_strncat(buf, vp, ep - vp); |
138 | 0 | vp = ep; |
139 | 0 | } |
140 | 0 | if (VSTRING_LEN(buf) == 0) { |
141 | 0 | status |= MAC_PARSE_ERROR; |
142 | 0 | msg_warn("empty macro name: \"%s\"", value); |
143 | 0 | break; |
144 | 0 | } |
145 | 0 | MAC_PARSE_ACTION(status, MAC_PARSE_EXPR, buf, context); |
146 | 0 | } |
147 | 0 | } |
148 | 0 | if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0) |
149 | 0 | MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); |
150 | | |
151 | | /* |
152 | | * Cleanup. |
153 | | */ |
154 | 0 | vstring_free(buf); |
155 | |
|
156 | 0 | return (status); |
157 | 0 | } |
158 | | |
159 | | #ifdef TEST |
160 | | |
161 | | /* |
162 | | * Proof-of-concept test program. Read strings from stdin, print parsed |
163 | | * result to stdout. |
164 | | */ |
165 | | #include <vstring_vstream.h> |
166 | | |
167 | | /* mac_parse_print - print parse tree */ |
168 | | |
169 | | static int mac_parse_print(int type, VSTRING *buf, void *unused_context) |
170 | | { |
171 | | char *type_name; |
172 | | |
173 | | switch (type) { |
174 | | case MAC_PARSE_EXPR: |
175 | | type_name = "MAC_PARSE_EXPR"; |
176 | | break; |
177 | | case MAC_PARSE_LITERAL: |
178 | | type_name = "MAC_PARSE_LITERAL"; |
179 | | break; |
180 | | default: |
181 | | msg_panic("unknown token type %d", type); |
182 | | } |
183 | | vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf)); |
184 | | return (0); |
185 | | } |
186 | | |
187 | | int main(int unused_argc, char **unused_argv) |
188 | | { |
189 | | VSTRING *buf = vstring_alloc(1); |
190 | | |
191 | | while (vstring_fgets_nonl(buf, VSTREAM_IN)) { |
192 | | mac_parse(vstring_str(buf), mac_parse_print, (void *) 0); |
193 | | vstream_fflush(VSTREAM_OUT); |
194 | | } |
195 | | vstring_free(buf); |
196 | | return (0); |
197 | | } |
198 | | |
199 | | #endif |