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

Sources/ucode/lib.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt