Fix strftime/1 and strflocaltime/1 to allow an empty result (#3670)
These functions failed with "unknown system failure" whenever strftime()
returned 0 for a non-empty format, but 0 also means the format expands to
an empty string, as %p does in a locale without AM/PM.
Prepend a character to the format and trim it from the result, so that 0
can only mean the result is too long. Now 0 | strflocaltime("%p") gives
"" in fr_FR.UTF-8, and a result that is still too long fails with
"result too long" error instead. The size limit is unchanged.
This commit is contained in:
1 parent
80fc7a169d
commit
6b53f80311
3 files changed
+37
-10
No files matched your search
+12
-10
@@ -1849,8 +1849,10 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
|
||||
if (!jv2tm(a, &tm, 0))
|
||||
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
|
||||
|
||||
/* Prepend a character so that strftime() returning 0 means the result
|
||||
* did not fit, even if the format expands to nothing; it is trimmed below. */
|
||||
b = jv_string_concat(jv_string(" "), b);
|
||||
const char *fmt = jv_string_value(b);
|
||||
int fmt_not_empty = *fmt != '\0';
|
||||
size_t max_size = strlen(fmt) + 100;
|
||||
char *buf = jv_mem_alloc(max_size);
|
||||
#if defined(__APPLE__) || defined(__sun)
|
||||
@@ -1884,12 +1886,11 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
|
||||
tzset();
|
||||
#endif
|
||||
jv_free(b);
|
||||
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
||||
if ((n == 0 && fmt_not_empty) || n > max_size) {
|
||||
if (n == 0 || n > max_size) {
|
||||
free(buf);
|
||||
return jv_invalid_with_msg(jv_string("strftime/1: unknown system failure"));
|
||||
return jv_invalid_with_msg(jv_string("strftime/1: result too long"));
|
||||
}
|
||||
jv ret = jv_string_sized(buf, n);
|
||||
jv ret = jv_string_sized(buf + 1, n - 1);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
@@ -1917,18 +1918,19 @@ static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
||||
struct tm tm;
|
||||
if (!jv2tm(a, &tm, 1))
|
||||
return ret_error(b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
||||
/* Prepend a character so that strftime() returning 0 means the result
|
||||
* did not fit, even if the format expands to nothing; it is trimmed below. */
|
||||
b = jv_string_concat(jv_string(" "), b);
|
||||
const char *fmt = jv_string_value(b);
|
||||
int fmt_not_empty = *fmt != '\0';
|
||||
size_t max_size = strlen(fmt) + 100;
|
||||
char *buf = jv_mem_alloc(max_size);
|
||||
size_t n = strftime(buf, max_size, fmt, &tm);
|
||||
jv_free(b);
|
||||
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
||||
if ((n == 0 && fmt_not_empty) || n > max_size) {
|
||||
if (n == 0 || n > max_size) {
|
||||
free(buf);
|
||||
return jv_invalid_with_msg(jv_string("strflocaltime/1: unknown system failure"));
|
||||
return jv_invalid_with_msg(jv_string("strflocaltime/1: result too long"));
|
||||
}
|
||||
jv ret = jv_string_sized(buf, n);
|
||||
jv ret = jv_string_sized(buf + 1, n - 1);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1945,6 +1945,22 @@ try ["OK", strflocaltime({})] catch ["KO", .]
|
||||
0
|
||||
["KO","strflocaltime/1 requires a string format"]
|
||||
|
||||
[strftime("", " %Y ")]
|
||||
0
|
||||
["", " 1970 "]
|
||||
|
||||
try strftime("%c" * 10) catch .
|
||||
0
|
||||
"strftime/1: result too long"
|
||||
|
||||
[strflocaltime("", " %Y ")]
|
||||
[2015,2,5,23,51,47,4,63]
|
||||
["", " 2015 "]
|
||||
|
||||
try strflocaltime("%c" * 10) catch .
|
||||
[2015,2,5,23,51,47,4,63]
|
||||
"strflocaltime/1: result too long"
|
||||
|
||||
[strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)]
|
||||
"2015-03-05T23:51:47Z"
|
||||
[[2015,2,5,23,51,47,4,63],1425599507]
|
||||
|
||||
@@ -779,6 +779,15 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# strflocaltime/1 allows a format that expands to an empty string
|
||||
l=fr_FR.UTF-8
|
||||
if ! $msys && ! $mingw && [ -z "$(LC_ALL=$l date +%p 2>/dev/null)" ]; then
|
||||
if ! out=$(LC_ALL=$l $VALGRIND $Q $JQ -n '0 | strflocaltime("%p")' 2>&1) || [ "$out" != '""' ]; then
|
||||
echo "strflocaltime(\"%p\") in $l should output an empty string but got: $out"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test syntax error messages of multiline filter (we can't test in jq.test)
|
||||
if $VALGRIND $Q $JQ -n '[
|
||||
try if .
|
||||
|
||||
Reference in new issue
Block a user