xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/mac_parse.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: mac_parse.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	mac_parse 3
6 /* SUMMARY
7 /*	locate macro references in string
8 /* SYNOPSIS
9 /*	#include <mac_parse.h>
10 /*
11 /*	int	mac_parse(string, action, context)
12 /*	const char *string;
13 /*	int	(*action)(int type, VSTRING *buf, void *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.
20 /*
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.
46 /*
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 /*--*/
63 
64 /* System library. */
65 
66 #include <sys_defs.h>
67 #include <ctype.h>
68 
69 /* Utility library. */
70 
71 #include <msg.h>
72 #include <mac_parse.h>
73 
74  /*
75   * Helper macro for consistency. Null-terminate the temporary buffer,
76   * execute the action, and reset the temporary buffer for re-use.
77   */
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)
84 
85 /* mac_parse - split string into literal text and macro references */
86 
mac_parse(const char * value,MAC_PARSE_FN action,void * context)87 int     mac_parse(const char *value, MAC_PARSE_FN action, void *context)
88 {
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;
98 
99 #define SKIP(start, var, cond) do { \
100         for (var = start; *var && (cond); var++) \
101 	    /* void */; \
102     } while (0)
103 
104     if (msg_verbose > 1)
105 	msg_info("%s: %s", myname, value);
106 
107     for (vp = value; *vp;) {
108 	if (*vp != '$') {			/* ordinary character */
109 	    VSTRING_ADDCH(buf, *vp);
110 	    vp += 1;
111 	} else if (vp[1] == '$') {		/* $$ becomes $ */
112 	    VSTRING_ADDCH(buf, *vp);
113 	    vp += 2;
114 	} else {				/* found bare $ */
115 	    if (VSTRING_LEN(buf) > 0)
116 		MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context);
117 	    vp += 1;
118 	    pp = open_paren;
119 	    if (*vp == *pp || *vp == *++pp) {	/* ${x} or $(x) */
120 		level = 1;
121 		vp += 1;
122 		for (ep = vp; level > 0; ep++) {
123 		    if (*ep == 0) {
124 			msg_warn("truncated macro reference: \"%s\"", value);
125 			status |= MAC_PARSE_ERROR;
126 			break;
127 		    }
128 		    if (*ep == *pp)
129 			level++;
130 		    if (*ep == close_paren[pp - open_paren])
131 			level--;
132 		}
133 		if (status & MAC_PARSE_ERROR)
134 		    break;
135 		vstring_strncat(buf, vp, level > 0 ? ep - vp : ep - vp - 1);
136 		vp = ep;
137 	    } else {				/* plain $x */
138 		SKIP(vp, ep, ISALNUM(*ep) || *ep == '_');
139 		vstring_strncat(buf, vp, ep - vp);
140 		vp = ep;
141 	    }
142 	    if (VSTRING_LEN(buf) == 0) {
143 		status |= MAC_PARSE_ERROR;
144 		msg_warn("empty macro name: \"%s\"", value);
145 		break;
146 	    }
147 	    MAC_PARSE_ACTION(status, MAC_PARSE_EXPR, buf, context);
148 	}
149     }
150     if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0)
151 	MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context);
152 
153     /*
154      * Cleanup.
155      */
156     vstring_free(buf);
157 
158     return (status);
159 }
160 
161 #ifdef TEST
162 
163  /*
164   * Proof-of-concept test program. Read strings from stdin, print parsed
165   * result to stdout.
166   */
167 #include <vstring_vstream.h>
168 
169 /* mac_parse_print - print parse tree */
170 
mac_parse_print(int type,VSTRING * buf,void * unused_context)171 static int mac_parse_print(int type, VSTRING *buf, void *unused_context)
172 {
173     char   *type_name;
174 
175     switch (type) {
176     case MAC_PARSE_EXPR:
177 	type_name = "MAC_PARSE_EXPR";
178 	break;
179     case MAC_PARSE_LITERAL:
180 	type_name = "MAC_PARSE_LITERAL";
181 	break;
182     default:
183 	msg_panic("unknown token type %d", type);
184     }
185     vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf));
186     return (0);
187 }
188 
main(int unused_argc,char ** unused_argv)189 int     main(int unused_argc, char **unused_argv)
190 {
191     VSTRING *buf = vstring_alloc(1);
192 
193     while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
194 	mac_parse(vstring_str(buf), mac_parse_print, (void *) 0);
195 	vstream_fflush(VSTREAM_OUT);
196     }
197     vstring_free(buf);
198     return (0);
199 }
200 
201 #endif
202