/* mark variable as poisoned, in a given setup. */ void Var_MarkPoisoned(const char *name, const char *ename, unsigned int type) { Var *v; uint32_t k; int idx; idx = classify_var(name, &ename, &k); if (idx != GLOBAL_INDEX) { Parse_Error(PARSE_FATAL, "Trying to poison dynamic variable $%s", varnames[idx]); return; } v = find_global_var(name, ename, k); v->flags |= type; /* POISON_NORMAL is not lazy: if the variable already exists in * the Makefile, then it's a mistake. */ if (v->flags & POISON_NORMAL) { if (v->flags & VAR_DUMMY) return; if (v->flags & VAR_FROM_ENV) return; Parse_Error(PARSE_FATAL, "Poisoned variable %s is already set\n", v->name); } }
char * Parse_ReadNextConditionalLine(Buffer linebuf) { int c; /* If first char isn't dot, skip to end of line, handling \ */ while ((c = read_char()) != '.') { for (;c != '\n'; c = read_char()) { if (c == '\\') { c = read_char(); if (c == '\n') current->lineno++; } if (c == EOF) { Parse_Error(PARSE_FATAL, "Unclosed conditional"); return NULL; } } current->lineno++; } /* This is the line we need to copy */ return Parse_ReadUnparsedLine(linebuf, "conditional"); }
char * Parse_ReadUnparsedLine(Buffer linebuf, const char *type) { int c; Buf_Reset(linebuf); c = read_char(); if (c == EOF) { Parse_Error(PARSE_FATAL, "Unclosed %s", type); return NULL; } /* Handle '\' at beginning of line, since \\n needs special treatment */ while (c == '\\') { c = read_char(); if (c == '\n') { current->lineno++; do { c = read_char(); } while (c == ' ' || c == '\t'); } else { Buf_AddChar(linebuf, '\\'); if (c == '\\') { Buf_AddChar(linebuf, '\\'); c = read_char(); } break; } } read_logical_line(linebuf, c); return Buf_Retrieve(linebuf); }
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 == '\0') Parse_Error(PARSE_FATAL, "Unterminated variable spec in %s", *pstr); else if (*tstr == ':') has_modifier = true; else tstr++; break; default: name->s = str+1; name->e = str+2; name->tofree = false; tstr = str + 2; break; } *pstr = tstr; return has_modifier; }
/* Delete global variable. */ void Var_Deletei(const char *name, const char *ename) { Var *v; uint32_t k; unsigned int slot; int idx; idx = classify_var(name, &ename, &k); if (idx != GLOBAL_INDEX) { Parse_Error(PARSE_FATAL, "Trying to delete dynamic variable $%s", varnames[idx]); return; } slot = ohash_lookup_interval(&global_variables, name, ename, k); v = ohash_find(&global_variables, slot); if (v == NULL) return; if (checkEnvFirst && (v->flags & VAR_FROM_ENV)) return; if (v->flags & VAR_FROM_CMD) return; ohash_remove(&global_variables, slot); delete_var(v); }
/*- *----------------------------------------------------------------------- * Cond_EvalExpression -- * Evaluate an expression in the passed line. The expression * consists of &&, ||, !, make(target), defined(variable) * and parenthetical groupings thereof. * * Results: * COND_PARSE if the condition was valid grammatically * COND_INVALID if not a valid conditional. * * (*value) is set to the boolean value of the condition * * Side Effects: * None. * *----------------------------------------------------------------------- */ int Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint) { static const struct If *dflt_info; const struct If *sv_if_info = if_info; char *sv_condExpr = condExpr; Token sv_condPushBack = condPushBack; int rval; while (*line == ' ' || *line == '\t') line++; if (info == NULL && (info = dflt_info) == NULL) { /* Scan for the entry for .if - it can't be first */ for (info = ifs; ; info++) if (info->form[0] == 0) break; dflt_info = info; } if_info = info != NULL ? info : ifs + 4; condExpr = line; condPushBack = TOK_NONE; rval = do_Cond_EvalExpression(value); if (rval == COND_INVALID && eprint) Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); if_info = sv_if_info; condExpr = sv_condExpr; condPushBack = sv_condPushBack; return rval; }
ImplementationArtifactDescription * IAD_Handler::resolve_iad (const ACE_TCHAR *uri) { DANCE_TRACE ("IAD_Handler::resolve_iad"); xercesc::DOMDocument *dom = XML::XML_Typedef::XML_HELPER.create_dom (uri); if (!dom) throw Parse_Error (ACE_TEXT ("Unable to create DOM for IAD")); try { return new ImplementationArtifactDescription (reader::implementationArtifactDescription (dom)); } catch (...) { throw Parse_Error (ACE_TEXT ("Unable to create XSC structure for IAD")); } }
/* Check if there's any reason not to use the variable in this context. */ static void poison_check(Var *v) { if (v->flags & POISON_NORMAL) { Parse_Error(PARSE_FATAL, "Poisoned variable %s has been referenced\n", v->name); return; } if (v->flags & VAR_DUMMY) { Parse_Error(PARSE_FATAL, "Poisoned variable %s is not defined\n", v->name); return; } if (v->flags & POISON_EMPTY) if (strcmp(var_get_value(v), "") == 0) Parse_Error(PARSE_FATAL, "Poisoned variable %s is empty\n", v->name); }
ComponentImplementationDescription * CID_Handler::resolve_cid (const ACE_TCHAR *uri) { DANCE_TRACE ("CID_Handler::resolve_cid"); xercesc::DOMDocument *dom = XML::XML_Typedef::XML_HELPER.create_dom (uri); if (!dom) throw Parse_Error (ACE_TEXT ("Unable to create DOM for CID")); try { return new ComponentImplementationDescription (reader::componentImplementationDescription (dom)); } catch (...) { throw Parse_Error ( ACE_TEXT ("Unable to create XSC structure for CID")); } }
int main() { double i_abc[3], i_alfabeta[2], i_dq[2]; double i_alfabeta_prime[2], i_dq_prime[2]; motor_err_t err; double theta; int i; //open loop simulation i_dq[ID]=0.0; i_dq[IQ]=1.0; //1 Amp for example printf("Header\n"); printf("i, theta, id, iq, ialfa, ibeta, ia, ib, ic, ialfa_prime, ibeta_prime, id_prime, iq_prime\n"); for (i=0; i<360; i++) { theta = TO_RADIANS(i); err = Convert_dq_to_alfabeta(i_dq, theta, i_alfabeta); Parse_Error(err, "Convert_dq_to_alfabeta", QUIET); err = Convert_alfabeta_to_abc(i_alfabeta, i_abc); Parse_Error(err, "Convert_dq_to_alfabeta", QUIET); err = Convert_abc_to_alfabeta(i_abc, i_alfabeta_prime); Parse_Error(err, "Convert_dq_to_alfabeta", QUIET); err = Convert_alfabeta_to_dq(i_alfabeta_prime, theta, i_dq_prime); Parse_Error(err, "Convert_dq_to_alfabeta", QUIET); printf("%d, %2.5f; %2.5f, %2.5f; %2.5f, %2.5f; %2.5f, %2.5f, %2.5f; %2.5f, %2.5f; %2.5f, %2.5f\n", i, theta, i_dq[ID], i_dq[IQ], i_alfabeta[IALFA], i_alfabeta[IBETA], i_abc[IA], i_abc[IB], i_abc[IC], i_alfabeta_prime[IALFA], i_alfabeta_prime[IBETA], i_dq_prime[ID], i_dq_prime[IQ]); } return 0; }
/* Set or add a global variable, in VAR_CMD or VAR_GLOBAL context. */ static void var_set_append(const char *name, const char *ename, const char *val, int ctxt, bool append) { Var *v; uint32_t k; int idx; idx = classify_var(name, &ename, &k); if (idx != GLOBAL_INDEX) { Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s", append ? "append to" : "set", varnames[idx]); return; } v = find_global_var(name, ename, k); if (v->flags & POISON_NORMAL) Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n", append ? "append to" : "set", v->name); /* so can we write to it ? */ if (ctxt == VAR_CMD) { /* always for command line */ (append ? var_append_value : var_set_value)(v, val); v->flags |= VAR_FROM_CMD; if ((v->flags & VAR_SHELL) == 0) { /* Any variables given on the command line are * automatically exported to the environment, * except for SHELL (as per POSIX standard). */ esetenv(v->name, val); } if (DEBUG(VAR)) printf("command:%s = %s\n", v->name, var_get_value(v)); } else if ((v->flags & VAR_FROM_CMD) == 0 && (!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) { (append ? var_append_value : var_set_value)(v, val); if (DEBUG(VAR)) printf("global:%s = %s\n", v->name, var_get_value(v)); } else if (DEBUG(VAR)) printf("overridden:%s = %s\n", v->name, var_get_value(v)); }
/*- *----------------------------------------------------------------------- * Cond_End -- * Make sure everything's clean at the end of a makefile. * * Results: * None. * * Side Effects: * Parse_Error will be called if open conditionals are around. * *----------------------------------------------------------------------- */ void Cond_restore_depth(unsigned int saved_depth) { int open_conds = cond_depth - cond_min_depth; if (open_conds != 0 || saved_depth > cond_depth) { Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, open_conds == 1 ? "" : "s"); cond_depth = cond_min_depth; } cond_min_depth = saved_depth; }
ComponentPackageDescription * CPD_Handler::resolve_cpd ( const ACE_TCHAR *uri) { DANCE_TRACE ("CPD_Handler::resolve_cpd"); if (!XML::XML_Typedef::XML_HELPER.is_initialized ()) return 0; xercesc::DOMDocument* dom = XML::XML_Typedef::XML_HELPER.create_dom (uri); if (!dom) throw Parse_Error ( ACE_TEXT ( "Unable to create DOM for component package description")); try { return new ComponentPackageDescription (reader::componentPackageDescription (dom)); } catch (...) { throw Parse_Error (ACE_TEXT ( "Unable to create XSC structure for CID")); } }
/*- *--------------------------------------------------------------------- * ParseDoOp -- * Apply the parsed operator to the given target node. Used in a * Array_Find call by ParseDoDependency once all targets have * been found and their operator parsed. If the previous and new * operators are incompatible, a major error is taken. * * Side Effects: * The type field of the node is altered to reflect any new bits in * the op. *--------------------------------------------------------------------- */ static int ParseDoOp(GNode **gnp, unsigned int op) { GNode *gn = *gnp; /* * If the dependency mask of the operator and the node don't match and * the node has actually had an operator applied to it before, and the * operator actually has some dependency information in it, complain. */ if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && !OP_NOP(gn->type) && !OP_NOP(op)) { Parse_Error(PARSE_FATAL, "Inconsistent dependency operator for target %s\n" "\t(was %s%s, now %s%s)", gn->name, gn->name, operator_string(gn->type), gn->name, operator_string(op)); return 0; } if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { /* If the node was the object of a :: operator, we need to * create a new instance of it for the children and commands on * this dependency line. The new instance is placed on the * 'cohorts' list of the initial one (note the initial one is * not on its own cohorts list) and the new instance is linked * to all parents of the initial instance. */ GNode *cohort; LstNode ln; cohort = Targ_NewGN(gn->name); /* Duplicate links to parents so graph traversal is simple. * Perhaps some type bits should be duplicated? * * Make the cohort invisible as well to avoid duplicating it * into other variables. True, parents of this target won't * tend to do anything with their local variables, but better * safe than sorry. */ for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) ParseLinkSrc((GNode *)Lst_Datum(ln), cohort); cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; Lst_AtEnd(&gn->cohorts, cohort); /* Replace the node in the targets list with the new copy */ *gnp = cohort; gn = cohort; } /* We don't want to nuke any previous flags (whatever they were) so we * just OR the new operator into the old. */ gn->type |= op; return 1; }
/* Figure out what kind of name we're looking for from a start character. */ static find_t find_pos(int c) { switch(c) { case '(': return find_rparen; case '{': return find_ket; default: Parse_Error(PARSE_FATAL, "Wrong character in variable spec %c (can't happen)"); return find_rparen; } }
void Cond_End(void) { int i; if (condTop != MAXIF) { Parse_Error(PARSE_FATAL, "%s%d open conditional%s", condTop == 0 ? "at least ": "", MAXIF-condTop, MAXIF-condTop == 1 ? "" : "s"); for (i = MAXIF-1; i >= condTop; i--) { fprintf(stderr, "\t at line %lu of %s\n", condStack[i].lineno, condStack[i].filename); } } condTop = MAXIF; }
/*- *----------------------------------------------------------------------- * 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; }
void Cond_End(void) { int i; if (condTop != MAXIF) { Parse_Error(PARSE_FATAL, "%s%d open conditional%s", condTop == 0 ? "at least ": "", MAXIF-condTop, MAXIF-condTop == 1 ? "" : "s"); for (i = MAXIF-1; i >= condTop; i--) { fprintf(stderr, "\t(%s:%lu)\n", condStack[i].origin.fname, condStack[i].origin.lineno); } } condTop = MAXIF; }
/*- *----------------------------------------------------------------------- * 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; }
/* XXX different semantics for Var_Valuei() and Var_Definedi(): * references to poisoned value variables will error out in Var_Valuei(), * but not in Var_Definedi(), so the following construct works: * .poison BINDIR * BINDIR ?= /usr/bin */ char * Var_Valuei(const char *name, const char *ename) { Var *v; uint32_t k; int idx; idx = classify_var(name, &ename, &k); if (idx != GLOBAL_INDEX) { Parse_Error(PARSE_FATAL, "Trying to get value of dynamic variable $%s", varnames[idx]); return NULL; } v = find_global_var(name, ename, k); if (v->flags & POISONS) poison_check(v); if ((v->flags & VAR_DUMMY) == 0) return var_get_value(v); else return NULL; }
/*- *----------------------------------------------------------------------- * Cond_Eval -- * Evaluate the conditional in the passed line. The line * looks like this: * .<cond-type> <expr> * where <cond-type> is any of if, ifmake, ifnmake, ifdef, * ifndef, elif, elifmake, elifnmake, elifdef, elifndef * and <expr> consists of &&, ||, !, make(target), defined(variable) * and parenthetical groupings thereof. * * Input: * line Line to parse * * Results: * COND_PARSE if should parse lines after the conditional * COND_SKIP if should skip lines after the conditional * COND_INVALID if not a valid conditional. * * Side Effects: * None. * * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) * otherwise .else could be treated as '.elif 1'. * *----------------------------------------------------------------------- */ int Cond_Eval(char *line) { #define MAXIF 64 /* maximum depth of .if'ing */ enum if_states { IF_ACTIVE, /* .if or .elif part active */ ELSE_ACTIVE, /* .else part active */ SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ SKIP_TO_ELSE, /* has been true, but not seen '.else' */ SKIP_TO_ENDIF /* nothing else to execute */ }; static enum if_states cond_state[MAXIF + 1] = { IF_ACTIVE }; const struct If *ifp; Boolean isElif; Boolean value; int level; /* Level at which to report errors. */ enum if_states state; level = PARSE_FATAL; /* skip leading character (the '.') and any whitespace */ for (line++; *line == ' ' || *line == '\t'; line++) continue; /* Find what type of if we're dealing with. */ if (line[0] == 'e') { if (line[1] != 'l') { if (!istoken(line + 1, "ndif", 4)) return COND_INVALID; /* End of conditional section */ if (cond_depth == cond_min_depth) { Parse_Error(level, "if-less endif"); return COND_PARSE; } /* Return state for previous conditional */ cond_depth--; if (cond_depth > MAXIF) return COND_SKIP; return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; } /* Quite likely this is 'else' or 'elif' */ line += 2; if (istoken(line, "se", 2)) { /* It is else... */ if (cond_depth == cond_min_depth) { Parse_Error(level, "if-less else"); return COND_PARSE; } if (cond_depth > MAXIF) return COND_SKIP; state = cond_state[cond_depth]; switch (state) { case SEARCH_FOR_ELIF: state = ELSE_ACTIVE; break; case ELSE_ACTIVE: case SKIP_TO_ENDIF: Parse_Error(PARSE_WARNING, "extra else"); /* FALLTHROUGH */ default: case IF_ACTIVE: case SKIP_TO_ELSE: state = SKIP_TO_ENDIF; break; } cond_state[cond_depth] = state; return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; } /* Assume for now it is an elif */ isElif = TRUE; } else isElif = FALSE; if (line[0] != 'i' || line[1] != 'f') /* Not an ifxxx or elifxxx line */ return COND_INVALID; /* * Figure out what sort of conditional it is -- what its default * function is, etc. -- by looking in the table of valid "ifs" */ line += 2; for (ifp = ifs; ; ifp++) { if (ifp->form == NULL) return COND_INVALID; if (istoken(ifp->form, line, ifp->formlen)) { line += ifp->formlen; break; } } /* Now we know what sort of 'if' it is... */ if (isElif) { if (cond_depth == cond_min_depth) { Parse_Error(level, "if-less elif"); return COND_PARSE; } if (cond_depth > MAXIF) /* Error reported when we saw the .if ... */ return COND_SKIP; state = cond_state[cond_depth]; if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { Parse_Error(PARSE_WARNING, "extra elif"); cond_state[cond_depth] = SKIP_TO_ENDIF; return COND_SKIP; } if (state != SEARCH_FOR_ELIF) { /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ cond_state[cond_depth] = SKIP_TO_ELSE; return COND_SKIP; } } else { /* Normal .if */ if (cond_depth >= MAXIF) { cond_depth++; Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); return COND_SKIP; } state = cond_state[cond_depth]; cond_depth++; if (state > ELSE_ACTIVE) { /* If we aren't parsing the data, treat as always false */ cond_state[cond_depth] = SKIP_TO_ELSE; return COND_SKIP; } } /* And evaluate the conditional expresssion */ if (Cond_EvalExpression(ifp, line, &value, 1) == COND_INVALID) { /* Syntax error in conditional, error message already output. */ /* Skip everything to matching .endif */ cond_state[cond_depth] = SKIP_TO_ELSE; return COND_SKIP; } if (!value) { cond_state[cond_depth] = SEARCH_FOR_ELIF; return COND_SKIP; } cond_state[cond_depth] = IF_ACTIVE; return COND_PARSE; }
/*- *----------------------------------------------------------------------- * CondGetArg -- * Find the argument of a built-in function. * * Input: * parens TRUE if arg should be bounded by parens * * Results: * The length of the argument and the address of the argument. * * Side Effects: * The pointer is set to point to the closing parenthesis of the * function call. * *----------------------------------------------------------------------- */ static int CondGetArg(char **linePtr, char **argPtr, const char *func) { char *cp; int argLen; Buffer buf; int paren_depth; char ch; cp = *linePtr; if (func != NULL) /* Skip opening '(' - verfied by caller */ 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... */ *argPtr = NULL; return (0); } while (*cp == ' ' || *cp == '\t') { cp++; } /* * Create a buffer for the argument and start it out at 16 characters * long. Why 16? Why not? */ Buf_Init(&buf, 16); paren_depth = 0; for (;;) { ch = *cp; if (ch == 0 || ch == ' ' || ch == '\t') break; if ((ch == '&' || ch == '|') && paren_depth == 0) break; if (*cp == '$') { /* * Parse the variable spec and install it as part of the argument * if it's valid. We tell Var_Parse to complain on an undefined * variable, so we don't do it too. Nor do we return an error, * though perhaps we should... */ char *cp2; int len; void *freeIt; cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES, &len, &freeIt); Buf_AddBytes(&buf, strlen(cp2), cp2); free(freeIt); cp += len; continue; } if (ch == '(') paren_depth++; else if (ch == ')' && --paren_depth < 0) break; Buf_AddByte(&buf, *cp); cp++; } *argPtr = Buf_GetAll(&buf, &argLen); Buf_Destroy(&buf, FALSE); while (*cp == ' ' || *cp == '\t') { cp++; } if (func != NULL && *cp++ != ')') { Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", func); return (0); } *linePtr = cp; return (argLen); }
/*- *----------------------------------------------------------------------- * CondToken -- * Return the next token from the input. * * Results: * A Token for the next lexical token in the stream. * * Side Effects: * condPushback will be set back to TOK_NONE if it is used. * *----------------------------------------------------------------------- */ static Token compare_expression(Boolean doEval) { Token t; char *lhs; char *rhs; char *op; void *lhsFree; void *rhsFree; Boolean lhsQuoted; Boolean rhsQuoted; double left, right; t = TOK_ERROR; rhs = NULL; lhsFree = rhsFree = FALSE; lhsQuoted = rhsQuoted = FALSE; /* * Parse the variable spec and skip over it, saving its * value in lhs. */ lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict); if (!lhs) goto done; /* * Skip whitespace to get to the operator */ while (isspace((unsigned char) *condExpr)) condExpr++; /* * Make sure the operator is a valid one. If it isn't a * known relational operator, pretend we got a * != 0 comparison. */ op = condExpr; switch (*condExpr) { case '!': case '=': case '<': case '>': if (condExpr[1] == '=') { condExpr += 2; } else { condExpr += 1; } break; default: if (!doEval) { t = TOK_FALSE; goto done; } /* For .ifxxx "..." check for non-empty string. */ if (lhsQuoted) { t = lhs[0] != 0; goto done; } /* For .ifxxx <number> compare against zero */ if (CondCvtArg(lhs, &left)) { t = left != 0.0; goto done; } /* For .if ${...} check for non-empty string (defProc is ifdef). */ if (if_info->form[0] == 0) { t = lhs[0] != 0; goto done; } /* Otherwise action default test ... */ t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; goto done; } while (isspace((unsigned char)*condExpr)) condExpr++; if (*condExpr == '\0') { Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator"); goto done; } rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE); if (!rhs) goto done; if (rhsQuoted || lhsQuoted) { do_string_compare: if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { Parse_Error(PARSE_WARNING, "String comparison operator should be either == or !="); goto done; } if (DEBUG(COND)) { fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op); } /* * Null-terminate rhs and perform the comparison. * t is set to the result. */ if (*op == '=') { t = strcmp(lhs, rhs) == 0; } else { t = strcmp(lhs, rhs) != 0; } } else { /* * rhs is either a float or an integer. Convert both the * lhs and the rhs to a double and compare the two. */ if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) goto do_string_compare; if (DEBUG(COND)) { fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, right, op); } switch(op[0]) { case '!': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto done; } t = (left != right); break; case '=': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto done; } t = (left == right); break; case '<': if (op[1] == '=') { t = (left <= right); } else { t = (left < right); } break; case '>': if (op[1] == '=') { t = (left >= right); } else { t = (left > right); } break; } } done: free(lhsFree); free(rhsFree); return t; }
/** * Parse a shell specification and set up commandShell appropriately. * * Results: * TRUE if the specification was correct. FALSE otherwise. * * Side Effects: * commandShell points to a Shell structure. * created from the shell spec). * * Notes: * A shell specification consists of a .SHELL target, with dependency * operator, followed by a series of blank-separated words. Double * quotes can be used to use blanks in words. A backslash escapes * anything (most notably a double-quote and a space) and * provides the functionality it does in C. Each word consists of * keyword and value separated by an equal sign. There should be no * unnecessary spaces in the word. The keywords are as follows: * name Name of shell. * path Location of shell. Overrides "name" if given * quiet Command to turn off echoing. * echo Command to turn echoing on * filter Result of turning off echoing that shouldn't be * printed. * echoFlag Flag to turn echoing on at the start * errFlag Flag to turn error checking on at the start * hasErrCtl True if shell has error checking control * check Command to turn on error checking if hasErrCtl * is TRUE or template of command to echo a command * for which error checking is off if hasErrCtl is * FALSE. * ignore Command to turn off error checking if hasErrCtl * is TRUE or template of command to execute a * command so as to ignore any errors it returns if * hasErrCtl is FALSE. * builtins A space separated list of builtins. If one * of these builtins is detected when make wants * to execute a command line, the command line is * handed to the shell. Otherwise make may try to * execute the command directly. If this list is empty * it is assumed, that the command must always be * handed over to the shell. * meta The shell meta characters. If this is not specified * or empty, commands are alway passed to the shell. * Otherwise they are not passed when they contain * neither a meta character nor a builtin command. * unsetenv Unsetenv("ENV") before executing anything. */ Boolean Shell_Parse(const char line[]) { Boolean fullSpec; struct Shell *sh; struct Shell *match; /* parse the specification */ if ((sh = ShellParseSpec(line, &fullSpec)) == NULL) return (FALSE); if (sh->path == NULL) { /* * If no path was given, the user wants one of the pre-defined * shells, yes? So we find the one s/he wants with the help of * JobMatchShell and set things up the right way. */ if (sh->name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); ShellFree(sh); return (FALSE); } if (fullSpec) { /* * XXX May want to merge sh into match. But this * require ShellParseSpec to return information * which attributes actuall have been specified. */ Parse_Error(PARSE_FATAL, "No path specified"); ShellFree(sh); return (FALSE); } if ((match = ShellMatch(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); ShellFree(sh); return (FALSE); } ShellFree(sh); commandShell = match; return (TRUE); } /* * The user provided a path. If s/he gave nothing else * (fullSpec is FALSE), try and find a matching shell in the * ones we know of. Else we just take the specification at its * word and copy it to a new location. In either case, we need * to record the path the user gave for the shell. */ if (sh->name == NULL) { /* get the base name as the name */ if ((sh->name = strrchr(sh->path, '/')) == NULL) { sh->name = estrdup(sh->path); } else { sh->name = estrdup(sh->name + 1); } } if (!fullSpec) { if ((match = ShellMatch(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); ShellFree(sh); return (FALSE); } /* set the patch on the matching shell */ free(match->path); match->path = sh->path; sh->path = NULL; ShellFree(sh); commandShell = match; return (TRUE); } TAILQ_INSERT_HEAD(&shells, sh, link); /* set the new shell */ commandShell = sh; 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; }
/** * Given the line following a .SHELL target, parse it as a shell * specification. * * Results: * A pointer to a Shell structure, or NULL if no the spec was invalid. */ Shell * Shell_Parse(const char line[]) { bool fullSpec; Shell *sh; /* parse the specification */ if ((sh = ShellParseSpec(line, &fullSpec)) == NULL) return (NULL); if (sh->path == NULL) { Shell *match; /* * If no path was given, the user wants one of the pre-defined * shells, yes? So we find the one s/he wants with the help of * ShellMatch and set things up the right way. */ if (sh->name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); Shell_Destroy(sh); return (NULL); } if (fullSpec) { Parse_Error(PARSE_FATAL, "No path specified"); Shell_Destroy(sh); return (NULL); } if ((match = Shell_Match(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); Shell_Destroy(sh); return (NULL); } Shell_Destroy(sh); return (match); } else { Shell *match; /* * The user provided a path. If s/he gave nothing else * (fullSpec is false), try and find a matching shell in the * ones we know of. Else we just take the specification at its * word and copy it to a new location. In either case, we need * to record the path the user gave for the shell. */ if (sh->name == NULL) { /* get the base name as the name */ if ((sh->name = strrchr(sh->path, '/')) == NULL) { sh->name = estrdup(sh->path); } else { sh->name = estrdup(sh->name + 1); } } if (fullSpec) { return (sh); } if ((match = Shell_Match(sh->name)) == NULL) { Parse_Error(PARSE_FATAL, "%s: no matching shell", sh->name); Shell_Destroy(sh); return (NULL); } free(match->path); match->path = sh->path; sh->path = NULL; Shell_Destroy(sh); return (match); } }
static Token CondHandleComparison(char *lhs, bool doFree, bool doEval) { Token t; const char *rhs; const char *op; t = Err; /* Skip whitespace to get to the operator. */ while (isspace(*condExpr)) condExpr++; /* Make sure the operator is a valid one. If it isn't a * known relational operator, pretend we got a * != 0 comparison. */ op = condExpr; switch (*condExpr) { case '!': case '=': case '<': case '>': if (condExpr[1] == '=') condExpr += 2; else condExpr += 1; break; default: op = "!="; rhs = "0"; goto do_compare; } while (isspace(*condExpr)) condExpr++; if (*condExpr == '\0') { Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator"); goto error; } rhs = condExpr; do_compare: if (*rhs == '"') { /* Doing a string comparison. Only allow == and != for * operators. */ char *string; const char *cp; int qt; BUFFER buf; do_string_compare: if ((*op != '!' && *op != '=') || op[1] != '=') { Parse_Error(PARSE_WARNING, "String comparison operator should be either == or !="); goto error; } Buf_Init(&buf, 0); qt = *rhs == '"' ? 1 : 0; for (cp = &rhs[qt]; ((qt && *cp != '"') || (!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) { if (*cp == '$') { size_t len; if (Var_ParseBuffer(&buf, cp, NULL, doEval, &len)) { cp += len; continue; } } else if (*cp == '\\' && cp[1] != '\0') /* Backslash escapes things -- skip over next * character, if it exists. */ cp++; Buf_AddChar(&buf, *cp++); } string = Buf_Retrieve(&buf); if (DEBUG(COND)) printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, string, op); /* Null-terminate rhs and perform the comparison. * t is set to the result. */ if (*op == '=') t = strcmp(lhs, string) ? False : True; else t = strcmp(lhs, string) ? True : False; free(string); if (rhs == condExpr) { if (!qt && *cp == ')') condExpr = cp; else if (*cp == '\0') condExpr = cp; else condExpr = cp + 1; } } else { /* rhs is either a float or an integer. Convert both the * lhs and the rhs to a double and compare the two. */ double left, right; char *string; if (!CondCvtArg(lhs, &left)) goto do_string_compare; if (*rhs == '$') { size_t len; bool freeIt; string = Var_Parse(rhs, NULL, doEval,&len,&freeIt); if (string == var_Error) right = 0.0; else { if (!CondCvtArg(string, &right)) { if (freeIt) free(string); goto do_string_compare; } if (freeIt) free(string); if (rhs == condExpr) condExpr += len; } } else { if (!CondCvtArg(rhs, &right)) goto do_string_compare; if (rhs == condExpr) { /* Skip over the right-hand side. */ while (!isspace(*condExpr) && *condExpr != '\0') condExpr++; } } if (DEBUG(COND)) printf("left = %f, right = %f, op = %.2s\n", left, right, op); switch (op[0]) { case '!': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto error; } t = left != right ? True : False; break; case '=': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto error; } t = left == right ? True : False; break; case '<': if (op[1] == '=') t = left <= right ? True : False; else t = left < right ? True : False; break; case '>': if (op[1] == '=') t = left >= right ? True : False; else t = left > right ? True : False; break; } } error: if (doFree) free(lhs); return t; }
/* Evaluate conditional in line. * returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE, * COND_ISUNDEF. * A conditional line looks like this: * <cond-type> <expr> * where <cond-type> is any of if, ifmake, ifnmake, ifdef, * ifndef, elif, elifmake, elifnmake, elifdef, elifndef * and <expr> consists of &&, ||, !, make(target), defined(variable) * and parenthetical groupings thereof. */ int Cond_Eval(const char *line) { /* find end of keyword */ const char *end; uint32_t k; size_t len; struct If *ifp; bool value = false; int level; /* Level at which to report errors. */ level = PARSE_FATAL; for (end = line; islower(*end); end++) ; /* quick path: recognize special targets early on */ if (*end == '.' || *end == ':') return COND_INVALID; len = end - line; k = ohash_interval(line, &end); switch(k % MAGICSLOTS2) { case K_COND_IF % MAGICSLOTS2: if (k == K_COND_IF && len == strlen(COND_IF) && strncmp(line, COND_IF, len) == 0) { ifp = ifs + COND_IF_INDEX; } else return COND_INVALID; break; case K_COND_IFDEF % MAGICSLOTS2: if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) && strncmp(line, COND_IFDEF, len) == 0) { ifp = ifs + COND_IFDEF_INDEX; } else return COND_INVALID; break; case K_COND_IFNDEF % MAGICSLOTS2: if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) && strncmp(line, COND_IFNDEF, len) == 0) { ifp = ifs + COND_IFNDEF_INDEX; } else return COND_INVALID; break; case K_COND_IFMAKE % MAGICSLOTS2: if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) && strncmp(line, COND_IFMAKE, len) == 0) { ifp = ifs + COND_IFMAKE_INDEX; } else return COND_INVALID; break; case K_COND_IFNMAKE % MAGICSLOTS2: if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) && strncmp(line, COND_IFNMAKE, len) == 0) { ifp = ifs + COND_IFNMAKE_INDEX; } else return COND_INVALID; break; case K_COND_ELIF % MAGICSLOTS2: if (k == K_COND_ELIF && len == strlen(COND_ELIF) && strncmp(line, COND_ELIF, len) == 0) { ifp = ifs + COND_ELIF_INDEX; } else return COND_INVALID; break; case K_COND_ELIFDEF % MAGICSLOTS2: if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) && strncmp(line, COND_ELIFDEF, len) == 0) { ifp = ifs + COND_ELIFDEF_INDEX; } else return COND_INVALID; break; case K_COND_ELIFNDEF % MAGICSLOTS2: if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) && strncmp(line, COND_ELIFNDEF, len) == 0) { ifp = ifs + COND_ELIFNDEF_INDEX; } else return COND_INVALID; break; case K_COND_ELIFMAKE % MAGICSLOTS2: if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) && strncmp(line, COND_ELIFMAKE, len) == 0) { ifp = ifs + COND_ELIFMAKE_INDEX; } else return COND_INVALID; break; case K_COND_ELIFNMAKE % MAGICSLOTS2: if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) && strncmp(line, COND_ELIFNMAKE, len) == 0) { ifp = ifs + COND_ELIFNMAKE_INDEX; } else return COND_INVALID; break; case K_COND_ELSE % MAGICSLOTS2: /* valid conditional whose value is the inverse * of the previous if we parsed. */ if (k == K_COND_ELSE && len == strlen(COND_ELSE) && strncmp(line, COND_ELSE, len) == 0) { if (condTop == MAXIF) { Parse_Error(level, "if-less else"); return COND_INVALID; } else if (skipIfLevel == 0) { value = !condStack[condTop].value; ifp = ifs + COND_ELSE_INDEX; } else return COND_SKIP; } else return COND_INVALID; break; case K_COND_ENDIF % MAGICSLOTS2: if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) && strncmp(line, COND_ENDIF, len) == 0) { /* End of a conditional section. If skipIfLevel is * non-zero, that conditional was skipped, so lines * following it should also be skipped. Hence, we * return COND_SKIP. Otherwise, the conditional was * read so succeeding lines should be parsed (think * about it...) so we return COND_PARSE, unless this * endif isn't paired with a decent if. */ if (skipIfLevel != 0) { skipIfLevel--; return COND_SKIP; } else { if (condTop == MAXIF) { Parse_Error(level, "if-less endif"); return COND_INVALID; } else { skipLine = false; condTop++; return COND_PARSE; } } } else return COND_INVALID; break; /* Recognize other keywords there, to simplify parser's task */ case K_COND_FOR % MAGICSLOTS2: if (k == K_COND_FOR && len == strlen(COND_FOR) && strncmp(line, COND_FOR, len) == 0) return COND_ISFOR; else return COND_INVALID; case K_COND_UNDEF % MAGICSLOTS2: if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) && strncmp(line, COND_UNDEF, len) == 0) return COND_ISUNDEF; else return COND_INVALID; case K_COND_POISON % MAGICSLOTS2: if (k == K_COND_POISON && len == strlen(COND_POISON) && strncmp(line, COND_POISON, len) == 0) return COND_ISPOISON; else return COND_INVALID; case K_COND_INCLUDE % MAGICSLOTS2: if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) && strncmp(line, COND_INCLUDE, len) == 0) return COND_ISINCLUDE; else return COND_INVALID; default: /* Not a valid conditional type. No error... */ return COND_INVALID; } if (ifp->isElse) { if (condTop == MAXIF) { Parse_Error(level, "if-less elif"); return COND_INVALID; } else if (skipIfLevel != 0 || condStack[condTop].value) { /* * Skip if we're meant to or is an else-type * conditional and previous corresponding one was * evaluated to true. */ skipLine = true; return COND_SKIP; } } else if (skipLine) { /* Don't even try to evaluate a conditional that's not an else * if we're skipping things... */ skipIfLevel++; return COND_SKIP; } else condTop--; if (condTop < 0) { /* This is the one case where we can definitely proclaim a fatal * error. If we don't, we're hosed. */ Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); condTop = 0; return COND_INVALID; } if (ifp->defProc) { /* Initialize file-global variables for parsing. */ condDefProc = ifp->defProc; condInvert = ifp->doNot; line += len; while (*line == ' ' || *line == '\t') line++; condExpr = line; condPushBack = None; switch (CondE(true)) { case True: if (CondToken(true) == EndOfFile) { value = true; break; } goto err; /* FALLTHROUGH */ case False: if (CondToken(true) == EndOfFile) { value = false; break; } /* FALLTHROUGH */ case Err: err: Parse_Error(level, "Malformed conditional (%s)", line); return COND_INVALID; default: break; } } condStack[condTop].value = value; Parse_FillLocation(&condStack[condTop].origin); skipLine = !value; return value ? COND_PARSE : COND_SKIP; }
char * Var_Subst(const char *str, /* the string in which to substitute */ SymTable *ctxt, /* the context wherein to find variables */ bool undefErr) /* true if undefineds are an error */ { BUFFER buf; /* Buffer for forming things */ static bool errorReported; Buf_Init(&buf, MAKE_BSIZE); errorReported = false; for (;;) { char *val; /* Value to substitute for a variable */ size_t length; /* Length of the variable invocation */ bool doFree; /* Set true if val should be freed */ const char *cp; /* copy uninteresting stuff */ for (cp = str; *str != '\0' && *str != '$'; str++) ; Buf_Addi(&buf, cp, str); if (*str == '\0') break; if (str[1] == '$') { /* A $ may be escaped with another $. */ Buf_AddChar(&buf, '$'); str += 2; continue; } val = Var_Parse(str, ctxt, undefErr, &length, &doFree); /* When we come down here, val should either point to the * value of this variable, suitably modified, or be NULL. * Length should be the total length of the potential * variable invocation (from $ to end character...) */ if (val == var_Error || val == varNoError) { /* If errors are not an issue, skip over the variable * and continue with the substitution. Otherwise, store * the dollar sign and advance str so we continue with * the string... */ if (errorIsOkay) str += length; else if (undefErr) { /* If variable is undefined, complain and * skip the variable name. The complaint * will stop us from doing anything when * the file is parsed. */ if (!errorReported) Parse_Error(PARSE_FATAL, "Undefined variable \"%.*s\"", length, str); str += length; errorReported = true; } else { Buf_AddChar(&buf, *str); str++; } } else { /* We've now got a variable structure to store in. * But first, advance the string pointer. */ str += length; /* Copy all the characters from the variable value * straight into the new string. */ Buf_AddString(&buf, val); if (doFree) free(val); } } return Buf_Retrieve(&buf); }
/** * Parse a shell specification line and return the new Shell structure. * In case of an error a message is printed and NULL is returned. * * Notes: * A shell specification consists of a .SHELL target, with dependency * operator, followed by a series of blank-separated words. Double * quotes can be used to use blanks in words. A backslash escapes * anything (most notably a double-quote and a space) and * provides the functionality it does in C. Each word consists of * keyword and value separated by an equal sign. There should be no * unnecessary spaces in the word. The keywords are as follows: * name Name of shell. * path Location of shell. Overrides "name" if given * quiet Command to turn off echoing. * echo Command to turn echoing on * filter Result of turning off echoing that shouldn't be * printed. * echoFlag Flag to turn echoing on at the start * errFlag Flag to turn error checking on at the start * hasErrCtl True if shell has error checking control * check Command to turn on error checking if hasErrCtl * is true or template of command to echo a command * for which error checking is off if hasErrCtl is * false. * ignore Command to turn off error checking if hasErrCtl * is true or template of command to execute a * command so as to ignore any errors it returns if * hasErrCtl is false. * builtins A space separated list of builtins. If one * of these builtins is detected when make wants * to execute a command line, the command line is * handed to the shell. Otherwise make may try to * execute the command directly. If this list is empty * it is assumed, that the command must always be * handed over to the shell. * meta The shell meta characters. If this is not specified * or empty, commands are alway passed to the shell. * Otherwise they are not passed when they contain * neither a meta character nor a builtin command. */ static Shell * ShellParseSpec(const char spec[], bool *fullSpec) { ArgArray aa; Shell *sh; char *eq; char *keyw; int arg; *fullSpec = false; sh = emalloc(sizeof(*sh)); memset(sh, 0, sizeof(*sh)); ArgArray_Init(&sh->builtins); /* * Parse the specification by keyword but skip the first word */ brk_string(&aa, spec, true); for (arg = 1; arg < aa.argc; arg++) { /* * Split keyword and value */ keyw = aa.argv[arg]; if ((eq = strchr(keyw, '=')) == NULL) { Parse_Error(PARSE_FATAL, "missing '=' in shell " "specification keyword '%s'", keyw); ArgArray_Done(&aa); Shell_Destroy(sh); return (NULL); } *eq++ = '\0'; if (strcmp(keyw, "path") == 0) { free(sh->path); sh->path = estrdup(eq); } else if (strcmp(keyw, "name") == 0) { free(sh->name); sh->name = estrdup(eq); } else if (strcmp(keyw, "quiet") == 0) { free(sh->echoOff); sh->echoOff = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "echo") == 0) { free(sh->echoOn); sh->echoOn = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "filter") == 0) { free(sh->noPrint); sh->noPrint = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "echoFlag") == 0) { free(sh->echo); sh->echo = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "errFlag") == 0) { free(sh->exit); sh->exit = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "hasErrCtl") == 0) { sh->hasErrCtl = ( *eq == 'Y' || *eq == 'y' || *eq == 'T' || *eq == 't'); *fullSpec = true; } else if (strcmp(keyw, "check") == 0) { free(sh->errCheck); sh->errCheck = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "ignore") == 0) { free(sh->ignErr); sh->ignErr = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "builtins") == 0) { ArgArray_Done(&sh->builtins); brk_string(&sh->builtins, eq, true); qsort(sh->builtins.argv + 1, sh->builtins.argc - 1, sizeof(char *), sort_builtins); *fullSpec = true; } else if (strcmp(keyw, "meta") == 0) { free(sh->meta); sh->meta = estrdup(eq); *fullSpec = true; } else if (strcmp(keyw, "unsetenv") == 0) { sh->unsetenv = ( *eq == 'Y' || *eq == 'y' || *eq == 'T' || *eq == 't'); *fullSpec = true; } else { Parse_Error(PARSE_FATAL, "unknown keyword in shell " "specification '%s'", keyw); ArgArray_Done(&aa); Shell_Destroy(sh); return (NULL); } } ArgArray_Done(&aa); /* * Some checks (could be more) */ if (*fullSpec) { if ((sh->echoOn != NULL) ^ (sh->echoOff != NULL)) { Parse_Error(PARSE_FATAL, "Shell must have either both " "echoOff and echoOn or none of them"); Shell_Destroy(sh); return (NULL); } if (sh->echoOn != NULL && sh->echoOff != NULL) sh->hasEchoCtl = true; } return (sh); }