Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 53 additions & 75 deletions libpromises/evalfunction.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@

static bool CheckIDChar(const char ch);
static bool CheckID(const char *id);
static const Rlist *GetListReferenceArgument(const EvalContext *ctx, const FnCall *fp, const char *lval_str, DataType *datatype_out);
static char *CfReadFile(const char *filename, size_t maxsize);

/*******************************************************************/
Expand Down Expand Up @@ -5486,41 +5485,6 @@

/*********************************************************************/

static const Rlist *GetListReferenceArgument(const EvalContext *ctx, const FnCall *fp, const char *lval_str, DataType *datatype_out)
{
VarRef *ref = VarRefParse(lval_str);
DataType value_type;
const Rlist *value = EvalContextVariableGet(ctx, ref, &value_type);
VarRefDestroy(ref);

/* Error 1: variable not found. */
if (value_type == CF_DATA_TYPE_NONE)
{
Log(LOG_LEVEL_VERBOSE,
"Could not resolve expected list variable '%s' in function '%s'",
lval_str, fp->name);
assert(value == NULL);
}
/* Error 2: variable is not a list. */
else if (DataTypeToRvalType(value_type) != RVAL_TYPE_LIST)
{
Log(LOG_LEVEL_ERR, "Function '%s' expected a list variable,"
" got variable of type '%s'",
fp->name, DataTypeToString(value_type));

value = NULL;
value_type = CF_DATA_TYPE_NONE;
}

if (datatype_out)
{
*datatype_out = value_type;
}
return value;
}

/*********************************************************************/

static FnCallResult FilterInternal(EvalContext *ctx,
const FnCall *fp,
const char *regex,
Expand Down Expand Up @@ -6248,6 +6212,25 @@

/*********************************************************************/

static bool CanFormatAsStringList(EvalContext *ctx, const Rlist *arg, const Rlist **out)
{
assert(arg != NULL);

if (arg->val.type != RVAL_TYPE_SCALAR)
{
*out = NULL;
return false;
}

const char* const varname = RlistScalarValue(arg);
VarRef *ref = VarRefParse(varname);
DataType type;
*out = EvalContextVariableGet(ctx, ref, &type);
VarRefDestroy(ref);

return type == CF_DATA_TYPE_STRING_LIST;
}

static FnCallResult FnCallFormat(EvalContext *ctx, ARG_UNUSED const Policy *policy, const FnCall *fp, const Rlist *finalargs)
{
const char *const id = "built-in FnCall format-arg";
Expand Down Expand Up @@ -6293,18 +6276,18 @@
{
const char *format_piece = BufferData(SeqAt(s, 1));
bool percent = StringEqualN(format_piece, "%%", 2);
char *data = NULL;
const Rlist *arg = NULL;

if (percent)
{
// "%%" in format string
}
else if (rp != NULL)
{
data = RlistScalarValue(rp);
arg = rp;
rp = rp->next;
}
else // not %% and no data
else // not %% and no arg
{
Log(LOG_LEVEL_ERR, "format() didn't have enough parameters");
BufferDestroy(buf);
Expand Down Expand Up @@ -6333,7 +6316,7 @@
if (strrchr(format_piece, 'd') != NULL || strrchr(format_piece, 'o') != NULL || strrchr(format_piece, 'x') != NULL)
{
long x = 0;
sscanf(data, "%ld", &x);
sscanf(RlistScalarValue(arg), "%ld", &x);
snprintf(piece, CF_BUFSIZE, format_piece, x);
BufferAppend(buf, piece, strlen(piece));
}
Expand All @@ -6345,13 +6328,13 @@
else if (strrchr(format_piece, 'f') != NULL)
{
double x = 0;
sscanf(data, "%lf", &x);
sscanf(RlistScalarValue(arg), "%lf", &x);
snprintf(piece, CF_BUFSIZE, format_piece, x);
BufferAppend(buf, piece, strlen(piece));
}
else if (strrchr(format_piece, 's') != NULL)
{
BufferAppendF(buf, format_piece, data);
BufferAppendF(buf, format_piece, RlistScalarValue(arg));
}
else if (strrchr(format_piece, 'S') != NULL)
{
Expand All @@ -6370,51 +6353,46 @@
ProgrammingError("Couldn't find the expected S format spec in %s", format_piece);
}

const char* const varname = data;
VarRef *ref = VarRefParse(varname);
DataType type;
const void *value = EvalContextVariableGet(ctx, ref, &type);
VarRefDestroy(ref);

if (type == CF_DATA_TYPE_CONTAINER)
const Rlist *list;
if (CanFormatAsStringList(ctx, arg, &list))
{
Writer *w = StringWriter();
JsonWriteCompact(w, value);
WriterWrite(w, "{ ");
for (const Rlist *tmp = list; tmp; tmp = tmp->next)
{
char *escaped = EscapeCharCopy(RlistScalarValue(tmp), '"', '\\');
WriterWriteF(w, "\"%s\"", escaped);
free(escaped);

if (tmp != NULL && tmp->next != NULL)
{
WriterWrite(w, ", ");
}
}
WriterWrite(w, " }");

BufferAppendF(buf, format_rewrite, StringWriterData(w));
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
WriterClose(w);
}
else // it might be a list reference
else
{
DataType data_type;
const Rlist *list = GetListReferenceArgument(ctx, fp, varname, &data_type);
if (data_type == CF_DATA_TYPE_STRING_LIST)
{
Writer *w = StringWriter();
WriterWrite(w, "{ ");
for (const Rlist *rp = list; rp; rp = rp->next)
{
char *escaped = EscapeCharCopy(RlistScalarValue(rp), '"', '\\');
WriterWriteF(w, "\"%s\"", escaped);
free(escaped);

if (rp != NULL && rp->next != NULL)
{
WriterWrite(w, ", ");
}
}
WriterWrite(w, " }");
bool allocated = false;
JsonElement *element = VarNameOrInlineToJson(ctx, fp, arg, false, &allocated);

BufferAppendF(buf, format_rewrite, StringWriterData(w));
WriterClose(w);
}
else // whatever this is, it's not a list reference or a data container
if (element == NULL)
{
Log(LOG_LEVEL_VERBOSE, "format() with %%S specifier needs a data container or a list instead of '%s'.",
varname);
RlistScalarValue(arg));
BufferDestroy(buf);
SeqDestroy(s);
return FnFailure();
}

Writer *w = StringWriter();
JsonWriteCompact(w, element);
BufferAppendF(buf, format_rewrite, StringWriterData(w));
WriterClose(w);
JsonDestroyMaybe(element, allocated);
}
}
else
Expand Down Expand Up @@ -11716,7 +11694,7 @@
FnCallTypeNew("findprocesses", CF_DATA_TYPE_CONTAINER, PROCESSEXISTS_ARGS, &FnCallProcessExists, "Returns data container of processes matching the regular expression",
FNCALL_OPTION_CACHED, FNCALL_CATEGORY_SYSTEM, SYNTAX_STATUS_NORMAL, DEFAULT_ARGC),
FnCallTypeNew("format", CF_DATA_TYPE_STRING, FORMAT_ARGS, &FnCallFormat, "Applies a list of string values in arg2,arg3... to a string format in arg1 with sprintf() rules",
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL, ARGC(1, -1)),
FNCALL_OPTION_VARARG | FNCALL_OPTION_COLLECTING, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL, ARGC(1, -1)),
FnCallTypeNew("getclassmetatags", CF_DATA_TYPE_STRING_LIST, GETCLASSMETATAGS_ARGS, &FnCallGetMetaTags, "Collect the class arg1's meta tags into an slist, optionally collecting only tag key arg2",
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_UTILS, SYNTAX_STATUS_NORMAL, ARGC(1, -1)),
FnCallTypeNew("getenv", CF_DATA_TYPE_STRING, GETENV_ARGS, &FnCallGetEnv, "Return the environment variable named arg1, truncated at arg2 characters",
Expand Down
4 changes: 4 additions & 0 deletions tests/acceptance/01_vars/02_functions/format.cf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ bundle edit_line init_insert
"key='S_123_list' value='{ \"one\", \"two\", \"\\\"three\\\"\" }'";
"key='S_container_1' value='[null]'";
"key='S_container_2' value='[{\"x\":123},\"yz\"]'";
"key='S_function' value='{\"hello\":\"world\"}'";
Comment thread
victormlg marked this conversation as resolved.
"key='S_inline_json' value='{\"hello\":\"world\"}'";
}

#######################################################
Expand Down Expand Up @@ -82,6 +84,8 @@ bundle agent test
"array[S_container_1]" string => format("%S", mycontainer1);
"array[S_container_2]" string => format("%S", mycontainer2);
"formatted" slist => maparray("key='$(this.k)' value='$(this.v)'", "array");
"array[S_function]" string => format("%S", parsejson('{"hello":"world"}'));
"array[S_inline_json]" string => format("%S", '{"hello":"world"}');

files:
"$(G.testfile).actual"
Expand Down
6 changes: 3 additions & 3 deletions tests/acceptance/01_vars/02_functions/nth_datacontainer.cf
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ bundle common test_common
"numbers"
ilist => { "-100", "-1", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1000 };

"keys" slist => getindices(data);
"object_keys" slist => getindices("data[object]");
"list_keys" slist => getindices("data[list]");
"keys" slist => sort(getindices(data), "lex");
"object_keys" slist => sort(getindices("data[object]"), "lex");
"list_keys" slist => sort(getindices("data[list]"), "lex");

"all_keys"
slist => {
Expand Down
Loading