1*56afcde6Sespie /* $OpenBSD: parsevar.c,v 1.15 2013/11/22 15:47:35 espie Exp $ */ 2f7923656Sespie /* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ 3f7923656Sespie 4f7923656Sespie /* 5f7923656Sespie * Copyright (c) 2001 Marc Espie. 6f7923656Sespie * 7f7923656Sespie * Redistribution and use in source and binary forms, with or without 8f7923656Sespie * modification, are permitted provided that the following conditions 9f7923656Sespie * are met: 10f7923656Sespie * 1. Redistributions of source code must retain the above copyright 11f7923656Sespie * notice, this list of conditions and the following disclaimer. 12f7923656Sespie * 2. Redistributions in binary form must reproduce the above copyright 13f7923656Sespie * notice, this list of conditions and the following disclaimer in the 14f7923656Sespie * documentation and/or other materials provided with the distribution. 15f7923656Sespie * 16f7923656Sespie * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 17f7923656Sespie * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18f7923656Sespie * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19f7923656Sespie * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 20f7923656Sespie * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21f7923656Sespie * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22f7923656Sespie * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23f7923656Sespie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24f7923656Sespie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25f7923656Sespie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26f7923656Sespie * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27f7923656Sespie */ 28f7923656Sespie 29f7923656Sespie #include <ctype.h> 30f7923656Sespie #include <stddef.h> 31f7923656Sespie #include <stdlib.h> 32f7923656Sespie #include <string.h> 33f7923656Sespie #include "config.h" 34f7923656Sespie #include "defines.h" 35f7923656Sespie #include "var.h" 36f7923656Sespie #include "varname.h" 37f7923656Sespie #include "error.h" 38f7923656Sespie #include "cmd_exec.h" 39f7923656Sespie #include "parsevar.h" 40f7923656Sespie 41f7923656Sespie static const char *find_op1(const char *); 42f7923656Sespie static const char *find_op2(const char *); 43d88ab755Sespie static bool parse_variable_assignment(const char *, int); 44f7923656Sespie 45f7923656Sespie static const char * 46ad2b054bSespie find_op1(const char *p) 47f7923656Sespie { 48f7923656Sespie for(;; p++) { 49*56afcde6Sespie if (ISSPACE(*p) || *p == '$' || *p == '\0') 50f7923656Sespie break; 51f7923656Sespie if (p[strspn(p, "?:!+")] == '=') 52f7923656Sespie break; 53f7923656Sespie if (p[0] == ':' && p[1] == 's' && p[2] == 'h') 54f7923656Sespie break; 55f7923656Sespie } 56f7923656Sespie return p; 57f7923656Sespie } 58f7923656Sespie 59f7923656Sespie static const char * 60ad2b054bSespie find_op2(const char *p) 61f7923656Sespie { 62f7923656Sespie for(;; p++) { 63*56afcde6Sespie if (ISSPACE(*p) || *p == '$' || *p == '\0') 64f7923656Sespie break; 65f7923656Sespie if (p[strspn(p, "?:!+")] == '=') 66f7923656Sespie break; 67f7923656Sespie } 68f7923656Sespie return p; 69f7923656Sespie } 70f7923656Sespie 71d88ab755Sespie static bool 72142b1e92Sespie parse_variable_assignment(const char *line, int ctxt) 73f7923656Sespie { 74f7923656Sespie const char *arg; 75f7923656Sespie char *res1 = NULL, *res2 = NULL; 76c6b7973eSespie #define VAR_INVALID -1 77f7923656Sespie #define VAR_NORMAL 0 78f7923656Sespie #define VAR_SUBST 1 79f7923656Sespie #define VAR_APPEND 2 80f7923656Sespie #define VAR_SHELL 4 81f7923656Sespie #define VAR_OPT 8 82c6b7973eSespie int type; 83f7923656Sespie struct Name name; 84f7923656Sespie 85d88ab755Sespie arg = VarName_Get(line, &name, NULL, true, 86f7923656Sespie FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2); 87f7923656Sespie 88*56afcde6Sespie while (ISSPACE(*arg)) 89f7923656Sespie arg++; 90f7923656Sespie 91f7923656Sespie type = VAR_NORMAL; 92f7923656Sespie 93c6b7973eSespie while (*arg != '=') { 94f7923656Sespie /* Check operator type. */ 95f7923656Sespie switch (*arg++) { 96f7923656Sespie case '+': 97c6b7973eSespie if (type & (VAR_OPT|VAR_APPEND)) 98c6b7973eSespie type = VAR_INVALID; 99c6b7973eSespie else 100f7923656Sespie type |= VAR_APPEND; 101f7923656Sespie break; 102f7923656Sespie 103f7923656Sespie case '?': 104c6b7973eSespie if (type & (VAR_OPT|VAR_APPEND)) 105c6b7973eSespie type = VAR_INVALID; 106c6b7973eSespie else 107f7923656Sespie type |= VAR_OPT; 108f7923656Sespie break; 109f7923656Sespie 110f7923656Sespie case ':': 111142b1e92Sespie if (FEATURES(FEATURE_SUNSHCMD) && 112142b1e92Sespie strncmp(arg, "sh", 2) == 0) { 113f7923656Sespie type = VAR_SHELL; 114f7923656Sespie arg += 2; 115f7923656Sespie while (*arg != '=' && *arg != '\0') 116f7923656Sespie arg++; 117f7923656Sespie } else { 118c6b7973eSespie if (type & VAR_SUBST) 119c6b7973eSespie type = VAR_INVALID; 120c6b7973eSespie else 121f7923656Sespie type |= VAR_SUBST; 122f7923656Sespie } 123f7923656Sespie break; 124f7923656Sespie 125f7923656Sespie case '!': 126c6b7973eSespie if (type & VAR_SHELL) 127c6b7973eSespie type = VAR_INVALID; 128c6b7973eSespie else 129f7923656Sespie type |= VAR_SHELL; 130f7923656Sespie break; 131f7923656Sespie 132f7923656Sespie default: 133c6b7973eSespie type = VAR_INVALID; 134c6b7973eSespie break; 135c6b7973eSespie } 136c6b7973eSespie if (type == VAR_INVALID) { 137f7923656Sespie VarName_Free(&name); 138f7923656Sespie return false; 139f7923656Sespie } 140f7923656Sespie } 141f7923656Sespie 142c6b7973eSespie arg++; 143*56afcde6Sespie while (ISSPACE(*arg)) 144f7923656Sespie arg++; 145f7923656Sespie /* If the variable already has a value, we don't do anything. */ 146d88ab755Sespie if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) { 147f7923656Sespie VarName_Free(&name); 148f7923656Sespie return true; 149f7923656Sespie } 150f7923656Sespie if (type & VAR_SHELL) { 151f7923656Sespie char *err; 152f7923656Sespie 153f7923656Sespie if (strchr(arg, '$') != NULL) { 154f7923656Sespie char *sub; 155142b1e92Sespie /* There's a dollar sign in the command, so perform 156142b1e92Sespie * variable expansion on the whole thing. */ 157f7923656Sespie sub = Var_Subst(arg, NULL, true); 158f7923656Sespie res1 = Cmd_Exec(sub, &err); 159f7923656Sespie free(sub); 160f7923656Sespie } else 161f7923656Sespie res1 = Cmd_Exec(arg, &err); 162f7923656Sespie 163f7923656Sespie if (err) 164f7923656Sespie Parse_Error(PARSE_WARNING, err, arg); 165f7923656Sespie arg = res1; 166f7923656Sespie } 167f7923656Sespie if (type & VAR_SUBST) { 168f7923656Sespie /* 169142b1e92Sespie * Allow variables in the old value to be undefined, but leave 170142b1e92Sespie * their invocation alone -- this is done by forcing 171142b1e92Sespie * errorIsOkay to be false. 172142b1e92Sespie * XXX: This can cause recursive variables, but that's not 173142b1e92Sespie * hard to do, and this allows someone to do something like 174f7923656Sespie * 175f7923656Sespie * CFLAGS = $(.INCLUDES) 176f7923656Sespie * CFLAGS := -I.. $(CFLAGS) 177f7923656Sespie * 178f7923656Sespie * And not get an error. 179f7923656Sespie */ 18093a10c24Sespie bool saved = errorIsOkay; 181f7923656Sespie 18293a10c24Sespie errorIsOkay = false; 183f7923656Sespie /* ensure the variable is set to something to avoid `variable 184f7923656Sespie * is recursive' errors. */ 185c6b7973eSespie if (!Var_Definedi(name.s, name.e)) 1865a5ca93eSespie Var_Seti_with_ctxt(name.s, name.e, "", ctxt); 187f7923656Sespie 188d88ab755Sespie res2 = Var_Subst(arg, NULL, false); 18993a10c24Sespie errorIsOkay = saved; 190f7923656Sespie 191f7923656Sespie arg = res2; 192f7923656Sespie } 193f7923656Sespie 194f7923656Sespie if (type & VAR_APPEND) 1955a5ca93eSespie Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt); 196f7923656Sespie else 1975a5ca93eSespie Var_Seti_with_ctxt(name.s, name.e, arg, ctxt); 198f7923656Sespie 199f7923656Sespie VarName_Free(&name); 200f7923656Sespie free(res2); 201f7923656Sespie free(res1); 202f7923656Sespie return true; 203f7923656Sespie } 204f7923656Sespie 205d88ab755Sespie bool 20616620742Sespie Parse_As_Var_Assignment(const char *line) 207d88ab755Sespie { 208d88ab755Sespie return parse_variable_assignment(line, VAR_GLOBAL); 209d88ab755Sespie } 210d88ab755Sespie 211d88ab755Sespie bool 212d88ab755Sespie Parse_CmdlineVar(const char *line) 213d88ab755Sespie { 214d88ab755Sespie bool result; 21593a10c24Sespie bool saved = errorIsOkay; 216d88ab755Sespie 21793a10c24Sespie errorIsOkay = false; 218d88ab755Sespie result = parse_variable_assignment(line, VAR_CMD); 21994c8ed6aSespie errorIsOkay = saved; 220d88ab755Sespie return result; 221d88ab755Sespie } 222d88ab755Sespie 223