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