2015-03-08 18:56:51 -05:00
|
|
|
#define _BSD_SOURCE
|
2015-06-27 23:14:46 -05:00
|
|
|
#define _GNU_SOURCE
|
2016-08-10 22:27:21 +02:00
|
|
|
#ifndef __sun__
|
|
|
|
|
# define _XOPEN_SOURCE
|
|
|
|
|
# define _XOPEN_SOURCE_EXTENDED 1
|
|
|
|
|
#else
|
|
|
|
|
# define _XPG6
|
|
|
|
|
# define __EXTENSIONS__
|
|
|
|
|
#endif
|
2015-03-08 18:56:51 -05:00
|
|
|
#include <sys/time.h>
|
2015-06-18 11:07:16 -07:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#ifdef HAVE_ALLOCA_H
|
|
|
|
|
# include <alloca.h>
|
|
|
|
|
#elif !defined alloca
|
|
|
|
|
# ifdef __GNUC__
|
|
|
|
|
# define alloca __builtin_alloca
|
|
|
|
|
# elif defined _MSC_VER
|
|
|
|
|
# include <malloc.h>
|
|
|
|
|
# define alloca _alloca
|
|
|
|
|
# elif !defined HAVE_ALLOCA
|
|
|
|
|
# ifdef __cplusplus
|
|
|
|
|
extern "C"
|
|
|
|
|
# endif
|
|
|
|
|
void *alloca (size_t);
|
|
|
|
|
# endif
|
2015-05-24 09:42:13 -05:00
|
|
|
#endif
|
2014-06-13 17:51:41 -05:00
|
|
|
#include <assert.h>
|
2015-03-06 00:14:15 -06:00
|
|
|
#include <ctype.h>
|
2014-06-13 17:51:41 -05:00
|
|
|
#include <limits.h>
|
2013-06-17 20:50:45 -05:00
|
|
|
#include <math.h>
|
2017-01-26 23:29:00 -05:00
|
|
|
#ifdef HAVE_LIBONIG
|
2014-06-18 19:49:38 -04:00
|
|
|
#include <oniguruma.h>
|
2015-02-14 13:31:34 -06:00
|
|
|
#endif
|
2012-09-18 10:17:38 +01:00
|
|
|
#include <string.h>
|
2015-03-06 00:14:15 -06:00
|
|
|
#include <time.h>
|
2018-12-26 02:49:05 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#endif
|
2012-08-16 01:00:30 +01:00
|
|
|
#include "builtin.h"
|
2012-09-17 20:59:34 +01:00
|
|
|
#include "compile.h"
|
2012-11-28 01:08:23 -06:00
|
|
|
#include "jq_parser.h"
|
2013-06-21 12:06:28 +01:00
|
|
|
#include "bytecode.h"
|
2014-08-10 16:52:03 -05:00
|
|
|
#include "linker.h"
|
2012-09-18 10:17:38 +01:00
|
|
|
#include "locfile.h"
|
2012-12-27 20:49:34 +00:00
|
|
|
#include "jv_unicode.h"
|
2017-04-21 18:40:39 -05:00
|
|
|
#include "jv_alloc.h"
|
2012-12-27 20:49:34 +00:00
|
|
|
|
2012-09-18 10:17:38 +01:00
|
|
|
|
2012-12-02 22:12:08 +00:00
|
|
|
static jv type_error(jv bad, const char* msg) {
|
2015-04-17 18:18:33 -04:00
|
|
|
char errbuf[15];
|
|
|
|
|
jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s",
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_kind_name(jv_get_kind(bad)),
|
2015-04-17 18:18:33 -04:00
|
|
|
jv_dump_string_trunc(jv_copy(bad), errbuf, sizeof(errbuf)),
|
2012-12-02 22:12:08 +00:00
|
|
|
msg));
|
|
|
|
|
jv_free(bad);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv type_error2(jv bad1, jv bad2, const char* msg) {
|
2015-04-17 18:18:33 -04:00
|
|
|
char errbuf1[15],errbuf2[15];
|
|
|
|
|
jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) and %s (%s) %s",
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_kind_name(jv_get_kind(bad1)),
|
2015-04-17 18:18:33 -04:00
|
|
|
jv_dump_string_trunc(jv_copy(bad1), errbuf1, sizeof(errbuf1)),
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_kind_name(jv_get_kind(bad2)),
|
2015-04-17 18:18:33 -04:00
|
|
|
jv_dump_string_trunc(jv_copy(bad2), errbuf2, sizeof(errbuf2)),
|
2012-12-02 22:12:08 +00:00
|
|
|
msg));
|
|
|
|
|
jv_free(bad1);
|
|
|
|
|
jv_free(bad2);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-27 20:37:14 -05:00
|
|
|
static inline jv ret_error(jv bad, jv msg) {
|
|
|
|
|
jv_free(bad);
|
|
|
|
|
return jv_invalid_with_msg(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline jv ret_error2(jv bad1, jv bad2, jv msg) {
|
|
|
|
|
jv_free(bad1);
|
|
|
|
|
jv_free(bad2);
|
|
|
|
|
return jv_invalid_with_msg(msg);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_plus(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_free(input);
|
2012-12-29 16:50:58 +00:00
|
|
|
if (jv_get_kind(a) == JV_KIND_NULL) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
return b;
|
|
|
|
|
} else if (jv_get_kind(b) == JV_KIND_NULL) {
|
|
|
|
|
jv_free(b);
|
|
|
|
|
return a;
|
|
|
|
|
} else if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
2015-07-19 09:36:16 -07:00
|
|
|
return jv_number(jv_number_value(a) +
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_number_value(b));
|
|
|
|
|
} else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) {
|
|
|
|
|
return jv_string_concat(a, b);
|
|
|
|
|
} else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
|
|
|
|
|
return jv_array_concat(a, b);
|
|
|
|
|
} else if (jv_get_kind(a) == JV_KIND_OBJECT && jv_get_kind(b) == JV_KIND_OBJECT) {
|
|
|
|
|
return jv_object_merge(a, b);
|
|
|
|
|
} else {
|
|
|
|
|
return type_error2(a, b, "cannot be added");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-21 08:46:56 +00:00
|
|
|
#define LIBM_DD(name) \
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_ ## name(jq_state *jq, jv input) { \
|
2014-02-21 08:46:56 +00:00
|
|
|
if (jv_get_kind(input) != JV_KIND_NUMBER) { \
|
|
|
|
|
return type_error(input, "number required"); \
|
|
|
|
|
} \
|
|
|
|
|
jv ret = jv_number(name(jv_number_value(input))); \
|
|
|
|
|
jv_free(input); \
|
|
|
|
|
return ret; \
|
2013-09-11 20:22:56 -04:00
|
|
|
}
|
2015-06-27 23:14:46 -05:00
|
|
|
#define LIBM_DD_NO(name)
|
|
|
|
|
|
|
|
|
|
#define LIBM_DDD(name) \
|
|
|
|
|
static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \
|
|
|
|
|
jv_free(input); \
|
2017-02-05 00:47:19 -05:00
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER) { \
|
|
|
|
|
jv_free(b); \
|
|
|
|
|
return type_error(a, "number required"); \
|
|
|
|
|
} \
|
|
|
|
|
if (jv_get_kind(b) != JV_KIND_NUMBER) { \
|
|
|
|
|
jv_free(a); \
|
|
|
|
|
return type_error(b, "number required"); \
|
|
|
|
|
} \
|
2015-06-27 23:14:46 -05:00
|
|
|
jv ret = jv_number(name(jv_number_value(a), jv_number_value(b))); \
|
|
|
|
|
jv_free(a); \
|
|
|
|
|
jv_free(b); \
|
|
|
|
|
return ret; \
|
|
|
|
|
}
|
|
|
|
|
#define LIBM_DDD_NO(name)
|
2017-02-04 00:11:19 -06:00
|
|
|
|
|
|
|
|
#define LIBM_DDDD(name) \
|
|
|
|
|
static jv f_ ## name(jq_state *jq, jv input, jv a, jv b, jv c) { \
|
|
|
|
|
jv_free(input); \
|
2017-02-05 00:47:19 -05:00
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER) { \
|
|
|
|
|
jv_free(b); \
|
|
|
|
|
jv_free(c); \
|
|
|
|
|
return type_error(a, "number required"); \
|
|
|
|
|
} \
|
|
|
|
|
if (jv_get_kind(b) != JV_KIND_NUMBER) { \
|
|
|
|
|
jv_free(a); \
|
|
|
|
|
jv_free(c); \
|
|
|
|
|
return type_error(b, "number required"); \
|
|
|
|
|
} \
|
|
|
|
|
if (jv_get_kind(c) != JV_KIND_NUMBER) { \
|
|
|
|
|
jv_free(a); \
|
|
|
|
|
jv_free(b); \
|
|
|
|
|
return type_error(c, "number required"); \
|
|
|
|
|
} \
|
2017-02-04 00:11:19 -06:00
|
|
|
jv ret = jv_number(name(jv_number_value(a), jv_number_value(b), jv_number_value(c))); \
|
|
|
|
|
jv_free(a); \
|
|
|
|
|
jv_free(b); \
|
|
|
|
|
jv_free(c); \
|
|
|
|
|
return ret; \
|
|
|
|
|
}
|
|
|
|
|
#define LIBM_DDDD_NO(name)
|
2014-02-21 08:46:56 +00:00
|
|
|
#include "libm.h"
|
2017-02-04 00:11:19 -06:00
|
|
|
#undef LIBM_DDDD_NO
|
2015-06-27 23:14:46 -05:00
|
|
|
#undef LIBM_DDD_NO
|
|
|
|
|
#undef LIBM_DD_NO
|
2017-02-04 00:11:19 -06:00
|
|
|
#undef LIBM_DDDD
|
2015-06-27 23:14:46 -05:00
|
|
|
#undef LIBM_DDD
|
2014-02-21 08:46:56 +00:00
|
|
|
#undef LIBM_DD
|
2013-09-11 20:22:56 -04:00
|
|
|
|
2017-03-29 00:07:42 -05:00
|
|
|
#ifdef HAVE_FREXP
|
|
|
|
|
static jv f_frexp(jq_state *jq, jv input) {
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
|
|
|
|
return type_error(input, "number required");
|
|
|
|
|
}
|
|
|
|
|
int exp;
|
|
|
|
|
double d = frexp(jv_number_value(input), &exp);
|
|
|
|
|
jv ret = JV_ARRAY(jv_number(d), jv_number(exp));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_MODF
|
|
|
|
|
static jv f_modf(jq_state *jq, jv input) {
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
|
|
|
|
return type_error(input, "number required");
|
|
|
|
|
}
|
|
|
|
|
double i;
|
|
|
|
|
jv ret = JV_ARRAY(jv_number(modf(jv_number_value(input), &i)));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_array_append(ret, jv_number(i));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_LGAMMA_R
|
|
|
|
|
static jv f_lgamma_r(jq_state *jq, jv input) {
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
|
|
|
|
return type_error(input, "number required");
|
|
|
|
|
}
|
|
|
|
|
int sign;
|
|
|
|
|
jv ret = JV_ARRAY(jv_number(lgamma_r(jv_number_value(input), &sign)));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_array_append(ret, jv_number(sign));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_negate(jq_state *jq, jv input) {
|
2013-01-03 12:53:06 +00:00
|
|
|
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
|
|
|
|
return type_error(input, "cannot be negated");
|
|
|
|
|
}
|
|
|
|
|
jv ret = jv_number(-jv_number_value(input));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_startswith(jq_state *jq, jv a, jv b) {
|
2015-03-06 00:18:45 -06:00
|
|
|
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string("startswith() requires string inputs"));
|
2013-11-28 15:17:32 -06:00
|
|
|
int alen = jv_string_length_bytes(jv_copy(a));
|
|
|
|
|
int blen = jv_string_length_bytes(jv_copy(b));
|
|
|
|
|
jv ret;
|
|
|
|
|
|
|
|
|
|
if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0)
|
|
|
|
|
ret = jv_true();
|
|
|
|
|
else
|
|
|
|
|
ret = jv_false();
|
|
|
|
|
jv_free(a);
|
|
|
|
|
jv_free(b);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_endswith(jq_state *jq, jv a, jv b) {
|
2015-03-06 00:18:45 -06:00
|
|
|
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string("endswith() requires string inputs"));
|
2013-11-28 15:17:32 -06:00
|
|
|
const char *astr = jv_string_value(a);
|
|
|
|
|
const char *bstr = jv_string_value(b);
|
|
|
|
|
size_t alen = jv_string_length_bytes(jv_copy(a));
|
|
|
|
|
size_t blen = jv_string_length_bytes(jv_copy(b));
|
|
|
|
|
jv ret;;
|
|
|
|
|
|
|
|
|
|
if (alen < blen ||
|
|
|
|
|
memcmp(astr + (alen - blen), bstr, blen) != 0)
|
|
|
|
|
ret = jv_false();
|
|
|
|
|
else
|
|
|
|
|
ret = jv_true();
|
|
|
|
|
jv_free(a);
|
|
|
|
|
jv_free(b);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_ltrimstr(jq_state *jq, jv input, jv left) {
|
|
|
|
|
if (jv_get_kind(f_startswith(jq, jv_copy(input), jv_copy(left))) != JV_KIND_TRUE) {
|
2013-11-30 00:44:14 -06:00
|
|
|
jv_free(left);
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* FIXME It'd be better to share the suffix with the original input --
|
|
|
|
|
* that we could do, we just can't share prefixes.
|
|
|
|
|
*/
|
|
|
|
|
int prefixlen = jv_string_length_bytes(left);
|
|
|
|
|
jv res = jv_string_sized(jv_string_value(input) + prefixlen,
|
|
|
|
|
jv_string_length_bytes(jv_copy(input)) - prefixlen);
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_rtrimstr(jq_state *jq, jv input, jv right) {
|
|
|
|
|
if (jv_get_kind(f_endswith(jq, jv_copy(input), jv_copy(right))) == JV_KIND_TRUE) {
|
2013-11-30 00:44:14 -06:00
|
|
|
jv res = jv_string_sized(jv_string_value(input),
|
|
|
|
|
jv_string_length_bytes(jv_copy(input)) - jv_string_length_bytes(right));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
jv_free(right);
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_minus(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_free(input);
|
|
|
|
|
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
|
|
|
|
return jv_number(jv_number_value(a) - jv_number_value(b));
|
2012-09-10 16:49:25 +01:00
|
|
|
} else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
|
|
|
|
|
jv out = jv_array();
|
2012-12-31 23:27:00 +00:00
|
|
|
jv_array_foreach(a, i, x) {
|
2012-09-10 16:49:25 +01:00
|
|
|
int include = 1;
|
2012-12-31 23:27:00 +00:00
|
|
|
jv_array_foreach(b, j, y) {
|
|
|
|
|
if (jv_equal(jv_copy(x), y)) {
|
2012-09-10 16:49:25 +01:00
|
|
|
include = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (include)
|
|
|
|
|
out = jv_array_append(out, jv_copy(x));
|
|
|
|
|
jv_free(x);
|
|
|
|
|
}
|
|
|
|
|
jv_free(a);
|
|
|
|
|
jv_free(b);
|
2012-12-02 22:12:08 +00:00
|
|
|
return out;
|
2012-09-10 16:49:25 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return type_error2(a, b, "cannot be subtracted");
|
2012-09-10 16:49:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_multiply(jq_state *jq, jv input, jv a, jv b) {
|
2014-06-07 21:43:30 -05:00
|
|
|
jv_kind ak = jv_get_kind(a);
|
|
|
|
|
jv_kind bk = jv_get_kind(b);
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_free(input);
|
2014-06-07 21:43:30 -05:00
|
|
|
if (ak == JV_KIND_NUMBER && bk == JV_KIND_NUMBER) {
|
2012-12-02 22:12:08 +00:00
|
|
|
return jv_number(jv_number_value(a) * jv_number_value(b));
|
2014-06-07 21:43:30 -05:00
|
|
|
} else if ((ak == JV_KIND_STRING && bk == JV_KIND_NUMBER) ||
|
|
|
|
|
(ak == JV_KIND_NUMBER && bk == JV_KIND_STRING)) {
|
|
|
|
|
jv str = a;
|
|
|
|
|
jv num = b;
|
|
|
|
|
if (ak == JV_KIND_NUMBER) {
|
|
|
|
|
str = b;
|
|
|
|
|
num = a;
|
|
|
|
|
}
|
2013-11-29 15:49:11 -06:00
|
|
|
int n;
|
2014-06-07 21:43:30 -05:00
|
|
|
size_t alen = jv_string_length_bytes(jv_copy(str));
|
|
|
|
|
jv res = str;
|
2013-11-29 15:49:11 -06:00
|
|
|
|
2014-06-07 21:43:30 -05:00
|
|
|
for (n = jv_number_value(num) - 1; n > 0; n--)
|
|
|
|
|
res = jv_string_append_buf(res, jv_string_value(str), alen);
|
2013-11-29 15:49:11 -06:00
|
|
|
|
2014-06-07 21:43:30 -05:00
|
|
|
jv_free(num);
|
2013-11-29 15:49:11 -06:00
|
|
|
if (n < 0) {
|
2014-06-07 21:43:30 -05:00
|
|
|
jv_free(str);
|
2013-11-29 15:49:11 -06:00
|
|
|
return jv_null();
|
|
|
|
|
}
|
|
|
|
|
return res;
|
2014-06-07 21:43:30 -05:00
|
|
|
} else if (ak == JV_KIND_OBJECT && bk == JV_KIND_OBJECT) {
|
2014-03-08 03:56:05 +01:00
|
|
|
return jv_object_merge_recursive(a, b);
|
2012-09-10 16:57:17 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return type_error2(a, b, "cannot be multiplied");
|
2015-07-19 09:36:16 -07:00
|
|
|
}
|
2012-09-10 16:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_divide(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_free(input);
|
2012-09-10 16:57:17 +01:00
|
|
|
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
2015-06-17 19:46:57 -05:00
|
|
|
if (jv_number_value(b) == 0.0)
|
|
|
|
|
return type_error2(a, b, "cannot be divided because the divisor is zero");
|
2012-12-02 22:12:08 +00:00
|
|
|
return jv_number(jv_number_value(a) / jv_number_value(b));
|
2013-11-28 22:27:23 -06:00
|
|
|
} else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) {
|
|
|
|
|
return jv_string_split(a, b);
|
2012-09-10 16:57:17 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return type_error2(a, b, "cannot be divided");
|
2015-07-19 09:36:16 -07:00
|
|
|
}
|
2012-09-10 16:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_mod(jq_state *jq, jv input, jv a, jv b) {
|
2013-06-17 20:21:37 -05:00
|
|
|
jv_free(input);
|
|
|
|
|
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
2015-06-17 19:46:57 -05:00
|
|
|
if ((intmax_t)jv_number_value(b) == 0)
|
|
|
|
|
return type_error2(a, b, "cannot be divided (remainder) because the divisor is zero");
|
2013-06-17 20:21:37 -05:00
|
|
|
return jv_number((intmax_t)jv_number_value(a) % (intmax_t)jv_number_value(b));
|
|
|
|
|
} else {
|
2015-06-17 19:46:57 -05:00
|
|
|
return type_error2(a, b, "cannot be divided (remainder)");
|
2015-07-19 09:36:16 -07:00
|
|
|
}
|
2013-06-17 20:21:37 -05:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_equal(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_free(input);
|
|
|
|
|
return jv_bool(jv_equal(a, b));
|
2012-09-10 18:08:00 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_notequal(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
jv_free(input);
|
|
|
|
|
return jv_bool(!jv_equal(a, b));
|
2012-10-23 17:01:39 +02:00
|
|
|
}
|
|
|
|
|
|
2012-12-02 22:12:08 +00:00
|
|
|
enum cmp_op {
|
|
|
|
|
CMP_OP_LESS,
|
|
|
|
|
CMP_OP_GREATER,
|
|
|
|
|
CMP_OP_LESSEQ,
|
|
|
|
|
CMP_OP_GREATEREQ
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static jv order_cmp(jv input, jv a, jv b, enum cmp_op op) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
int r = jv_cmp(a, b);
|
|
|
|
|
return jv_bool((op == CMP_OP_LESS && r < 0) ||
|
|
|
|
|
(op == CMP_OP_LESSEQ && r <= 0) ||
|
|
|
|
|
(op == CMP_OP_GREATEREQ && r >= 0) ||
|
|
|
|
|
(op == CMP_OP_GREATER && r > 0));
|
2012-10-07 22:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_less(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
return order_cmp(input, a, b, CMP_OP_LESS);
|
2012-10-07 22:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_greater(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
return order_cmp(input, a, b, CMP_OP_GREATER);
|
2012-10-07 22:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_lesseq(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
return order_cmp(input, a, b, CMP_OP_LESSEQ);
|
2012-10-07 22:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_greatereq(jq_state *jq, jv input, jv a, jv b) {
|
2012-12-02 22:12:08 +00:00
|
|
|
return order_cmp(input, a, b, CMP_OP_GREATEREQ);
|
2012-10-07 22:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_contains(jq_state *jq, jv a, jv b) {
|
2012-12-31 23:27:00 +00:00
|
|
|
if (jv_get_kind(a) == jv_get_kind(b)) {
|
2012-12-02 22:12:08 +00:00
|
|
|
return jv_bool(jv_contains(a, b));
|
|
|
|
|
} else {
|
|
|
|
|
return type_error2(a, b, "cannot have their containment checked");
|
2012-10-24 11:42:25 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_dump(jq_state *jq, jv input) {
|
2013-11-29 16:16:52 -06:00
|
|
|
return jv_dump_string(input, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_json_parse(jq_state *jq, jv input) {
|
2013-11-29 16:16:52 -06:00
|
|
|
if (jv_get_kind(input) != JV_KIND_STRING)
|
|
|
|
|
return type_error(input, "only strings can be parsed");
|
|
|
|
|
jv res = jv_parse_sized(jv_string_value(input),
|
|
|
|
|
jv_string_length_bytes(jv_copy(input)));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_tonumber(jq_state *jq, jv input) {
|
2012-12-02 22:12:08 +00:00
|
|
|
if (jv_get_kind(input) == JV_KIND_NUMBER) {
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
if (jv_get_kind(input) == JV_KIND_STRING) {
|
|
|
|
|
jv parsed = jv_parse(jv_string_value(input));
|
|
|
|
|
if (!jv_is_valid(parsed) || jv_get_kind(parsed) == JV_KIND_NUMBER) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return parsed;
|
2012-09-10 17:08:13 +01:00
|
|
|
}
|
2012-12-02 22:12:08 +00:00
|
|
|
}
|
|
|
|
|
return type_error(input, "cannot be parsed as a number");
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_length(jq_state *jq, jv input) {
|
2012-12-02 22:12:08 +00:00
|
|
|
if (jv_get_kind(input) == JV_KIND_ARRAY) {
|
|
|
|
|
return jv_number(jv_array_length(input));
|
|
|
|
|
} else if (jv_get_kind(input) == JV_KIND_OBJECT) {
|
|
|
|
|
return jv_number(jv_object_length(input));
|
|
|
|
|
} else if (jv_get_kind(input) == JV_KIND_STRING) {
|
2013-05-15 00:37:38 +01:00
|
|
|
return jv_number(jv_string_length_codepoints(input));
|
2013-11-30 02:29:21 -06:00
|
|
|
} else if (jv_get_kind(input) == JV_KIND_NUMBER) {
|
|
|
|
|
return jv_number(fabs(jv_number_value(input)));
|
2012-12-16 13:10:48 +00:00
|
|
|
} else if (jv_get_kind(input) == JV_KIND_NULL) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_number(0);
|
2012-09-10 17:08:13 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return type_error(input, "has no length");
|
2012-09-10 17:08:13 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_tostring(jq_state *jq, jv input) {
|
2012-12-02 22:12:08 +00:00
|
|
|
if (jv_get_kind(input) == JV_KIND_STRING) {
|
|
|
|
|
return input;
|
2012-09-16 11:08:42 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return jv_dump_string(input, 0);
|
2012-09-16 11:08:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-08 12:51:42 +10:00
|
|
|
static jv f_utf8bytelength(jq_state *jq, jv input) {
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_STRING)
|
|
|
|
|
return type_error(input, "only strings have UTF-8 byte length");
|
|
|
|
|
return jv_number(jv_string_length_bytes(input));
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-27 20:49:34 +00:00
|
|
|
#define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
|
|
|
|
2016-01-24 23:30:28 +00:00
|
|
|
static const unsigned char BASE64_ENCODE_TABLE[64 + 1] = CHARS_ALPHANUM "+/";
|
|
|
|
|
static const unsigned char BASE64_INVALID_ENTRY = 0xFF;
|
|
|
|
|
static const unsigned char BASE64_DECODE_TABLE[255] = {
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
|
|
|
62, // +
|
|
|
|
|
0xFF, 0xFF, 0xFF,
|
|
|
|
|
63, // /
|
|
|
|
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9
|
|
|
|
|
0xFF, 0xFF, 0xFF,
|
|
|
|
|
99, // =
|
|
|
|
|
0xFF, 0xFF, 0xFF,
|
|
|
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // A-Z
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
|
|
|
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // a-z
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2012-12-27 20:49:34 +00:00
|
|
|
static jv escape_string(jv input, const char* escapings) {
|
|
|
|
|
|
|
|
|
|
assert(jv_get_kind(input) == JV_KIND_STRING);
|
|
|
|
|
const char* lookup[128] = {0};
|
|
|
|
|
const char* p = escapings;
|
2015-04-20 16:51:48 -04:00
|
|
|
lookup[0] = "\\0";
|
2012-12-27 20:49:34 +00:00
|
|
|
while (*p) {
|
|
|
|
|
lookup[(int)*p] = p+1;
|
|
|
|
|
p++;
|
|
|
|
|
p += strlen(p);
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jv ret = jv_string("");
|
|
|
|
|
const char* i = jv_string_value(input);
|
2013-05-15 00:37:38 +01:00
|
|
|
const char* end = i + jv_string_length_bytes(jv_copy(input));
|
2012-12-27 20:49:34 +00:00
|
|
|
const char* cstart;
|
|
|
|
|
int c = 0;
|
|
|
|
|
while ((i = jvp_utf8_next((cstart = i), end, &c))) {
|
|
|
|
|
if (c < 128 && lookup[c]) {
|
|
|
|
|
ret = jv_string_append_str(ret, lookup[c]);
|
|
|
|
|
} else {
|
|
|
|
|
ret = jv_string_append_buf(ret, cstart, i - cstart);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_format(jq_state *jq, jv input, jv fmt) {
|
2012-12-27 20:49:34 +00:00
|
|
|
if (jv_get_kind(fmt) != JV_KIND_STRING) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return type_error(fmt, "is not a valid format");
|
|
|
|
|
}
|
|
|
|
|
const char* fmt_s = jv_string_value(fmt);
|
|
|
|
|
if (!strcmp(fmt_s, "json")) {
|
|
|
|
|
jv_free(fmt);
|
|
|
|
|
return jv_dump_string(input, 0);
|
|
|
|
|
} else if (!strcmp(fmt_s, "text")) {
|
|
|
|
|
jv_free(fmt);
|
2014-07-07 22:49:46 -05:00
|
|
|
return f_tostring(jq, input);
|
2014-12-24 11:18:53 -06:00
|
|
|
} else if (!strcmp(fmt_s, "csv") || !strcmp(fmt_s, "tsv")) {
|
|
|
|
|
const char *quotes, *sep, *escapings;
|
2015-05-21 18:20:05 -05:00
|
|
|
const char *msg;
|
2014-12-24 11:18:53 -06:00
|
|
|
if (!strcmp(fmt_s, "csv")) {
|
2015-05-21 18:20:05 -05:00
|
|
|
msg = "cannot be csv-formatted, only array";
|
2014-12-24 11:18:53 -06:00
|
|
|
quotes = "\"";
|
|
|
|
|
sep = ",";
|
|
|
|
|
escapings = "\"\"\"\0";
|
|
|
|
|
} else {
|
2015-05-21 18:20:05 -05:00
|
|
|
msg = "cannot be tsv-formatted, only array";
|
2014-12-24 11:18:53 -06:00
|
|
|
assert(!strcmp(fmt_s, "tsv"));
|
|
|
|
|
quotes = "";
|
|
|
|
|
sep = "\t";
|
2015-04-15 15:55:27 -04:00
|
|
|
escapings = "\t\\t\0\r\\r\0\n\\n\0\\\\\\\0";
|
2014-12-24 11:18:53 -06:00
|
|
|
}
|
2012-12-27 20:49:34 +00:00
|
|
|
jv_free(fmt);
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_ARRAY)
|
2015-05-21 18:20:05 -05:00
|
|
|
return type_error(input, msg);
|
2012-12-27 20:49:34 +00:00
|
|
|
jv line = jv_string("");
|
2012-12-31 23:27:00 +00:00
|
|
|
jv_array_foreach(input, i, x) {
|
2014-12-24 11:18:53 -06:00
|
|
|
if (i) line = jv_string_append_str(line, sep);
|
2012-12-27 20:49:34 +00:00
|
|
|
switch (jv_get_kind(x)) {
|
|
|
|
|
case JV_KIND_NULL:
|
|
|
|
|
/* null rendered as empty string */
|
|
|
|
|
jv_free(x);
|
|
|
|
|
break;
|
|
|
|
|
case JV_KIND_TRUE:
|
|
|
|
|
case JV_KIND_FALSE:
|
|
|
|
|
line = jv_string_concat(line, jv_dump_string(x, 0));
|
|
|
|
|
break;
|
|
|
|
|
case JV_KIND_NUMBER:
|
|
|
|
|
if (jv_number_value(x) != jv_number_value(x)) {
|
|
|
|
|
/* NaN, render as empty string */
|
|
|
|
|
jv_free(x);
|
|
|
|
|
} else {
|
|
|
|
|
line = jv_string_concat(line, jv_dump_string(x, 0));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case JV_KIND_STRING: {
|
2014-12-24 11:18:53 -06:00
|
|
|
line = jv_string_append_str(line, quotes);
|
|
|
|
|
line = jv_string_concat(line, escape_string(x, escapings));
|
|
|
|
|
line = jv_string_append_str(line, quotes);
|
2012-12-27 20:49:34 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(line);
|
|
|
|
|
return type_error(x, "is not valid in a csv row");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return line;
|
|
|
|
|
} else if (!strcmp(fmt_s, "html")) {
|
|
|
|
|
jv_free(fmt);
|
2014-07-07 22:49:46 -05:00
|
|
|
return escape_string(f_tostring(jq, input), "&&\0<<\0>>\0''\0\""\0");
|
2012-12-27 20:49:34 +00:00
|
|
|
} else if (!strcmp(fmt_s, "uri")) {
|
|
|
|
|
jv_free(fmt);
|
2014-07-07 22:49:46 -05:00
|
|
|
input = f_tostring(jq, input);
|
2012-12-27 20:49:34 +00:00
|
|
|
|
|
|
|
|
int unreserved[128] = {0};
|
|
|
|
|
const char* p = CHARS_ALPHANUM "-_.!~*'()";
|
|
|
|
|
while (*p) unreserved[(int)*p++] = 1;
|
|
|
|
|
|
|
|
|
|
jv line = jv_string("");
|
|
|
|
|
const char* s = jv_string_value(input);
|
2013-05-15 00:37:38 +01:00
|
|
|
for (int i=0; i<jv_string_length_bytes(jv_copy(input)); i++) {
|
2012-12-29 01:37:22 +00:00
|
|
|
unsigned ch = (unsigned)(unsigned char)*s;
|
2012-12-27 20:49:34 +00:00
|
|
|
if (ch < 128 && unreserved[ch]) {
|
|
|
|
|
line = jv_string_append_buf(line, s, 1);
|
|
|
|
|
} else {
|
2014-06-29 19:48:01 -07:00
|
|
|
line = jv_string_concat(line, jv_string_fmt("%%%02X", ch));
|
2012-12-27 20:49:34 +00:00
|
|
|
}
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return line;
|
|
|
|
|
} else if (!strcmp(fmt_s, "sh")) {
|
|
|
|
|
jv_free(fmt);
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_ARRAY)
|
|
|
|
|
input = jv_array_set(jv_array(), 0, input);
|
|
|
|
|
jv line = jv_string("");
|
2012-12-31 23:27:00 +00:00
|
|
|
jv_array_foreach(input, i, x) {
|
2012-12-27 20:49:34 +00:00
|
|
|
if (i) line = jv_string_append_str(line, " ");
|
|
|
|
|
switch (jv_get_kind(x)) {
|
|
|
|
|
case JV_KIND_NULL:
|
|
|
|
|
case JV_KIND_TRUE:
|
|
|
|
|
case JV_KIND_FALSE:
|
|
|
|
|
case JV_KIND_NUMBER:
|
|
|
|
|
line = jv_string_concat(line, jv_dump_string(x, 0));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JV_KIND_STRING: {
|
|
|
|
|
line = jv_string_append_str(line, "'");
|
|
|
|
|
line = jv_string_concat(line, escape_string(x, "''\\''\0"));
|
|
|
|
|
line = jv_string_append_str(line, "'");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(line);
|
|
|
|
|
return type_error(x, "can not be escaped for shell");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return line;
|
|
|
|
|
} else if (!strcmp(fmt_s, "base64")) {
|
|
|
|
|
jv_free(fmt);
|
2014-07-07 22:49:46 -05:00
|
|
|
input = f_tostring(jq, input);
|
2012-12-27 20:49:34 +00:00
|
|
|
jv line = jv_string("");
|
2014-07-01 13:17:48 -04:00
|
|
|
const unsigned char* data = (const unsigned char*)jv_string_value(input);
|
2013-05-15 00:37:38 +01:00
|
|
|
int len = jv_string_length_bytes(jv_copy(input));
|
2012-12-27 20:49:34 +00:00
|
|
|
for (int i=0; i<len; i+=3) {
|
|
|
|
|
uint32_t code = 0;
|
|
|
|
|
int n = len - i >= 3 ? 3 : len-i;
|
|
|
|
|
for (int j=0; j<3; j++) {
|
|
|
|
|
code <<= 8;
|
|
|
|
|
code |= j < n ? (unsigned)data[i+j] : 0;
|
|
|
|
|
}
|
|
|
|
|
char buf[4];
|
|
|
|
|
for (int j=0; j<4; j++) {
|
2016-01-24 23:30:28 +00:00
|
|
|
buf[j] = BASE64_ENCODE_TABLE[(code >> (18 - j*6)) & 0x3f];
|
2012-12-27 20:49:34 +00:00
|
|
|
}
|
|
|
|
|
if (n < 3) buf[3] = '=';
|
|
|
|
|
if (n < 2) buf[2] = '=';
|
|
|
|
|
line = jv_string_append_buf(line, buf, sizeof(buf));
|
|
|
|
|
}
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return line;
|
2016-01-24 23:30:28 +00:00
|
|
|
} else if (!strcmp(fmt_s, "base64d")) {
|
|
|
|
|
jv_free(fmt);
|
|
|
|
|
input = f_tostring(jq, input);
|
|
|
|
|
const unsigned char* data = (const unsigned char*)jv_string_value(input);
|
|
|
|
|
int len = jv_string_length_bytes(jv_copy(input));
|
|
|
|
|
size_t decoded_len = (3 * len) / 4; // 3 usable bytes for every 4 bytes of input
|
2017-04-21 18:40:39 -05:00
|
|
|
char *result = jv_mem_calloc(decoded_len, sizeof(char));
|
2016-01-24 23:30:28 +00:00
|
|
|
memset(result, 0, decoded_len * sizeof(char));
|
|
|
|
|
uint32_t ri = 0;
|
|
|
|
|
int input_bytes_read=0;
|
|
|
|
|
uint32_t code = 0;
|
|
|
|
|
for (int i=0; i<len && data[i] != '='; i++) {
|
|
|
|
|
if (BASE64_DECODE_TABLE[data[i]] == BASE64_INVALID_ENTRY) {
|
|
|
|
|
free(result);
|
|
|
|
|
return type_error(input, "is not valid base64 data");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code <<= 6;
|
|
|
|
|
code |= BASE64_DECODE_TABLE[data[i]];
|
|
|
|
|
input_bytes_read++;
|
|
|
|
|
|
|
|
|
|
if (input_bytes_read == 4) {
|
|
|
|
|
result[ri++] = (code >> 16) & 0xFF;
|
|
|
|
|
result[ri++] = (code >> 8) & 0xFF;
|
|
|
|
|
result[ri++] = code & 0xFF;
|
|
|
|
|
input_bytes_read = 0;
|
|
|
|
|
code = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (input_bytes_read == 3) {
|
|
|
|
|
result[ri++] = (code >> 10) & 0xFF;
|
|
|
|
|
result[ri++] = (code >> 2) & 0xFF;
|
|
|
|
|
} else if (input_bytes_read == 2) {
|
|
|
|
|
result[ri++] = (code >> 4) & 0xFF;
|
|
|
|
|
} else if (input_bytes_read == 1) {
|
|
|
|
|
free(result);
|
|
|
|
|
return type_error(input, "trailing base64 byte found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jv line = jv_string_sized(result, ri);
|
|
|
|
|
jv_free(input);
|
|
|
|
|
free(result);
|
|
|
|
|
return line;
|
2012-12-27 20:49:34 +00:00
|
|
|
} else {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_keys(jq_state *jq, jv input) {
|
2012-12-02 22:12:08 +00:00
|
|
|
if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {
|
|
|
|
|
return jv_keys(input);
|
2012-09-17 20:14:07 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return type_error(input, "has no keys");
|
2012-09-17 20:14:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 21:49:37 -05:00
|
|
|
static jv f_keys_unsorted(jq_state *jq, jv input) {
|
|
|
|
|
if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {
|
|
|
|
|
return jv_keys_unsorted(input);
|
|
|
|
|
} else {
|
|
|
|
|
return type_error(input, "has no keys");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_sort(jq_state *jq, jv input){
|
2012-12-02 22:12:08 +00:00
|
|
|
if (jv_get_kind(input) == JV_KIND_ARRAY) {
|
|
|
|
|
return jv_sort(input, jv_copy(input));
|
2012-10-20 00:26:37 +01:00
|
|
|
} else {
|
2012-12-02 22:12:08 +00:00
|
|
|
return type_error(input, "cannot be sorted, as it is not an array");
|
2012-10-20 00:26:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_sort_by_impl(jq_state *jq, jv input, jv keys) {
|
2015-07-19 09:36:16 -07:00
|
|
|
if (jv_get_kind(input) == JV_KIND_ARRAY &&
|
2012-12-02 23:22:15 +00:00
|
|
|
jv_get_kind(keys) == JV_KIND_ARRAY &&
|
|
|
|
|
jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {
|
|
|
|
|
return jv_sort(input, keys);
|
|
|
|
|
} else {
|
|
|
|
|
return type_error2(input, keys, "cannot be sorted, as they are not both arrays");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_group_by_impl(jq_state *jq, jv input, jv keys) {
|
2015-07-19 09:36:16 -07:00
|
|
|
if (jv_get_kind(input) == JV_KIND_ARRAY &&
|
2012-12-02 23:22:15 +00:00
|
|
|
jv_get_kind(keys) == JV_KIND_ARRAY &&
|
|
|
|
|
jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {
|
|
|
|
|
return jv_group(input, keys);
|
|
|
|
|
} else {
|
|
|
|
|
return type_error2(input, keys, "cannot be sorted, as they are not both arrays");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-26 23:29:00 -05:00
|
|
|
#ifdef HAVE_LIBONIG
|
2014-06-18 19:49:38 -04:00
|
|
|
static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups,
|
|
|
|
|
int *groups, regex_t *reg, void *arg) {
|
|
|
|
|
jv captures = *(jv*)arg;
|
|
|
|
|
for (int i = 0; i < ngroups; ++i) {
|
|
|
|
|
jv cap = jv_array_get(jv_copy(captures),groups[i]-1);
|
|
|
|
|
if (jv_get_kind(cap) == JV_KIND_OBJECT) {
|
|
|
|
|
cap = jv_object_set(cap, jv_string("name"), jv_string_sized((const char*)name, name_end-name));
|
|
|
|
|
captures = jv_array_set(captures,groups[i]-1,cap);
|
|
|
|
|
} else {
|
|
|
|
|
jv_free(cap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*(jv *)arg = captures;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
|
2014-06-18 19:49:38 -04:00
|
|
|
int test = jv_equal(testmode, jv_true());
|
|
|
|
|
jv result;
|
|
|
|
|
int onigret;
|
|
|
|
|
int global = 0;
|
|
|
|
|
regex_t *reg;
|
|
|
|
|
OnigErrorInfo einfo;
|
|
|
|
|
OnigRegion* region;
|
|
|
|
|
|
|
|
|
|
if (jv_get_kind(input) != JV_KIND_STRING) {
|
|
|
|
|
jv_free(regex);
|
|
|
|
|
jv_free(modifiers);
|
|
|
|
|
return type_error(input, "cannot be matched, as it is not a string");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (jv_get_kind(regex) != JV_KIND_STRING) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(modifiers);
|
|
|
|
|
return type_error(regex, "is not a string");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP;
|
|
|
|
|
|
|
|
|
|
if (jv_get_kind(modifiers) == JV_KIND_STRING) {
|
|
|
|
|
jv modarray = jv_string_explode(jv_copy(modifiers));
|
|
|
|
|
jv_array_foreach(modarray, i, mod) {
|
|
|
|
|
switch ((int)jv_number_value(mod)) {
|
|
|
|
|
case 'g':
|
|
|
|
|
global = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'i':
|
|
|
|
|
options |= ONIG_OPTION_IGNORECASE;
|
|
|
|
|
break;
|
|
|
|
|
case 'x':
|
|
|
|
|
options |= ONIG_OPTION_EXTEND;
|
|
|
|
|
break;
|
|
|
|
|
case 'm':
|
|
|
|
|
options |= ONIG_OPTION_MULTILINE;
|
|
|
|
|
break;
|
|
|
|
|
case 's':
|
|
|
|
|
options |= ONIG_OPTION_SINGLELINE;
|
|
|
|
|
break;
|
|
|
|
|
case 'p':
|
|
|
|
|
options |= ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE;
|
|
|
|
|
break;
|
|
|
|
|
case 'l':
|
|
|
|
|
options |= ONIG_OPTION_FIND_LONGEST;
|
|
|
|
|
break;
|
|
|
|
|
case 'n':
|
|
|
|
|
options |= ONIG_OPTION_FIND_NOT_EMPTY;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(regex);
|
|
|
|
|
jv_free(modarray);
|
|
|
|
|
return jv_invalid_with_msg(jv_string_concat(modifiers,
|
|
|
|
|
jv_string(" is not a valid modifier string")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jv_free(modarray);
|
2015-07-19 09:36:16 -07:00
|
|
|
} else if (jv_get_kind(modifiers) != JV_KIND_NULL) {
|
2014-06-18 19:49:38 -04:00
|
|
|
// If it isn't a string or null, then it is the wrong type...
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(regex);
|
|
|
|
|
return type_error(modifiers, "is not a string");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jv_free(modifiers);
|
|
|
|
|
|
|
|
|
|
onigret = onig_new(®, (const UChar*)jv_string_value(regex),
|
|
|
|
|
(const UChar*)(jv_string_value(regex) + jv_string_length_bytes(jv_copy(regex))),
|
|
|
|
|
options, ONIG_ENCODING_UTF8, ONIG_SYNTAX_PERL_NG, &einfo);
|
|
|
|
|
if (onigret != ONIG_NORMAL) {
|
|
|
|
|
UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];
|
2014-10-06 21:48:17 -04:00
|
|
|
onig_error_code_to_str(ebuf, onigret, &einfo);
|
2014-06-18 19:49:38 -04:00
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(regex);
|
|
|
|
|
return jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),
|
|
|
|
|
jv_string((char*)ebuf)));
|
|
|
|
|
}
|
2015-06-19 10:33:35 -07:00
|
|
|
result = test ? jv_false() : jv_array();
|
2014-06-18 19:49:38 -04:00
|
|
|
const char *input_string = jv_string_value(input);
|
|
|
|
|
const UChar* start = (const UChar*)jv_string_value(input);
|
|
|
|
|
const unsigned long length = jv_string_length_bytes(jv_copy(input));
|
|
|
|
|
const UChar* end = start + length;
|
|
|
|
|
region = onig_region_new();
|
|
|
|
|
do {
|
2015-07-19 09:36:16 -07:00
|
|
|
onigret = onig_search(reg,
|
2014-06-18 19:49:38 -04:00
|
|
|
(const UChar*)jv_string_value(input), end, /* string boundaries */
|
|
|
|
|
start, end, /* search boundaries */
|
|
|
|
|
region, ONIG_OPTION_NONE);
|
|
|
|
|
if (onigret >= 0) {
|
|
|
|
|
if (test) {
|
|
|
|
|
result = jv_true();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Zero-width match
|
|
|
|
|
if (region->end[0] == region->beg[0]) {
|
|
|
|
|
unsigned long idx;
|
|
|
|
|
const char *fr = (const char*)input_string;
|
2019-02-02 18:18:18 -08:00
|
|
|
for (idx = 0; fr < input_string+region->beg[0]; idx++) {
|
2014-06-18 19:49:38 -04:00
|
|
|
fr += jvp_utf8_decode_length(*fr);
|
|
|
|
|
}
|
|
|
|
|
jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
|
|
|
|
match = jv_object_set(match, jv_string("length"), jv_number(0));
|
|
|
|
|
match = jv_object_set(match, jv_string("string"), jv_string(""));
|
|
|
|
|
match = jv_object_set(match, jv_string("captures"), jv_array());
|
|
|
|
|
result = jv_array_append(result, match);
|
|
|
|
|
start += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned long idx;
|
|
|
|
|
unsigned long len;
|
|
|
|
|
const char *fr = (const char*)input_string;
|
|
|
|
|
|
2014-08-11 14:58:01 -05:00
|
|
|
for (idx = len = 0; fr < input_string+region->end[0]; len++) {
|
2014-06-18 19:49:38 -04:00
|
|
|
if (fr == input_string+region->beg[0]) idx = len, len=0;
|
|
|
|
|
fr += jvp_utf8_decode_length(*fr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
|
|
|
|
|
|
|
|
|
unsigned long blen = region->end[0]-region->beg[0];
|
|
|
|
|
match = jv_object_set(match, jv_string("length"), jv_number(len));
|
|
|
|
|
match = jv_object_set(match, jv_string("string"), jv_string_sized(input_string+region->beg[0],blen));
|
|
|
|
|
jv captures = jv_array();
|
|
|
|
|
for (int i = 1; i < region->num_regs; ++i) {
|
|
|
|
|
// Empty capture.
|
|
|
|
|
if (region->beg[i] == region->end[i]) {
|
|
|
|
|
// Didn't match.
|
|
|
|
|
jv cap;
|
|
|
|
|
if (region->beg[i] == -1) {
|
|
|
|
|
cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1));
|
|
|
|
|
cap = jv_object_set(cap, jv_string("string"), jv_null());
|
|
|
|
|
} else {
|
|
|
|
|
fr = input_string;
|
2019-02-02 18:18:18 -08:00
|
|
|
for (idx = 0; fr < input_string+region->beg[i]; idx++) {
|
2014-06-18 19:49:38 -04:00
|
|
|
fr += jvp_utf8_decode_length(*fr);
|
|
|
|
|
}
|
|
|
|
|
cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
|
|
|
|
cap = jv_object_set(cap, jv_string("string"), jv_string(""));
|
|
|
|
|
}
|
|
|
|
|
cap = jv_object_set(cap, jv_string("length"), jv_number(0));
|
|
|
|
|
cap = jv_object_set(cap, jv_string("name"), jv_null());
|
|
|
|
|
captures = jv_array_append(captures, cap);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
fr = input_string;
|
2019-02-02 18:18:18 -08:00
|
|
|
for (idx = len = 0; fr < input_string+region->end[i]; len++) {
|
2014-06-18 19:49:38 -04:00
|
|
|
if (fr == input_string+region->beg[i]) idx = len, len=0;
|
|
|
|
|
fr += jvp_utf8_decode_length(*fr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
blen = region->end[i]-region->beg[i];
|
|
|
|
|
jv cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
|
|
|
|
cap = jv_object_set(cap, jv_string("length"), jv_number(len));
|
|
|
|
|
cap = jv_object_set(cap, jv_string("string"), jv_string_sized(input_string+region->beg[i],blen));
|
|
|
|
|
cap = jv_object_set(cap, jv_string("name"), jv_null());
|
|
|
|
|
captures = jv_array_append(captures,cap);
|
|
|
|
|
}
|
|
|
|
|
onig_foreach_name(reg,f_match_name_iter,&captures);
|
|
|
|
|
match = jv_object_set(match, jv_string("captures"), captures);
|
|
|
|
|
result = jv_array_append(result, match);
|
|
|
|
|
start = (const UChar*)(input_string+region->end[0]);
|
|
|
|
|
onig_region_free(region,0);
|
|
|
|
|
} else if (onigret == ONIG_MISMATCH) {
|
|
|
|
|
break;
|
|
|
|
|
} else { /* Error */
|
|
|
|
|
UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];
|
2019-02-02 16:17:12 -08:00
|
|
|
onig_error_code_to_str(ebuf, onigret, &einfo);
|
2014-06-18 19:49:38 -04:00
|
|
|
jv_free(result);
|
|
|
|
|
result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),
|
|
|
|
|
jv_string((char*)ebuf)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (global && start != end);
|
|
|
|
|
onig_region_free(region,1);
|
|
|
|
|
region = NULL;
|
|
|
|
|
if (region)
|
|
|
|
|
onig_region_free(region,1);
|
|
|
|
|
onig_free(reg);
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jv_free(regex);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2017-01-26 23:29:00 -05:00
|
|
|
#else /* !HAVE_LIBONIG */
|
2015-04-22 17:34:49 -04:00
|
|
|
static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
|
2015-05-20 10:21:30 -05:00
|
|
|
return jv_invalid_with_msg(jv_string("jq was compiled without ONIGURUMA regex libary. match/test/sub and related functions are not available."));
|
2015-04-22 17:34:49 -04:00
|
|
|
}
|
2017-01-26 23:29:00 -05:00
|
|
|
#endif /* HAVE_LIBONIG */
|
2014-06-18 19:49:38 -04:00
|
|
|
|
2012-12-04 22:45:03 +00:00
|
|
|
static jv minmax_by(jv values, jv keys, int is_min) {
|
|
|
|
|
if (jv_get_kind(values) != JV_KIND_ARRAY)
|
|
|
|
|
return type_error2(values, keys, "cannot be iterated over");
|
|
|
|
|
if (jv_get_kind(keys) != JV_KIND_ARRAY)
|
|
|
|
|
return type_error2(values, keys, "cannot be iterated over");
|
|
|
|
|
if (jv_array_length(jv_copy(values)) != jv_array_length(jv_copy(keys)))
|
|
|
|
|
return type_error2(values, keys, "have wrong length");
|
|
|
|
|
|
|
|
|
|
if (jv_array_length(jv_copy(values)) == 0) {
|
|
|
|
|
jv_free(values);
|
|
|
|
|
jv_free(keys);
|
|
|
|
|
return jv_null();
|
|
|
|
|
}
|
|
|
|
|
jv ret = jv_array_get(jv_copy(values), 0);
|
|
|
|
|
jv retkey = jv_array_get(jv_copy(keys), 0);
|
|
|
|
|
for (int i=1; i<jv_array_length(jv_copy(values)); i++) {
|
|
|
|
|
jv item = jv_array_get(jv_copy(keys), i);
|
|
|
|
|
int cmp = jv_cmp(jv_copy(item), jv_copy(retkey));
|
|
|
|
|
if ((cmp < 0) == (is_min == 1)) {
|
|
|
|
|
jv_free(retkey);
|
|
|
|
|
retkey = item;
|
|
|
|
|
jv_free(ret);
|
|
|
|
|
ret = jv_array_get(jv_copy(values), i);
|
|
|
|
|
} else {
|
|
|
|
|
jv_free(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jv_free(values);
|
|
|
|
|
jv_free(keys);
|
|
|
|
|
jv_free(retkey);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_min(jq_state *jq, jv x) {
|
2012-12-04 22:45:03 +00:00
|
|
|
return minmax_by(x, jv_copy(x), 1);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_max(jq_state *jq, jv x) {
|
2012-12-04 22:45:03 +00:00
|
|
|
return minmax_by(x, jv_copy(x), 0);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_min_by_impl(jq_state *jq, jv x, jv y) {
|
2012-12-04 22:45:03 +00:00
|
|
|
return minmax_by(x, y, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_max_by_impl(jq_state *jq, jv x, jv y) {
|
2012-12-04 22:45:03 +00:00
|
|
|
return minmax_by(x, y, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_type(jq_state *jq, jv input) {
|
2012-12-02 22:12:08 +00:00
|
|
|
jv out = jv_string(jv_kind_name(jv_get_kind(input)));
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return out;
|
2012-09-17 23:06:48 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-17 23:14:26 -05:00
|
|
|
static jv f_isinfinite(jq_state *jq, jv input) {
|
2015-06-17 19:46:57 -05:00
|
|
|
jv_kind k = jv_get_kind(input);
|
|
|
|
|
if (k != JV_KIND_NUMBER) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_false();
|
|
|
|
|
}
|
|
|
|
|
double n = jv_number_value(input);
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return isinf(n) ? jv_true() : jv_false();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_isnan(jq_state *jq, jv input) {
|
|
|
|
|
jv_kind k = jv_get_kind(input);
|
|
|
|
|
if (k != JV_KIND_NUMBER) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_false();
|
|
|
|
|
}
|
|
|
|
|
double n = jv_number_value(input);
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return isnan(n) ? jv_true() : jv_false();
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-17 23:14:26 -05:00
|
|
|
static jv f_isnormal(jq_state *jq, jv input) {
|
|
|
|
|
jv_kind k = jv_get_kind(input);
|
|
|
|
|
if (k != JV_KIND_NUMBER) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_false();
|
|
|
|
|
}
|
|
|
|
|
double n = jv_number_value(input);
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return isnormal(n) ? jv_true() : jv_false();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_infinite(jq_state *jq, jv input) {
|
2015-06-17 19:46:57 -05:00
|
|
|
jv_free(input);
|
|
|
|
|
return jv_number(INFINITY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_nan(jq_state *jq, jv input) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jv_number(NAN);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 21:00:02 -05:00
|
|
|
static jv f_error(jq_state *jq, jv input) {
|
|
|
|
|
return jv_invalid_with_msg(input);
|
2012-12-10 22:30:09 +00:00
|
|
|
}
|
|
|
|
|
|
2014-06-16 22:33:24 -05:00
|
|
|
// FIXME Should autoconf check for this!
|
|
|
|
|
#ifndef WIN32
|
2017-02-26 18:06:35 -06:00
|
|
|
extern char **environ;
|
2014-06-16 22:33:24 -05:00
|
|
|
#endif
|
2014-06-13 17:51:41 -05:00
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_env(jq_state *jq, jv input) {
|
2014-06-13 17:51:41 -05:00
|
|
|
jv_free(input);
|
|
|
|
|
jv env = jv_object();
|
|
|
|
|
const char *var, *val;
|
2017-02-26 18:22:30 -06:00
|
|
|
for (char **e = environ; *e != NULL; e++) {
|
2014-06-13 17:51:41 -05:00
|
|
|
var = e[0];
|
|
|
|
|
val = strchr(e[0], '=');
|
|
|
|
|
if (val == NULL)
|
|
|
|
|
env = jv_object_set(env, jv_string(var), jv_null());
|
|
|
|
|
else if (var - val < INT_MAX)
|
|
|
|
|
env = jv_object_set(env, jv_string_sized(var, val - var), jv_string(val + 1));
|
|
|
|
|
}
|
|
|
|
|
return env;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-22 23:01:56 -06:00
|
|
|
static jv f_halt(jq_state *jq, jv input) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jq_halt(jq, jv_invalid(), jv_invalid());
|
|
|
|
|
return jv_true();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_halt_error(jq_state *jq, jv input, jv a) {
|
2017-11-27 20:37:14 -05:00
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
return type_error(input, "halt_error/1: number required");
|
|
|
|
|
}
|
2017-02-22 23:01:56 -06:00
|
|
|
jq_halt(jq, a, input);
|
|
|
|
|
return jv_true();
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-30 13:13:30 -06:00
|
|
|
static jv f_get_search_list(jq_state *jq, jv input) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jq_get_lib_dirs(jq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_get_prog_origin(jq_state *jq, jv input) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jq_get_prog_origin(jq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_get_jq_origin(jq_state *jq, jv input) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
return jq_get_jq_origin(jq);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-21 00:04:38 -05:00
|
|
|
static jv f_string_split(jq_state *jq, jv a, jv b) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string("split input and separator must be strings"));
|
2014-08-21 00:04:38 -05:00
|
|
|
}
|
|
|
|
|
return jv_string_split(a, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jv f_string_explode(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_STRING) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("explode input must be a string"));
|
2014-08-21 00:04:38 -05:00
|
|
|
}
|
|
|
|
|
return jv_string_explode(a);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-30 11:17:58 -06:00
|
|
|
static jv f_string_indexes(jq_state *jq, jv a, jv b) {
|
|
|
|
|
return jv_string_indexes(a, b);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-21 00:04:38 -05:00
|
|
|
static jv f_string_implode(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_ARRAY) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("implode input must be an array"));
|
2014-08-21 00:04:38 -05:00
|
|
|
}
|
|
|
|
|
return jv_string_implode(a);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_setpath(jq_state *jq, jv a, jv b, jv c) { return jv_setpath(a, b, c); }
|
2017-02-28 21:39:54 -06:00
|
|
|
extern jv _jq_path_append(jq_state *, jv, jv, jv);
|
|
|
|
|
static jv f_getpath(jq_state *jq, jv a, jv b) {
|
|
|
|
|
return _jq_path_append(jq, a, b, jv_getpath(jv_copy(a), jv_copy(b)));
|
|
|
|
|
}
|
2014-07-07 22:49:46 -05:00
|
|
|
static jv f_delpaths(jq_state *jq, jv a, jv b) { return jv_delpaths(a, b); }
|
|
|
|
|
static jv f_has(jq_state *jq, jv a, jv b) { return jv_has(a, b); }
|
|
|
|
|
|
2014-08-21 00:04:38 -05:00
|
|
|
static jv f_modulemeta(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_STRING) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("modulemeta input module name must be a string"));
|
2014-08-21 00:04:38 -05:00
|
|
|
}
|
|
|
|
|
return load_module_meta(jq, a);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-22 23:06:27 -06:00
|
|
|
static jv f_input(jq_state *jq, jv input) {
|
|
|
|
|
jv_free(input);
|
|
|
|
|
jq_input_cb cb;
|
|
|
|
|
void *data;
|
|
|
|
|
jq_get_input_cb(jq, &cb, &data);
|
2014-12-27 16:51:39 -06:00
|
|
|
if (cb == NULL)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("break"));
|
2014-12-22 23:06:27 -06:00
|
|
|
jv v = cb(jq, data);
|
|
|
|
|
if (jv_is_valid(v) || jv_invalid_has_msg(jv_copy(v)))
|
|
|
|
|
return v;
|
|
|
|
|
return jv_invalid_with_msg(jv_string("break"));
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-27 17:15:56 -06:00
|
|
|
static jv f_debug(jq_state *jq, jv input) {
|
2015-02-13 15:28:30 -06:00
|
|
|
jq_msg_cb cb;
|
2014-12-27 17:15:56 -06:00
|
|
|
void *data;
|
|
|
|
|
jq_get_debug_cb(jq, &cb, &data);
|
|
|
|
|
if (cb != NULL)
|
2015-02-13 15:28:30 -06:00
|
|
|
cb(data, jv_copy(input));
|
2014-12-27 17:15:56 -06:00
|
|
|
return input;
|
|
|
|
|
}
|
2014-12-22 23:06:27 -06:00
|
|
|
|
2014-12-27 17:15:56 -06:00
|
|
|
static jv f_stderr(jq_state *jq, jv input) {
|
|
|
|
|
jv_dumpf(jv_copy(input), stderr, 0);
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
static jv tm2jv(struct tm *tm) {
|
|
|
|
|
return JV_ARRAY(jv_number(tm->tm_year + 1900),
|
|
|
|
|
jv_number(tm->tm_mon),
|
|
|
|
|
jv_number(tm->tm_mday),
|
|
|
|
|
jv_number(tm->tm_hour),
|
|
|
|
|
jv_number(tm->tm_min),
|
|
|
|
|
jv_number(tm->tm_sec),
|
|
|
|
|
jv_number(tm->tm_wday),
|
|
|
|
|
jv_number(tm->tm_yday));
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-18 16:49:32 +08:00
|
|
|
#if defined(WIN32) && !defined(HAVE_SETENV)
|
|
|
|
|
static int setenv(const char *var, const char *val, int ovr)
|
|
|
|
|
{
|
|
|
|
|
BOOL b;
|
|
|
|
|
char c[2];
|
|
|
|
|
if (!ovr)
|
|
|
|
|
{
|
|
|
|
|
DWORD d;
|
|
|
|
|
d = GetEnvironmentVariableA (var, c, 2);
|
|
|
|
|
if (0 != d && GetLastError () != ERROR_ENVVAR_NOT_FOUND) {
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
b = SetEnvironmentVariableA (var, val);
|
|
|
|
|
if (b) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-03-31 10:03:35 -05:00
|
|
|
/*
|
|
|
|
|
* mktime() has side-effects and anyways, returns time in the local
|
|
|
|
|
* timezone, not UTC. We want timegm(), which isn't standard.
|
|
|
|
|
*
|
|
|
|
|
* To make things worse, mktime() tells you what the timezone
|
|
|
|
|
* adjustment is, but you have to #define _BSD_SOURCE to get this
|
|
|
|
|
* field of struct tm on some systems.
|
|
|
|
|
*
|
|
|
|
|
* This is all to blame on POSIX, of course.
|
|
|
|
|
*
|
|
|
|
|
* Our wrapper tries to use timegm() if available, or mktime() and
|
|
|
|
|
* correct for its side-effects if possible.
|
|
|
|
|
*
|
|
|
|
|
* Returns (time_t)-2 if mktime()'s side-effects cannot be corrected.
|
|
|
|
|
*/
|
2019-01-30 00:41:47 -05:00
|
|
|
static time_t my_mktime(struct tm *tm) {
|
2015-03-31 10:03:35 -05:00
|
|
|
#ifdef HAVE_TIMEGM
|
|
|
|
|
return timegm(tm);
|
2019-01-30 00:41:47 -05:00
|
|
|
#elif HAVE_TM_TM_GMT_OFF
|
|
|
|
|
|
|
|
|
|
time_t t = mktime(tm);
|
|
|
|
|
if (t == (time_t)-1)
|
|
|
|
|
return t;
|
|
|
|
|
return t + tm->tm_gmtoff;
|
|
|
|
|
#elif HAVE_TM___TM_GMT_OFF
|
|
|
|
|
time_t t = mktime(tm);
|
|
|
|
|
if (t == (time_t)-1)
|
|
|
|
|
return t;
|
|
|
|
|
return t + tm->__tm_gmtoff;
|
2019-02-14 18:42:51 +10:00
|
|
|
#elif WIN32
|
|
|
|
|
return _mkgmtime(tm);
|
2019-01-30 00:41:47 -05:00
|
|
|
#else
|
2017-05-21 01:57:55 -05:00
|
|
|
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;
|
2015-03-31 10:03:35 -05:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-21 16:24:48 -05:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-14 18:39:23 +10:00
|
|
|
#ifndef HAVE_STRPTIME
|
|
|
|
|
static char *strptime(const char *s, const char *format, struct tm *tm) {
|
|
|
|
|
if (strcmp(format, "%Y-%m-%dT%H:%M:%SZ"))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
int count, end;
|
|
|
|
|
count = sscanf(s, "%d-%d-%dT%d:%d:%d%n",
|
|
|
|
|
&tm->tm_year, &tm->tm_mon, &tm->tm_mday,
|
|
|
|
|
&tm->tm_hour, &tm->tm_min, &tm->tm_sec,
|
|
|
|
|
&end );
|
|
|
|
|
if (count == 6 && s[end] == 'Z') {
|
|
|
|
|
tm->tm_year -= 1900;
|
|
|
|
|
tm->tm_mon--;
|
|
|
|
|
return (char*)s + end + 1;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-03-06 00:14:15 -06:00
|
|
|
static jv f_strptime(jq_state *jq, jv a, jv b) {
|
2017-11-23 01:51:22 -06:00
|
|
|
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string("strptime/1 requires string inputs and arguments"));
|
2017-11-23 01:51:22 -06:00
|
|
|
}
|
2015-03-06 00:14:15 -06:00
|
|
|
|
|
|
|
|
struct tm tm;
|
2015-03-08 18:56:51 -05:00
|
|
|
memset(&tm, 0, sizeof(tm));
|
2017-05-21 01:58:18 -05:00
|
|
|
tm.tm_wday = 8; // sentinel
|
|
|
|
|
tm.tm_yday = 367; // sentinel
|
2015-03-06 00:14:15 -06:00
|
|
|
const char *input = jv_string_value(a);
|
|
|
|
|
const char *fmt = jv_string_value(b);
|
|
|
|
|
|
2019-02-14 18:39:23 +10:00
|
|
|
#ifndef HAVE_STRPTIME
|
|
|
|
|
if (strcmp(fmt, "%Y-%m-%dT%H:%M:%SZ")) {
|
|
|
|
|
return ret_error2(a, b, jv_string("strptime/1 only supports ISO 8601 on this platform"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
const char *end = strptime(input, fmt, &tm);
|
2015-03-06 00:14:15 -06:00
|
|
|
if (end == NULL || (*end != '\0' && !isspace(*end))) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt));
|
2015-03-06 00:14:15 -06:00
|
|
|
}
|
|
|
|
|
jv_free(b);
|
2017-05-21 16:24:48 -05:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2017-11-29 20:40:36 -05:00
|
|
|
#ifdef __APPLE__
|
|
|
|
|
/*
|
|
|
|
|
* Apple has made it worse, and different versions of the OS have different
|
|
|
|
|
* behaviors. Some versions just don't touch the fields, but others do, and
|
|
|
|
|
* sometimes provide wrong answers, at that! We can't tell at compile-time
|
|
|
|
|
* which behavior the target system will have, so instead we always use our
|
|
|
|
|
* functions to set these on OS X, and document that %u and %j are
|
|
|
|
|
* unsupported on OS X.
|
|
|
|
|
*/
|
|
|
|
|
set_tm_wday(&tm);
|
|
|
|
|
set_tm_yday(&tm);
|
|
|
|
|
#else
|
2017-05-21 16:24:48 -05:00
|
|
|
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);
|
2017-11-29 20:40:36 -05:00
|
|
|
#endif
|
2015-03-08 18:56:51 -05:00
|
|
|
jv r = tm2jv(&tm);
|
2015-03-06 00:14:15 -06:00
|
|
|
if (*end != '\0')
|
|
|
|
|
r = jv_array_append(r, jv_string(end));
|
2015-06-18 22:15:41 -07:00
|
|
|
jv_free(a); // must come after `*end` because `end` is a pointer into `a`'s string
|
2015-03-06 00:14:15 -06:00
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
#define TO_TM_FIELD(t, j, i) \
|
2015-03-06 00:14:15 -06:00
|
|
|
do { \
|
|
|
|
|
jv n = jv_array_get(jv_copy(j), (i)); \
|
2017-11-27 20:37:14 -05:00
|
|
|
if (jv_get_kind(n) != (JV_KIND_NUMBER)) { \
|
|
|
|
|
jv_free(j); \
|
2015-03-08 18:56:51 -05:00
|
|
|
return 0; \
|
2017-11-27 20:37:14 -05:00
|
|
|
} \
|
2015-03-06 00:14:15 -06:00
|
|
|
t = jv_number_value(n); \
|
|
|
|
|
jv_free(n); \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
static int jv2tm(jv a, struct tm *tm) {
|
|
|
|
|
memset(tm, 0, sizeof(*tm));
|
|
|
|
|
TO_TM_FIELD(tm->tm_year, a, 0);
|
2015-06-30 08:24:26 -07:00
|
|
|
tm->tm_year -= 1900;
|
2015-03-08 18:56:51 -05:00
|
|
|
TO_TM_FIELD(tm->tm_mon, a, 1);
|
|
|
|
|
TO_TM_FIELD(tm->tm_mday, a, 2);
|
|
|
|
|
TO_TM_FIELD(tm->tm_hour, a, 3);
|
|
|
|
|
TO_TM_FIELD(tm->tm_min, a, 4);
|
|
|
|
|
TO_TM_FIELD(tm->tm_sec, a, 5);
|
2015-06-30 08:24:26 -07:00
|
|
|
TO_TM_FIELD(tm->tm_wday, a, 6);
|
|
|
|
|
TO_TM_FIELD(tm->tm_yday, a, 7);
|
2015-03-06 00:14:15 -06:00
|
|
|
jv_free(a);
|
2015-06-30 08:24:26 -07:00
|
|
|
|
|
|
|
|
// We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.
|
|
|
|
|
// Setting tm_isdst to 0 is done by the memset.
|
|
|
|
|
// tm->tm_isdst = 0;
|
|
|
|
|
|
|
|
|
|
// The standard permits the tm structure to contain additional members. We
|
|
|
|
|
// hope it is okay to initialize them to zero, because the standard does not
|
|
|
|
|
// provide an alternative.
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
return 1;
|
2015-03-06 00:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef TO_TM_FIELD
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
static jv f_mktime(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_ARRAY)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("mktime requires array inputs"));
|
2015-03-08 18:56:51 -05:00
|
|
|
if (jv_array_length(jv_copy(a)) < 6)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("mktime requires parsed datetime inputs"));
|
2015-03-08 18:56:51 -05:00
|
|
|
struct tm tm;
|
|
|
|
|
if (!jv2tm(a, &tm))
|
2015-03-31 10:03:35 -05:00
|
|
|
return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));
|
|
|
|
|
time_t t = my_mktime(&tm);
|
2015-03-08 18:56:51 -05:00
|
|
|
if (t == (time_t)-1)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("invalid gmtime representation"));
|
2015-03-31 10:03:35 -05:00
|
|
|
if (t == (time_t)-2)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("mktime not supported on this platform"));
|
2015-03-08 18:56:51 -05:00
|
|
|
return jv_number(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_GMTIME_R
|
|
|
|
|
static jv f_gmtime(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("gmtime() requires numeric inputs"));
|
2015-03-08 18:56:51 -05:00
|
|
|
struct tm tm, *tmp;
|
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
|
|
|
|
double fsecs = jv_number_value(a);
|
|
|
|
|
time_t secs = fsecs;
|
|
|
|
|
jv_free(a);
|
|
|
|
|
tmp = gmtime_r(&secs, &tm);
|
|
|
|
|
if (tmp == NULL)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("errror converting number of seconds since epoch to datetime"));
|
|
|
|
|
a = tm2jv(tmp);
|
|
|
|
|
return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));
|
|
|
|
|
}
|
|
|
|
|
#elif defined HAVE_GMTIME
|
|
|
|
|
static jv f_gmtime(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("gmtime requires numeric inputs"));
|
2015-05-24 09:42:13 -05:00
|
|
|
struct tm tm, *tmp;
|
2015-03-08 18:56:51 -05:00
|
|
|
memset(&tm, 0, sizeof(tm));
|
|
|
|
|
double fsecs = jv_number_value(a);
|
|
|
|
|
time_t secs = fsecs;
|
|
|
|
|
jv_free(a);
|
|
|
|
|
tmp = gmtime(&secs);
|
|
|
|
|
if (tmp == NULL)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("errror converting number of seconds since epoch to datetime"));
|
|
|
|
|
a = tm2jv(tmp);
|
|
|
|
|
return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static jv f_gmtime(jq_state *jq, jv a) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
return jv_invalid_with_msg(jv_string("gmtime not implemented on this platform"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-02-23 22:26:53 -06:00
|
|
|
#ifdef HAVE_LOCALTIME_R
|
|
|
|
|
static jv f_localtime(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("localtime() requires numeric inputs"));
|
2017-02-23 22:26:53 -06:00
|
|
|
struct tm tm, *tmp;
|
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
|
|
|
|
double fsecs = jv_number_value(a);
|
|
|
|
|
time_t secs = fsecs;
|
|
|
|
|
jv_free(a);
|
|
|
|
|
tmp = localtime_r(&secs, &tm);
|
|
|
|
|
if (tmp == NULL)
|
2017-11-27 20:37:14 -05:00
|
|
|
return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime"));
|
2017-02-23 22:26:53 -06:00
|
|
|
a = tm2jv(tmp);
|
|
|
|
|
return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));
|
|
|
|
|
}
|
|
|
|
|
#elif defined HAVE_GMTIME
|
|
|
|
|
static jv f_localtime(jq_state *jq, jv a) {
|
|
|
|
|
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(a, jv_string("localtime requires numeric inputs"));
|
2017-02-23 22:26:53 -06:00
|
|
|
struct tm tm, *tmp;
|
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
|
|
|
|
double fsecs = jv_number_value(a);
|
|
|
|
|
time_t secs = fsecs;
|
|
|
|
|
jv_free(a);
|
|
|
|
|
tmp = localtime(&secs);
|
|
|
|
|
if (tmp == NULL)
|
2017-11-27 20:37:14 -05:00
|
|
|
return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime"));
|
2017-02-23 22:26:53 -06:00
|
|
|
a = tm2jv(tmp);
|
|
|
|
|
return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static jv f_localtime(jq_state *jq, jv a) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
return jv_invalid_with_msg(jv_string("localtime not implemented on this platform"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
#ifdef HAVE_STRFTIME
|
|
|
|
|
static jv f_strftime(jq_state *jq, jv a, jv b) {
|
|
|
|
|
if (jv_get_kind(a) == JV_KIND_NUMBER) {
|
|
|
|
|
a = f_gmtime(jq, a);
|
|
|
|
|
} else if (jv_get_kind(a) != JV_KIND_ARRAY) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string("strftime/1 requires parsed datetime inputs"));
|
|
|
|
|
} else if (jv_get_kind(b) != JV_KIND_STRING) {
|
|
|
|
|
return ret_error2(a, b, jv_string("strftime/1 requires a string format"));
|
2015-03-08 18:56:51 -05:00
|
|
|
}
|
|
|
|
|
struct tm tm;
|
|
|
|
|
if (!jv2tm(a, &tm))
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
const char *fmt = jv_string_value(b);
|
|
|
|
|
size_t alloced = strlen(fmt) + 100;
|
|
|
|
|
char *buf = alloca(alloced);
|
|
|
|
|
size_t n = strftime(buf, alloced, fmt, &tm);
|
|
|
|
|
jv_free(b);
|
|
|
|
|
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
|
|
|
|
if (n == 0 || n > alloced)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("strftime/1: unknown system failure"));
|
|
|
|
|
return jv_string(buf);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2017-11-27 20:37:14 -05:00
|
|
|
static jv f_strftime(jq_state *jq, jv a, jv b) {
|
2015-03-08 18:56:51 -05:00
|
|
|
jv_free(a);
|
|
|
|
|
jv_free(b);
|
|
|
|
|
return jv_invalid_with_msg(jv_string("strftime/1 not implemented on this platform"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-02-23 22:26:53 -06:00
|
|
|
#ifdef HAVE_STRFTIME
|
|
|
|
|
static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
|
|
|
|
if (jv_get_kind(a) == JV_KIND_NUMBER) {
|
|
|
|
|
a = f_localtime(jq, a);
|
|
|
|
|
} else if (jv_get_kind(a) != JV_KIND_ARRAY) {
|
2017-11-27 20:37:14 -05:00
|
|
|
return ret_error2(a, b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
|
|
|
|
} else if (jv_get_kind(b) != JV_KIND_STRING) {
|
|
|
|
|
return ret_error2(a, b, jv_string("strflocaltime/1 requires a string format"));
|
2017-02-23 22:26:53 -06:00
|
|
|
}
|
|
|
|
|
struct tm tm;
|
|
|
|
|
if (!jv2tm(a, &tm))
|
2017-11-27 20:37:14 -05:00
|
|
|
return jv_invalid_with_msg(jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
2017-02-23 22:26:53 -06:00
|
|
|
const char *fmt = jv_string_value(b);
|
|
|
|
|
size_t alloced = strlen(fmt) + 100;
|
|
|
|
|
char *buf = alloca(alloced);
|
|
|
|
|
size_t n = strftime(buf, alloced, fmt, &tm);
|
|
|
|
|
jv_free(b);
|
|
|
|
|
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
|
|
|
|
if (n == 0 || n > alloced)
|
|
|
|
|
return jv_invalid_with_msg(jv_string("strflocaltime/1: unknown system failure"));
|
|
|
|
|
return jv_string(buf);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2017-11-27 20:37:14 -05:00
|
|
|
static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
2017-02-23 22:26:53 -06:00
|
|
|
jv_free(a);
|
|
|
|
|
jv_free(b);
|
|
|
|
|
return jv_invalid_with_msg(jv_string("strflocaltime/1 not implemented on this platform"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-03-08 18:56:51 -05:00
|
|
|
#ifdef HAVE_GETTIMEOFDAY
|
|
|
|
|
static jv f_now(jq_state *jq, jv a) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
if (gettimeofday(&tv, NULL) == -1)
|
|
|
|
|
return jv_number(time(NULL));
|
|
|
|
|
return jv_number(tv.tv_sec + tv.tv_usec / 1000000.0);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static jv f_now(jq_state *jq, jv a) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
return jv_number(time(NULL));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-01-03 22:19:58 +01:00
|
|
|
static jv f_current_filename(jq_state *jq, jv a) {
|
|
|
|
|
jv_free(a);
|
|
|
|
|
|
2015-07-10 12:32:11 -05:00
|
|
|
jv r = jq_util_input_get_current_filename(jq);
|
|
|
|
|
if (jv_is_valid(r))
|
|
|
|
|
return r;
|
|
|
|
|
jv_free(r);
|
|
|
|
|
return jv_null();
|
2015-04-17 16:08:10 -04:00
|
|
|
}
|
2017-01-03 22:19:58 +01:00
|
|
|
static jv f_current_line(jq_state *jq, jv a) {
|
|
|
|
|
jv_free(a);
|
2015-04-17 16:08:10 -04:00
|
|
|
return jq_util_input_get_current_line(jq);
|
|
|
|
|
}
|
2014-08-10 16:52:03 -05:00
|
|
|
|
2014-02-21 08:46:56 +00:00
|
|
|
#define LIBM_DD(name) \
|
2017-02-05 01:28:15 -05:00
|
|
|
{(cfunction_ptr)f_ ## name, #name, 1},
|
2015-06-27 23:14:46 -05:00
|
|
|
#define LIBM_DD_NO(name)
|
|
|
|
|
|
|
|
|
|
#define LIBM_DDD(name) \
|
2017-02-05 01:28:15 -05:00
|
|
|
{(cfunction_ptr)f_ ## name, #name, 3},
|
2015-07-09 14:59:37 -04:00
|
|
|
#define LIBM_DDD_NO(name)
|
2015-07-19 09:36:16 -07:00
|
|
|
|
2017-02-04 00:11:19 -06:00
|
|
|
#define LIBM_DDDD(name) \
|
2017-02-05 01:28:15 -05:00
|
|
|
{(cfunction_ptr)f_ ## name, #name, 4},
|
2017-02-04 00:11:19 -06:00
|
|
|
#define LIBM_DDDD_NO(name)
|
|
|
|
|
|
2013-05-05 22:37:46 +01:00
|
|
|
static const struct cfunction function_list[] = {
|
2014-02-21 08:46:56 +00:00
|
|
|
#include "libm.h"
|
2017-03-29 00:07:42 -05:00
|
|
|
#ifdef HAVE_FREXP
|
|
|
|
|
{(cfunction_ptr)f_frexp,"frexp", 1},
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_MODF
|
|
|
|
|
{(cfunction_ptr)f_modf,"modf", 1},
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_LGAMMA_R
|
|
|
|
|
{(cfunction_ptr)f_lgamma_r,"lgamma_r", 1},
|
|
|
|
|
#endif
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_plus, "_plus", 3},
|
2013-01-03 12:53:06 +00:00
|
|
|
{(cfunction_ptr)f_negate, "_negate", 1},
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_minus, "_minus", 3},
|
|
|
|
|
{(cfunction_ptr)f_multiply, "_multiply", 3},
|
|
|
|
|
{(cfunction_ptr)f_divide, "_divide", 3},
|
2013-06-17 20:21:37 -05:00
|
|
|
{(cfunction_ptr)f_mod, "_mod", 3},
|
2013-11-29 16:16:52 -06:00
|
|
|
{(cfunction_ptr)f_dump, "tojson", 1},
|
|
|
|
|
{(cfunction_ptr)f_json_parse, "fromjson", 1},
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_tonumber, "tonumber", 1},
|
|
|
|
|
{(cfunction_ptr)f_tostring, "tostring", 1},
|
|
|
|
|
{(cfunction_ptr)f_keys, "keys", 1},
|
2014-09-30 21:49:37 -05:00
|
|
|
{(cfunction_ptr)f_keys_unsorted, "keys_unsorted", 1},
|
2013-11-28 15:17:32 -06:00
|
|
|
{(cfunction_ptr)f_startswith, "startswith", 2},
|
|
|
|
|
{(cfunction_ptr)f_endswith, "endswith", 2},
|
2013-11-30 00:44:14 -06:00
|
|
|
{(cfunction_ptr)f_ltrimstr, "ltrimstr", 2},
|
|
|
|
|
{(cfunction_ptr)f_rtrimstr, "rtrimstr", 2},
|
2014-07-07 22:49:46 -05:00
|
|
|
{(cfunction_ptr)f_string_split, "split", 2},
|
|
|
|
|
{(cfunction_ptr)f_string_explode, "explode", 1},
|
|
|
|
|
{(cfunction_ptr)f_string_implode, "implode", 1},
|
2014-12-30 11:17:58 -06:00
|
|
|
{(cfunction_ptr)f_string_indexes, "_strindices", 2},
|
2014-07-07 22:49:46 -05:00
|
|
|
{(cfunction_ptr)f_setpath, "setpath", 3}, // FIXME typechecking
|
|
|
|
|
{(cfunction_ptr)f_getpath, "getpath", 2},
|
|
|
|
|
{(cfunction_ptr)f_delpaths, "delpaths", 2},
|
|
|
|
|
{(cfunction_ptr)f_has, "has", 2},
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_equal, "_equal", 3},
|
|
|
|
|
{(cfunction_ptr)f_notequal, "_notequal", 3},
|
|
|
|
|
{(cfunction_ptr)f_less, "_less", 3},
|
|
|
|
|
{(cfunction_ptr)f_greater, "_greater", 3},
|
|
|
|
|
{(cfunction_ptr)f_lesseq, "_lesseq", 3},
|
|
|
|
|
{(cfunction_ptr)f_greatereq, "_greatereq", 3},
|
2012-12-02 22:24:02 +00:00
|
|
|
{(cfunction_ptr)f_contains, "contains", 2},
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_length, "length", 1},
|
2015-09-08 12:51:42 +10:00
|
|
|
{(cfunction_ptr)f_utf8bytelength, "utf8bytelength", 1},
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_type, "type", 1},
|
2015-06-17 23:14:26 -05:00
|
|
|
{(cfunction_ptr)f_isinfinite, "isinfinite", 1},
|
2015-06-17 19:46:57 -05:00
|
|
|
{(cfunction_ptr)f_isnan, "isnan", 1},
|
2015-06-17 23:14:26 -05:00
|
|
|
{(cfunction_ptr)f_isnormal, "isnormal", 1},
|
|
|
|
|
{(cfunction_ptr)f_infinite, "infinite", 1},
|
2015-06-17 19:46:57 -05:00
|
|
|
{(cfunction_ptr)f_nan, "nan", 1},
|
2012-12-02 22:12:08 +00:00
|
|
|
{(cfunction_ptr)f_sort, "sort", 1},
|
2012-12-02 23:22:15 +00:00
|
|
|
{(cfunction_ptr)f_sort_by_impl, "_sort_by_impl", 2},
|
|
|
|
|
{(cfunction_ptr)f_group_by_impl, "_group_by_impl", 2},
|
2012-12-04 22:45:03 +00:00
|
|
|
{(cfunction_ptr)f_min, "min", 1},
|
|
|
|
|
{(cfunction_ptr)f_max, "max", 1},
|
|
|
|
|
{(cfunction_ptr)f_min_by_impl, "_min_by_impl", 2},
|
|
|
|
|
{(cfunction_ptr)f_max_by_impl, "_max_by_impl", 2},
|
2019-02-06 21:00:02 -05:00
|
|
|
{(cfunction_ptr)f_error, "error", 1},
|
2012-12-27 20:49:34 +00:00
|
|
|
{(cfunction_ptr)f_format, "format", 2},
|
2014-06-13 17:51:41 -05:00
|
|
|
{(cfunction_ptr)f_env, "env", 1},
|
2017-02-22 23:01:56 -06:00
|
|
|
{(cfunction_ptr)f_halt, "halt", 1},
|
|
|
|
|
{(cfunction_ptr)f_halt_error, "halt_error", 2},
|
2014-12-30 13:13:30 -06:00
|
|
|
{(cfunction_ptr)f_get_search_list, "get_search_list", 1},
|
|
|
|
|
{(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},
|
|
|
|
|
{(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},
|
2014-06-18 19:49:38 -04:00
|
|
|
{(cfunction_ptr)f_match, "_match_impl", 4},
|
2014-08-10 16:52:03 -05:00
|
|
|
{(cfunction_ptr)f_modulemeta, "modulemeta", 1},
|
2019-02-05 23:52:50 -05:00
|
|
|
{(cfunction_ptr)f_input, "input", 1},
|
2014-12-27 17:15:56 -06:00
|
|
|
{(cfunction_ptr)f_debug, "debug", 1},
|
2014-12-27 17:15:56 -06:00
|
|
|
{(cfunction_ptr)f_stderr, "stderr", 1},
|
2015-03-06 00:14:15 -06:00
|
|
|
{(cfunction_ptr)f_strptime, "strptime", 2},
|
2015-03-08 18:56:51 -05:00
|
|
|
{(cfunction_ptr)f_strftime, "strftime", 2},
|
2017-02-23 22:26:53 -06:00
|
|
|
{(cfunction_ptr)f_strflocaltime, "strflocaltime", 2},
|
2015-03-06 00:14:15 -06:00
|
|
|
{(cfunction_ptr)f_mktime, "mktime", 1},
|
2015-03-08 18:56:51 -05:00
|
|
|
{(cfunction_ptr)f_gmtime, "gmtime", 1},
|
2017-02-23 22:26:53 -06:00
|
|
|
{(cfunction_ptr)f_localtime, "localtime", 1},
|
2015-03-08 18:56:51 -05:00
|
|
|
{(cfunction_ptr)f_now, "now", 1},
|
2015-04-17 16:08:10 -04:00
|
|
|
{(cfunction_ptr)f_current_filename, "input_filename", 1},
|
|
|
|
|
{(cfunction_ptr)f_current_line, "input_line_number", 1},
|
2012-08-16 01:00:30 +01:00
|
|
|
};
|
2017-02-04 00:11:19 -06:00
|
|
|
#undef LIBM_DDDD_NO
|
2015-06-27 23:14:46 -05:00
|
|
|
#undef LIBM_DDD_NO
|
|
|
|
|
#undef LIBM_DD_NO
|
2017-02-04 00:11:19 -06:00
|
|
|
#undef LIBM_DDDD
|
2015-06-27 23:14:46 -05:00
|
|
|
#undef LIBM_DDD
|
2014-02-21 08:46:56 +00:00
|
|
|
#undef LIBM_DD
|
2012-09-17 20:59:34 +01:00
|
|
|
|
2012-11-26 18:53:47 +00:00
|
|
|
struct bytecoded_builtin { const char* name; block code; };
|
|
|
|
|
static block bind_bytecoded_builtins(block b) {
|
|
|
|
|
block builtins = gen_noop();
|
2012-12-28 16:08:29 +00:00
|
|
|
{
|
|
|
|
|
struct bytecoded_builtin builtin_defs[] = {
|
|
|
|
|
{"empty", gen_op_simple(BACKTRACK)},
|
|
|
|
|
{"not", gen_condbranch(gen_const(jv_false()),
|
|
|
|
|
gen_const(jv_true()))}
|
|
|
|
|
};
|
|
|
|
|
for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {
|
|
|
|
|
builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),
|
|
|
|
|
builtin_defs[i].code));
|
|
|
|
|
}
|
2012-11-26 18:53:47 +00:00
|
|
|
}
|
2012-12-28 16:08:29 +00:00
|
|
|
{
|
|
|
|
|
struct bytecoded_builtin builtin_def_1arg[] = {
|
2015-07-19 09:36:16 -07:00
|
|
|
{"path", BLOCK(gen_op_simple(PATH_BEGIN),
|
2012-12-28 16:08:29 +00:00
|
|
|
gen_call("arg", gen_noop()),
|
2013-05-13 15:00:05 +01:00
|
|
|
gen_op_simple(PATH_END))},
|
2012-12-28 16:08:29 +00:00
|
|
|
};
|
|
|
|
|
for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {
|
|
|
|
|
builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,
|
2013-06-18 01:07:18 +01:00
|
|
|
gen_param("arg"),
|
2012-12-28 16:08:29 +00:00
|
|
|
builtin_def_1arg[i].code));
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-14 16:09:10 +01:00
|
|
|
{
|
2014-07-02 21:22:53 -05:00
|
|
|
// Note that we can now define `range` as a jq-coded function
|
2013-06-18 01:07:18 +01:00
|
|
|
block rangevar = gen_op_var_fresh(STOREV, "rangevar");
|
2015-08-06 23:04:47 -07:00
|
|
|
block rangestart = gen_op_var_fresh(STOREV, "rangestart");
|
|
|
|
|
block range = BLOCK(gen_op_simple(DUP),
|
|
|
|
|
gen_call("start", gen_noop()),
|
|
|
|
|
rangestart,
|
2013-05-14 16:09:10 +01:00
|
|
|
gen_call("end", gen_noop()),
|
2015-08-06 23:04:47 -07:00
|
|
|
gen_op_simple(DUP),
|
|
|
|
|
gen_op_bound(LOADV, rangestart),
|
|
|
|
|
// Reset rangevar for every value generated by "end"
|
|
|
|
|
rangevar,
|
2013-06-18 01:07:18 +01:00
|
|
|
gen_op_bound(RANGE, rangevar));
|
2013-05-14 16:09:10 +01:00
|
|
|
builtins = BLOCK(builtins, gen_function("range",
|
2013-06-18 01:07:18 +01:00
|
|
|
BLOCK(gen_param("start"), gen_param("end")),
|
2013-05-14 16:09:10 +01:00
|
|
|
range));
|
|
|
|
|
}
|
2017-02-05 01:28:15 -05:00
|
|
|
return block_bind(builtins, b, OP_IS_CALL_PSEUDO);
|
2012-11-26 18:53:47 +00:00
|
|
|
}
|
2012-09-17 20:59:34 +01:00
|
|
|
|
2017-02-05 01:28:15 -05:00
|
|
|
|
2014-02-21 08:46:56 +00:00
|
|
|
|
2015-10-17 23:24:13 -07:00
|
|
|
static const char* const jq_builtins =
|
2017-02-05 01:28:15 -05:00
|
|
|
/* Include jq-coded builtins */
|
2016-01-15 16:21:16 -06:00
|
|
|
#include "src/builtin.inc"
|
2017-02-04 00:11:10 -06:00
|
|
|
|
|
|
|
|
/* Include unsupported math functions next */
|
|
|
|
|
#define LIBM_DD(name)
|
|
|
|
|
#define LIBM_DDD(name)
|
2017-02-04 00:11:19 -06:00
|
|
|
#define LIBM_DDDD(name)
|
2017-03-29 00:07:42 -05:00
|
|
|
#define LIBM_DD_NO(name) "def " #name ": \"Error: " #name "/0 not found at build time\"|error;"
|
|
|
|
|
#define LIBM_DDD_NO(name) "def " #name "(a;b): \"Error: " #name "/2 not found at build time\"|error;"
|
|
|
|
|
#define LIBM_DDDD_NO(name) "def " #name "(a;b;c): \"Error: " #name "/3 not found at build time\"|error;"
|
2017-02-04 00:11:10 -06:00
|
|
|
#include "libm.h"
|
2017-03-29 00:07:42 -05:00
|
|
|
#ifndef HAVE_FREXP
|
|
|
|
|
"def frexp: \"Error: frexp/0 not found found at build time\"|error;"
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef HAVE_MODF
|
|
|
|
|
"def modf: \"Error: modf/0 not found found at build time\"|error;"
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef HAVE_LGAMMA_R
|
|
|
|
|
"def lgamma_r: \"Error: lgamma_r/0 not found found at build time\"|error;"
|
|
|
|
|
#endif
|
2015-10-17 23:24:13 -07:00
|
|
|
;
|
2015-08-11 02:50:48 -04:00
|
|
|
|
2017-02-04 00:11:19 -06:00
|
|
|
#undef LIBM_DDDD_NO
|
2015-06-27 23:14:46 -05:00
|
|
|
#undef LIBM_DDD_NO
|
|
|
|
|
#undef LIBM_DD_NO
|
2017-02-04 00:11:19 -06:00
|
|
|
#undef LIBM_DDDD
|
2015-06-27 23:14:46 -05:00
|
|
|
#undef LIBM_DDD
|
2014-02-21 08:46:56 +00:00
|
|
|
#undef LIBM_DD
|
2012-09-18 10:17:38 +01:00
|
|
|
|
2012-09-17 20:59:34 +01:00
|
|
|
|
2017-02-05 01:28:15 -05:00
|
|
|
static block gen_builtin_list(block builtins) {
|
2017-02-26 19:50:10 -05:00
|
|
|
jv list = jv_array_append(block_list_funcs(builtins, 1), jv_string("builtins/0"));
|
2017-02-05 01:28:15 -05:00
|
|
|
return BLOCK(builtins, gen_function("builtins", gen_noop(), gen_const(list)));
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-30 02:05:42 -06:00
|
|
|
static int builtins_bind_one(jq_state *jq, block* bb, const char* code) {
|
2014-07-09 00:55:20 -04:00
|
|
|
struct locfile* src;
|
2015-03-29 19:12:23 -05:00
|
|
|
src = locfile_init(jq, "<builtin>", code, strlen(code));
|
2013-05-17 03:03:42 +10:00
|
|
|
block funcs;
|
2014-07-09 00:55:20 -04:00
|
|
|
int nerrors = jq_parse_library(src, &funcs);
|
2013-05-17 03:03:42 +10:00
|
|
|
if (nerrors == 0) {
|
2017-02-05 01:28:15 -05:00
|
|
|
*bb = block_bind(funcs, *bb, OP_IS_CALL_PSEUDO);
|
2012-09-18 10:17:38 +01:00
|
|
|
}
|
2014-07-09 00:55:20 -04:00
|
|
|
locfile_free(src);
|
2013-05-17 03:03:42 +10:00
|
|
|
return nerrors;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-30 02:05:42 -06:00
|
|
|
static int slurp_lib(jq_state *jq, block* bb) {
|
2013-05-17 03:03:42 +10:00
|
|
|
int nerrors = 0;
|
|
|
|
|
char* home = getenv("HOME");
|
|
|
|
|
if (home) { // silently ignore no $HOME
|
|
|
|
|
jv filename = jv_string_append_str(jv_string(home), "/.jq");
|
2013-06-15 00:08:59 -05:00
|
|
|
jv data = jv_load_file(jv_string_value(filename), 1);
|
2013-05-17 03:03:42 +10:00
|
|
|
if (jv_is_valid(data)) {
|
2013-11-30 02:05:42 -06:00
|
|
|
nerrors = builtins_bind_one(jq, bb, jv_string_value(data) );
|
2013-05-17 03:03:42 +10:00
|
|
|
}
|
|
|
|
|
jv_free(filename);
|
|
|
|
|
jv_free(data);
|
|
|
|
|
}
|
|
|
|
|
return nerrors;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-30 02:05:42 -06:00
|
|
|
int builtins_bind(jq_state *jq, block* bb) {
|
2017-02-05 01:28:15 -05:00
|
|
|
block builtins = gen_noop();
|
2013-11-30 02:05:42 -06:00
|
|
|
int nerrors = slurp_lib(jq, bb);
|
2013-05-17 03:03:42 +10:00
|
|
|
if (nerrors) {
|
|
|
|
|
block_free(*bb);
|
|
|
|
|
return nerrors;
|
|
|
|
|
}
|
2017-02-05 01:28:15 -05:00
|
|
|
nerrors = builtins_bind_one(jq, &builtins, jq_builtins);
|
2015-10-17 23:24:13 -07:00
|
|
|
assert(!nerrors);
|
2017-02-05 01:28:15 -05:00
|
|
|
builtins = bind_bytecoded_builtins(builtins);
|
|
|
|
|
builtins = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), builtins);
|
|
|
|
|
builtins = gen_builtin_list(builtins);
|
|
|
|
|
*bb = block_bind(builtins, *bb, OP_IS_CALL_PSEUDO);
|
|
|
|
|
*bb = block_drop_unreferenced(*bb);
|
2013-05-17 03:03:42 +10:00
|
|
|
return nerrors;
|
2012-09-16 17:08:56 +01:00
|
|
|
}
|