xref: /openbsd-src/usr.bin/make/parsevar.c (revision 0eea0d082377cb9c3ec583313dc4d52b7b6a4d6d)
1 /*	$OpenPackages$ */
2 /*	$OpenBSD: parsevar.c,v 1.2 2004/04/07 13:11:36 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 
45 static const char *
46 find_op1(const char *p)
47 {
48     for(;; p++) {
49     	if (isspace(*p) || *p == '$' || *p == '\0')
50 	    break;
51 	if (p[strspn(p, "?:!+")] == '=')
52 	    break;
53 	if (p[0] == ':' && p[1] == 's' && p[2] == 'h')
54 	    break;
55     }
56     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 bool
72 Parse_DoVar(const char *line,
73     GSymT *ctxt) 		/* Context in which to do the assignment */
74 {
75     const char	*arg;
76     char	*res1 = NULL, *res2 = NULL;
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;	/* Type of assignment */
83     struct Name	    name;
84 
85     arg = VarName_Get(line, &name, (SymTable *)ctxt, true,
86 	FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2);
87 
88     while (isspace(*arg))
89     	arg++;
90 
91     type = VAR_NORMAL;
92 
93     while (*arg != '=' && !isspace(*arg)) {
94 	/* Check operator type.  */
95 	switch (*arg++) {
96 	case '+':
97 	    if (type & (VAR_OPT|VAR_APPEND)) {
98 	    	VarName_Free(&name);
99 		return false;
100 	    }
101 	    type |= VAR_APPEND;
102 	    break;
103 
104 	case '?':
105 	    if (type & (VAR_OPT|VAR_APPEND)) {
106 	    	VarName_Free(&name);
107 		return false;
108 	    }
109 	    type |= VAR_OPT;
110 	    break;
111 
112 	case ':':
113 	    if (FEATURES(FEATURE_SUNSHCMD) && 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 		    VarName_Free(&name);
121 		    return false;
122 		}
123 		type |= VAR_SUBST;
124 	    }
125 	    break;
126 
127 	case '!':
128 	    if (type & VAR_SHELL) {
129 	    	VarName_Free(&name);
130 		return false;
131 	    }
132 	    type |= VAR_SHELL;
133 	    break;
134 
135 	default:
136 	    VarName_Free(&name);
137 	    return false;
138 	}
139     }
140 
141     /* Check validity of operator */
142     if (*arg++ != '=') {
143 	VarName_Free(&name);
144 	return false;
145     }
146 
147     while (isspace(*arg))
148     	arg++;
149     /* If the variable already has a value, we don't do anything.  */
150     if ((type & VAR_OPT) && Var_Valuei(name.s, name.e) != NULL) {
151 	VarName_Free(&name);
152 	return true;
153     }
154     if (type & VAR_SHELL) {
155 	char *err;
156 
157 	if (strchr(arg, '$') != NULL) {
158 	    char *sub;
159 	    /* There's a dollar sign in the command, so perform variable
160 	     * expansion on the whole thing. */
161 	    sub = Var_Subst(arg, NULL, true);
162 	    res1 = Cmd_Exec(sub, &err);
163 	    free(sub);
164 	} else
165 	    res1 = Cmd_Exec(arg, &err);
166 
167 	if (err)
168 	    Parse_Error(PARSE_WARNING, err, arg);
169 	arg = res1;
170     }
171     if (type & VAR_SUBST) {
172 	/*
173 	 * Allow variables in the old value to be undefined, but leave their
174 	 * invocation alone -- this is done by forcing oldVars to be false.
175 	 * XXX: This can cause recursive variables, but that's not hard to do,
176 	 * and this allows someone to do something like
177 	 *
178 	 *  CFLAGS = $(.INCLUDES)
179 	 *  CFLAGS := -I.. $(CFLAGS)
180 	 *
181 	 * And not get an error.
182 	 */
183 	bool   oldOldVars = oldVars;
184 
185 	oldVars = false;
186 	/* ensure the variable is set to something to avoid `variable
187 	 * is recursive' errors.  */
188 	if (Var_Valuei(name.s, name.e) == NULL)
189 	    Var_Seti(name.s, name.e, "", ctxt);
190 
191 	res2 = Var_Subst(arg, (SymTable *)ctxt, false);
192 	oldVars = oldOldVars;
193 
194 	arg = res2;
195     }
196 
197     if (type & VAR_APPEND)
198 	Var_Appendi(name.s, name.e, arg, ctxt);
199     else
200 	Var_Seti(name.s, name.e, arg, ctxt);
201 
202     VarName_Free(&name);
203     free(res2);
204     free(res1);
205     return true;
206 }
207 
208