Line | Count | Source |
1 | | #if __SIZEOF_POINTER__==4 |
2 | | # define _TIME_BITS 64 |
3 | | #endif |
4 | | #ifndef __sun__ |
5 | | # define _XOPEN_SOURCE |
6 | | # define _XOPEN_SOURCE_EXTENDED 1 |
7 | | #else |
8 | | # define _XPG6 |
9 | | # define __EXTENSIONS__ |
10 | | #endif |
11 | | #ifdef __OpenBSD__ |
12 | | # define _BSD_SOURCE |
13 | | #endif |
14 | | #include <sys/time.h> |
15 | | #include <stdlib.h> |
16 | | #include <stddef.h> |
17 | | #include <assert.h> |
18 | | #include <ctype.h> |
19 | | #include <limits.h> |
20 | | #include <math.h> |
21 | | #ifdef HAVE_LIBONIG |
22 | | #include <oniguruma.h> |
23 | | #endif |
24 | | #include <string.h> |
25 | | #include <time.h> |
26 | | #ifdef WIN32 |
27 | | #include <windows.h> |
28 | | #endif |
29 | | #include "builtin.h" |
30 | | #include "compile.h" |
31 | | #include "jq_parser.h" |
32 | | #include "bytecode.h" |
33 | | #include "linker.h" |
34 | | #include "locfile.h" |
35 | | #include "iso8601.h" |
36 | | #include "jv_unicode.h" |
37 | | #include "jv_alloc.h" |
38 | | #include "jv_dtoa.h" |
39 | | #include "jv_dtoa_tsd.h" |
40 | | #include "jv_private.h" |
41 | | #include "util.h" |
42 | | |
43 | | |
44 | | #define BINOP(name) \ |
45 | 0 | static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \ |
46 | 0 | jv_free(input); \ |
47 | 0 | return binop_ ## name(a, b); \ |
48 | 0 | } Unexecuted instantiation: builtin.c:f_plus Unexecuted instantiation: builtin.c:f_minus Unexecuted instantiation: builtin.c:f_multiply Unexecuted instantiation: builtin.c:f_divide Unexecuted instantiation: builtin.c:f_mod Unexecuted instantiation: builtin.c:f_equal Unexecuted instantiation: builtin.c:f_notequal Unexecuted instantiation: builtin.c:f_less Unexecuted instantiation: builtin.c:f_lesseq Unexecuted instantiation: builtin.c:f_greater Unexecuted instantiation: builtin.c:f_greatereq |
49 | | BINOPS |
50 | | #undef BINOP |
51 | | |
52 | | |
53 | 0 | static jv type_error(jv bad, const char* msg) { |
54 | 0 | char errbuf[30]; |
55 | 0 | const char *badkind = jv_kind_name(jv_get_kind(bad)); |
56 | 0 | jv err = jv_invalid_with_msg(jv_string_fmt( |
57 | 0 | "%s (%s) %s", badkind, |
58 | 0 | jv_dump_string_trunc(bad, errbuf, sizeof(errbuf)), msg)); |
59 | 0 | return err; |
60 | 0 | } |
61 | | |
62 | 0 | static jv type_error2(jv bad1, jv bad2, const char* msg) { |
63 | 0 | char errbuf1[30], errbuf2[30]; |
64 | 0 | const char *badkind1 = jv_kind_name(jv_get_kind(bad1)); |
65 | 0 | const char *badkind2 = jv_kind_name(jv_get_kind(bad2)); |
66 | 0 | jv err = jv_invalid_with_msg(jv_string_fmt( |
67 | 0 | "%s (%s) and %s (%s) %s", badkind1, |
68 | 0 | jv_dump_string_trunc(bad1, errbuf1, sizeof(errbuf1)), |
69 | 0 | badkind2, |
70 | 0 | jv_dump_string_trunc(bad2, errbuf2, sizeof(errbuf2)), msg)); |
71 | 0 | return err; |
72 | 0 | } |
73 | | |
74 | 0 | static inline jv ret_error(jv bad, jv msg) { |
75 | 0 | jv_free(bad); |
76 | 0 | return jv_invalid_with_msg(msg); |
77 | 0 | } |
78 | | |
79 | 0 | static inline jv ret_error2(jv bad1, jv bad2, jv msg) { |
80 | 0 | jv_free(bad1); |
81 | 0 | jv_free(bad2); |
82 | 0 | return jv_invalid_with_msg(msg); |
83 | 0 | } |
84 | | |
85 | 0 | jv binop_plus(jv a, jv b) { |
86 | 0 | if (jv_get_kind(a) == JV_KIND_NULL) { |
87 | 0 | jv_free(a); |
88 | 0 | return b; |
89 | 0 | } else if (jv_get_kind(b) == JV_KIND_NULL) { |
90 | 0 | jv_free(b); |
91 | 0 | return a; |
92 | 0 | } else if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { |
93 | 0 | jv r = jv_number(jv_number_value(a) + |
94 | 0 | jv_number_value(b)); |
95 | 0 | jv_free(a); |
96 | 0 | jv_free(b); |
97 | 0 | return r; |
98 | 0 | } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) { |
99 | 0 | return jv_string_concat(a, b); |
100 | 0 | } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { |
101 | 0 | return jv_array_concat(a, b); |
102 | 0 | } else if (jv_get_kind(a) == JV_KIND_OBJECT && jv_get_kind(b) == JV_KIND_OBJECT) { |
103 | 0 | return jv_object_merge(a, b); |
104 | 0 | } else { |
105 | 0 | return type_error2(a, b, "cannot be added"); |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | #ifdef __APPLE__ |
110 | | // macOS has a bunch of libm deprecation warnings, so let's clean those up |
111 | | #ifdef HAVE_TGAMMA |
112 | | #define HAVE_GAMMA |
113 | | #define gamma tgamma |
114 | | #endif |
115 | | #ifdef HAVE___EXP10 |
116 | | #define HAVE_EXP10 |
117 | | #define exp10 __exp10 |
118 | | #endif |
119 | | #ifdef HAVE_REMAINDER |
120 | | #define HAVE_DREM |
121 | | #define drem remainder |
122 | | #endif |
123 | | |
124 | | // We replace significand with our own, since there's not a rename-replacement |
125 | | #ifdef HAVE_FREXP |
126 | | static double __jq_significand(double x) { |
127 | | int z; |
128 | | return 2*frexp(x, &z); |
129 | | } |
130 | | #define HAVE_SIGNIFICAND |
131 | | #define significand __jq_significand |
132 | | #elif defined(HAVE_SCALBN) && defined(HAVE_ILOGB) |
133 | | static double __jq_significand(double x) { |
134 | | return scalbn(x, -ilogb(x)); |
135 | | } |
136 | | #define HAVE_SIGNIFICAND |
137 | | #define significand __jq_significand |
138 | | #endif |
139 | | |
140 | | #endif // ifdef __APPLE__ |
141 | | |
142 | | #define LIBM_DD(name) \ |
143 | 0 | static jv f_ ## name(jq_state *jq, jv input) { \ |
144 | 0 | if (jv_get_kind(input) != JV_KIND_NUMBER) { \ |
145 | 0 | return type_error(input, "number required"); \ |
146 | 0 | } \ |
147 | 0 | jv ret = jv_number(name(jv_number_value(input))); \ |
148 | 0 | jv_free(input); \ |
149 | 0 | return ret; \ |
150 | 0 | } Unexecuted instantiation: builtin.c:f_acos Unexecuted instantiation: builtin.c:f_acosh Unexecuted instantiation: builtin.c:f_asin Unexecuted instantiation: builtin.c:f_asinh Unexecuted instantiation: builtin.c:f_atan Unexecuted instantiation: builtin.c:f_atanh Unexecuted instantiation: builtin.c:f_cbrt Unexecuted instantiation: builtin.c:f_cos Unexecuted instantiation: builtin.c:f_cosh Unexecuted instantiation: builtin.c:f_exp Unexecuted instantiation: builtin.c:f_exp2 Unexecuted instantiation: builtin.c:f_floor Unexecuted instantiation: builtin.c:f_j0 Unexecuted instantiation: builtin.c:f_j1 Unexecuted instantiation: builtin.c:f_log Unexecuted instantiation: builtin.c:f_log10 Unexecuted instantiation: builtin.c:f_log2 Unexecuted instantiation: builtin.c:f_sin Unexecuted instantiation: builtin.c:f_sinh Unexecuted instantiation: builtin.c:f_sqrt Unexecuted instantiation: builtin.c:f_tan Unexecuted instantiation: builtin.c:f_tanh Unexecuted instantiation: builtin.c:f_tgamma Unexecuted instantiation: builtin.c:f_y0 Unexecuted instantiation: builtin.c:f_y1 Unexecuted instantiation: builtin.c:f_ceil Unexecuted instantiation: builtin.c:f_erf Unexecuted instantiation: builtin.c:f_erfc Unexecuted instantiation: builtin.c:f_exp10 Unexecuted instantiation: builtin.c:f_expm1 Unexecuted instantiation: builtin.c:f_fabs Unexecuted instantiation: builtin.c:f_gamma Unexecuted instantiation: builtin.c:f_lgamma Unexecuted instantiation: builtin.c:f_log1p Unexecuted instantiation: builtin.c:f_logb Unexecuted instantiation: builtin.c:f_nearbyint Unexecuted instantiation: builtin.c:f_rint Unexecuted instantiation: builtin.c:f_round Unexecuted instantiation: builtin.c:f_significand Unexecuted instantiation: builtin.c:f_trunc |
151 | | #define LIBM_DD_NO(name) \ |
152 | | static jv f_ ## name(jq_state *jq, jv input) { \ |
153 | | jv error = jv_string("Error: " #name "/0 not found at build time"); \ |
154 | | return ret_error(input, error); \ |
155 | | } |
156 | | |
157 | | #define LIBM_DDD(name) \ |
158 | 0 | static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \ |
159 | 0 | jv_free(input); \ |
160 | 0 | if (jv_get_kind(a) != JV_KIND_NUMBER) { \ |
161 | 0 | jv_free(b); \ |
162 | 0 | return type_error(a, "number required"); \ |
163 | 0 | } \ |
164 | 0 | if (jv_get_kind(b) != JV_KIND_NUMBER) { \ |
165 | 0 | jv_free(a); \ |
166 | 0 | return type_error(b, "number required"); \ |
167 | 0 | } \ |
168 | 0 | jv ret = jv_number(name(jv_number_value(a), jv_number_value(b))); \ |
169 | 0 | jv_free(a); \ |
170 | 0 | jv_free(b); \ |
171 | 0 | return ret; \ |
172 | 0 | } Unexecuted instantiation: builtin.c:f_atan2 Unexecuted instantiation: builtin.c:f_hypot Unexecuted instantiation: builtin.c:f_pow Unexecuted instantiation: builtin.c:f_remainder Unexecuted instantiation: builtin.c:f_jn Unexecuted instantiation: builtin.c:f_yn Unexecuted instantiation: builtin.c:f_copysign Unexecuted instantiation: builtin.c:f_drem Unexecuted instantiation: builtin.c:f_fdim Unexecuted instantiation: builtin.c:f_fmax Unexecuted instantiation: builtin.c:f_fmin Unexecuted instantiation: builtin.c:f_fmod Unexecuted instantiation: builtin.c:f_nextafter Unexecuted instantiation: builtin.c:f_nexttoward Unexecuted instantiation: builtin.c:f_scalb Unexecuted instantiation: builtin.c:f_scalbln Unexecuted instantiation: builtin.c:f_ldexp |
173 | | #define LIBM_DDD_NO(name) \ |
174 | | static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \ |
175 | | jv_free(b); \ |
176 | | jv error = jv_string("Error: " #name "/2 not found at build time"); \ |
177 | | return ret_error2(input, a, error); \ |
178 | | } |
179 | | |
180 | | #define LIBM_DDDD(name) \ |
181 | 0 | static jv f_ ## name(jq_state *jq, jv input, jv a, jv b, jv c) { \ |
182 | 0 | jv_free(input); \ |
183 | 0 | if (jv_get_kind(a) != JV_KIND_NUMBER) { \ |
184 | 0 | jv_free(b); \ |
185 | 0 | jv_free(c); \ |
186 | 0 | return type_error(a, "number required"); \ |
187 | 0 | } \ |
188 | 0 | if (jv_get_kind(b) != JV_KIND_NUMBER) { \ |
189 | 0 | jv_free(a); \ |
190 | 0 | jv_free(c); \ |
191 | 0 | return type_error(b, "number required"); \ |
192 | 0 | } \ |
193 | 0 | if (jv_get_kind(c) != JV_KIND_NUMBER) { \ |
194 | 0 | jv_free(a); \ |
195 | 0 | jv_free(b); \ |
196 | 0 | return type_error(c, "number required"); \ |
197 | 0 | } \ |
198 | 0 | jv ret = jv_number(name(jv_number_value(a), jv_number_value(b), jv_number_value(c))); \ |
199 | 0 | jv_free(a); \ |
200 | 0 | jv_free(b); \ |
201 | 0 | jv_free(c); \ |
202 | 0 | return ret; \ |
203 | 0 | } |
204 | | #define LIBM_DDDD_NO(name) \ |
205 | | static jv f_ ## name(jq_state *jq, jv input, jv a, jv b, jv c) { \ |
206 | | jv_free(c); \ |
207 | | jv_free(b); \ |
208 | | jv error = jv_string("Error: " #name "/3 not found at build time"); \ |
209 | | return ret_error2(input, a, error); \ |
210 | | } |
211 | | |
212 | | #define LIBM_DA(name, type) \ |
213 | 0 | static jv f_ ## name(jq_state *jq, jv input) { \ |
214 | 0 | if (jv_get_kind(input) != JV_KIND_NUMBER) { \ |
215 | 0 | return type_error(input, "number required"); \ |
216 | 0 | } \ |
217 | 0 | type value; \ |
218 | 0 | double d = name(jv_number_value(input), &value); \ |
219 | 0 | jv ret = JV_ARRAY(jv_number(d), jv_number(value)); \ |
220 | 0 | jv_free(input); \ |
221 | 0 | return ret; \ |
222 | 0 | } Unexecuted instantiation: builtin.c:f_modf Unexecuted instantiation: builtin.c:f_frexp Unexecuted instantiation: builtin.c:f_lgamma_r |
223 | | #define LIBM_DA_NO(name, type) \ |
224 | | static jv f_ ## name(jq_state *jq, jv input) { \ |
225 | | jv error = jv_string("Error: " #name "/0 not found at build time"); \ |
226 | | return ret_error(input, error); \ |
227 | | } |
228 | | |
229 | | #include "libm.h" |
230 | | #undef LIBM_DDDD_NO |
231 | | #undef LIBM_DDD_NO |
232 | | #undef LIBM_DD_NO |
233 | | #undef LIBM_DA_NO |
234 | | #undef LIBM_DDDD |
235 | | #undef LIBM_DDD |
236 | | #undef LIBM_DD |
237 | | #undef LIBM_DA |
238 | | |
239 | | #ifdef __APPLE__ |
240 | | #undef gamma |
241 | | #undef drem |
242 | | #undef significand |
243 | | #undef exp10 |
244 | | #endif |
245 | | |
246 | 0 | static jv f_negate(jq_state *jq, jv input) { |
247 | 0 | if (jv_get_kind(input) != JV_KIND_NUMBER) { |
248 | 0 | return type_error(input, "cannot be negated"); |
249 | 0 | } |
250 | 0 | jv ret = jv_number_negate(input); |
251 | 0 | jv_free(input); |
252 | 0 | return ret; |
253 | 0 | } |
254 | | |
255 | 0 | static jv f_startswith(jq_state *jq, jv a, jv b) { |
256 | 0 | if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) |
257 | 0 | return ret_error2(a, b, jv_string("startswith() requires string inputs")); |
258 | 0 | int alen = jv_string_length_bytes(jv_copy(a)); |
259 | 0 | int blen = jv_string_length_bytes(jv_copy(b)); |
260 | 0 | jv ret; |
261 | |
|
262 | 0 | if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0) |
263 | 0 | ret = jv_true(); |
264 | 0 | else |
265 | 0 | ret = jv_false(); |
266 | 0 | jv_free(a); |
267 | 0 | jv_free(b); |
268 | 0 | return ret; |
269 | 0 | } |
270 | | |
271 | 0 | static jv f_endswith(jq_state *jq, jv a, jv b) { |
272 | 0 | if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) |
273 | 0 | return ret_error2(a, b, jv_string("endswith() requires string inputs")); |
274 | 0 | const char *astr = jv_string_value(a); |
275 | 0 | const char *bstr = jv_string_value(b); |
276 | 0 | size_t alen = jv_string_length_bytes(jv_copy(a)); |
277 | 0 | size_t blen = jv_string_length_bytes(jv_copy(b)); |
278 | 0 | jv ret; |
279 | |
|
280 | 0 | if (alen < blen || |
281 | 0 | memcmp(astr + (alen - blen), bstr, blen) != 0) |
282 | 0 | ret = jv_false(); |
283 | 0 | else |
284 | 0 | ret = jv_true(); |
285 | 0 | jv_free(a); |
286 | 0 | jv_free(b); |
287 | 0 | return ret; |
288 | 0 | } |
289 | | |
290 | 0 | jv binop_minus(jv a, jv b) { |
291 | 0 | if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { |
292 | 0 | jv r = jv_number(jv_number_value(a) - jv_number_value(b)); |
293 | 0 | jv_free(a); |
294 | 0 | jv_free(b); |
295 | 0 | return r; |
296 | 0 | } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { |
297 | 0 | jv out = jv_array(); |
298 | 0 | jv_array_foreach(a, i, x) { |
299 | 0 | int include = 1; |
300 | 0 | jv_array_foreach(b, j, y) { |
301 | 0 | int equal = jv_equal(jv_copy(x), y); |
302 | 0 | if (equal < 0) { |
303 | 0 | jv_free(out); |
304 | 0 | jv_free(x); |
305 | 0 | jv_free(a); |
306 | 0 | jv_free(b); |
307 | 0 | return jv_invalid_with_msg(jv_string("Equality check too deep")); |
308 | 0 | } |
309 | 0 | if (equal) { |
310 | 0 | include = 0; |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | } |
314 | 0 | if (include) |
315 | 0 | out = jv_array_append(out, jv_copy(x)); |
316 | 0 | jv_free(x); |
317 | 0 | } |
318 | 0 | jv_free(a); |
319 | 0 | jv_free(b); |
320 | 0 | return out; |
321 | 0 | } else { |
322 | 0 | return type_error2(a, b, "cannot be subtracted"); |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | 0 | jv binop_multiply(jv a, jv b) { |
327 | 0 | jv_kind ak = jv_get_kind(a); |
328 | 0 | jv_kind bk = jv_get_kind(b); |
329 | 0 | if (ak == JV_KIND_NUMBER && bk == JV_KIND_NUMBER) { |
330 | 0 | jv r = jv_number(jv_number_value(a) * jv_number_value(b)); |
331 | 0 | jv_free(a); |
332 | 0 | jv_free(b); |
333 | 0 | return r; |
334 | 0 | } else if ((ak == JV_KIND_STRING && bk == JV_KIND_NUMBER) || |
335 | 0 | (ak == JV_KIND_NUMBER && bk == JV_KIND_STRING)) { |
336 | 0 | jv str = a; |
337 | 0 | jv num = b; |
338 | 0 | if (ak == JV_KIND_NUMBER) { |
339 | 0 | str = b; |
340 | 0 | num = a; |
341 | 0 | } |
342 | 0 | double d = jv_number_value(num); |
343 | 0 | jv_free(num); |
344 | 0 | return jv_string_repeat(str, |
345 | 0 | d < 0 || isnan(d) ? -1 : d > INT_MAX ? INT_MAX : (int)d); |
346 | 0 | } else if (ak == JV_KIND_OBJECT && bk == JV_KIND_OBJECT) { |
347 | 0 | return jv_object_merge_recursive(a, b); |
348 | 0 | } else { |
349 | 0 | return type_error2(a, b, "cannot be multiplied"); |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | 0 | jv binop_divide(jv a, jv b) { |
354 | 0 | if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { |
355 | 0 | if (jv_number_value(b) == 0.0) |
356 | 0 | return type_error2(a, b, "cannot be divided because the divisor is zero"); |
357 | 0 | jv r = jv_number(jv_number_value(a) / jv_number_value(b)); |
358 | 0 | jv_free(a); |
359 | 0 | jv_free(b); |
360 | 0 | return r; |
361 | 0 | } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) { |
362 | 0 | return jv_string_split(a, b); |
363 | 0 | } else { |
364 | 0 | return type_error2(a, b, "cannot be divided"); |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | 0 | #define dtoi(n) ((n) < INTMAX_MIN ? INTMAX_MIN : -(n) <= INTMAX_MIN ? INTMAX_MAX : (intmax_t)(n)) |
369 | 0 | jv binop_mod(jv a, jv b) { |
370 | 0 | if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { |
371 | 0 | double na = jv_number_value(a); |
372 | 0 | double nb = jv_number_value(b); |
373 | 0 | if (isnan(na) || isnan(nb)) { |
374 | 0 | jv_free(a); |
375 | 0 | jv_free(b); |
376 | 0 | return jv_number(NAN); |
377 | 0 | } |
378 | 0 | intmax_t bi = dtoi(nb); |
379 | 0 | if (bi == 0) |
380 | 0 | return type_error2(a, b, "cannot be divided (remainder) because the divisor is zero"); |
381 | | // Check if the divisor is -1 to avoid overflow when the dividend is INTMAX_MIN. |
382 | 0 | jv r = jv_number(bi == -1 ? 0 : dtoi(na) % bi); |
383 | 0 | jv_free(a); |
384 | 0 | jv_free(b); |
385 | 0 | return r; |
386 | 0 | } else { |
387 | 0 | return type_error2(a, b, "cannot be divided (remainder)"); |
388 | 0 | } |
389 | 0 | } |
390 | | #undef dtoi |
391 | | |
392 | 0 | jv binop_equal(jv a, jv b) { |
393 | 0 | int r = jv_equal(a, b); |
394 | 0 | if (r < 0) |
395 | 0 | return jv_invalid_with_msg(jv_string("Equality check too deep")); |
396 | 0 | return jv_bool(r); |
397 | 0 | } |
398 | | |
399 | 0 | jv binop_notequal(jv a, jv b) { |
400 | 0 | int r = jv_equal(a, b); |
401 | 0 | if (r < 0) |
402 | 0 | return jv_invalid_with_msg(jv_string("Equality check too deep")); |
403 | 0 | return jv_bool(!r); |
404 | 0 | } |
405 | | |
406 | | enum cmp_op { |
407 | | CMP_OP_LESS, |
408 | | CMP_OP_GREATER, |
409 | | CMP_OP_LESSEQ, |
410 | | CMP_OP_GREATEREQ |
411 | | }; |
412 | | |
413 | 0 | static jv order_cmp(jv a, jv b, enum cmp_op op) { |
414 | 0 | int r = jv_cmp(a, b); |
415 | 0 | if (r == INT_MIN) |
416 | 0 | return jv_invalid_with_msg(jv_string("Comparison too deep")); |
417 | 0 | return jv_bool((op == CMP_OP_LESS && r < 0) || |
418 | 0 | (op == CMP_OP_LESSEQ && r <= 0) || |
419 | 0 | (op == CMP_OP_GREATEREQ && r >= 0) || |
420 | 0 | (op == CMP_OP_GREATER && r > 0)); |
421 | 0 | } |
422 | | |
423 | 0 | jv binop_less(jv a, jv b) { |
424 | 0 | return order_cmp(a, b, CMP_OP_LESS); |
425 | 0 | } |
426 | | |
427 | 0 | jv binop_greater(jv a, jv b) { |
428 | 0 | return order_cmp(a, b, CMP_OP_GREATER); |
429 | 0 | } |
430 | | |
431 | 0 | jv binop_lesseq(jv a, jv b) { |
432 | 0 | return order_cmp(a, b, CMP_OP_LESSEQ); |
433 | 0 | } |
434 | | |
435 | 0 | jv binop_greatereq(jv a, jv b) { |
436 | 0 | return order_cmp(a, b, CMP_OP_GREATEREQ); |
437 | 0 | } |
438 | | |
439 | 0 | static jv f_contains(jq_state *jq, jv a, jv b) { |
440 | 0 | if (jv_get_kind(a) == jv_get_kind(b)) { |
441 | 0 | int r = jv_contains(a, b); |
442 | 0 | if (r < 0) |
443 | 0 | return jv_invalid_with_msg(jv_string("Containment check too deep")); |
444 | 0 | return jv_bool(r); |
445 | 0 | } else { |
446 | 0 | return type_error2(a, b, "cannot have their containment checked"); |
447 | 0 | } |
448 | 0 | } |
449 | | |
450 | 0 | static jv f_dump(jq_state *jq, jv input) { |
451 | 0 | return jv_dump_string(input, 0); |
452 | 0 | } |
453 | | |
454 | 0 | static jv f_json_parse(jq_state *jq, jv input) { |
455 | 0 | if (jv_get_kind(input) != JV_KIND_STRING) |
456 | 0 | return type_error(input, "only strings can be parsed"); |
457 | 0 | jv res = jv_parse_sized(jv_string_value(input), |
458 | 0 | jv_string_length_bytes(jv_copy(input))); |
459 | 0 | jv_free(input); |
460 | 0 | return res; |
461 | 0 | } |
462 | | |
463 | 0 | static jv f_tonumber(jq_state *jq, jv input) { |
464 | 0 | if (jv_get_kind(input) == JV_KIND_NUMBER) { |
465 | 0 | return input; |
466 | 0 | } |
467 | 0 | if (jv_get_kind(input) == JV_KIND_STRING) { |
468 | 0 | const char* s = jv_string_value(input); |
469 | 0 | int len = jv_string_length_bytes(jv_copy(input)); |
470 | 0 | if ((size_t)len != strlen(s)) { |
471 | 0 | return type_error(input, "cannot be parsed as a number"); |
472 | 0 | } |
473 | 0 | #ifdef USE_DECNUM |
474 | 0 | jv number = jv_number_with_literal(s); |
475 | 0 | if (jv_get_kind(number) == JV_KIND_INVALID) { |
476 | 0 | return type_error(input, "cannot be parsed as a number"); |
477 | 0 | } |
478 | | #else |
479 | | char *end = 0; |
480 | | double d = jvp_strtod(tsd_dtoa_context_get(), s, &end); |
481 | | if (end == 0 || *end != 0) { |
482 | | return type_error(input, "cannot be parsed as a number"); |
483 | | } |
484 | | jv number = jv_number(d); |
485 | | #endif |
486 | 0 | jv_free(input); |
487 | 0 | return number; |
488 | 0 | } |
489 | 0 | return type_error(input, "cannot be parsed as a number"); |
490 | 0 | } |
491 | | |
492 | 0 | static jv f_toboolean(jq_state *jq, jv input) { |
493 | 0 | if (jv_get_kind(input) == JV_KIND_TRUE || jv_get_kind(input) == JV_KIND_FALSE) { |
494 | 0 | return input; |
495 | 0 | } |
496 | 0 | if (jv_get_kind(input) == JV_KIND_STRING) { |
497 | 0 | const char *s = jv_string_value(input); |
498 | 0 | int len = jv_string_length_bytes(jv_copy(input)); |
499 | 0 | if ((size_t)len != strlen(s)) { |
500 | 0 | return type_error(input, "cannot be parsed as a boolean"); |
501 | 0 | } |
502 | 0 | if (strcmp(s, "true") == 0) { |
503 | 0 | jv_free(input); |
504 | 0 | return jv_true(); |
505 | 0 | } else if (strcmp(s, "false") == 0) { |
506 | 0 | jv_free(input); |
507 | 0 | return jv_false(); |
508 | 0 | } |
509 | 0 | } |
510 | 0 | return type_error(input, "cannot be parsed as a boolean"); |
511 | 0 | } |
512 | | |
513 | 0 | static jv f_length(jq_state *jq, jv input) { |
514 | 0 | if (jv_get_kind(input) == JV_KIND_ARRAY) { |
515 | 0 | return jv_number(jv_array_length(input)); |
516 | 0 | } else if (jv_get_kind(input) == JV_KIND_OBJECT) { |
517 | 0 | return jv_number(jv_object_length(input)); |
518 | 0 | } else if (jv_get_kind(input) == JV_KIND_STRING) { |
519 | 0 | return jv_number(jv_string_length_codepoints(input)); |
520 | 0 | } else if (jv_get_kind(input) == JV_KIND_NUMBER) { |
521 | 0 | jv r = jv_number_abs(input); |
522 | 0 | jv_free(input); |
523 | 0 | return r; |
524 | 0 | } else if (jv_get_kind(input) == JV_KIND_NULL) { |
525 | 0 | jv_free(input); |
526 | 0 | return jv_number(0); |
527 | 0 | } else { |
528 | 0 | return type_error(input, "has no length"); |
529 | 0 | } |
530 | 0 | } |
531 | | |
532 | 0 | static jv f_tostring(jq_state *jq, jv input) { |
533 | 0 | if (jv_get_kind(input) == JV_KIND_STRING) { |
534 | 0 | return input; |
535 | 0 | } else { |
536 | 0 | return jv_dump_string(input, 0); |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | 0 | static jv f_utf8bytelength(jq_state *jq, jv input) { |
541 | 0 | if (jv_get_kind(input) != JV_KIND_STRING) |
542 | 0 | return type_error(input, "only strings have UTF-8 byte length"); |
543 | 0 | return jv_number(jv_string_length_bytes(input)); |
544 | 0 | } |
545 | | |
546 | | static const unsigned char URI_UNRESERVED[128] = { |
547 | | // 1 2 3 4 5 6 7 8 9 A B C D E F |
548 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 |
549 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 |
550 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 0x20: - . |
551 | | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 0x30: 0-9 |
552 | | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40: A-O |
553 | | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50: P-Z _ |
554 | | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60: a-o |
555 | | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 0x70: p-z ~ |
556 | | }; |
557 | | |
558 | | #define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" |
559 | | |
560 | | static const unsigned char BASE64_ENCODE_TABLE[64 + 1] = CHARS_ALPHANUM "+/"; |
561 | | static const unsigned char BASE64_INVALID_ENTRY = 0xFF; |
562 | | static const unsigned char BASE64_DECODE_TABLE[256] = { |
563 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
564 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
565 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
566 | | 62, // + |
567 | | 0xFF, 0xFF, 0xFF, |
568 | | 63, // / |
569 | | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9 |
570 | | 0xFF, 0xFF, 0xFF, |
571 | | 99, // = |
572 | | 0xFF, 0xFF, 0xFF, |
573 | | 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 |
574 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
575 | | 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 |
576 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
577 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
578 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
579 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
580 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
581 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
582 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
583 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
584 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
585 | | }; |
586 | | |
587 | | |
588 | 0 | static jv escape_string(jv input, const char* escapings) { |
589 | |
|
590 | 0 | assert(jv_get_kind(input) == JV_KIND_STRING); |
591 | 0 | const char* lookup[128] = {0}; |
592 | 0 | const char* p = escapings; |
593 | 0 | lookup[0] = "\\0"; |
594 | 0 | while (*p) { |
595 | 0 | lookup[(int)*p] = p+1; |
596 | 0 | p++; |
597 | 0 | p += strlen(p); |
598 | 0 | p++; |
599 | 0 | } |
600 | |
|
601 | 0 | jv ret = jv_string(""); |
602 | 0 | const char* i = jv_string_value(input); |
603 | 0 | const char* end = i + jv_string_length_bytes(jv_copy(input)); |
604 | 0 | const char* cstart; |
605 | 0 | int c = 0; |
606 | 0 | while ((i = jvp_utf8_next((cstart = i), end, &c))) { |
607 | 0 | if (c < 128 && lookup[c]) { |
608 | 0 | ret = jv_string_append_str(ret, lookup[c]); |
609 | 0 | } else { |
610 | 0 | ret = jv_string_append_buf(ret, cstart, i - cstart); |
611 | 0 | } |
612 | 0 | } |
613 | 0 | jv_free(input); |
614 | 0 | return ret; |
615 | |
|
616 | 0 | } |
617 | | |
618 | 0 | static jv f_format(jq_state *jq, jv input, jv fmt) { |
619 | 0 | if (jv_get_kind(fmt) != JV_KIND_STRING) { |
620 | 0 | jv_free(input); |
621 | 0 | return type_error(fmt, "is not a valid format"); |
622 | 0 | } |
623 | 0 | const char* fmt_s = jv_string_value(fmt); |
624 | 0 | if (!strcmp(fmt_s, "json")) { |
625 | 0 | jv_free(fmt); |
626 | 0 | return jv_dump_string(input, 0); |
627 | 0 | } else if (!strcmp(fmt_s, "text")) { |
628 | 0 | jv_free(fmt); |
629 | 0 | return f_tostring(jq, input); |
630 | 0 | } else if (!strcmp(fmt_s, "csv") || !strcmp(fmt_s, "tsv")) { |
631 | 0 | const char *quotes, *sep, *escapings; |
632 | 0 | const char *msg; |
633 | 0 | if (!strcmp(fmt_s, "csv")) { |
634 | 0 | msg = "cannot be csv-formatted, only array"; |
635 | 0 | quotes = "\""; |
636 | 0 | sep = ","; |
637 | 0 | escapings = "\"\"\"\0"; |
638 | 0 | } else { |
639 | 0 | msg = "cannot be tsv-formatted, only array"; |
640 | 0 | assert(!strcmp(fmt_s, "tsv")); |
641 | 0 | quotes = ""; |
642 | 0 | sep = "\t"; |
643 | 0 | escapings = "\t\\t\0\r\\r\0\n\\n\0\\\\\\\0"; |
644 | 0 | } |
645 | 0 | jv_free(fmt); |
646 | 0 | if (jv_get_kind(input) != JV_KIND_ARRAY) |
647 | 0 | return type_error(input, msg); |
648 | 0 | jv line = jv_string(""); |
649 | 0 | jv_array_foreach(input, i, x) { |
650 | 0 | if (i) line = jv_string_append_str(line, sep); |
651 | 0 | switch (jv_get_kind(x)) { |
652 | 0 | case JV_KIND_NULL: |
653 | | /* null rendered as empty string */ |
654 | 0 | jv_free(x); |
655 | 0 | break; |
656 | 0 | case JV_KIND_TRUE: |
657 | 0 | case JV_KIND_FALSE: |
658 | 0 | line = jv_string_concat(line, jv_dump_string(x, 0)); |
659 | 0 | break; |
660 | 0 | case JV_KIND_NUMBER: |
661 | 0 | if (jv_number_value(x) != jv_number_value(x)) { |
662 | | /* NaN, render as empty string */ |
663 | 0 | jv_free(x); |
664 | 0 | } else { |
665 | 0 | line = jv_string_concat(line, jv_dump_string(x, 0)); |
666 | 0 | } |
667 | 0 | break; |
668 | 0 | case JV_KIND_STRING: { |
669 | 0 | line = jv_string_append_str(line, quotes); |
670 | 0 | line = jv_string_concat(line, escape_string(x, escapings)); |
671 | 0 | line = jv_string_append_str(line, quotes); |
672 | 0 | break; |
673 | 0 | } |
674 | 0 | default: |
675 | 0 | jv_free(input); |
676 | 0 | jv_free(line); |
677 | 0 | return type_error(x, "is not valid in a csv row"); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | jv_free(input); |
681 | 0 | return line; |
682 | 0 | } else if (!strcmp(fmt_s, "html")) { |
683 | 0 | jv_free(fmt); |
684 | 0 | return escape_string(f_tostring(jq, input), "&&\0<<\0>>\0''\0\""\0"); |
685 | 0 | } else if (!strcmp(fmt_s, "uri")) { |
686 | 0 | jv_free(fmt); |
687 | 0 | input = f_tostring(jq, input); |
688 | 0 | const char* s = jv_string_value(input); |
689 | 0 | int len = jv_string_length_bytes(jv_copy(input)); |
690 | 0 | char *result = jv_mem_alloc((size_t)len * 3 + 1); |
691 | 0 | uint32_t ri = 0; |
692 | 0 | for (int i = 0; i < len; i++) { |
693 | 0 | unsigned c = (unsigned)(unsigned char)s[i]; |
694 | 0 | if (c < 128 && URI_UNRESERVED[c]) { |
695 | 0 | result[ri++] = (char)c; |
696 | 0 | } else { |
697 | 0 | result[ri++] = '%'; |
698 | 0 | result[ri++] = "0123456789ABCDEF"[c >> 4]; |
699 | 0 | result[ri++] = "0123456789ABCDEF"[c & 0x0F]; |
700 | 0 | } |
701 | 0 | } |
702 | 0 | jv line = jv_string_sized(result, ri); |
703 | 0 | free(result); |
704 | 0 | jv_free(input); |
705 | 0 | return line; |
706 | 0 | } else if (!strcmp(fmt_s, "urid")) { |
707 | 0 | jv_free(fmt); |
708 | 0 | input = f_tostring(jq, input); |
709 | 0 | const char *errmsg = "is not a valid uri encoding"; |
710 | 0 | const char *s = jv_string_value(input); |
711 | 0 | int len = jv_string_length_bytes(jv_copy(input)); |
712 | 0 | char *result = jv_mem_alloc((size_t)len + 1); |
713 | 0 | uint32_t ri = 0; |
714 | 0 | for (int i = 0; i < len; i++) { |
715 | 0 | char c = s[i]; |
716 | 0 | if (c != '%') { |
717 | 0 | result[ri++] = c; |
718 | 0 | } else { |
719 | 0 | int hi = 0, lo = 0; |
720 | 0 | for (int j = 0; j < 2; j++) { |
721 | 0 | if (++i >= len) { |
722 | 0 | free(result); |
723 | 0 | return type_error(input, errmsg); |
724 | 0 | } |
725 | 0 | int *d = j == 0 ? &hi : &lo; |
726 | 0 | c = s[i]; |
727 | 0 | if ('0' <= c && c <= '9') *d = c - '0'; |
728 | 0 | else if ('a' <= c && c <= 'f') *d = c - 'a' + 10; |
729 | 0 | else if ('A' <= c && c <= 'F') *d = c - 'A' + 10; |
730 | 0 | else { |
731 | 0 | free(result); |
732 | 0 | return type_error(input, errmsg); |
733 | 0 | } |
734 | 0 | } |
735 | 0 | result[ri++] = (hi << 4) | lo; |
736 | 0 | } |
737 | 0 | } |
738 | 0 | if (!jvp_utf8_is_valid(result, result + ri)) { |
739 | 0 | free(result); |
740 | 0 | return type_error(input, errmsg); |
741 | 0 | } |
742 | 0 | jv line = jv_string_sized(result, ri); |
743 | 0 | free(result); |
744 | 0 | jv_free(input); |
745 | 0 | return line; |
746 | 0 | } else if (!strcmp(fmt_s, "sh")) { |
747 | 0 | jv_free(fmt); |
748 | 0 | if (jv_get_kind(input) != JV_KIND_ARRAY) |
749 | 0 | input = jv_array_set(jv_array(), 0, input); |
750 | 0 | jv line = jv_string(""); |
751 | 0 | jv_array_foreach(input, i, x) { |
752 | 0 | if (i) line = jv_string_append_str(line, " "); |
753 | 0 | switch (jv_get_kind(x)) { |
754 | 0 | case JV_KIND_NULL: |
755 | 0 | case JV_KIND_TRUE: |
756 | 0 | case JV_KIND_FALSE: |
757 | 0 | case JV_KIND_NUMBER: |
758 | 0 | line = jv_string_concat(line, jv_dump_string(x, 0)); |
759 | 0 | break; |
760 | | |
761 | 0 | case JV_KIND_STRING: { |
762 | 0 | line = jv_string_append_str(line, "'"); |
763 | 0 | line = jv_string_concat(line, escape_string(x, "''\\''\0")); |
764 | 0 | line = jv_string_append_str(line, "'"); |
765 | 0 | break; |
766 | 0 | } |
767 | | |
768 | 0 | default: |
769 | 0 | jv_free(input); |
770 | 0 | jv_free(line); |
771 | 0 | return type_error(x, "can not be escaped for shell"); |
772 | 0 | } |
773 | 0 | } |
774 | 0 | jv_free(input); |
775 | 0 | return line; |
776 | 0 | } else if (!strcmp(fmt_s, "base64")) { |
777 | 0 | jv_free(fmt); |
778 | 0 | input = f_tostring(jq, input); |
779 | 0 | jv line = jv_string(""); |
780 | 0 | const unsigned char* data = (const unsigned char*)jv_string_value(input); |
781 | 0 | int len = jv_string_length_bytes(jv_copy(input)); |
782 | 0 | for (int i=0; i<len; i+=3) { |
783 | 0 | uint32_t code = 0; |
784 | 0 | int n = len - i >= 3 ? 3 : len-i; |
785 | 0 | for (int j=0; j<3; j++) { |
786 | 0 | code <<= 8; |
787 | 0 | code |= j < n ? (unsigned)data[i+j] : 0; |
788 | 0 | } |
789 | 0 | char buf[4]; |
790 | 0 | for (int j=0; j<4; j++) { |
791 | 0 | buf[j] = BASE64_ENCODE_TABLE[(code >> (18 - j*6)) & 0x3f]; |
792 | 0 | } |
793 | 0 | if (n < 3) buf[3] = '='; |
794 | 0 | if (n < 2) buf[2] = '='; |
795 | 0 | line = jv_string_append_buf(line, buf, sizeof(buf)); |
796 | 0 | } |
797 | 0 | jv_free(input); |
798 | 0 | return line; |
799 | 0 | } else if (!strcmp(fmt_s, "base64d")) { |
800 | 0 | jv_free(fmt); |
801 | 0 | input = f_tostring(jq, input); |
802 | 0 | const unsigned char* data = (const unsigned char*)jv_string_value(input); |
803 | 0 | int len = jv_string_length_bytes(jv_copy(input)); |
804 | 0 | size_t decoded_len = MAX((3 * (size_t)len) / 4, (size_t)1); // 3 usable bytes for every 4 bytes of input |
805 | 0 | char *result = jv_mem_calloc(decoded_len, sizeof(char)); |
806 | 0 | uint32_t ri = 0; |
807 | 0 | int input_bytes_read=0; |
808 | 0 | uint32_t code = 0; |
809 | 0 | for (int i=0; i<len && data[i] != '='; i++) { |
810 | 0 | if (BASE64_DECODE_TABLE[data[i]] == BASE64_INVALID_ENTRY) { |
811 | 0 | free(result); |
812 | 0 | return type_error(input, "is not valid base64 data"); |
813 | 0 | } |
814 | | |
815 | 0 | code <<= 6; |
816 | 0 | code |= BASE64_DECODE_TABLE[data[i]]; |
817 | 0 | input_bytes_read++; |
818 | |
|
819 | 0 | if (input_bytes_read == 4) { |
820 | 0 | result[ri++] = (code >> 16) & 0xFF; |
821 | 0 | result[ri++] = (code >> 8) & 0xFF; |
822 | 0 | result[ri++] = code & 0xFF; |
823 | 0 | input_bytes_read = 0; |
824 | 0 | code = 0; |
825 | 0 | } |
826 | 0 | } |
827 | 0 | if (input_bytes_read == 3) { |
828 | 0 | result[ri++] = (code >> 10) & 0xFF; |
829 | 0 | result[ri++] = (code >> 2) & 0xFF; |
830 | 0 | } else if (input_bytes_read == 2) { |
831 | 0 | result[ri++] = (code >> 4) & 0xFF; |
832 | 0 | } else if (input_bytes_read == 1) { |
833 | 0 | free(result); |
834 | 0 | return type_error(input, "trailing base64 byte found"); |
835 | 0 | } |
836 | | |
837 | 0 | jv line = jv_string_sized(result, ri); |
838 | 0 | jv_free(input); |
839 | 0 | free(result); |
840 | 0 | return line; |
841 | 0 | } else { |
842 | 0 | jv_free(input); |
843 | 0 | return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format"))); |
844 | 0 | } |
845 | 0 | } |
846 | | |
847 | 0 | static jv f_keys(jq_state *jq, jv input) { |
848 | 0 | if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) { |
849 | 0 | return jv_keys(input); |
850 | 0 | } else { |
851 | 0 | return type_error(input, "has no keys"); |
852 | 0 | } |
853 | 0 | } |
854 | | |
855 | 0 | static jv f_keys_unsorted(jq_state *jq, jv input) { |
856 | 0 | if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) { |
857 | 0 | return jv_keys_unsorted(input); |
858 | 0 | } else { |
859 | 0 | return type_error(input, "has no keys"); |
860 | 0 | } |
861 | 0 | } |
862 | | |
863 | 0 | static jv f_sort(jq_state *jq, jv input){ |
864 | 0 | if (jv_get_kind(input) == JV_KIND_ARRAY) { |
865 | 0 | return jv_sort(input, jv_copy(input)); |
866 | 0 | } else { |
867 | 0 | return type_error(input, "cannot be sorted, as it is not an array"); |
868 | 0 | } |
869 | 0 | } |
870 | | |
871 | 0 | static jv f_sort_by_impl(jq_state *jq, jv input, jv keys) { |
872 | 0 | if (jv_get_kind(input) == JV_KIND_ARRAY && |
873 | 0 | jv_get_kind(keys) == JV_KIND_ARRAY && |
874 | 0 | jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) { |
875 | 0 | return jv_sort(input, keys); |
876 | 0 | } else { |
877 | 0 | return type_error2(input, keys, "cannot be sorted, as they are not both arrays"); |
878 | 0 | } |
879 | 0 | } |
880 | | |
881 | | /* |
882 | | * Assuming the input array is sorted, bsearch/1 returns |
883 | | * the index of the target if the target is in the input array; and otherwise |
884 | | * (-1 - ix), where ix is the insertion point that would leave the array sorted. |
885 | | * If the input is not sorted, bsearch will terminate but with irrelevant results. |
886 | | */ |
887 | 0 | static jv f_bsearch(jq_state *jq, jv input, jv target) { |
888 | 0 | if (jv_get_kind(input) != JV_KIND_ARRAY) { |
889 | 0 | jv_free(target); |
890 | 0 | return type_error(input, "cannot be searched from"); |
891 | 0 | } |
892 | 0 | int start = 0; |
893 | 0 | int end = jv_array_length(jv_copy(input)); |
894 | 0 | jv answer = jv_invalid(); |
895 | 0 | while (start < end) { |
896 | 0 | int mid = start + (end - start) / 2; |
897 | 0 | int result = jv_cmp(jv_copy(target), jv_array_get(jv_copy(input), mid)); |
898 | 0 | if (result == INT_MIN) { |
899 | 0 | jv_free(answer); |
900 | 0 | jv_free(input); |
901 | 0 | jv_free(target); |
902 | 0 | return jv_invalid_with_msg(jv_string("Comparison too deep")); |
903 | 0 | } |
904 | 0 | if (result == 0) { |
905 | 0 | answer = jv_number(mid); |
906 | 0 | break; |
907 | 0 | } else if (result < 0) { |
908 | 0 | end = mid; |
909 | 0 | } else { |
910 | 0 | start = mid + 1; |
911 | 0 | } |
912 | 0 | } |
913 | 0 | if (!jv_is_valid(answer)) { |
914 | 0 | answer = jv_number(-1 - start); |
915 | 0 | } |
916 | 0 | jv_free(input); |
917 | 0 | jv_free(target); |
918 | 0 | return answer; |
919 | 0 | } |
920 | | |
921 | 0 | static jv f_group_by_impl(jq_state *jq, jv input, jv keys) { |
922 | 0 | if (jv_get_kind(input) == JV_KIND_ARRAY && |
923 | 0 | jv_get_kind(keys) == JV_KIND_ARRAY && |
924 | 0 | jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) { |
925 | 0 | return jv_group(input, keys); |
926 | 0 | } else { |
927 | 0 | return type_error2(input, keys, "cannot be sorted, as they are not both arrays"); |
928 | 0 | } |
929 | 0 | } |
930 | | |
931 | 0 | static jv f_unique(jq_state *jq, jv input) { |
932 | 0 | if (jv_get_kind(input) == JV_KIND_ARRAY) { |
933 | 0 | return jv_unique(input, jv_copy(input)); |
934 | 0 | } else { |
935 | 0 | return type_error(input, "cannot be sorted, as it is not an array"); |
936 | 0 | } |
937 | 0 | } |
938 | | |
939 | 0 | static jv f_unique_by_impl(jq_state *jq, jv input, jv keys) { |
940 | 0 | if (jv_get_kind(input) == JV_KIND_ARRAY && |
941 | 0 | jv_get_kind(keys) == JV_KIND_ARRAY && |
942 | 0 | jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) { |
943 | 0 | return jv_unique(input, keys); |
944 | 0 | } else { |
945 | 0 | return type_error2(input, keys, "cannot be sorted, as they are not both arrays"); |
946 | 0 | } |
947 | 0 | } |
948 | | |
949 | | #ifdef HAVE_LIBONIG |
950 | | static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups, |
951 | 0 | int *groups, regex_t *reg, void *arg) { |
952 | 0 | jv captures = *(jv*)arg; |
953 | 0 | for (int i = 0; i < ngroups; ++i) { |
954 | 0 | jv cap = jv_array_get(jv_copy(captures),groups[i]-1); |
955 | 0 | if (jv_get_kind(cap) == JV_KIND_OBJECT) { |
956 | 0 | cap = jv_object_set(cap, jv_string("name"), jv_string_sized((const char*)name, name_end-name)); |
957 | 0 | captures = jv_array_set(captures,groups[i]-1,cap); |
958 | 0 | } else { |
959 | 0 | jv_free(cap); |
960 | 0 | } |
961 | 0 | } |
962 | 0 | *(jv *)arg = captures; |
963 | 0 | return 0; |
964 | 0 | } |
965 | | |
966 | 0 | static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) { |
967 | 0 | int test = jv_equal(testmode, jv_true()); |
968 | 0 | jv result; |
969 | 0 | int onigret; |
970 | 0 | int global = 0; |
971 | 0 | regex_t *reg; |
972 | 0 | OnigErrorInfo einfo; |
973 | 0 | OnigRegion* region; |
974 | |
|
975 | 0 | if (jv_get_kind(input) != JV_KIND_STRING) { |
976 | 0 | jv_free(regex); |
977 | 0 | jv_free(modifiers); |
978 | 0 | return type_error(input, "cannot be matched, as it is not a string"); |
979 | 0 | } |
980 | | |
981 | 0 | if (jv_get_kind(regex) != JV_KIND_STRING) { |
982 | 0 | jv_free(input); |
983 | 0 | jv_free(modifiers); |
984 | 0 | return type_error(regex, "is not a string"); |
985 | 0 | } |
986 | | |
987 | 0 | OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP; |
988 | |
|
989 | 0 | if (jv_get_kind(modifiers) == JV_KIND_STRING) { |
990 | 0 | jv modarray = jv_string_explode(jv_copy(modifiers)); |
991 | 0 | jv_array_foreach(modarray, i, mod) { |
992 | 0 | switch ((int)jv_number_value(mod)) { |
993 | 0 | case 'g': |
994 | 0 | global = 1; |
995 | 0 | break; |
996 | 0 | case 'i': |
997 | 0 | options |= ONIG_OPTION_IGNORECASE; |
998 | 0 | break; |
999 | 0 | case 'x': |
1000 | 0 | options |= ONIG_OPTION_EXTEND; |
1001 | 0 | break; |
1002 | 0 | case 'm': |
1003 | 0 | options |= ONIG_OPTION_MULTILINE; |
1004 | 0 | break; |
1005 | 0 | case 's': |
1006 | 0 | options |= ONIG_OPTION_SINGLELINE; |
1007 | 0 | break; |
1008 | 0 | case 'p': |
1009 | 0 | options |= ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE; |
1010 | 0 | break; |
1011 | 0 | case 'l': |
1012 | 0 | options |= ONIG_OPTION_FIND_LONGEST; |
1013 | 0 | break; |
1014 | 0 | case 'n': |
1015 | 0 | options |= ONIG_OPTION_FIND_NOT_EMPTY; |
1016 | 0 | break; |
1017 | 0 | default: |
1018 | 0 | jv_free(input); |
1019 | 0 | jv_free(regex); |
1020 | 0 | jv_free(modarray); |
1021 | 0 | return jv_invalid_with_msg(jv_string_concat(modifiers, |
1022 | 0 | jv_string(" is not a valid modifier string"))); |
1023 | 0 | } |
1024 | 0 | } |
1025 | 0 | jv_free(modarray); |
1026 | 0 | } else if (jv_get_kind(modifiers) != JV_KIND_NULL) { |
1027 | | // If it isn't a string or null, then it is the wrong type... |
1028 | 0 | jv_free(input); |
1029 | 0 | jv_free(regex); |
1030 | 0 | return type_error(modifiers, "is not a string"); |
1031 | 0 | } |
1032 | | |
1033 | 0 | jv_free(modifiers); |
1034 | |
|
1035 | 0 | onigret = onig_new(®, (const UChar*)jv_string_value(regex), |
1036 | 0 | (const UChar*)(jv_string_value(regex) + jv_string_length_bytes(jv_copy(regex))), |
1037 | 0 | options, ONIG_ENCODING_UTF8, ONIG_SYNTAX_PERL_NG, &einfo); |
1038 | 0 | if (onigret != ONIG_NORMAL) { |
1039 | 0 | UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN]; |
1040 | 0 | onig_error_code_to_str(ebuf, onigret, &einfo); |
1041 | 0 | jv_free(input); |
1042 | 0 | jv_free(regex); |
1043 | 0 | return jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "), |
1044 | 0 | jv_string((char*)ebuf))); |
1045 | 0 | } |
1046 | 0 | result = test ? jv_false() : jv_array(); |
1047 | 0 | const char *input_string = jv_string_value(input); |
1048 | 0 | const UChar* start = (const UChar*)jv_string_value(input); |
1049 | 0 | const unsigned long length = jv_string_length_bytes(jv_copy(input)); |
1050 | 0 | const UChar* end = start + length; |
1051 | 0 | region = onig_region_new(); |
1052 | 0 | do { |
1053 | 0 | onigret = onig_search(reg, |
1054 | 0 | (const UChar*)jv_string_value(input), end, /* string boundaries */ |
1055 | 0 | start, end, /* search boundaries */ |
1056 | 0 | region, ONIG_OPTION_NONE); |
1057 | 0 | if (onigret >= 0) { |
1058 | 0 | if (test) { |
1059 | 0 | result = jv_true(); |
1060 | 0 | break; |
1061 | 0 | } |
1062 | | |
1063 | | // Zero-width match |
1064 | 0 | if (region->end[0] == region->beg[0]) { |
1065 | 0 | unsigned long idx; |
1066 | 0 | const char *fr = (const char*)input_string; |
1067 | 0 | for (idx = 0; fr < input_string+region->beg[0]; idx++) { |
1068 | 0 | fr += jvp_utf8_decode_length(*fr); |
1069 | 0 | } |
1070 | 0 | jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); |
1071 | 0 | match = jv_object_set(match, jv_string("length"), jv_number(0)); |
1072 | 0 | match = jv_object_set(match, jv_string("string"), jv_string("")); |
1073 | 0 | jv captures = jv_array(); |
1074 | 0 | for (int i = 1; i < region->num_regs; ++i) { |
1075 | 0 | jv cap = jv_object(); |
1076 | 0 | if (region->beg[i] == -1) { |
1077 | 0 | cap = jv_object_set(cap, jv_string("offset"), jv_number(-1)); |
1078 | 0 | cap = jv_object_set(cap, jv_string("string"), jv_null()); |
1079 | 0 | } else { |
1080 | 0 | cap = jv_object_set(cap, jv_string("offset"), jv_number(idx)); |
1081 | 0 | cap = jv_object_set(cap, jv_string("string"), jv_string("")); |
1082 | 0 | } |
1083 | 0 | cap = jv_object_set(cap, jv_string("length"), jv_number(0)); |
1084 | 0 | cap = jv_object_set(cap, jv_string("name"), jv_null()); |
1085 | 0 | captures = jv_array_append(captures, cap); |
1086 | 0 | } |
1087 | 0 | onig_foreach_name(reg, f_match_name_iter, &captures); |
1088 | 0 | match = jv_object_set(match, jv_string("captures"), captures); |
1089 | 0 | result = jv_array_append(result, match); |
1090 | | // ensure '"qux" | match("(?=u)"; "g")' matches just once; advance the |
1091 | | // search start past the whole codepoint, not a single byte, so we don't |
1092 | | // land in the middle of a multibyte character (which would yield a |
1093 | | // spurious duplicate match at the same offset). |
1094 | 0 | start = (const UChar*)(input_string + region->end[0] + |
1095 | 0 | ((size_t)region->end[0] < length |
1096 | 0 | ? jvp_utf8_decode_length(input_string[region->end[0]]) |
1097 | 0 | : 1)); |
1098 | 0 | continue; |
1099 | 0 | } |
1100 | | |
1101 | 0 | unsigned long idx; |
1102 | 0 | unsigned long len; |
1103 | 0 | const char *fr = (const char*)input_string; |
1104 | |
|
1105 | 0 | for (idx = len = 0; fr < input_string+region->end[0]; len++) { |
1106 | 0 | if (fr == input_string+region->beg[0]) idx = len, len=0; |
1107 | 0 | fr += jvp_utf8_decode_length(*fr); |
1108 | 0 | } |
1109 | |
|
1110 | 0 | jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); |
1111 | |
|
1112 | 0 | unsigned long blen = region->end[0]-region->beg[0]; |
1113 | 0 | match = jv_object_set(match, jv_string("length"), jv_number(len)); |
1114 | 0 | match = jv_object_set(match, jv_string("string"), jv_string_sized(input_string+region->beg[0],blen)); |
1115 | 0 | jv captures = jv_array(); |
1116 | 0 | for (int i = 1; i < region->num_regs; ++i) { |
1117 | | // Empty capture. |
1118 | 0 | if (region->beg[i] == region->end[i]) { |
1119 | | // Didn't match. |
1120 | 0 | jv cap; |
1121 | 0 | if (region->beg[i] == -1) { |
1122 | 0 | cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1)); |
1123 | 0 | cap = jv_object_set(cap, jv_string("string"), jv_null()); |
1124 | 0 | } else { |
1125 | 0 | fr = input_string; |
1126 | 0 | for (idx = 0; fr < input_string+region->beg[i]; idx++) { |
1127 | 0 | fr += jvp_utf8_decode_length(*fr); |
1128 | 0 | } |
1129 | 0 | cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); |
1130 | 0 | cap = jv_object_set(cap, jv_string("string"), jv_string("")); |
1131 | 0 | } |
1132 | 0 | cap = jv_object_set(cap, jv_string("length"), jv_number(0)); |
1133 | 0 | cap = jv_object_set(cap, jv_string("name"), jv_null()); |
1134 | 0 | captures = jv_array_append(captures, cap); |
1135 | 0 | continue; |
1136 | 0 | } |
1137 | 0 | fr = input_string; |
1138 | 0 | for (idx = len = 0; fr < input_string+region->end[i]; len++) { |
1139 | 0 | if (fr == input_string+region->beg[i]) idx = len, len=0; |
1140 | 0 | fr += jvp_utf8_decode_length(*fr); |
1141 | 0 | } |
1142 | |
|
1143 | 0 | blen = region->end[i]-region->beg[i]; |
1144 | 0 | jv cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); |
1145 | 0 | cap = jv_object_set(cap, jv_string("length"), jv_number(len)); |
1146 | 0 | cap = jv_object_set(cap, jv_string("string"), jv_string_sized(input_string+region->beg[i],blen)); |
1147 | 0 | cap = jv_object_set(cap, jv_string("name"), jv_null()); |
1148 | 0 | captures = jv_array_append(captures,cap); |
1149 | 0 | } |
1150 | 0 | onig_foreach_name(reg,f_match_name_iter,&captures); |
1151 | 0 | match = jv_object_set(match, jv_string("captures"), captures); |
1152 | 0 | result = jv_array_append(result, match); |
1153 | 0 | start = (const UChar*)(input_string+region->end[0]); |
1154 | 0 | onig_region_free(region,0); |
1155 | 0 | } else if (onigret == ONIG_MISMATCH) { |
1156 | 0 | break; |
1157 | 0 | } else { /* Error */ |
1158 | 0 | UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN]; |
1159 | 0 | onig_error_code_to_str(ebuf, onigret, &einfo); |
1160 | 0 | jv_free(result); |
1161 | 0 | result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "), |
1162 | 0 | jv_string((char*)ebuf))); |
1163 | 0 | break; |
1164 | 0 | } |
1165 | 0 | } while (global && start <= end); |
1166 | 0 | onig_region_free(region,1); |
1167 | 0 | region = NULL; |
1168 | 0 | onig_free(reg); |
1169 | 0 | jv_free(input); |
1170 | 0 | jv_free(regex); |
1171 | 0 | return result; |
1172 | 0 | } |
1173 | | #else /* !HAVE_LIBONIG */ |
1174 | | static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) { |
1175 | | jv_free(input); |
1176 | | jv_free(regex); |
1177 | | jv_free(modifiers); |
1178 | | jv_free(testmode); |
1179 | | return jv_invalid_with_msg(jv_string("jq was compiled without ONIGURUMA regex library. match/test/sub and related functions are not available.")); |
1180 | | } |
1181 | | #endif /* HAVE_LIBONIG */ |
1182 | | |
1183 | 0 | static jv minmax_by(jv values, jv keys, int is_min) { |
1184 | 0 | if (jv_get_kind(values) != JV_KIND_ARRAY) |
1185 | 0 | return type_error2(values, keys, "cannot be iterated over"); |
1186 | 0 | if (jv_get_kind(keys) != JV_KIND_ARRAY) |
1187 | 0 | return type_error2(values, keys, "cannot be iterated over"); |
1188 | 0 | if (jv_array_length(jv_copy(values)) != jv_array_length(jv_copy(keys))) |
1189 | 0 | return type_error2(values, keys, "have wrong length"); |
1190 | | |
1191 | 0 | if (jv_array_length(jv_copy(values)) == 0) { |
1192 | 0 | jv_free(values); |
1193 | 0 | jv_free(keys); |
1194 | 0 | return jv_null(); |
1195 | 0 | } |
1196 | 0 | jv ret = jv_array_get(jv_copy(values), 0); |
1197 | 0 | jv retkey = jv_array_get(jv_copy(keys), 0); |
1198 | 0 | for (int i=1; i<jv_array_length(jv_copy(values)); i++) { |
1199 | 0 | jv item = jv_array_get(jv_copy(keys), i); |
1200 | 0 | int cmp = jv_cmp(jv_copy(item), jv_copy(retkey)); |
1201 | 0 | if (cmp == INT_MIN) { |
1202 | 0 | jv_free(item); |
1203 | 0 | jv_free(values); |
1204 | 0 | jv_free(keys); |
1205 | 0 | jv_free(retkey); |
1206 | 0 | jv_free(ret); |
1207 | 0 | return jv_invalid_with_msg(jv_string("Comparison too deep")); |
1208 | 0 | } |
1209 | 0 | if ((cmp < 0) == (is_min == 1)) { |
1210 | 0 | jv_free(retkey); |
1211 | 0 | retkey = item; |
1212 | 0 | jv_free(ret); |
1213 | 0 | ret = jv_array_get(jv_copy(values), i); |
1214 | 0 | } else { |
1215 | 0 | jv_free(item); |
1216 | 0 | } |
1217 | 0 | } |
1218 | 0 | jv_free(values); |
1219 | 0 | jv_free(keys); |
1220 | 0 | jv_free(retkey); |
1221 | 0 | return ret; |
1222 | 0 | } |
1223 | | |
1224 | 0 | static jv f_min(jq_state *jq, jv x) { |
1225 | 0 | return minmax_by(x, jv_copy(x), 1); |
1226 | 0 | } |
1227 | | |
1228 | 0 | static jv f_max(jq_state *jq, jv x) { |
1229 | 0 | return minmax_by(x, jv_copy(x), 0); |
1230 | 0 | } |
1231 | | |
1232 | 0 | static jv f_min_by_impl(jq_state *jq, jv x, jv y) { |
1233 | 0 | return minmax_by(x, y, 1); |
1234 | 0 | } |
1235 | | |
1236 | 0 | static jv f_max_by_impl(jq_state *jq, jv x, jv y) { |
1237 | 0 | return minmax_by(x, y, 0); |
1238 | 0 | } |
1239 | | |
1240 | | |
1241 | 0 | static jv f_type(jq_state *jq, jv input) { |
1242 | 0 | jv out = jv_string(jv_kind_name(jv_get_kind(input))); |
1243 | 0 | jv_free(input); |
1244 | 0 | return out; |
1245 | 0 | } |
1246 | | |
1247 | 0 | static jv f_isinfinite(jq_state *jq, jv input) { |
1248 | 0 | jv_kind k = jv_get_kind(input); |
1249 | 0 | if (k != JV_KIND_NUMBER) { |
1250 | 0 | jv_free(input); |
1251 | 0 | return jv_false(); |
1252 | 0 | } |
1253 | 0 | double n = jv_number_value(input); |
1254 | 0 | jv_free(input); |
1255 | 0 | return isinf(n) ? jv_true() : jv_false(); |
1256 | 0 | } |
1257 | | |
1258 | 0 | static jv f_isnan(jq_state *jq, jv input) { |
1259 | 0 | jv_kind k = jv_get_kind(input); |
1260 | 0 | if (k != JV_KIND_NUMBER) { |
1261 | 0 | jv_free(input); |
1262 | 0 | return jv_false(); |
1263 | 0 | } |
1264 | 0 | double n = jv_number_value(input); |
1265 | 0 | jv_free(input); |
1266 | 0 | return isnan(n) ? jv_true() : jv_false(); |
1267 | 0 | } |
1268 | | |
1269 | 0 | static jv f_isnormal(jq_state *jq, jv input) { |
1270 | 0 | jv_kind k = jv_get_kind(input); |
1271 | 0 | if (k != JV_KIND_NUMBER) { |
1272 | 0 | jv_free(input); |
1273 | 0 | return jv_false(); |
1274 | 0 | } |
1275 | 0 | double n = jv_number_value(input); |
1276 | 0 | jv_free(input); |
1277 | 0 | return isnormal(n) ? jv_true() : jv_false(); |
1278 | 0 | } |
1279 | | |
1280 | 0 | static jv f_infinite(jq_state *jq, jv input) { |
1281 | 0 | jv_free(input); |
1282 | 0 | return jv_number(INFINITY); |
1283 | 0 | } |
1284 | | |
1285 | 0 | static jv f_nan(jq_state *jq, jv input) { |
1286 | 0 | jv_free(input); |
1287 | 0 | return jv_number(NAN); |
1288 | 0 | } |
1289 | | |
1290 | 0 | static jv f_error(jq_state *jq, jv input) { |
1291 | 0 | return jv_invalid_with_msg(input); |
1292 | 0 | } |
1293 | | |
1294 | | // FIXME Should autoconf check for this! |
1295 | | #ifndef WIN32 |
1296 | | extern char **environ; |
1297 | | #endif |
1298 | | |
1299 | 0 | static jv f_env(jq_state *jq, jv input) { |
1300 | 0 | jv_free(input); |
1301 | 0 | jv env = jv_object(); |
1302 | 0 | const char *var, *val; |
1303 | 0 | for (char **e = environ; *e != NULL; e++) { |
1304 | 0 | var = e[0]; |
1305 | 0 | val = strchr(e[0], '='); |
1306 | 0 | if (val == NULL) |
1307 | 0 | env = jv_object_set(env, jv_string(var), jv_null()); |
1308 | 0 | else if (val - var < INT_MAX) |
1309 | 0 | env = jv_object_set(env, jv_string_sized(var, val - var), jv_string(val + 1)); |
1310 | 0 | } |
1311 | 0 | return env; |
1312 | 0 | } |
1313 | | |
1314 | 0 | static jv f_halt(jq_state *jq, jv input) { |
1315 | 0 | jv_free(input); |
1316 | 0 | jq_halt(jq, jv_invalid(), jv_invalid()); |
1317 | 0 | return jv_true(); |
1318 | 0 | } |
1319 | | |
1320 | 0 | static jv f_halt_error(jq_state *jq, jv input, jv a) { |
1321 | 0 | if (jv_get_kind(a) != JV_KIND_NUMBER) { |
1322 | 0 | jv_free(a); |
1323 | 0 | return type_error(input, "halt_error/1: number required"); |
1324 | 0 | } |
1325 | 0 | jq_halt(jq, a, input); |
1326 | 0 | return jv_true(); |
1327 | 0 | } |
1328 | | |
1329 | 0 | static jv f_get_search_list(jq_state *jq, jv input) { |
1330 | 0 | jv_free(input); |
1331 | 0 | return jq_get_lib_dirs(jq); |
1332 | 0 | } |
1333 | | |
1334 | 0 | static jv f_get_prog_origin(jq_state *jq, jv input) { |
1335 | 0 | jv_free(input); |
1336 | 0 | return jq_get_prog_origin(jq); |
1337 | 0 | } |
1338 | | |
1339 | 0 | static jv f_get_jq_origin(jq_state *jq, jv input) { |
1340 | 0 | jv_free(input); |
1341 | 0 | return jq_get_jq_origin(jq); |
1342 | 0 | } |
1343 | | |
1344 | 0 | static jv f_string_split(jq_state *jq, jv a, jv b) { |
1345 | 0 | if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) { |
1346 | 0 | return ret_error2(a, b, jv_string("split input and separator must be strings")); |
1347 | 0 | } |
1348 | 0 | return jv_string_split(a, b); |
1349 | 0 | } |
1350 | | |
1351 | 0 | static jv f_string_explode(jq_state *jq, jv a) { |
1352 | 0 | if (jv_get_kind(a) != JV_KIND_STRING) { |
1353 | 0 | return ret_error(a, jv_string("explode input must be a string")); |
1354 | 0 | } |
1355 | 0 | return jv_string_explode(a); |
1356 | 0 | } |
1357 | | |
1358 | 0 | static jv f_string_indexes(jq_state *jq, jv a, jv b) { |
1359 | 0 | if (jv_get_kind(a) != JV_KIND_STRING) { |
1360 | 0 | jv_free(b); |
1361 | 0 | return type_error(a, "cannot be searched, as it is not a string"); |
1362 | 0 | } |
1363 | 0 | if (jv_get_kind(b) != JV_KIND_STRING) { |
1364 | 0 | jv_free(a); |
1365 | 0 | return type_error(b, "is not a string"); |
1366 | 0 | } |
1367 | 0 | return jv_string_indexes(a, b); |
1368 | 0 | } |
1369 | | |
1370 | | enum trim_op { |
1371 | | TRIM_LEFT = 1 << 0, |
1372 | | TRIM_RIGHT = 1 << 1 |
1373 | | }; |
1374 | | |
1375 | 0 | static jv string_trim(jv a, int op) { |
1376 | 0 | if (jv_get_kind(a) != JV_KIND_STRING) { |
1377 | 0 | return ret_error(a, jv_string("trim input must be a string")); |
1378 | 0 | } |
1379 | | |
1380 | 0 | int len = jv_string_length_bytes(jv_copy(a)); |
1381 | 0 | const char *start = jv_string_value(a); |
1382 | 0 | const char *trim_start = start; |
1383 | 0 | const char *end = trim_start + len; |
1384 | 0 | const char *trim_end = end; |
1385 | 0 | int c; |
1386 | |
|
1387 | 0 | if (op & TRIM_LEFT) { |
1388 | 0 | for (;;) { |
1389 | 0 | const char *ns = jvp_utf8_next(trim_start, end, &c); |
1390 | 0 | if (!ns || !jvp_codepoint_is_whitespace(c)) |
1391 | 0 | break; |
1392 | 0 | trim_start = ns; |
1393 | 0 | } |
1394 | 0 | } |
1395 | | |
1396 | | // make sure not empty string or start trim has trimmed everything |
1397 | 0 | if ((op & TRIM_RIGHT) && trim_end > trim_start) { |
1398 | 0 | for (;;) { |
1399 | 0 | const char *ns = jvp_utf8_backtrack(trim_end-1, trim_start, NULL); |
1400 | 0 | jvp_utf8_next(ns, trim_end, &c); |
1401 | 0 | if (!jvp_codepoint_is_whitespace(c)) |
1402 | 0 | break; |
1403 | 0 | trim_end = ns; |
1404 | 0 | if (ns == trim_start) |
1405 | 0 | break; |
1406 | 0 | } |
1407 | 0 | } |
1408 | | |
1409 | | // no new string needed if there is nothing to trim |
1410 | 0 | if (trim_start == start && trim_end == end) |
1411 | 0 | return a; |
1412 | | |
1413 | 0 | jv ts = jv_string_sized(trim_start, trim_end - trim_start); |
1414 | 0 | jv_free(a); |
1415 | 0 | return ts; |
1416 | 0 | } |
1417 | | |
1418 | 0 | static jv f_string_trim(jq_state *jq, jv a) { return string_trim(a, TRIM_LEFT | TRIM_RIGHT); } |
1419 | 0 | static jv f_string_ltrim(jq_state *jq, jv a) { return string_trim(a, TRIM_LEFT); } |
1420 | 0 | static jv f_string_rtrim(jq_state *jq, jv a) { return string_trim(a, TRIM_RIGHT); } |
1421 | | |
1422 | 0 | static jv f_string_implode(jq_state *jq, jv a) { |
1423 | 0 | if (jv_get_kind(a) != JV_KIND_ARRAY) { |
1424 | 0 | return ret_error(a, jv_string("implode input must be an array")); |
1425 | 0 | } |
1426 | | |
1427 | 0 | int len = jv_array_length(jv_copy(a)); |
1428 | 0 | jv s = jv_string_empty(len); |
1429 | |
|
1430 | 0 | for (int i = 0; i < len; i++) { |
1431 | 0 | jv n = jv_array_get(jv_copy(a), i); |
1432 | 0 | if (jv_get_kind(n) != JV_KIND_NUMBER || jvp_number_is_nan(n)) { |
1433 | 0 | jv_free(a); |
1434 | 0 | jv_free(s); |
1435 | 0 | return type_error(n, "can't be imploded, unicode codepoint needs to be numeric"); |
1436 | 0 | } |
1437 | | |
1438 | 0 | int nv = jv_number_value(n); |
1439 | 0 | jv_free(n); |
1440 | | // outside codepoint range or in utf16 surrogate pair range |
1441 | 0 | if (nv < 0 || nv > 0x10FFFF || (nv >= 0xD800 && nv <= 0xDFFF)) |
1442 | 0 | nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER |
1443 | 0 | s = jv_string_append_codepoint(s, nv); |
1444 | 0 | if (!jv_is_valid(s)) break; |
1445 | 0 | } |
1446 | | |
1447 | 0 | jv_free(a); |
1448 | 0 | return s; |
1449 | 0 | } |
1450 | | |
1451 | 0 | static jv f_setpath(jq_state *jq, jv a, jv b, jv c) { return jv_setpath(a, b, c); } |
1452 | | extern jv _jq_path_append(jq_state *, jv, jv, jv); |
1453 | 0 | static jv f_getpath(jq_state *jq, jv a, jv b) { |
1454 | 0 | return _jq_path_append(jq, a, b, jv_getpath(jv_copy(a), jv_copy(b))); |
1455 | 0 | } |
1456 | 0 | static jv f_delpaths(jq_state *jq, jv a, jv b) { return jv_delpaths(a, b); } |
1457 | 0 | static jv f_has(jq_state *jq, jv a, jv b) { return jv_has(a, b); } |
1458 | | |
1459 | 0 | static jv f_modulemeta(jq_state *jq, jv a) { |
1460 | 0 | if (jv_get_kind(a) != JV_KIND_STRING) { |
1461 | 0 | return ret_error(a, jv_string("modulemeta input module name must be a string")); |
1462 | 0 | } |
1463 | 0 | return load_module_meta(jq, a); |
1464 | 0 | } |
1465 | | |
1466 | 0 | static jv f_input(jq_state *jq, jv input) { |
1467 | 0 | jv_free(input); |
1468 | 0 | jq_input_cb cb; |
1469 | 0 | void *data; |
1470 | 0 | jq_get_input_cb(jq, &cb, &data); |
1471 | 0 | if (cb == NULL) |
1472 | 0 | return jv_invalid_with_msg(jv_string("break")); |
1473 | 0 | jv v = cb(jq, data); |
1474 | 0 | if (jv_is_valid(v) || jv_invalid_has_msg(jv_copy(v))) |
1475 | 0 | return v; |
1476 | 0 | return jv_invalid_with_msg(jv_string("break")); |
1477 | 0 | } |
1478 | | |
1479 | 0 | static jv f_debug(jq_state *jq, jv input) { |
1480 | 0 | jq_msg_cb cb; |
1481 | 0 | void *data; |
1482 | 0 | jq_get_debug_cb(jq, &cb, &data); |
1483 | 0 | if (cb != NULL) |
1484 | 0 | cb(data, jv_copy(input)); |
1485 | 0 | return input; |
1486 | 0 | } |
1487 | | |
1488 | 0 | static jv f_stderr(jq_state *jq, jv input) { |
1489 | 0 | jq_msg_cb cb; |
1490 | 0 | void *data; |
1491 | 0 | jq_get_stderr_cb(jq, &cb, &data); |
1492 | 0 | if (cb != NULL) |
1493 | 0 | cb(data, jv_copy(input)); |
1494 | 0 | return input; |
1495 | 0 | } |
1496 | | |
1497 | 0 | static jv tm2jv(struct tm *tm, double fsecs) { |
1498 | 0 | return JV_ARRAY(jv_number(tm->tm_year + 1900), |
1499 | 0 | jv_number(tm->tm_mon), |
1500 | 0 | jv_number(tm->tm_mday), |
1501 | 0 | jv_number(tm->tm_hour), |
1502 | 0 | jv_number(tm->tm_min), |
1503 | 0 | jv_number(tm->tm_sec + (fsecs - floor(fsecs))), |
1504 | 0 | jv_number(tm->tm_wday), |
1505 | 0 | jv_number(tm->tm_yday)); |
1506 | 0 | } |
1507 | | |
1508 | | #if defined(WIN32) && !defined(HAVE_SETENV) |
1509 | | static int setenv(const char *var, const char *val, int ovr) |
1510 | | { |
1511 | | BOOL b; |
1512 | | char c[2]; |
1513 | | if (!ovr) |
1514 | | { |
1515 | | DWORD d; |
1516 | | d = GetEnvironmentVariableA (var, c, 2); |
1517 | | if (0 != d && GetLastError () != ERROR_ENVVAR_NOT_FOUND) { |
1518 | | return d; |
1519 | | } |
1520 | | } |
1521 | | b = SetEnvironmentVariableA (var, val); |
1522 | | if (b) { |
1523 | | return 0; |
1524 | | } |
1525 | | return 1; |
1526 | | } |
1527 | | #endif |
1528 | | |
1529 | | /* |
1530 | | * mktime() has side-effects and anyways, returns time in the local |
1531 | | * timezone, not UTC. We want timegm(), which isn't standard. |
1532 | | * |
1533 | | * To make things worse, mktime() tells you what the timezone |
1534 | | * adjustment is, but you have to #define _BSD_SOURCE to get this |
1535 | | * field of struct tm on some systems. |
1536 | | * |
1537 | | * This is all to blame on POSIX, of course. |
1538 | | * |
1539 | | * Our wrapper tries to use timegm() if available, or mktime() and |
1540 | | * correct for its side-effects if possible. |
1541 | | * |
1542 | | * Returns (time_t)-2 if mktime()'s side-effects cannot be corrected. |
1543 | | */ |
1544 | 0 | static time_t my_mktime(struct tm *tm) { |
1545 | 0 | #ifdef HAVE_TIMEGM |
1546 | 0 | return timegm(tm); |
1547 | | #elif HAVE_TM_TM_GMT_OFF |
1548 | | |
1549 | | time_t t = mktime(tm); |
1550 | | if (t == (time_t)-1) |
1551 | | return t; |
1552 | | return t + tm->tm_gmtoff; |
1553 | | #elif HAVE_TM___TM_GMT_OFF |
1554 | | time_t t = mktime(tm); |
1555 | | if (t == (time_t)-1) |
1556 | | return t; |
1557 | | return t + tm->__tm_gmtoff; |
1558 | | #elif WIN32 |
1559 | | return _mkgmtime(tm); |
1560 | | #else |
1561 | | char *tz; |
1562 | | |
1563 | | tz = (tz = getenv("TZ")) != NULL ? strdup(tz) : NULL; |
1564 | | if (tz != NULL) |
1565 | | setenv("TZ", "", 1); |
1566 | | time_t t = mktime(tm); |
1567 | | if (tz != NULL) { |
1568 | | setenv("TZ", tz, 1); |
1569 | | free(tz); |
1570 | | } |
1571 | | return t; |
1572 | | #endif |
1573 | 0 | } |
1574 | | |
1575 | | /* Compute and set tm_wday */ |
1576 | 0 | static void set_tm_wday(struct tm *tm) { |
1577 | | /* |
1578 | | * https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Gauss.27s_algorithm |
1579 | | * https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html |
1580 | | * |
1581 | | * Tested with dates from 1900-01-01 through 2100-01-01. This |
1582 | | * algorithm produces the wrong day-of-the-week number for dates in |
1583 | | * the range 1900-01-01..1900-02-28, and for 2100-01-01..2100-02-28. |
1584 | | * Since this is only needed on OS X and *BSD, we might just document |
1585 | | * this. |
1586 | | */ |
1587 | 0 | int century = (1900 + tm->tm_year) / 100; |
1588 | 0 | int year = (1900 + tm->tm_year) % 100; |
1589 | 0 | if (tm->tm_mon < 2) |
1590 | 0 | year--; |
1591 | | /* |
1592 | | * The month value in the wday computation below is shifted so that |
1593 | | * March is 1, April is 2, .., January is 11, and February is 12. |
1594 | | */ |
1595 | 0 | int mon = tm->tm_mon - 1; |
1596 | 0 | if (mon < 1) |
1597 | 0 | mon += 12; |
1598 | 0 | int wday = |
1599 | 0 | (tm->tm_mday + (int)floor((2.6 * mon - 0.2)) + year + (int)floor(year / 4.0) + (int)floor(century / 4.0) - 2 * century) % 7; |
1600 | 0 | if (wday < 0) |
1601 | 0 | wday += 7; |
1602 | | #if 0 |
1603 | | /* See commentary above */ |
1604 | | assert(wday == tm->tm_wday || tm->tm_wday == 8); |
1605 | | #endif |
1606 | 0 | tm->tm_wday = wday; |
1607 | 0 | } |
1608 | | /* |
1609 | | * Compute and set tm_yday. |
1610 | | * |
1611 | | */ |
1612 | 0 | static void set_tm_yday(struct tm *tm) { |
1613 | 0 | static const int d[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; |
1614 | 0 | int mon = tm->tm_mon; |
1615 | 0 | int year = 1900 + tm->tm_year; |
1616 | 0 | int leap_day = 0; |
1617 | 0 | if (tm->tm_mon > 1 && |
1618 | 0 | ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) |
1619 | 0 | leap_day = 1; |
1620 | | |
1621 | | /* Bound check index into d[] */ |
1622 | 0 | if (mon < 0) |
1623 | 0 | mon = -mon; |
1624 | 0 | if (mon > 11) |
1625 | 0 | mon %= 12; |
1626 | |
|
1627 | 0 | int yday = d[mon] + leap_day + tm->tm_mday - 1; |
1628 | 0 | assert(yday == tm->tm_yday || tm->tm_yday == 367); |
1629 | 0 | tm->tm_yday = yday; |
1630 | 0 | } |
1631 | | |
1632 | 0 | static jv f_strptime(jq_state *jq, jv a, jv b) { |
1633 | 0 | if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) { |
1634 | 0 | return ret_error2(a, b, jv_string("strptime/1 requires string inputs and arguments")); |
1635 | 0 | } |
1636 | | |
1637 | 0 | struct tm tm; |
1638 | 0 | memset(&tm, 0, sizeof(tm)); |
1639 | 0 | tm.tm_wday = 8; // sentinel |
1640 | 0 | tm.tm_yday = 367; // sentinel |
1641 | |
|
1642 | 0 | const char *input = jv_string_value(a); |
1643 | 0 | const char *fmt = jv_string_value(b); |
1644 | 0 | const char *end = strptime(input, fmt, &tm); |
1645 | 0 | if (end == NULL || (*end != '\0' && !isspace((unsigned char)*end))) { |
1646 | 0 | return ret_error2(a, b, jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt)); |
1647 | 0 | } |
1648 | 0 | jv_free(b); |
1649 | | /* |
1650 | | * This is OS X or some *BSD whose strptime() is just not that |
1651 | | * helpful! |
1652 | | * |
1653 | | * We don't know that the format string did involve parsing a |
1654 | | * year, or a month (if tm->tm_mon == 0). But with our invalid |
1655 | | * day-of-week and day-of-year sentinel checks above, the worst |
1656 | | * this can do is produce garbage. |
1657 | | */ |
1658 | | #ifdef __APPLE__ |
1659 | | /* |
1660 | | * Apple has made it worse, and different versions of the OS have different |
1661 | | * behaviors. Some versions just don't touch the fields, but others do, and |
1662 | | * sometimes provide wrong answers, at that! We can't tell at compile-time |
1663 | | * which behavior the target system will have, so instead we always use our |
1664 | | * functions to set these on OS X, and document that %u and %j are |
1665 | | * unsupported on OS X. |
1666 | | */ |
1667 | | set_tm_wday(&tm); |
1668 | | set_tm_yday(&tm); |
1669 | | #elif defined(WIN32) || !defined(HAVE_STRPTIME) |
1670 | | set_tm_wday(&tm); |
1671 | | #else |
1672 | 0 | if (tm.tm_wday == 8 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11) |
1673 | 0 | set_tm_wday(&tm); |
1674 | 0 | if (tm.tm_yday == 367 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11) |
1675 | 0 | set_tm_yday(&tm); |
1676 | 0 | #endif |
1677 | 0 | jv r = tm2jv(&tm, 0); |
1678 | 0 | if (*end != '\0') |
1679 | 0 | r = jv_array_append(r, jv_string(end)); |
1680 | 0 | jv_free(a); // must come after `*end` because `end` is a pointer into `a`'s string |
1681 | 0 | return r; |
1682 | 0 | } |
1683 | | |
1684 | 0 | static jv f_fromdateiso8601(jq_state *jq, jv a) { |
1685 | 0 | if (jv_get_kind(a) != JV_KIND_STRING) |
1686 | 0 | return ret_error(a, jv_string("fromdateiso8601 requires string inputs")); |
1687 | 0 | double t; |
1688 | 0 | if (!iso8601_parse(jv_string_value(a), jv_string_length_bytes(jv_copy(a)), &t)) |
1689 | 0 | return ret_error(a, jv_string_fmt("date \"%s\" is not a valid ISO 8601 datetime", |
1690 | 0 | jv_string_value(a))); |
1691 | 0 | jv_free(a); |
1692 | 0 | return jv_number(t); |
1693 | 0 | } |
1694 | | |
1695 | 0 | static int jv2tm(jv a, struct tm *tm, int localtime) { |
1696 | 0 | memset(tm, 0, sizeof(*tm)); |
1697 | 0 | static const size_t offsets[] = { |
1698 | 0 | offsetof(struct tm, tm_year), |
1699 | 0 | offsetof(struct tm, tm_mon), |
1700 | 0 | offsetof(struct tm, tm_mday), |
1701 | 0 | offsetof(struct tm, tm_hour), |
1702 | 0 | offsetof(struct tm, tm_min), |
1703 | 0 | offsetof(struct tm, tm_sec), |
1704 | 0 | offsetof(struct tm, tm_wday), |
1705 | 0 | offsetof(struct tm, tm_yday), |
1706 | 0 | }; |
1707 | |
|
1708 | 0 | for (size_t i = 0; i < (sizeof offsets / sizeof *offsets); ++i) { |
1709 | 0 | jv n = jv_array_get(jv_copy(a), i); |
1710 | 0 | if (!jv_is_valid(n)) |
1711 | 0 | break; |
1712 | 0 | if (jv_get_kind(n) != JV_KIND_NUMBER || jvp_number_is_nan(n)) { |
1713 | 0 | jv_free(a); |
1714 | 0 | jv_free(n); |
1715 | 0 | return 0; |
1716 | 0 | } |
1717 | 0 | double d = jv_number_value(n); |
1718 | 0 | if (i == 0) /* year */ |
1719 | 0 | d -= 1900; |
1720 | 0 | *(int *)((void *)tm + offsets[i]) = d < INT_MIN ? INT_MIN : |
1721 | 0 | d > INT_MAX ? INT_MAX : (int)d; |
1722 | 0 | jv_free(n); |
1723 | 0 | } |
1724 | | |
1725 | 0 | if (localtime) { |
1726 | 0 | tm->tm_isdst = -1; |
1727 | 0 | mktime(tm); |
1728 | 0 | } else { |
1729 | 0 | #ifdef HAVE_TIMEGM |
1730 | 0 | timegm(tm); |
1731 | | #elif HAVE_TM_TM_GMT_OFF |
1732 | | // tm->tm_gmtoff = 0; |
1733 | | tm->tm_zone = "GMT"; |
1734 | | #elif HAVE_TM___TM_GMT_OFF |
1735 | | // tm->__tm_gmtoff = 0; |
1736 | | tm->__tm_zone = "GMT"; |
1737 | | #endif |
1738 | | // tm->tm_isdst = 0; |
1739 | | |
1740 | | // The standard permits the tm structure to contain additional members. We |
1741 | | // hope it is okay to initialize them to zero, because the standard does not |
1742 | | // provide an alternative. |
1743 | 0 | } |
1744 | |
|
1745 | 0 | jv_free(a); |
1746 | 0 | return 1; |
1747 | 0 | } |
1748 | | |
1749 | | #undef TO_TM_FIELD |
1750 | | |
1751 | 0 | static jv f_mktime(jq_state *jq, jv a) { |
1752 | 0 | if (jv_get_kind(a) != JV_KIND_ARRAY) |
1753 | 0 | return ret_error(a, jv_string("mktime requires array inputs")); |
1754 | 0 | struct tm tm; |
1755 | 0 | if (!jv2tm(a, &tm, 0)) |
1756 | 0 | return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs")); |
1757 | 0 | time_t t = my_mktime(&tm); |
1758 | 0 | if (t == (time_t)-1) |
1759 | 0 | return jv_invalid_with_msg(jv_string("invalid gmtime representation")); |
1760 | 0 | if (t == (time_t)-2) |
1761 | 0 | return jv_invalid_with_msg(jv_string("mktime not supported on this platform")); |
1762 | 0 | return jv_number(t); |
1763 | 0 | } |
1764 | | |
1765 | | #ifdef HAVE_GMTIME_R |
1766 | 0 | static jv f_gmtime(jq_state *jq, jv a) { |
1767 | 0 | if (jv_get_kind(a) != JV_KIND_NUMBER) |
1768 | 0 | return ret_error(a, jv_string("gmtime() requires numeric inputs")); |
1769 | 0 | struct tm tm, *tmp; |
1770 | 0 | memset(&tm, 0, sizeof(tm)); |
1771 | 0 | double fsecs = jv_number_value(a); |
1772 | 0 | time_t secs = floor(fsecs); |
1773 | 0 | jv_free(a); |
1774 | 0 | tmp = gmtime_r(&secs, &tm); |
1775 | 0 | if (tmp == NULL) |
1776 | 0 | return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime")); |
1777 | 0 | return tm2jv(tmp, fsecs); |
1778 | 0 | } |
1779 | | #elif defined HAVE_GMTIME |
1780 | | static jv f_gmtime(jq_state *jq, jv a) { |
1781 | | if (jv_get_kind(a) != JV_KIND_NUMBER) |
1782 | | return ret_error(a, jv_string("gmtime requires numeric inputs")); |
1783 | | struct tm tm, *tmp; |
1784 | | memset(&tm, 0, sizeof(tm)); |
1785 | | double fsecs = jv_number_value(a); |
1786 | | time_t secs = floor(fsecs); |
1787 | | jv_free(a); |
1788 | | tmp = gmtime(&secs); |
1789 | | if (tmp == NULL) |
1790 | | return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime")); |
1791 | | return tm2jv(tmp, fsecs); |
1792 | | } |
1793 | | #else |
1794 | | static jv f_gmtime(jq_state *jq, jv a) { |
1795 | | jv_free(a); |
1796 | | return jv_invalid_with_msg(jv_string("gmtime not implemented on this platform")); |
1797 | | } |
1798 | | #endif |
1799 | | |
1800 | | #ifdef HAVE_LOCALTIME_R |
1801 | 0 | static jv f_localtime(jq_state *jq, jv a) { |
1802 | 0 | if (jv_get_kind(a) != JV_KIND_NUMBER) |
1803 | 0 | return ret_error(a, jv_string("localtime() requires numeric inputs")); |
1804 | 0 | struct tm tm, *tmp; |
1805 | 0 | memset(&tm, 0, sizeof(tm)); |
1806 | 0 | double fsecs = jv_number_value(a); |
1807 | 0 | time_t secs = floor(fsecs); |
1808 | 0 | jv_free(a); |
1809 | 0 | tmp = localtime_r(&secs, &tm); |
1810 | 0 | if (tmp == NULL) |
1811 | 0 | return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime")); |
1812 | 0 | return tm2jv(tmp, fsecs); |
1813 | 0 | } |
1814 | | #elif defined HAVE_GMTIME |
1815 | | static jv f_localtime(jq_state *jq, jv a) { |
1816 | | if (jv_get_kind(a) != JV_KIND_NUMBER) |
1817 | | return ret_error(a, jv_string("localtime requires numeric inputs")); |
1818 | | struct tm tm, *tmp; |
1819 | | memset(&tm, 0, sizeof(tm)); |
1820 | | double fsecs = jv_number_value(a); |
1821 | | time_t secs = floor(fsecs); |
1822 | | jv_free(a); |
1823 | | tmp = localtime(&secs); |
1824 | | if (tmp == NULL) |
1825 | | return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime")); |
1826 | | return tm2jv(tmp, fsecs); |
1827 | | } |
1828 | | #else |
1829 | | static jv f_localtime(jq_state *jq, jv a) { |
1830 | | jv_free(a); |
1831 | | return jv_invalid_with_msg(jv_string("localtime not implemented on this platform")); |
1832 | | } |
1833 | | #endif |
1834 | | |
1835 | | #ifdef HAVE_STRFTIME |
1836 | 0 | static jv f_strftime(jq_state *jq, jv a, jv b) { |
1837 | 0 | if (jv_get_kind(a) == JV_KIND_NUMBER) { |
1838 | 0 | a = f_gmtime(jq, a); |
1839 | 0 | if (!jv_is_valid(a)) { |
1840 | 0 | jv_free(b); |
1841 | 0 | return a; |
1842 | 0 | } |
1843 | 0 | } else if (jv_get_kind(a) != JV_KIND_ARRAY) { |
1844 | 0 | return ret_error2(a, b, jv_string("strftime/1 requires parsed datetime inputs")); |
1845 | 0 | } |
1846 | 0 | if (jv_get_kind(b) != JV_KIND_STRING) |
1847 | 0 | return ret_error2(a, b, jv_string("strftime/1 requires a string format")); |
1848 | 0 | struct tm tm; |
1849 | 0 | if (!jv2tm(a, &tm, 0)) |
1850 | 0 | return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs")); |
1851 | | |
1852 | | /* Prepend a character so that strftime() returning 0 means the result |
1853 | | * did not fit, even if the format expands to nothing; it is trimmed below. */ |
1854 | 0 | b = jv_string_concat(jv_string(" "), b); |
1855 | 0 | const char *fmt = jv_string_value(b); |
1856 | 0 | size_t max_size = strlen(fmt) + 100; |
1857 | 0 | char *buf = jv_mem_alloc(max_size); |
1858 | | #if defined(__APPLE__) || defined(__sun) |
1859 | | /* Apple Libc (as of version 1669.40.2) contains a bug which causes it to |
1860 | | * ignore the `tm.tm_gmtoff` in favor of the global timezone. To print the |
1861 | | * proper timezone offset we temporarily switch the TZ to UTC. */ |
1862 | | char *tz = (tz = getenv("TZ")) != NULL ? strdup(tz) : NULL; |
1863 | | setenv("TZ", "UTC", 1); |
1864 | | #endif |
1865 | | #if defined(__sun) |
1866 | | /* Solaris moreover needs call to tzset to take the changed environment into |
1867 | | * account ... */ |
1868 | | tzset(); |
1869 | | #endif |
1870 | | #ifdef __NetBSD__ |
1871 | | timezone_t tz_utc = tzalloc("UTC"); |
1872 | | size_t n = strftime_z(tz_utc, buf, max_size, fmt, &tm); |
1873 | | tzfree(tz_utc); |
1874 | | #else |
1875 | 0 | size_t n = strftime(buf, max_size, fmt, &tm); |
1876 | 0 | #endif |
1877 | | #if defined(__APPLE__) || defined(__sun) |
1878 | | if (tz) { |
1879 | | setenv("TZ", tz, 1); |
1880 | | free(tz); |
1881 | | } else { |
1882 | | unsetenv("TZ"); |
1883 | | } |
1884 | | #endif |
1885 | | #if defined(__sun) |
1886 | | tzset(); |
1887 | | #endif |
1888 | 0 | jv_free(b); |
1889 | 0 | if (n == 0 || n > max_size) { |
1890 | 0 | free(buf); |
1891 | 0 | return jv_invalid_with_msg(jv_string("strftime/1: result too long")); |
1892 | 0 | } |
1893 | 0 | jv ret = jv_string_sized(buf + 1, n - 1); |
1894 | 0 | free(buf); |
1895 | 0 | return ret; |
1896 | 0 | } |
1897 | | #else |
1898 | | static jv f_strftime(jq_state *jq, jv a, jv b) { |
1899 | | jv_free(a); |
1900 | | jv_free(b); |
1901 | | return jv_invalid_with_msg(jv_string("strftime/1 not implemented on this platform")); |
1902 | | } |
1903 | | #endif |
1904 | | |
1905 | | #ifdef HAVE_STRFTIME |
1906 | 0 | static jv f_strflocaltime(jq_state *jq, jv a, jv b) { |
1907 | 0 | if (jv_get_kind(a) == JV_KIND_NUMBER) { |
1908 | 0 | a = f_localtime(jq, a); |
1909 | 0 | if (!jv_is_valid(a)) { |
1910 | 0 | jv_free(b); |
1911 | 0 | return a; |
1912 | 0 | } |
1913 | 0 | } else if (jv_get_kind(a) != JV_KIND_ARRAY) { |
1914 | 0 | return ret_error2(a, b, jv_string("strflocaltime/1 requires parsed datetime inputs")); |
1915 | 0 | } |
1916 | 0 | if (jv_get_kind(b) != JV_KIND_STRING) |
1917 | 0 | return ret_error2(a, b, jv_string("strflocaltime/1 requires a string format")); |
1918 | 0 | struct tm tm; |
1919 | 0 | if (!jv2tm(a, &tm, 1)) |
1920 | 0 | return ret_error(b, jv_string("strflocaltime/1 requires parsed datetime inputs")); |
1921 | | /* Prepend a character so that strftime() returning 0 means the result |
1922 | | * did not fit, even if the format expands to nothing; it is trimmed below. */ |
1923 | 0 | b = jv_string_concat(jv_string(" "), b); |
1924 | 0 | const char *fmt = jv_string_value(b); |
1925 | 0 | size_t max_size = strlen(fmt) + 100; |
1926 | 0 | char *buf = jv_mem_alloc(max_size); |
1927 | 0 | size_t n = strftime(buf, max_size, fmt, &tm); |
1928 | 0 | jv_free(b); |
1929 | 0 | if (n == 0 || n > max_size) { |
1930 | 0 | free(buf); |
1931 | 0 | return jv_invalid_with_msg(jv_string("strflocaltime/1: result too long")); |
1932 | 0 | } |
1933 | 0 | jv ret = jv_string_sized(buf + 1, n - 1); |
1934 | 0 | free(buf); |
1935 | 0 | return ret; |
1936 | 0 | } |
1937 | | #else |
1938 | | static jv f_strflocaltime(jq_state *jq, jv a, jv b) { |
1939 | | jv_free(a); |
1940 | | jv_free(b); |
1941 | | return jv_invalid_with_msg(jv_string("strflocaltime/1 not implemented on this platform")); |
1942 | | } |
1943 | | #endif |
1944 | | |
1945 | | #ifdef HAVE_GETTIMEOFDAY |
1946 | 0 | static jv f_now(jq_state *jq, jv a) { |
1947 | 0 | jv_free(a); |
1948 | 0 | struct timeval tv; |
1949 | 0 | if (gettimeofday(&tv, NULL) == -1) |
1950 | 0 | return jv_number(time(NULL)); |
1951 | 0 | return jv_number(tv.tv_sec + tv.tv_usec / 1000000.0); |
1952 | 0 | } |
1953 | | #else |
1954 | | static jv f_now(jq_state *jq, jv a) { |
1955 | | jv_free(a); |
1956 | | return jv_number(time(NULL)); |
1957 | | } |
1958 | | #endif |
1959 | | |
1960 | 0 | static jv f_current_filename(jq_state *jq, jv a) { |
1961 | 0 | jv_free(a); |
1962 | |
|
1963 | 0 | jv r = jq_util_input_get_current_filename(jq); |
1964 | 0 | if (jv_is_valid(r)) |
1965 | 0 | return r; |
1966 | 0 | jv_free(r); |
1967 | 0 | return jv_null(); |
1968 | 0 | } |
1969 | 0 | static jv f_current_line(jq_state *jq, jv a) { |
1970 | 0 | jv_free(a); |
1971 | 0 | return jq_util_input_get_current_line(jq); |
1972 | 0 | } |
1973 | | |
1974 | 0 | static jv f_have_decnum(jq_state *jq, jv a) { |
1975 | 0 | jv_free(a); |
1976 | 0 | #ifdef USE_DECNUM |
1977 | 0 | return jv_true(); |
1978 | | #else |
1979 | | return jv_false(); |
1980 | | #endif |
1981 | 0 | } |
1982 | | |
1983 | | #define CFUNC(func, name, arity) \ |
1984 | | {.fptr = { .a ## arity = func }, name, arity} |
1985 | | |
1986 | | #define LIBM_DD(name) \ |
1987 | | CFUNC(f_ ## name, #name, 1), |
1988 | | #define LIBM_DD_NO(name) LIBM_DD(name) |
1989 | | #define LIBM_DA(name, type) LIBM_DD(name) |
1990 | | #define LIBM_DA_NO(name, type) LIBM_DD(name) |
1991 | | |
1992 | | #define LIBM_DDD(name) \ |
1993 | | CFUNC(f_ ## name, #name, 3), |
1994 | | #define LIBM_DDD_NO(name) LIBM_DDD(name) |
1995 | | |
1996 | | #define LIBM_DDDD(name) \ |
1997 | | CFUNC(f_ ## name, #name, 4), |
1998 | | #define LIBM_DDDD_NO(name) LIBM_DDDD(name) |
1999 | | |
2000 | | static const struct cfunction function_list[] = { |
2001 | | #include "libm.h" |
2002 | | CFUNC(f_negate, "_negate", 1), |
2003 | | #define BINOP(name) CFUNC(f_ ## name, "_" #name, 3), |
2004 | | BINOPS |
2005 | | #undef BINOP |
2006 | | CFUNC(f_dump, "tojson", 1), |
2007 | | CFUNC(f_json_parse, "fromjson", 1), |
2008 | | CFUNC(f_tonumber, "tonumber", 1), |
2009 | | CFUNC(f_toboolean, "toboolean", 1), |
2010 | | CFUNC(f_tostring, "tostring", 1), |
2011 | | CFUNC(f_keys, "keys", 1), |
2012 | | CFUNC(f_keys_unsorted, "keys_unsorted", 1), |
2013 | | CFUNC(f_startswith, "startswith", 2), |
2014 | | CFUNC(f_endswith, "endswith", 2), |
2015 | | CFUNC(f_string_split, "split", 2), |
2016 | | CFUNC(f_string_explode, "explode", 1), |
2017 | | CFUNC(f_string_implode, "implode", 1), |
2018 | | CFUNC(f_string_indexes, "_strindices", 2), |
2019 | | CFUNC(f_string_trim, "trim", 1), |
2020 | | CFUNC(f_string_ltrim, "ltrim", 1), |
2021 | | CFUNC(f_string_rtrim, "rtrim", 1), |
2022 | | CFUNC(f_setpath, "setpath", 3), |
2023 | | CFUNC(f_getpath, "getpath", 2), |
2024 | | CFUNC(f_delpaths, "delpaths", 2), |
2025 | | CFUNC(f_has, "has", 2), |
2026 | | CFUNC(f_contains, "contains", 2), |
2027 | | CFUNC(f_length, "length", 1), |
2028 | | CFUNC(f_utf8bytelength, "utf8bytelength", 1), |
2029 | | CFUNC(f_type, "type", 1), |
2030 | | CFUNC(f_isinfinite, "isinfinite", 1), |
2031 | | CFUNC(f_isnan, "isnan", 1), |
2032 | | CFUNC(f_isnormal, "isnormal", 1), |
2033 | | CFUNC(f_infinite, "infinite", 1), |
2034 | | CFUNC(f_nan, "nan", 1), |
2035 | | CFUNC(f_sort, "sort", 1), |
2036 | | CFUNC(f_sort_by_impl, "_sort_by_impl", 2), |
2037 | | CFUNC(f_group_by_impl, "_group_by_impl", 2), |
2038 | | CFUNC(f_unique, "unique", 1), |
2039 | | CFUNC(f_unique_by_impl, "_unique_by_impl", 2), |
2040 | | CFUNC(f_bsearch, "bsearch", 2), |
2041 | | CFUNC(f_min, "min", 1), |
2042 | | CFUNC(f_max, "max", 1), |
2043 | | CFUNC(f_min_by_impl, "_min_by_impl", 2), |
2044 | | CFUNC(f_max_by_impl, "_max_by_impl", 2), |
2045 | | CFUNC(f_error, "error", 1), |
2046 | | CFUNC(f_format, "format", 2), |
2047 | | CFUNC(f_env, "env", 1), |
2048 | | CFUNC(f_halt, "halt", 1), |
2049 | | CFUNC(f_halt_error, "halt_error", 2), |
2050 | | CFUNC(f_get_search_list, "get_search_list", 1), |
2051 | | CFUNC(f_get_prog_origin, "get_prog_origin", 1), |
2052 | | CFUNC(f_get_jq_origin, "get_jq_origin", 1), |
2053 | | CFUNC(f_match, "_match_impl", 4), |
2054 | | CFUNC(f_modulemeta, "modulemeta", 1), |
2055 | | CFUNC(f_input, "input", 1), |
2056 | | CFUNC(f_debug, "debug", 1), |
2057 | | CFUNC(f_stderr, "stderr", 1), |
2058 | | CFUNC(f_fromdateiso8601, "fromdateiso8601", 1), |
2059 | | CFUNC(f_strptime, "strptime", 2), |
2060 | | CFUNC(f_strftime, "strftime", 2), |
2061 | | CFUNC(f_strflocaltime, "strflocaltime", 2), |
2062 | | CFUNC(f_mktime, "mktime", 1), |
2063 | | CFUNC(f_gmtime, "gmtime", 1), |
2064 | | CFUNC(f_localtime, "localtime", 1), |
2065 | | CFUNC(f_now, "now", 1), |
2066 | | CFUNC(f_current_filename, "input_filename", 1), |
2067 | | CFUNC(f_current_line, "input_line_number", 1), |
2068 | | CFUNC(f_have_decnum, "have_decnum", 1), |
2069 | | CFUNC(f_have_decnum, "have_literal_numbers", 1), |
2070 | | }; |
2071 | | #undef LIBM_DDDD_NO |
2072 | | #undef LIBM_DDD_NO |
2073 | | #undef LIBM_DD_NO |
2074 | | #undef LIBM_DA_NO |
2075 | | #undef LIBM_DDDD |
2076 | | #undef LIBM_DDD |
2077 | | #undef LIBM_DD |
2078 | | #undef LIBM_DA |
2079 | | |
2080 | | // This is a hack to make last(g) yield no output values, |
2081 | | // if g yields no output values, without using boxing. |
2082 | 0 | static block gen_last_1(void) { |
2083 | 0 | block last_var = gen_op_var_fresh(STOREV, "last"); |
2084 | 0 | block is_empty_var = gen_op_var_fresh(STOREV, "is_empty"); |
2085 | 0 | block init = BLOCK(gen_op_simple(DUP), |
2086 | 0 | gen_const(jv_null()), |
2087 | 0 | last_var, |
2088 | 0 | gen_op_simple(DUP), |
2089 | 0 | gen_const(jv_true()), |
2090 | 0 | is_empty_var); |
2091 | 0 | block call_arg = BLOCK(gen_call("arg", gen_noop()), |
2092 | 0 | gen_op_simple(DUP), |
2093 | 0 | gen_op_bound(STOREV, last_var), |
2094 | 0 | gen_const(jv_false()), |
2095 | 0 | gen_op_bound(STOREV, is_empty_var), |
2096 | 0 | gen_op_simple(BACKTRACK)); |
2097 | 0 | block if_empty = gen_op_simple(BACKTRACK); |
2098 | 0 | return BLOCK(init, |
2099 | 0 | gen_op_target(FORK, call_arg), |
2100 | 0 | call_arg, |
2101 | 0 | BLOCK(gen_op_bound(LOADVN, is_empty_var), |
2102 | 0 | gen_op_target(JUMP_F, if_empty), |
2103 | 0 | if_empty, |
2104 | 0 | gen_op_bound(LOADVN, last_var))); |
2105 | 0 | } |
2106 | | |
2107 | | struct bytecoded_builtin { const char* name; block code; }; |
2108 | 0 | static block bind_bytecoded_builtins(block b) { |
2109 | 0 | block builtins = gen_noop(); |
2110 | 0 | { |
2111 | 0 | struct bytecoded_builtin builtin_defs[] = { |
2112 | 0 | {"empty", gen_op_simple(BACKTRACK)}, |
2113 | 0 | {"not", gen_condbranch(gen_const(jv_false()), |
2114 | 0 | gen_const(jv_true()))} |
2115 | 0 | }; |
2116 | 0 | for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) { |
2117 | 0 | builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(), |
2118 | 0 | builtin_defs[i].code)); |
2119 | 0 | } |
2120 | 0 | } |
2121 | 0 | { |
2122 | 0 | struct bytecoded_builtin builtin_def_1arg[] = { |
2123 | 0 | {"path", BLOCK(gen_op_simple(PATH_BEGIN), |
2124 | 0 | gen_call("arg", gen_noop()), |
2125 | 0 | gen_op_simple(PATH_END))}, |
2126 | 0 | {"last", gen_last_1()}, |
2127 | 0 | }; |
2128 | 0 | for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) { |
2129 | 0 | builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name, |
2130 | 0 | gen_param("arg"), |
2131 | 0 | builtin_def_1arg[i].code)); |
2132 | 0 | } |
2133 | 0 | } |
2134 | 0 | { |
2135 | 0 | block rangevar = gen_op_var_fresh(STOREV, "rangevar"); |
2136 | 0 | builtins = BLOCK(builtins, |
2137 | 0 | gen_function("range", |
2138 | 0 | BLOCK(gen_param_regular("start"), |
2139 | 0 | gen_param_regular("end"), |
2140 | 0 | gen_param_regular("step")), |
2141 | 0 | BLOCK(gen_op_unbound(LOADV, "step"), |
2142 | 0 | gen_op_simple(DUP), |
2143 | 0 | gen_op_unbound(LOADV, "end"), |
2144 | 0 | gen_op_simple(DUP), |
2145 | 0 | gen_op_unbound(LOADV, "start"), |
2146 | 0 | rangevar, |
2147 | 0 | gen_op_bound(RANGE, rangevar)))); |
2148 | 0 | } |
2149 | 0 | return BLOCK(builtins, b); |
2150 | 0 | } |
2151 | | |
2152 | | static const char jq_builtins[] = { |
2153 | | /* Include jq-coded builtins */ |
2154 | | #include "src/builtin.inc" |
2155 | | '\0', |
2156 | | }; |
2157 | | |
2158 | 0 | static block gen_builtin_list(block builtins) { |
2159 | 0 | jv list = jv_array_append(block_list_funcs(builtins, 1), jv_string("builtins/0")); |
2160 | 0 | return BLOCK(builtins, gen_function("builtins", gen_noop(), gen_const(list))); |
2161 | 0 | } |
2162 | | |
2163 | 0 | int builtins_bind(jq_state *jq, block* bb) { |
2164 | 0 | block builtins; |
2165 | 0 | struct locfile* src = locfile_init(jq, "<builtin>", jq_builtins, sizeof(jq_builtins)-1); |
2166 | 0 | int nerrors = jq_parse_library(src, &builtins); |
2167 | 0 | assert(!nerrors); |
2168 | 0 | locfile_free(src); |
2169 | |
|
2170 | 0 | builtins = bind_bytecoded_builtins(builtins); |
2171 | 0 | builtins = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), builtins); |
2172 | 0 | builtins = gen_builtin_list(builtins); |
2173 | |
|
2174 | 0 | *bb = block_bind_referenced(builtins, *bb, OP_IS_CALL_PSEUDO); |
2175 | 0 | return nerrors; |
2176 | 0 | } |