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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt