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

Sources/ucode/lib.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt