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
89 changes: 49 additions & 40 deletions libpromises/evalfunction.c
Original file line number Diff line number Diff line change
Expand Up @@ -6293,18 +6293,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 +6333,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 +6345,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,50 +6370,59 @@
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)
if (arg->val.type == RVAL_TYPE_CONTAINER)
{
Writer *w = StringWriter();
JsonWriteCompact(w, value);
JsonWriteCompact(w, (JsonElement*) arg->val.item);
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
{
DataType data_type;
const Rlist *list = GetListReferenceArgument(ctx, fp, varname, &data_type);
if (data_type == CF_DATA_TYPE_STRING_LIST)
else {
const char* const varname = RlistScalarValue(arg);
VarRef *ref = VarRefParse(varname);
DataType type;
const void *value = EvalContextVariableGet(ctx, ref, &type);
VarRefDestroy(ref);

if (type == CF_DATA_TYPE_CONTAINER)
{
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, " }");

JsonWriteCompact(w, value);
BufferAppendF(buf, format_rewrite, StringWriterData(w));
WriterClose(w);
}
else // whatever this is, it's not a list reference or a data container
else // it might be a list reference
{
Log(LOG_LEVEL_VERBOSE, "format() with %%S specifier needs a data container or a list instead of '%s'.",
varname);
BufferDestroy(buf);
SeqDestroy(s);
return FnFailure();
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 *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));
WriterClose(w);
}
else // whatever this is, it's not a list reference or a data container
{
Log(LOG_LEVEL_VERBOSE, "format() with %%S specifier needs a data container or a list instead of '%s'.",
varname);
BufferDestroy(buf);
SeqDestroy(s);
return FnFailure();
}
}
}
}
Expand Down Expand Up @@ -11716,7 +11725,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
2 changes: 2 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,7 @@ 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\"}'";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must you escape the double quotes? I see it's done there before, but is it required?

}

#######################################################
Expand Down Expand Up @@ -82,6 +83,7 @@ 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"}'));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should cover a json string directly to illustrate the behavior

"array[S_function]" string => format("%S", '{"hello": "world"}');


files:
"$(G.testfile).actual"
Expand Down
Loading