• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib.c

  1 /*
  2  * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 /**
 18  * # Builtin functions
 19  *
 20  * The core namespace is not an actual module but refers to the set of
 21  * builtin functions and properties available to `ucode` scripts.
 22  *
 23  * @module core
 24  */
 25 
 26 #include <stdio.h>
 27 #include <stdlib.h>
 28 #include <stdarg.h>
 29 #include <string.h>
 30 #include <signal.h>
 31 #include <ctype.h>
 32 #include <errno.h>
 33 #include <math.h>
 34 #include <time.h>
 35 #include <dlfcn.h>
 36 #include <libgen.h>
 37 #include <unistd.h>
 38 #include <arpa/inet.h>
 39 #include <sys/stat.h>
 40 #include <sys/types.h>
 41 #include <sys/wait.h>
 42 #include <fnmatch.h>
 43 #include <assert.h>
 44 
 45 #include "json-c-compat.h"
 46 
 47 #include "ucode/lexer.h"
 48 #include "ucode/compiler.h"
 49 #include "ucode/vm.h"
 50 #include "ucode/lib.h"
 51 #include "ucode/source.h"
 52 #include "ucode/program.h"
 53 #include "ucode/platform.h"
 54 
 55 static void
 56 format_context_line(uc_stringbuf_t *buf, const char *line, size_t off, bool compact)
 57 {
 58         unsigned padlen, i;
 59         const char *p;
 60 
 61         for (p = line, padlen = 0; *p != '\n' && *p != '\0'; p++) {
 62                 if (compact && (p - line) == (ptrdiff_t)off)
 63                         ucv_stringbuf_append(buf, "\033[22m");
 64 
 65                 switch (*p) {
 66                 case '\t':
 67                         ucv_stringbuf_append(buf, "    ");
 68                         if (p < line + off)
 69                                 padlen += 4;
 70                         break;
 71 
 72                 case '\r':
 73                 case '\v':
 74                         ucv_stringbuf_append(buf, " ");
 75                         if (p < line + off)
 76                                 padlen++;
 77                         break;
 78 
 79                 default:
 80                         ucv_stringbuf_addstr(buf, p, 1);
 81                         if (p < line + off)
 82                                 padlen++;
 83                 }
 84         }
 85 
 86         if (compact) {
 87                 ucv_stringbuf_append(buf, "\033[m\n");
 88 
 89                 return;
 90         }
 91 
 92         ucv_stringbuf_append(buf, "`\n  ");
 93 
 94         if (padlen < strlen("Near here ^")) {
 95                 for (i = 0; i < padlen; i++)
 96                         ucv_stringbuf_append(buf, " ");
 97 
 98                 ucv_stringbuf_append(buf, "^-- Near here\n");
 99         }
100         else {
101                 ucv_stringbuf_append(buf, "Near here ");
102 
103                 for (i = strlen("Near here "); i < padlen; i++)
104                         ucv_stringbuf_append(buf, "-");
105 
106                 ucv_stringbuf_append(buf, "^\n");
107         }
108 }
109 
110 static char *
111 source_filename(uc_source_t *src, uint32_t line)
112 {
113         const char *name = src->filename ? basename(src->filename) : "[?]";
114         static char buf[sizeof("xxxxxxxxx.uc:0000000000")];
115         size_t len = strlen(name);
116 
117         if (len > 12)
118                 snprintf(buf, sizeof(buf), "...%s:%u", name + (len - 9), line);
119         else
120                 snprintf(buf, sizeof(buf), "%12s:%u", name, line);
121 
122         return buf;
123 }
124 
125 bool
126 uc_source_context_format(uc_stringbuf_t *buf, uc_source_t *src, size_t off, bool compact)
127 {
128         size_t len, rlen;
129         bool truncated;
130         char line[256];
131         long srcpos;
132         int eline;
133 
134         srcpos = ftell(src->fp);
135 
136         if (srcpos == -1)
137                 return false;
138 
139         fseek(src->fp, 0, SEEK_SET);
140 
141         truncated = false;
142         eline = 1;
143         rlen = 0;
144 
145         while (fgets(line, sizeof(line), src->fp)) {
146                 len = strlen(line);
147                 rlen += len;
148 
149                 if (rlen >= off) {
150                         if (compact)
151                                 ucv_stringbuf_printf(buf, "\033[2;40;97m%17s  %s",
152                                         source_filename(src, eline),
153                                         truncated ? "..." : "");
154                         else
155                                 ucv_stringbuf_printf(buf, "\n `%s",
156                                         truncated ? "..." : "");
157 
158                         format_context_line(buf, line, len - (rlen - off) + (truncated ? 3 : 0), compact);
159                         break;
160                 }
161 
162                 truncated = (len > 0 && line[len-1] != '\n');
163                 eline += !truncated;
164         }
165 
166         fseek(src->fp, srcpos, SEEK_SET);
167 
168         return true;
169 }
170 
171 bool
172 uc_error_context_format(uc_stringbuf_t *buf, uc_source_t *src, uc_value_t *stacktrace, size_t off)
173 {
174         uc_value_t *e, *fn, *file, *line, *byte;
175         const char *path;
176         size_t idx;
177 
178         for (idx = 0; idx < (stacktrace ? ucv_array_length(stacktrace) : 0); idx++) {
179                 e = ucv_array_get(stacktrace, idx);
180                 fn = ucv_object_get(e, "function", NULL);
181                 file = ucv_object_get(e, "filename", NULL);
182 
183                 if (idx == 0) {
184                         path = (file && strcmp(ucv_string_get(file), "[stdin]"))
185                                 ? ucv_string_get(file) : NULL;
186 
187                         if (path && fn)
188                                 ucv_stringbuf_printf(buf, "In %s(), file %s, ", ucv_string_get(fn), path);
189                         else if (fn)
190                                 ucv_stringbuf_printf(buf, "In %s(), ", ucv_string_get(fn));
191                         else if (path)
192                                 ucv_stringbuf_printf(buf, "In %s, ", path);
193                         else
194                                 ucv_stringbuf_append(buf, "In ");
195 
196                         ucv_stringbuf_printf(buf, "line %" PRId64 ", byte %" PRId64 ":\n",
197                                 ucv_int64_get(ucv_object_get(e, "line", NULL)),
198                                 ucv_int64_get(ucv_object_get(e, "byte", NULL)));
199                 }
200                 else {
201                         line = ucv_object_get(e, "line", NULL);
202                         byte = ucv_object_get(e, "byte", NULL);
203 
204                         ucv_stringbuf_printf(buf, "  called from %s%s (%s",
205                                 fn ? "function " : "anonymous function",
206                                 fn ? ucv_string_get(fn) : "",
207                                 file ? ucv_string_get(file) : "");
208 
209                         if (line && byte)
210                                 ucv_stringbuf_printf(buf, ":%" PRId64 ":%" PRId64 ")\n",
211                                         ucv_int64_get(line),
212                                         ucv_int64_get(byte));
213                         else
214                                 ucv_stringbuf_append(buf, "[C])\n");
215                 }
216         }
217 
218         return uc_source_context_format(buf, src, off, false);
219 }
220 
221 void
222 uc_error_message_indent(char **msg) {
223         uc_stringbuf_t *buf = xprintbuf_new();
224         char *s, *p, *nl;
225         size_t len;
226 
227         if (!msg || !*msg)
228                 return;
229 
230         s = *msg;
231         len = strlen(s);
232 
233         while (len > 0 && s[len-1] == '\n')
234                 s[--len] = 0;
235 
236         for (p = s, nl = strchr(p, '\n'); p != NULL;
237              p = nl ? nl + 1 : NULL, nl = p ? strchr(p, '\n') : NULL)
238         {
239                 if (!nl)
240                         ucv_stringbuf_printf(buf, "  | %s", p);
241                 else if (nl != p)
242                         ucv_stringbuf_printf(buf, "  | %.*s\n", (int)(nl - p), p);
243                 else
244                         ucv_stringbuf_append(buf, "  |\n");
245         }
246 
247         ucv_stringbuf_append(buf, "\n");
248 
249         *msg = buf->buf;
250 
251         free(buf);
252         free(s);
253 }
254 
255 static char *uc_cast_string(uc_vm_t *vm, uc_value_t **v, bool *freeable) {
256         if (ucv_type(*v) == UC_STRING) {
257                 *freeable = false;
258 
259                 return _ucv_string_get(v);
260         }
261 
262         *freeable = true;
263 
264         return ucv_to_string(vm, *v);
265 }
266 
267 static void
268 uc_vm_ctx_push(uc_vm_t *vm)
269 {
270         uc_value_t *ctx = NULL;
271 
272         if (vm->callframes.count >= 2)
273                 ctx = vm->callframes.entries[vm->callframes.count - 2].ctx;
274 
275         uc_vm_stack_push(vm, ucv_get(ctx));
276 }
277 
278 static uc_value_t *
279 uc_print_common(uc_vm_t *vm, size_t nargs, FILE *fh)
280 {
281         uc_value_t *item;
282         size_t reslen = 0;
283         size_t len = 0;
284         size_t arridx;
285         char *p;
286 
287         for (arridx = 0; arridx < nargs; arridx++) {
288                 item = uc_fn_arg(arridx);
289 
290                 if (ucv_type(item) == UC_STRING) {
291                         len = ucv_string_length(item);
292                         reslen += fwrite(ucv_string_get(item), 1, len, fh);
293                 }
294                 else if (item != NULL) {
295                         p = ucv_to_string(vm, item);
296                         len = strlen(p);
297                         reslen += fwrite(p, 1, len, fh);
298                         free(p);
299                 }
300         }
301 
302         return ucv_int64_new(reslen);
303 }
304 
305 
306 /**
307  * Print any of the given values to stdout.
308  *
309  * The `print()` function writes a string representation of each given argument
310  * to stdout and returns the amount of bytes written.
311  *
312  * String values are printed as-is, integer and double values are printed in
313  * decimal notation, boolean values are printed as `true` or `false` while
314  * arrays and objects are converted to their JSON representation before being
315  * written to the standard output. The `null` value is represented by an empty
316  * string so `print(null)` would print nothing. Resource values are printed in
317  * the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`.
318  *
319  * If resource, array or object values contain a `tostring()` function in their
320  * prototypes, then this function is invoked to obtain an alternative string
321  * representation of the value.
322  *
323  * Examples:
324  *
325  * ```javascript
326  * print(1 != 2);                       // Will print 'true'
327  * print(0xff);                         // Will print '255'
328  * print(2e3);                          // Will print '2000'
329  * print(null);                         // Will print nothing
330  * print({ hello: true, world: 123 });  // Will print '{ "hello": true, "world": 123 }'
331  * print([1,2,3]);                      // Will print '[ 1, 2, 3 ]'
332  *
333  * print(proto({ foo: "bar" },          // Will print 'MyObj'
334  *   { tostring: () => "MyObj" }));     // instead of '{ "foo": "bar" }'
335  *
336  * ```
337  *
338  * Returns the amount of bytes printed.
339  *
340  * @function module:core#print
341  *
342  * @param {...*} values
343  * Arbitrary values to print
344  *
345  * @returns {number}
346  */
347 static uc_value_t *
348 uc_print(uc_vm_t *vm, size_t nargs)
349 {
350         return uc_print_common(vm, nargs, vm->output);
351 }
352 
353 /**
354  * Determine the length of the given object, array or string.
355  *
356  * Returns the length of the given value.
357  *
358  *  - For strings, the length is the amount of bytes within the string
359  *  - For arrays, the length is the amount of array elements
360  *  - For objects, the length is defined as the amount of keys
361  *
362  * Returns `null` if the given argument is not an object, array or string.
363  *
364  * @function module:core#length
365  *
366  * @param {Object|Array|string} x - The input object, array, or string.
367  *
368  * @returns {?number} - The length of the input.
369  *
370  * @example
371  * length("test")                             // 4
372  * length([true, false, null, 123, "test"])   // 5
373  * length({foo: true, bar: 123, baz: "test"}) // 3
374  * length({})                                 // 0
375  * length(true)                               // null
376  * length(10.0)                               // null
377  */
378 static uc_value_t *
379 uc_length(uc_vm_t *vm, size_t nargs)
380 {
381         uc_value_t *arg = uc_fn_arg(0);
382 
383         switch (ucv_type(arg)) {
384         case UC_OBJECT:
385                 return ucv_int64_new(ucv_object_length(arg));
386 
387         case UC_ARRAY:
388                 return ucv_int64_new(ucv_array_length(arg));
389 
390         case UC_STRING:
391                 return ucv_int64_new(ucv_string_length(arg));
392 
393         default:
394                 return NULL;
395         }
396 }
397 
398 static int
399 uc_uniq_ucv_equal(const void *k1, const void *k2);
400 
401 static uc_value_t *
402 uc_index(uc_vm_t *vm, size_t nargs, bool right)
403 {
404         uc_value_t *stack = uc_fn_arg(0);
405         uc_value_t *needle = uc_fn_arg(1);
406         const char *sstr, *nstr, *p;
407         size_t arridx, slen, nlen;
408         ssize_t ret = -1;
409 
410         switch (ucv_type(stack)) {
411         case UC_ARRAY:
412                 if (right) {
413                         for (arridx = ucv_array_length(stack); arridx > 0; arridx--) {
414                                 if (uc_uniq_ucv_equal(ucv_array_get(stack, arridx - 1), needle)) {
415                                         ret = (ssize_t)(arridx - 1);
416                                         break;
417                                 }
418                         }
419                 }
420                 else {
421                         for (arridx = 0, slen = ucv_array_length(stack); arridx < slen; arridx++) {
422                                 if (uc_uniq_ucv_equal(ucv_array_get(stack, arridx), needle)) {
423                                         ret = (ssize_t)arridx;
424                                         break;
425                                 }
426                         }
427                 }
428 
429                 return ucv_int64_new(ret);
430 
431         case UC_STRING:
432                 if (ucv_type(needle) == UC_STRING) {
433                         sstr = ucv_string_get(stack);
434                         slen = ucv_string_length(stack);
435                         nstr = ucv_string_get(needle);
436                         nlen = ucv_string_length(needle);
437 
438                         if (slen == nlen) {
439                                 if (memcmp(sstr, nstr, nlen) == 0)
440                                         ret = 0;
441                         }
442                         else if (slen > nlen) {
443                                 if (right) {
444                                         p = sstr + slen - nlen;
445 
446                                         do {
447                                                 if (memcmp(p, nstr, nlen) == 0) {
448                                                         ret = (ssize_t)(p - sstr);
449                                                         break;
450                                                 }
451                                         }
452                                         while (p-- != sstr);
453                                 }
454                                 else if (nlen > 0) {
455                                         p = (const char *)memmem(sstr, slen, nstr, nlen);
456 
457                                         if (p)
458                                                 ret = (ssize_t)(p - sstr);
459                                 }
460                                 else {
461                                         ret = 0;
462                                 }
463                         }
464                 }
465 
466                 return ucv_int64_new(ret);
467 
468         default:
469                 return NULL;
470         }
471 }
472 
473 /**
474  * Finds the given value passed as the second argument within the array or
475  * string specified in the first argument.
476  *
477  * Returns the first matching array index or first matching string offset or
478  * `-1` if the value was not found.
479  *
480  * Returns `null` if the first argument was neither an array nor a string.
481  *
482  * @function module:core#index
483  *
484  * @param {Array|string} arr_or_str
485  * The array or string to search for the value.
486  *
487  * @param {*} needle
488  * The value to find within the array or string.
489  *
490  * @returns {?number}
491  *
492  * @example
493  * index("Hello hello hello", "ll")          // 2
494  * index([ 1, 2, 3, 1, 2, 3, 1, 2, 3 ], 2)   // 1
495  * index("foo", "bar")                       // -1
496  * index(["Red", "Blue", "Green"], "Brown")  // -1
497  * index(123, 2)                             // null
498  */
499 static uc_value_t *
500 uc_lindex(uc_vm_t *vm, size_t nargs)
501 {
502         return uc_index(vm, nargs, false);
503 }
504 
505 /**
506  * Finds the given value passed as the second argument within the array or
507  * string specified in the first argument.
508  *
509  * Returns the last matching array index or last matching string offset or
510  * `-1` if the value was not found.
511  *
512  * Returns `null` if the first argument was neither an array nor a string.
513  *
514  * @function module:core#rindex
515  *
516  * @param {Array|string} arr_or_str
517  * The array or string to search for the value.
518  *
519  * @param {*} needle
520  * The value to find within the array or string.
521  *
522  * @returns {?number}
523  *
524  * @example
525  * rindex("Hello hello hello", "ll")          // 14
526  * rindex([ 1, 2, 3, 1, 2, 3, 1, 2, 3 ], 2)   //  7
527  * rindex("foo", "bar")                       // -1
528  * rindex(["Red", "Blue", "Green"], "Brown")  // -1
529  * rindex(123, 2)                             // null
530  */
531 static uc_value_t *
532 uc_rindex(uc_vm_t *vm, size_t nargs)
533 {
534         return uc_index(vm, nargs, true);
535 }
536 
537 static bool
538 assert_mutable(uc_vm_t *vm, uc_value_t *val)
539 {
540         if (ucv_is_constant(val)) {
541                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
542                                       "%s value is immutable",
543                                       ucv_typename(val));
544 
545                 return false;
546         }
547 
548         return true;
549 }
550 
551 static bool
552 assert_mutable_array(uc_vm_t *vm, uc_value_t *val)
553 {
554         if (ucv_type(val) != UC_ARRAY)
555                 return false;
556 
557         return assert_mutable(vm, val);
558 }
559 
560 /**
561  * Pushes the given argument(s) to the given array.
562  *
563  * Returns the last pushed value.
564  *
565  * @function module:core#push
566  *
567  * @param {Array} arr
568  * The array to push values to.
569  *
570  * @param {...*} [values]
571  * The values to push.
572  *
573  * @returns {*}
574  *
575  * @example
576  * let x = [ 1, 2, 3 ];
577  * push(x, 4, 5, 6);    // 6
578  * print(x, "\n");      // [ 1, 2, 3, 4, 5, 6 ]
579  */
580 static uc_value_t *
581 uc_push(uc_vm_t *vm, size_t nargs)
582 {
583         uc_value_t *arr = uc_fn_arg(0);
584         uc_value_t *item = NULL;
585         size_t arridx;
586 
587         if (!assert_mutable_array(vm, arr))
588                 return NULL;
589 
590         for (arridx = 1; arridx < nargs; arridx++) {
591                 item = uc_fn_arg(arridx);
592                 ucv_array_push(arr, ucv_get(item));
593         }
594 
595         return ucv_get(item);
596 }
597 
598 /**
599  * Pops the last item from the given array and returns it.
600  *
601  * Returns `null` if the array was empty or if a non-array argument was passed.
602  *
603  * @function module:core#pop
604  *
605  * @param {Array} arr
606  * The input array.
607  *
608  * @returns {*}
609  *
610  * @example
611  * let x = [ 1, 2, 3 ];
612  * pop(x);          // 3
613  * print(x, "\n");  // [ 1, 2 ]
614  */
615 static uc_value_t *
616 uc_pop(uc_vm_t *vm, size_t nargs)
617 {
618         uc_value_t *arr = uc_fn_arg(0);
619 
620         if (!assert_mutable_array(vm, arr))
621                 return NULL;
622 
623         return ucv_array_pop(arr);
624 }
625 
626 /**
627  * Pops the first item from the given array and returns it.
628  *
629  * Returns `null` if the array was empty or if a non-array argument was passed.
630  *
631  * @function module:core#shift
632  *
633  * @param {Array} arr
634  * The array from which to pop the first item.
635  *
636  * @returns {*}
637  *
638  * @example
639  * let x = [ 1, 2, 3 ];
640  * shift(x);        // 1
641  * print(x, "\n");  // [ 2, 3 ]
642  */
643 static uc_value_t *
644 uc_shift(uc_vm_t *vm, size_t nargs)
645 {
646         uc_value_t *arr = uc_fn_arg(0);
647 
648         if (!assert_mutable_array(vm, arr))
649                 return NULL;
650 
651         return ucv_array_shift(arr);
652 }
653 
654 /**
655  * Add the given values to the beginning of the array passed via first argument.
656  *
657  * Returns the last value added to the array.
658  *
659  * @function module:core#unshift
660  *
661  * @param {Array} arr
662  * The array to which the values will be added.
663  *
664  * @param {...*}
665  * Values to add.
666  *
667  * @returns {*}
668  *
669  * @example
670  * let x = [ 3, 4, 5 ];
671  * unshift(x, 1, 2);  // 2
672  * print(x, "\n");    // [ 1, 2, 3, 4, 5 ]
673  */
674 static uc_value_t *
675 uc_unshift(uc_vm_t *vm, size_t nargs)
676 {
677         uc_value_t *arr = uc_fn_arg(0);
678         uc_value_t *item;
679         size_t i;
680 
681         if (!assert_mutable_array(vm, arr))
682                 return NULL;
683 
684         for (i = 1; i < nargs; i++) {
685                 item = uc_fn_arg(nargs - i);
686                 ucv_array_unshift(arr, ucv_get(item));
687         }
688 
689         return (nargs > 1) ? ucv_get(uc_fn_arg(nargs - 1)) : NULL;
690 }
691 
692 /**
693  * Converts each given numeric value to a byte and return the resulting string.
694  * Invalid numeric values or values < 0 result in `\0` bytes, values larger than
695  * 255 are truncated to 255.
696  *
697  * Returns a new strings consisting of the given byte values.
698  *
699  * @function module:core#chr
700  *
701  * @param {...number} n1
702  * The numeric values.
703  *
704  * @returns {string}
705  *
706  * @example
707  * chr(65, 98, 99);  // "Abc"
708  * chr(-1, 300);     // string consisting of an `0x0` and a `0xff` byte
709  */
710 static uc_value_t *
711 uc_chr(uc_vm_t *vm, size_t nargs)
712 {
713         uc_value_t *rv = NULL;
714         size_t idx;
715         int64_t n;
716         char *str;
717 
718         if (!nargs)
719                 return ucv_string_new_length("", 0);
720 
721         str = xalloc(nargs);
722 
723         for (idx = 0; idx < nargs; idx++) {
724                 n = ucv_to_integer(uc_fn_arg(idx));
725 
726                 if (n < 0)
727                         n = 0;
728                 else if (n > 255)
729                         n = 255;
730 
731                 str[idx] = (char)n;
732         }
733 
734         rv = ucv_string_new_length(str, nargs);
735         free(str);
736 
737         return rv;
738 }
739 
740 /**
741  * Raise an exception with the given message and abort execution.
742  *
743  * @function module:core#die
744  *
745  * @param {string} msg
746  * The error message.
747  *
748  * @throws {Error}
749  * The error with the given message.
750  *
751  * @example
752  * die(msg);
753  */
754 static uc_value_t *
755 uc_die(uc_vm_t *vm, size_t nargs)
756 {
757         uc_value_t *msg = uc_fn_arg(0);
758         bool freeable = false;
759         char *s;
760 
761         s = msg ? uc_cast_string(vm, &msg, &freeable) : "Died";
762 
763         uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s);
764 
765         if (freeable)
766                 free(s);
767 
768         return NULL;
769 }
770 
771 /**
772  * Check whether the given key exists within the given object value.
773  *
774  * Returns `true` if the given key is present within the object passed as the
775  * first argument, otherwise `false`.
776  *
777  * @function module:core#exists
778  *
779  * @param {Object} obj
780  * The input object.
781  *
782  * @param {string} key
783  * The key to check for existence.
784  *
785  * @returns {boolean}
786  *
787  * @example
788  * let x = { foo: true, bar: false, qrx: null };
789  * exists(x, 'foo');  // true
790  * exists(x, 'qrx');  // true
791  * exists(x, 'baz');  // false
792  */
793 static uc_value_t *
794 uc_exists(uc_vm_t *vm, size_t nargs)
795 {
796         uc_value_t *obj = uc_fn_arg(0);
797         uc_value_t *key = uc_fn_arg(1);
798         bool found, freeable;
799         char *k;
800 
801         if (ucv_type(obj) != UC_OBJECT)
802                 return ucv_boolean_new(false);
803 
804         k = uc_cast_string(vm, &key, &freeable);
805 
806         ucv_object_get(obj, k, &found);
807 
808         if (freeable)
809                 free(k);
810 
811         return ucv_boolean_new(found);
812 }
813 
814 /**
815  * Terminate the interpreter with the given exit code.
816  *
817  * This function does not return.
818  *
819  * @function module:core#exit
820  *
821  * @param {number} n
822  * The exit code.
823  *
824  * @example
825  * exit();
826  * exit(5);
827  */
828 static uc_value_t *
829 uc_exit(uc_vm_t *vm, size_t nargs)
830 {
831         int64_t n = ucv_to_integer(uc_fn_arg(0));
832 
833         vm->arg.s32 = (int32_t)n;
834         uc_vm_raise_exception(vm, EXCEPTION_EXIT, "Terminated");
835 
836         return NULL;
837 }
838 
839 /**
840  * Query an environment variable or then entire environment.
841  *
842  * Returns the value of the given environment variable, or - if omitted - a
843  * dictionary containing all environment variables.
844  *
845  * @function module:core#getenv
846  *
847  * @param {string} [name]
848  * The name of the environment variable.
849  *
850  * @returns {string|Object<string, string>}
851  */
852 static uc_value_t *
853 uc_getenv(uc_vm_t *vm, size_t nargs)
854 {
855         uc_value_t *key = uc_fn_arg(0), *rv = NULL;
856         extern char **environ;
857         char **env = environ;
858         char *k, *v;
859 
860         if (!key) {
861                 rv = ucv_object_new(vm);
862 
863                 while (*env) {
864                         v = strchr(*env, '=');
865 
866                         if (v) {
867                                 xasprintf(&k, "%.*s", (int)(v - *env), *env);
868                                 ucv_object_add(rv, k, ucv_string_new(v + 1));
869                                 free(k);
870                         }
871 
872                         env++;
873                 }
874         }
875         else if (ucv_type(key) == UC_STRING) {
876                 k = ucv_string_get(key);
877                 v = getenv(k);
878 
879                 if (v)
880                         rv = ucv_string_new(v);
881         }
882 
883         return rv;
884 }
885 
886 /**
887  * Filter the array passed as the first argument by invoking the function
888  * specified in the second argument for each array item.
889  *
890  * If the invoked function returns a truthy result, the item is retained,
891  * otherwise, it is dropped. The filter function is invoked with three
892  * arguments:
893  *
894  * 1. The array value
895  * 2. The current index
896  * 3. The array being filtered
897  *
898  * (Note that the `map` function behaves similarly to `filter` with respect
899  * to its `fn` parameters.)
900  *
901  * Returns a new array containing only retained items, in the same order as
902  * the input array.
903  *
904  * @function module:core#filter
905  *
906  * @param {Array} arr
907  * The input array.
908  *
909  * @param {Function} fn
910  * The filter function.
911  *
912  * @returns {Array}
913  *
914  * @example
915  * // filter out any empty string:
916  * a = filter(["foo", "", "bar", "", "baz"], length)
917  * // a = ["foo", "bar", "baz"]
918  *
919  * // filter out any non-number type:
920  * a = filter(["foo", 1, true, null, 2.2], function(v) {
921  *     return (type(v) == "int" || type(v) == "double");
922  * });
923  * // a = [1, 2.2]
924  */
925 static uc_value_t *
926 uc_filter(uc_vm_t *vm, size_t nargs)
927 {
928         uc_value_t *obj = uc_fn_arg(0);
929         uc_value_t *func = uc_fn_arg(1);
930         uc_value_t *rv, *arr;
931         size_t arridx, arrlen;
932 
933         if (ucv_type(obj) != UC_ARRAY)
934                 return NULL;
935 
936         arr = ucv_array_new(vm);
937 
938         for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) {
939                 uc_vm_ctx_push(vm);
940                 uc_vm_stack_push(vm, ucv_get(func));
941                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx)));
942                 uc_vm_stack_push(vm, ucv_int64_new(arridx));
943                 uc_vm_stack_push(vm, ucv_get(obj));
944 
945                 if (uc_vm_call(vm, true, 3)) {
946                         ucv_put(arr);
947 
948                         return NULL;
949                 }
950 
951                 rv = uc_vm_stack_pop(vm);
952 
953                 if (ucv_is_truish(rv))
954                         ucv_array_push(arr, ucv_get(ucv_array_get(obj, arridx)));
955 
956                 ucv_put(rv);
957         }
958 
959         return arr;
960 }
961 
962 /**
963  * Converts the given hexadecimal string into a number.
964  *
965  * Returns the resulting integer value or `NaN` if the input value cannot be
966  * interpreted as hexadecimal number.
967  *
968  * @function module:core#hex
969  *
970  * @param {*} x
971  * The hexadecimal string to be converted.
972  *
973  * @returns {number}
974  */
975 static uc_value_t *
976 uc_hex(uc_vm_t *vm, size_t nargs)
977 {
978         uc_value_t *val = uc_fn_arg(0);
979         char *e, *v;
980         int64_t n;
981 
982         v = ucv_string_get(val);
983 
984         if (!v || !isxdigit(*v))
985                 return ucv_double_new(NAN);
986 
987         n = strtoll(v, &e, 16);
988 
989         if (e == v || *e)
990                 return ucv_double_new(NAN);
991 
992         return ucv_int64_new(n);
993 }
994 
995 /**
996  * Converts the given value to an integer, using an optional base.
997  *
998  * Returns `NaN` if the value is not convertible.
999  *
1000  * @function module:core#int
1001  *
1002  * @param {*} x
1003  * The value to be converted to an integer.
1004  *
1005  * @param {int} [base]
1006  * The base into which the value is to be converted, the default is 10.
1007  * Note that the base parameter is ignored if the `x` value is already numeric.
1008  *
1009  * @returns {number}
1010  *
1011  * @example
1012  * int("123")         // Returns 123
1013  * int("123", 10)     // 123
1014  * int("10 or more")  // 10
1015  * int("12.3")        // 12
1016  * int("123", 7)      // 66
1017  * int("abc", 16)     // 2748
1018  * int("xyz", 36)     // 44027
1019  * int(10.10, "2")    // 10, the invalid base is ignored
1020  * int("xyz", 16)     // NaN, bad value
1021  * int("1010", "2")   // NaN, bad base
1022  */
1023 static uc_value_t *
1024 uc_int(uc_vm_t *vm, size_t nargs)
1025 {
1026         uc_value_t *val = uc_fn_arg(0);
1027         uc_value_t *base = uc_fn_arg(1);
1028         char *e, *v;
1029         int64_t n;
1030 
1031         if (ucv_type(val) == UC_STRING) {
1032                 errno = 0;
1033                 v = ucv_string_get(val);
1034                 n = strtoll(v, &e, base ? ucv_int64_get(base) : 10);
1035 
1036                 if (e == v)
1037                         return ucv_double_new(NAN);
1038         }
1039         else {
1040                 n = ucv_to_integer(val);
1041         }
1042 
1043         if (errno == EINVAL || errno == ERANGE)
1044                 return ucv_double_new(NAN);
1045 
1046         return ucv_int64_new(n);
1047 }
1048 
1049 /**
1050  * Joins the array passed as the second argument into a string, using the
1051  * separator passed in the first argument as glue.
1052  *
1053  * Returns `null` if the second argument is not an array.
1054  *
1055  * @function module:core#join
1056  *
1057  * @param {string} sep
1058  * The separator to be used in joining the array elements.
1059  *
1060  * @param {Array} arr
1061  * The array to be joined into a string.
1062  *
1063  * @returns {?string}
1064  */
1065 static uc_value_t *
1066 uc_join(uc_vm_t *vm, size_t nargs)
1067 {
1068         uc_value_t *sep = uc_fn_arg(0);
1069         uc_value_t *arr = uc_fn_arg(1);
1070         size_t arrlen, arridx;
1071         uc_stringbuf_t *buf;
1072 
1073         if (ucv_type(arr) != UC_ARRAY)
1074                 return NULL;
1075 
1076         buf = ucv_stringbuf_new();
1077 
1078         for (arrlen = ucv_array_length(arr), arridx = 0; arridx < arrlen; arridx++) {
1079                 if (arridx > 0)
1080                         ucv_to_stringbuf(vm, buf, sep, false);
1081 
1082                 ucv_to_stringbuf(vm, buf, ucv_array_get(arr, arridx), false);
1083         }
1084 
1085         return ucv_stringbuf_finish(buf);
1086 }
1087 
1088 /**
1089  * Enumerates all object key names.
1090  *
1091  * Returns an array of all key names present in the passed object.
1092  * Returns `null` if the given argument is not an object.
1093  *
1094  * @function module:core#keys
1095  *
1096  * @param {object} obj
1097  * The object from which to retrieve the key names.
1098  *
1099  * @returns {?Array}
1100  */
1101 static uc_value_t *
1102 uc_keys(uc_vm_t *vm, size_t nargs)
1103 {
1104         uc_value_t *obj = uc_fn_arg(0);
1105         uc_value_t *arr = NULL;
1106 
1107         if (ucv_type(obj) != UC_OBJECT)
1108                 return NULL;
1109 
1110         arr = ucv_array_new(vm);
1111 
1112         ucv_object_foreach(obj, key, val) {
1113                 (void)val;
1114                 ucv_array_push(arr, ucv_string_new(key));
1115         }
1116 
1117         return arr;
1118 }
1119 
1120 /**
1121  * Convert the given string to lowercase and return the resulting string.
1122  *
1123  * Returns `null` if the given argument could not be converted to a string.
1124  *
1125  * @function module:core#lc
1126  *
1127  * @param {string} s
1128  * The input string.
1129  *
1130  * @returns {?string}
1131  * The lowercase string.
1132  *
1133  * @example
1134  * lc("HeLLo WoRLd!");  // "hello world!"
1135  */
1136 static uc_value_t *
1137 uc_lc(uc_vm_t *vm, size_t nargs)
1138 {
1139         char *str = ucv_to_string(vm, uc_fn_arg(0));
1140         uc_value_t *rv = NULL;
1141         char *p;
1142 
1143         if (!str)
1144                 return NULL;
1145 
1146         for (p = str; *p; p++)
1147                 if (*p >= 'A' && *p <= 'Z')
1148                         *p |= 32;
1149 
1150         rv = ucv_string_new(str);
1151 
1152         free(str);
1153 
1154         return rv;
1155 }
1156 
1157 /**
1158  * Transform the array passed as the first argument by invoking the function
1159  * specified in the second argument for each array item.
1160  *
1161  * The mapping function is invoked with three arguments (see examples, below,
1162  * for some possibly counterintuitive usage):
1163  *
1164  * 1. The array value
1165  * 2. The current index
1166  * 3. The array being filtered
1167  *
1168  * (Note that the `filter` function behaves similarly to `map` with respect
1169  * to its `fn` parameters.)
1170  *
1171  * Returns a new array of the same length as the input array containing the
1172  * transformed values.
1173  *
1174  * @function module:core#map
1175  *
1176  * @param {Array} arr
1177  * The input array.
1178  *
1179  * @param {Function} fn
1180  * The mapping function.
1181  *
1182  * @returns {Array}
1183  *
1184  * @example
1185  * // turn into an array of string lengths:
1186  * a = map(["Apple", "Banana", "Bean"], length);
1187  * // a = [5, 6, 4]
1188  *
1189  * // map to type names:
1190  * a = map(["foo", 1, true, null, 2.2], type);
1191  * // a = ["string", "int", "bool", null, "double"]
1192  *
1193  * // attempt to naively use built-in 'int' to map an array:
1194  * a = map(["x", "2", "11", "7"], int)
1195  * // a = [NaN, NaN, 3, NaN]
1196  * //
1197  * // This is a direct result of 'int' being provided the second, index parameter
1198  * // for its base value in the conversion.
1199  * //
1200  * // The resulting calls to 'int' are as follows:
1201  * //  int("x",  0, [...]) - convert "x"  to base 0, 'int' ignores the third value
1202  * //  int("2",  1, [...]) - convert "2"  to base 1, digit out of range, so NaN
1203  * //  int("11", 2, [...]) - convert "11" to base 2, produced unexpected 3
1204  * //  int("7",  3, [...]) - convert "7"  to base 3, digit out of range, NaN again
1205  *
1206  * // remedy this by using an arrow function to ensure the proper base value
1207  * // (in this case, the default of 10) is passed to 'int':
1208  * a = map(["x", "2", "1", "7"], (x) => int(x))
1209  * // a = [NaN, 2, 1, 7]
1210  *
1211  * // convert base-2 values:
1212  * a = map(["22", "1010", "0001", "0101"], (x) => int(x, 2))
1213  * // a = [NaN, 10, 1, 5]
1214  */
1215 static uc_value_t *
1216 uc_map(uc_vm_t *vm, size_t nargs)
1217 {
1218         uc_value_t *obj = uc_fn_arg(0);
1219         uc_value_t *func = uc_fn_arg(1);
1220         uc_value_t *arr, *rv;
1221         size_t arridx, arrlen;
1222 
1223         if (ucv_type(obj) != UC_ARRAY)
1224                 return NULL;
1225 
1226         arr = ucv_array_new(vm);
1227 
1228         for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) {
1229                 uc_vm_ctx_push(vm);
1230                 uc_vm_stack_push(vm, ucv_get(func));
1231                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx)));
1232                 uc_vm_stack_push(vm, ucv_int64_new(arridx));
1233                 uc_vm_stack_push(vm, ucv_get(obj));
1234 
1235                 if (uc_vm_call(vm, true, 3)) {
1236                         ucv_put(arr);
1237 
1238                         return NULL;
1239                 }
1240 
1241                 rv = uc_vm_stack_pop(vm);
1242 
1243                 ucv_array_push(arr, rv);
1244         }
1245 
1246         return arr;
1247 }
1248 
1249 /**
1250  * Without further arguments, this function returns the byte value of the first
1251  * character in the given string.
1252  *
1253  * If an offset argument is supplied, the byte value of the character at this
1254  * position is returned. If an invalid index is supplied, the function will
1255  * return `null`. Negative index entries are counted towards the end of the
1256  * string, e.g. `-2` will return the value of the second last character.
1257  *
1258  * Returns the byte value of the character.
1259  * Returns `null` if the offset is invalid or if the input is not a string.
1260  *
1261  * @function module:core#ord
1262  *
1263  * @param {string} s
1264  * The input string.
1265  *
1266  * @param {number} [offset]
1267  * The offset of the character.
1268  *
1269  * @returns {?number}
1270  *
1271  * @example
1272  * ord("Abc");         // 65
1273  * ord("Abc", 0);      // 65
1274  * ord("Abc", 1);      // 98
1275  * ord("Abc", 2);      // 99
1276  * ord("Abc", 10);     // null
1277  * ord("Abc", -10);    // null
1278  * ord("Abc", "nan");  // null
1279  */
1280 static uc_value_t *
1281 uc_ord(uc_vm_t *vm, size_t nargs)
1282 {
1283         uc_value_t *obj = uc_fn_arg(0);
1284         const char *str;
1285         int64_t n = 0;
1286         size_t len;
1287 
1288         if (ucv_type(obj) != UC_STRING)
1289                 return NULL;
1290 
1291         str = ucv_string_get(obj);
1292         len = ucv_string_length(obj);
1293 
1294         if (nargs > 1) {
1295                 n = ucv_int64_get(uc_fn_arg(1));
1296 
1297                 if (errno == EINVAL)
1298                         return NULL;
1299 
1300                 if (n < 0)
1301                         n += len;
1302         }
1303 
1304         if (n < 0 || (uint64_t)n >= len)
1305                 return NULL;
1306 
1307         return ucv_int64_new((uint8_t)str[n]);
1308 }
1309 
1310 /**
1311  * Query the type of the given value.
1312  *
1313  * Returns the type of the given value as a string which might be one of
1314  * `"function"`, `"object"`, `"array"`, `"double"`, `"int"`, or `"bool"`.
1315  *
1316  * Returns `null` when no value or `null` is passed.
1317  *
1318  * @function module:core#type
1319  *
1320  * @param {*} x
1321  * The value to determine the type of.
1322  *
1323  * @returns {?string}
1324  */
1325 static uc_value_t *
1326 uc_type(uc_vm_t *vm, size_t nargs)
1327 {
1328         uc_value_t *v = uc_fn_arg(0);
1329         uc_type_t t = ucv_type(v);
1330 
1331         switch (t) {
1332         case UC_CFUNCTION:
1333         case UC_CLOSURE:
1334                 return ucv_string_new("function");
1335 
1336         case UC_INTEGER:
1337                 return ucv_string_new("int");
1338 
1339         case UC_BOOLEAN:
1340                 return ucv_string_new("bool");
1341 
1342         case UC_NULL:
1343                 return NULL;
1344 
1345         default:
1346                 return ucv_string_new(ucv_typename(v));
1347         }
1348 }
1349 
1350 /**
1351  * Reverse the order of the given input array or string.
1352  *
1353  * If an array is passed, returns the array in reverse order.
1354  * If a string is passed, returns the string with the sequence of the characters
1355  * reversed.
1356  *
1357  * Returns the reversed array or string.
1358  * Returns `null` if neither an array nor a string were passed.
1359  *
1360  * @function module:core#reverse
1361  *
1362  * @param {Array|string} arr_or_str
1363  * The input array or string.
1364  *
1365  * @returns {?(Array|string)}
1366  *
1367  * @example
1368  * reverse([1, 2, 3]);   // [ 3, 2, 1 ]
1369  * reverse("Abc");       // "cbA"
1370  */
1371 static uc_value_t *
1372 uc_reverse(uc_vm_t *vm, size_t nargs)
1373 {
1374         uc_value_t *obj = uc_fn_arg(0);
1375         uc_value_t *rv = NULL;
1376         size_t len, arridx;
1377         const char *str;
1378         char *dup, *p;
1379 
1380         if (ucv_type(obj) == UC_ARRAY) {
1381                 if (!assert_mutable_array(vm, obj))
1382                         return NULL;
1383 
1384                 rv = ucv_array_new(vm);
1385 
1386                 for (arridx = ucv_array_length(obj); arridx > 0; arridx--)
1387                         ucv_array_push(rv, ucv_get(ucv_array_get(obj, arridx - 1)));
1388         }
1389         else if (ucv_type(obj) == UC_STRING) {
1390                 len = ucv_string_length(obj);
1391                 str = ucv_string_get(obj);
1392                 p = dup = xalloc(len + 1);
1393 
1394                 while (len > 0)
1395                         *p++ = str[--len];
1396 
1397                 rv = ucv_string_new(dup);
1398 
1399                 free(dup);
1400         }
1401 
1402         return rv;
1403 }
1404 
1405 
1406 typedef struct {
1407         uc_vm_t *vm;
1408         bool ex;
1409         uc_value_t *fn;
1410 } sort_ctx_t;
1411 
1412 static int
1413 default_cmp(uc_value_t *v1, uc_value_t *v2, uc_vm_t *vm)
1414 {
1415         char *s1, *s2;
1416         bool f1, f2;
1417         int res;
1418 
1419         /* when both operands are numeric then compare numerically */
1420         if ((ucv_type(v1) == UC_INTEGER || ucv_type(v1) == UC_DOUBLE) &&
1421             (ucv_type(v2) == UC_INTEGER || ucv_type(v2) == UC_DOUBLE)) {
1422                 ucv_compare(0, v1, v2, &res);
1423 
1424                 return res;
1425         }
1426 
1427         /* otherwise convert both operands to strings and compare lexically */
1428         s1 = uc_cast_string(vm, &v1, &f1);
1429         s2 = uc_cast_string(vm, &v2, &f2);
1430 
1431         res = strcmp(s1, s2);
1432 
1433         if (f1) free(s1);
1434         if (f2) free(s2);
1435 
1436         return res;
1437 }
1438 
1439 static int
1440 array_sort_fn(uc_value_t *v1, uc_value_t *v2, void *ud)
1441 {
1442         uc_value_t *rv, *null = ucv_int64_new(0);
1443         sort_ctx_t *ctx = ud;
1444         int res;
1445 
1446         if (!ctx->fn)
1447                 return default_cmp(v1, v2, ctx->vm);
1448 
1449         if (ctx->ex)
1450                 return 0;
1451 
1452         uc_vm_ctx_push(ctx->vm);
1453         uc_vm_stack_push(ctx->vm, ucv_get(ctx->fn));
1454         uc_vm_stack_push(ctx->vm, ucv_get(v1));
1455         uc_vm_stack_push(ctx->vm, ucv_get(v2));
1456 
1457         if (uc_vm_call(ctx->vm, true, 2)) {
1458                 ctx->ex = true;
1459 
1460                 return 0;
1461         }
1462 
1463         rv = uc_vm_stack_pop(ctx->vm);
1464 
1465         ucv_compare(0, rv, null, &res);
1466 
1467         ucv_put(null);
1468         ucv_put(rv);
1469 
1470         return res;
1471 }
1472 
1473 static int
1474 object_sort_fn(const char *k1, uc_value_t *v1, const char *k2, uc_value_t *v2,
1475                void *ud)
1476 {
1477         uc_value_t *rv, *null = ucv_int64_new(0);
1478         sort_ctx_t *ctx = ud;
1479         int res;
1480 
1481         if (!ctx->fn)
1482                 return strcmp(k1, k2);
1483 
1484         if (ctx->ex)
1485                 return 0;
1486 
1487         uc_vm_ctx_push(ctx->vm);
1488         uc_vm_stack_push(ctx->vm, ucv_get(ctx->fn));
1489         uc_vm_stack_push(ctx->vm, ucv_string_new(k1));
1490         uc_vm_stack_push(ctx->vm, ucv_string_new(k2));
1491         uc_vm_stack_push(ctx->vm, ucv_get(v1));
1492         uc_vm_stack_push(ctx->vm, ucv_get(v2));
1493 
1494         if (uc_vm_call(ctx->vm, true, 4)) {
1495                 ctx->ex = true;
1496 
1497                 return 0;
1498         }
1499 
1500         rv = uc_vm_stack_pop(ctx->vm);
1501 
1502         ucv_compare(0, rv, null, &res);
1503 
1504         ucv_put(null);
1505         ucv_put(rv);
1506 
1507         return res;
1508 }
1509 
1510 /**
1511  * Sort the given array according to the given sort function.
1512  * If no sort function is provided, a default ascending sort order is applied.
1513  *
1514  * The input array is sorted in-place, no copy is made.
1515  *
1516  * The custom sort function is repeatedly called until the entire array is
1517  * sorted. It will receive two values as arguments and should return a value
1518  * lower than, larger than or equal to zero depending on whether the first
1519  * argument is smaller, larger or equal to the second argument respectively.
1520  *
1521  * Returns the sorted input array.
1522  *
1523  * @function module:core#sort
1524  *
1525  * @param {Array} arr
1526  * The input array to be sorted.
1527  *
1528  * @param {Function} [fn]
1529  * The sort function.
1530  *
1531  * @returns {Array}
1532  *
1533  * @example
1534  * sort([8, 1, 5, 9]) // [1, 5, 8, 9]
1535  * sort(["Bean", "Orange", "Apple"], function(a, b) {
1536  *    return length(a) - length(b);
1537  * }) // ["Bean", "Apple", "Orange"]
1538  */
1539 static uc_value_t *
1540 uc_sort(uc_vm_t *vm, size_t nargs)
1541 {
1542         uc_value_t *val = uc_fn_arg(0);
1543         uc_value_t *fn = uc_fn_arg(1);
1544         sort_ctx_t ctx = {
1545                 .vm = vm,
1546                 .fn = fn,
1547                 .ex = false
1548         };
1549 
1550         if (!assert_mutable(vm, val))
1551                 return NULL;
1552 
1553         switch (ucv_type(val)) {
1554         case UC_ARRAY:
1555                 ucv_array_sort_r(val, array_sort_fn, &ctx);
1556                 break;
1557 
1558         case UC_OBJECT:
1559                 ucv_object_sort_r(val, object_sort_fn, &ctx);
1560                 break;
1561 
1562         default:
1563                 return NULL;
1564         }
1565 
1566         return ctx.ex ? NULL : ucv_get(val);
1567 }
1568 
1569 /**
1570  * Removes the elements designated by `off` and `len` from the given array,
1571  * and replaces them with the additional arguments passed, if any.
1572  *
1573  * The array grows or shrinks as necessary.
1574  *
1575  * Returns the modified input array.
1576  *
1577  * @function module:core#splice
1578  *
1579  * @param {Array} arr
1580  * The input array to be modified.
1581  *
1582  * @param {number} off
1583  * The index to start removing elements.
1584  *
1585  * @param {number} [len]
1586  * The number of elements to remove.
1587  *
1588  * @param {...*} [elements]
1589  * The elements to insert.
1590  *
1591  * @returns {*}
1592  *
1593  * @example
1594  * let x = [ 1, 2, 3, 4 ];
1595  * splice(x, 1, 2, "a", "b", "c");  // [ 1, "a", "b", "c", 4 ]
1596  * print(x, "\n");                  // [ 1, "a", "b", "c", 4 ]
1597  */
1598 static uc_value_t *
1599 uc_splice(uc_vm_t *vm, size_t nargs)
1600 {
1601         uc_value_t *arr = uc_fn_arg(0);
1602         int64_t ofs = ucv_to_integer(uc_fn_arg(1));
1603         int64_t remlen = ucv_to_integer(uc_fn_arg(2));
1604         size_t arrlen, addlen, idx;
1605 
1606         if (!assert_mutable_array(vm, arr))
1607                 return NULL;
1608 
1609         arrlen = ucv_array_length(arr);
1610         addlen = nargs;
1611 
1612         if (addlen == 1) {
1613                 ofs = 0;
1614                 addlen = 0;
1615                 remlen = arrlen;
1616         }
1617         else if (addlen == 2) {
1618                 if (ofs < 0) {
1619                         ofs = arrlen + ofs;
1620 
1621                         if (ofs < 0)
1622                                 ofs = 0;
1623                 }
1624                 else if ((uint64_t)ofs > arrlen) {
1625                         ofs = arrlen;
1626                 }
1627 
1628                 addlen = 0;
1629                 remlen = arrlen - ofs;
1630         }
1631         else {
1632                 if (ofs < 0) {
1633                         ofs = arrlen + ofs;
1634 
1635                         if (ofs < 0)
1636                                 ofs = 0;
1637                 }
1638                 else if ((uint64_t)ofs > arrlen) {
1639                         ofs = arrlen;
1640                 }
1641 
1642                 if (remlen < 0) {
1643                         remlen = arrlen - ofs + remlen;
1644 
1645                         if (remlen < 0)
1646                                 remlen = 0;
1647                 }
1648                 else if ((uint64_t)remlen > arrlen - (uint64_t)ofs) {
1649                         remlen = arrlen - ofs;
1650                 }
1651 
1652                 addlen -= 3;
1653         }
1654 
1655         if (addlen < (uint64_t)remlen) {
1656                 ucv_array_delete(arr, ofs, remlen - addlen);
1657         }
1658         else if (addlen > (uint64_t)remlen) {
1659                 for (idx = arrlen; idx > (uint64_t)ofs; idx--)
1660                         ucv_array_set(arr, idx + addlen - remlen - 1,
1661                                 ucv_get(ucv_array_get(arr, idx - 1)));
1662         }
1663 
1664         for (idx = 0; idx < addlen; idx++)
1665                 ucv_array_set(arr, ofs + idx,
1666                         ucv_get(uc_fn_arg(3 + idx)));
1667 
1668         return ucv_get(arr);
1669 }
1670 
1671 /**
1672  * Performs a shallow copy of a portion of the source array, as specified by
1673  * the start and end offsets. The original array is not modified.
1674  *
1675  * Returns a new array containing the copied elements, if any.
1676  * Returns `null` if the given source argument is not an array value.
1677  *
1678  * @function module:core#slice
1679  *
1680  * @param {Array} arr
1681  * The source array to be copied.
1682  *
1683  * @param {number} [off]
1684  * The index of the first element to copy.
1685  *
1686  * @param {number} [end]
1687  * The index of the first element to exclude from the returned array.
1688  *
1689  * @returns {Array}
1690  *
1691  * @example
1692  * slice([1, 2, 3])          // [1, 2, 3]
1693  * slice([1, 2, 3], 1)       // [2, 3]
1694  * slice([1, 2, 3], -1)      // [3]
1695  * slice([1, 2, 3], -3, -1)  // [1, 2]
1696  * slice([1, 2, 3], 10)      // []
1697  * slice([1, 2, 3], 2, 1)    // []
1698  * slice("invalid", 1, 2)    // null
1699  */
1700 static uc_value_t *
1701 uc_slice(uc_vm_t *vm, size_t nargs)
1702 {
1703         uc_value_t *arr = uc_fn_arg(0);
1704         uc_value_t *sv = uc_fn_arg(1);
1705         uc_value_t *ev = uc_fn_arg(2);
1706         uc_value_t *res = NULL;
1707         int64_t off, end;
1708         size_t len;
1709 
1710         if (ucv_type(arr) != UC_ARRAY)
1711                 return NULL;
1712 
1713         len = ucv_array_length(arr);
1714         off = sv ? ucv_to_integer(sv) : 0;
1715         end = ev ? ucv_to_integer(ev) : (int64_t)len;
1716 
1717         if (off < 0) {
1718                 off = len + off;
1719 
1720                 if (off < 0)
1721                         off = 0;
1722         }
1723         else if ((uint64_t)off > len) {
1724                 off = len;
1725         }
1726 
1727         if (end < 0) {
1728                 end = len + end;
1729 
1730                 if (end < 0)
1731                         end = 0;
1732         }
1733         else if ((uint64_t)end > len) {
1734                 end = len;
1735         }
1736 
1737         res = ucv_array_new(vm);
1738 
1739         while (off < end)
1740                 ucv_array_push(res, ucv_get(ucv_array_get(arr, off++)));
1741 
1742         return res;
1743 }
1744 
1745 /**
1746  * Split the given string using the separator passed as the second argument
1747  * and return an array containing the resulting pieces.
1748  *
1749  * If a limit argument is supplied, the resulting array contains no more than
1750  * the given amount of entries, that means the string is split at most
1751  * `limit - 1` times total.
1752  *
1753  * The separator may either be a plain string or a regular expression.
1754  *
1755  * Returns a new array containing the resulting pieces.
1756  *
1757  * @function module:core#split
1758  *
1759  * @param {string} str
1760  * The input string to be split.
1761  *
1762  * @param {string|RegExp} sep
1763  * The separator.
1764  *
1765  * @param {number} [limit]
1766  * The limit on the number of splits.
1767  *
1768  * @returns {Array}
1769  *
1770  * @example
1771  * split("foo,bar,baz", ",")     // ["foo", "bar", "baz"]
1772  * split("foobar", "")           // ["f", "o", "o", "b", "a", "r"]
1773  * split("foo,bar,baz", /[ao]/)  // ["f", "", ",b", "r,b", "z"]
1774  * split("foo=bar=baz", "=", 2)  // ["foo", "bar=baz"]
1775  */
1776 static uc_value_t *
1777 uc_split(uc_vm_t *vm, size_t nargs)
1778 {
1779         uc_value_t *str = uc_fn_arg(0);
1780         uc_value_t *sep = uc_fn_arg(1);
1781         uc_value_t *lim = uc_fn_arg(2);
1782         uc_value_t *arr = NULL;
1783         const char *p, *sepstr, *splitstr;
1784         size_t seplen, splitlen, limit;
1785         int eflags = 0, res;
1786         regmatch_t pmatch;
1787         uc_regexp_t *re;
1788 
1789         if (!sep || ucv_type(str) != UC_STRING)
1790                 return NULL;
1791 
1792         arr = ucv_array_new(vm);
1793         splitlen = ucv_string_length(str);
1794         p = splitstr = ucv_string_get(str);
1795         limit = lim ? ucv_uint64_get(lim) : SIZE_MAX;
1796 
1797         if (limit == 0)
1798                 goto out;
1799 
1800         if (ucv_type(sep) == UC_REGEXP) {
1801                 re = (uc_regexp_t *)sep;
1802 
1803                 while (limit > 1) {
1804                         res = regexec(&re->regexp, splitstr, 1, &pmatch, eflags);
1805 
1806                         if (res == REG_NOMATCH)
1807                                 break;
1808 
1809                         if (pmatch.rm_so != pmatch.rm_eo) {
1810                                 ucv_array_push(arr, ucv_string_new_length(splitstr, pmatch.rm_so));
1811                                 splitstr += pmatch.rm_eo;
1812                         }
1813                         else if (*splitstr) {
1814                                 ucv_array_push(arr, ucv_string_new_length(splitstr, 1));
1815                                 splitstr++;
1816                         }
1817                         else {
1818                                 goto out;
1819                         }
1820 
1821                         eflags |= REG_NOTBOL;
1822                         limit--;
1823                 }
1824 
1825                 ucv_array_push(arr, ucv_string_new(splitstr));
1826         }
1827         else if (ucv_type(sep) == UC_STRING) {
1828                 sepstr = ucv_string_get(sep);
1829                 seplen = ucv_string_length(sep);
1830 
1831                 if (splitlen == 0) {
1832                         ucv_array_push(arr, ucv_string_new_length("", 0));
1833                 }
1834                 else if (seplen == 0) {
1835                         while (limit > 1 && splitlen > 0) {
1836                                 ucv_array_push(arr, ucv_string_new_length(p, 1));
1837 
1838                                 limit--;
1839                                 splitlen--;
1840                                 p++;
1841                         }
1842 
1843                         if (splitlen > 0)
1844                                 ucv_array_push(arr, ucv_string_new_length(p, splitlen));
1845                 }
1846                 else {
1847                         while (limit > 1 && splitlen >= seplen) {
1848                                 if (!memcmp(p, sepstr, seplen)) {
1849                                         ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr));
1850 
1851                                         p = splitstr = p + seplen;
1852                                         splitlen -= seplen;
1853                                         limit--;
1854                                         continue;
1855                                 }
1856 
1857                                 splitlen--;
1858                                 p++;
1859                         }
1860 
1861                         ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr + splitlen));
1862                 }
1863         }
1864         else {
1865                 ucv_put(arr);
1866 
1867                 return NULL;
1868         }
1869 
1870 out:
1871         return arr;
1872 }
1873 
1874 /**
1875  * Extracts a substring out of `str` and returns it. First character is at
1876  * offset zero.
1877  *
1878  *  - If `off` is negative, starts that far back from the end of the string.
1879  *  - If `len` is omitted, returns everything through the end of the string.
1880  *  - If `len` is negative, leaves that many characters off the string end.
1881  *
1882  * Returns the extracted substring.
1883  *
1884  * @function module:core#substr
1885  *
1886  * @param {string} str
1887  * The input string.
1888  *
1889  * @param {number} off
1890  * The starting offset.
1891  *
1892  * @param {number} [len]
1893  * The length of the substring.
1894  *
1895  * @returns {string}
1896  *
1897  * @example
1898  * s = "The black cat climbed the green tree";
1899  * substr(s, 4, 5);      // black
1900  * substr(s, 4, -11);    // black cat climbed the
1901  * substr(s, 14);        // climbed the green tree
1902  * substr(s, -4);        // tree
1903  * substr(s, -4, 2);     // tr
1904  */
1905 static uc_value_t *
1906 uc_substr(uc_vm_t *vm, size_t nargs)
1907 {
1908         uc_value_t *str = uc_fn_arg(0);
1909         int64_t ofs = ucv_to_integer(uc_fn_arg(1));
1910         int64_t sublen = ucv_to_integer(uc_fn_arg(2));
1911         const char *p;
1912         size_t len;
1913 
1914         if (ucv_type(str) != UC_STRING)
1915                 return NULL;
1916 
1917         p = ucv_string_get(str);
1918         len = ucv_string_length(str);
1919 
1920         switch (nargs) {
1921         case 1:
1922                 ofs = 0;
1923                 sublen = len;
1924 
1925                 break;
1926 
1927         case 2:
1928                 if (ofs < 0) {
1929                         ofs = len + ofs;
1930 
1931                         if (ofs < 0)
1932                                 ofs = 0;
1933                 }
1934                 else if ((uint64_t)ofs > len) {
1935                         ofs = len;
1936                 }
1937 
1938                 sublen = len - ofs;
1939 
1940                 break;
1941 
1942         default:
1943                 if (ofs < 0) {
1944                         ofs = len + ofs;
1945 
1946                         if (ofs < 0)
1947                                 ofs = 0;
1948                 }
1949                 else if ((uint64_t)ofs > len) {
1950                         ofs = len;
1951                 }
1952 
1953                 if (sublen < 0) {
1954                         sublen = len - ofs + sublen;
1955 
1956                         if (sublen < 0)
1957                                 sublen = 0;
1958                 }
1959                 else if ((uint64_t)sublen > len - (uint64_t)ofs) {
1960                         sublen = len - ofs;
1961                 }
1962 
1963                 break;
1964         }
1965 
1966         return ucv_string_new_length(p + ofs, sublen);
1967 }
1968 
1969 /**
1970  * Returns the current UNIX epoch.
1971  *
1972  * @function module:core#time
1973  *
1974  * @returns {number}
1975  *
1976  * @example
1977  * time();     // 1598043054
1978  */
1979 static uc_value_t *
1980 uc_time(uc_vm_t *vm, size_t nargs)
1981 {
1982         time_t t = time(NULL);
1983 
1984         return ucv_int64_new((int64_t)t);
1985 }
1986 
1987 /**
1988  * Converts the given string to uppercase and returns the resulting string.
1989  *
1990  * Returns null if the given argument could not be converted to a string.
1991  *
1992  * @function module:core#uc
1993  *
1994  * @param {*} str
1995  * The string to be converted to uppercase.
1996  *
1997  * @returns {?string}
1998  *
1999  * @example
2000  * uc("hello");   // "HELLO"
2001  * uc(123);       // null
2002  */
2003 
2004 static uc_value_t *
2005 uc_uc(uc_vm_t *vm, size_t nargs)
2006 {
2007         char *str = ucv_to_string(vm, uc_fn_arg(0));
2008         uc_value_t *rv = NULL;
2009         char *p;
2010 
2011         if (!str)
2012                 return NULL;
2013 
2014         for (p = str; *p; p++)
2015                 if (*p >= 'a' && *p <= 'z')
2016                         *p &= ~32;
2017 
2018         rv = ucv_string_new(str);
2019 
2020         free(str);
2021 
2022         return rv;
2023 }
2024 
2025 /**
2026  * Converts each given numeric value to an UTF-8 multibyte sequence and returns
2027  * the resulting string.
2028  *
2029  * Invalid numeric values or values outside the range `0`..`0x10FFFF` are
2030  * represented by the unicode replacement character `0xFFFD`.
2031  *
2032  * Returns a new UTF-8 encoded string consisting of unicode characters
2033  * corresponding to the given numeric codepoints.
2034  *
2035  * @function module:core#uchr
2036  *
2037  * @param {...number}
2038  * Numeric values to convert.
2039  *
2040  * @returns {string}
2041  *
2042  * @example
2043  * uchr(0x2600, 0x26C6, 0x2601);  // "☀⛆☁"
2044  * uchr(-1, 0x20ffff, "foo");     // "���"
2045  */
2046 static uc_value_t *
2047 uc_uchr(uc_vm_t *vm, size_t nargs)
2048 {
2049         uc_value_t *rv = NULL;
2050         size_t idx, ulen;
2051         char *p, *str;
2052         int64_t n;
2053         int rem;
2054 
2055         for (idx = 0, ulen = 0; idx < nargs; idx++) {
2056                 n = ucv_to_integer(uc_fn_arg(idx));
2057 
2058                 if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
2059                         ulen += 3;
2060                 else if (n <= 0x7F)
2061                         ulen++;
2062                 else if (n <= 0x7FF)
2063                         ulen += 2;
2064                 else if (n <= 0xFFFF)
2065                         ulen += 3;
2066                 else
2067                         ulen += 4;
2068         }
2069 
2070         str = xalloc(ulen);
2071 
2072         for (idx = 0, p = str, rem = ulen; idx < nargs; idx++) {
2073                 n = ucv_to_integer(uc_fn_arg(idx));
2074 
2075                 if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
2076                         n = 0xFFFD;
2077 
2078                 if (!utf8enc(&p, &rem, n))
2079                         break;
2080         }
2081 
2082         rv = ucv_string_new_length(str, ulen);
2083 
2084         free(str);
2085 
2086         return rv;
2087 }
2088 
2089 /**
2090  * Returns an array containing all values of the given object.
2091  *
2092  * Returns null if no object was passed.
2093  *
2094  * @function module:core#values
2095  *
2096  * @param {*} obj
2097  * The object from which to extract values.
2098  *
2099  * @returns {?Array}
2100  *
2101  * @example
2102  * values({ foo: true, bar: false });   // [true, false]
2103  */
2104 static uc_value_t *
2105 uc_values(uc_vm_t *vm, size_t nargs)
2106 {
2107         uc_value_t *obj = uc_fn_arg(0);
2108         uc_value_t *arr;
2109 
2110         if (ucv_type(obj) != UC_OBJECT)
2111                 return NULL;
2112 
2113         arr = ucv_array_new(vm);
2114 
2115         ucv_object_foreach(obj, key, val) {
2116                 (void)key;
2117                 ucv_array_push(arr, ucv_get(val));
2118         }
2119 
2120         return arr;
2121 }
2122 
2123 static uc_value_t *
2124 uc_trim_common(uc_vm_t *vm, size_t nargs, bool start, bool end)
2125 {
2126         uc_value_t *str = uc_fn_arg(0);
2127         uc_value_t *chr = uc_fn_arg(1);
2128         const char *p, *c;
2129         size_t len;
2130 
2131         if (ucv_type(str) != UC_STRING ||
2132                 (chr != NULL && ucv_type(chr) != UC_STRING))
2133                 return NULL;
2134 
2135         c = ucv_string_get(chr);
2136         c = c ? c : " \t\r\n";
2137 
2138         p = ucv_string_get(str);
2139         len = ucv_string_length(str);
2140 
2141         if (start) {
2142                 while (*p) {
2143                         if (!strchr(c, *p))
2144                                 break;
2145 
2146                         p++;
2147                         len--;
2148                 }
2149         }
2150 
2151         if (end) {
2152                 while (len > 0) {
2153                         if (!strchr(c, p[len - 1]))
2154                                 break;
2155 
2156                         len--;
2157                 }
2158         }
2159 
2160         return ucv_string_new_length(p, len);
2161 }
2162 
2163 /**
2164  * Trim any of the specified characters in `c` from the start and end of `str`.
2165  * If the second argument is omitted, trims the characters, ` ` (space), `\t`,
2166  * `\r`, and `\n`.
2167  *
2168  * Returns the trimmed string.
2169  *
2170  * @function module:core#trim
2171  *
2172  * @param {string} str
2173  * The string to be trimmed.
2174  *
2175  * @param {string} [c]
2176  * The characters to be trimmed from the start and end of the string.
2177  *
2178  * @returns {string}
2179  */
2180 static uc_value_t *
2181 uc_trim(uc_vm_t *vm, size_t nargs)
2182 {
2183         return uc_trim_common(vm, nargs, true, true);
2184 }
2185 
2186 /**
2187  * Trim any of the specified characters from the start of the string.
2188  * If the second argument is omitted, trims the characters ` ` (space), '\t',
2189  * '\r', and '\n'.
2190  *
2191  * Returns the left trimmed string.
2192  *
2193  * @function module:core#ltrim
2194  *
2195  * @param {string} s
2196  * The input string.
2197  *
2198  * @param {string} [c]
2199  * The characters to trim.
2200  *
2201  * @returns {string}
2202  *
2203  * @example
2204  * ltrim("  foo  \n")     // "foo  \n"
2205  * ltrim("--bar--", "-")  // "bar--"
2206  */
2207 static uc_value_t *
2208 uc_ltrim(uc_vm_t *vm, size_t nargs)
2209 {
2210         return uc_trim_common(vm, nargs, true, false);
2211 }
2212 
2213 /**
2214  * Trim any of the specified characters from the end of the string.
2215  * If the second argument is omitted, trims the characters ` ` (space), '\t',
2216  * '\r', and '\n'.
2217  *
2218 * Returns the right trimmed string.
2219  *
2220  * @function module:core#rtrim
2221  *
2222  * @param {string} str
2223  * The input string.
2224  *
2225  * @param {string} [c]
2226  * The characters to trim.
2227  *
2228  * @returns {string}
2229  *
2230  * @example
2231  * rtrim("  foo  \n")     // "  foo"
2232  * rtrim("--bar--", "-")  // "--bar"
2233  */
2234 static uc_value_t *
2235 uc_rtrim(uc_vm_t *vm, size_t nargs)
2236 {
2237         return uc_trim_common(vm, nargs, false, true);
2238 }
2239 
2240 enum {
2241         FMT_F_ALT   = (1 << 0),
2242         FMT_F_ZERO  = (1 << 1),
2243         FMT_F_LEFT  = (1 << 2),
2244         FMT_F_SPACE = (1 << 3),
2245         FMT_F_SIGN  = (1 << 4),
2246         FMT_F_WIDTH = (1 << 5),
2247         FMT_F_PREC  = (1 << 6),
2248 };
2249 
2250 enum {
2251         FMT_C_NONE = (1 << 0),
2252         FMT_C_INT  = (1 << 1),
2253         FMT_C_UINT = (1 << 2),
2254         FMT_C_DBL  = (1 << 3),
2255         FMT_C_CHR  = (1 << 4),
2256         FMT_C_STR  = (1 << 5),
2257         FMT_C_JSON = (1 << 6),
2258 };
2259 
2260 static void
2261 uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
2262 {
2263         char *s, sfmt[sizeof("%#0- +0123456789.0123456789%")];
2264         uint32_t conv, flags, width, precision;
2265         uc_value_t *fmt = uc_fn_arg(0), *arg;
2266         const char *fstr, *last, *p, *cfmt;
2267         size_t argidx = 1, argpos, sfmtlen;
2268         uint64_t u;
2269         int64_t n;
2270         double d;
2271 
2272         if (ucv_type(fmt) == UC_STRING)
2273                 fstr = ucv_string_get(fmt);
2274         else
2275                 fstr = "";
2276 
2277         for (last = p = fstr; *p; p++) {
2278                 if (*p == '%') {
2279                         ucv_stringbuf_addstr(buf, last, p - last);
2280 
2281                         last = p++;
2282 
2283                         flags = 0;
2284                         width = 0;
2285                         precision = 0;
2286 
2287                         argpos = argidx;
2288 
2289                         if (*p >= '1' && *p <= '9') {
2290                                 while (isdigit(*p))
2291                                         width = width * 10 + (*p++ - '');
2292 
2293                                 /* if a dollar sign follows, this is an argument index */
2294                                 if (*p == '$') {
2295                                         argpos = width;
2296                                         width = 0;
2297                                         p++;
2298                                 }
2299 
2300                                 /* otherwise skip to parsing precision, flags can't possibly follow */
2301                                 else {
2302                                         flags |= FMT_F_WIDTH;
2303                                         goto parse_precision;
2304                                 }
2305                         }
2306 
2307                         while (*p != '\0' && strchr("#0- +", *p)) {
2308                                 switch (*p++) {
2309                                 case '#': flags |= FMT_F_ALT;   break;
2310                                 case '': flags |= FMT_F_ZERO;  break;
2311                                 case '-': flags |= FMT_F_LEFT;  break;
2312                                 case ' ': flags |= FMT_F_SPACE; break;
2313                                 case '+': flags |= FMT_F_SIGN;  break;
2314                                 }
2315                         }
2316 
2317                         if (*p >= '1' && *p <= '9') {
2318                                 while (isdigit(*p))
2319                                         width = width * 10 + (*p++ - '');
2320 
2321                                 flags |= FMT_F_WIDTH;
2322                         }
2323 
2324 parse_precision:
2325                         if (*p == '.') {
2326                                 p++;
2327 
2328                                 if (*p == '-') {
2329                                         p++;
2330 
2331                                         while (isdigit(*p))
2332                                                 p++;
2333                                 }
2334                                 else {
2335                                         while (isdigit(*p))
2336                                                 precision = precision * 10 + (*p++ - '');
2337                                 }
2338 
2339                                 flags |= FMT_F_PREC;
2340                         }
2341 
2342                         switch (*p) {
2343                         case 'd':
2344                         case 'i':
2345                                 conv = FMT_C_INT;
2346                                 flags &= ~FMT_F_PREC;
2347                                 cfmt = PRId64;
2348                                 break;
2349 
2350                         case 'o':
2351                                 conv = FMT_C_UINT;
2352                                 flags &= ~FMT_F_PREC;
2353                                 cfmt = PRIo64;
2354                                 break;
2355 
2356                         case 'u':
2357                                 conv = FMT_C_UINT;
2358                                 flags &= ~FMT_F_PREC;
2359                                 cfmt = PRIu64;
2360                                 break;
2361 
2362                         case 'x':
2363                                 conv = FMT_C_UINT;
2364                                 flags &= ~FMT_F_PREC;
2365                                 cfmt = PRIx64;
2366                                 break;
2367 
2368                         case 'X':
2369                                 conv = FMT_C_UINT;
2370                                 flags &= ~FMT_F_PREC;
2371                                 cfmt = PRIX64;
2372                                 break;
2373 
2374                         case 'e':
2375                                 conv = FMT_C_DBL;
2376                                 cfmt = "e";
2377                                 break;
2378 
2379                         case 'E':
2380                                 conv = FMT_C_DBL;
2381                                 cfmt = "E";
2382                                 break;
2383 
2384                         case 'f':
2385                                 conv = FMT_C_DBL;
2386                                 cfmt = "f";
2387                                 break;
2388 
2389                         case 'F':
2390                                 conv = FMT_C_DBL;
2391                                 cfmt = "F";
2392                                 break;
2393 
2394                         case 'g':
2395                                 conv = FMT_C_DBL;
2396                                 cfmt = "g";
2397                                 break;
2398 
2399                         case 'G':
2400                                 conv = FMT_C_DBL;
2401                                 cfmt = "G";
2402                                 break;
2403 
2404                         case 'c':
2405                                 conv = FMT_C_CHR;
2406                                 flags &= ~FMT_F_PREC;
2407                                 cfmt = "c";
2408                                 break;
2409 
2410                         case 's':
2411                                 conv = FMT_C_STR;
2412                                 flags &= ~FMT_F_ZERO;
2413                                 cfmt = "s";
2414                                 break;
2415 
2416                         case 'J':
2417                                 conv = FMT_C_JSON;
2418 
2419                                 if (flags & FMT_F_PREC) {
2420                                         flags &= ~FMT_F_PREC;
2421                                         precision++;
2422                                 }
2423 
2424                                 cfmt = "s";
2425                                 break;
2426 
2427                         case '%':
2428                                 conv = FMT_C_NONE;
2429                                 flags = 0;
2430                                 cfmt = "%";
2431                                 break;
2432 
2433                         case '\0':
2434                                 p--;
2435                                 /* fall through */
2436 
2437                         default:
2438                                 continue;
2439                         }
2440 
2441                         sfmtlen = 0;
2442                         sfmt[sfmtlen++] = '%';
2443 
2444                         if (flags & FMT_F_ALT)   sfmt[sfmtlen++] = '#';
2445                         if (flags & FMT_F_ZERO)  sfmt[sfmtlen++] = '';
2446                         if (flags & FMT_F_LEFT)  sfmt[sfmtlen++] = '-';
2447                         if (flags & FMT_F_SPACE) sfmt[sfmtlen++] = ' ';
2448                         if (flags & FMT_F_SIGN)  sfmt[sfmtlen++] = '+';
2449 
2450                         if (flags & FMT_F_WIDTH)
2451                                 sfmtlen += snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, "%" PRIu32, width);
2452 
2453                         if (flags & FMT_F_PREC)
2454                                 sfmtlen += snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, ".%" PRIu32, precision);
2455 
2456                         snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, "%s", cfmt);
2457 
2458                         switch (conv) {
2459                         case FMT_C_NONE:
2460                                 ucv_stringbuf_addstr(buf, cfmt, strlen(cfmt));
2461                                 break;
2462 
2463                         case FMT_C_INT:
2464                                 argidx++;
2465                                 arg = uc_fn_arg(argpos);
2466                                 n = ucv_to_integer(arg);
2467 
2468                                 if (errno == ERANGE)
2469                                         n = (int64_t)ucv_to_unsigned(arg);
2470 
2471                                 ucv_stringbuf_printf(buf, sfmt, n);
2472                                 break;
2473 
2474                         case FMT_C_UINT:
2475                                 argidx++;
2476                                 arg = uc_fn_arg(argpos);
2477                                 u = ucv_to_unsigned(arg);
2478 
2479                                 if (errno == ERANGE)
2480                                         u = (uint64_t)ucv_to_integer(arg);
2481 
2482                                 ucv_stringbuf_printf(buf, sfmt, u);
2483                                 break;
2484 
2485                         case FMT_C_DBL:
2486                                 argidx++;
2487                                 d = ucv_to_double(uc_fn_arg(argpos));
2488                                 ucv_stringbuf_printf(buf, sfmt, d);
2489                                 break;
2490 
2491                         case FMT_C_CHR:
2492                                 argidx++;
2493                                 n = ucv_to_integer(uc_fn_arg(argpos));
2494                                 ucv_stringbuf_printf(buf, sfmt, (int)n);
2495                                 break;
2496 
2497                         case FMT_C_STR:
2498                                 argidx++;
2499                                 arg = uc_fn_arg(argpos);
2500 
2501                                 switch (ucv_type(arg)) {
2502                                 case UC_STRING:
2503                                         ucv_stringbuf_printf(buf, sfmt, ucv_string_get(arg));
2504                                         break;
2505 
2506                                 case UC_NULL:
2507                                         ucv_stringbuf_append(buf, "(null)");
2508                                         break;
2509 
2510                                 default:
2511                                         s = ucv_to_string(vm, arg);
2512                                         ucv_stringbuf_printf(buf, sfmt, s ? s : "(null)");
2513                                         free(s);
2514                                 }
2515 
2516                                 break;
2517 
2518                         case FMT_C_JSON:
2519                                 argidx++;
2520                                 s = ucv_to_jsonstring_formatted(vm,
2521                                         uc_fn_arg(argpos),
2522                                         precision > 0 ? (precision > 1 ? ' ' : '\t') : '\0',
2523                                         precision > 0 ? (precision > 1 ? precision - 1 : 1) : 0);
2524 
2525                                 ucv_stringbuf_printf(buf, sfmt, s ? s : "null");
2526                                 free(s);
2527                                 break;
2528                         }
2529 
2530                         last = p + 1;
2531                 }
2532         }
2533 
2534         ucv_stringbuf_addstr(buf, last, p - last);
2535 }
2536 
2537 /**
2538  * Formats the given arguments according to the given format string.
2539  *
2540  * See `printf()` for details.
2541  *
2542  * Returns the formatted string.
2543  *
2544  * @function module:core#sprintf
2545  *
2546  * @param {string} fmt
2547  * The format string.
2548  *
2549  * @param {...*}
2550  * Arguments to be formatted.
2551  *
2552  * @returns {string}
2553  *
2554  * @example
2555  * sprintf("Hello %s", "world");    // "Hello world"
2556  * sprintf("%08x", 123);            // "0000007b"
2557  * sprintf("%c%c%c", 65, 98, 99);   // "Abc"
2558  * sprintf("%g", 10 / 3.0);         // "3.33333"
2559  * sprintf("%2$d %1$d", 12, 34);    // "34 12"
2560  * sprintf("%J", [1,2,3]);          // "[1,2,3]"
2561  */
2562 static uc_value_t *
2563 uc_sprintf(uc_vm_t *vm, size_t nargs)
2564 {
2565         uc_stringbuf_t *buf = ucv_stringbuf_new();
2566 
2567         uc_printf_common(vm, nargs, buf);
2568 
2569         return ucv_stringbuf_finish(buf);
2570 }
2571 
2572 /**
2573  * Formats the given arguments according to the given format string and outputs
2574  * the result to stdout.
2575  *
2576  * Ucode supports a restricted subset of the formats allowed by the underlying
2577  * libc's `printf()` implementation, namely it allows the `d`, `i`, `o`, `u`,
2578  * `x`, `X`, `e`, `E`, `f`, `F`, `g`, `G`, `c` and `s` conversions.
2579  *
2580  * Additionally, an ucode specific `J` format is implemented, which causes the
2581  * corresponding value to be formatted as JSON string. By prefixing the `J`
2582  * format letter with a precision specifier, the resulting JSON output will be
2583  * pretty printed. A precision of `0` will use tabs for indentation, any other
2584  * positive precision will use that many spaces for indentation while a negative
2585  * or omitted precision specifier will turn off pretty printing.
2586  *
2587  * Other format specifiers such as `n` or `z` are not accepted and returned
2588  * verbatim. Format specifiers including `*` directives are rejected as well.
2589  *
2590  * Returns the number of bytes written to the standard output.
2591  *
2592  * @function module:core#printf
2593  *
2594  * @param {string} fmt
2595  * The format string.
2596  *
2597  * @param {...*}
2598  * Arguments to be formatted.
2599  *
2600  * @returns {number}
2601  *
2602  * @example
2603  * {%
2604  *   printf("Hello %s\n", "world");  // Hello world
2605  *   printf("%08x\n", 123);          // 0000007b
2606  *   printf("%c%c%c\n", 65, 98, 99); // Abc
2607  *   printf("%g\n", 10 / 3.0);       // 3.33333
2608  *   printf("%2$d %1$d\n", 12, 34);  // 34 12
2609  *   printf("%J", [1,2,3]);          // [ 1, 2, 3 ]
2610  *
2611  *   printf("%.J", [1,2,3]);
2612  *   // [
2613  *   //         1,
2614  *   //         2,
2615  *   //         3
2616  *   // ]
2617  *
2618  *   printf("%.2J", [1,2,3]);
2619  *   // [
2620  *   //   1,
2621  *   //   2,
2622  *   //   3
2623  *   // ]
2624  * %}
2625  */
2626 static uc_value_t *
2627 uc_printf(uc_vm_t *vm, size_t nargs)
2628 {
2629         uc_stringbuf_t *buf = xprintbuf_new();
2630         size_t len;
2631 
2632         uc_printf_common(vm, nargs, buf);
2633 
2634         len = fwrite(buf->buf, 1, printbuf_length(buf), vm->output);
2635 
2636         printbuf_free(buf);
2637 
2638         return ucv_int64_new(len);
2639 }
2640 
2641 static bool
2642 uc_require_so(uc_vm_t *vm, const char *path, uc_value_t **res)
2643 {
2644         void (*init)(uc_vm_t *, uc_value_t *);
2645         uc_value_t *scope;
2646         struct stat st;
2647         void *dlh;
2648 
2649         if (stat(path, &st))
2650                 return false;
2651 
2652         dlerror();
2653         dlh = dlopen(path, RTLD_LAZY|RTLD_LOCAL);
2654 
2655         if (!dlh) {
2656                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2657                                       "Unable to dlopen file '%s': %s", path, dlerror());
2658 
2659                 return true;
2660         }
2661 
2662         *(void **)(&init) = dlsym(dlh, "uc_module_entry");
2663 
2664         if (!init) {
2665                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2666                                       "Module '%s' provides no 'uc_module_entry' function", path);
2667 
2668                 return true;
2669         }
2670 
2671         scope = ucv_object_new(vm);
2672 
2673         init(vm, scope);
2674 
2675         *res = scope;
2676 
2677         return true;
2678 }
2679 
2680 static uc_value_t *
2681 uc_loadfile(uc_vm_t *vm, size_t nargs);
2682 
2683 static uc_value_t *
2684 uc_callfunc(uc_vm_t *vm, size_t nargs);
2685 
2686 static uc_value_t *
2687 uc_require_imports(uc_vm_t *vm, uc_value_t *closure)
2688 {
2689         uc_function_t *fn = ((uc_closure_t *)closure)->function;
2690         uc_source_t *src = uc_program_function_source(fn);
2691         uc_value_t *ns = ucv_object_new(vm);
2692         size_t i = 0;
2693 
2694         uc_vector_foreach(&src->exports, sym) {
2695                 if (i >= fn->program->exports.count)
2696                         break;
2697 
2698                 if (ucv_type(*sym) == UC_STRING)
2699                         ucv_object_add(ns, ucv_string_get(*sym),
2700                                 ucv_get(&fn->program->exports.entries[i++]->header));
2701                 else if (ucv_type(*sym) == UC_NULL)
2702                         ucv_object_add(ns, "default",
2703                                 ucv_get(&fn->program->exports.entries[i++]->header));
2704         }
2705 
2706         ucv_set_constant(ns, true);
2707 
2708         return ns;
2709 }
2710 
2711 static bool
2712 uc_require_ucode(uc_vm_t *vm, const char *path, uc_value_t *scope, uc_value_t **res, bool raw_mode, bool module_mode)
2713 {
2714         uc_parse_config_t config = *vm->config, *prev_config = vm->config;
2715         uc_value_t *closure;
2716         struct stat st;
2717 
2718         if (stat(path, &st))
2719                 return false;
2720 
2721         config.raw_mode = raw_mode;
2722         config.compile_module = module_mode;
2723         vm->config = &config;
2724 
2725         uc_vm_stack_push(vm, ucv_string_new(path));
2726 
2727         closure = uc_loadfile(vm, 1);
2728 
2729         ucv_put(uc_vm_stack_pop(vm));
2730 
2731         if (closure) {
2732                 uc_vm_stack_push(vm, closure);
2733                 uc_vm_stack_push(vm, NULL);
2734                 uc_vm_stack_push(vm, scope);
2735 
2736                 *res = uc_callfunc(vm, 3);
2737 
2738                 if (vm->exception.type != EXCEPTION_EXIT) {
2739                         if (module_mode) {
2740                                 ucv_put(*res);
2741                                 *res = uc_require_imports(vm, closure);
2742                         }
2743 
2744                         uc_vm_stack_pop(vm);
2745                         uc_vm_stack_pop(vm);
2746                         uc_vm_stack_pop(vm);
2747                 }
2748         }
2749 
2750         vm->config = prev_config;
2751 
2752         return true;
2753 }
2754 
2755 static bool
2756 uc_require_path(uc_vm_t *vm, const char *path_template, const char *name,
2757                 uc_value_t **res, bool module_mode)
2758 {
2759         uc_stringbuf_t *buf = xprintbuf_new();
2760         const char *p, *q, *last;
2761         uc_value_t *modtable;
2762         bool rv;
2763 
2764         modtable = ucv_property_get(uc_vm_scope_get(vm), "modules");
2765         *res = ucv_get(ucv_object_get(modtable, name, &rv));
2766 
2767         if (rv)
2768                 goto out;
2769 
2770         p = strchr(path_template, '*');
2771 
2772         if (!p)
2773                 goto out;
2774 
2775         ucv_stringbuf_addstr(buf, path_template, p - path_template);
2776 
2777         for (q = last = name;; q++) {
2778                 if (*q == '.' || *q == '\0') {
2779                         ucv_stringbuf_addstr(buf, last, q - last);
2780 
2781                         if (*q)
2782                                 ucv_stringbuf_append(buf, "/");
2783                         else
2784                                 ucv_stringbuf_addstr(buf, p + 1, strlen(p + 1));
2785 
2786                         if (*q == '\0')
2787                                 break;
2788 
2789                         last = q + 1;
2790                 }
2791                 else if (!isalnum(*q) && *q != '_') {
2792                         goto out;
2793                 }
2794         }
2795 
2796         if (!strcmp(p + 1, ".so"))
2797                 rv = uc_require_so(vm, buf->buf, res);
2798         else if (!strcmp(p + 1, ".uc"))
2799                 rv = uc_require_ucode(vm, buf->buf, NULL, res, true, module_mode);
2800 
2801         if (rv)
2802                 ucv_object_add(modtable, name, ucv_get(*res));
2803 
2804 out:
2805         printbuf_free(buf);
2806 
2807         return rv;
2808 }
2809 
2810 uc_value_t *
2811 uc_require_library(uc_vm_t *vm, uc_value_t *nameval, bool module_mode)
2812 {
2813         uc_value_t *search, *se, *res;
2814         size_t arridx, arrlen;
2815         const char *name;
2816 
2817         if (ucv_type(nameval) != UC_STRING)
2818                 return NULL;
2819 
2820         name = ucv_string_get(nameval);
2821         search = ucv_property_get(uc_vm_scope_get(vm), "REQUIRE_SEARCH_PATH");
2822 
2823         if (ucv_type(search) != UC_ARRAY) {
2824                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2825                                       "Global require search path not set");
2826 
2827                 return NULL;
2828         }
2829 
2830         for (arridx = 0, arrlen = ucv_array_length(search); arridx < arrlen; arridx++) {
2831                 se = ucv_array_get(search, arridx);
2832 
2833                 if (ucv_type(se) != UC_STRING)
2834                         continue;
2835 
2836                 if (uc_require_path(vm, ucv_string_get(se), name, &res, module_mode))
2837                         return res;
2838         }
2839 
2840         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2841                               "No module named '%s' could be found", name);
2842 
2843         return NULL;
2844 }
2845 
2846 /**
2847  * Load and evaluate ucode scripts or shared library extensions.
2848  *
2849  * The `require()` function expands each member of the global
2850  * `REQUIRE_SEARCH_PATH` array to a filesystem path by replacing the `*`
2851  * placeholder with a slash-separated version of the given dotted module name
2852  * and subsequently tries to load a file at the resulting location.
2853  *
2854  * If a file is found at one of the search path locations, it is compiled and
2855  * evaluated or loaded via the C runtime's `dlopen()` function, depending on
2856  * whether the found file is a ucode script or a compiled dynamic library.
2857  *
2858  * The resulting program function of the compiled/loaded module is then
2859  * subsequently executed with the current global environment, without a `this`
2860  * context and without arguments.
2861  *
2862  * Finally, the return value of the invoked program function is returned back
2863  * by `require()` to the caller.
2864  *
2865  * By default, modules are cached in the global `modules` dictionary and
2866  * subsequent attempts to require the same module will return the cached module
2867  * dictionary entry without re-evaluating the module.
2868  *
2869  * To force reloading a module, the corresponding entry from the global
2870  * `modules` dictionary can be deleted.
2871  *
2872  * To preload a module or to provide a "virtual" module without a corresponding
2873  * filesystem resource, an entry can be manually added to the global `modules`
2874  * dictionary.
2875  *
2876  * Summarized, the `require()` function can be roughly described by the
2877  * following code:
2878  *
2879  * ```
2880  * function require(name) {
2881  *     if (exists(modules, name))
2882  *         return modules[name];
2883  *
2884  *     for (const item in REQUIRE_SEARCH_PATH) {
2885  *         const modpath = replace(item, '*', replace(name, '.', '/'));
2886  *         const entryfunc = loadfile(modpath, { raw_mode: true });
2887  *
2888  *         if (entryfunc) {
2889  *             const modval = entryfunc();
2890  *             modules[name] = modval;
2891  *
2892  *             return modval;
2893  *         }
2894  *     }
2895  *
2896  *     die(`Module ${name} not found`);
2897  * }
2898  * ```
2899  *
2900  * Due to the fact that `require()` is a runtime operation, module source code
2901  * is only lazily evaluated/loaded upon invoking the first require invocation,
2902  * which might lead to situations where errors in module sources are only
2903  * reported much later throughout the program execution. Unless runtime loading
2904  * of modules is absolutely required, e.g. to conditionally load extensions, the
2905  * compile time
2906  * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import|`import` syntax}
2907  * should be preferred.
2908  *
2909  * Returns the module value (typically an object) on success.
2910  *
2911  * Throws an exception if the module function threw an exception.
2912  *
2913  * Throws an exception if no matching module could be found, if the module
2914  * contains syntax errors or upon other I/O related problems.
2915  *
2916  * @function module:core#require
2917  *
2918  * @param {string} name
2919  * The name of the module to require in dotted notation.
2920  *
2921  * @returns {*}
2922  *
2923  * @example
2924  * // Require the `example/acme.uc` or `example/acme.so` module
2925  * const acme = require('example.acme');
2926  *
2927  * // Requiring the same name again will yield the cached instance
2928  * const acme2 = require('example.acme');
2929  * assert(acme === acme2);
2930  *
2931  * // Deleting the module dictionary entry will force a reload
2932  * delete modules['example.acme'];
2933  * const acme3 = require('example.acme');
2934  * assert(acme !== acme3);
2935  *
2936  * // Preloading a "virtual" module
2937  * modules['example.test'] = {
2938  *   hello: function() { print("This is the example module\n"); }
2939  * };
2940  *
2941  * const test = require('example.test');
2942  * test.hello();  // will print "This is the example module"
2943  */
2944 static uc_value_t *
2945 uc_require(uc_vm_t *vm, size_t nargs)
2946 {
2947         return uc_require_library(vm, uc_fn_arg(0), false);
2948 }
2949 
2950 /**
2951  * Convert the given IP address string to an array of byte values.
2952  *
2953  * IPv4 addresses result in arrays of 4 integers while IPv6 ones in arrays
2954  * containing 16 integers. The resulting array can be turned back into IP
2955  * address strings using the inverse `arrtoip()` function.
2956  *
2957  * Returns an array containing the address byte values.
2958  * Returns `null` if the given argument is not a string or an invalid IP.
2959  *
2960  * @function module:core#iptoarr
2961  *
2962  * @param {string} address
2963  * The IP address string to convert.
2964  *
2965  * @returns {?number[]}
2966  *
2967  * @example
2968  * iptoarr("192.168.1.1")              // [ 192, 168, 1, 1 ]
2969  * iptoarr("fe80::fc54:ff:fe82:abbd")  // [ 254, 128, 0, 0, 0, 0, 0, 0, 252, 84,
2970  *                                     //   0, 255, 254, 130, 171, 189 ])
2971  * iptoarr("foo")                      // null (invalid address)
2972  * iptoarr(123)                        // null (not a string)
2973  */
2974 static uc_value_t *
2975 uc_iptoarr(uc_vm_t *vm, size_t nargs)
2976 {
2977         uc_value_t *ip = uc_fn_arg(0);
2978         uc_value_t *res;
2979         union {
2980                 uint8_t u8[4];
2981                 struct in_addr in;
2982                 struct in6_addr in6;
2983         } a;
2984         int i;
2985 
2986         if (ucv_type(ip) != UC_STRING)
2987                 return NULL;
2988 
2989         if (inet_pton(AF_INET6, ucv_string_get(ip), &a)) {
2990                 res = ucv_array_new(vm);
2991 
2992                 for (i = 0; i < 16; i++)
2993                         ucv_array_push(res, ucv_int64_new(a.in6.s6_addr[i]));
2994 
2995                 return res;
2996         }
2997         else if (inet_pton(AF_INET, ucv_string_get(ip), &a)) {
2998                 res = ucv_array_new(vm);
2999 
3000                 ucv_array_push(res, ucv_int64_new(a.u8[0]));
3001                 ucv_array_push(res, ucv_int64_new(a.u8[1]));
3002                 ucv_array_push(res, ucv_int64_new(a.u8[2]));
3003                 ucv_array_push(res, ucv_int64_new(a.u8[3]));
3004 
3005                 return res;
3006         }
3007 
3008         return NULL;
3009 }
3010 
3011 static int
3012 check_byte(uc_value_t *v)
3013 {
3014         int n;
3015 
3016         if (ucv_type(v) != UC_INTEGER)
3017                 return -1;
3018 
3019         n = ucv_int64_get(v);
3020 
3021         if (n < 0 || n > 255)
3022                 return -1;
3023 
3024         return n;
3025 }
3026 
3027 /**
3028  * Convert the given input array of byte values to an IP address string.
3029  *
3030  * Input arrays of length 4 are converted to IPv4 addresses, arrays of length 16
3031  * to IPv6 ones. All other lengths are rejected. If any array element is not an
3032  * integer or exceeds the range 0..255 (inclusive), the array is rejected.
3033  *
3034  * Returns a string containing the formatted IP address.
3035  * Returns `null` if the input array was invalid.
3036  *
3037  * @function module:core#arrtoip
3038  *
3039  * @param {number[]} arr
3040  * The byte array to convert into an IP address string.
3041  *
3042  * @returns {?string}
3043  *
3044  * @example
3045  * arrtoip([ 192, 168, 1, 1 ])   // "192.168.1.1"
3046  * arrtoip([ 254, 128, 0, 0, 0, 0, 0, 0, 252, 84, 0, 255, 254, 130, 171, 189 ])
3047  *                               // "fe80::fc54:ff:fe82:abbd"
3048  * arrtoip([ 1, 2, 3])           // null (invalid length)
3049  * arrtoip([ 1, "2", -5, 300 ])  // null (invalid values)
3050  * arrtoip("123")                // null (not an array)
3051  */
3052 static uc_value_t *
3053 uc_arrtoip(uc_vm_t *vm, size_t nargs)
3054 {
3055         uc_value_t *arr = uc_fn_arg(0);
3056         union {
3057                 uint8_t u8[4];
3058                 struct in6_addr in6;
3059         } a;
3060         char buf[INET6_ADDRSTRLEN];
3061         int i, n;
3062 
3063         if (ucv_type(arr) != UC_ARRAY)
3064                 return NULL;
3065 
3066         switch (ucv_array_length(arr)) {
3067         case 4:
3068                 for (i = 0; i < 4; i++) {
3069                         n = check_byte(ucv_array_get(arr, i));
3070 
3071                         if (n < 0)
3072                                 return NULL;
3073 
3074                         a.u8[i] = n;
3075                 }
3076 
3077                 inet_ntop(AF_INET, &a, buf, sizeof(buf));
3078 
3079                 return ucv_string_new(buf);
3080 
3081         case 16:
3082                 for (i = 0; i < 16; i++) {
3083                         n = check_byte(ucv_array_get(arr, i));
3084 
3085                         if (n < 0)
3086                                 return NULL;
3087 
3088                         a.in6.s6_addr[i] = n;
3089                 }
3090 
3091                 inet_ntop(AF_INET6, &a, buf, sizeof(buf));
3092 
3093                 return ucv_string_new(buf);
3094 
3095         default:
3096                 return NULL;
3097         }
3098 }
3099 
3100 /**
3101  * Match the given string against the regular expression pattern specified as
3102  * the second argument.
3103  *
3104  * If the passed regular expression uses the `g` flag, the return value will be
3105  * an array of arrays describing all found occurrences within the string.
3106  *
3107  * Without the `g` modifier, an array describing the first match is returned.
3108  *
3109  * Returns `null` if the pattern was not found within the given string.
3110  *
3111  * @function module:core#match
3112  *
3113  * @param {string} str
3114  * The string to be matched against the pattern.
3115  *
3116  * @param {RegExp} pattern
3117  * The regular expression pattern.
3118  *
3119  * @returns {?Array}
3120  *
3121  * @example
3122  * match("foobarbaz", /b.(.)/)   // ["bar", "r"]
3123  * match("foobarbaz", /b.(.)/g)  // [["bar", "r"], ["baz", "z"]]
3124  */
3125 static uc_value_t *
3126 uc_match(uc_vm_t *vm, size_t nargs)
3127 {
3128         uc_value_t *subject = uc_fn_arg(0);
3129         uc_value_t *pattern = uc_fn_arg(1);
3130         uc_value_t *rv = NULL, *m;
3131         regmatch_t *pmatch = NULL;
3132         int eflags = 0, res;
3133         uc_regexp_t *re;
3134         bool freeable;
3135         char *p;
3136         size_t i;
3137 
3138         if (ucv_type(pattern) != UC_REGEXP || !subject)
3139                 return NULL;
3140 
3141         re = (uc_regexp_t *)pattern;
3142 
3143         pmatch = calloc(1 + re->regexp.re_nsub, sizeof(regmatch_t));
3144 
3145         if (!pmatch)
3146                 return NULL;
3147 
3148         p = uc_cast_string(vm, &subject, &freeable);
3149 
3150         while (true) {
3151                 res = regexec(&re->regexp, p, 1 + re->regexp.re_nsub, pmatch, eflags);
3152 
3153                 if (res == REG_NOMATCH)
3154                         break;
3155 
3156                 m = ucv_array_new(vm);
3157 
3158                 for (i = 0; i < 1 + re->regexp.re_nsub; i++) {
3159                         if (pmatch[i].rm_so != -1)
3160                                 ucv_array_push(m,
3161                                         ucv_string_new_length(p + pmatch[i].rm_so,
3162                                                               pmatch[i].rm_eo - pmatch[i].rm_so));
3163                         else
3164                                 ucv_array_push(m, NULL);
3165                 }
3166 
3167                 if (re->global) {
3168                         if (!rv)
3169                                 rv = ucv_array_new(vm);
3170 
3171                         ucv_array_push(rv, m);
3172 
3173                         if (pmatch[0].rm_so != pmatch[0].rm_eo)
3174                                 p += pmatch[0].rm_eo;
3175                         else if (*p)
3176                                 p++;
3177                         else
3178                                 break;
3179 
3180                         eflags |= REG_NOTBOL;
3181                 }
3182                 else {
3183                         rv = m;
3184                         break;
3185                 }
3186         }
3187 
3188         free(pmatch);
3189 
3190         if (freeable)
3191                 free(p);
3192 
3193         return rv;
3194 }
3195 
3196 static void
3197 uc_replace_cb(uc_vm_t *vm, uc_value_t *func,
3198               const char *subject, regmatch_t *pmatch, size_t plen,
3199               uc_stringbuf_t *resbuf)
3200 {
3201         uc_value_t *rv;
3202         size_t i;
3203 
3204         uc_vm_ctx_push(vm);
3205         uc_vm_stack_push(vm, ucv_get(func));
3206 
3207         for (i = 0; i < plen; i++) {
3208                 if (pmatch[i].rm_so != -1)
3209                         uc_vm_stack_push(vm,
3210                                 ucv_string_new_length(subject + pmatch[i].rm_so,
3211                                                       pmatch[i].rm_eo - pmatch[i].rm_so));
3212                 else
3213                         uc_vm_stack_push(vm, NULL);
3214         }
3215 
3216         if (uc_vm_call(vm, true, i) == EXCEPTION_NONE) {
3217                 rv = uc_vm_stack_pop(vm);
3218 
3219                 ucv_to_stringbuf(vm, resbuf, rv, false);
3220 
3221                 ucv_put(rv);
3222         }
3223 }
3224 
3225 static void
3226 uc_replace_str(uc_vm_t *vm, uc_value_t *str,
3227                const char *subject, regmatch_t *pmatch, size_t plen,
3228                uc_stringbuf_t *resbuf)
3229 {
3230         bool esc = false;
3231         char *p, *r;
3232         uint8_t i;
3233 
3234         for (p = r = ucv_to_string(vm, str); *p; p++) {
3235                 if (esc) {
3236                         switch (*p) {
3237                         case '&':
3238                                 if (pmatch[0].rm_so != -1)
3239                                         ucv_stringbuf_addstr(resbuf,
3240                                                 subject + pmatch[0].rm_so,
3241                                                 pmatch[0].rm_eo - pmatch[0].rm_so);
3242                                 break;
3243 
3244                         case '`':
3245                                 if (pmatch[0].rm_so != -1)
3246                                         ucv_stringbuf_addstr(resbuf, subject, pmatch[0].rm_so);
3247                                 break;
3248 
3249                         case '\'':
3250                                 if (pmatch[0].rm_so != -1)
3251                                         ucv_stringbuf_addstr(resbuf,
3252                                                 subject + pmatch[0].rm_eo,
3253                                                 strlen(subject + pmatch[0].rm_eo));
3254                                 break;
3255 
3256                         case '1':
3257                         case '2':
3258                         case '3':
3259                         case '4':
3260                         case '5':
3261                         case '6':
3262                         case '7':
3263                         case '8':
3264                         case '9':
3265                                 i = *p - '';
3266                                 if (i < plen && pmatch[i].rm_so != -1) {
3267                                         ucv_stringbuf_addstr(resbuf,
3268                                                 subject + pmatch[i].rm_so,
3269                                                 pmatch[i].rm_eo - pmatch[i].rm_so);
3270                                 }
3271                                 else {
3272                                         ucv_stringbuf_append(resbuf, "$");
3273                                         ucv_stringbuf_addstr(resbuf, p, 1);
3274                                 }
3275                                 break;
3276 
3277                         case '$':
3278                                 ucv_stringbuf_append(resbuf, "$");
3279                                 break;
3280 
3281                         default:
3282                                 ucv_stringbuf_append(resbuf, "$");
3283                                 ucv_stringbuf_addstr(resbuf, p, 1);
3284                         }
3285 
3286                         esc = false;
3287                 }
3288                 else if (*p == '$') {
3289                         esc = true;
3290                 }
3291                 else {
3292                         ucv_stringbuf_addstr(resbuf, p, 1);
3293                 }
3294         }
3295 
3296         free(r);
3297 }
3298 
3299 /**
3300  * Replace occurrences of the specified pattern in the string passed as the
3301  * first argument.
3302  *
3303  * - The pattern value may be either a regular expression or a plain string.
3304  * - The replace value may be a function which is invoked for each found pattern
3305  *   or any other value which is converted into a plain string and used as
3306  *   replacement.
3307  * - When an optional limit is specified, substitutions are performed only that
3308  *   many times.
3309  * - If the pattern is a regular expression and not using the `g` flag, then
3310  *   only the first occurrence in the string is replaced.
3311  * - If the `g` flag is used or if the pattern is not a regular expression, all
3312  *   occurrences are replaced.
3313  * - If the replace value is a callback function, it is invoked with the found
3314  *   substring as the first and any capture group values as subsequent
3315  *   parameters.
3316  * - If the replace value is a string, specific substrings are substituted
3317  *   before it is inserted into the result.
3318  *
3319  * Returns a new string with the pattern replaced.
3320  *
3321  * @function module:core#replace
3322  *
3323  * @param {string} str
3324  * The string in which to replace occurrences.
3325  *
3326  * @param {RegExp|string} pattern
3327  * The pattern to be replaced.
3328  *
3329  * @param {Function|string} replace
3330  * The replacement value.
3331  *
3332  * @param {number} [limit]
3333  * The optional limit of substitutions.
3334  *
3335  * @returns {string}
3336  *
3337  * @example
3338  * replace("barfoobaz", /(f)(o+)/g, "[$$|$`|$&|$'|$1|$2|$3]")  // bar[$|bar|foo|baz|f|oo|$3]baz
3339  * replace("barfoobaz", /(f)(o+)/g, uc)                        // barFOObaz
3340  * replace("barfoobaz", "a", "X")                              // bXrfoobXz
3341  * replace("barfoobaz", /(.)(.)(.)/g, function(m, c1, c2, c3) {
3342  *     return c3 + c2 + c1;
3343  * })                                                          // raboofzab
3344  * replace("aaaaa", "a", "x", 3)                               // xxxaa
3345  * replace("foo bar baz", /[ao]/g, "x", 3)                     // fxx bxr baz
3346  */
3347 static uc_value_t *
3348 uc_replace(uc_vm_t *vm, size_t nargs)
3349 {
3350         char *sb = NULL, *pt = NULL, *p, *l;
3351         uc_value_t *subject = uc_fn_arg(0);
3352         uc_value_t *pattern = uc_fn_arg(1);
3353         uc_value_t *replace = uc_fn_arg(2);
3354         uc_value_t *limitval = uc_fn_arg(3);
3355         bool sb_freeable, pt_freeable;
3356         regmatch_t *pmatch = NULL;
3357         size_t pl, nmatch, limit;
3358         uc_regexp_t *re = NULL;
3359         uc_stringbuf_t *resbuf;
3360         int eflags = 0, res;
3361 
3362         if (!pattern || !subject || !replace)
3363                 return NULL;
3364 
3365         nmatch = 1;
3366 
3367         if (ucv_type(pattern) == UC_REGEXP) {
3368                 re = (uc_regexp_t *)pattern;
3369                 nmatch += re->regexp.re_nsub;
3370         }
3371 
3372         pmatch = calloc(nmatch, sizeof(regmatch_t));
3373 
3374         if (!pmatch)
3375                 return NULL;
3376 
3377         sb = uc_cast_string(vm, &subject, &sb_freeable);
3378         resbuf = ucv_stringbuf_new();
3379         limit = limitval ? ucv_uint64_get(limitval) : SIZE_MAX;
3380 
3381         if (re) {
3382                 p = sb;
3383 
3384                 while (limit > 0) {
3385                         res = regexec(&re->regexp, p, nmatch, pmatch, eflags);
3386 
3387                         if (res == REG_NOMATCH)
3388                                 break;
3389 
3390                         ucv_stringbuf_addstr(resbuf, p, pmatch[0].rm_so);
3391 
3392                         if (ucv_is_callable(replace))
3393                                 uc_replace_cb(vm, replace, p, pmatch, nmatch, resbuf);
3394                         else
3395                                 uc_replace_str(vm, replace, p, pmatch, nmatch, resbuf);
3396 
3397                         if (pmatch[0].rm_so != pmatch[0].rm_eo)
3398                                 p += pmatch[0].rm_eo;
3399                         else if (*p)
3400                                 ucv_stringbuf_addstr(resbuf, p++, 1);
3401                         else
3402                                 break;
3403 
3404                         if (re->global)
3405                                 eflags |= REG_NOTBOL;
3406                         else
3407                                 break;
3408 
3409                         limit--;
3410                 }
3411 
3412                 ucv_stringbuf_addstr(resbuf, p, strlen(p));
3413         }
3414         else {
3415                 pt = uc_cast_string(vm, &pattern, &pt_freeable);
3416                 pl = strlen(pt);
3417 
3418                 l = p = sb;
3419 
3420                 while (limit > 0) {
3421                         if (pl == 0 || !strncmp(p, pt, pl)) {
3422                                 ucv_stringbuf_addstr(resbuf, l, p - l);
3423 
3424                                 pmatch[0].rm_so = p - l;
3425                                 pmatch[0].rm_eo = pmatch[0].rm_so + pl;
3426 
3427                                 if (ucv_is_callable(replace))
3428                                         uc_replace_cb(vm, replace, l, pmatch, 1, resbuf);
3429                                 else
3430                                         uc_replace_str(vm, replace, l, pmatch, 1, resbuf);
3431 
3432                                 if (pl) {
3433                                         l = p + pl;
3434                                         p += pl - 1;
3435                                 }
3436                                 else {
3437                                         l = p;
3438                                 }
3439 
3440                                 limit--;
3441                         }
3442 
3443                         if (!*p++)
3444                                 break;
3445                 }
3446 
3447                 ucv_stringbuf_addstr(resbuf, l, strlen(l));
3448 
3449                 if (pt_freeable)
3450                         free(pt);
3451         }
3452 
3453         free(pmatch);
3454 
3455         if (sb_freeable)
3456                 free(sb);
3457 
3458         return ucv_stringbuf_finish(resbuf);
3459 }
3460 
3461 static struct json_tokener *
3462 uc_json_from_object(uc_vm_t *vm, uc_value_t *obj, json_object **jso)
3463 {
3464         bool trail = false, eof = false;
3465         enum json_tokener_error err;
3466         struct json_tokener *tok;
3467         uc_value_t *rfn, *rbuf;
3468         uc_stringbuf_t *buf;
3469 
3470         rfn = ucv_property_get(obj, "read");
3471 
3472         if (!ucv_is_callable(rfn)) {
3473                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3474                                       "Input object does not implement read() method");
3475 
3476                 return NULL;
3477         }
3478 
3479         tok = xjs_new_tokener();
3480 
3481         while (true) {
3482                 uc_vm_stack_push(vm, ucv_get(obj));
3483                 uc_vm_stack_push(vm, ucv_get(rfn));
3484                 uc_vm_stack_push(vm, ucv_int64_new(1024));
3485 
3486                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
3487                         json_tokener_free(tok);
3488 
3489                         return NULL;
3490                 }
3491 
3492                 rbuf = uc_vm_stack_pop(vm);
3493 
3494                 /* check EOF */
3495                 eof = (rbuf == NULL || (ucv_type(rbuf) == UC_STRING && ucv_string_length(rbuf) == 0));
3496 
3497                 /* on EOF, stop parsing unless trailing garbage was detected which handled below */
3498                 if (eof && !trail) {
3499                         ucv_put(rbuf);
3500 
3501                         /* Didn't parse a complete object yet, possibly a non-delimitted atomic value
3502                            such as `null`, `true` etc. - nudge parser by sending final zero byte.
3503                            See json-c issue #681 <https://github.com/json-c/json-c/issues/681> */
3504                         if (json_tokener_get_error(tok) == json_tokener_continue)
3505                                 *jso = json_tokener_parse_ex(tok, "\0", 1);
3506 
3507                         break;
3508                 }
3509 
3510                 if (trail || *jso) {
3511                         uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3512                                               "Trailing garbage after JSON data");
3513 
3514                         json_tokener_free(tok);
3515                         ucv_put(rbuf);
3516 
3517                         return NULL;
3518                 }
3519 
3520                 if (ucv_type(rbuf) != UC_STRING) {
3521                         buf = xprintbuf_new();
3522                         ucv_to_stringbuf_formatted(vm, buf, rbuf, 0, '\0', 0);
3523 
3524                         *jso = json_tokener_parse_ex(tok, buf->buf, printbuf_length(buf));
3525 
3526                         trail = (json_tokener_get_error(tok) == json_tokener_success &&
3527                                  json_tokener_get_parse_end(tok) < (size_t)printbuf_length(buf));
3528 
3529                         printbuf_free(buf);
3530                 }
3531                 else {
3532                         *jso = json_tokener_parse_ex(tok, ucv_string_get(rbuf), ucv_string_length(rbuf));
3533 
3534                         trail = (json_tokener_get_error(tok) == json_tokener_success &&
3535                                  json_tokener_get_parse_end(tok) < ucv_string_length(rbuf));
3536                 }
3537 
3538                 ucv_put(rbuf);
3539 
3540                 err = json_tokener_get_error(tok);
3541 
3542                 if (err != json_tokener_success && err != json_tokener_continue)
3543                         break;
3544         }
3545 
3546         return tok;
3547 }
3548 
3549 static struct json_tokener *
3550 uc_json_from_string(uc_vm_t *vm, uc_value_t *str, json_object **jso)
3551 {
3552         struct json_tokener *tok = xjs_new_tokener();
3553         size_t i;
3554         char *p;
3555 
3556         /* NB: the len + 1 here is intentional to pass the terminating \0 byte
3557          * to the json-c parser. This is required to work-around upstream
3558          * issue #681 <https://github.com/json-c/json-c/issues/681> */
3559         *jso = json_tokener_parse_ex(tok, ucv_string_get(str), ucv_string_length(str) + 1);
3560 
3561         if (json_tokener_get_error(tok) == json_tokener_success) {
3562                 p = ucv_string_get(str);
3563 
3564                 for (i = json_tokener_get_parse_end(tok); i < ucv_string_length(str); i++) {
3565                         if (!isspace(p[i])) {
3566                                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3567                                                       "Trailing garbage after JSON data");
3568 
3569 
3570                                 json_tokener_free(tok);
3571 
3572                                 return NULL;
3573                         }
3574                 }
3575         }
3576 
3577         return tok;
3578 }
3579 
3580 /**
3581  * Parse the given string or resource as JSON and return the resulting value.
3582  *
3583  * If the input argument is a plain string, it is directly parsed as JSON.
3584  *
3585  * If an array, object or resource value is given, this function will attempt to
3586  * invoke a `read()` method on it to read chunks of input text to incrementally
3587  * parse as JSON data. Reading will stop if the object's `read()` method returns
3588  * either `null` or an empty string.
3589  *
3590  * Throws an exception on parse errors, trailing garbage, or premature EOF.
3591  *
3592  * Returns the parsed JSON data.
3593  *
3594  * @function module:core#json
3595  *
3596  * @param {string} str_or_resource
3597  * The string or resource object to be parsed as JSON.
3598  *
3599  * @returns {*}
3600  *
3601  * @example
3602  * json('{"a":true, "b":123}')   // { "a": true, "b": 123 }
3603  * json('[1,2,')                 // Throws an exception
3604  *
3605  * import { open } from 'fs';
3606  * let fd = open('example.json', 'r');
3607  * json(fd);                     // will keep invoking `fd.read()` until EOF and
3608  *                               // incrementally parse each read chunk.
3609  *
3610  * let x = proto(
3611  *     [ '{"foo":', 'true, ', '"bar":', 'false}' ],
3612  *     { read: function() { return shift(this) } }
3613  * );
3614  * json(x);                      // will keep invoking `x.read()` until array
3615  *                               // is empty incrementally parse each piece
3616  *
3617  */
3618 static uc_value_t *
3619 uc_json(uc_vm_t *vm, size_t nargs)
3620 {
3621         uc_value_t *rv = NULL, *src = uc_fn_arg(0);
3622         struct json_tokener *tok = NULL;
3623         enum json_tokener_error err;
3624         json_object *jso = NULL;
3625 
3626         switch (ucv_type(src)) {
3627         case UC_STRING:
3628                 tok = uc_json_from_string(vm, src, &jso);
3629                 break;
3630 
3631         case UC_RESOURCE:
3632         case UC_OBJECT:
3633         case UC_ARRAY:
3634                 tok = uc_json_from_object(vm, src, &jso);
3635                 break;
3636 
3637         default:
3638                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3639                                       "Passed value is neither a string nor an object");
3640         }
3641 
3642         if (!tok)
3643                 goto out;
3644 
3645         err = json_tokener_get_error(tok);
3646 
3647         if (err == json_tokener_continue) {
3648                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3649                                       "Unexpected end of string in JSON data");
3650 
3651                 goto out;
3652         }
3653         else if (err != json_tokener_success) {
3654                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3655                                       "Failed to parse JSON string: %s",
3656                                       json_tokener_error_desc(err));
3657 
3658                 goto out;
3659         }
3660 
3661         rv = ucv_from_json(vm, jso);
3662 
3663 out:
3664         if (tok)
3665                 json_tokener_free(tok);
3666 
3667         json_object_put(jso);
3668 
3669         return rv;
3670 }
3671 
3672 static char *
3673 include_path(const char *curpath, const char *incpath)
3674 {
3675         char *dup, *res;
3676         int len;
3677 
3678         if (*incpath == '/')
3679                 return realpath(incpath, NULL);
3680 
3681         dup = curpath ? strrchr(curpath, '/') : NULL;
3682 
3683         if (dup)
3684                 len = asprintf(&res, "%.*s/%s", (int)(dup - curpath), curpath, incpath);
3685         else
3686                 len = asprintf(&res, "./%s", incpath);
3687 
3688         if (len == -1)
3689                 return NULL;
3690 
3691         dup = realpath(res, NULL);
3692 
3693         free(res);
3694 
3695         return dup;
3696 }
3697 
3698 static uc_value_t *
3699 uc_include_common(uc_vm_t *vm, size_t nargs, bool raw_mode)
3700 {
3701         uc_value_t *path = uc_fn_arg(0);
3702         uc_value_t *scope = uc_fn_arg(1);
3703         uc_value_t *rv = NULL, *sc = NULL;
3704         uc_closure_t *closure = NULL;
3705         size_t i;
3706         char *p;
3707 
3708         if (ucv_type(path) != UC_STRING) {
3709                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3710                                       "Passed filename is not a string");
3711 
3712                 return NULL;
3713         }
3714 
3715         if (scope && ucv_type(scope) != UC_OBJECT) {
3716                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3717                                       "Passed scope value is not an object");
3718 
3719                 return NULL;
3720         }
3721 
3722         /* find calling closure */
3723         for (i = vm->callframes.count; i > 0; i--) {
3724                 closure = vm->callframes.entries[i - 1].closure;
3725 
3726                 if (closure)
3727                         break;
3728         }
3729 
3730         if (!closure)
3731                 return NULL;
3732 
3733         p = include_path(uc_program_function_source(closure->function)->runpath, ucv_string_get(path));
3734 
3735         if (!p) {
3736                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3737                                       "Include file not found");
3738 
3739                 return NULL;
3740         }
3741 
3742         if (ucv_prototype_get(scope)) {
3743                 sc = ucv_get(scope);
3744         }
3745         else if (scope) {
3746                 sc = ucv_object_new(vm);
3747 
3748                 ucv_object_foreach(scope, key, val)
3749                         ucv_object_add(sc, key, ucv_get(val));
3750 
3751                 ucv_prototype_set(sc, ucv_get(uc_vm_scope_get(vm)));
3752         }
3753         else {
3754                 sc = ucv_get(uc_vm_scope_get(vm));
3755         }
3756 
3757         if (uc_require_ucode(vm, p, sc, &rv, raw_mode, false))
3758                 ucv_put(rv);
3759 
3760         ucv_put(sc);
3761         free(p);
3762 
3763         return NULL;
3764 }
3765 
3766 /**
3767  * Evaluate and include the file at the given path and optionally override the
3768  * execution scope with the given scope object.
3769  *
3770  * By default, the file is executed within the same scope as the calling
3771  * `include()`, but by passing an object as the second argument, it is possible
3772  * to extend the scope available to the included file.
3773  *
3774  * This is useful to supply additional properties as global variables to the
3775  * included code. To sandbox included code, that is giving it only access to
3776  * explicitly provided properties, the `proto()` function can be used to create
3777  * a scope object with an empty prototype.
3778  *
3779  * @function module:core#include
3780  *
3781  * @param {string} path
3782  * The path to the file to be included.
3783  *
3784  * @param {Object} [scope]
3785  * The optional scope object to override the execution scope.
3786  *
3787  * @example
3788  * // Load and execute "foo.uc" immediately
3789  * include("./foo.uc")
3790  *
3791  * // Execute the "supplemental.ucode" in an extended scope and make the "foo"
3792  * // and "bar" properties available as global variables
3793  * include("./supplemental.uc", {
3794  *   foo: true,
3795  *   bar: 123
3796  * })
3797  *
3798  * // Execute the "untrusted.ucode" in a sandboxed scope and make the "foo" and
3799  * // "bar" variables as well as the "print" function available to it.
3800  * // By assigning an empty prototype object to the scope, included code has no
3801  * // access to other global values anymore.
3802  * include("./untrusted.uc", proto({
3803  *   foo: true,
3804  *   bar: 123,
3805  *   print: print
3806  * }, {}))
3807  */
3808 static uc_value_t *
3809 uc_include(uc_vm_t *vm, size_t nargs)
3810 {
3811         return uc_include_common(vm, nargs, vm->config && vm->config->raw_mode);
3812 }
3813 
3814 /**
3815  * When invoked with a string value as the first argument, the function acts
3816  * like `include()` but captures the output of the included file as a string and
3817  * returns the captured contents.
3818  *
3819  * The second argument is treated as the scope.
3820  *
3821  * When invoked with a function value as the first argument, `render()` calls
3822  * the given function and passes all subsequent arguments to it.
3823  *
3824  * Any output produced by the called function is captured and returned as a
3825  * string. The return value of the called function is discarded.
3826  *
3827  * @function module:core#render
3828  *
3829  * @param {string|Function} path_or_func
3830  * The path to the file or the function to be rendered.
3831  *
3832  * @param {Object|*} [scope_or_fnarg1]
3833  * The optional scope or the first argument for the function.
3834  *
3835  * @param {*} [fnarg2]
3836  * The second argument for the function.
3837  *
3838  * @param {...*} [fnargN]
3839  * Additional arguments for the function.
3840  *
3841  * @returns {string}
3842  *
3843  * @example
3844  * // Renders template file with given scope and captures the output as a string
3845  * const output = render("./template.uc", { foo: "bar" });
3846  *
3847  * // Calls a function, captures the output, and returns it as a string
3848  * const result = render(function(name) {
3849  *     printf("Hello, %s!\n", name);
3850  * }, "Alice");
3851  */
3852 static uc_value_t *
3853 uc_render(uc_vm_t *vm, size_t nargs)
3854 {
3855         uc_string_t hdr = { .header = { .type = UC_STRING, .refcount = 1 } };
3856         uc_string_t *ustr = NULL;
3857         FILE *mem, *prev;
3858         size_t len = 0;
3859 
3860         mem = open_memstream((char **)&ustr, &len);
3861 
3862         if (!mem)
3863                 goto out;
3864 
3865         /* reserve space for uc_string_t header... */
3866         if (fwrite(&hdr, 1, sizeof(hdr), mem) != sizeof(hdr))
3867                 goto out;
3868 
3869         /* divert VM output to memory fd */
3870         prev = vm->output;
3871         vm->output = mem;
3872 
3873         /* execute function */
3874         if (ucv_is_callable(uc_fn_arg(0)))
3875                 (void) uc_vm_call(vm, false, nargs - 1);
3876 
3877         /* execute include */
3878         else
3879                 (void) uc_include_common(vm, nargs, false);
3880 
3881         /* restore previous VM output */
3882         vm->output = prev;
3883         fclose(mem);
3884 
3885         /* update uc_string_t length */
3886         ustr->length = len - sizeof(*ustr);
3887 
3888         return &ustr->header;
3889 
3890 out:
3891         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3892                               "Unable to initialize output memory: %s",
3893                               strerror(errno));
3894 
3895         if (mem)
3896                 fclose(mem);
3897 
3898         free(ustr);
3899 
3900         return NULL;
3901 }
3902 
3903 /**
3904  * Print any of the given values to stderr. Arrays and objects are converted to
3905  * their JSON representation.
3906  *
3907  * Returns the amount of bytes printed.
3908  *
3909  * @function module:core#warn
3910  *
3911  * @param {...*} x
3912  * The values to be printed.
3913  *
3914  * @returns {number}
3915  *
3916  * @example
3917  * warn("Hello", "world");  // Print "Helloworld" to stderr
3918  * warn({ key: "value" });  // Print JSON representation of the object to stderr
3919  */
3920 static uc_value_t *
3921 uc_warn(uc_vm_t *vm, size_t nargs)
3922 {
3923         return uc_print_common(vm, nargs, stderr);
3924 }
3925 
3926 /**
3927  * Executes the given command, waits for completion, and returns the resulting
3928  * exit code.
3929  *
3930  * The command argument may be either a string, in which case it is passed to
3931  * `/bin/sh -c`, or an array, which is directly converted into an `execv()`
3932  * argument vector.
3933  *
3934  *  - If the program terminated normally, a positive integer holding the
3935  *    program's `exit()` code is returned.
3936  *  - If the program was terminated by an uncaught signal, a negative signal
3937  *    number is returned.
3938  *  - If the optional timeout argument is specified, the program is terminated
3939  *    by `SIGKILL` after that many milliseconds if it doesn't complete within
3940  *    the timeout.
3941  *
3942  * Omitting the timeout argument or passing `0` disables the command timeout.
3943  *
3944  * Returns the program exit code.
3945  *
3946  * @function module:core#system
3947  *
3948  * @param {string|Array} command
3949  * The command to be executed.
3950  *
3951  * @param {number} [timeout]
3952  * The optional timeout in milliseconds.
3953  *
3954  * @returns {number}
3955  *
3956  * @example
3957  * // Execute through `/bin/sh`
3958  * // prints "Hello world" to stdout and returns 3
3959  * system("echo 'Hello world' && exit 3");
3960  *
3961  * // Execute argument vector
3962  * // prints the UNIX timestamp to stdout and returns 0
3963  * system(["/usr/bin/date", "+%s"]);
3964  *
3965  * // Apply a timeout
3966  * // returns -9
3967  * system("sleep 3 && echo 'Success'", 1000);
3968  */
3969 static uc_value_t *
3970 uc_system(uc_vm_t *vm, size_t nargs)
3971 {
3972         uc_value_t *cmdline = uc_fn_arg(0);
3973         uc_value_t *timeout = uc_fn_arg(1);
3974         const char **arglist, *fn;
3975         sigset_t sigmask, sigomask;
3976         struct timespec ts;
3977         size_t i, len;
3978         int64_t tms;
3979         pid_t cld;
3980         int rc;
3981 
3982         if (timeout && (ucv_type(timeout) != UC_INTEGER || ucv_int64_get(timeout) < 0)) {
3983                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3984                                       "Invalid timeout specified");
3985 
3986                 return NULL;
3987         }
3988 
3989         switch (ucv_type(cmdline)) {
3990         case UC_STRING:
3991                 arglist = xalloc(sizeof(*arglist) * 4);
3992                 arglist[0] = xstrdup("/bin/sh");
3993                 arglist[1] = xstrdup("-c");
3994                 arglist[2] = ucv_to_string(vm, cmdline);
3995                 arglist[3] = NULL;
3996                 break;
3997 
3998         case UC_ARRAY:
3999                 len = ucv_array_length(cmdline);
4000 
4001                 if (len == 0) {
4002                         uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4003                                               "Passed command array is empty");
4004 
4005                         return NULL;
4006                 }
4007 
4008                 arglist = xalloc(sizeof(*arglist) * (len + 1));
4009 
4010                 for (i = 0; i < len; i++)
4011                         arglist[i] = ucv_to_string(vm, ucv_array_get(cmdline, i));
4012 
4013                 arglist[i] = NULL;
4014 
4015                 break;
4016 
4017         default:
4018                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4019                                       "Passed command is neither string nor array");
4020 
4021                 return NULL;
4022         }
4023 
4024         tms = timeout ? ucv_int64_get(timeout) : 0;
4025 
4026         if (tms > 0) {
4027                 sigemptyset(&sigmask);
4028                 sigaddset(&sigmask, SIGCHLD);
4029 
4030                 if (sigprocmask(SIG_BLOCK, &sigmask, &sigomask) < 0) {
4031                         fn = "sigprocmask";
4032                         goto fail;
4033                 }
4034         }
4035 
4036         cld = fork();
4037 
4038         switch (cld) {
4039         case -1:
4040                 fn = "fork";
4041                 goto fail;
4042 
4043         case 0:
4044                 execvp(arglist[0], (char * const *)arglist);
4045                 exit(-1);
4046 
4047                 break;
4048 
4049         default:
4050                 if (tms > 0) {
4051                         ts.tv_sec = tms / 1000;
4052                         ts.tv_nsec = (tms % 1000) * 1000000;
4053 
4054                         while (1) {
4055                                 if (sigtimedwait(&sigmask, NULL, &ts) < 0) {
4056                                         if (errno == EINTR)
4057                                                 continue;
4058 
4059                                         if (errno != EAGAIN) {
4060                                                 fn = "sigtimedwait";
4061                                                 goto fail;
4062                                         }
4063 
4064                                         kill(cld, SIGKILL);
4065                                 }
4066 
4067                                 break;
4068                         }
4069                 }
4070 
4071                 while (waitpid(cld, &rc, 0) < 0) {
4072                         if (errno == EINTR)
4073                                 continue;
4074 
4075                         fn = "waitpid";
4076                         goto fail;
4077                 }
4078 
4079                 if (tms > 0)
4080                         sigprocmask(SIG_SETMASK, &sigomask, NULL);
4081 
4082                 for (i = 0; arglist[i]; i++)
4083                         free((char *)arglist[i]);
4084 
4085                 free(arglist);
4086 
4087                 if (WIFEXITED(rc))
4088                         return ucv_int64_new(WEXITSTATUS(rc));
4089                 else if (WIFSIGNALED(rc))
4090                         return ucv_int64_new(-WTERMSIG(rc));
4091                 else if (WIFSTOPPED(rc))
4092                         return ucv_int64_new(-WSTOPSIG(rc));
4093 
4094                 return NULL;
4095         }
4096 
4097 fail:
4098         if (tms > 0)
4099                 sigprocmask(SIG_SETMASK, &sigomask, NULL);
4100 
4101         for (i = 0; arglist[i]; i++)
4102                 free((char *)arglist[i]);
4103 
4104         free(arglist);
4105 
4106         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
4107                               "%s(): %s", fn, strerror(errno));
4108 
4109         return NULL;
4110 }
4111 
4112 /**
4113  * Enables or disables VM opcode tracing.
4114  *
4115  * When invoked with a positive non-zero level, opcode tracing is enabled and
4116  * debug information is printed to stderr as the program is executed.
4117  *
4118  * Invoking `trace()` with zero as an argument turns off opcode tracing.
4119  *
4120  * @function module:core#trace
4121  *
4122  * @param {number} level
4123  * The level of tracing to enable.
4124  *
4125  * @example
4126  * trace(1);   // Enables opcode tracing
4127  * trace(0);   // Disables opcode tracing
4128  */
4129 static uc_value_t *
4130 uc_trace(uc_vm_t *vm, size_t nargs)
4131 {
4132         uc_value_t *level = uc_fn_arg(0);
4133         uint8_t prev_level;
4134 
4135         if (ucv_type(level) != UC_INTEGER) {
4136                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid level specified");
4137 
4138                 return NULL;
4139         }
4140 
4141         prev_level = vm->trace;
4142         vm->trace = ucv_int64_get(level);
4143 
4144         return ucv_int64_new(prev_level);
4145 }
4146 
4147 /**
4148  * Get or set the prototype of the array or object value `val`.
4149  *
4150  * When invoked without a second argument, the function returns the current
4151  * prototype of the value in `val` or `null` if there is no prototype or if the
4152  * given value is neither an object nor an array.
4153  *
4154  * When invoked with a second prototype argument, the given `proto` value is set
4155  * as the prototype on the array or object in `val`.
4156  *
4157  * Throws an exception if the given prototype value is not an object.
4158  *
4159  * @function module:core#proto
4160  *
4161  * @param {Array|Object} val
4162  * The array or object value.
4163  *
4164  * @param {Object} [proto]
4165  * The optional prototype object.
4166  *
4167  * @returns {?Object}
4168  *
4169  * @example
4170  * const arr = [1, 2, 3];
4171  * proto(arr);                 // Returns the current prototype of the array (null by default)
4172  * proto(arr, { foo: true });  // Sets the given object as the prototype of the array
4173  */
4174 static uc_value_t *
4175 uc_proto(uc_vm_t *vm, size_t nargs)
4176 {
4177         uc_value_t *val = uc_fn_arg(0);
4178         uc_value_t *proto = NULL;
4179 
4180         if (nargs < 2)
4181                 return ucv_get(ucv_prototype_get(val));
4182 
4183         proto = uc_fn_arg(1);
4184 
4185         if (!ucv_prototype_set(val, proto))
4186                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed value is neither a prototype, resource or object");
4187 
4188         ucv_get(proto);
4189 
4190         return ucv_get(val);
4191 }
4192 
4193 /**
4194  * Pause execution for the given amount of milliseconds.
4195  *
4196  * @function module:core#sleep
4197  *
4198  * @param {number} milliseconds
4199  * The amount of milliseconds to sleep.
4200  *
4201  * @returns {boolean}
4202  *
4203  * @example
4204  * sleep(1000);                          // Sleeps for 1 second
4205  */
4206 static uc_value_t *
4207 uc_sleep(uc_vm_t *vm, size_t nargs)
4208 {
4209         uc_value_t *duration = uc_fn_arg(0);
4210         struct timeval tv;
4211         int64_t ms;
4212 
4213         ms = ucv_to_integer(duration);
4214 
4215         if (errno != 0 || ms <= 0)
4216                 return ucv_boolean_new(false);
4217 
4218         tv.tv_sec = ms / 1000;
4219         tv.tv_usec = (ms % 1000) * 1000;
4220 
4221         select(0, NULL, NULL, NULL, &tv);
4222 
4223         return ucv_boolean_new(true);
4224 }
4225 
4226 /**
4227  * Raise an exception with the given message parameter when the value in `cond`
4228  * is not truish.
4229  *
4230  * When `message` is omitted, the default value is `Assertion failed`.
4231  *
4232  * @function module:core#assert
4233  *
4234  * @param {*} cond
4235  * The value to check for truthiness.
4236  *
4237  * @param {string} [message]
4238  * The message to include in the exception.
4239  *
4240  * @throws {Error} When the condition is falsy.
4241  *
4242  * @example
4243  * assert(true, "This is true");  // No exception is raised
4244  * assert(false);                 // Exception is raised with the default message "Assertion failed"
4245  */
4246 static uc_value_t *
4247 uc_assert(uc_vm_t *vm, size_t nargs)
4248 {
4249         uc_value_t *cond = uc_fn_arg(0);
4250         uc_value_t *msg = uc_fn_arg(1);
4251         bool freeable = false;
4252         char *s;
4253 
4254         if (!ucv_is_truish(cond)) {
4255                 s = msg ? uc_cast_string(vm, &msg, &freeable) : "Assertion failed";
4256 
4257                 uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s);
4258 
4259                 if (freeable)
4260                         free(s);
4261 
4262                 return NULL;
4263         }
4264 
4265         return ucv_get(cond);
4266 }
4267 
4268 /**
4269  * Construct a regular expression instance from the given `source` pattern
4270  * string and any flags optionally specified by the `flags` argument.
4271  *
4272  *  - Throws a type error exception if `flags` is not a string or if the string
4273  *    in `flags` contains unrecognized regular expression flag characters.
4274  *  - Throws a syntax error when the pattern in `source` cannot be compiled into
4275  *    a valid regular expression.
4276  *
4277  * Returns the compiled regular expression value.
4278  *
4279  * @function module:core#regexp
4280  *
4281  * @param {string} source
4282  * The pattern string.
4283  *
4284  * @param {string} [flags]
4285  * The optional regular expression flags.
4286  *
4287  * @returns {RegExp}
4288  *
4289  * @example
4290  * regexp('foo.*bar', 'is');   // equivalent to /foo.*bar/is
4291  * regexp('foo.*bar', 'x');    // throws a "Type error: Unrecognized flag character 'x'" exception
4292  * regexp('foo.*(');           // throws a "Syntax error: Unmatched ( or \( exception"
4293  */
4294 static uc_value_t *
4295 uc_regexp(uc_vm_t *vm, size_t nargs)
4296 {
4297         bool icase = false, newline = false, global = false, freeable;
4298         uc_value_t *source = uc_fn_arg(0);
4299         uc_value_t *flags = uc_fn_arg(1);
4300         uc_value_t *regex = NULL;
4301         char *p, *err = NULL;
4302 
4303         if (flags) {
4304                 if (ucv_type(flags) != UC_STRING) {
4305                         uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Given flags argument is not a string");
4306 
4307                         return NULL;
4308                 }
4309 
4310                 for (p = ucv_string_get(flags); *p; p++) {
4311                         switch (*p) {
4312                         case 'i':
4313                                 icase = true;
4314                                 break;
4315 
4316                         case 's':
4317                                 newline = true;
4318                                 break;
4319 
4320                         case 'g':
4321                                 global = true;
4322                                 break;
4323 
4324                         default:
4325                                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Unrecognized flag character '%c'", *p);
4326 
4327                                 return NULL;
4328                         }
4329                 }
4330         }
4331 
4332         p = uc_cast_string(vm, &source, &freeable);
4333         regex = ucv_regexp_new(p, icase, newline, global, &err);
4334 
4335         if (freeable)
4336                 free(p);
4337 
4338         if (err) {
4339                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "%s", err);
4340                 ucv_put(regex);
4341                 free(err);
4342 
4343                 return NULL;
4344         }
4345 
4346         return regex;
4347 }
4348 
4349 /**
4350  * Match the given subject against the supplied wildcard (file glob) pattern.
4351  *
4352  *  - If a truthy value is supplied as the third argument, case-insensitive
4353  *    matching is performed.
4354  *  - If a non-string value is supplied as the subject, it is converted into a
4355  *    string before being matched.
4356  *
4357  * Returns `true` when the value matched the given pattern, otherwise `false`.
4358  *
4359  * @function module:core#wildcard
4360  *
4361  * @param {*} subject
4362  * The subject to match against the wildcard pattern.
4363  *
4364  * @param {string} pattern
4365  * The wildcard pattern.
4366  *
4367  * @param {boolean} [nocase]
4368  * Whether to perform case-insensitive matching.
4369  *
4370  * @returns {boolean}
4371  *
4372  * @example
4373  * wildcard("file.txt", "*.txt");        // Returns true
4374  * wildcard("file.txt", "*.TXT", true);  // Returns true (case-insensitive match)
4375  * wildcard("file.txt", "*.jpg");        // Returns false
4376  */
4377 static uc_value_t *
4378 uc_wildcard(uc_vm_t *vm, size_t nargs)
4379 {
4380         uc_value_t *subject = uc_fn_arg(0);
4381         uc_value_t *pattern = uc_fn_arg(1);
4382         uc_value_t *icase = uc_fn_arg(2);
4383         int flags = 0, rv;
4384         bool freeable;
4385         char *s;
4386 
4387         if (!subject || ucv_type(pattern) != UC_STRING)
4388                 return NULL;
4389 
4390         if (ucv_is_truish(icase))
4391                 flags |= FNM_CASEFOLD;
4392 
4393         s = uc_cast_string(vm, &subject, &freeable);
4394         rv = fnmatch(ucv_string_get(pattern), s, flags);
4395 
4396         if (freeable)
4397                 free(s);
4398 
4399         return ucv_boolean_new(rv == 0);
4400 }
4401 
4402 /**
4403  * Determine the path of the source file currently being executed by ucode.
4404  *
4405  * @function module:core#sourcepath
4406  *
4407  * @param {number} [depth=0]
4408  * The depth to walk up the call stack.
4409  *
4410  * @param {boolean} [dironly]
4411  * Whether to return only the directory portion of the source file path.
4412  *
4413  * @returns {?string}
4414  *
4415  * @example
4416  * sourcepath();         // Returns the path of the currently executed file
4417  * sourcepath(1);        // Returns the path of the parent source file
4418  * sourcepath(2, true);  // Returns the directory portion of the grandparent source file path
4419  */
4420 static uc_value_t *
4421 uc_sourcepath(uc_vm_t *vm, size_t nargs)
4422 {
4423         uc_value_t *calldepth = uc_fn_arg(0);
4424         uc_value_t *dironly = uc_fn_arg(1);
4425         uc_value_t *rv = NULL;
4426         uc_callframe_t *frame;
4427         char *path = NULL;
4428         int64_t depth;
4429         size_t i;
4430 
4431         depth = ucv_to_integer(calldepth);
4432 
4433         if (errno)
4434                 depth = 0;
4435 
4436         for (i = vm->callframes.count; i > 0; i--) {
4437                 frame = &vm->callframes.entries[i - 1];
4438 
4439                 if (!frame->closure)
4440                         continue;
4441 
4442                 if (depth > 0) {
4443                         depth--;
4444                         continue;
4445                 }
4446 
4447                 path = realpath(uc_program_function_source(frame->closure->function)->runpath, NULL);
4448                 break;
4449         }
4450 
4451         if (path) {
4452                 if (ucv_is_truish(dironly))
4453                         rv = ucv_string_new(dirname(path));
4454                 else
4455                         rv = ucv_string_new(path);
4456 
4457                 free(path);
4458         }
4459 
4460         return rv;
4461 }
4462 
4463 static uc_value_t *
4464 uc_min_max(uc_vm_t *vm, size_t nargs, int cmp)
4465 {
4466         uc_value_t *rv = NULL, *val;
4467         bool set = false;
4468         size_t i;
4469 
4470         for (i = 0; i < nargs; i++) {
4471                 val = uc_fn_arg(i);
4472 
4473                 if (!set || ucv_compare(cmp, val, rv, NULL)) {
4474                         set = true;
4475                         rv = val;
4476                 }
4477         }
4478 
4479         return ucv_get(rv);
4480 }
4481 
4482 /**
4483  * Return the smallest value among all parameters passed to the function.
4484  *
4485  * @function module:core#min
4486  *
4487  * @param {...*} [val]
4488  * The values to compare.
4489  *
4490  * @returns {*}
4491  *
4492  * @example
4493  * min(5, 2.1, 3, "abc", 0.3);            // Returns 0.3
4494  * min(1, "abc");                         // Returns 1
4495  * min("1", "abc");                       // Returns "1"
4496  * min("def", "abc", "ghi");              // Returns "abc"
4497  * min(true, false);                      // Returns false
4498  */
4499 static uc_value_t *
4500 uc_min(uc_vm_t *vm, size_t nargs)
4501 {
4502         return uc_min_max(vm, nargs, I_LT);
4503 }
4504 
4505 /**
4506  * Return the largest value among all parameters passed to the function.
4507  *
4508  * @function module:core#max
4509  *
4510  * @param {...*} [val]
4511  * The values to compare.
4512  *
4513  * @returns {*}
4514  *
4515  * @example
4516  * max(5, 2.1, 3, "abc", 0.3);            // Returns 5
4517  * max(1, "abc");                         // Returns 1 (!)
4518  * max("1", "abc");                       // Returns "abc"
4519  * max("def", "abc", "ghi");              // Returns "ghi"
4520  * max(true, false);                      // Returns true
4521  */
4522 static uc_value_t *
4523 uc_max(uc_vm_t *vm, size_t nargs)
4524 {
4525         return uc_min_max(vm, nargs, I_GT);
4526 }
4527 
4528 
4529 /* -------------------------------------------------------------------------
4530  * The following base64 encoding and decoding routines are taken from
4531  * https://git.openwrt.org/?p=project/libubox.git;a=blob;f=base64.c
4532  * and modified for use in ucode.
4533  *
4534  * Original copyright and license statements below.
4535  */
4536 
4537 /*
4538  * base64 - libubox base64 functions
4539  *
4540  * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
4541  *
4542  * Permission to use, copy, modify, and/or distribute this software for any
4543  * purpose with or without fee is hereby granted, provided that the above
4544  * copyright notice and this permission notice appear in all copies.
4545  *
4546  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
4547  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
4548  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
4549  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4550  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4551  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
4552  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4553  */
4554 
4555 /*      $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */
4556 
4557 /*
4558  * Copyright (c) 1996 by Internet Software Consortium.
4559  *
4560  * Permission to use, copy, modify, and distribute this software for any
4561  * purpose with or without fee is hereby granted, provided that the above
4562  * copyright notice and this permission notice appear in all copies.
4563  *
4564  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
4565  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
4566  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
4567  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
4568  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
4569  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
4570  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
4571  * SOFTWARE.
4572  */
4573 
4574 /*
4575  * Portions Copyright (c) 1995 by International Business Machines, Inc.
4576  *
4577  * International Business Machines, Inc. (hereinafter called IBM) grants
4578  * permission under its copyrights to use, copy, modify, and distribute this
4579  * Software with or without fee, provided that the above copyright notice and
4580  * all paragraphs of this notice appear in all copies, and that the name of IBM
4581  * not be used in connection with the marketing of any product incorporating
4582  * the Software or modifications thereof, without specific, written prior
4583  * permission.
4584  *
4585  * To the extent it has a right to do so, IBM grants an immunity from suit
4586  * under its patents, if any, for the use, sale or manufacture of products to
4587  * the extent that such products are used for performing Domain Name System
4588  * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
4589  * granted for any product per se or for any other function of any product.
4590  *
4591  * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
4592  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
4593  * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
4594  * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
4595  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
4596  * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
4597  */
4598 
4599 /* skips all whitespace anywhere.
4600    converts characters, four at a time, starting at (or after)
4601    src from base - 64 numbers into three 8 bit bytes in the target area.
4602    it returns the number of data bytes stored at the target, or -1 on error.
4603  */
4604 
4605 /**
4606  * Decodes the given base64 encoded string and returns the decoded result.
4607  *
4608  *  - If non-whitespace, non-base64 characters are encountered, if invalid
4609  *    padding or trailing garbage is found, the function returns `null`.
4610  *  - If a non-string argument is given, the function returns `null`.
4611  *
4612  * @function module:core#b64dec
4613  *
4614  * @param {string} str
4615  * The base64 encoded string to decode.
4616  *
4617  * @returns {?string}
4618  *
4619  * @example
4620  * b64dec("VGhpcyBpcyBhIHRlc3Q=");         // Returns "This is a test"
4621  * b64dec(123);                           // Returns null
4622  * b64dec("XXX");                         // Returns null
4623  */
4624 static uc_value_t *
4625 uc_b64dec(uc_vm_t *vm, size_t nargs)
4626 {
4627         enum { BYTE1, BYTE2, BYTE3, BYTE4 } state;
4628         uc_value_t *str = uc_fn_arg(0);
4629         uc_stringbuf_t *buf;
4630         const char *src;
4631         unsigned int ch;
4632         uint8_t val;
4633         size_t off;
4634 
4635         if (ucv_type(str) != UC_STRING)
4636                 return NULL;
4637 
4638         buf = ucv_stringbuf_new();
4639         src = ucv_string_get(str);
4640         off = printbuf_length(buf);
4641 
4642         state = BYTE1;
4643 
4644         /* memset the last expected output char to pre-grow the output buffer */
4645         printbuf_memset(buf, off + (ucv_string_length(str) / 4) * 3, 0, 1);
4646 
4647         while ((ch = (unsigned char)*src++) != '\0') {
4648                 if (isspace(ch))        /* Skip whitespace anywhere. */
4649                         continue;
4650 
4651                 if (ch == '=')
4652                         break;
4653 
4654                 if (ch >= 'A' && ch <= 'Z')
4655                         val = ch - 'A';
4656                 else if (ch >= 'a' && ch <= 'z')
4657                         val = ch - 'a' + 26;
4658                 else if (ch >= '' && ch <= '9')
4659                         val = ch - '' + 52;
4660                 else if (ch == '+')
4661                         val = 62;
4662                 else if (ch == '/')
4663                         val = 63;
4664                 else
4665                         goto err;
4666 
4667                 switch (state) {
4668                 case BYTE1:
4669                         buf->buf[off] = val << 2;
4670                         state = BYTE2;
4671                         break;
4672 
4673                 case BYTE2:
4674                         buf->buf[off++] |= val >> 4;
4675                         buf->buf[off] = (val & 0x0f) << 4;
4676                         state = BYTE3;
4677                         break;
4678 
4679                 case BYTE3:
4680                         buf->buf[off++] |= val >> 2;
4681                         buf->buf[off] = (val & 0x03) << 6;
4682                         state = BYTE4;
4683                         break;
4684 
4685                 case BYTE4:
4686                         buf->buf[off++] |= val;
4687                         state = BYTE1;
4688                         break;
4689                 }
4690         }
4691 
4692         /*
4693          * We are done decoding Base-64 chars.  Let's see if we ended
4694          * on a byte boundary, and/or with erroneous trailing characters.
4695          */
4696 
4697         if (ch == '=') {                        /* We got a pad char. */
4698                 ch = (unsigned char)*src++;     /* Skip it, get next. */
4699                 switch (state) {
4700                 case BYTE1:             /* Invalid = in first position */
4701                 case BYTE2:             /* Invalid = in second position */
4702                         goto err;
4703 
4704                 case BYTE3:             /* Valid, means one byte of info */
4705                         /* Skip any number of spaces. */
4706                         for (; ch != '\0'; ch = (unsigned char)*src++)
4707                                 if (!isspace(ch))
4708                                         break;
4709                         /* Make sure there is another trailing = sign. */
4710                         if (ch != '=')
4711                                 goto err;
4712                         ch = (unsigned char)*src++;             /* Skip the = */
4713                         /* Fall through to "single trailing =" case. */
4714                         /* FALLTHROUGH */
4715 
4716                 case BYTE4:             /* Valid, means two bytes of info */
4717                         /*
4718                          * We know this char is an =.  Is there anything but
4719                          * whitespace after it?
4720                          */
4721                         for (; ch != '\0'; ch = (unsigned char)*src++)
4722                                 if (!isspace(ch))
4723                                         goto err;
4724 
4725                         /*
4726                          * Now make sure for cases BYTE3 and BYTE4 that the "extra"
4727                          * bits that slopped past the last full byte were
4728                          * zeros.  If we don't check them, they become a
4729                          * subliminal channel.
4730                          */
4731                         if (buf->buf[off] != 0)
4732                                 goto err;
4733                 }
4734         } else {
4735                 /*
4736                  * We ended by seeing the end of the string.  Make sure we
4737                  * have no partial bytes lying around.
4738                  */
4739                 if (state != BYTE1)
4740                         goto err;
4741         }
4742 
4743         /* Truncate buffer length to actual output length */
4744         buf->bpos = off;
4745 
4746         return ucv_stringbuf_finish(buf);
4747 
4748 err:
4749         printbuf_free(buf);
4750 
4751         return NULL;
4752 }
4753 
4754 static const char Base64[] =
4755         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4756 
4757 /**
4758  * Encodes the given string into base64 and returns the resulting string.
4759  *
4760  *  - If a non-string argument is given, the function returns `null`.
4761  *
4762  * @function module:core#b64enc
4763  *
4764  * @param {string} str
4765  * The string to encode.
4766  *
4767  * @returns {?string}
4768  *
4769  * @example
4770  * b64enc("This is a test");  // Returns "VGhpcyBpcyBhIHRlc3Q="
4771  * b64enc(123);               // Returns null
4772  */
4773 static uc_value_t *
4774 uc_b64enc(uc_vm_t *vm, size_t nargs)
4775 {
4776         uc_value_t *str = uc_fn_arg(0);
4777         unsigned char input[3] = {0};
4778         uc_stringbuf_t *buf;
4779         const char *src;
4780         char output[4];
4781         size_t len, i;
4782 
4783         if (ucv_type(str) != UC_STRING)
4784                 return NULL;
4785 
4786         buf = ucv_stringbuf_new();
4787         src = ucv_string_get(str);
4788         len = ucv_string_length(str);
4789 
4790         while (2 < len) {
4791                 input[0] = (unsigned char)*src++;
4792                 input[1] = (unsigned char)*src++;
4793                 input[2] = (unsigned char)*src++;
4794                 len -= 3;
4795 
4796                 output[0] = Base64[input[0] >> 2];
4797                 output[1] = Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)];
4798                 output[2] = Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)];
4799                 output[3] = Base64[input[2] & 0x3f];
4800 
4801                 ucv_stringbuf_addstr(buf, output, sizeof(output));
4802         }
4803 
4804         /* Now we worry about padding. */
4805         if (0 != len) {
4806                 /* Get what's left. */
4807                 input[0] = input[1] = input[2] = '\0';
4808                 for (i = 0; i < len; i++)
4809                         input[i] = *src++;
4810 
4811                 output[0] = Base64[input[0] >> 2];
4812                 output[1] = Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)];
4813                 output[2] = (len == 1) ? '=' : Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)];
4814                 output[3] = '=';
4815 
4816                 ucv_stringbuf_addstr(buf, output, sizeof(output));
4817         }
4818 
4819         return ucv_stringbuf_finish(buf);
4820 }
4821 
4822 /* End of base64 code.
4823  * -------------------------------------------------------------------------
4824  */
4825 
4826 static unsigned long
4827 uc_uniq_ucv_hash(const void *k)
4828 {
4829         union { double d; int64_t i; uint64_t u; } conv;
4830         uc_value_t *uv = (uc_value_t *)k;
4831         unsigned int h;
4832         uint8_t *u8;
4833         size_t len;
4834 
4835         h = ucv_type(uv);
4836 
4837         switch (h) {
4838         case UC_STRING:
4839                 u8 = (uint8_t *)ucv_string_get(uv);
4840                 len = ucv_string_length(uv);
4841                 break;
4842 
4843         case UC_INTEGER:
4844                 conv.i = ucv_int64_get(uv);
4845 
4846                 if (errno == ERANGE) {
4847                         h *= 2;
4848                         conv.u = ucv_uint64_get(uv);
4849                 }
4850 
4851                 u8 = (uint8_t *)&conv.u;
4852                 len = sizeof(conv.u);
4853                 break;
4854 
4855         case UC_DOUBLE:
4856                 conv.d = ucv_double_get(uv);
4857 
4858                 u8 = (uint8_t *)&conv.u;
4859                 len = sizeof(conv.u);
4860                 break;
4861 
4862         default:
4863                 u8 = (uint8_t *)&uv;
4864                 len = sizeof(uv);
4865                 break;
4866         }
4867 
4868         while (len > 0) {
4869                 h = h * 129 + (*u8++) + LH_PRIME;
4870                 len--;
4871         }
4872 
4873         return h;
4874 }
4875 
4876 static int
4877 uc_uniq_ucv_equal(const void *k1, const void *k2)
4878 {
4879         uc_value_t *uv1 = (uc_value_t *)k1;
4880         uc_value_t *uv2 = (uc_value_t *)k2;
4881 
4882         if (!ucv_is_scalar(uv1) && !ucv_is_scalar(uv2))
4883                 return (uv1 == uv2);
4884 
4885         /* for the sake of array item uniqueness, treat two NaNs as equal */
4886         if (ucv_type(uv1) == UC_DOUBLE && ucv_type(uv2) == UC_DOUBLE &&
4887             isnan(ucv_double_get(uv1)) && isnan(ucv_double_get(uv2)))
4888             return true;
4889 
4890         return ucv_is_equal(uv1, uv2);
4891 }
4892 
4893 /**
4894  * Returns a new array containing all unique values of the given input array.
4895  *
4896  *  - The order is preserved, and subsequent duplicate values are skipped.
4897  *  - If a non-array argument is given, the function returns `null`.
4898  *
4899  * @function module:core#uniq
4900  *
4901  * @param {Array} array
4902  * The input array.
4903  *
4904  * @returns {?Array}
4905  *
4906  * @example
4907  * uniq([1, true, "foo", 2, true, "bar", "foo"]);       // Returns [1, true, "foo", 2, "bar"]
4908  * uniq("test");                                        // Returns null
4909  */
4910 static uc_value_t *
4911 uc_uniq(uc_vm_t *vm, size_t nargs)
4912 {
4913         uc_value_t *list = uc_fn_arg(0);
4914         uc_value_t *uniq = NULL;
4915         struct lh_table *seen;
4916         unsigned long hash;
4917         uc_value_t *item;
4918         size_t i, len;
4919 
4920         if (ucv_type(list) != UC_ARRAY)
4921                 return NULL;
4922 
4923         seen = lh_table_new(16, NULL, uc_uniq_ucv_hash, uc_uniq_ucv_equal);
4924         uniq = ucv_array_new(vm);
4925 
4926         assert(seen && uniq);
4927 
4928         for (i = 0, len = ucv_array_length(list); i < len; i++) {
4929                 item = ucv_array_get(list, i);
4930                 hash = lh_get_hash(seen, item);
4931 
4932                 if (!lh_table_lookup_entry_w_hash(seen, item, hash)) {
4933                         lh_table_insert_w_hash(seen, item, NULL, hash, 0);
4934                         ucv_array_push(uniq, ucv_get(item));
4935                 }
4936         }
4937 
4938         lh_table_free(seen);
4939 
4940         return uniq;
4941 }
4942 
4943 /**
4944  * A time spec is a plain object describing a point in time, it is returned by
4945  * the {@link module:core#gmtime|gmtime()} and
4946  * {@link module:core#localtime|localtime()} functions and expected as parameter
4947  * by the complementary {@link module:core#timegm|timegm()} and
4948  * {@link module:core#timelocal|timelocal()} functions.
4949  *
4950  * When returned by `gmtime()` or `localtime()`, all members of the object will
4951  * be initialized, when passed as argument to `timegm()` or `timelocal()`, most
4952  * member values are optional.
4953  *
4954  * @typedef {Object} module:core.TimeSpec
4955  * @property {number} sec - Seconds (0..60)
4956  * @property {number} min - Minutes (0..59)
4957  * @property {number} hour - Hours (0..23)
4958  * @property {number} mday - Day of month (1..31)
4959  * @property {number} mon - Month (1..12)
4960  * @property {number} year - Year (>= 1900)
4961  * @property {number} wday - Day of week (1..7, Sunday = 7)
4962  * @property {number} yday - Day of year (1-366, Jan 1st = 1)
4963  * @property {number} isdst - Daylight saving time in effect (yes = 1)
4964  */
4965 static uc_value_t *
4966 uc_gettime_common(uc_vm_t *vm, size_t nargs, bool local)
4967 {
4968         uc_value_t *ts = uc_fn_arg(0), *res;
4969         time_t t = ts ? (time_t)ucv_to_integer(ts) : time(NULL);
4970         struct tm *tm = (local ? localtime : gmtime)(&t);
4971 
4972         if (!tm)
4973                 return NULL;
4974 
4975         res = ucv_object_new(vm);
4976 
4977         ucv_object_add(res, "sec", ucv_int64_new(tm->tm_sec));
4978         ucv_object_add(res, "min", ucv_int64_new(tm->tm_min));
4979         ucv_object_add(res, "hour", ucv_int64_new(tm->tm_hour));
4980         ucv_object_add(res, "mday", ucv_int64_new(tm->tm_mday));
4981         ucv_object_add(res, "mon", ucv_int64_new(tm->tm_mon + 1));
4982         ucv_object_add(res, "year", ucv_int64_new(tm->tm_year + 1900));
4983         ucv_object_add(res, "wday", ucv_int64_new(tm->tm_wday ? tm->tm_wday : 7));
4984         ucv_object_add(res, "yday", ucv_int64_new(tm->tm_yday + 1));
4985         ucv_object_add(res, "isdst", ucv_int64_new(tm->tm_isdst));
4986 
4987         return res;
4988 }
4989 
4990 /**
4991  * Return the given epoch timestamp (or now, if omitted) as a dictionary
4992  * containing broken-down date and time information according to the local
4993  * system timezone.
4994  *
4995  * See {@link module:core.TimeSpec|TimeSpec} for a description of the fields.
4996  *
4997  * Note that in contrast to the underlying `localtime(3)` C library function,
4998  * the values for `mon`, `wday`, and `yday` are 1-based, and the `year` is
4999  * 1900-based.
5000  *
5001  * @function module:core#localtime
5002  *
5003  * @param {number} [epoch]
5004  * The epoch timestamp.
5005  *
5006  * @returns {module:core.TimeSpec}
5007  *
5008  * @example
5009  * localtime(1647953502);
5010  * // Returns:
5011  * // {
5012  * //     sec: 42,
5013  * //     min: 51,
5014  * //     hour: 13,
5015  * //     mday: 22,
5016  * //     mon: 3,
5017  * //     year: 2022,
5018  * //     wday: 2,
5019  * //     yday: 81,
5020  * //     isdst: 0
5021  * // }
5022  */
5023 static uc_value_t *
5024 uc_localtime(uc_vm_t *vm, size_t nargs)
5025 {
5026         return uc_gettime_common(vm, nargs, true);
5027 }
5028 
5029 /**
5030  * Like `localtime()` but interpreting the given epoch value as UTC time.
5031  *
5032  * See {@link module:core#localtime|localtime()} for details on the return value.
5033  *
5034  * @function module:core#gmtime
5035  *
5036  * @param {number} [epoch]
5037  * The epoch timestamp.
5038  *
5039  * @returns {module:core.TimeSpec}
5040  *
5041  * @example
5042  * gmtime(1647953502);
5043  * // Returns:
5044  * // {
5045  * //     sec: 42,
5046  * //     min: 51,
5047  * //     hour: 13,
5048  * //     mday: 22,
5049  * //     mon: 3,
5050  * //     year: 2022,
5051  * //     wday: 2,
5052  * //     yday: 81,
5053  * //     isdst: 0
5054  * // }
5055  */
5056 static uc_value_t *
5057 uc_gmtime(uc_vm_t *vm, size_t nargs)
5058 {
5059         return uc_gettime_common(vm, nargs, false);
5060 }
5061 
5062 static uc_value_t *
5063 uc_mktime_common(uc_vm_t *vm, size_t nargs, bool local)
5064 {
5065 #define FIELD(name, required) \
5066         { #name, required, offsetof(struct tm, tm_##name) }
5067 
5068         const struct {
5069                 const char *name;
5070                 bool required;
5071                 size_t off;
5072         } fields[] = {
5073                 FIELD(sec, false),
5074                 FIELD(min, false),
5075                 FIELD(hour, false),
5076                 FIELD(mday, true),
5077                 FIELD(mon, true),
5078                 FIELD(year, true),
5079                 FIELD(isdst, false)
5080         };
5081 
5082         uc_value_t *to = uc_fn_arg(0), *v;
5083         struct tm tm = { 0 };
5084         bool exists;
5085         time_t t;
5086         size_t i;
5087 
5088         if (ucv_type(to) != UC_OBJECT)
5089                 return NULL;
5090 
5091         for (i = 0; i < ARRAY_SIZE(fields); i++) {
5092                 v = ucv_object_get(to, fields[i].name, &exists);
5093 
5094                 if (!exists && fields[i].required)
5095                         return NULL;
5096 
5097                 *(int *)((char *)&tm + fields[i].off) = (int)ucv_to_integer(v);
5098         }
5099 
5100         if (tm.tm_mon > 0)
5101                 tm.tm_mon--;
5102 
5103         if (tm.tm_year >= 1900)
5104                 tm.tm_year -= 1900;
5105 
5106         t = (local ? mktime : timegm)(&tm);
5107 
5108         return (t != (time_t)-1) ? ucv_int64_new((int64_t)t) : NULL;
5109 }
5110 
5111 /**
5112  * Performs the inverse operation of {@link module:core#localtime|localtime()}
5113  * by taking a broken-down date and time dictionary and transforming it into an
5114  * epoch value according to the local system timezone.
5115  *
5116  * The `wday` and `yday` fields of the given date time specification are
5117  * ignored. Field values outside of their valid range are internally normalized,
5118  * e.g. October 40th is interpreted as November 9th.
5119  *
5120  * Returns the resulting epoch value or null if the input date time dictionary
5121  * was invalid or if the date time specification cannot be represented as epoch
5122  * value.
5123  *
5124  * @function module:core#timelocal
5125  *
5126  * @param {module:core.TimeSpec} datetimespec
5127  * The broken-down date and time dictionary.
5128  *
5129  * @returns {?number}
5130  *
5131  * @example
5132  * timelocal({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 });
5133  * // Returns 1647953502
5134  */
5135 static uc_value_t *
5136 uc_timelocal(uc_vm_t *vm, size_t nargs)
5137 {
5138         return uc_mktime_common(vm, nargs, true);
5139 }
5140 
5141 /**
5142  * Like `timelocal()` but interpreting the given date time specification as UTC
5143  * time.
5144  *
5145  * See {@link module:core#timelocal|timelocal()} for details.
5146  *
5147  * @function module:core#timegm
5148  *
5149  * @param {module:core.TimeSpec} datetimespec
5150  * The broken-down date and time dictionary.
5151  *
5152  * @returns {?number}
5153  *
5154  * @example
5155  * timegm({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 });
5156  * // Returns 1647953502
5157  */
5158 static uc_value_t *
5159 uc_timegm(uc_vm_t *vm, size_t nargs)
5160 {
5161         return uc_mktime_common(vm, nargs, false);
5162 }
5163 
5164 /**
5165  * Reads the current second and microsecond value of the system clock.
5166  *
5167  * By default, the realtime clock is queried which might skew forwards or
5168  * backwards due to NTP changes, system sleep modes etc. If a truish value is
5169  * passed as argument, the monotonic system clock is queried instead, which will
5170  * return the monotonically increasing time since some arbitrary point in the
5171  * past (usually the system boot time).
5172  *
5173  * Returns a two element array containing the full seconds as the first element
5174  * and the nanosecond fraction as the second element.
5175  *
5176  * Returns `null` if a monotonic clock value is requested and the system does
5177  * not implement this clock type.
5178  *
5179  * @function module:core#clock
5180  *
5181  * @param {boolean} [monotonic]
5182  * Whether to query the monotonic system clock.
5183  *
5184  * @returns {?number[]}
5185  *
5186  * @example
5187  * clock();        // [ 1647954926, 798269464 ]
5188  * clock(true);    // [ 474751, 527959975 ]
5189  */
5190 static uc_value_t *
5191 uc_clock(uc_vm_t *vm, size_t nargs)
5192 {
5193         clockid_t id = ucv_is_truish(uc_fn_arg(0)) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
5194         struct timespec ts;
5195         uc_value_t *res;
5196 
5197         if (clock_gettime(id, &ts) == -1)
5198                 return NULL;
5199 
5200         res = ucv_array_new(vm);
5201 
5202         ucv_array_set(res, 0, ucv_int64_new((int64_t)ts.tv_sec));
5203         ucv_array_set(res, 1, ucv_int64_new((int64_t)ts.tv_nsec));
5204 
5205         return res;
5206 }
5207 
5208 /**
5209  * Encodes the given byte string into a hexadecimal digit string, converting
5210  * the input value to a string if needed.
5211  *
5212  * @function module:core#hexenc
5213  *
5214  * @param {string} val
5215  * The byte string to encode.
5216  *
5217  * @returns {string}
5218  *
5219  * @example
5220  * hexenc("Hello world!\n");   // "48656c6c6f20776f726c64210a"
5221  */
5222 static uc_value_t *
5223 uc_hexenc(uc_vm_t *vm, size_t nargs)
5224 {
5225         const char *hex = "0123456789abcdef";
5226         uc_value_t *input = uc_fn_arg(0);
5227         uc_stringbuf_t *buf;
5228         size_t off, len;
5229         uint8_t byte;
5230 
5231         if (!input)
5232                 return NULL;
5233 
5234         buf = ucv_stringbuf_new();
5235         off = printbuf_length(buf);
5236 
5237         ucv_to_stringbuf(vm, buf, input, false);
5238 
5239         len = printbuf_length(buf) - off;
5240 
5241         /* memset the last expected output char to grow the output buffer */
5242         printbuf_memset(buf, off + len * 2, 0, 1);
5243 
5244         /* translate string into hex back to front to reuse the same buffer */
5245         while (len > 0) {
5246                 byte = buf->buf[--len + off];
5247                 buf->buf[off + len * 2 + 0] = hex[byte / 16];
5248                 buf->buf[off + len * 2 + 1] = hex[byte % 16];
5249         }
5250 
5251         /* do not include sentinel `\0` in string length */
5252         buf->bpos--;
5253 
5254         return ucv_stringbuf_finish(buf);
5255 }
5256 
5257 static inline uint8_t
5258 hexval(unsigned char c, bool lo)
5259 {
5260         return ((c > '9') ? (c - 'a') + 10 : c - '') << (lo ? 0 : 4);
5261 }
5262 
5263 /**
5264  * Decodes the given hexadecimal digit string into a byte string, optionally
5265  * skipping specified characters.
5266  *
5267  * If the characters to skip are not specified, a default of `" \t\n"` is used.
5268  *
5269  * Returns null if the input string contains invalid characters or an uneven
5270  * amount of hex digits.
5271  *
5272  * Returns the decoded byte string on success.
5273  *
5274  * @function module:core#hexdec
5275  *
5276  * @param {string} hexstring
5277  * The hexadecimal digit string to decode.
5278  *
5279  * @param {string} [skipchars]
5280  * The characters to skip during decoding.
5281  *
5282  * @returns {?string}
5283  *
5284  * @example
5285  * hexdec("48656c6c6f20776f726c64210a");  // "Hello world!\n"
5286  * hexdec("44:55:66:77:33:44", ":");      // "DUfw3D"
5287  */
5288 static uc_value_t *
5289 uc_hexdec(uc_vm_t *vm, size_t nargs)
5290 {
5291         uc_value_t *input = uc_fn_arg(0);
5292         uc_value_t *skip = uc_fn_arg(1);
5293         size_t len, off, n, i;
5294         uc_stringbuf_t *buf;
5295         unsigned char *p;
5296         const char *s;
5297 
5298         if (ucv_type(input) != UC_STRING)
5299                 return NULL;
5300 
5301         if (skip && ucv_type(skip) != UC_STRING)
5302                 return NULL;
5303 
5304         p = (unsigned char *)ucv_string_get(input);
5305         len = ucv_string_length(input);
5306 
5307         s = skip ? (const char *)ucv_string_get(skip) : " \t\n";
5308 
5309         for (i = 0, n = 0; i < len; i++) {
5310                 if (isxdigit(p[i]))
5311                         n++;
5312                 else if (!s || !strchr(s, p[i]))
5313                         return NULL;
5314         }
5315 
5316         if (n & 1)
5317                 return NULL;
5318 
5319         buf = ucv_stringbuf_new();
5320         off = printbuf_length(buf);
5321 
5322         /* preallocate the output buffer */
5323         printbuf_memset(buf, off, 0, n / 2 + 1);
5324 
5325         for (i = 0, n = 0; i < len; i++) {
5326                 if (!isxdigit(p[i]))
5327                         continue;
5328 
5329                 buf->buf[off + (n >> 1)] |= hexval(p[i] | 32, n & 1);
5330                 n++;
5331         }
5332 
5333         /* do not include sentinel `\0` in string length */
5334         buf->bpos--;
5335 
5336         return ucv_stringbuf_finish(buf);
5337 }
5338 
5339 /**
5340  * Interacts with the mark and sweep garbage collector of the running ucode
5341  * virtual machine.
5342  *
5343  * Depending on the given `operation` string argument, the meaning of `argument`
5344  * and the function return value differs.
5345  *
5346  * The following operations are defined:
5347  *
5348  * - `collect` - Perform a complete garbage collection cycle, returns `true`.
5349  * - `start` - (Re-)start periodic garbage collection, `argument` is an optional
5350  *             integer in the range `1..65535` specifying the interval.
5351  *             Defaults to `1000` if omitted. Returns `true` if the periodic GC
5352  *             was previously stopped and is now started or if the interval
5353  *             changed. Returns `false` otherwise.
5354  * - `stop` - Stop periodic garbage collection. Returns `true` if the periodic
5355  *            GC was previously started and is now stopped, `false` otherwise.
5356  * - `count` - Count the amount of active complex object references in the VM
5357  *             context, returns the counted amount.
5358  *
5359  * If the `operation` argument is omitted, the default is `collect`.
5360  *
5361  * @function module:core#gc
5362  *
5363  * @param {string} [operation]
5364  * The operation to perform.
5365  *
5366  * @param {*} [argument]
5367  * The argument for the operation.
5368  *
5369  * @returns {?(boolean|number)}
5370  *
5371  * @example
5372  * gc();         // true
5373  * gc("start");  // true
5374  * gc("count");  // 42
5375  */
5376 static uc_value_t *
5377 uc_gc(uc_vm_t *vm, size_t nargs)
5378 {
5379         uc_value_t *operation = uc_fn_arg(0);
5380         uc_value_t *argument = uc_fn_arg(1);
5381         const char *op = NULL;
5382         uc_weakref_t *ref;
5383         int64_t n;
5384 
5385         if (operation != NULL && ucv_type(operation) != UC_STRING)
5386                 return NULL;
5387 
5388         op = ucv_string_get(operation);
5389 
5390         if (!op || !strcmp(op, "collect")) {
5391                 ucv_gc(vm);
5392 
5393                 return ucv_boolean_new(true);
5394         }
5395         else if (!strcmp(op, "start")) {
5396                 n = argument ? ucv_int64_get(argument) : 0;
5397 
5398                 if (errno || n < 0 || n > 0xFFFF)
5399                         return NULL;
5400 
5401                 if (n == 0)
5402                         n = GC_DEFAULT_INTERVAL;
5403 
5404                 return ucv_boolean_new(uc_vm_gc_start(vm, n));
5405         }
5406         else if (!strcmp(op, "stop")) {
5407                 return ucv_boolean_new(uc_vm_gc_stop(vm));
5408         }
5409         else if (!strcmp(op, "count")) {
5410                 for (n = 0, ref = vm->values.next; ref != &vm->values; ref = ref->next)
5411                         n++;
5412 
5413                 return ucv_uint64_new(n);
5414         }
5415 
5416         return NULL;
5417 }
5418 
5419 /**
5420  * A parse configuration is a plain object describing options to use when
5421  * compiling ucode at runtime. It is expected as parameter by the
5422  * {@link module:core#loadfile|loadfile()} and
5423  * {@link module:core#loadstring|loadstring()} functions.
5424  *
5425  * All members of the parse configuration object are optional and will default
5426  * to the state of the running ucode file if omitted.
5427  *
5428  * @typedef {Object} module:core.ParseConfig
5429  *
5430  * @property {boolean} lstrip_blocks
5431  * Whether to strip whitespace preceding template directives.
5432  * See {@link tutorial-02-syntax.html#whitespace-handling|Whitespace handling}.
5433  *
5434  * @property {boolean} trim_blocks
5435  * Whether to trim trailing newlines following template directives.
5436  * See {@link tutorial-02-syntax.html#whitespace-handling|Whitespace handling}.
5437  *
5438  * @property {boolean} strict_declarations
5439  * Whether to compile the code in strict mode (`true`) or not (`false`).
5440  *
5441  * @property {boolean} raw_mode
5442  * Whether to compile the code in plain script mode (`true`) or not (`false`).
5443  *
5444  * @property {string[]} module_search_path
5445  * Override the module search path for compile time imports while compiling the
5446  * ucode source.
5447  *
5448  * @property {string[]} force_dynlink_list
5449  * List of module names assumed to be dynamic library extensions, allows
5450  * compiling ucode source with import statements referring to `*.so` extensions
5451  * not present at compile time.
5452  */
5453 static void
5454 uc_compile_parse_config(uc_parse_config_t *config, uc_value_t *spec)
5455 {
5456         uc_value_t *v, *p;
5457         size_t i, j;
5458         bool found;
5459 
5460         struct {
5461                 const char *key;
5462                 bool *flag;
5463                 uc_search_path_t *path;
5464         } fields[] = {
5465                 { "lstrip_blocks",       &config->lstrip_blocks,       NULL },
5466                 { "trim_blocks",         &config->trim_blocks,         NULL },
5467                 { "strict_declarations", &config->strict_declarations, NULL },
5468                 { "raw_mode",            &config->raw_mode,            NULL },
5469                 { "module_search_path",  NULL, &config->module_search_path  },
5470                 { "force_dynlink_list",  NULL, &config->force_dynlink_list  }
5471         };
5472 
5473         for (i = 0; i < ARRAY_SIZE(fields); i++) {
5474                 v = ucv_object_get(spec, fields[i].key, &found);
5475 
5476                 if (!found)
5477                         continue;
5478 
5479                 if (fields[i].flag) {
5480                         *fields[i].flag = ucv_is_truish(v);
5481                 }
5482                 else if (fields[i].path) {
5483                         fields[i].path->count = 0;
5484                         fields[i].path->entries = NULL;
5485 
5486                         for (j = 0; j < ucv_array_length(v); j++) {
5487                                 p = ucv_array_get(v, j);
5488 
5489                                 if (ucv_type(p) != UC_STRING)
5490                                         continue;
5491 
5492                                 uc_vector_push(fields[i].path, ucv_string_get(p));
5493                         }
5494                 }
5495         }
5496 }
5497 
5498 static uc_value_t *
5499 uc_load_common(uc_vm_t *vm, size_t nargs, uc_source_t *source)
5500 {
5501         uc_parse_config_t conf = *vm->config;
5502         uc_program_t *program;
5503         uc_value_t *closure;
5504         char *err = NULL;
5505 
5506         uc_compile_parse_config(&conf, uc_fn_arg(1));
5507 
5508         program = uc_compile(&conf, source, &err);
5509         closure = program ? ucv_closure_new(vm, uc_program_entry(program), false) : NULL;
5510 
5511         uc_program_put(program);
5512 
5513         if (!vm->config || conf.module_search_path.entries != vm->config->module_search_path.entries)
5514                 uc_vector_clear(&conf.module_search_path);
5515 
5516         if (!vm->config || conf.force_dynlink_list.entries != vm->config->force_dynlink_list.entries)
5517                 uc_vector_clear(&conf.force_dynlink_list);
5518 
5519         if (!closure) {
5520                 uc_error_message_indent(&err);
5521 
5522                 if (source->buffer)
5523                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5524                                 "Unable to compile source string:\n\n%s", err);
5525                 else
5526                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5527                                 "Unable to compile source file '%s':\n\n%s", source->filename, err);
5528         }
5529 
5530         uc_source_put(source);
5531         free(err);
5532 
5533         return closure;
5534 }
5535 
5536 /**
5537  * Compiles the given code string into a ucode program and returns the resulting
5538  * program entry function.
5539  *
5540  * The optional `options` dictionary overrides parse and compile options.
5541  *
5542  *  - If a non-string `code` argument is given, it is implicitly converted to a
5543  *    string value first.
5544  *  - If `options` is omitted or a non-object value, the compile options of the
5545  *    running ucode program are reused.
5546  *
5547  * See {@link module:core.ParseConfig|ParseConfig} for known keys within the
5548  * `options` object. Unrecognized keys are ignored, unspecified options default
5549  * to those of the running program.
5550  *
5551  * Returns the compiled program entry function.
5552  *
5553  * Throws an exception on compilation errors.
5554  *
5555  * @function module:core#loadstring
5556  *
5557  * @param {string} code
5558  * The code string to compile.
5559  *
5560  * @param {module:core.ParseConfig} [options]
5561  * The options for compilation.
5562  *
5563  * @returns {Function}
5564  *
5565  * @example
5566  * let fn1 = loadstring("Hello, {{ name }}", { raw_mode: false });
5567  *
5568  * global.name = "Alice";
5569  * fn1(); // prints `Hello, Alice`
5570  *
5571  *
5572  * let fn2 = loadstring("return 1 + 2;", { raw_mode: true });
5573  * fn2(); // 3
5574  */
5575 static uc_value_t *
5576 uc_loadstring(uc_vm_t *vm, size_t nargs)
5577 {
5578         uc_value_t *code = uc_fn_arg(0);
5579         uc_source_t *source;
5580         size_t len;
5581         char *s;
5582 
5583         if (ucv_type(code) == UC_STRING) {
5584                 len = ucv_string_length(code);
5585                 s = xalloc(len);
5586                 memcpy(s, ucv_string_get(code), len);
5587         }
5588         else {
5589                 s = ucv_to_string(vm, code);
5590                 len = strlen(s);
5591         }
5592 
5593         source = uc_source_new_buffer("[loadstring argument]", s, len);
5594 
5595         if (!source) {
5596                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5597                         "Unable to allocate source buffer: %s",
5598                         strerror(errno));
5599 
5600                 return NULL;
5601         }
5602 
5603         return uc_load_common(vm, nargs, source);
5604 }
5605 
5606 /**
5607  * Compiles the given file into a ucode program and returns the resulting
5608  * program entry function.
5609  *
5610  * See {@link module:core#loadstring|`loadstring()`} for details.
5611  *
5612  * Returns the compiled program entry function.
5613  *
5614  * Throws an exception on compilation or file I/O errors.
5615  *
5616  * @function module:core#loadfile
5617  *
5618  * @param {string} path
5619  * The path of the file to compile.
5620  *
5621  * @param {module:core.ParseConfig} [options]
5622  * The options for compilation.
5623  *
5624  * @returns {Function}
5625  *
5626  * @example
5627  * loadfile("./templates/example.uc");  // function main() { ... }
5628  */
5629 static uc_value_t *
5630 uc_loadfile(uc_vm_t *vm, size_t nargs)
5631 {
5632         uc_value_t *path = uc_fn_arg(0);
5633         uc_source_t *source;
5634 
5635         if (ucv_type(path) != UC_STRING)
5636                 return NULL;
5637 
5638         source = uc_source_new_file(ucv_string_get(path));
5639 
5640         if (!source) {
5641                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5642                         "Unable to open source file %s: %s",
5643                         ucv_string_get(path), strerror(errno));
5644 
5645                 return NULL;
5646         }
5647 
5648         return uc_load_common(vm, nargs, source);
5649 }
5650 
5651 /**
5652  * Calls the given function value with a modified environment.
5653  *
5654  * The given `ctx` argument is used as `this` context for the invoked function
5655  * and the given `scope` value as global environment. Any further arguments are
5656  * passed to the invoked function as-is.
5657  *
5658  * When `ctx` is omitted or `null`, the function will get invoked with `this`
5659  * being `null`.
5660  *
5661  * When `scope` is omitted or `null`, the function will get executed with the
5662  * current global environment of the running program. When `scope` is set to a
5663  * dictionary, the dictionary is used as global function environment.
5664  *
5665  * When the `scope` dictionary has no prototype, the current global environment
5666  * will be set as prototype, means the scope will inherit from it.
5667  *
5668  * When a scope prototype is set, it is kept. This allows passing an isolated
5669  * (sandboxed) function scope without access to the global environment.
5670  *
5671  * Any further argument is forwarded as-is to the invoked function as function
5672  * call argument.
5673  *
5674  * Returns `null` if the given function value `fn` is not callable.
5675  *
5676  * Returns the return value of the invoked function in all other cases.
5677  *
5678  * Forwards exceptions thrown by the invoked function.
5679  *
5680  * @function module:core#call
5681  *
5682  * @param {Function} fn
5683  * Function value to call.
5684  *
5685  * @param {*} [ctx=null]
5686  * `this` context for the invoked function.
5687  *
5688  * @param {Object} [scope=null]
5689  * Global environment for the invoked function.
5690  *
5691  * @param {...*} [arg]
5692  * Additional arguments to pass to the invoked function.
5693  *
5694  * @returns {*}
5695  *
5696  * @example
5697  * // Override this context
5698  * call(function() { printf("%J\n", this) });            // null
5699  * call(function() { printf("%J\n", this) }, null);      // null
5700  * call(function() { printf("%J\n", this) }, { x: 1 });  // { "x": 1 }
5701  * call(function() { printf("%J\n", this) }, { x: 2 });  // { "x": 2 }
5702  *
5703  * // Run with default scope
5704  * global.a = 1;
5705  * call(function() { printf("%J\n", a) });                  // 1
5706  *
5707  * // Override scope, inherit from current global scope (implicit)
5708  * call(function() { printf("%J\n", a) }, null, { a: 2 });  // 2
5709  *
5710  * // Override scope, inherit from current global scope (explicit)
5711  * call(function() { printf("%J\n", a) }, null,
5712  *         proto({ a: 2 }, global));                        // 2
5713  *
5714  * // Override scope, don't inherit (pass `printf()` but not `a`)
5715  * call(function() { printf("%J\n", a) }, null,
5716  *         proto({}, { printf }));                          // null
5717  *
5718  * // Forward arguments
5719  * x = call((x, y, z) => x * y * z, null, null, 2, 3, 4);   // x = 24
5720  */
5721 static uc_value_t *
5722 uc_callfunc(uc_vm_t *vm, size_t nargs)
5723 {
5724         size_t argoff = vm->stack.count - nargs, i;
5725         uc_value_t *fn_scope, *prev_scope, *res;
5726         uc_value_t *fn = uc_fn_arg(0);
5727         uc_value_t *this = uc_fn_arg(1);
5728         uc_value_t *scope = uc_fn_arg(2);
5729 
5730         if (!ucv_is_callable(fn))
5731                 return NULL;
5732 
5733         if (scope && ucv_type(scope) != UC_OBJECT)
5734                 return NULL;
5735 
5736         if (ucv_prototype_get(scope)) {
5737                 fn_scope = ucv_get(scope);
5738         }
5739         else if (scope) {
5740                 fn_scope = ucv_object_new(vm);
5741 
5742                 ucv_object_foreach(scope, k, v)
5743                         ucv_object_add(fn_scope, k, ucv_get(v));
5744 
5745                 ucv_prototype_set(fn_scope, ucv_get(uc_vm_scope_get(vm)));
5746         }
5747         else {
5748                 fn_scope = NULL;
5749         }
5750 
5751         uc_vm_stack_push(vm, ucv_get(this));
5752         uc_vm_stack_push(vm, ucv_get(fn));
5753 
5754         for (i = 3; i < nargs; i++)
5755                 uc_vm_stack_push(vm, ucv_get(vm->stack.entries[3 + argoff++]));
5756 
5757         if (fn_scope) {
5758                 prev_scope = ucv_get(uc_vm_scope_get(vm));
5759                 uc_vm_scope_set(vm, fn_scope);
5760         }
5761 
5762         if (uc_vm_call(vm, true, i - 3) == EXCEPTION_NONE)
5763                 res = uc_vm_stack_pop(vm);
5764         else
5765                 res = NULL;
5766 
5767         if (fn_scope)
5768                 uc_vm_scope_set(vm, prev_scope);
5769 
5770         return res;
5771 }
5772 
5773 /**
5774  * Set or query process signal handler function.
5775  *
5776  * When invoked with two arguments, a signal specification and a signal handler
5777  * value, this function configures a new process signal handler.
5778  *
5779  * When invoked with one argument, a signal specification, this function returns
5780  * the currently configured handler for the given signal.
5781  *
5782  * The signal specification might either be an integer signal number or a string
5783  * value containing a signal name (with or without "SIG" prefix). Signal names
5784  * are treated case-insensitively.
5785  *
5786  * The signal handler might be either a callable function value or one of the
5787  * two special string values `"ignore"` and `"default"`. Passing `"ignore"` will
5788  * mask the given process signal while `"default"` will restore the operating
5789  * systems default behaviour for the given signal.
5790  *
5791  * In case a callable handler function is provided, it is invoked at the
5792  * earliest  opportunity after receiving the corresponding signal from the
5793  * operating system. The invoked function will receive a single argument, the
5794  * number of the signal it is invoked for.
5795  *
5796  * Note that within the ucode VM, process signals are not immediately delivered,
5797  * instead the VM keeps track of received signals and delivers them to the ucode
5798  * script environment at the next opportunity, usually before executing the next
5799  * byte code instruction. This means that if a signal is received while
5800  * performing a computationally expensive operation in C mode, such as a complex
5801  * regexp match, the corresponding ucode signal handler will only be invoked
5802  * after that operation concluded and control flow returns to the VM.
5803  *
5804  * Returns the signal handler function or one of the special values `"ignore"`
5805  * or `"default"` corresponding to the given signal specification.
5806  *
5807  * Returns `null` if an invalid signal spec or signal handler was provided.
5808  *
5809  * Returns `null` if changing the signal action failed, e.g. due to insufficient
5810  * permission, or when attempting to ignore a non-ignorable signal.
5811  *
5812  * @function module:core#signal
5813  *
5814  * @param {number|string} signal
5815  * The signal to query/set handler for.
5816  *
5817  * @param {Function|string} [handler]
5818  * The signal handler to install for the given signal.
5819  *
5820  * @returns {Function|string}
5821  *
5822  * @example
5823  * // Ignore signals
5824  * signal('INT', 'ignore');      // "ignore"
5825  * signal('SIGINT', 'ignore');   // "ignore" (equivalent to 'INT')
5826  * signal('sigterm', 'ignore');  // "ignore" (signal names are case insensitive)
5827  * signal(9, 'ignore');          // null (SIGKILL cannot be ignored)
5828  *
5829  * // Restore signal default behavior
5830  * signal('INT', 'default');     // "default"
5831  * signal('foobar', 'default');  // null (unknown signal name)
5832  * signal(-313, 'default');      // null (invalid signal number)
5833  *
5834  * // Set custom handler function
5835  * function intexit(signo) {
5836  *   printf("I received signal number %d\n", signo);
5837  *   exit(1);
5838  * }
5839  *
5840  * signal('SIGINT', intexit);    // returns intexit
5841  * signal('SIGINT') == intexit;  // true
5842  */
5843 static uc_value_t *
5844 uc_signal(uc_vm_t *vm, size_t nargs)
5845 {
5846         uc_value_t *signame = uc_fn_arg(0);
5847         uc_value_t *sighandler = uc_fn_arg(1);
5848         struct sigaction sa = { 0 };
5849         char *sigstr;
5850         int sig;
5851 
5852         if (ucv_type(signame) == UC_INTEGER) {
5853                 sig = (int)ucv_int64_get(signame);
5854 
5855                 if (errno || sig < 0 || sig >= UC_SYSTEM_SIGNAL_COUNT)
5856                         return NULL;
5857 
5858                 if (!uc_system_signal_names[sig])
5859                         return NULL;
5860         }
5861         else if (ucv_type(signame) == UC_STRING) {
5862                 sigstr = ucv_string_get(signame);
5863 
5864                 if (!strncasecmp(sigstr, "SIG", 3))
5865                         sigstr += 3;
5866 
5867                 for (sig = 0; sig < UC_SYSTEM_SIGNAL_COUNT; sig++)
5868                         if (uc_system_signal_names[sig] &&
5869                             !strcasecmp(uc_system_signal_names[sig], sigstr))
5870                                 break;
5871 
5872                 if (sig == UC_SYSTEM_SIGNAL_COUNT)
5873                         return NULL;
5874         }
5875         else {
5876                 return NULL;
5877         }
5878 
5879         /* Query current signal handler state */
5880         if (nargs < 2) {
5881                 if (sigaction(sig, NULL, &sa) != 0)
5882                         return NULL;
5883 
5884                 if (sa.sa_handler == SIG_IGN)
5885                         return ucv_string_new("ignore");
5886 
5887                 if (sa.sa_handler == SIG_DFL)
5888                         return ucv_string_new("default");
5889 
5890                 return ucv_get(ucv_array_get(vm->signal.handler, sig));
5891         }
5892 
5893         /* Install new signal handler */
5894         if (ucv_type(sighandler) == UC_STRING) {
5895                 sigstr = ucv_string_get(sighandler);
5896 
5897                 sa.sa_flags = SA_ONSTACK | SA_RESTART;
5898                 sigemptyset(&sa.sa_mask);
5899 
5900                 if (!strcmp(sigstr, "ignore"))
5901                         sa.sa_handler = SIG_IGN;
5902                 else if (!strcmp(sigstr, "default"))
5903                         sa.sa_handler = SIG_DFL;
5904                 else
5905                         return NULL;
5906 
5907                 if (sigaction(sig, &sa, NULL) != 0)
5908                         return NULL;
5909 
5910                 ucv_array_set(vm->signal.handler, sig, NULL);
5911         }
5912         else if (ucv_is_callable(sighandler)) {
5913                 if (sigaction(sig, &vm->signal.sa, NULL) != 0)
5914                         return NULL;
5915 
5916                 ucv_array_set(vm->signal.handler, sig, ucv_get(sighandler));
5917         }
5918         else {
5919                 return NULL;
5920         }
5921 
5922         return ucv_get(sighandler);
5923 }
5924 
5925 
5926 const uc_function_list_t uc_stdlib_functions[] = {
5927         { "chr",                uc_chr },
5928         { "die",                uc_die },
5929         { "exists",             uc_exists },
5930         { "exit",               uc_exit },
5931         { "filter",             uc_filter },
5932         { "getenv",             uc_getenv },
5933         { "hex",                uc_hex },
5934         { "index",              uc_lindex },
5935         { "int",                uc_int },
5936         { "join",               uc_join },
5937         { "keys",               uc_keys },
5938         { "lc",                 uc_lc },
5939         { "length",             uc_length },
5940         { "ltrim",              uc_ltrim },
5941         { "map",                uc_map },
5942         { "ord",                uc_ord },
5943         { "pop",                uc_pop },
5944         { "print",              uc_print },
5945         { "push",               uc_push },
5946         { "reverse",    uc_reverse },
5947         { "rindex",             uc_rindex },
5948         { "rtrim",              uc_rtrim },
5949         { "shift",              uc_shift },
5950         { "sort",               uc_sort },
5951         { "splice",             uc_splice },
5952         { "slice",              uc_slice },
5953         { "split",              uc_split },
5954         { "substr",             uc_substr },
5955         { "time",               uc_time },
5956         { "trim",               uc_trim },
5957         { "type",               uc_type },
5958         { "uchr",               uc_uchr },
5959         { "uc",                 uc_uc },
5960         { "unshift",    uc_unshift },
5961         { "values",             uc_values },
5962         { "sprintf",    uc_sprintf },
5963         { "printf",             uc_printf },
5964         { "require",    uc_require },
5965         { "iptoarr",    uc_iptoarr },
5966         { "arrtoip",    uc_arrtoip },
5967         { "match",              uc_match },
5968         { "replace",    uc_replace },
5969         { "json",               uc_json },
5970         { "include",    uc_include },
5971         { "warn",               uc_warn },
5972         { "system",             uc_system },
5973         { "trace",              uc_trace },
5974         { "proto",              uc_proto },
5975         { "sleep",              uc_sleep },
5976         { "assert",             uc_assert },
5977         { "render",             uc_render },
5978         { "regexp",             uc_regexp },
5979         { "wildcard",   uc_wildcard },
5980         { "sourcepath", uc_sourcepath },
5981         { "min",                uc_min },
5982         { "max",                uc_max },
5983         { "b64dec",             uc_b64dec },
5984         { "b64enc",             uc_b64enc },
5985         { "uniq",               uc_uniq },
5986         { "localtime",  uc_localtime },
5987         { "gmtime",             uc_gmtime },
5988         { "timelocal",  uc_timelocal },
5989         { "timegm",             uc_timegm },
5990         { "clock",              uc_clock },
5991         { "hexdec",             uc_hexdec },
5992         { "hexenc",             uc_hexenc },
5993         { "gc",                 uc_gc },
5994         { "loadstring", uc_loadstring },
5995         { "loadfile",   uc_loadfile },
5996         { "call",               uc_callfunc },
5997         { "signal",             uc_signal },
5998 };
5999 
6000 
6001 void
6002 uc_stdlib_load(uc_value_t *scope)
6003 {
6004         uc_function_list_register(scope, uc_stdlib_functions);
6005 }
6006 
6007 uc_cfn_ptr_t
6008 uc_stdlib_function(const char *name)
6009 {
6010         size_t i;
6011 
6012         for (i = 0; i < ARRAY_SIZE(uc_stdlib_functions); i++)
6013                 if (!strcmp(uc_stdlib_functions[i].name, name))
6014                         return uc_stdlib_functions[i].func;
6015 
6016         return NULL;
6017 }
6018 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt