Пример #1
0
static SyntaxTypeMatch CheckParseOpts(const char *lval, const char *s, const char *range)
{
    Item *split;

/* List/menu types are separated by comma str "a,b,c,..." */

    CfDebug("\nCheckParseOpts(%s => %s/%s)\n", lval, s, range);

    if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

    split = SplitString(range, ',');

    if (!IsItemIn(split, s))
    {
        DeleteItemList(split);
        return SYNTAX_TYPE_MATCH_ERROR_OPTS_OUT_OF_RANGE;
    }

    DeleteItemList(split);

    return SYNTAX_TYPE_MATCH_OK;
}
Пример #2
0
static int CheckParseString(char *lval, char *s, const char *range)
{
    char output[CF_BUFSIZE];

    CfDebug("\nCheckParseString(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return true;
    }

    if (strlen(range) == 0)
    {
        return true;
    }

    if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
        CfDebug("Validation: Unable to verify variable expansion of %s at this stage\n", s);
        return false;
    }

/* Deal with complex strings as special cases */

    if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
    {
        mode_t plus, minus;

        if (!ParseModeString(s, &plus, &minus))
        {
            snprintf(output, CF_BUFSIZE, "Error parsing Unix permission string %s)", s);
            ReportError(output);
            return false;
        }
    }

    if (FullTextMatch(range, s))
    {
        return true;
    }

    if (IsCf3VarString(s))
    {
        CfDebug("Validation: Unable to verify syntax of %s due to variable expansion at this stage\n", s);
    }
    else
    {
        snprintf(output, CF_BUFSIZE,
                 "Scalar item in %s => { %s } in rvalue is out of bounds (value should match pattern %s)", lval, s,
                 range);
        ReportError(output);
        return false;
    }

    return true;
}
Пример #3
0
static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range)
{
 if (s == NULL)
    {
    return SYNTAX_TYPE_MATCH_OK;
    }

 if (strlen(range) == 0)
    {
    return SYNTAX_TYPE_MATCH_OK;
    }

 if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
    return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

/* Deal with complex strings as special cases */

 if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
    {
    mode_t plus, minus;

    if (!ParseModeString(s, &plus, &minus))
       {
       return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION;
       }
    }

 /* FIXME: review this strcmp. Moved out from StringMatch */
 if (!strcmp(range, s) || StringMatchFull(range, s))
    {
    return SYNTAX_TYPE_MATCH_OK;
    }

 if (IsCf3VarString(s))
    {
    return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }
 else if ('\0' == s[0])
    {
    return SYNTAX_TYPE_MATCH_ERROR_EMPTY_SCALAR_OUT_OF_RANGE;
    }
 else if (!strcmp(range, CF_ABSPATHRANGE))
    {
    return SYNTAX_TYPE_MATCH_ERROR_ABSOLUTE_PATH;
    }
 else
    {
    return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE;
    }

 return SYNTAX_TYPE_MATCH_OK;
}
Пример #4
0
Файл: expand.c Проект: tzz/core
Rlist *ExpandList(EvalContext *ctx, const char *ns, const char *scope, const Rlist *list, int expandnaked)
{
    Rlist *start = NULL;
    Rval returnval;

    for (const Rlist *rp = list; rp != NULL; rp = rp->next)
    {
        if (!expandnaked && (rp->val.type == RVAL_TYPE_SCALAR) && IsNakedVar(RlistScalarValue(rp), '@'))
        {
            returnval = RvalNew(RlistScalarValue(rp), RVAL_TYPE_SCALAR);
        }
        else if ((rp->val.type == RVAL_TYPE_SCALAR) && IsNakedVar(RlistScalarValue(rp), '@'))
        {
            char naked[CF_MAXVARSIZE];
            GetNaked(naked, RlistScalarValue(rp));

            if (!IsExpandable(naked))
            {
                VarRef *ref = VarRefParseFromScope(naked, scope);

                DataType value_type = DATA_TYPE_NONE;
                const void *value = EvalContextVariableGet(ctx, ref, &value_type);
                if (value)
                {
                    returnval = ExpandPrivateRval(ctx, ns, scope, value, DataTypeToRvalType(value_type));
                }
                else
                {
                    returnval = ExpandPrivateRval(ctx, ns, scope, rp->val.item, rp->val.type);
                }

                VarRefDestroy(ref);
            }
            else
            {
                returnval = ExpandPrivateRval(ctx, ns, scope, rp->val.item, rp->val.type);
            }
        }
        else
        {
            returnval = ExpandPrivateRval(ctx, ns, scope, rp->val.item, rp->val.type);
        }

        RlistAppend(&start, returnval.item, returnval.type);
        RvalDestroy(returnval);
    }

    return start;
}
Пример #5
0
static Rval ExpandListEntry(EvalContext *ctx,
                            const char *ns, const char *scope,
                            int expandnaked, Rval entry)
{
    if (entry.type == RVAL_TYPE_SCALAR &&
        IsNakedVar(entry.item, '@'))
    {
        if (expandnaked)
        {
            char naked[CF_MAXVARSIZE];
            GetNaked(naked, entry.item);

            if (!IsExpandable(naked))
            {
                VarRef *ref = VarRefParseFromScope(naked, scope);

                DataType value_type = CF_DATA_TYPE_NONE;
                const void *value = EvalContextVariableGet(ctx, ref, &value_type);
                VarRefDestroy(ref);

                if (value)
                {
                    return ExpandPrivateRval(ctx, ns, scope, value,
                                             DataTypeToRvalType(value_type));
                }
            }
        }
        else
        {
            return RvalNew(entry.item, RVAL_TYPE_SCALAR);
        }
    }

    return ExpandPrivateRval(ctx, ns, scope, entry.item, entry.type);
}
Пример #6
0
static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range)
{
    CfDebug("\nCheckParseString(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return SYNTAX_TYPE_MATCH_OK;
    }

    if (strlen(range) == 0)
    {
        return SYNTAX_TYPE_MATCH_OK;
    }

    if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

/* Deal with complex strings as special cases */

    if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
    {
        mode_t plus, minus;

        if (!ParseModeString(s, &plus, &minus))
        {
            return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION;
        }
    }

    if (FullTextMatch(range, s))
    {
        return SYNTAX_TYPE_MATCH_OK;
    }

    if (IsCf3VarString(s))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }
    else
    {
        return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE;
    }

    return SYNTAX_TYPE_MATCH_OK;
}
Пример #7
0
static void CheckParseOpts(char *lval, char *s, const char *range)
{
    Item *split;
    int err = false;
    char output[CF_BUFSIZE];

/* List/menu types are separated by comma str "a,b,c,..." */

    CfDebug("\nCheckParseOpts(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return;
    }

    if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
        CfDebug("Validation: Unable to verify variable expansion of %s at this stage\n", s);
        return;
    }

    split = SplitString(range, ',');

    if (!IsItemIn(split, s))
    {
        snprintf(output, CF_BUFSIZE,
                 "Selection on rhs of lval \'%s\' given as \'%s\' is out of bounds, should be in [%s]", lval, s, range);
        ReportError(output);
        err = true;
    }

    DeleteItemList(split);

    if (!err)
    {
        CfDebug("CheckParseOpts - syntax verified\n\n");
    }
}
Пример #8
0
void RlistFlatten(EvalContext *ctx, Rlist **list)
{
    for (Rlist *rp = *list; rp != NULL;)
    {
        if (rp->val.type != RVAL_TYPE_SCALAR)
        {
            rp = rp->next;
            continue;
        }

        char naked[CF_BUFSIZE] = "";
        if (IsNakedVar(RlistScalarValue(rp), '@'))
        {
            GetNaked(naked, RlistScalarValue(rp));

            if (!IsExpandable(naked))
            {
                Rval rv;
                VarRef *ref = VarRefParse(naked);

                bool var_found = EvalContextVariableGet(ctx, ref, &rv, NULL);

                VarRefDestroy(ref);

                if (var_found)
                {
                    switch (rv.type)
                    {
                    case RVAL_TYPE_LIST:
                        for (const Rlist *srp = rv.item; srp != NULL; srp = srp->next)
                        {
                            RlistAppendRval(list, RvalCopy(srp->val));
                        }
                        Rlist *next = rp->next;
                        RlistDestroyEntry(list, rp);
                        rp = next;
                        continue;

                    default:
                        ProgrammingError("List variable does not resolve to a list");
                        RlistAppendRval(list, RvalCopy(rp->val));
                        break;
                    }
                }
            }
        }
        rp = rp->next;
    }
}
Пример #9
0
static Rval ExpandListEntry(EvalContext *ctx,
                            const char *ns, const char *scope,
                            int expandnaked, Rval entry)
{
    if (entry.type == RVAL_TYPE_SCALAR &&
        IsNakedVar(entry.item, '@'))
    {
        if (expandnaked)
        {
            char naked[CF_MAXVARSIZE];
            GetNaked(naked, entry.item);

            if (IsExpandable(naked))
            {
                char *exp = ExpandScalar(ctx, ns, scope, naked, NULL);
                strlcpy(naked, exp, sizeof(naked));             /* TODO err */
                free(exp);
            }

            /* Check again, it might have changed. */
            if (!IsExpandable(naked))
            {
                VarRef *ref = VarRefParseFromScope(naked, scope);

                DataType value_type;
                const void *value = EvalContextVariableGet(ctx, ref, &value_type);
                VarRefDestroy(ref);

                if (value_type != CF_DATA_TYPE_NONE)     /* variable found? */
                {
                    return ExpandPrivateRval(ctx, ns, scope, value,
                                             DataTypeToRvalType(value_type));
                }
            }
        }
        else
        {
            return RvalNew(entry.item, RVAL_TYPE_SCALAR);
        }
    }

    return ExpandPrivateRval(ctx, ns, scope, entry.item, entry.type);
}
Пример #10
0
void RlistFlatten(EvalContext *ctx, Rlist **list)
{
    for (Rlist *rp = *list; rp != NULL;)
    {
        if (rp->type != RVAL_TYPE_SCALAR)
        {
            rp = rp->next;
            continue;
        }

        char naked[CF_BUFSIZE] = "";
        if (IsNakedVar(rp->item, '@'))
        {
            GetNaked(naked, rp->item);

            Rval rv;
            if (EvalContextVariableGet(ctx, (VarRef) { NULL, ScopeGetCurrent()->scope, naked }, &rv, NULL))
            {
                switch (rv.type)
                {
                case RVAL_TYPE_LIST:
                    for (const Rlist *srp = rv.item; srp != NULL; srp = srp->next)
                    {
                        RlistAppend(list, srp->item, srp->type);
                    }
                    Rlist *next = rp->next;
                    RlistDestroyEntry(list, rp);
                    rp = next;
                    continue;

                default:
                    ProgrammingError("List variable does not resolve to a list");
                    RlistAppend(list, rp->item, rp->type);
                    break;
                }
            }
        }
        rp = rp->next;
    }
}
Пример #11
0
void RlistFlatten(EvalContext *ctx, Rlist **list)
{
    Rlist *prev = NULL, *next;
    for (Rlist *rp = *list; rp != NULL; rp = next)
    {
        next = rp->next;
        if (rp->val.type != RVAL_TYPE_SCALAR)
        {
            prev = rp;
            continue;
        }

        char naked[CF_BUFSIZE] = "";
        if (IsNakedVar(RlistScalarValue(rp), '@'))
        {
            GetNaked(naked, RlistScalarValue(rp));

            if (!IsExpandable(naked))
            {
                VarRef *ref = VarRefParse(naked);
                DataType value_type = CF_DATA_TYPE_NONE;
                const void *value = EvalContextVariableGet(ctx, ref, &value_type);
                VarRefDestroy(ref);

                if (value)
                {
                    switch (DataTypeToRvalType(value_type))
                    {
                    case RVAL_TYPE_LIST:
                        {
                            RlistDestroyEntry(list, rp);

                            for (const Rlist *srp = value; srp != NULL; srp = srp->next)
                            {
                                Rlist *nrp = xmalloc(sizeof(Rlist));
                                nrp->val = RvalCopy(srp->val);
                                nrp->next = next;

                                if (prev)
                                {
                                    prev->next = nrp;
                                }
                                else
                                {
                                    *list = nrp;
                                }

                                prev = nrp;
                            }
                        }
                        continue;

                    default:
                        Log(LOG_LEVEL_WARNING, "Attempted to dereference variable '%s' using @ but variable did not resolve to a list",
                            RlistScalarValue(rp));
                        break;
                    }
                }
            }
        }

        prev = rp;
    }
}
Пример #12
0
PromiseResult VerifyVarPromise(EvalContext *ctx, const Promise *pp, bool allow_duplicates)
{
    ConvergeVariableOptions opts = CollectConvergeVariableOptions(ctx, pp, allow_duplicates);
    if (!opts.should_converge)
    {
        return PROMISE_RESULT_NOOP;
    }

    Attributes a = { {0} };
    // More consideration needs to be given to using these
    //a.transaction = GetTransactionConstraints(pp);
    a.classes = GetClassDefinitionConstraints(ctx, pp);

    VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp));
    if (strcmp("meta", pp->parent_promise_type->name) == 0)
    {
        VarRefSetMeta(ref, true);
    }

    DataType existing_value_type = CF_DATA_TYPE_NONE;
    const void *const existing_value =
        IsExpandable(pp->promiser) ? NULL : EvalContextVariableGet(ctx, ref, &existing_value_type);

    PromiseResult result = PROMISE_RESULT_NOOP;
    Rval rval = opts.cp_save->rval;

    if (rval.item != NULL)
    {
        DataType data_type = DataTypeFromString(opts.cp_save->lval);

        if (opts.cp_save->rval.type == RVAL_TYPE_FNCALL)
        {
            FnCall *fp = RvalFnCallValue(rval);
            const FnCallType *fn = FnCallTypeGet(fp->name);
            if (!fn)
            {
                assert(false && "Canary: should have been caught before this point");
                FatalError(ctx, "While setting variable '%s' in bundle '%s', unknown function '%s'",
                           pp->promiser, PromiseGetBundle(pp)->name, fp->name);
            }

            if (fn->dtype != DataTypeFromString(opts.cp_save->lval))
            {
                FatalError(ctx, "While setting variable '%s' in bundle '%s', variable declared type '%s' but function '%s' returns type '%s'",
                           pp->promiser, PromiseGetBundle(pp)->name, opts.cp_save->lval,
                           fp->name, DataTypeToString(fn->dtype));
            }

            if (existing_value_type != CF_DATA_TYPE_NONE)
            {
                // Already did this
                VarRefDestroy(ref);
                return PROMISE_RESULT_NOOP;
            }

            FnCallResult res = FnCallEvaluate(ctx, PromiseGetPolicy(pp), fp, pp);

            if (res.status == FNCALL_FAILURE)
            {
                /* We do not assign variables to failed fn calls */
                RvalDestroy(res.rval);
                VarRefDestroy(ref);
                return PROMISE_RESULT_NOOP;
            }
            else
            {
                rval = res.rval;
            }
        }
        else
        {
            Buffer *conv = BufferNew();
            bool malformed = false, misprint = false;

            if (strcmp(opts.cp_save->lval, "int") == 0)
            {
                long int asint = IntFromString(opts.cp_save->rval.item);
                if (asint == CF_NOINT)
                {
                    malformed = true;
                }
                else if (0 > BufferPrintf(conv, "%ld", asint))
                {
                    misprint = true;
                }
                else
                {
                    rval = RvalNew(BufferData(conv), opts.cp_save->rval.type);
                }
            }
            else if (strcmp(opts.cp_save->lval, "real") == 0)
            {
                double real_value;
                if (!DoubleFromString(opts.cp_save->rval.item, &real_value))
                {
                    malformed = true;
                }
                else if (0 > BufferPrintf(conv, "%lf", real_value))
                {
                    misprint = true;
                }
                else
                {
                    rval = RvalNew(BufferData(conv), opts.cp_save->rval.type);
                }
            }
            else
            {
                rval = RvalCopy(opts.cp_save->rval);
            }
            BufferDestroy(conv);

            if (malformed)
            {
                /* Arises when opts->cp_save->rval.item isn't yet expanded. */
                /* Has already been logged by *FromString */
                VarRefDestroy(ref);
                return PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
            }
            else if (misprint)
            {
                /* Even though no problems with memory allocation can
                 * get here, there might be other problems. */
                UnexpectedError("Problems writing to buffer");
                VarRefDestroy(ref);
                return PROMISE_RESULT_NOOP;
            }
            else if (rval.type == RVAL_TYPE_LIST)
            {
                Rlist *rval_list = RvalRlistValue(rval);
                RlistFlatten(ctx, &rval_list);
                rval.item = rval_list;
            }
        }

        if (Epimenides(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, pp->promiser, rval, 0))
        {
            Log(LOG_LEVEL_ERR, "Variable '%s' contains itself indirectly - an unkeepable promise", pp->promiser);
            exit(EXIT_FAILURE);
        }
        else
        {
            /* See if the variable needs recursively expanding again */

            Rval returnval = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), ref->ns, ref->scope, rval, true, pp);

            RvalDestroy(rval);

            // freed before function exit
            rval = returnval;
        }

        if (existing_value_type != CF_DATA_TYPE_NONE)
        {
            if (!opts.ok_redefine)    /* only on second iteration, else we ignore broken promises */
            {
                if (THIS_AGENT_TYPE == AGENT_TYPE_COMMON &&
                     !CompareRval(existing_value, DataTypeToRvalType(existing_value_type),
                                  rval.item, rval.type))
                {
                    switch (rval.type)
                    {
                    case RVAL_TYPE_SCALAR:
                        Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant scalar '%s', was '%s' now '%s'",
                            pp->promiser, (const char *)existing_value, RvalScalarValue(rval));
                        PromiseRef(LOG_LEVEL_VERBOSE, pp);
                        break;

                    case RVAL_TYPE_LIST:
                        {
                            Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant list '%s'", pp->promiser);
                            Writer *w = StringWriter();
                            RlistWrite(w, existing_value);
                            char *oldstr = StringWriterClose(w);
                            Log(LOG_LEVEL_VERBOSE, "Old value '%s'", oldstr);
                            free(oldstr);

                            w = StringWriter();
                            RlistWrite(w, rval.item);
                            char *newstr = StringWriterClose(w);
                            Log(LOG_LEVEL_VERBOSE, " New value '%s'", newstr);
                            free(newstr);
                            PromiseRef(LOG_LEVEL_VERBOSE, pp);
                        }
                        break;

                    case RVAL_TYPE_CONTAINER:
                    case RVAL_TYPE_FNCALL:
                    case RVAL_TYPE_NOPROMISEE:
                        break;
                    }
                }

                RvalDestroy(rval);
                VarRefDestroy(ref);
                return result;
            }
        }

        if (IsCf3VarString(pp->promiser))
        {
            // Unexpanded variables, we don't do anything with
            RvalDestroy(rval);
            VarRefDestroy(ref);
            return result;
        }

        if (!IsValidVariableName(pp->promiser))
        {
            Log(LOG_LEVEL_ERR, "Variable identifier contains illegal characters");
            PromiseRef(LOG_LEVEL_ERR, pp);
            RvalDestroy(rval);
            VarRefDestroy(ref);
            return result;
        }

        if (rval.type == RVAL_TYPE_LIST)
        {
            if (opts.drop_undefined)
            {
                for (Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next)
                {
                    if (IsNakedVar(RlistScalarValue(rp), '@'))
                    {
                        free(rp->val.item);
                        rp->val.item = xstrdup(CF_NULL_VALUE);
                    }
                }
            }

            for (const Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next)
            {
                switch (rp->val.type)
                {
                case RVAL_TYPE_SCALAR:
                    break;

                default:
                    // Cannot assign variable because value is a list containing a non-scalar item
                    VarRefDestroy(ref);
                    RvalDestroy(rval);
                    return result;
                }
            }
        }

        if (ref->num_indices > 0)
        {
            if (data_type == CF_DATA_TYPE_CONTAINER)
            {
                char *lval_str = VarRefToString(ref, true);
                Log(LOG_LEVEL_ERR, "Cannot assign a container to an indexed variable name '%s'. Should be assigned to '%s' instead",
                    lval_str, ref->lval);
                free(lval_str);
                VarRefDestroy(ref);
                RvalDestroy(rval);
                return result;
            }
            else
            {
                DataType existing_type = CF_DATA_TYPE_NONE;
                VarRef *base_ref = VarRefCopyIndexless(ref);
                if (EvalContextVariableGet(ctx, ref, &existing_type) && existing_type == CF_DATA_TYPE_CONTAINER)
                {
                    char *lval_str = VarRefToString(ref, true);
                    char *base_ref_str = VarRefToString(base_ref, true);
                    Log(LOG_LEVEL_ERR, "Cannot assign value to indexed variable name '%s', because a container is already assigned to the base name '%s'",
                        lval_str, base_ref_str);
                    free(lval_str);
                    free(base_ref_str);
                    VarRefDestroy(base_ref);
                    VarRefDestroy(ref);
                    RvalDestroy(rval);
                    return result;
                }
                VarRefDestroy(base_ref);
            }
        }


        DataType required_datatype = DataTypeFromString(opts.cp_save->lval);
        if (rval.type != DataTypeToRvalType(required_datatype))
        {
            char *ref_str = VarRefToString(ref, true);
            char *value_str = RvalToString(rval);
            Log(LOG_LEVEL_ERR, "Variable '%s' expected a variable of type '%s', but was given incompatible value '%s'",
                ref_str, DataTypeToString(required_datatype), value_str);
            PromiseRef(LOG_LEVEL_ERR, pp);
            free(ref_str);
            free(value_str);
            VarRefDestroy(ref);
            RvalDestroy(rval);
            return PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
        }

        if (!EvalContextVariablePut(ctx, ref, rval.item, required_datatype, "source=promise"))
        {
            Log(LOG_LEVEL_VERBOSE,
                "Unable to converge %s.%s value (possibly empty or infinite regression)",
                ref->scope, pp->promiser);
            PromiseRef(LOG_LEVEL_VERBOSE, pp);
            result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
        }
        else
        {
            Rlist *promise_meta = PromiseGetConstraintAsList(ctx, "meta", pp);
            if (promise_meta)
            {
                StringSet *class_meta = EvalContextVariableTags(ctx, ref);
                Buffer *print;
                for (const Rlist *rp = promise_meta; rp; rp = rp->next)
                {
                    StringSetAdd(class_meta, xstrdup(RlistScalarValue(rp)));
                    print = StringSetToBuffer(class_meta, ',');
                    Log(LOG_LEVEL_DEBUG,
                        "Added tag %s to class %s, tags now [%s]",
                        RlistScalarValue(rp), pp->promiser, BufferData(print));
                    BufferDestroy(print);
                }
            }
        }
    }
    else
    {
        Log(LOG_LEVEL_ERR, "Variable %s has no promised value", pp->promiser);
        Log(LOG_LEVEL_ERR, "Rule from %s at/before line %llu",
            PromiseGetBundle(pp)->source_path,
            (unsigned long long)opts.cp_save->offset.line);
        result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
    }

    /*
     * FIXME: Variable promise are exempt from normal evaluation logic still, so
     * they are not pushed to evaluation stack before being evaluated. Due to
     * this reason, we cannot call cfPS here to set classes, as it will error
     * out with ProgrammingError.
     *
     * In order to support 'classes' body for variables as well, we call
     * ClassAuditLog explicitly.
     */
    ClassAuditLog(ctx, pp, a, result);

    VarRefDestroy(ref);
    RvalDestroy(rval);

    return result;
}
Пример #13
0
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;
}
Пример #14
0
void ScopeAugment(EvalContext *ctx, const Bundle *bp, const Promise *pp, const Rlist *arguments)
{
    if (RlistLen(bp->args) != RlistLen(arguments))
    {
        Log(LOG_LEVEL_ERR, "While constructing scope '%s'", 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");
    }

    const Bundle *pbp = NULL;
    if (pp != NULL)
    {
        pbp = PromiseGetBundle(pp);
    }

    for (const Rlist *rpl = bp->args, *rpr = arguments; rpl != NULL; rpl = rpl->next, rpr = rpr->next)
    {
        const char *lval = rpl->item;

        Log(LOG_LEVEL_VERBOSE, "Augment scope '%s' with variable '%s' (type: %c)", 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 naked[CF_BUFSIZE];
            
            GetNaked(naked, rpr->item);

            Rval retval;
            if (pbp != NULL)
            {
                VarRef *ref = VarRefParseFromBundle(naked, pbp);
                EvalContextVariableGet(ctx, ref, &retval, &vtype);
                VarRefDestroy(ref);
            }
            else
            {
                VarRef *ref = VarRefParseFromBundle(naked, bp);
                EvalContextVariableGet(ctx, ref, &retval, &vtype);
                VarRefDestroy(ref);
            }

            switch (vtype)
            {
            case DATA_TYPE_STRING_LIST:
            case DATA_TYPE_INT_LIST:
            case DATA_TYPE_REAL_LIST:
                {
                    VarRef *ref = VarRefParseFromBundle(lval, bp);
                    EvalContextVariablePut(ctx, ref, (Rval) { retval.item, RVAL_TYPE_LIST}, DATA_TYPE_STRING_LIST);
                    VarRefDestroy(ref);
                }
                break;
            default:
                {
                    Log(LOG_LEVEL_ERR, "List parameter '%s' not found while constructing scope '%s' - use @(scope.variable) in calling reference", naked, bp->name);
                    VarRef *ref = VarRefParseFromBundle(lval, bp);
                    EvalContextVariablePut(ctx, ref, (Rval) { rpr->item, RVAL_TYPE_SCALAR }, DATA_TYPE_STRING);
                    VarRefDestroy(ref);
                }
                break;
            }
        }
        else
        {
            switch(rpr->type)
            {
            case RVAL_TYPE_SCALAR:
                {
                    VarRef *ref = VarRefParseFromBundle(lval, bp);
                    EvalContextVariablePut(ctx, ref, (Rval) { rpr->item, RVAL_TYPE_SCALAR }, DATA_TYPE_STRING);
                    VarRefDestroy(ref);
                }
                break;

            case RVAL_TYPE_FNCALL:
                {
                    FnCall *subfp = rpr->item;
                    Rval rval = FnCallEvaluate(ctx, subfp, pp).rval;
                    if (rval.type == RVAL_TYPE_SCALAR)
                    {
                        VarRef *ref = VarRefParseFromBundle(lval, bp);
                        EvalContextVariablePut(ctx, ref, (Rval) { rval.item, RVAL_TYPE_SCALAR }, DATA_TYPE_STRING);
                        VarRefDestroy(ref);
                    }
                    else
                    {
                        Log(LOG_LEVEL_ERR, "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 */

    return;
}
Пример #15
0
/**
 * @brief Flattens an Rlist by expanding naked scalar list-variable
 *        members. Flattening is only one-level deep.
 */
void RlistFlatten(EvalContext *ctx, Rlist **list)
{
    Rlist *next;
    for (Rlist *rp = *list; rp != NULL; rp = next)
    {
        next = rp->next;

        if (rp->val.type == RVAL_TYPE_SCALAR      &&
            IsNakedVar(RlistScalarValue(rp), '@'))
        {
            char naked[CF_MAXVARSIZE];
            GetNaked(naked, RlistScalarValue(rp));

            /* Make sure there are no inner expansions to take place, like if
             * rp was "@{blah_$(blue)}".  */
            if (!IsExpandable(naked))
            {
                Log(LOG_LEVEL_DEBUG,
                    "Flattening slist: %s", RlistScalarValue(rp));

                VarRef *ref = VarRefParse(naked);
                DataType value_type;
                const void *value = EvalContextVariableGet(ctx, ref, &value_type);
                VarRefDestroy(ref);

                if (value_type == CF_DATA_TYPE_NONE)
                {
                    assert(value == NULL);
                    continue;                         /* undefined variable */
                }

                if (DataTypeToRvalType(value_type) != RVAL_TYPE_LIST)
                {
                    Log(LOG_LEVEL_WARNING,
                        "'%s' failed - variable is not list but %s",
                        RlistScalarValue(rp), DataTypeToString(value_type));
                    continue;
                }

                /* NOTE: Remember that value can be NULL as an empty Rlist. */

                /* at_node: just a mnemonic name for the
                            list node with @{blah}. */
                Rlist *at_node      = rp;
                Rlist *insert_after = at_node;
                for (const Rlist *rp2 = value; rp2 != NULL; rp2 = rp2->next)
                {
                    assert(insert_after != NULL);

                    RlistInsertAfter(insert_after, RvalCopy(rp2->val));
                    insert_after = insert_after->next;
                }

                /* Make sure we won't miss any element. */
                assert(insert_after->next == next);
                RlistDestroyEntry(list, at_node);   /* Delete @{blah} entry */

                char *list_s = RlistToString(*list);
                Log(LOG_LEVEL_DEBUG, "Flattened slist: %s", list_s);
                free(list_s);
            }
        }
    }
}
Пример #16
0
Файл: expand.c Проект: tzz/core
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope,
                       Rval rval, bool forcelist, const Promise *pp)
{
    assert(ctx);
    assert(policy);

    Rval returnval, newret;
    if ((rval.type == RVAL_TYPE_SCALAR) && IsNakedVar(rval.item, '@'))        /* Treat lists specially here */
    {
        char naked[CF_MAXVARSIZE];
        GetNaked(naked, rval.item);

        if (!IsExpandable(naked))
        {
            VarRef *ref = VarRefParseFromScope(naked, scope);
            DataType value_type = DATA_TYPE_NONE;
            const void *value = EvalContextVariableGet(ctx, ref, &value_type);

            if (!value || DataTypeToRvalType(value_type) != RVAL_TYPE_LIST)
            {
                returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
            }
            else
            {
                returnval.item = ExpandList(ctx, ns, scope, value, true);
                returnval.type = RVAL_TYPE_LIST;
            }

            VarRefDestroy(ref);
        }
        else
        {
            returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
        }
    }
    else
    {
        if (forcelist)          /* We are replacing scalar @(name) with list */
        {
            returnval = ExpandPrivateRval(ctx, ns, scope, rval.item, rval.type);
        }
        else
        {
            if (FnCallIsBuiltIn(rval))
            {
                returnval = RvalCopy(rval);
            }
            else
            {
                returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
            }
        }
    }

    switch (returnval.type)
    {
    case RVAL_TYPE_SCALAR:
    case RVAL_TYPE_CONTAINER:
        break;

    case RVAL_TYPE_LIST:
        for (Rlist *rp = RvalRlistValue(returnval); rp; rp = rp->next)
        {
            if (rp->val.type == RVAL_TYPE_FNCALL)
            {
                FnCall *fp = RlistFnCallValue(rp);
                FnCallResult res = FnCallEvaluate(ctx, policy, fp, pp);

                FnCallDestroy(fp);
                rp->val = res.rval;
            }
            else
            {
                if (EvalContextStackCurrentPromise(ctx))
                {
                    if (IsCf3VarString(RlistScalarValue(rp)))
                    {
                        newret = ExpandPrivateRval(ctx, NULL, "this", rp->val.item, rp->val.type);
                        free(rp->val.item);
                        rp->val.item = newret.item;
                    }
                }
            }

            /* returnval unchanged */
        }
        break;

    case RVAL_TYPE_FNCALL:
        if (FnCallIsBuiltIn(returnval))
        {
            FnCall *fp = RvalFnCallValue(returnval);
            returnval = FnCallEvaluate(ctx, policy, fp, pp).rval;
            FnCallDestroy(fp);
        }
        break;

    default:
        returnval.item = NULL;
        returnval.type = RVAL_TYPE_NOPROMISEE;
        break;
    }

    return returnval;
}
Пример #17
0
void VerifyVarPromise(EvalContext *ctx, const Promise *pp, bool allow_duplicates)
{
    ConvergeVariableOptions opts = CollectConvergeVariableOptions(ctx, pp, allow_duplicates);
    if (!opts.should_converge)
    {
        return;
    }

    char *scope = NULL;
    if (strcmp("meta", pp->parent_promise_type->name) == 0)
    {
        scope = StringConcatenate(2, PromiseGetBundle(pp)->name, "_meta");
    }
    else
    {
        scope = xstrdup(PromiseGetBundle(pp)->name);
    }

    //More consideration needs to be given to using these
    //a.transaction = GetTransactionConstraints(pp);
    Attributes a = { {0} };
    a.classes = GetClassDefinitionConstraints(ctx, pp);

    Rval existing_var_rval;
    DataType existing_var_type = DATA_TYPE_NONE;
    EvalContextVariableGet(ctx, (VarRef) { NULL, scope, pp->promiser }, &existing_var_rval, &existing_var_type);
    Buffer *qualified_scope = BufferNew();
    int result = 0;
    if (strcmp(PromiseGetNamespace(pp), "default") == 0)
    {
        result = BufferSet(qualified_scope, scope, strlen(scope));
        if (result < 0)
        {
            /*
             * Even though there will be no problems with memory allocation, there
             * might be other problems.
             */
            UnexpectedError("Problems writing to buffer");
            free(scope);
            BufferDestroy(&qualified_scope);
            return;
        }
    }
    else
    {
        if (strchr(scope, ':') == NULL)
        {
            result = BufferPrintf(qualified_scope, "%s:%s", PromiseGetNamespace(pp), scope);
            if (result < 0)
            {
                /*
                 * Even though there will be no problems with memory allocation, there
                 * might be other problems.
                 */
                UnexpectedError("Problems writing to buffer");
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }
        }
        else
        {
            result = BufferSet(qualified_scope, scope, strlen(scope));
            if (result < 0)
            {
                /*
                 * Even though there will be no problems with memory allocation, there
                 * might be other problems.
                 */
                UnexpectedError("Problems writing to buffer");
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }
        }
    }

    PromiseResult promise_result;

    Rval rval = opts.cp_save->rval;

    if (rval.item != NULL)
    {
        FnCall *fp = (FnCall *) rval.item;

        if (opts.cp_save->rval.type == RVAL_TYPE_FNCALL)
        {
            if (existing_var_type != DATA_TYPE_NONE)
            {
                // Already did this
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }

            FnCallResult res = FnCallEvaluate(ctx, fp, pp);

            if (res.status == FNCALL_FAILURE)
            {
                /* We do not assign variables to failed fn calls */
                RvalDestroy(res.rval);
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }
            else
            {
                rval = res.rval;
            }
        }
        else
        {
            Buffer *conv = BufferNew();

            if (strcmp(opts.cp_save->lval, "int") == 0)
            {
                result = BufferPrintf(conv, "%ld", IntFromString(opts.cp_save->rval.item));
                if (result < 0)
                {
                    /*
                     * Even though there will be no problems with memory allocation, there
                     * might be other problems.
                     */
                    UnexpectedError("Problems writing to buffer");
                    free(scope);
                    BufferDestroy(&qualified_scope);
                    BufferDestroy(&conv);
                    return;
                }
                rval = RvalCopy((Rval) {(char *)BufferData(conv), opts.cp_save->rval.type});
            }
            else if (strcmp(opts.cp_save->lval, "real") == 0)
            {
                double real_value = 0.0;
                if (DoubleFromString(opts.cp_save->rval.item, &real_value))
                {
                    result = BufferPrintf(conv, "%lf", real_value);
                }
                else
                {
                    result = BufferPrintf(conv, "(double conversion error)");
                }

                if (result < 0)
                {
                    /*
                     * Even though there will be no problems with memory allocation, there
                     * might be other problems.
                     */
                    UnexpectedError("Problems writing to buffer");
                    free(scope);
                    BufferDestroy(&conv);
                    BufferDestroy(&qualified_scope);
                    return;
                }
                rval = RvalCopy((Rval) {(char *)BufferData(conv), opts.cp_save->rval.type});
            }
            else
            {
                rval = RvalCopy(opts.cp_save->rval);
            }

            if (rval.type == RVAL_TYPE_LIST)
            {
                Rlist *rval_list = RvalRlistValue(rval);
                RlistFlatten(ctx, &rval_list);
                rval.item = rval_list;
            }

            BufferDestroy(&conv);
        }

        if (Epimenides(ctx, PromiseGetBundle(pp)->name, pp->promiser, rval, 0))
        {
            Log(LOG_LEVEL_ERR, "Variable \"%s\" contains itself indirectly - an unkeepable promise", pp->promiser);
            exit(1);
        }
        else
        {
            /* See if the variable needs recursively expanding again */

            Rval returnval = EvaluateFinalRval(ctx, BufferData(qualified_scope), rval, true, pp);

            RvalDestroy(rval);

            // freed before function exit
            rval = returnval;
        }

        if (existing_var_type != DATA_TYPE_NONE)
        {
            if (opts.ok_redefine)    /* only on second iteration, else we ignore broken promises */
            {
                ScopeDeleteVariable(BufferData(qualified_scope), pp->promiser);
            }
            else if ((THIS_AGENT_TYPE == AGENT_TYPE_COMMON) && (CompareRval(existing_var_rval, rval) == false))
            {
                switch (rval.type)
                {
                case RVAL_TYPE_SCALAR:
                    Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant scalar \"%s\" (was %s now %s)",
                          pp->promiser, RvalScalarValue(existing_var_rval), RvalScalarValue(rval));
                    PromiseRef(LOG_LEVEL_VERBOSE, pp);
                    break;

                case RVAL_TYPE_LIST:
                    {
                        Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant list \"%s\".", pp->promiser);
                        Writer *w = StringWriter();
                        RlistWrite(w, existing_var_rval.item);
                        char *oldstr = StringWriterClose(w);
                        Log(LOG_LEVEL_VERBOSE, "Old value: %s", oldstr);
                        free(oldstr);

                        w = StringWriter();
                        RlistWrite(w, rval.item);
                        char *newstr = StringWriterClose(w);
                        Log(LOG_LEVEL_VERBOSE, " New value: %s", newstr);
                        free(newstr);
                        PromiseRef(LOG_LEVEL_VERBOSE, pp);
                    }
                    break;

                default:
                    break;
                }
            }
        }

        if (IsCf3VarString(pp->promiser))
        {
            // Unexpanded variables, we don't do anything with
            RvalDestroy(rval);
            free(scope);
            BufferDestroy(&qualified_scope);
            return;
        }

        if (!FullTextMatch("[a-zA-Z0-9_\200-\377.]+(\\[.+\\])*", pp->promiser))
        {
            Log(LOG_LEVEL_ERR, "Variable identifier contains illegal characters");
            PromiseRef(LOG_LEVEL_ERR, pp);
            RvalDestroy(rval);
            free(scope);
            BufferDestroy(&qualified_scope);
            return;
        }

        if (opts.drop_undefined && rval.type == RVAL_TYPE_LIST)
        {
            for (Rlist *rp = rval.item; rp != NULL; rp = rp->next)
            {
                if (IsNakedVar(rp->item, '@'))
                {
                    free(rp->item);
                    rp->item = xstrdup(CF_NULL_VALUE);
                }
            }
        }

        if (!EvalContextVariablePut(ctx, (VarRef) { NULL, BufferData(qualified_scope), pp->promiser }, rval, DataTypeFromString(opts.cp_save->lval)))
        {
            Log(LOG_LEVEL_VERBOSE, "Unable to converge %s.%s value (possibly empty or infinite regression)", BufferData(qualified_scope), pp->promiser);
            PromiseRef(LOG_LEVEL_VERBOSE, pp);
            promise_result = PROMISE_RESULT_FAIL;
        }
        else
        {
            promise_result = PROMISE_RESULT_CHANGE;
        }
    }
    else
    {
        Log(LOG_LEVEL_ERR, "Variable %s has no promised value", pp->promiser);
        Log(LOG_LEVEL_ERR, "Rule from %s at/before line %zu", PromiseGetBundle(pp)->source_path, opts.cp_save->offset.line);
        promise_result = PROMISE_RESULT_FAIL;
    }

    /*
     * FIXME: Variable promise are exempt from normal evaluation logic still, so
     * they are not pushed to evaluation stack before being evaluated. Due to
     * this reason, we cannot call cfPS here to set classes, as it will error
     * out with ProgrammingError.
     *
     * In order to support 'classes' body for variables as well, we call
     * ClassAuditLog explicitly.
     */
    ClassAuditLog(ctx, pp, a, promise_result);

    free(scope);
    BufferDestroy(&qualified_scope);
    RvalDestroy(rval);
}
Пример #18
0
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy,
                       const char *ns, const char *scope,
                       Rval rval, bool forcelist, const Promise *pp)
{
    assert(ctx);
    assert(policy);
    Rval returnval;

    /* Treat lists specially. */
    if (rval.type == RVAL_TYPE_SCALAR && IsNakedVar(rval.item, '@'))
    {
        char naked[CF_MAXVARSIZE];
        GetNaked(naked, rval.item);

        if (IsExpandable(naked))                /* example: @(blah_$(blue)) */
        {
            returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
        }
        else
        {
            VarRef *ref = VarRefParseFromScope(naked, scope);
            DataType value_type;
            const void *value = EvalContextVariableGet(ctx, ref, &value_type);
            VarRefDestroy(ref);

            if (DataTypeToRvalType(value_type) == RVAL_TYPE_LIST)
            {
                returnval.item = ExpandList(ctx, ns, scope, value, true);
                returnval.type = RVAL_TYPE_LIST;
            }
            else
            {
                returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
            }
        }
    }
    else if (forcelist) /* We are replacing scalar @(name) with list */
    {
        returnval = ExpandPrivateRval(ctx, ns, scope, rval.item, rval.type);
    }
    else if (FnCallIsBuiltIn(rval))
    {
        returnval = RvalCopy(rval);
    }
    else
    {
        returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
    }

    switch (returnval.type)
    {
    case RVAL_TYPE_SCALAR:
    case RVAL_TYPE_CONTAINER:
        break;

    case RVAL_TYPE_LIST:
        for (Rlist *rp = RvalRlistValue(returnval); rp; rp = rp->next)
        {
            switch (rp->val.type)
            {
            case RVAL_TYPE_FNCALL:
            {
                FnCall *fp = RlistFnCallValue(rp);
                rp->val = FnCallEvaluate(ctx, policy, fp, pp).rval;
                FnCallDestroy(fp);
                break;
            }
            case RVAL_TYPE_SCALAR:
                if (EvalContextStackCurrentPromise(ctx) &&
                    IsCf3VarString(RlistScalarValue(rp)))
                {
                    void *prior = rp->val.item;
                    rp->val = ExpandPrivateRval(ctx, NULL, "this",
                                                prior, RVAL_TYPE_SCALAR);
                    free(prior);
                }
                /* else: returnval unchanged. */
                break;
            default:
                assert(!"Bad type for entry in Rlist");
            }
        }
        break;

    case RVAL_TYPE_FNCALL:
        if (FnCallIsBuiltIn(returnval))
        {
            FnCall *fp = RvalFnCallValue(returnval);
            returnval = FnCallEvaluate(ctx, policy, fp, pp).rval;
            FnCallDestroy(fp);
        }
        break;

    default:
        assert(returnval.item == NULL); /* else we're leaking it */
        returnval.item = NULL;
        returnval.type = RVAL_TYPE_NOPROMISEE;
        break;
    }

    return returnval;
}