1*c9fc29cfSespie /* $OpenBSD: parsevar.c,v 1.17 2023/09/04 11:35:11 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 "defines.h" 34f7923656Sespie #include "var.h" 35f7923656Sespie #include "varname.h" 36f7923656Sespie #include "error.h" 37f7923656Sespie #include "cmd_exec.h" 38f7923656Sespie #include "parsevar.h" 39f7923656Sespie 40f7923656Sespie static const char *find_op1(const char *); 41f7923656Sespie static const char *find_op2(const char *); 42d88ab755Sespie static bool parse_variable_assignment(const char *, int); 43f7923656Sespie 44f7923656Sespie static const char * 45ad2b054bSespie find_op1(const char *p) 46f7923656Sespie { 47f7923656Sespie for(;; p++) { 4856afcde6Sespie if (ISSPACE(*p) || *p == '$' || *p == '\0') 49f7923656Sespie break; 50f7923656Sespie if (p[strspn(p, "?:!+")] == '=') 51f7923656Sespie break; 52f7923656Sespie if (p[0] == ':' && p[1] == 's' && p[2] == 'h') 53f7923656Sespie break; 54f7923656Sespie } 55f7923656Sespie return p; 56f7923656Sespie } 57f7923656Sespie 58f7923656Sespie static const char * 59ad2b054bSespie find_op2(const char *p) 60f7923656Sespie { 61f7923656Sespie for(;; p++) { 6256afcde6Sespie if (ISSPACE(*p) || *p == '$' || *p == '\0') 63f7923656Sespie break; 64f7923656Sespie if (p[strspn(p, "?:!+")] == '=') 65f7923656Sespie break; 66f7923656Sespie } 67f7923656Sespie return p; 68f7923656Sespie } 69f7923656Sespie 70d88ab755Sespie static bool 71142b1e92Sespie parse_variable_assignment(const char *line, int ctxt) 72f7923656Sespie { 73f7923656Sespie const char *arg; 74f7923656Sespie char *res1 = NULL, *res2 = NULL; 75c6b7973eSespie #define VAR_INVALID -1 76f7923656Sespie #define VAR_NORMAL 0 77f7923656Sespie #define VAR_SUBST 1 78f7923656Sespie #define VAR_APPEND 2 79f7923656Sespie #define VAR_SHELL 4 80f7923656Sespie #define VAR_OPT 8 8183f3589bSespie #define VAR_LAZYSHELL 16 8283f3589bSespie #define VAR_SUNSHELL 32 83c6b7973eSespie int type; 84f7923656Sespie struct Name name; 85f7923656Sespie 86*c9fc29cfSespie arg = VarName_Get(line, &name, NULL, true, find_op1); 87f7923656Sespie 8856afcde6Sespie while (ISSPACE(*arg)) 89f7923656Sespie arg++; 90f7923656Sespie 91f7923656Sespie type = VAR_NORMAL; 92f7923656Sespie 9383f3589bSespie /* double operators (except for :) are forbidden */ 9483f3589bSespie /* OPT and APPEND don't match */ 9583f3589bSespie /* APPEND and LAZYSHELL can't really work */ 96c6b7973eSespie while (*arg != '=') { 97f7923656Sespie /* Check operator type. */ 98f7923656Sespie switch (*arg++) { 99f7923656Sespie case '+': 10083f3589bSespie if (type & (VAR_OPT|VAR_LAZYSHELL|VAR_APPEND)) 101c6b7973eSespie type = VAR_INVALID; 102c6b7973eSespie else 103f7923656Sespie type |= VAR_APPEND; 104f7923656Sespie break; 105f7923656Sespie 106f7923656Sespie case '?': 107c6b7973eSespie if (type & (VAR_OPT|VAR_APPEND)) 108c6b7973eSespie type = VAR_INVALID; 109c6b7973eSespie else 110f7923656Sespie type |= VAR_OPT; 111f7923656Sespie break; 112f7923656Sespie 113f7923656Sespie case ':': 114*c9fc29cfSespie if (strncmp(arg, "sh", 2) == 0) { 11583f3589bSespie type = VAR_SUNSHELL; 116f7923656Sespie arg += 2; 117f7923656Sespie while (*arg != '=' && *arg != '\0') 118f7923656Sespie arg++; 119f7923656Sespie } else { 120c6b7973eSespie if (type & VAR_SUBST) 121c6b7973eSespie type = VAR_INVALID; 122c6b7973eSespie else 123f7923656Sespie type |= VAR_SUBST; 124f7923656Sespie } 125f7923656Sespie break; 126f7923656Sespie 127f7923656Sespie case '!': 12883f3589bSespie if (type & VAR_SHELL) { 12983f3589bSespie if (type & (VAR_APPEND)) 13083f3589bSespie type = VAR_INVALID; 13183f3589bSespie else 13283f3589bSespie type = VAR_LAZYSHELL; 13383f3589bSespie } else if (type & (VAR_LAZYSHELL|VAR_SUNSHELL)) 134c6b7973eSespie type = VAR_INVALID; 135c6b7973eSespie else 136f7923656Sespie type |= VAR_SHELL; 137f7923656Sespie break; 138f7923656Sespie 139f7923656Sespie default: 140c6b7973eSespie type = VAR_INVALID; 141c6b7973eSespie break; 142c6b7973eSespie } 143c6b7973eSespie if (type == VAR_INVALID) { 144f7923656Sespie VarName_Free(&name); 145f7923656Sespie return false; 146f7923656Sespie } 147f7923656Sespie } 148f7923656Sespie 149c6b7973eSespie arg++; 15056afcde6Sespie while (ISSPACE(*arg)) 151f7923656Sespie arg++; 152f7923656Sespie /* If the variable already has a value, we don't do anything. */ 153d88ab755Sespie if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) { 154f7923656Sespie VarName_Free(&name); 155f7923656Sespie return true; 156f7923656Sespie } 15783f3589bSespie if (type & (VAR_SHELL|VAR_SUNSHELL)) { 158f7923656Sespie char *err; 159f7923656Sespie 160f7923656Sespie if (strchr(arg, '$') != NULL) { 161f7923656Sespie char *sub; 162142b1e92Sespie /* There's a dollar sign in the command, so perform 163142b1e92Sespie * variable expansion on the whole thing. */ 164f7923656Sespie sub = Var_Subst(arg, NULL, true); 165f7923656Sespie res1 = Cmd_Exec(sub, &err); 166f7923656Sespie free(sub); 167f7923656Sespie } else 168f7923656Sespie res1 = Cmd_Exec(arg, &err); 169f7923656Sespie 170f7923656Sespie if (err) 171f7923656Sespie Parse_Error(PARSE_WARNING, err, arg); 172f7923656Sespie arg = res1; 173f7923656Sespie } 17483f3589bSespie if (type & VAR_LAZYSHELL) { 17583f3589bSespie if (strchr(arg, '$') != NULL) { 17683f3589bSespie /* There's a dollar sign in the command, so perform 17783f3589bSespie * variable expansion on the whole thing. */ 17883f3589bSespie arg = Var_Subst(arg, NULL, true); 17983f3589bSespie } 18083f3589bSespie } 181f7923656Sespie if (type & VAR_SUBST) { 182f7923656Sespie /* 183142b1e92Sespie * Allow variables in the old value to be undefined, but leave 184142b1e92Sespie * their invocation alone -- this is done by forcing 185142b1e92Sespie * errorIsOkay to be false. 186142b1e92Sespie * XXX: This can cause recursive variables, but that's not 187142b1e92Sespie * hard to do, and this allows someone to do something like 188f7923656Sespie * 189f7923656Sespie * CFLAGS = $(.INCLUDES) 190f7923656Sespie * CFLAGS := -I.. $(CFLAGS) 191f7923656Sespie * 192f7923656Sespie * And not get an error. 193f7923656Sespie */ 19493a10c24Sespie bool saved = errorIsOkay; 195f7923656Sespie 19693a10c24Sespie errorIsOkay = false; 197f7923656Sespie /* ensure the variable is set to something to avoid `variable 198f7923656Sespie * is recursive' errors. */ 199c6b7973eSespie if (!Var_Definedi(name.s, name.e)) 2005a5ca93eSespie Var_Seti_with_ctxt(name.s, name.e, "", ctxt); 201f7923656Sespie 202d88ab755Sespie res2 = Var_Subst(arg, NULL, false); 20393a10c24Sespie errorIsOkay = saved; 204f7923656Sespie 205f7923656Sespie arg = res2; 206f7923656Sespie } 207f7923656Sespie 208f7923656Sespie if (type & VAR_APPEND) 2095a5ca93eSespie Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt); 210f7923656Sespie else 2115a5ca93eSespie Var_Seti_with_ctxt(name.s, name.e, arg, ctxt); 21283f3589bSespie if (type & VAR_LAZYSHELL) 21383f3589bSespie Var_Mark(name.s, name.e, VAR_EXEC_LATER); 214f7923656Sespie 215f7923656Sespie VarName_Free(&name); 216f7923656Sespie free(res2); 217f7923656Sespie free(res1); 218f7923656Sespie return true; 219f7923656Sespie } 220f7923656Sespie 221d88ab755Sespie bool 22216620742Sespie Parse_As_Var_Assignment(const char *line) 223d88ab755Sespie { 224d88ab755Sespie return parse_variable_assignment(line, VAR_GLOBAL); 225d88ab755Sespie } 226d88ab755Sespie 227d88ab755Sespie bool 228d88ab755Sespie Parse_CmdlineVar(const char *line) 229d88ab755Sespie { 230d88ab755Sespie bool result; 23193a10c24Sespie bool saved = errorIsOkay; 232d88ab755Sespie 23393a10c24Sespie errorIsOkay = false; 234d88ab755Sespie result = parse_variable_assignment(line, VAR_CMD); 23594c8ed6aSespie errorIsOkay = saved; 236d88ab755Sespie return result; 237d88ab755Sespie } 238d88ab755Sespie 239