Annotation of src/usr.bin/make/parsevar.c, Revision 1.17
1.17 ! espie 1: /* $OpenBSD: parsevar.c,v 1.16 2016/10/23 14:54:14 espie Exp $ */
1.1 espie 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 "defines.h"
34: #include "var.h"
35: #include "varname.h"
36: #include "error.h"
37: #include "cmd_exec.h"
38: #include "parsevar.h"
39:
40: static const char *find_op1(const char *);
41: static const char *find_op2(const char *);
1.3 espie 42: static bool parse_variable_assignment(const char *, int);
1.1 espie 43:
44: static const char *
1.2 espie 45: find_op1(const char *p)
1.1 espie 46: {
1.6 espie 47: for(;; p++) {
1.15 espie 48: if (ISSPACE(*p) || *p == '$' || *p == '\0')
1.6 espie 49: break;
50: if (p[strspn(p, "?:!+")] == '=')
51: break;
52: if (p[0] == ':' && p[1] == 's' && p[2] == 'h')
53: break;
54: }
55: return p;
1.1 espie 56: }
57:
58: static const char *
1.2 espie 59: find_op2(const char *p)
1.1 espie 60: {
1.6 espie 61: for(;; p++) {
1.15 espie 62: if (ISSPACE(*p) || *p == '$' || *p == '\0')
1.6 espie 63: break;
64: if (p[strspn(p, "?:!+")] == '=')
65: break;
66: }
67: return p;
1.1 espie 68: }
69:
1.3 espie 70: static bool
1.6 espie 71: parse_variable_assignment(const char *line, int ctxt)
1.1 espie 72: {
1.6 espie 73: const char *arg;
74: char *res1 = NULL, *res2 = NULL;
1.7 espie 75: #define VAR_INVALID -1
1.1 espie 76: #define VAR_NORMAL 0
77: #define VAR_SUBST 1
78: #define VAR_APPEND 2
79: #define VAR_SHELL 4
80: #define VAR_OPT 8
1.16 espie 81: #define VAR_LAZYSHELL 16
82: #define VAR_SUNSHELL 32
1.11 espie 83: int type;
1.6 espie 84: struct Name name;
1.1 espie 85:
1.17 ! espie 86: arg = VarName_Get(line, &name, NULL, true, find_op1);
1.1 espie 87:
1.15 espie 88: while (ISSPACE(*arg))
1.6 espie 89: arg++;
1.1 espie 90:
1.6 espie 91: type = VAR_NORMAL;
92:
1.16 espie 93: /* double operators (except for :) are forbidden */
94: /* OPT and APPEND don't match */
95: /* APPEND and LAZYSHELL can't really work */
1.7 espie 96: while (*arg != '=') {
1.6 espie 97: /* Check operator type. */
98: switch (*arg++) {
99: case '+':
1.16 espie 100: if (type & (VAR_OPT|VAR_LAZYSHELL|VAR_APPEND))
1.7 espie 101: type = VAR_INVALID;
102: else
103: type |= VAR_APPEND;
1.6 espie 104: break;
105:
106: case '?':
1.7 espie 107: if (type & (VAR_OPT|VAR_APPEND))
108: type = VAR_INVALID;
1.10 espie 109: else
1.7 espie 110: type |= VAR_OPT;
1.6 espie 111: break;
112:
113: case ':':
1.17 ! espie 114: if (strncmp(arg, "sh", 2) == 0) {
1.16 espie 115: type = VAR_SUNSHELL;
1.6 espie 116: arg += 2;
117: while (*arg != '=' && *arg != '\0')
118: arg++;
119: } else {
1.7 espie 120: if (type & VAR_SUBST)
121: type = VAR_INVALID;
122: else
123: type |= VAR_SUBST;
1.6 espie 124: }
125: break;
126:
127: case '!':
1.16 espie 128: if (type & VAR_SHELL) {
129: if (type & (VAR_APPEND))
130: type = VAR_INVALID;
131: else
132: type = VAR_LAZYSHELL;
133: } else if (type & (VAR_LAZYSHELL|VAR_SUNSHELL))
1.7 espie 134: type = VAR_INVALID;
135: else
136: type |= VAR_SHELL;
1.6 espie 137: break;
138:
139: default:
1.7 espie 140: type = VAR_INVALID;
141: break;
142: }
143: if (type == VAR_INVALID) {
1.6 espie 144: VarName_Free(&name);
145: return false;
1.1 espie 146: }
1.6 espie 147: }
148:
1.7 espie 149: arg++;
1.15 espie 150: while (ISSPACE(*arg))
1.6 espie 151: arg++;
152: /* If the variable already has a value, we don't do anything. */
153: if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) {
154: VarName_Free(&name);
155: return true;
156: }
1.16 espie 157: if (type & (VAR_SHELL|VAR_SUNSHELL)) {
1.6 espie 158: char *err;
159:
160: if (strchr(arg, '$') != NULL) {
161: char *sub;
162: /* There's a dollar sign in the command, so perform
163: * variable expansion on the whole thing. */
164: sub = Var_Subst(arg, NULL, true);
165: res1 = Cmd_Exec(sub, &err);
166: free(sub);
167: } else
168: res1 = Cmd_Exec(arg, &err);
169:
170: if (err)
171: Parse_Error(PARSE_WARNING, err, arg);
172: arg = res1;
173: }
1.16 espie 174: if (type & VAR_LAZYSHELL) {
175: if (strchr(arg, '$') != NULL) {
176: /* There's a dollar sign in the command, so perform
177: * variable expansion on the whole thing. */
178: arg = Var_Subst(arg, NULL, true);
179: }
180: }
1.6 espie 181: if (type & VAR_SUBST) {
182: /*
183: * Allow variables in the old value to be undefined, but leave
184: * their invocation alone -- this is done by forcing
1.10 espie 185: * errorIsOkay to be false.
186: * XXX: This can cause recursive variables, but that's not
1.6 espie 187: * hard to do, and this allows someone to do something like
188: *
1.10 espie 189: * CFLAGS = $(.INCLUDES)
1.6 espie 190: * CFLAGS := -I.. $(CFLAGS)
191: *
192: * And not get an error.
193: */
194: bool saved = errorIsOkay;
195:
196: errorIsOkay = false;
197: /* ensure the variable is set to something to avoid `variable
198: * is recursive' errors. */
1.7 espie 199: if (!Var_Definedi(name.s, name.e))
1.8 espie 200: Var_Seti_with_ctxt(name.s, name.e, "", ctxt);
1.6 espie 201:
202: res2 = Var_Subst(arg, NULL, false);
203: errorIsOkay = saved;
204:
205: arg = res2;
206: }
207:
208: if (type & VAR_APPEND)
1.8 espie 209: Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt);
1.6 espie 210: else
1.8 espie 211: Var_Seti_with_ctxt(name.s, name.e, arg, ctxt);
1.16 espie 212: if (type & VAR_LAZYSHELL)
213: Var_Mark(name.s, name.e, VAR_EXEC_LATER);
1.1 espie 214:
215: VarName_Free(&name);
1.6 espie 216: free(res2);
217: free(res1);
1.1 espie 218: return true;
1.3 espie 219: }
220:
221: bool
1.12 espie 222: Parse_As_Var_Assignment(const char *line)
1.3 espie 223: {
224: return parse_variable_assignment(line, VAR_GLOBAL);
225: }
226:
227: bool
228: Parse_CmdlineVar(const char *line)
229: {
230: bool result;
1.4 espie 231: bool saved = errorIsOkay;
1.3 espie 232:
1.4 espie 233: errorIsOkay = false;
1.3 espie 234: result = parse_variable_assignment(line, VAR_CMD);
1.5 espie 235: errorIsOkay = saved;
1.3 espie 236: return result;
1.1 espie 237: }
238: