1 /* $OpenPackages$ */ 2 /* $OpenBSD: parsevar.c,v 1.1 2001/05/23 12:34:48 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(p) 47 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(p) 62 const char *p; 63 { 64 for(;; p++) { 65 if (isspace(*p) || *p == '$' || *p == '\0') 66 break; 67 if (p[strspn(p, "?:!+")] == '=') 68 break; 69 } 70 return p; 71 } 72 73 bool 74 Parse_DoVar(line, ctxt) 75 const char *line; 76 GSymT *ctxt; /* Context in which to do the assignment */ 77 { 78 const char *arg; 79 char *res1 = NULL, *res2 = NULL; 80 #define VAR_NORMAL 0 81 #define VAR_SUBST 1 82 #define VAR_APPEND 2 83 #define VAR_SHELL 4 84 #define VAR_OPT 8 85 int type; /* Type of assignment */ 86 struct Name name; 87 88 arg = VarName_Get(line, &name, (SymTable *)ctxt, true, 89 FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2); 90 91 while (isspace(*arg)) 92 arg++; 93 94 type = VAR_NORMAL; 95 96 while (*arg != '=' && !isspace(*arg)) { 97 /* Check operator type. */ 98 switch (*arg++) { 99 case '+': 100 if (type & (VAR_OPT|VAR_APPEND)) { 101 VarName_Free(&name); 102 return false; 103 } 104 type |= VAR_APPEND; 105 break; 106 107 case '?': 108 if (type & (VAR_OPT|VAR_APPEND)) { 109 VarName_Free(&name); 110 return false; 111 } 112 type |= VAR_OPT; 113 break; 114 115 case ':': 116 if (FEATURES(FEATURE_SUNSHCMD) && strncmp(arg, "sh", 2) == 0) { 117 type = VAR_SHELL; 118 arg += 2; 119 while (*arg != '=' && *arg != '\0') 120 arg++; 121 } else { 122 if (type & VAR_SUBST) { 123 VarName_Free(&name); 124 return false; 125 } 126 type |= VAR_SUBST; 127 } 128 break; 129 130 case '!': 131 if (type & VAR_SHELL) { 132 VarName_Free(&name); 133 return false; 134 } 135 type |= VAR_SHELL; 136 break; 137 138 default: 139 VarName_Free(&name); 140 return false; 141 } 142 } 143 144 /* Check validity of operator */ 145 if (*arg++ != '=') { 146 VarName_Free(&name); 147 return false; 148 } 149 150 while (isspace(*arg)) 151 arg++; 152 /* If the variable already has a value, we don't do anything. */ 153 if ((type & VAR_OPT) && Var_Valuei(name.s, name.e) != NULL) { 154 VarName_Free(&name); 155 return true; 156 } 157 if (type & VAR_SHELL) { 158 char *err; 159 160 if (strchr(arg, '$') != NULL) { 161 char *sub; 162 /* There's a dollar sign in the command, so perform variable 163 * 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 } 174 if (type & VAR_SUBST) { 175 /* 176 * Allow variables in the old value to be undefined, but leave their 177 * invocation alone -- this is done by forcing oldVars to be false. 178 * XXX: This can cause recursive variables, but that's not hard to do, 179 * and this allows someone to do something like 180 * 181 * CFLAGS = $(.INCLUDES) 182 * CFLAGS := -I.. $(CFLAGS) 183 * 184 * And not get an error. 185 */ 186 bool oldOldVars = oldVars; 187 188 oldVars = false; 189 /* ensure the variable is set to something to avoid `variable 190 * is recursive' errors. */ 191 if (Var_Valuei(name.s, name.e) == NULL) 192 Var_Seti(name.s, name.e, "", ctxt); 193 194 res2 = Var_Subst(arg, (SymTable *)ctxt, false); 195 oldVars = oldOldVars; 196 197 arg = res2; 198 } 199 200 if (type & VAR_APPEND) 201 Var_Appendi(name.s, name.e, arg, ctxt); 202 else 203 Var_Seti(name.s, name.e, arg, ctxt); 204 205 VarName_Free(&name); 206 free(res2); 207 free(res1); 208 return true; 209 } 210 211