summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md16
-rw-r--r--configure.ac4
-rw-r--r--docs/content/3.manual/manual.yml8
-rw-r--r--jq.1.prebuilt4
-rw-r--r--src/builtin.c97
-rw-r--r--src/jv_file.c10
-rw-r--r--src/jv_unicode.c2
-rw-r--r--tests/optional.test7
8 files changed, 119 insertions, 29 deletions
diff --git a/README.md b/README.md
index 5d2f01a7..3bcee29b 100644
--- a/README.md
+++ b/README.md
@@ -23,19 +23,21 @@ Source tarball and built executable releases can be found on the
homepage and on the github release page, https://github.com/stedolan/jq/releases
If you're building directly from the latest git, you'll need flex,
-bison (3.0 or newer), libtool, make, and autoconf installed. To get
-regexp support you'll also need to install Oniguruma (note that jq's
-tests require regexp support to pass). To build, run:
-
- autoreconf -i # if building from git
- ./configure
+bison (3.0 or newer), libtool, make, and autoconf installed.
+To get regexp support you'll also need to install Oniguruma or clone it as a
+git submodule as per the instructions below.
+(note that jq's tests require regexp support to pass). To build, run:
+
+ git submodule update --init # if building from git to get oniguruma
+ autoreconf -fi # if building from git
+ ./configure --with-oniguruma=builtin
make -j8
make check
To build without bison or flex, add `--disable-maintainer-mode` to the
./configure invocation:
- ./configure --disable-maintainer-mode
+ ./configure --with-oniguruma=builtin --disable-maintainer-mode
(Developers must not use `--disable-maintainer-mode`, not when making
changes to the jq parser and/or lexer.)
diff --git a/configure.ac b/configure.ac
index e6ca29da..9186d00d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -187,8 +187,8 @@ AC_CHECK_MATH_FUNC(log1p,[.5])
AC_CHECK_MATH_FUNC(log2, [.5])
AC_CHECK_MATH_FUNC(log, [.5])
AC_CHECK_MATH_FUNC(logb,[.5])
-AC_FIND_FUNC([modf], [m c], [#include <math.h>], [0, 0])
-AC_FIND_FUNC([lgamma_r], [m c], [#include <math.h>], [0, 0])
+AC_CHECK_MATH_FUNC([modf], [m c], [#include <math.h>], [0, 0])
+AC_CHECK_MATH_FUNC([lgamma_r], [m c], [#include <math.h>], [0, 0])
AC_CHECK_MATH_FUNC(nearbyint,[.5])
AC_CHECK_MATH_FUNC(nextafter,[.5,1.0])
AC_CHECK_MATH_FUNC(nexttoward,[.5,1.0])
diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml
index fcca8412..119d42bf 100644
--- a/docs/content/3.manual/manual.yml
+++ b/docs/content/3.manual/manual.yml
@@ -1908,9 +1908,11 @@ sections:
Unix epoch and outputs a "broken down time" representation of
Greenwhich Meridian time as an array of numbers representing
(in this order): the year, the month (zero-based), the day of
- the month, the hour of the day, the minute of the hour, the
- second of the minute, the day of the week, and the day of the
- year -- all one-based unless otherwise stated.
+ the month (one-based), the hour of the day, the minute of the
+ hour, the second of the minute, the day of the week, and the
+ day of the year -- all one-based unless otherwise stated. The
+ day of the week number may be wrong on some systems for dates
+ before March 1st 1900, or after December 31 2099.
The `localtime` builtin works like the `gmtime` builtin, but
using the local timezone setting.
diff --git a/jq.1.prebuilt b/jq.1.prebuilt
index 21a0fcd0..03672cc2 100644
--- a/jq.1.prebuilt
+++ b/jq.1.prebuilt
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "JQ" "1" "April 2017" "" ""
+.TH "JQ" "1" "May 2017" "" ""
.
.SH "NAME"
\fBjq\fR \- Command\-line JSON processor
@@ -2082,7 +2082,7 @@ The \fBnow\fR builtin outputs the current time, in seconds since the Unix epoch\
Low\-level jq interfaces to the C\-library time functions are also provided: \fBstrptime\fR, \fBstrftime\fR, \fBstrflocaltime\fR, \fBmktime\fR, \fBgmtime\fR, and \fBlocaltime\fR\. Refer to your host operating system\'s documentation for the format strings used by \fBstrptime\fR and \fBstrftime\fR\. Note: these are not necessarily stable interfaces in jq, particularly as to their localization functionality\.
.
.P
-The \fBgmtime\fR builtin consumes a number of seconds since the Unix epoch and outputs a "broken down time" representation of Greenwhich Meridian time as an array of numbers representing (in this order): the year, the month (zero\-based), the day of the month, the hour of the day, the minute of the hour, the second of the minute, the day of the week, and the day of the year \-\- all one\-based unless otherwise stated\.
+The \fBgmtime\fR builtin consumes a number of seconds since the Unix epoch and outputs a "broken down time" representation of Greenwhich Meridian time as an array of numbers representing (in this order): the year, the month (zero\-based), the day of the month (one\-based), the hour of the day, the minute of the hour, the second of the minute, the day of the week, and the day of the year \-\- all one\-based unless otherwise stated\. The day of the week number may be wrong on some systems for dates before March 1st 1900, or after December 31 2099\.
.
.P
The \fBlocaltime\fR builtin works like the \fBgmtime\fR builtin, but using the local timezone setting\.
diff --git a/src/builtin.c b/src/builtin.c
index cfdd4fef..23b91546 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -1193,21 +1193,89 @@ static jv tm2jv(struct tm *tm) {
*
* Returns (time_t)-2 if mktime()'s side-effects cannot be corrected.
*/
-static time_t my_mktime(struct tm *tm) {
+static time_t my_timegm(struct tm *tm) {
#ifdef HAVE_TIMEGM
return timegm(tm);
#else /* HAVE_TIMEGM */
+ char *tz;
+
+ tz = (tz = getenv("TZ")) != NULL ? strdup(tz) : NULL;
+ if (tz != NULL)
+ setenv("TZ", "", 1);
+ time_t t = mktime(tm);
+ if (tz != NULL)
+ setenv("TZ", tz, 1);
+ return t;
+#endif /* !HAVE_TIMEGM */
+}
+static time_t my_mktime(struct tm *tm) {
time_t t = mktime(tm);
if (t == (time_t)-1)
return t;
#ifdef HAVE_TM_TM_GMT_OFF
- return t + tm.tm_gmtoff;
-#elif defined(HAVE_TM_TM_GMT_OFF)
- return t + tm.__tm_gmtoff;
+ return t + tm->tm_gmtoff;
+#elif HAVE_TM___TM_GMT_OFF
+ return t + tm->__tm_gmtoff;
#else
return (time_t)-2; /* Not supported */
#endif
-#endif /* !HAVE_TIMEGM */
+}
+
+/* Compute and set tm_wday */
+static void set_tm_wday(struct tm *tm) {
+ /*
+ * https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Gauss.27s_algorithm
+ * https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html
+ *
+ * Tested with dates from 1900-01-01 through 2100-01-01. This
+ * algorithm produces the wrong day-of-the-week number for dates in
+ * the range 1900-01-01..1900-02-28, and for 2100-01-01..2100-02-28.
+ * Since this is only needed on OS X and *BSD, we might just document
+ * this.
+ */
+ int century = (1900 + tm->tm_year) / 100;
+ int year = (1900 + tm->tm_year) % 100;
+ if (tm->tm_mon < 2)
+ year--;
+ /*
+ * The month value in the wday computation below is shifted so that
+ * March is 1, April is 2, .., January is 11, and February is 12.
+ */
+ int mon = tm->tm_mon - 1;
+ if (mon < 1)
+ mon += 12;
+ int wday =
+ (tm->tm_mday + (int)floor((2.6 * mon - 0.2)) + year + (int)floor(year / 4.0) + (int)floor(century / 4.0) - 2 * century) % 7;
+ if (wday < 0)
+ wday += 7;
+#if 0
+ /* See commentary above */
+ assert(wday == tm->tm_wday || tm->tm_wday == 8);
+#endif
+ tm->tm_wday = wday;
+}
+/*
+ * Compute and set tm_yday.
+ *
+ */
+static void set_tm_yday(struct tm *tm) {
+ static const int d[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+ int mon = tm->tm_mon;
+ int year = 1900 + tm->tm_year;
+ int leap_day = 0;
+ if (tm->tm_mon > 1 &&
+ ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
+ leap_day = 1;
+
+ /* Bound check index into d[] */
+ if (mon < 0)
+ mon = -mon;
+ if (mon > 11)
+ mon %= 12;
+
+ int yday = d[mon] + leap_day + tm->tm_mday - 1;
+ assert(yday == tm->tm_yday || tm->tm_yday == 367);
+ tm->tm_yday = yday;
}
#ifdef HAVE_STRPTIME
@@ -1217,6 +1285,8 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
struct tm tm;
memset(&tm, 0, sizeof(tm));
+ tm.tm_wday = 8; // sentinel
+ tm.tm_yday = 367; // sentinel
const char *input = jv_string_value(a);
const char *fmt = jv_string_value(b);
const char *end = strptime(input, fmt, &tm);
@@ -1228,10 +1298,19 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
return e;
}
jv_free(b);
- if (tm.tm_wday == 0 && tm.tm_yday == 0 && my_mktime(&tm) == (time_t)-2) {
- jv_free(a);
- return jv_invalid_with_msg(jv_string("strptime/1 not supported on this platform"));
- }
+ /*
+ * This is OS X or some *BSD whose strptime() is just not that
+ * helpful!
+ *
+ * We don't know that the format string did involve parsing a
+ * year, or a month (if tm->tm_mon == 0). But with our invalid
+ * day-of-week and day-of-year sentinel checks above, the worst
+ * this can do is produce garbage.
+ */
+ if (tm.tm_wday == 8 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11)
+ set_tm_wday(&tm);
+ if (tm.tm_yday == 367 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11)
+ set_tm_yday(&tm);
jv r = tm2jv(&tm);
if (*end != '\0')
r = jv_array_append(r, jv_string(end));
diff --git a/src/jv_file.c b/src/jv_file.c
index a5829a8f..4c0060fc 100644
--- a/src/jv_file.c
+++ b/src/jv_file.c
@@ -30,10 +30,12 @@ jv jv_load_file(const char* filename, int raw) {
while (!feof(file) && !ferror(file)) {
size_t n = fread(buf, 1, sizeof(buf)-max_utf8_len, file);
int len = 0;
- if (jvp_utf8_backtrack(buf+(n-1), buf, &len) && len > 0) {
- if (!feof(file) && !ferror(file)) {
- n += fread(buf+n, 1, len, file);
- }
+
+ if (n == 0)
+ continue;
+ if (jvp_utf8_backtrack(buf+(n-1), buf, &len) && len > 0 &&
+ !feof(file) && !ferror(file)) {
+ n += fread(buf+n, 1, len, file);
}
if (raw) {
diff --git a/src/jv_unicode.c b/src/jv_unicode.c
index b3a50b2d..d197349f 100644
--- a/src/jv_unicode.c
+++ b/src/jv_unicode.c
@@ -9,7 +9,7 @@
// *missing_bytes. If there are no leading bytes or an invalid byte is
// encountered, NULL is returned and *missing_bytes is not altered.
const char* jvp_utf8_backtrack(const char* start, const char* min, int *missing_bytes) {
- assert(min < start);
+ assert(min <= start);
if (min == start) {
return min;
}
diff --git a/tests/optional.test b/tests/optional.test
index 0ee1fb27..fc37e607 100644
--- a/tests/optional.test
+++ b/tests/optional.test
@@ -2,9 +2,14 @@
# strptime() is not available on mingw/WIN32
[strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)]
-"2015-03-05T23:51:47Z"
[[2015,2,5,23,51,47,4,63],1425599507]
+# Check day-of-week and day of year computations
+# (should trip an assert if this fails)
+last(range(365 * 199)|("1900-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime) + (86400 * .)|strftime("%Y-%m-%dT%H:%M:%SZ")|strptime("%Y-%m-%dT%H:%M:%SZ"))
+null
+[2099,0,10,1,2,3,6,9]
+
# %e is not available on mingw/WIN32
strftime("%A, %B %e, %Y")
1435677542.822351