1 |
|
|
/* $OpenBSD: parsevar.c,v 1.16 2016/10/23 14:54:14 espie Exp $ */ |
2 |
|
|
/* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 2001 Marc Espie. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS |
17 |
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 |
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 |
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD |
20 |
|
|
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 |
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 |
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 |
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include <ctype.h> |
30 |
|
|
#include <stddef.h> |
31 |
|
|
#include <stdlib.h> |
32 |
|
|
#include <string.h> |
33 |
|
|
#include "config.h" |
34 |
|
|
#include "defines.h" |
35 |
|
|
#include "var.h" |
36 |
|
|
#include "varname.h" |
37 |
|
|
#include "error.h" |
38 |
|
|
#include "cmd_exec.h" |
39 |
|
|
#include "parsevar.h" |
40 |
|
|
|
41 |
|
|
static const char *find_op1(const char *); |
42 |
|
|
static const char *find_op2(const char *); |
43 |
|
|
static bool parse_variable_assignment(const char *, int); |
44 |
|
|
|
45 |
|
|
static const char * |
46 |
|
|
find_op1(const char *p) |
47 |
|
|
{ |
48 |
|
179393295 |
for(;; p++) { |
49 |
✓✓✓✓ ✓✓ |
496203015 |
if (ISSPACE(*p) || *p == '$' || *p == '\0') |
50 |
|
|
break; |
51 |
✓✓ |
160562480 |
if (p[strspn(p, "?:!+")] == '=') |
52 |
|
|
break; |
53 |
✓✓✗✓ ✗✗ |
158562242 |
if (p[0] == ':' && p[1] == 's' && p[2] == 'h') |
54 |
|
|
break; |
55 |
|
|
} |
56 |
|
13002153 |
return p; |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
static const char * |
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 |
|
|
static bool |
72 |
|
|
parse_variable_assignment(const char *line, int ctxt) |
73 |
|
|
{ |
74 |
|
|
const char *arg; |
75 |
|
|
char *res1 = NULL, *res2 = NULL; |
76 |
|
|
#define VAR_INVALID -1 |
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 |
|
|
#define VAR_LAZYSHELL 16 |
83 |
|
|
#define VAR_SUNSHELL 32 |
84 |
|
|
int type; |
85 |
|
25764952 |
struct Name name; |
86 |
|
|
|
87 |
|
12882476 |
arg = VarName_Get(line, &name, NULL, true, |
88 |
|
|
FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2); |
89 |
|
|
|
90 |
✓✓ |
28644358 |
while (ISSPACE(*arg)) |
91 |
|
1439703 |
arg++; |
92 |
|
|
|
93 |
|
|
type = VAR_NORMAL; |
94 |
|
|
|
95 |
|
|
/* double operators (except for :) are forbidden */ |
96 |
|
|
/* OPT and APPEND don't match */ |
97 |
|
|
/* APPEND and LAZYSHELL can't really work */ |
98 |
✓✓ |
39337152 |
while (*arg != '=') { |
99 |
|
|
/* Check operator type. */ |
100 |
✓✓✓✓ ✓ |
12051956 |
switch (*arg++) { |
101 |
|
|
case '+': |
102 |
✗✓ |
3654180 |
if (type & (VAR_OPT|VAR_LAZYSHELL|VAR_APPEND)) |
103 |
|
|
type = VAR_INVALID; |
104 |
|
|
else |
105 |
|
3654180 |
type |= VAR_APPEND; |
106 |
|
|
break; |
107 |
|
|
|
108 |
|
|
case '?': |
109 |
✗✓ |
3117521 |
if (type & (VAR_OPT|VAR_APPEND)) |
110 |
|
|
type = VAR_INVALID; |
111 |
|
|
else |
112 |
|
3117521 |
type |= VAR_OPT; |
113 |
|
|
break; |
114 |
|
|
|
115 |
|
|
case ':': |
116 |
✗✓ |
6823 |
if (FEATURES(FEATURE_SUNSHCMD) && |
117 |
|
6823 |
strncmp(arg, "sh", 2) == 0) { |
118 |
|
|
type = VAR_SUNSHELL; |
119 |
|
|
arg += 2; |
120 |
|
|
while (*arg != '=' && *arg != '\0') |
121 |
|
|
arg++; |
122 |
|
|
} else { |
123 |
✗✓ |
6823 |
if (type & VAR_SUBST) |
124 |
|
|
type = VAR_INVALID; |
125 |
|
|
else |
126 |
|
6823 |
type |= VAR_SUBST; |
127 |
|
|
} |
128 |
|
|
break; |
129 |
|
|
|
130 |
|
|
case '!': |
131 |
✓✓ |
7576 |
if (type & VAR_SHELL) { |
132 |
✗✓ |
128 |
if (type & (VAR_APPEND)) |
133 |
|
|
type = VAR_INVALID; |
134 |
|
|
else |
135 |
|
|
type = VAR_LAZYSHELL; |
136 |
✗✓ |
7448 |
} else if (type & (VAR_LAZYSHELL|VAR_SUNSHELL)) |
137 |
|
|
type = VAR_INVALID; |
138 |
|
|
else |
139 |
|
7448 |
type |= VAR_SHELL; |
140 |
|
|
break; |
141 |
|
|
|
142 |
|
|
default: |
143 |
|
|
type = VAR_INVALID; |
144 |
|
5265856 |
break; |
145 |
|
|
} |
146 |
✓✓ |
12051956 |
if (type == VAR_INVALID) { |
147 |
|
5265856 |
VarName_Free(&name); |
148 |
|
5265856 |
return false; |
149 |
|
|
} |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
arg++; |
153 |
✓✓ |
30328918 |
while (ISSPACE(*arg)) |
154 |
|
7547839 |
arg++; |
155 |
|
|
/* If the variable already has a value, we don't do anything. */ |
156 |
✓✓✓✓
|
10734141 |
if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) { |
157 |
|
909330 |
VarName_Free(&name); |
158 |
|
909330 |
return true; |
159 |
|
|
} |
160 |
✓✓ |
6707290 |
if (type & (VAR_SHELL|VAR_SUNSHELL)) { |
161 |
|
7320 |
char *err; |
162 |
|
|
|
163 |
✓✓ |
7320 |
if (strchr(arg, '$') != NULL) { |
164 |
|
|
char *sub; |
165 |
|
|
/* There's a dollar sign in the command, so perform |
166 |
|
|
* variable expansion on the whole thing. */ |
167 |
|
6903 |
sub = Var_Subst(arg, NULL, true); |
168 |
|
6903 |
res1 = Cmd_Exec(sub, &err); |
169 |
|
6903 |
free(sub); |
170 |
|
6903 |
} else |
171 |
|
417 |
res1 = Cmd_Exec(arg, &err); |
172 |
|
|
|
173 |
✗✓ |
7320 |
if (err) |
174 |
|
|
Parse_Error(PARSE_WARNING, err, arg); |
175 |
|
|
arg = res1; |
176 |
|
7320 |
} |
177 |
✓✓ |
6707290 |
if (type & VAR_LAZYSHELL) { |
178 |
✓✗ |
128 |
if (strchr(arg, '$') != NULL) { |
179 |
|
|
/* There's a dollar sign in the command, so perform |
180 |
|
|
* variable expansion on the whole thing. */ |
181 |
|
128 |
arg = Var_Subst(arg, NULL, true); |
182 |
|
128 |
} |
183 |
|
|
} |
184 |
✓✓ |
6707290 |
if (type & VAR_SUBST) { |
185 |
|
|
/* |
186 |
|
|
* Allow variables in the old value to be undefined, but leave |
187 |
|
|
* their invocation alone -- this is done by forcing |
188 |
|
|
* errorIsOkay to be false. |
189 |
|
|
* XXX: This can cause recursive variables, but that's not |
190 |
|
|
* hard to do, and this allows someone to do something like |
191 |
|
|
* |
192 |
|
|
* CFLAGS = $(.INCLUDES) |
193 |
|
|
* CFLAGS := -I.. $(CFLAGS) |
194 |
|
|
* |
195 |
|
|
* And not get an error. |
196 |
|
|
*/ |
197 |
|
6823 |
bool saved = errorIsOkay; |
198 |
|
|
|
199 |
|
6823 |
errorIsOkay = false; |
200 |
|
|
/* ensure the variable is set to something to avoid `variable |
201 |
|
|
* is recursive' errors. */ |
202 |
✓✓ |
6823 |
if (!Var_Definedi(name.s, name.e)) |
203 |
|
235 |
Var_Seti_with_ctxt(name.s, name.e, "", ctxt); |
204 |
|
|
|
205 |
|
6823 |
res2 = Var_Subst(arg, NULL, false); |
206 |
|
6823 |
errorIsOkay = saved; |
207 |
|
|
|
208 |
|
|
arg = res2; |
209 |
|
6823 |
} |
210 |
|
|
|
211 |
✓✓ |
6707290 |
if (type & VAR_APPEND) |
212 |
|
3654180 |
Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt); |
213 |
|
|
else |
214 |
|
3053110 |
Var_Seti_with_ctxt(name.s, name.e, arg, ctxt); |
215 |
✓✓ |
6707290 |
if (type & VAR_LAZYSHELL) |
216 |
|
128 |
Var_Mark(name.s, name.e, VAR_EXEC_LATER); |
217 |
|
|
|
218 |
|
6707290 |
VarName_Free(&name); |
219 |
|
6707290 |
free(res2); |
220 |
|
6707290 |
free(res1); |
221 |
|
6707290 |
return true; |
222 |
|
12882476 |
} |
223 |
|
|
|
224 |
|
|
bool |
225 |
|
|
Parse_As_Var_Assignment(const char *line) |
226 |
|
|
{ |
227 |
|
25615756 |
return parse_variable_assignment(line, VAR_GLOBAL); |
228 |
|
|
} |
229 |
|
|
|
230 |
|
|
bool |
231 |
|
|
Parse_CmdlineVar(const char *line) |
232 |
|
|
{ |
233 |
|
|
bool result; |
234 |
|
149196 |
bool saved = errorIsOkay; |
235 |
|
|
|
236 |
|
74598 |
errorIsOkay = false; |
237 |
|
74598 |
result = parse_variable_assignment(line, VAR_CMD); |
238 |
|
74598 |
errorIsOkay = saved; |
239 |
|
74598 |
return result; |
240 |
|
|
} |
241 |
|
|
|