1 /* $OpenPackages$ */
2 /* $OpenBSD: parsevar.c,v 1.2 2004/04/07 13:11:36 espie Exp $ */
3 /* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */
4
5 /*
6 * Copyright (c) 2001 Marc Espie.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
21 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <ctype.h>
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "config.h"
35 #include "defines.h"
36 #include "var.h"
37 #include "varname.h"
38 #include "error.h"
39 #include "cmd_exec.h"
40 #include "parsevar.h"
41
42 static const char *find_op1(const char *);
43 static const char *find_op2(const char *);
44
45 static const char *
find_op1(const char * p)46 find_op1(const char *p)
47 {
48 for(;; p++) {
49 if (isspace(*p) || *p == '$' || *p == '\0')
50 break;
51 if (p[strspn(p, "?:!+")] == '=')
52 break;
53 if (p[0] == ':' && p[1] == 's' && p[2] == 'h')
54 break;
55 }
56 return p;
57 }
58
59 static const char *
find_op2(const char * p)60 find_op2(const char *p)
61 {
62 for(;; p++) {
63 if (isspace(*p) || *p == '$' || *p == '\0')
64 break;
65 if (p[strspn(p, "?:!+")] == '=')
66 break;
67 }
68 return p;
69 }
70
71 bool
Parse_DoVar(const char * line,GSymT * ctxt)72 Parse_DoVar(const char *line,
73 GSymT *ctxt) /* Context in which to do the assignment */
74 {
75 const char *arg;
76 char *res1 = NULL, *res2 = NULL;
77 #define VAR_NORMAL 0
78 #define VAR_SUBST 1
79 #define VAR_APPEND 2
80 #define VAR_SHELL 4
81 #define VAR_OPT 8
82 int type; /* Type of assignment */
83 struct Name name;
84
85 arg = VarName_Get(line, &name, (SymTable *)ctxt, true,
86 FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2);
87
88 while (isspace(*arg))
89 arg++;
90
91 type = VAR_NORMAL;
92
93 while (*arg != '=' && !isspace(*arg)) {
94 /* Check operator type. */
95 switch (*arg++) {
96 case '+':
97 if (type & (VAR_OPT|VAR_APPEND)) {
98 VarName_Free(&name);
99 return false;
100 }
101 type |= VAR_APPEND;
102 break;
103
104 case '?':
105 if (type & (VAR_OPT|VAR_APPEND)) {
106 VarName_Free(&name);
107 return false;
108 }
109 type |= VAR_OPT;
110 break;
111
112 case ':':
113 if (FEATURES(FEATURE_SUNSHCMD) && strncmp(arg, "sh", 2) == 0) {
114 type = VAR_SHELL;
115 arg += 2;
116 while (*arg != '=' && *arg != '\0')
117 arg++;
118 } else {
119 if (type & VAR_SUBST) {
120 VarName_Free(&name);
121 return false;
122 }
123 type |= VAR_SUBST;
124 }
125 break;
126
127 case '!':
128 if (type & VAR_SHELL) {
129 VarName_Free(&name);
130 return false;
131 }
132 type |= VAR_SHELL;
133 break;
134
135 default:
136 VarName_Free(&name);
137 return false;
138 }
139 }
140
141 /* Check validity of operator */
142 if (*arg++ != '=') {
143 VarName_Free(&name);
144 return false;
145 }
146
147 while (isspace(*arg))
148 arg++;
149 /* If the variable already has a value, we don't do anything. */
150 if ((type & VAR_OPT) && Var_Valuei(name.s, name.e) != NULL) {
151 VarName_Free(&name);
152 return true;
153 }
154 if (type & VAR_SHELL) {
155 const char *err;
156
157 if (strchr(arg, '$') != NULL) {
158 char *sub;
159 /* There's a dollar sign in the command, so perform variable
160 * expansion on the whole thing. */
161 sub = Var_Subst(arg, NULL, true);
162 res1 = Cmd_Exec(sub, &err);
163 free(sub);
164 } else
165 res1 = Cmd_Exec(arg, &err);
166
167 if (err)
168 Parse_Error(PARSE_WARNING, err, arg);
169 arg = res1;
170 }
171 if (type & VAR_SUBST) {
172 /*
173 * Allow variables in the old value to be undefined, but leave their
174 * invocation alone -- this is done by forcing oldVars to be false.
175 * XXX: This can cause recursive variables, but that's not hard to do,
176 * and this allows someone to do something like
177 *
178 * CFLAGS = $(.INCLUDES)
179 * CFLAGS := -I.. $(CFLAGS)
180 *
181 * And not get an error.
182 */
183 bool oldOldVars = oldVars;
184
185 oldVars = false;
186 /* ensure the variable is set to something to avoid `variable
187 * is recursive' errors. */
188 if (Var_Valuei(name.s, name.e) == NULL)
189 Var_Seti(name.s, name.e, "", ctxt);
190
191 res2 = Var_Subst(arg, (SymTable *)ctxt, false);
192 oldVars = oldOldVars;
193
194 arg = res2;
195 }
196
197 if (type & VAR_APPEND)
198 Var_Appendi(name.s, name.e, arg, ctxt);
199 else
200 Var_Seti(name.s, name.e, arg, ctxt);
201
202 VarName_Free(&name);
203 free(res2);
204 free(res1);
205 return true;
206 }
207
208