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