GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/make/parsevar.c Lines: 64 76 84.2 %
Date: 2016-12-06 Branches: 42 55 76.4 %

Line Branch Exec Source
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