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