Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / mac_parse.c
blob468025bbc93e74dbccf69867221a58d2adb59af0
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* mac_parse 3
6 /* SUMMARY
7 /* locate macro references in string
8 /* SYNOPSIS
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);
14 /* DESCRIPTION
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
19 /* as literal text.
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.
29 /* .IP MAC_PARSE_EXPR
30 /* The content of \fIbuf\fR is a macro expression: either a
31 /* bare macro name without the preceding "$", or all the text
32 /* inside $() or ${}.
33 /* .PP
34 /* The action routine result value is the bit-wise OR of zero or more
35 /* of the following:
36 /* .IP MAC_PARSE_ERROR
37 /* A parsing error was detected.
38 /* .IP MAC_PARSE_UNDEF
39 /* A macro was expanded but not defined.
40 /* .PP
41 /* Use the constant MAC_PARSE_OK when no error was detected.
42 /* SEE ALSO
43 /* dict(3) dictionary interface.
44 /* DIAGNOSTICS
45 /* Fatal errors: out of memory. malformed macro name.
47 /* The result value is the bit-wise OR of zero or more of the
48 /* following:
49 /* .IP MAC_PARSE_ERROR
50 /* A parsing error was detected.
51 /* .IP MAC_PARSE_UNDEF
52 /* A macro was expanded but not defined.
53 /* LICENSE
54 /* .ad
55 /* .fi
56 /* The Secure Mailer license must be distributed with this software.
57 /* AUTHOR(S)
58 /* Wietse Venema
59 /* IBM T.J. Watson Research
60 /* P.O. Box 704
61 /* Yorktown Heights, NY 10598, USA
62 /*--*/
64 /* System library. */
66 #include <sys_defs.h>
67 #include <ctype.h>
69 /* Utility library. */
71 #include <msg.h>
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) \
79 do { \
80 VSTRING_TERMINATE(buf); \
81 status |= action((type), (buf), (context)); \
82 VSTRING_RESET(buf); \
83 } while(0)
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[] = ")}";
96 int level;
97 int status = 0;
99 #define SKIP(start, var, cond) \
100 for (var = start; *var && (cond); var++);
102 if (msg_verbose > 1)
103 msg_info("%s: %s", myname, value);
105 for (vp = value; *vp;) {
106 if (*vp != '$') { /* ordinary character */
107 VSTRING_ADDCH(buf, *vp);
108 vp += 1;
109 } else if (vp[1] == '$') { /* $$ becomes $ */
110 VSTRING_ADDCH(buf, *vp);
111 vp += 2;
112 } else { /* found bare $ */
113 if (VSTRING_LEN(buf) > 0)
114 MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context);
115 vp += 1;
116 pp = open_paren;
117 if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */
118 level = 1;
119 vp += 1;
120 for (ep = vp; level > 0; ep++) {
121 if (*ep == 0) {
122 msg_warn("truncated macro reference: \"%s\"", value);
123 status |= MAC_PARSE_ERROR;
124 break;
126 if (*ep == *pp)
127 level++;
128 if (*ep == close_paren[pp - open_paren])
129 level--;
131 if (status & MAC_PARSE_ERROR)
132 break;
133 vstring_strncat(buf, vp, level > 0 ? ep - vp : ep - vp - 1);
134 vp = ep;
135 } else { /* plain $x */
136 SKIP(vp, ep, ISALNUM(*ep) || *ep == '_');
137 vstring_strncat(buf, vp, ep - vp);
138 vp = ep;
140 if (VSTRING_LEN(buf) == 0) {
141 status |= MAC_PARSE_ERROR;
142 msg_warn("empty macro name: \"%s\"", value);
143 break;
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);
152 * Cleanup.
154 vstring_free(buf);
156 return (status);
159 #ifdef TEST
162 * Proof-of-concept test program. Read strings from stdin, print parsed
163 * result to stdout.
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)
171 char *type_name;
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);
183 vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf));
184 return (0);
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);
195 vstring_free(buf);
196 return (0);
199 #endif