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

Sources/ucode/lib.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt