7 /* locate macro references in string
9 /* #include <mac_parse.h>
11 /* int mac_parse(string, action, context)
12 /* const char *string;
13 /* int (*action)(int type, VSTRING *buf, char *context);
15 /* This module recognizes macro expressions in null-terminated
16 /* strings. Macro expressions have the form $name, $(text) or
17 /* ${text}. A macro name consists of alphanumerics and/or
18 /* underscore. Text other than macro expressions is treated
21 /* mac_parse() breaks up its string argument into macro references
22 /* and other text, and invokes the \fIaction\fR routine for each item
23 /* found. With each action routine call, the \fItype\fR argument
24 /* indicates what was found, \fIbuf\fR contains a copy of the text
25 /* found, and \fIcontext\fR is passed on unmodified from the caller.
26 /* The application is at liberty to clobber \fIbuf\fR.
27 /* .IP MAC_PARSE_LITERAL
28 /* The content of \fIbuf\fR is literal text.
30 /* The content of \fIbuf\fR is a macro expression: either a
31 /* bare macro name without the preceding "$", or all the text
34 /* The action routine result value is the bit-wise OR of zero or more
36 /* .IP MAC_PARSE_ERROR
37 /* A parsing error was detected.
38 /* .IP MAC_PARSE_UNDEF
39 /* A macro was expanded but not defined.
41 /* Use the constant MAC_PARSE_OK when no error was detected.
43 /* dict(3) dictionary interface.
45 /* Fatal errors: out of memory. malformed macro name.
47 /* The result value is the bit-wise OR of zero or more of the
49 /* .IP MAC_PARSE_ERROR
50 /* A parsing error was detected.
51 /* .IP MAC_PARSE_UNDEF
52 /* A macro was expanded but not defined.
56 /* The Secure Mailer license must be distributed with this software.
59 /* IBM T.J. Watson Research
61 /* Yorktown Heights, NY 10598, USA
69 /* Utility library. */
72 #include <mac_parse.h>
75 * Helper macro for consistency. Null-terminate the temporary buffer,
76 * execute the action, and reset the temporary buffer for re-use.
78 #define MAC_PARSE_ACTION(status, type, buf, context) \
80 VSTRING_TERMINATE(buf); \
81 status |= action((type), (buf), (context)); \
85 /* mac_parse - split string into literal text and macro references */
87 int mac_parse(const char *value
, MAC_PARSE_FN action
, char *context
)
89 const char *myname
= "mac_parse";
90 VSTRING
*buf
= vstring_alloc(1); /* result buffer */
91 const char *vp
; /* value pointer */
92 const char *pp
; /* open_paren pointer */
93 const char *ep
; /* string end pointer */
94 static char open_paren
[] = "({";
95 static char close_paren
[] = ")}";
99 #define SKIP(start, var, cond) \
100 for (var = start; *var && (cond); var++);
103 msg_info("%s: %s", myname
, value
);
105 for (vp
= value
; *vp
;) {
106 if (*vp
!= '$') { /* ordinary character */
107 VSTRING_ADDCH(buf
, *vp
);
109 } else if (vp
[1] == '$') { /* $$ becomes $ */
110 VSTRING_ADDCH(buf
, *vp
);
112 } else { /* found bare $ */
113 if (VSTRING_LEN(buf
) > 0)
114 MAC_PARSE_ACTION(status
, MAC_PARSE_LITERAL
, buf
, context
);
117 if (*vp
== *pp
|| *vp
== *++pp
) { /* ${x} or $(x) */
120 for (ep
= vp
; level
> 0; ep
++) {
122 msg_warn("truncated macro reference: \"%s\"", value
);
123 status
|= MAC_PARSE_ERROR
;
128 if (*ep
== close_paren
[pp
- open_paren
])
131 if (status
& MAC_PARSE_ERROR
)
133 vstring_strncat(buf
, vp
, level
> 0 ? ep
- vp
: ep
- vp
- 1);
135 } else { /* plain $x */
136 SKIP(vp
, ep
, ISALNUM(*ep
) || *ep
== '_');
137 vstring_strncat(buf
, vp
, ep
- vp
);
140 if (VSTRING_LEN(buf
) == 0) {
141 status
|= MAC_PARSE_ERROR
;
142 msg_warn("empty macro name: \"%s\"", value
);
145 MAC_PARSE_ACTION(status
, MAC_PARSE_EXPR
, buf
, context
);
148 if (VSTRING_LEN(buf
) > 0 && (status
& MAC_PARSE_ERROR
) == 0)
149 MAC_PARSE_ACTION(status
, MAC_PARSE_LITERAL
, buf
, context
);
162 * Proof-of-concept test program. Read strings from stdin, print parsed
165 #include <vstring_vstream.h>
167 /* mac_parse_print - print parse tree */
169 static int mac_parse_print(int type
, VSTRING
*buf
, char *unused_context
)
175 type_name
= "MAC_PARSE_EXPR";
177 case MAC_PARSE_LITERAL
:
178 type_name
= "MAC_PARSE_LITERAL";
181 msg_panic("unknown token type %d", type
);
183 vstream_printf("%s \"%s\"\n", type_name
, vstring_str(buf
));
187 int main(int unused_argc
, char **unused_argv
)
189 VSTRING
*buf
= vstring_alloc(1);
191 while (vstring_fgets_nonl(buf
, VSTREAM_IN
)) {
192 mac_parse(vstring_str(buf
), mac_parse_print
, (char *) 0);
193 vstream_fflush(VSTREAM_OUT
);