Postfix3.3.1
mac_parse.c
[詳解]
1 /*++
2 /* NAME
3 /* mac_parse 3
4 /* SUMMARY
5 /* locate macro references in string
6 /* SYNOPSIS
7 /* #include <mac_parse.h>
8 /*
9 /* int mac_parse(string, action, context)
10 /* const char *string;
11 /* int (*action)(int type, VSTRING *buf, void *context);
12 /* DESCRIPTION
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
17 /* as literal text.
18 /*
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 content of \fIbuf\fR is literal text.
27 /* .IP MAC_PARSE_EXPR
28 /* The content of \fIbuf\fR is a macro expression: either a
29 /* bare macro name without the preceding "$", or all the text
30 /* inside $() or ${}.
31 /* .PP
32 /* The action routine result value is the bit-wise OR of zero or more
33 /* of the following:
34 /* .IP MAC_PARSE_ERROR
35 /* A parsing error was detected.
36 /* .IP MAC_PARSE_UNDEF
37 /* A macro was expanded but not defined.
38 /* .PP
39 /* Use the constant MAC_PARSE_OK when no error was detected.
40 /* SEE ALSO
41 /* dict(3) dictionary interface.
42 /* DIAGNOSTICS
43 /* Fatal errors: out of memory. malformed macro name.
44 /*
45 /* The result value is the bit-wise OR of zero or more of the
46 /* following:
47 /* .IP MAC_PARSE_ERROR
48 /* A parsing error was detected.
49 /* .IP MAC_PARSE_UNDEF
50 /* A macro was expanded but not defined.
51 /* LICENSE
52 /* .ad
53 /* .fi
54 /* The Secure Mailer license must be distributed with this software.
55 /* AUTHOR(S)
56 /* Wietse Venema
57 /* IBM T.J. Watson Research
58 /* P.O. Box 704
59 /* Yorktown Heights, NY 10598, USA
60 /*--*/
61 
62 /* System library. */
63 
64 #include <sys_defs.h>
65 #include <ctype.h>
66 
67 /* Utility library. */
68 
69 #include <msg.h>
70 #include <mac_parse.h>
71 
72  /*
73  * Helper macro for consistency. Null-terminate the temporary buffer,
74  * execute the action, and reset the temporary buffer for re-use.
75  */
76 #define MAC_PARSE_ACTION(status, type, buf, context) \
77  do { \
78  VSTRING_TERMINATE(buf); \
79  status |= action((type), (buf), (context)); \
80  VSTRING_RESET(buf); \
81  } while(0)
82 
83 /* mac_parse - split string into literal text and macro references */
84 
85 int mac_parse(const char *value, MAC_PARSE_FN action, void *context)
86 {
87  const char *myname = "mac_parse";
88  VSTRING *buf = vstring_alloc(1); /* result buffer */
89  const char *vp; /* value pointer */
90  const char *pp; /* open_paren pointer */
91  const char *ep; /* string end pointer */
92  static char open_paren[] = "({";
93  static char close_paren[] = ")}";
94  int level;
95  int status = 0;
96 
97 #define SKIP(start, var, cond) do { \
98  for (var = start; *var && (cond); var++) \
99  /* void */; \
100  } while (0)
101 
102  if (msg_verbose > 1)
103  msg_info("%s: %s", myname, value);
104 
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;
125  }
126  if (*ep == *pp)
127  level++;
128  if (*ep == close_paren[pp - open_paren])
129  level--;
130  }
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;
139  }
140  if (VSTRING_LEN(buf) == 0) {
141  status |= MAC_PARSE_ERROR;
142  msg_warn("empty macro name: \"%s\"", value);
143  break;
144  }
145  MAC_PARSE_ACTION(status, MAC_PARSE_EXPR, buf, context);
146  }
147  }
148  if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0)
149  MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context);
150 
151  /*
152  * Cleanup.
153  */
154  vstring_free(buf);
155 
156  return (status);
157 }
158 
159 #ifdef TEST
160 
161  /*
162  * Proof-of-concept test program. Read strings from stdin, print parsed
163  * result to stdout.
164  */
165 #include <vstring_vstream.h>
166 
167 /* mac_parse_print - print parse tree */
168 
169 static int mac_parse_print(int type, VSTRING *buf, void *unused_context)
170 {
171  char *type_name;
172 
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);
182  }
183  vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf));
184  return (0);
185 }
186 
187 int main(int unused_argc, char **unused_argv)
188 {
189  VSTRING *buf = vstring_alloc(1);
190 
191  while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
192  mac_parse(vstring_str(buf), mac_parse_print, (void *) 0);
194  }
195  vstring_free(buf);
196  return (0);
197 }
198 
199 #endif
int msg_verbose
Definition: msg.c:177
#define vstring_fgets_nonl(s, p)
#define MAC_PARSE_EXPR
Definition: mac_parse.h:23
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
#define VSTREAM_OUT
Definition: vstream.h:67
int main(int argc, char **argv)
Definition: anvil.c:1010
VSTRING * vstring_strncat(VSTRING *vp, const char *src, ssize_t len)
Definition: vstring.c:471
#define VSTREAM_IN
Definition: vstream.h:66
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define ISALNUM(c)
Definition: sys_defs.h:1745
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
int mac_parse(const char *value, MAC_PARSE_FN action, void *context)
Definition: mac_parse.c:85
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MAC_PARSE_ACTION(status, type, buf, context)
Definition: mac_parse.c:76
int(* MAC_PARSE_FN)(int, VSTRING *, void *)
Definition: mac_parse.h:31
#define MAC_PARSE_LITERAL
Definition: mac_parse.h:22
#define SKIP(start, var, cond)
#define MAC_PARSE_ERROR
Definition: mac_parse.h:27
void msg_info(const char *fmt,...)
Definition: msg.c:199