1 |
|
|
/* $OpenBSD: parsevar.c,v 1.15 2013/11/22 15:47:35 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 |
|
1498430 |
{ |
48 |
|
1318031 |
for(;; p++) { |
49 |
✓✓✓✓ ✓✓ |
1498430 |
if (ISSPACE(*p) || *p == '$' || *p == '\0') |
50 |
|
|
break; |
51 |
✓✓ |
1437273 |
if (p[strspn(p, "?:!+")] == '=') |
52 |
|
119242 |
break; |
53 |
✓✓✗✓ ✗✗ |
1318031 |
if (p[0] == ':' && p[1] == 's' && p[2] == 'h') |
54 |
|
|
break; |
55 |
|
1318031 |
} |
56 |
|
180399 |
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 |
|
179278 |
{ |
74 |
|
|
const char *arg; |
75 |
|
179278 |
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 |
|
|
int type; |
83 |
|
|
struct Name name; |
84 |
|
|
|
85 |
|
179278 |
arg = VarName_Get(line, &name, NULL, true, |
86 |
|
|
FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2); |
87 |
|
|
|
88 |
✓✓ |
586544 |
while (ISSPACE(*arg)) |
89 |
|
24355 |
arg++; |
90 |
|
|
|
91 |
|
179278 |
type = VAR_NORMAL; |
92 |
|
|
|
93 |
✓✓ |
456091 |
while (*arg != '=') { |
94 |
|
|
/* Check operator type. */ |
95 |
✓✓✓✓ ✓ |
153111 |
switch (*arg++) { |
96 |
|
|
case '+': |
97 |
✗✓ |
8464 |
if (type & (VAR_OPT|VAR_APPEND)) |
98 |
|
|
type = VAR_INVALID; |
99 |
|
|
else |
100 |
|
8464 |
type |= VAR_APPEND; |
101 |
|
|
break; |
102 |
|
|
|
103 |
|
|
case '?': |
104 |
✗✓ |
89049 |
if (type & (VAR_OPT|VAR_APPEND)) |
105 |
|
|
type = VAR_INVALID; |
106 |
|
|
else |
107 |
|
89049 |
type |= VAR_OPT; |
108 |
|
|
break; |
109 |
|
|
|
110 |
|
|
case ':': |
111 |
✗✓ |
10 |
if (FEATURES(FEATURE_SUNSHCMD) && |
112 |
|
|
strncmp(arg, "sh", 2) == 0) { |
113 |
|
|
type = VAR_SHELL; |
114 |
|
|
arg += 2; |
115 |
|
|
while (*arg != '=' && *arg != '\0') |
116 |
|
|
arg++; |
117 |
|
|
} else { |
118 |
✗✓ |
10 |
if (type & VAR_SUBST) |
119 |
|
|
type = VAR_INVALID; |
120 |
|
|
else |
121 |
|
10 |
type |= VAR_SUBST; |
122 |
|
|
} |
123 |
|
|
break; |
124 |
|
|
|
125 |
|
|
case '!': |
126 |
✗✓ |
12 |
if (type & VAR_SHELL) |
127 |
|
|
type = VAR_INVALID; |
128 |
|
|
else |
129 |
|
12 |
type |= VAR_SHELL; |
130 |
|
|
break; |
131 |
|
|
|
132 |
|
|
default: |
133 |
|
55576 |
type = VAR_INVALID; |
134 |
|
|
break; |
135 |
|
|
} |
136 |
✓✓ |
153111 |
if (type == VAR_INVALID) { |
137 |
|
55576 |
VarName_Free(&name); |
138 |
|
55576 |
return false; |
139 |
|
|
} |
140 |
|
|
} |
141 |
|
|
|
142 |
|
123702 |
arg++; |
143 |
✓✓ |
628886 |
while (ISSPACE(*arg)) |
144 |
|
128890 |
arg++; |
145 |
|
|
/* If the variable already has a value, we don't do anything. */ |
146 |
✓✓✓✓
|
123702 |
if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) { |
147 |
|
3968 |
VarName_Free(&name); |
148 |
|
3968 |
return true; |
149 |
|
|
} |
150 |
✓✓ |
119734 |
if (type & VAR_SHELL) { |
151 |
|
|
char *err; |
152 |
|
|
|
153 |
✓✗ |
12 |
if (strchr(arg, '$') != NULL) { |
154 |
|
|
char *sub; |
155 |
|
|
/* There's a dollar sign in the command, so perform |
156 |
|
|
* variable expansion on the whole thing. */ |
157 |
|
12 |
sub = Var_Subst(arg, NULL, true); |
158 |
|
12 |
res1 = Cmd_Exec(sub, &err); |
159 |
|
12 |
free(sub); |
160 |
|
|
} else |
161 |
|
|
res1 = Cmd_Exec(arg, &err); |
162 |
|
|
|
163 |
✗✓ |
12 |
if (err) |
164 |
|
|
Parse_Error(PARSE_WARNING, err, arg); |
165 |
|
12 |
arg = res1; |
166 |
|
|
} |
167 |
✓✓ |
119734 |
if (type & VAR_SUBST) { |
168 |
|
|
/* |
169 |
|
|
* Allow variables in the old value to be undefined, but leave |
170 |
|
|
* their invocation alone -- this is done by forcing |
171 |
|
|
* errorIsOkay to be false. |
172 |
|
|
* XXX: This can cause recursive variables, but that's not |
173 |
|
|
* hard to do, and this allows someone to do something like |
174 |
|
|
* |
175 |
|
|
* CFLAGS = $(.INCLUDES) |
176 |
|
|
* CFLAGS := -I.. $(CFLAGS) |
177 |
|
|
* |
178 |
|
|
* And not get an error. |
179 |
|
|
*/ |
180 |
|
10 |
bool saved = errorIsOkay; |
181 |
|
|
|
182 |
|
10 |
errorIsOkay = false; |
183 |
|
|
/* ensure the variable is set to something to avoid `variable |
184 |
|
|
* is recursive' errors. */ |
185 |
✗✓ |
10 |
if (!Var_Definedi(name.s, name.e)) |
186 |
|
|
Var_Seti_with_ctxt(name.s, name.e, "", ctxt); |
187 |
|
|
|
188 |
|
10 |
res2 = Var_Subst(arg, NULL, false); |
189 |
|
10 |
errorIsOkay = saved; |
190 |
|
|
|
191 |
|
10 |
arg = res2; |
192 |
|
|
} |
193 |
|
|
|
194 |
✓✓ |
119734 |
if (type & VAR_APPEND) |
195 |
|
8464 |
Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt); |
196 |
|
|
else |
197 |
|
111270 |
Var_Seti_with_ctxt(name.s, name.e, arg, ctxt); |
198 |
|
|
|
199 |
|
119734 |
VarName_Free(&name); |
200 |
|
119734 |
free(res2); |
201 |
|
119734 |
free(res1); |
202 |
|
119734 |
return true; |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
bool |
206 |
|
|
Parse_As_Var_Assignment(const char *line) |
207 |
|
176106 |
{ |
208 |
|
176106 |
return parse_variable_assignment(line, VAR_GLOBAL); |
209 |
|
|
} |
210 |
|
|
|
211 |
|
|
bool |
212 |
|
|
Parse_CmdlineVar(const char *line) |
213 |
|
3172 |
{ |
214 |
|
|
bool result; |
215 |
|
3172 |
bool saved = errorIsOkay; |
216 |
|
|
|
217 |
|
3172 |
errorIsOkay = false; |
218 |
|
3172 |
result = parse_variable_assignment(line, VAR_CMD); |
219 |
|
3172 |
errorIsOkay = saved; |
220 |
|
3172 |
return result; |
221 |
|
|
} |
222 |
|
|
|