void ScopeAugment(EvalContext *ctx, const Bundle *bp, const Rlist *arguments) { if (RlistLen(bp->args) != RlistLen(arguments)) { CfOut(OUTPUT_LEVEL_ERROR, "", "While constructing scope \"%s\"\n", bp->name); fprintf(stderr, "Formal = "); RlistShow(stderr, bp->args); fprintf(stderr, ", Actual = "); RlistShow(stderr, arguments); fprintf(stderr, "\n"); FatalError(ctx, "Augment scope, formal and actual parameter mismatch is fatal"); } for (const Rlist *rpl = bp->args, *rpr = arguments; rpl != NULL; rpl = rpl->next, rpr = rpr->next) { const char *lval = rpl->item; CfOut(OUTPUT_LEVEL_VERBOSE, "", " ? Augment scope %s with %s (%c)\n", bp->name, lval, rpr->type); // CheckBundleParameters() already checked that there is no namespace collision // By this stage all functions should have been expanded, so we only have scalars left if (IsNakedVar(rpr->item, '@')) { DataType vtype; char qnaked[CF_MAXVARSIZE]; char naked[CF_BUFSIZE]; GetNaked(naked, rpr->item); if (IsQualifiedVariable(naked) && strchr(naked, CF_NS) == NULL) { snprintf(qnaked, CF_MAXVARSIZE, "%s%c%s", bp->ns, CF_NS, naked); } Rval retval; EvalContextVariableGet(ctx, (VarRef) { NULL, bp->name, qnaked }, &retval, &vtype); switch (vtype) { case DATA_TYPE_STRING_LIST: case DATA_TYPE_INT_LIST: case DATA_TYPE_REAL_LIST: ScopeNewList(ctx, (VarRef) { NULL, bp->name, lval }, RvalCopy((Rval) { retval.item, RVAL_TYPE_LIST}).item, DATA_TYPE_STRING_LIST); break; default: CfOut(OUTPUT_LEVEL_ERROR, "", " !! List parameter \"%s\" not found while constructing scope \"%s\" - use @(scope.variable) in calling reference", qnaked, bp->name); ScopeNewScalar(ctx, (VarRef) { NULL, bp->name, lval }, rpr->item, DATA_TYPE_STRING); break; } } else { switch(rpr->type) { case RVAL_TYPE_SCALAR: ScopeNewScalar(ctx, (VarRef) { NULL, bp->name, lval }, rpr->item, DATA_TYPE_STRING); break; case RVAL_TYPE_FNCALL: { FnCall *subfp = rpr->item; Promise *pp = NULL; // This argument should really get passed down. Rval rval = FnCallEvaluate(ctx, subfp, pp).rval; if (rval.type == RVAL_TYPE_SCALAR) { ScopeNewScalar(ctx, (VarRef) { NULL, bp->name, lval }, rval.item, DATA_TYPE_STRING); } else { CfOut(OUTPUT_LEVEL_ERROR, "", "Only functions returning scalars can be used as arguments"); } } break; default: ProgrammingError("An argument neither a scalar nor a list seemed to appear. Impossible"); } } } /* Check that there are no danglers left to evaluate in the hash table itself */ { Scope *ptr = ScopeGet(bp->name); AssocHashTableIterator i = HashIteratorInit(ptr->hashtable); CfAssoc *assoc = NULL; while ((assoc = HashIteratorNext(&i))) { Rval retval = ExpandPrivateRval(ctx, bp->name, assoc->rval); // Retain the assoc, just replace rval RvalDestroy(assoc->rval); assoc->rval = retval; } } return; }
static void ExpandAndMapIteratorsFromScalar(EvalContext *ctx, const Bundle *bundle, char *string, size_t length, int level, Rlist **scalars, Rlist **lists, Rlist **containers, Rlist **full_expansion) { assert(string); if (!string) { return; } Buffer *value = BufferNew(); for (size_t i = 0; i < length; i++) { const char *sp = string + i; Rlist *tmp_list = NULL; BufferZero(value); if (ExtractScalarPrefix(value, sp, length - i)) { if (full_expansion) { RlistConcatInto(&tmp_list, *full_expansion, BufferData(value)); RlistDestroy(*full_expansion); *full_expansion = tmp_list; tmp_list = NULL; } sp += BufferSize(value); i += BufferSize(value); BufferZero(value); if (i >= length) { break; } } if (*sp == '$') { BufferZero(value); ExtractScalarReference(value, sp, length - i, true); if (BufferSize(value) > 0) { Rlist *inner_expansion = NULL; Rlist *exp = NULL; int success = 0; VarRef *ref = VarRefParse(BufferData(value)); int increment = BufferSize(value) - 1 + 3; // Handle any embedded variables char *substring = string + i + 2; ExpandAndMapIteratorsFromScalar(ctx, bundle, substring, BufferSize(value), level+1, scalars, lists, containers, &inner_expansion); for (exp = inner_expansion; exp != NULL; exp = exp->next) { // If a list is non-local, i.e. $(bundle.var), map it to local $(bundle#var) // NB without modifying variables as we map them, it's not // possible to handle remote lists referenced by a variable // scope. For example: // scope => "test."; var => "somelist"; $($(scope)$(var)) fails // varname => "test.somelist"; $($(varname)) also fails // TODO Unless the consumer handles it? const char *inner_ref_str = RlistScalarValue(exp); VarRef *inner_ref = VarRefParseFromBundle(inner_ref_str, bundle); // var is the expanded name of the variable in its native context // finalname will be the mapped name in the local context "this." DataType value_type = DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, inner_ref, &value_type); if (value) { char *mangled_inner_ref = xstrdup(inner_ref_str); MangleVarRefString(mangled_inner_ref, strlen(mangled_inner_ref)); success++; switch (DataTypeToRvalType(value_type)) { case RVAL_TYPE_LIST: if (level > 0) { RlistPrependScalarIdemp(lists, mangled_inner_ref); } else { RlistAppendScalarIdemp(lists, mangled_inner_ref); } if (full_expansion) { for (const Rlist *rp = value; rp != NULL; rp = rp->next) { // append each slist item to each of full_expansion RlistConcatInto(&tmp_list, *full_expansion, RlistScalarValue(rp)); } } break; case RVAL_TYPE_SCALAR: RlistAppendScalarIdemp(scalars, mangled_inner_ref); if (full_expansion) { // append the scalar value to each of full_expansion RlistConcatInto(&tmp_list, *full_expansion, value); } break; case RVAL_TYPE_CONTAINER: if (level > 0) { RlistPrependScalarIdemp(containers, mangled_inner_ref); } else { RlistAppendScalarIdemp(containers, mangled_inner_ref); } break; case RVAL_TYPE_FNCALL: case RVAL_TYPE_NOPROMISEE: break; } free(mangled_inner_ref); } VarRefDestroy(inner_ref); } RlistDestroy(inner_expansion); if (full_expansion) { RlistDestroy(*full_expansion); *full_expansion = tmp_list; tmp_list = NULL; } // No need to map this.* even though it's technically qualified if (success && IsQualifiedVariable(BufferData(value)) && strcmp(ref->scope, "this") != 0) { char *dotpos = strchr(substring, '.'); if (dotpos) { *dotpos = CF_MAPPEDLIST; // replace '.' with '#' } if (strchr(BufferData(value), ':')) { char *colonpos = strchr(substring, ':'); if (colonpos) { *colonpos = '*'; } } } VarRefDestroy(ref); sp += increment; i += increment; } } } BufferDestroy(value); }
bool EvalContextVariableGet(const EvalContext *ctx, VarRef lval, Rval *rval_out, DataType *type_out) { if (lval.lval == NULL) { if (rval_out) { *rval_out = (Rval) {NULL, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } char expanded_lval[CF_MAXVARSIZE] = ""; if (!IsExpandable(lval.lval)) { strncpy(expanded_lval, lval.lval, CF_MAXVARSIZE - 1); } else { char buffer[CF_EXPANDSIZE] = ""; if (ExpandScalar(ctx, lval.scope, lval.lval, buffer)) { strncpy(expanded_lval, buffer, CF_MAXVARSIZE - 1); } else { if (rval_out) { *rval_out = (Rval) {(char *) lval.lval, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } } Scope *get_scope = NULL; char lookup_key[CF_MAXVARSIZE] = ""; { char scopeid[CF_MAXVARSIZE] = ""; if (IsQualifiedVariable(expanded_lval)) { scopeid[0] = '\0'; sscanf(expanded_lval, "%[^.].", scopeid); strlcpy(lookup_key, expanded_lval + strlen(scopeid) + 1, sizeof(lookup_key)); } else { strlcpy(lookup_key, expanded_lval, sizeof(lookup_key)); strlcpy(scopeid, lval.scope, sizeof(scopeid)); } if (lval.ns != NULL && strchr(scopeid, CF_NS) == NULL && strcmp(lval.ns, "default") != 0) { char buffer[CF_EXPANDSIZE] = ""; sprintf(buffer, "%s%c%s", lval.ns, CF_NS, scopeid); strlcpy(scopeid, buffer, sizeof(scopeid)); } get_scope = ScopeGet(scopeid); } if (!get_scope) { if (rval_out) { *rval_out = (Rval) {(char *) lval.lval, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } CfAssoc *assoc = HashLookupElement(get_scope->hashtable, lookup_key); if (!assoc) { if (rval_out) { *rval_out = (Rval) {(char *) lval.lval, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } if (rval_out) { *rval_out = assoc->rval; } if (type_out) { *type_out = assoc->dtype; assert(*type_out != DATA_TYPE_NONE); } return true; }
enum cfdatatype GetVariable(const char *scope, const char *lval, Rval *returnv) { Scope *ptr = NULL; char scopeid[CF_MAXVARSIZE], vlval[CF_MAXVARSIZE], sval[CF_MAXVARSIZE]; char expbuf[CF_EXPANDSIZE]; CfAssoc *assoc; CfDebug("\nGetVariable(%s,%s) type=(to be determined)\n", scope, lval); if (lval == NULL) { *returnv = (Rval) {NULL, CF_SCALAR}; return cf_notype; } if (!IsExpandable(lval)) { strncpy(sval, lval, CF_MAXVARSIZE - 1); } else { if (ExpandScalar(lval, expbuf)) { strncpy(sval, expbuf, CF_MAXVARSIZE - 1); } else { /* C type system does not allow us to express the fact that returned value may contain immutable string. */ *returnv = (Rval) {(char *) lval, CF_SCALAR}; CfDebug("Couldn't expand array-like variable (%s) due to undefined dependencies\n", lval); return cf_notype; } } if (IsQualifiedVariable(sval)) { scopeid[0] = '\0'; sscanf(sval, "%[^.].%s", scopeid, vlval); CfDebug("Variable identifier %s is prefixed with scope id %s\n", vlval, scopeid); ptr = GetScope(scopeid); } else { strlcpy(vlval, sval, sizeof(vlval)); strlcpy(scopeid, scope, sizeof(scopeid)); } CfDebug("Looking for %s.%s\n", scopeid, vlval); if (ptr == NULL) { /* Assume current scope */ strcpy(vlval, lval); ptr = GetScope(scopeid); } if (ptr == NULL) { CfDebug("Scope for variable \"%s.%s\" does not seem to exist\n", scope, lval); /* C type system does not allow us to express the fact that returned value may contain immutable string. */ *returnv = (Rval) {(char *) lval, CF_SCALAR}; return cf_notype; } CfDebug("GetVariable(%s,%s): using scope '%s' for variable '%s'\n", scopeid, vlval, ptr->scope, vlval); assoc = HashLookupElement(ptr->hashtable, vlval); if (assoc == NULL) { CfDebug("No such variable found %s.%s\n\n", scopeid, lval); /* C type system does not allow us to express the fact that returned value may contain immutable string. */ *returnv = (Rval) {(char *) lval, CF_SCALAR}; return cf_notype; } CfDebug("return final variable type=%s, value={\n", CF_DATATYPES[assoc->dtype]); if (DEBUG) { ShowRval(stdout, assoc->rval); } CfDebug("}\n"); *returnv = assoc->rval; return assoc->dtype; }