Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / mac_expand.c
blobfcaa77814175b1250b738837c05d49aa6f847f79
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* mac_expand 3
6 /* SUMMARY
7 /* attribute expansion
8 /* SYNOPSIS
9 /* #include <mac_expand.h>
11 /* int mac_expand(result, pattern, flags, filter, lookup, context)
12 /* VSTRING *result;
13 /* const char *pattern;
14 /* int flags;
15 /* const char *filter;
16 /* const char *lookup(const char *key, int mode, char *context)
17 /* char *context;
18 /* DESCRIPTION
19 /* This module implements parameter-less macro expansions, both
20 /* conditional and unconditional, and both recursive and non-recursive.
22 /* In this text, an attribute is considered "undefined" when its value
23 /* is a null pointer. Otherwise, the attribute is considered "defined"
24 /* and is expected to have as value a null-terminated string.
26 /* The following expansions are implemented:
27 /* .IP "$name, ${name}, $(name)"
28 /* Unconditional expansion. If the named attribute value is non-empty, the
29 /* expansion is the value of the named attribute, optionally subjected
30 /* to further $name expansions. Otherwise, the expansion is empty.
31 /* .IP "${name?text}, $(name?text)"
32 /* Conditional expansion. If the named attribute value is non-empty, the
33 /* expansion is the given text, subjected to another iteration of
34 /* $name expansion. Otherwise, the expansion is empty.
35 /* .IP "${name:text}, $(name:text)"
36 /* Conditional expansion. If the attribute value is empty or undefined,
37 /* the expansion is the given text, subjected to another iteration
38 /* of $name expansion. Otherwise, the expansion is empty.
39 /* .PP
40 /* Arguments:
41 /* .IP result
42 /* Storage for the result of expansion. The result is truncated
43 /* upon entry.
44 /* .IP pattern
45 /* The string to be expanded.
46 /* .IP flags
47 /* Bit-wise OR of zero or more of the following:
48 /* .RS
49 /* .IP MAC_EXP_FLAG_RECURSE
50 /* Expand macros in lookup results. This should never be done with
51 /* data whose origin is untrusted.
52 /* .PP
53 /* The constant MAC_EXP_FLAG_NONE specifies a manifest null value.
54 /* .RE
55 /* .IP filter
56 /* A null pointer, or a null-terminated array of characters that
57 /* are allowed to appear in an expansion. Illegal characters are
58 /* replaced by underscores.
59 /* .IP lookup
60 /* The attribute lookup routine. Arguments are: the attribute name,
61 /* MAC_EXP_MODE_TEST to test the existence of the named attribute
62 /* or MAC_EXP_MODE_USE to use the value of the named attribute,
63 /* and the caller context that was given to mac_expand(). A null
64 /* result value means that the requested attribute was not defined.
65 /* .IP context
66 /* Caller context that is passed on to the attribute lookup routine.
67 /* DIAGNOSTICS
68 /* Fatal errors: out of memory. Warnings: syntax errors, unreasonable
69 /* macro nesting.
71 /* The result value is the binary OR of zero or more of the following:
72 /* .IP MAC_PARSE_ERROR
73 /* A syntax error was found in \fBpattern\fR, or some macro had
74 /* an unreasonable nesting depth.
75 /* .IP MAC_PARSE_UNDEF
76 /* A macro was expanded but its value not defined.
77 /* SEE ALSO
78 /* mac_parse(3) locate macro references in string.
79 /* LICENSE
80 /* .ad
81 /* .fi
82 /* The Secure Mailer license must be distributed with this software.
83 /* AUTHOR(S)
84 /* Wietse Venema
85 /* IBM T.J. Watson Research
86 /* P.O. Box 704
87 /* Yorktown Heights, NY 10598, USA
88 /*--*/
90 /* System library. */
92 #include <sys_defs.h>
93 #include <ctype.h>
94 #include <string.h>
96 /* Utility library. */
98 #include <msg.h>
99 #include <vstring.h>
100 #include <mymalloc.h>
101 #include <mac_parse.h>
102 #include <mac_expand.h>
105 * Little helper structure.
107 typedef struct {
108 VSTRING *result; /* result buffer */
109 int flags; /* features */
110 const char *filter; /* character filter */
111 MAC_EXP_LOOKUP_FN lookup; /* lookup routine */
112 char *context; /* caller context */
113 int status; /* findings */
114 int level; /* nesting level */
115 } MAC_EXP;
117 /* mac_expand_callback - callback for mac_parse */
119 static int mac_expand_callback(int type, VSTRING *buf, char *ptr)
121 MAC_EXP *mc = (MAC_EXP *) ptr;
122 int lookup_mode;
123 const char *text;
124 char *cp;
125 int ch;
126 ssize_t len;
129 * Sanity check.
131 if (mc->level++ > 100) {
132 msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf));
133 mc->status |= MAC_PARSE_ERROR;
135 if (mc->status & MAC_PARSE_ERROR)
136 return (mc->status);
139 * $Name etc. reference.
141 * In order to support expansion of lookup results, we must save the lookup
142 * result. We use the input buffer since it will not be needed anymore.
144 if (type == MAC_PARSE_EXPR) {
147 * Look for the ? or : delimiter. In case of a syntax error, return
148 * without doing damage, and issue a warning instead.
150 for (cp = vstring_str(buf); /* void */ ; cp++) {
151 if ((ch = *cp) == 0) {
152 lookup_mode = MAC_EXP_MODE_USE;
153 break;
155 if (ch == '?' || ch == ':') {
156 *cp++ = 0;
157 lookup_mode = MAC_EXP_MODE_TEST;
158 break;
160 if (!ISALNUM(ch) && ch != '_') {
161 msg_warn("macro name syntax error: \"%s\"", vstring_str(buf));
162 mc->status |= MAC_PARSE_ERROR;
163 return (mc->status);
168 * Look up the named parameter.
170 text = mc->lookup(vstring_str(buf), lookup_mode, mc->context);
173 * Perform the requested substitution.
175 switch (ch) {
176 case '?':
177 if (text != 0 && *text != 0)
178 mac_parse(cp, mac_expand_callback, (char *) mc);
179 break;
180 case ':':
181 if (text == 0 || *text == 0)
182 mac_parse(cp, mac_expand_callback, (char *) mc);
183 break;
184 default:
185 if (text == 0) {
186 mc->status |= MAC_PARSE_UNDEF;
187 } else if (*text == 0) {
188 /* void */ ;
189 } else if (mc->flags & MAC_EXP_FLAG_RECURSE) {
190 vstring_strcpy(buf, text);
191 mac_parse(vstring_str(buf), mac_expand_callback, (char *) mc);
192 } else {
193 len = VSTRING_LEN(mc->result);
194 vstring_strcat(mc->result, text);
195 if (mc->filter) {
196 cp = vstring_str(mc->result) + len;
197 while (*(cp += strspn(cp, mc->filter)))
198 *cp++ = '_';
201 break;
206 * Literal text.
208 else {
209 vstring_strcat(mc->result, vstring_str(buf));
212 mc->level--;
214 return (mc->status);
217 /* mac_expand - expand $name instances */
219 int mac_expand(VSTRING *result, const char *pattern, int flags,
220 const char *filter,
221 MAC_EXP_LOOKUP_FN lookup, char *context)
223 MAC_EXP mc;
224 int status;
227 * Bundle up the request and do the substitutions.
229 mc.result = result;
230 mc.flags = flags;
231 mc.filter = filter;
232 mc.lookup = lookup;
233 mc.context = context;
234 mc.status = 0;
235 mc.level = 0;
236 VSTRING_RESET(result);
237 status = mac_parse(pattern, mac_expand_callback, (char *) &mc);
238 VSTRING_TERMINATE(result);
240 return (status);
243 #ifdef TEST
246 * This code certainly deserves a stand-alone test program.
248 #include <stdlib.h>
249 #include <stringops.h>
250 #include <htable.h>
251 #include <vstream.h>
252 #include <vstring_vstream.h>
254 static const char *lookup(const char *name, int unused_mode, char *context)
256 HTABLE *table = (HTABLE *) context;
258 return (htable_find(table, name));
261 int main(int unused_argc, char **unused_argv)
263 VSTRING *buf = vstring_alloc(100);
264 VSTRING *result = vstring_alloc(100);
265 char *cp;
266 char *name;
267 char *value;
268 HTABLE *table;
269 int stat;
271 while (!vstream_feof(VSTREAM_IN)) {
273 table = htable_create(0);
276 * Read a block of definitions, terminated with an empty line.
278 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
279 vstream_printf("<< %s\n", vstring_str(buf));
280 vstream_fflush(VSTREAM_OUT);
281 if (VSTRING_LEN(buf) == 0)
282 break;
283 cp = vstring_str(buf);
284 name = mystrtok(&cp, " \t\r\n=");
285 value = mystrtok(&cp, " \t\r\n=");
286 htable_enter(table, name, value ? mystrdup(value) : 0);
290 * Read a block of patterns, terminated with an empty line or EOF.
292 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
293 vstream_printf("<< %s\n", vstring_str(buf));
294 vstream_fflush(VSTREAM_OUT);
295 if (VSTRING_LEN(buf) == 0)
296 break;
297 cp = vstring_str(buf);
298 VSTRING_RESET(result);
299 stat = mac_expand(result, vstring_str(buf), MAC_EXP_FLAG_NONE,
300 (char *) 0, lookup, (char *) table);
301 vstream_printf("stat=%d result=%s\n", stat, vstring_str(result));
302 vstream_fflush(VSTREAM_OUT);
304 htable_free(table, myfree);
305 vstream_printf("\n");
309 * Clean up.
311 vstring_free(buf);
312 vstring_free(result);
313 exit(0);
316 #endif