GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/make/parsevar.c Lines: 70 79 88.6 %
Date: 2017-11-13 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
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