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