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