bool Var_ParseSkip(const char **pstr, SymTable *ctxt) { const char *str = *pstr; struct Name name; bool result; bool has_modifier; const char *tstr = str; if (str[1] == 0) { *pstr = str+1; return false; } has_modifier = parse_base_variable_name(&tstr, &name, ctxt); VarName_Free(&name); result = true; if (has_modifier) { bool freePtr = false; char *s = VarModifiers_Apply(NULL, NULL, ctxt, true, &freePtr, &tstr, str[1]); if (s == var_Error) result = false; if (freePtr) free(s); } *pstr = tstr; return result; }
char * Var_Parse(const char *str, /* The string to parse */ SymTable *ctxt, /* The context for the variable */ bool err, /* true if undefined variables are an error */ size_t *lengthPtr, /* OUT: The length of the specification */ bool *freePtr) /* OUT: true if caller should free result */ { const char *tstr; struct Name name; char *val; uint32_t k; int idx; bool has_modifier; *freePtr = false; tstr = str; if (str[1] == 0) { *lengthPtr = 1; *freePtr = false; return err ? var_Error : varNoError; } has_modifier = parse_base_variable_name(&tstr, &name, ctxt); idx = classify_var(name.s, &name.e, &k); val = get_expanded_value(name.s, name.e, idx, k, ctxt, err, freePtr); if (has_modifier) { val = VarModifiers_Apply(val, &name, ctxt, err, freePtr, &tstr, str[1]); } if (val == NULL) { val = err ? var_Error : varNoError; /* If it comes from a dynamic source, and it doesn't have * a context, copy the spec instead. * Specifically, this make allows constructs like: * target.o: $*.c * Absence of a context means "parsing". But these can't * be expanded during parsing, to be consistent with the * way .SUFFIXES work. * .SUFFIXES may be added/reset/removed during parsing, * but in the end, the final list is what's considered for * handling targets. So those dynamic variables must be * handled lazily too. */ if (idx != GLOBAL_INDEX) { if (ctxt == NULL) { *freePtr = true; val = Str_dupi(str, tstr); } else { bad_dynamic_variable(idx); } } } VarName_Free(&name); *lengthPtr = tstr - str; return val; }
/* Very quick version of the variable scanner that just looks for target * variables, and never ever errors out */ bool Var_Check_for_target(const char *str) { bool seen_target = false; for (;;) { const char *tstr; uint32_t k; int idx; bool has_modifier; struct Name name; /* skip over uninteresting stuff */ for (; *str != '\0' && *str != '$'; str++) ; if (*str == '\0') break; if (str[1] == '$') { /* A $ may be escaped with another $. */ str += 2; continue; } tstr = str; has_modifier = parse_base_variable_name(&tstr, &name, NULL); idx = classify_var(name.s, &name.e, &k); if (has_modifier) { bool doFree = false; char *val = VarModifiers_Apply(NULL, NULL, NULL, false, &doFree, &tstr, str[1]); if (doFree) free(val); } if (tlist[idx]) seen_target = true; VarName_Free(&name); str = tstr; } return seen_target; }
char * Var_Parse(const char *str, /* The string to parse */ SymTable *ctxt, /* The context for the variable */ bool err, /* true if undefined variables are an error */ size_t *lengthPtr, /* OUT: The length of the specification */ bool *freePtr) /* OUT: true if caller should free result */ { const char *tstr; struct Name name; char *val; uint32_t k; int idx; bool has_modifier; *freePtr = false; tstr = str; has_modifier = parse_base_variable_name(&tstr, &name, ctxt); idx = classify_var(name.s, &name.e, &k); val = get_expanded_value(name.s, name.e, idx, k, ctxt, err, freePtr); if (has_modifier) { val = VarModifiers_Apply(val, &name, ctxt, err, freePtr, &tstr, str[1]); } if (val == NULL) { val = err ? var_Error : varNoError; /* Dynamic source */ if (idx != GLOBAL_INDEX) { /* can't be expanded for now: copy the spec instead. */ if (ctxt == NULL) { *freePtr = true; val = Str_dupi(str, tstr); } else { /* somehow, this should have been expanded already. */ GNode *n; /* XXX */ n = (GNode *)(((char *)ctxt) - offsetof(GNode, context)); if (idx >= LOCAL_SIZE) idx = EXTENDED2SIMPLE(idx); switch(idx) { case IMPSRC_INDEX: Fatal( "Using $< in a non-suffix rule context is a GNUmake idiom (line %lu of %s)", n->origin.lineno, n->origin.fname); break; default: Error( "Using undefined dynamic variable $%s (line %lu of %s)", varnames[idx], n->origin.lineno, n->origin.fname); break; } } } } VarName_Free(&name); *lengthPtr = tstr - str; return val; }
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; }
static Token CondHandleDefault(bool doEval) { bool t; bool (*evalProc)(struct Name *); bool invert = false; struct Name arg; size_t arglen; evalProc = NULL; if (strncmp(condExpr, "empty", 5) == 0) { /* Use Var_Parse to parse the spec in parens and return * True if the resulting string is empty. */ size_t length; bool doFree; char *val; condExpr += 5; for (arglen = 0; condExpr[arglen] != '(' && condExpr[arglen] != '\0';) arglen++; if (condExpr[arglen] != '\0') { val = Var_Parse(&condExpr[arglen - 1], NULL, doEval, &length, &doFree); if (val == var_Error) t = Err; else { /* A variable is empty when it just contains * spaces... 4/15/92, christos */ char *p; for (p = val; isspace(*p); p++) continue; t = *p == '\0' ? True : False; } if (doFree) free(val); /* Advance condExpr to beyond the closing ). Note that * we subtract one from arglen + length b/c length * is calculated from condExpr[arglen - 1]. */ condExpr += arglen + length - 1; return t; } else condExpr -= 5; } else { struct operator *op; for (op = ops; op != NULL; op++) if (strncmp(condExpr, op->s, op->len) == 0) { condExpr += op->len; if (CondGetArg(&condExpr, &arg, op->s, true)) evalProc = op->proc; else condExpr -= op->len; break; } } if (evalProc == NULL) { /* The symbol is itself the argument to the default * function. We advance condExpr to the end of the symbol * by hand (the next whitespace, closing paren or * binary operator) and set to invert the evaluation * function if condInvert is true. */ invert = condInvert; evalProc = condDefProc; /* XXX should we ignore problems now ? */ CondGetArg(&condExpr, &arg, "", false); } /* Evaluate the argument using the set function. If invert * is true, we invert the sense of the function. */ t = (!doEval || (*evalProc)(&arg) ? (invert ? False : True) : (invert ? True : False)); VarName_Free(&arg); return t; }