5
/* locate macro references in string
7
/* #include <mac_parse.h>
9
/* int mac_parse(string, action, context)
10
/* const char *string;
11
/* int (*action)(int type, VSTRING *buf, char *context);
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
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 text in \fIbuf\fR is literal text.
27
/* .IP MAC_PARSE_VARNAME
28
/* The text in \fIbuf\fR is a macro expression.
30
/* The action routine result value is the bit-wise OR of zero or more
32
/* .IP MAC_PARSE_ERROR
33
/* A parsing error was detected.
34
/* .IP MAC_PARSE_UNDEF
35
/* A macro was expanded but not defined.
37
/* Use the constant MAC_PARSE_OK when no error was detected.
39
/* dict(3) dictionary interface.
41
/* Fatal errors: out of memory. malformed macro name.
43
/* The result value is the bit-wise OR of zero or more of the
45
/* .IP MAC_PARSE_ERROR
46
/* A parsing error was detected.
47
/* .IP MAC_PARSE_UNDEF
48
/* A macro was expanded but not defined.
52
/* The Secure Mailer license must be distributed with this software.
55
/* IBM T.J. Watson Research
57
/* Yorktown Heights, NY 10598, USA
65
/* Utility library. */
68
#include <mac_parse.h>
71
* Helper macro for consistency. Null-terminate the temporary buffer,
72
* execute the action, and reset the temporary buffer for re-use.
74
#define MAC_PARSE_ACTION(status, type, buf, context) \
76
VSTRING_TERMINATE(buf); \
77
status |= action(type, buf, context); \
81
/* mac_parse - split string into literal text and macro references */
83
int mac_parse(const char *value, MAC_PARSE_FN action, char *context)
85
char *myname = "mac_parse";
86
VSTRING *buf = vstring_alloc(1); /* result buffer */
87
const char *vp; /* value pointer */
88
const char *pp; /* open_paren pointer */
89
const char *ep; /* string end pointer */
90
static char open_paren[] = "({";
91
static char close_paren[] = ")}";
95
#define SKIP(start, var, cond) \
96
for (var = start; *var && (cond); var++);
99
msg_info("%s: %s", myname, value);
101
for (vp = value; *vp;) {
102
if (*vp != '$') { /* ordinary character */
103
VSTRING_ADDCH(buf, *vp);
105
} else if (vp[1] == '$') { /* $$ becomes $ */
106
VSTRING_ADDCH(buf, *vp);
108
} else { /* found bare $ */
109
if (VSTRING_LEN(buf) > 0)
110
MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context);
113
if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */
116
for (ep = vp; level > 0; ep++) {
118
msg_warn("truncated macro reference: \"%s\"", value);
119
status |= MAC_PARSE_ERROR;
124
if (*ep == close_paren[pp - open_paren])
127
if (status & MAC_PARSE_ERROR)
129
vstring_strncat(buf, vp, level > 0 ? ep - vp : ep - vp - 1);
131
} else { /* plain $x */
132
SKIP(vp, ep, ISALNUM(*ep) || *ep == '_');
133
vstring_strncat(buf, vp, ep - vp);
136
if (VSTRING_LEN(buf) == 0) {
137
status |= MAC_PARSE_ERROR;
138
msg_warn("empty macro name: \"%s\"", value);
141
MAC_PARSE_ACTION(status, MAC_PARSE_VARNAME, buf, context);
144
if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0)
145
MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context);
158
* Proof-of-concept test program. Read strings from stdin, print parsed
161
#include <vstring_vstream.h>
163
/* mac_parse_print - print parse tree */
165
static void mac_parse_print(int type, VSTRING *buf, char *unused_context)
170
case MAC_PARSE_VARNAME:
171
type_name = "MAC_PARSE_VARNAME";
173
case MAC_PARSE_LITERAL:
174
type_name = "MAC_PARSE_LITERAL";
177
msg_panic("unknown token type %d", type);
179
vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf));
182
int main(int unused_argc, char **unused_argv)
184
VSTRING *buf = vstring_alloc(1);
186
while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
187
mac_parse(vstring_str(buf), mac_parse_print, (char *) 0);
188
vstream_fflush(VSTREAM_OUT);