GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/make/parsevar.c Lines: 70 79 88.6 %
Date: 2017-11-07 Branches: 52 67 77.6 %

Line Branch Exec Source
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
367636246
	for(;; p++) {
49

1019708174
		if (ISSPACE(*p) || *p == '$' || *p == '\0')
50
			break;
51
329740521
		if (p[strspn(p, "?:!+")] == '=')
52
			break;
53

326917990
		if (p[0] == ':' && p[1] == 's' && p[2] == 'h')
54
			break;
55
	}
56
25768171
	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
51042916
	struct Name name;
86
87
25521458
	arg = VarName_Get(line, &name, NULL, true,
88
	    FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2);
89
90
56744268
	while (ISSPACE(*arg))
91
2850676
		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
63855066
	while (*arg != '=') {
99
		/* Check operator type.  */
100

23861348
		switch (*arg++) {
101
		case '+':
102
6483388
			if (type & (VAR_OPT|VAR_LAZYSHELL|VAR_APPEND))
103
				type = VAR_INVALID;
104
			else
105
6483388
				type |= VAR_APPEND;
106
			break;
107
108
		case '?':
109
6304372
			if (type & (VAR_OPT|VAR_APPEND))
110
				type = VAR_INVALID;
111
			else
112
6304372
				type |= VAR_OPT;
113
			break;
114
115
		case ':':
116
10869
			if (FEATURES(FEATURE_SUNSHCMD) &&
117
10869
			    strncmp(arg, "sh", 2) == 0) {
118
				type = VAR_SUNSHELL;
119
				arg += 2;
120
				while (*arg != '=' && *arg != '\0')
121
					arg++;
122
			} else {
123
10869
				if (type & VAR_SUBST)
124
					type = VAR_INVALID;
125
				else
126
10869
					type |= VAR_SUBST;
127
			}
128
			break;
129
130
		case '!':
131
13521
			if (type & VAR_SHELL) {
132
227
				if (type & (VAR_APPEND))
133
					type = VAR_INVALID;
134
				else
135
					type = VAR_LAZYSHELL;
136
13294
			} else if (type & (VAR_LAZYSHELL|VAR_SUNSHELL))
137
				type = VAR_INVALID;
138
			else
139
13294
				type |= VAR_SHELL;
140
			break;
141
142
		default:
143
			type = VAR_INVALID;
144
11049198
			break;
145
		}
146
23861348
		if (type == VAR_INVALID) {
147
11049198
			VarName_Free(&name);
148
11049198
			return false;
149
		}
150
	}
151
152
	arg++;
153
39797172
	while (ISSPACE(*arg))
154
12662456
		arg++;
155
	/* If the variable already has a value, we don't do anything.  */
156

20776632
	if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) {
157
1861763
		VarName_Free(&name);
158
1861763
		return true;
159
	}
160
12610497
	if (type & (VAR_SHELL|VAR_SUNSHELL)) {
161
13067
		char *err;
162
163
13067
		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
12339
			sub = Var_Subst(arg, NULL, true);
168
12339
			res1 = Cmd_Exec(sub, &err);
169
12339
			free(sub);
170
12339
		} else
171
728
			res1 = Cmd_Exec(arg, &err);
172
173
13067
		if (err)
174
			Parse_Error(PARSE_WARNING, err, arg);
175
		arg = res1;
176
13067
	}
177
12610497
	if (type & VAR_LAZYSHELL) {
178
227
		if (strchr(arg, '$') != NULL) {
179
			/* There's a dollar sign in the command, so perform
180
			 * variable expansion on the whole thing. */
181
227
			arg = Var_Subst(arg, NULL, true);
182
227
		}
183
	}
184
12610497
	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
10869
		bool   saved = errorIsOkay;
198
199
10869
		errorIsOkay = false;
200
		/* ensure the variable is set to something to avoid `variable
201
		 * is recursive' errors.  */
202
10869
		if (!Var_Definedi(name.s, name.e))
203
406
			Var_Seti_with_ctxt(name.s, name.e, "", ctxt);
204
205
10869
		res2 = Var_Subst(arg, NULL, false);
206
10869
		errorIsOkay = saved;
207
208
		arg = res2;
209
10869
	}
210
211
12610497
	if (type & VAR_APPEND)
212
6483388
		Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt);
213
	else
214
6127109
		Var_Seti_with_ctxt(name.s, name.e, arg, ctxt);
215
12610497
	if (type & VAR_LAZYSHELL)
216
227
		Var_Mark(name.s, name.e, VAR_EXEC_LATER);
217
218
12610497
	VarName_Free(&name);
219
12610497
	free(res2);
220
12610497
	free(res1);
221
12610497
	return true;
222
25521458
}
223
224
bool
225
Parse_As_Var_Assignment(const char *line)
226
{
227
50746442
	return parse_variable_assignment(line, VAR_GLOBAL);
228
}
229
230
bool
231
Parse_CmdlineVar(const char *line)
232
{
233
	bool result;
234
296474
	bool saved = errorIsOkay;
235
236
148237
	errorIsOkay = false;
237
148237
	result = parse_variable_assignment(line, VAR_CMD);
238
148237
	errorIsOkay = saved;
239
148237
	return result;
240
}
241