9 /* #include <mac_expand.h>
11 /* int mac_expand(result, pattern, flags, filter, lookup, context)
13 /* const char *pattern;
15 /* const char *filter;
16 /* const char *lookup(const char *key, int mode, char *context)
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.
42 /* Storage for the result of expansion. The result is truncated
45 /* The string to be expanded.
47 /* Bit-wise OR of zero or more of the following:
49 /* .IP MAC_EXP_FLAG_RECURSE
50 /* Expand macros in lookup results. This should never be done with
51 /* data whose origin is untrusted.
53 /* The constant MAC_EXP_FLAG_NONE specifies a manifest null value.
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.
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.
66 /* Caller context that is passed on to the attribute lookup routine.
68 /* Fatal errors: out of memory. Warnings: syntax errors, unreasonable
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.
78 /* mac_parse(3) locate macro references in string.
82 /* The Secure Mailer license must be distributed with this software.
85 /* IBM T.J. Watson Research
87 /* Yorktown Heights, NY 10598, USA
96 /* Utility library. */
100 #include <mymalloc.h>
101 #include <mac_parse.h>
102 #include <mac_expand.h>
105 * Little helper structure.
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 */
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
;
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
)
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
;
155 if (ch
== '?' || ch
== ':') {
157 lookup_mode
= MAC_EXP_MODE_TEST
;
160 if (!ISALNUM(ch
) && ch
!= '_') {
161 msg_warn("macro name syntax error: \"%s\"", vstring_str(buf
));
162 mc
->status
|= MAC_PARSE_ERROR
;
168 * Look up the named parameter.
170 text
= mc
->lookup(vstring_str(buf
), lookup_mode
, mc
->context
);
173 * Perform the requested substitution.
177 if (text
!= 0 && *text
!= 0)
178 mac_parse(cp
, mac_expand_callback
, (char *) mc
);
181 if (text
== 0 || *text
== 0)
182 mac_parse(cp
, mac_expand_callback
, (char *) mc
);
186 mc
->status
|= MAC_PARSE_UNDEF
;
187 } else if (*text
== 0) {
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
);
193 len
= VSTRING_LEN(mc
->result
);
194 vstring_strcat(mc
->result
, text
);
196 cp
= vstring_str(mc
->result
) + len
;
197 while (*(cp
+= strspn(cp
, mc
->filter
)))
209 vstring_strcat(mc
->result
, vstring_str(buf
));
217 /* mac_expand - expand $name instances */
219 int mac_expand(VSTRING
*result
, const char *pattern
, int flags
,
221 MAC_EXP_LOOKUP_FN lookup
, char *context
)
227 * Bundle up the request and do the substitutions.
233 mc
.context
= context
;
236 VSTRING_RESET(result
);
237 status
= mac_parse(pattern
, mac_expand_callback
, (char *) &mc
);
238 VSTRING_TERMINATE(result
);
246 * This code certainly deserves a stand-alone test program.
249 #include <stringops.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);
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)
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)
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");
312 vstring_free(result
);