static bool parse_base_variable_name(const char **pstr, struct Name *name, SymTable *ctxt) { const char *str = *pstr; const char *tstr; bool has_modifier = false; switch(str[1]) { case '(': case '{': /* Find eventual modifiers in the variable */ tstr = VarName_Get(str+2, name, ctxt, false, find_pos(str[1])); if (*tstr == ':') has_modifier = true; else if (*tstr != '\0') { tstr++; } break; default: name->s = str+1; name->e = str+2; name->tofree = false; tstr = str + 2; break; } *pstr = tstr; return has_modifier; }
/*- *----------------------------------------------------------------------- * CondGetArg -- * Find the argument of a built-in function. * * Results: * true if evaluation went okay * * Side Effects: * The line pointer is set to point to the closing parenthesis of the * function call. The argument is filled. *----------------------------------------------------------------------- */ static bool CondGetArg(const char **linePtr, struct Name *arg, const char *func, bool parens) /* true if arg should be bounded by parens */ { const char *cp; cp = *linePtr; /* Set things up to return faster in case of problem */ arg->s = cp; arg->e = cp; arg->tofree = false; /* make and defined are not really keywords, so if CondGetArg doesn't * work... */ if (parens) { while (isspace(*cp)) cp++; if (*cp == '(') cp++; else return false; } if (*cp == '\0') return false; while (isspace(*cp)) cp++; cp = VarName_Get(cp, arg, NULL, true, find_cond); while (isspace(*cp)) cp++; if (parens) { if (*cp == ')') cp++; else { Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", func); return false; } } *linePtr = cp; return true; }
/*- *----------------------------------------------------------------------- * CondGetArg -- * Find the argument of a built-in function. * * Results: * true if evaluation went okay * * Side Effects: * The line pointer is set to point to the closing parenthesis of the * function call. The argument is filled. *----------------------------------------------------------------------- */ static bool CondGetArg(const char **linePtr, struct Name *arg, const char *func, bool parens) /* true if arg should be bounded by parens */ { const char *cp; cp = *linePtr; if (parens) { while (*cp != '(' && *cp != '\0') cp++; if (*cp == '(') cp++; } if (*cp == '\0') { /* No arguments whatsoever. Because 'make' and 'defined' aren't * really "reserved words", we don't print a message. I think * this is better than hitting the user with a warning message * every time s/he uses the word 'make' or 'defined' at the * beginning of a symbol... */ arg->s = cp; arg->e = cp; arg->tofree = false; return false; } while (*cp == ' ' || *cp == '\t') cp++; cp = VarName_Get(cp, arg, NULL, true, find_cond); while (*cp == ' ' || *cp == '\t') cp++; if (parens && *cp != ')') { Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", func); return false; } else if (parens) /* Advance pointer past close parenthesis. */ cp++; *linePtr = cp; return true; }
static bool parse_variable_assignment(const char *line, int ctxt) { const char *arg; char *res1 = NULL, *res2 = NULL; #define VAR_INVALID -1 #define VAR_NORMAL 0 #define VAR_SUBST 1 #define VAR_APPEND 2 #define VAR_SHELL 4 #define VAR_OPT 8 int type; struct Name name; arg = VarName_Get(line, &name, NULL, true, FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2); while (isspace(*arg)) arg++; type = VAR_NORMAL; while (*arg != '=') { /* Check operator type. */ switch (*arg++) { case '+': if (type & (VAR_OPT|VAR_APPEND)) type = VAR_INVALID; else type |= VAR_APPEND; break; case '?': if (type & (VAR_OPT|VAR_APPEND)) type = VAR_INVALID; else type |= VAR_OPT; break; case ':': if (FEATURES(FEATURE_SUNSHCMD) && strncmp(arg, "sh", 2) == 0) { type = VAR_SHELL; arg += 2; while (*arg != '=' && *arg != '\0') arg++; } else { if (type & VAR_SUBST) type = VAR_INVALID; else type |= VAR_SUBST; } break; case '!': if (type & VAR_SHELL) type = VAR_INVALID; else type |= VAR_SHELL; break; default: type = VAR_INVALID; break; } if (type == VAR_INVALID) { VarName_Free(&name); return false; } } arg++; while (isspace(*arg)) arg++; /* If the variable already has a value, we don't do anything. */ if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) { VarName_Free(&name); return true; } if (type & VAR_SHELL) { char *err; if (strchr(arg, '$') != NULL) { char *sub; /* There's a dollar sign in the command, so perform * variable expansion on the whole thing. */ sub = Var_Subst(arg, NULL, true); res1 = Cmd_Exec(sub, &err); free(sub); } else res1 = Cmd_Exec(arg, &err); if (err) Parse_Error(PARSE_WARNING, err, arg); arg = res1; } if (type & VAR_SUBST) { /* * Allow variables in the old value to be undefined, but leave * their invocation alone -- this is done by forcing * errorIsOkay to be false. * XXX: This can cause recursive variables, but that's not * hard to do, and this allows someone to do something like * * CFLAGS = $(.INCLUDES) * CFLAGS := -I.. $(CFLAGS) * * And not get an error. */ bool saved = errorIsOkay; errorIsOkay = false; /* ensure the variable is set to something to avoid `variable * is recursive' errors. */ if (!Var_Definedi(name.s, name.e)) Var_Seti_with_ctxt(name.s, name.e, "", ctxt); res2 = Var_Subst(arg, NULL, false); errorIsOkay = saved; arg = res2; } if (type & VAR_APPEND) Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt); else Var_Seti_with_ctxt(name.s, name.e, arg, ctxt); VarName_Free(&name); free(res2); free(res1); return true; }