diff --git a/src/builtin.c b/src/builtin.c index aecf5222..4261c30a 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -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; } diff --git a/tests/jq.test b/tests/jq.test index 183950d4..bb8dac2c 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -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] diff --git a/tests/shtest b/tests/shtest index 9b27dc2f..17bfe70b 100755 --- a/tests/shtest +++ b/tests/shtest @@ -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 .