• 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 #include <stdio.h>
 18 #include <stdlib.h>
 19 #include <stdarg.h>
 20 #include <string.h>
 21 #include <signal.h>
 22 #include <ctype.h>
 23 #include <errno.h>
 24 #include <math.h>
 25 #include <time.h>
 26 #include <dlfcn.h>
 27 #include <libgen.h>
 28 #include <unistd.h>
 29 #include <arpa/inet.h>
 30 #include <sys/stat.h>
 31 #include <sys/types.h>
 32 #include <sys/wait.h>
 33 #include <fnmatch.h>
 34 #include <assert.h>
 35 
 36 #include "json-c-compat.h"
 37 
 38 #include "ucode/lexer.h"
 39 #include "ucode/compiler.h"
 40 #include "ucode/vm.h"
 41 #include "ucode/lib.h"
 42 #include "ucode/source.h"
 43 #include "ucode/program.h"
 44 
 45 static void
 46 format_context_line(uc_stringbuf_t *buf, const char *line, size_t off, bool compact)
 47 {
 48         unsigned padlen, i;
 49         const char *p;
 50 
 51         for (p = line, padlen = 0; *p != '\n' && *p != '\0'; p++) {
 52                 if (compact && (p - line) == (ptrdiff_t)off)
 53                         ucv_stringbuf_append(buf, "\033[22m");
 54 
 55                 switch (*p) {
 56                 case '\t':
 57                         ucv_stringbuf_append(buf, "    ");
 58                         if (p < line + off)
 59                                 padlen += 4;
 60                         break;
 61 
 62                 case '\r':
 63                 case '\v':
 64                         ucv_stringbuf_append(buf, " ");
 65                         if (p < line + off)
 66                                 padlen++;
 67                         break;
 68 
 69                 default:
 70                         ucv_stringbuf_addstr(buf, p, 1);
 71                         if (p < line + off)
 72                                 padlen++;
 73                 }
 74         }
 75 
 76         if (compact) {
 77                 ucv_stringbuf_append(buf, "\033[m\n");
 78 
 79                 return;
 80         }
 81 
 82         ucv_stringbuf_append(buf, "`\n  ");
 83 
 84         if (padlen < strlen("Near here ^")) {
 85                 for (i = 0; i < padlen; i++)
 86                         ucv_stringbuf_append(buf, " ");
 87 
 88                 ucv_stringbuf_append(buf, "^-- Near here\n");
 89         }
 90         else {
 91                 ucv_stringbuf_append(buf, "Near here ");
 92 
 93                 for (i = strlen("Near here "); i < padlen; i++)
 94                         ucv_stringbuf_append(buf, "-");
 95 
 96                 ucv_stringbuf_append(buf, "^\n");
 97         }
 98 }
 99 
100 static char *
101 source_filename(uc_source_t *src, uint32_t line)
102 {
103         const char *name = src->filename ? basename(src->filename) : "[?]";
104         static char buf[sizeof("xxxxxxxxx.uc:0000000000")];
105         size_t len = strlen(name);
106 
107         if (len > 12)
108                 snprintf(buf, sizeof(buf), "...%s:%u", name + (len - 9), line);
109         else
110                 snprintf(buf, sizeof(buf), "%12s:%u", name, line);
111 
112         return buf;
113 }
114 
115 bool
116 uc_source_context_format(uc_stringbuf_t *buf, uc_source_t *src, size_t off, bool compact)
117 {
118         size_t len, rlen;
119         bool truncated;
120         char line[256];
121         long srcpos;
122         int eline;
123 
124         srcpos = ftell(src->fp);
125 
126         if (srcpos == -1)
127                 return false;
128 
129         fseek(src->fp, 0, SEEK_SET);
130 
131         truncated = false;
132         eline = 1;
133         rlen = 0;
134 
135         while (fgets(line, sizeof(line), src->fp)) {
136                 len = strlen(line);
137                 rlen += len;
138 
139                 if (rlen >= off) {
140                         if (compact)
141                                 ucv_stringbuf_printf(buf, "\033[2;40;97m%17s  %s",
142                                         source_filename(src, eline),
143                                         truncated ? "..." : "");
144                         else
145                                 ucv_stringbuf_printf(buf, "\n `%s",
146                                         truncated ? "..." : "");
147 
148                         format_context_line(buf, line, len - (rlen - off) + (truncated ? 3 : 0), compact);
149                         break;
150                 }
151 
152                 truncated = (len > 0 && line[len-1] != '\n');
153                 eline += !truncated;
154         }
155 
156         fseek(src->fp, srcpos, SEEK_SET);
157 
158         return true;
159 }
160 
161 bool
162 uc_error_context_format(uc_stringbuf_t *buf, uc_source_t *src, uc_value_t *stacktrace, size_t off)
163 {
164         uc_value_t *e, *fn, *file, *line, *byte;
165         const char *path;
166         size_t idx;
167 
168         for (idx = 0; idx < (stacktrace ? ucv_array_length(stacktrace) : 0); idx++) {
169                 e = ucv_array_get(stacktrace, idx);
170                 fn = ucv_object_get(e, "function", NULL);
171                 file = ucv_object_get(e, "filename", NULL);
172 
173                 if (idx == 0) {
174                         path = (file && strcmp(ucv_string_get(file), "[stdin]"))
175                                 ? ucv_string_get(file) : NULL;
176 
177                         if (path && fn)
178                                 ucv_stringbuf_printf(buf, "In %s(), file %s, ", ucv_string_get(fn), path);
179                         else if (fn)
180                                 ucv_stringbuf_printf(buf, "In %s(), ", ucv_string_get(fn));
181                         else if (path)
182                                 ucv_stringbuf_printf(buf, "In %s, ", path);
183                         else
184                                 ucv_stringbuf_append(buf, "In ");
185 
186                         ucv_stringbuf_printf(buf, "line %" PRId64 ", byte %" PRId64 ":\n",
187                                 ucv_int64_get(ucv_object_get(e, "line", NULL)),
188                                 ucv_int64_get(ucv_object_get(e, "byte", NULL)));
189                 }
190                 else {
191                         line = ucv_object_get(e, "line", NULL);
192                         byte = ucv_object_get(e, "byte", NULL);
193 
194                         ucv_stringbuf_printf(buf, "  called from %s%s (%s",
195                                 fn ? "function " : "anonymous function",
196                                 fn ? ucv_string_get(fn) : "",
197                                 file ? ucv_string_get(file) : "");
198 
199                         if (line && byte)
200                                 ucv_stringbuf_printf(buf, ":%" PRId64 ":%" PRId64 ")\n",
201                                         ucv_int64_get(line),
202                                         ucv_int64_get(byte));
203                         else
204                                 ucv_stringbuf_append(buf, "[C])\n");
205                 }
206         }
207 
208         return uc_source_context_format(buf, src, off, false);
209 }
210 
211 void
212 uc_error_message_indent(char **msg) {
213         uc_stringbuf_t *buf = xprintbuf_new();
214         char *s, *p, *nl;
215         size_t len;
216 
217         if (!msg || !*msg)
218                 return;
219 
220         s = *msg;
221         len = strlen(s);
222 
223         while (len > 0 && s[len-1] == '\n')
224                 s[--len] = 0;
225 
226         for (p = s, nl = strchr(p, '\n'); p != NULL;
227              p = nl ? nl + 1 : NULL, nl = p ? strchr(p, '\n') : NULL)
228         {
229                 if (!nl)
230                         ucv_stringbuf_printf(buf, "  | %s", p);
231                 else if (nl != p)
232                         ucv_stringbuf_printf(buf, "  | %.*s\n", (int)(nl - p), p);
233                 else
234                         ucv_stringbuf_append(buf, "  |\n");
235         }
236 
237         ucv_stringbuf_append(buf, "\n");
238 
239         *msg = buf->buf;
240 
241         free(buf);
242         free(s);
243 }
244 
245 static char *uc_cast_string(uc_vm_t *vm, uc_value_t **v, bool *freeable) {
246         if (ucv_type(*v) == UC_STRING) {
247                 *freeable = false;
248 
249                 return _ucv_string_get(v);
250         }
251 
252         *freeable = true;
253 
254         return ucv_to_string(vm, *v);
255 }
256 
257 static void
258 uc_vm_ctx_push(uc_vm_t *vm)
259 {
260         uc_value_t *ctx = NULL;
261 
262         if (vm->callframes.count >= 2)
263                 ctx = vm->callframes.entries[vm->callframes.count - 2].ctx;
264 
265         uc_vm_stack_push(vm, ucv_get(ctx));
266 }
267 
268 static uc_value_t *
269 uc_print_common(uc_vm_t *vm, size_t nargs, FILE *fh)
270 {
271         uc_value_t *item;
272         size_t reslen = 0;
273         size_t len = 0;
274         size_t arridx;
275         char *p;
276 
277         for (arridx = 0; arridx < nargs; arridx++) {
278                 item = uc_fn_arg(arridx);
279 
280                 if (ucv_type(item) == UC_STRING) {
281                         len = ucv_string_length(item);
282                         reslen += fwrite(ucv_string_get(item), 1, len, fh);
283                 }
284                 else if (item != NULL) {
285                         p = ucv_to_string(vm, item);
286                         len = strlen(p);
287                         reslen += fwrite(p, 1, len, fh);
288                         free(p);
289                 }
290         }
291 
292         return ucv_int64_new(reslen);
293 }
294 
295 
296 static uc_value_t *
297 uc_print(uc_vm_t *vm, size_t nargs)
298 {
299         return uc_print_common(vm, nargs, vm->output);
300 }
301 
302 static uc_value_t *
303 uc_length(uc_vm_t *vm, size_t nargs)
304 {
305         uc_value_t *arg = uc_fn_arg(0);
306 
307         switch (ucv_type(arg)) {
308         case UC_OBJECT:
309                 return ucv_int64_new(ucv_object_length(arg));
310 
311         case UC_ARRAY:
312                 return ucv_int64_new(ucv_array_length(arg));
313 
314         case UC_STRING:
315                 return ucv_int64_new(ucv_string_length(arg));
316 
317         default:
318                 return NULL;
319         }
320 }
321 
322 static int
323 uc_uniq_ucv_equal(const void *k1, const void *k2);
324 
325 static uc_value_t *
326 uc_index(uc_vm_t *vm, size_t nargs, bool right)
327 {
328         uc_value_t *stack = uc_fn_arg(0);
329         uc_value_t *needle = uc_fn_arg(1);
330         const char *sstr, *nstr, *p;
331         size_t arridx, slen, nlen;
332         ssize_t ret = -1;
333 
334         switch (ucv_type(stack)) {
335         case UC_ARRAY:
336                 if (right) {
337                         for (arridx = ucv_array_length(stack); arridx > 0; arridx--) {
338                                 if (uc_uniq_ucv_equal(ucv_array_get(stack, arridx - 1), needle)) {
339                                         ret = (ssize_t)(arridx - 1);
340                                         break;
341                                 }
342                         }
343                 }
344                 else {
345                         for (arridx = 0, slen = ucv_array_length(stack); arridx < slen; arridx++) {
346                                 if (uc_uniq_ucv_equal(ucv_array_get(stack, arridx), needle)) {
347                                         ret = (ssize_t)arridx;
348                                         break;
349                                 }
350                         }
351                 }
352 
353                 return ucv_int64_new(ret);
354 
355         case UC_STRING:
356                 if (ucv_type(needle) == UC_STRING) {
357                         sstr = ucv_string_get(stack);
358                         slen = ucv_string_length(stack);
359                         nstr = ucv_string_get(needle);
360                         nlen = ucv_string_length(needle);
361 
362                         if (slen == nlen) {
363                                 if (memcmp(sstr, nstr, nlen) == 0)
364                                         ret = 0;
365                         }
366                         else if (slen > nlen) {
367                                 if (right) {
368                                         p = sstr + slen - nlen;
369 
370                                         do {
371                                                 if (memcmp(p, nstr, nlen) == 0) {
372                                                         ret = (ssize_t)(p - sstr);
373                                                         break;
374                                                 }
375                                         }
376                                         while (--p != sstr);
377                                 }
378                                 else {
379                                         p = (const char *)memmem(sstr, slen, nstr, nlen);
380 
381                                         if (p)
382                                                 ret = (ssize_t)(p - sstr);
383                                 }
384                         }
385                 }
386 
387                 return ucv_int64_new(ret);
388 
389         default:
390                 return NULL;
391         }
392 }
393 
394 static uc_value_t *
395 uc_lindex(uc_vm_t *vm, size_t nargs)
396 {
397         return uc_index(vm, nargs, false);
398 }
399 
400 static uc_value_t *
401 uc_rindex(uc_vm_t *vm, size_t nargs)
402 {
403         return uc_index(vm, nargs, true);
404 }
405 
406 static bool
407 assert_mutable_array(uc_vm_t *vm, uc_value_t *val)
408 {
409         if (ucv_type(val) != UC_ARRAY)
410                 return false;
411 
412         if (ucv_is_constant(val)) {
413                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
414                                       "%s value is immutable",
415                                       ucv_typename(val));
416 
417                 return false;
418         }
419 
420         return true;
421 }
422 
423 static uc_value_t *
424 uc_push(uc_vm_t *vm, size_t nargs)
425 {
426         uc_value_t *arr = uc_fn_arg(0);
427         uc_value_t *item = NULL;
428         size_t arridx;
429 
430         if (!assert_mutable_array(vm, arr))
431                 return NULL;
432 
433         for (arridx = 1; arridx < nargs; arridx++) {
434                 item = uc_fn_arg(arridx);
435                 ucv_array_push(arr, ucv_get(item));
436         }
437 
438         return ucv_get(item);
439 }
440 
441 static uc_value_t *
442 uc_pop(uc_vm_t *vm, size_t nargs)
443 {
444         uc_value_t *arr = uc_fn_arg(0);
445 
446         if (!assert_mutable_array(vm, arr))
447                 return NULL;
448 
449         return ucv_array_pop(arr);
450 }
451 
452 static uc_value_t *
453 uc_shift(uc_vm_t *vm, size_t nargs)
454 {
455         uc_value_t *arr = uc_fn_arg(0);
456 
457         if (!assert_mutable_array(vm, arr))
458                 return NULL;
459 
460         return ucv_array_shift(arr);
461 }
462 
463 static uc_value_t *
464 uc_unshift(uc_vm_t *vm, size_t nargs)
465 {
466         uc_value_t *arr = uc_fn_arg(0);
467         uc_value_t *item;
468         size_t i;
469 
470         if (!assert_mutable_array(vm, arr))
471                 return NULL;
472 
473         for (i = 1; i < nargs; i++) {
474                 item = uc_fn_arg(nargs - i);
475                 ucv_array_unshift(arr, ucv_get(item));
476         }
477 
478         return (nargs > 1) ? ucv_get(uc_fn_arg(nargs - 1)) : NULL;
479 }
480 
481 static uc_value_t *
482 uc_chr(uc_vm_t *vm, size_t nargs)
483 {
484         uc_value_t *rv = NULL;
485         size_t idx;
486         int64_t n;
487         char *str;
488 
489         if (!nargs)
490                 return ucv_string_new_length("", 0);
491 
492         str = xalloc(nargs);
493 
494         for (idx = 0; idx < nargs; idx++) {
495                 n = ucv_to_integer(uc_fn_arg(idx));
496 
497                 if (n < 0)
498                         n = 0;
499                 else if (n > 255)
500                         n = 255;
501 
502                 str[idx] = (char)n;
503         }
504 
505         rv = ucv_string_new_length(str, nargs);
506         free(str);
507 
508         return rv;
509 }
510 
511 static uc_value_t *
512 uc_die(uc_vm_t *vm, size_t nargs)
513 {
514         uc_value_t *msg = uc_fn_arg(0);
515         bool freeable = false;
516         char *s;
517 
518         s = msg ? uc_cast_string(vm, &msg, &freeable) : "Died";
519 
520         uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s);
521 
522         if (freeable)
523                 free(s);
524 
525         return NULL;
526 }
527 
528 static uc_value_t *
529 uc_exists(uc_vm_t *vm, size_t nargs)
530 {
531         uc_value_t *obj = uc_fn_arg(0);
532         uc_value_t *key = uc_fn_arg(1);
533         bool found, freeable;
534         char *k;
535 
536         if (ucv_type(obj) != UC_OBJECT)
537                 return ucv_boolean_new(false);
538 
539         k = uc_cast_string(vm, &key, &freeable);
540 
541         ucv_object_get(obj, k, &found);
542 
543         if (freeable)
544                 free(k);
545 
546         return ucv_boolean_new(found);
547 }
548 
549 static uc_value_t *
550 uc_exit(uc_vm_t *vm, size_t nargs)
551 {
552         int64_t n = ucv_to_integer(uc_fn_arg(0));
553 
554         vm->arg.s32 = (int32_t)n;
555         uc_vm_raise_exception(vm, EXCEPTION_EXIT, "Terminated");
556 
557         return NULL;
558 }
559 
560 static uc_value_t *
561 uc_getenv(uc_vm_t *vm, size_t nargs)
562 {
563         uc_value_t *key = uc_fn_arg(0), *rv = NULL;
564         extern char **environ;
565         char *k, *v;
566 
567         if (!key) {
568                 rv = ucv_object_new(vm);
569 
570                 while (*environ) {
571                         v = strchr(*environ, '=');
572 
573                         if (v) {
574                                 xasprintf(&k, "%.*s", (int)(v - *environ), *environ);
575                                 ucv_object_add(rv, k, ucv_string_new(v + 1));
576                                 free(k);
577                         }
578 
579                         environ++;
580                 }
581         }
582         else if (ucv_type(key) == UC_STRING) {
583                 k = ucv_string_get(key);
584                 v = getenv(k);
585 
586                 if (v)
587                         rv = ucv_string_new(v);
588         }
589 
590         return rv;
591 }
592 
593 static uc_value_t *
594 uc_filter(uc_vm_t *vm, size_t nargs)
595 {
596         uc_value_t *obj = uc_fn_arg(0);
597         uc_value_t *func = uc_fn_arg(1);
598         uc_value_t *rv, *arr;
599         size_t arridx, arrlen;
600 
601         if (ucv_type(obj) != UC_ARRAY)
602                 return NULL;
603 
604         arr = ucv_array_new(vm);
605 
606         for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) {
607                 uc_vm_ctx_push(vm);
608                 uc_vm_stack_push(vm, ucv_get(func));
609                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx)));
610                 uc_vm_stack_push(vm, ucv_int64_new(arridx));
611                 uc_vm_stack_push(vm, ucv_get(obj));
612 
613                 if (uc_vm_call(vm, true, 3)) {
614                         ucv_put(arr);
615 
616                         return NULL;
617                 }
618 
619                 rv = uc_vm_stack_pop(vm);
620 
621                 if (ucv_is_truish(rv))
622                         ucv_array_push(arr, ucv_get(ucv_array_get(obj, arridx)));
623 
624                 ucv_put(rv);
625         }
626 
627         return arr;
628 }
629 
630 static uc_value_t *
631 uc_hex(uc_vm_t *vm, size_t nargs)
632 {
633         uc_value_t *val = uc_fn_arg(0);
634         char *e, *v;
635         int64_t n;
636 
637         v = ucv_string_get(val);
638 
639         if (!v || !isxdigit(*v))
640                 return ucv_double_new(NAN);
641 
642         n = strtoll(v, &e, 16);
643 
644         if (e == v || *e)
645                 return ucv_double_new(NAN);
646 
647         return ucv_int64_new(n);
648 }
649 
650 static uc_value_t *
651 uc_int(uc_vm_t *vm, size_t nargs)
652 {
653         uc_value_t *val = uc_fn_arg(0);
654         uc_value_t *base = uc_fn_arg(1);
655         char *e, *v;
656         int64_t n;
657 
658         if (ucv_type(val) == UC_STRING) {
659                 errno = 0;
660                 v = ucv_string_get(val);
661                 n = strtoll(v, &e, base ? ucv_int64_get(base) : 10);
662 
663                 if (e == v)
664                         return ucv_double_new(NAN);
665         }
666         else {
667                 n = ucv_to_integer(val);
668         }
669 
670         if (errno == EINVAL || errno == ERANGE)
671                 return ucv_double_new(NAN);
672 
673         return ucv_int64_new(n);
674 }
675 
676 static uc_value_t *
677 uc_join(uc_vm_t *vm, size_t nargs)
678 {
679         uc_value_t *sep = uc_fn_arg(0);
680         uc_value_t *arr = uc_fn_arg(1);
681         size_t arrlen, arridx;
682         uc_stringbuf_t *buf;
683 
684         if (ucv_type(arr) != UC_ARRAY)
685                 return NULL;
686 
687         buf = ucv_stringbuf_new();
688 
689         for (arrlen = ucv_array_length(arr), arridx = 0; arridx < arrlen; arridx++) {
690                 if (arridx > 0)
691                         ucv_to_stringbuf(vm, buf, sep, false);
692 
693                 ucv_to_stringbuf(vm, buf, ucv_array_get(arr, arridx), false);
694         }
695 
696         return ucv_stringbuf_finish(buf);
697 }
698 
699 static uc_value_t *
700 uc_keys(uc_vm_t *vm, size_t nargs)
701 {
702         uc_value_t *obj = uc_fn_arg(0);
703         uc_value_t *arr = NULL;
704 
705         if (ucv_type(obj) != UC_OBJECT)
706                 return NULL;
707 
708         arr = ucv_array_new(vm);
709 
710         ucv_object_foreach(obj, key, val) {
711                 (void)val;
712                 ucv_array_push(arr, ucv_string_new(key));
713         }
714 
715         return arr;
716 }
717 
718 static uc_value_t *
719 uc_lc(uc_vm_t *vm, size_t nargs)
720 {
721         char *str = ucv_to_string(vm, uc_fn_arg(0));
722         uc_value_t *rv = NULL;
723         char *p;
724 
725         if (!str)
726                 return NULL;
727 
728         for (p = str; *p; p++)
729                 if (*p >= 'A' && *p <= 'Z')
730                         *p |= 32;
731 
732         rv = ucv_string_new(str);
733 
734         free(str);
735 
736         return rv;
737 }
738 
739 static uc_value_t *
740 uc_map(uc_vm_t *vm, size_t nargs)
741 {
742         uc_value_t *obj = uc_fn_arg(0);
743         uc_value_t *func = uc_fn_arg(1);
744         uc_value_t *arr, *rv;
745         size_t arridx, arrlen;
746 
747         if (ucv_type(obj) != UC_ARRAY)
748                 return NULL;
749 
750         arr = ucv_array_new(vm);
751 
752         for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) {
753                 uc_vm_ctx_push(vm);
754                 uc_vm_stack_push(vm, ucv_get(func));
755                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx)));
756                 uc_vm_stack_push(vm, ucv_int64_new(arridx));
757                 uc_vm_stack_push(vm, ucv_get(obj));
758 
759                 if (uc_vm_call(vm, true, 3)) {
760                         ucv_put(arr);
761 
762                         return NULL;
763                 }
764 
765                 rv = uc_vm_stack_pop(vm);
766 
767                 ucv_array_push(arr, rv);
768         }
769 
770         return arr;
771 }
772 
773 static uc_value_t *
774 uc_ord(uc_vm_t *vm, size_t nargs)
775 {
776         uc_value_t *obj = uc_fn_arg(0);
777         const char *str;
778         int64_t n = 0;
779         size_t len;
780 
781         if (ucv_type(obj) != UC_STRING)
782                 return NULL;
783 
784         str = ucv_string_get(obj);
785         len = ucv_string_length(obj);
786 
787         if (nargs > 1) {
788                 n = ucv_int64_get(uc_fn_arg(1));
789 
790                 if (errno == EINVAL)
791                         return NULL;
792 
793                 if (n < 0)
794                         n += len;
795         }
796 
797         if (n < 0 || (uint64_t)n >= len)
798                 return NULL;
799 
800         return ucv_int64_new((uint8_t)str[n]);
801 }
802 
803 static uc_value_t *
804 uc_type(uc_vm_t *vm, size_t nargs)
805 {
806         uc_value_t *v = uc_fn_arg(0);
807         uc_type_t t = ucv_type(v);
808 
809         switch (t) {
810         case UC_CFUNCTION:
811         case UC_CLOSURE:
812                 return ucv_string_new("function");
813 
814         case UC_INTEGER:
815                 return ucv_string_new("int");
816 
817         case UC_BOOLEAN:
818                 return ucv_string_new("bool");
819 
820         case UC_NULL:
821                 return NULL;
822 
823         default:
824                 return ucv_string_new(ucv_typename(v));
825         }
826 }
827 
828 static uc_value_t *
829 uc_reverse(uc_vm_t *vm, size_t nargs)
830 {
831         uc_value_t *obj = uc_fn_arg(0);
832         uc_value_t *rv = NULL;
833         size_t len, arridx;
834         const char *str;
835         char *dup, *p;
836 
837         if (ucv_type(obj) == UC_ARRAY) {
838                 if (!assert_mutable_array(vm, obj))
839                         return NULL;
840 
841                 rv = ucv_array_new(vm);
842 
843                 for (arridx = ucv_array_length(obj); arridx > 0; arridx--)
844                         ucv_array_push(rv, ucv_get(ucv_array_get(obj, arridx - 1)));
845         }
846         else if (ucv_type(obj) == UC_STRING) {
847                 len = ucv_string_length(obj);
848                 str = ucv_string_get(obj);
849                 p = dup = xalloc(len + 1);
850 
851                 while (len > 0)
852                         *p++ = str[--len];
853 
854                 rv = ucv_string_new(dup);
855 
856                 free(dup);
857         }
858 
859         return rv;
860 }
861 
862 
863 static struct {
864         uc_vm_t *vm;
865         bool ex;
866         uc_value_t *fn;
867 } sort_ctx;
868 
869 static int
870 default_cmp(uc_value_t *v1, uc_value_t *v2)
871 {
872         char *s1, *s2;
873         bool f1, f2;
874         int res;
875 
876         /* when both operands are numeric then compare numerically */
877         if ((ucv_type(v1) == UC_INTEGER || ucv_type(v1) == UC_DOUBLE) &&
878             (ucv_type(v2) == UC_INTEGER || ucv_type(v2) == UC_DOUBLE)) {
879                 ucv_compare(0, v1, v2, &res);
880 
881                 return res;
882         }
883 
884         /* otherwise convert both operands to strings and compare lexically */
885         s1 = uc_cast_string(sort_ctx.vm, &v1, &f1);
886         s2 = uc_cast_string(sort_ctx.vm, &v2, &f2);
887 
888         res = strcmp(s1, s2);
889 
890         if (f1) free(s1);
891         if (f2) free(s2);
892 
893         return res;
894 }
895 
896 static int
897 sort_fn(const void *k1, const void *k2)
898 {
899         uc_value_t *rv, *null = ucv_int64_new(0);
900         uc_value_t * const *v1 = k1;
901         uc_value_t * const *v2 = k2;
902         int res;
903 
904         if (!sort_ctx.fn)
905                 return default_cmp(*v1, *v2);
906 
907         if (sort_ctx.ex)
908                 return 0;
909 
910         uc_vm_ctx_push(sort_ctx.vm);
911         uc_vm_stack_push(sort_ctx.vm, ucv_get(sort_ctx.fn));
912         uc_vm_stack_push(sort_ctx.vm, ucv_get(*v1));
913         uc_vm_stack_push(sort_ctx.vm, ucv_get(*v2));
914 
915         if (uc_vm_call(sort_ctx.vm, true, 2)) {
916                 sort_ctx.ex = true;
917 
918                 return 0;
919         }
920 
921         rv = uc_vm_stack_pop(sort_ctx.vm);
922 
923         ucv_compare(0, rv, null, &res);
924 
925         ucv_put(null);
926         ucv_put(rv);
927 
928         return res;
929 }
930 
931 static uc_value_t *
932 uc_sort(uc_vm_t *vm, size_t nargs)
933 {
934         uc_value_t *arr = uc_fn_arg(0);
935         uc_value_t *fn = uc_fn_arg(1);
936 
937         if (!assert_mutable_array(vm, arr))
938                 return NULL;
939 
940         sort_ctx.vm = vm;
941         sort_ctx.fn = fn;
942 
943         ucv_array_sort(arr, sort_fn);
944 
945         return sort_ctx.ex ? NULL : ucv_get(arr);
946 }
947 
948 static uc_value_t *
949 uc_splice(uc_vm_t *vm, size_t nargs)
950 {
951         uc_value_t *arr = uc_fn_arg(0);
952         int64_t ofs = ucv_to_integer(uc_fn_arg(1));
953         int64_t remlen = ucv_to_integer(uc_fn_arg(2));
954         size_t arrlen, addlen, idx;
955 
956         if (!assert_mutable_array(vm, arr))
957                 return NULL;
958 
959         arrlen = ucv_array_length(arr);
960         addlen = nargs;
961 
962         if (addlen == 1) {
963                 ofs = 0;
964                 addlen = 0;
965                 remlen = arrlen;
966         }
967         else if (addlen == 2) {
968                 if (ofs < 0) {
969                         ofs = arrlen + ofs;
970 
971                         if (ofs < 0)
972                                 ofs = 0;
973                 }
974                 else if ((uint64_t)ofs > arrlen) {
975                         ofs = arrlen;
976                 }
977 
978                 addlen = 0;
979                 remlen = arrlen - ofs;
980         }
981         else {
982                 if (ofs < 0) {
983                         ofs = arrlen + ofs;
984 
985                         if (ofs < 0)
986                                 ofs = 0;
987                 }
988                 else if ((uint64_t)ofs > arrlen) {
989                         ofs = arrlen;
990                 }
991 
992                 if (remlen < 0) {
993                         remlen = arrlen - ofs + remlen;
994 
995                         if (remlen < 0)
996                                 remlen = 0;
997                 }
998                 else if ((uint64_t)remlen > arrlen - (uint64_t)ofs) {
999                         remlen = arrlen - ofs;
1000                 }
1001 
1002                 addlen -= 3;
1003         }
1004 
1005         if (addlen < (uint64_t)remlen) {
1006                 ucv_array_delete(arr, ofs, remlen - addlen);
1007         }
1008         else if (addlen > (uint64_t)remlen) {
1009                 for (idx = arrlen; idx > (uint64_t)ofs; idx--)
1010                         ucv_array_set(arr, idx + addlen - remlen - 1,
1011                                 ucv_get(ucv_array_get(arr, idx - 1)));
1012         }
1013 
1014         for (idx = 0; idx < addlen; idx++)
1015                 ucv_array_set(arr, ofs + idx,
1016                         ucv_get(uc_fn_arg(3 + idx)));
1017 
1018         return ucv_get(arr);
1019 }
1020 
1021 static uc_value_t *
1022 uc_slice(uc_vm_t *vm, size_t nargs)
1023 {
1024         uc_value_t *arr = uc_fn_arg(0);
1025         uc_value_t *sv = uc_fn_arg(1);
1026         uc_value_t *ev = uc_fn_arg(2);
1027         uc_value_t *res = NULL;
1028         int64_t off, end;
1029         size_t len;
1030 
1031         if (ucv_type(arr) != UC_ARRAY)
1032                 return NULL;
1033 
1034         len = ucv_array_length(arr);
1035         off = sv ? ucv_to_integer(sv) : 0;
1036         end = ev ? ucv_to_integer(ev) : (int64_t)len;
1037 
1038         if (off < 0) {
1039                 off = len + off;
1040 
1041                 if (off < 0)
1042                         off = 0;
1043         }
1044         else if ((uint64_t)off > len) {
1045                 off = len;
1046         }
1047 
1048         if (end < 0) {
1049                 end = len + end;
1050 
1051                 if (end < 0)
1052                         end = 0;
1053         }
1054         else if ((uint64_t)end > len) {
1055                 end = len;
1056         }
1057 
1058         res = ucv_array_new(vm);
1059 
1060         while (off < end)
1061                 ucv_array_push(res, ucv_get(ucv_array_get(arr, off++)));
1062 
1063         return res;
1064 }
1065 
1066 static uc_value_t *
1067 uc_split(uc_vm_t *vm, size_t nargs)
1068 {
1069         uc_value_t *str = uc_fn_arg(0);
1070         uc_value_t *sep = uc_fn_arg(1);
1071         uc_value_t *lim = uc_fn_arg(2);
1072         uc_value_t *arr = NULL;
1073         const char *p, *sepstr, *splitstr;
1074         size_t seplen, splitlen, limit;
1075         int eflags = 0, res;
1076         regmatch_t pmatch;
1077         uc_regexp_t *re;
1078 
1079         if (!sep || ucv_type(str) != UC_STRING)
1080                 return NULL;
1081 
1082         arr = ucv_array_new(vm);
1083         splitlen = ucv_string_length(str);
1084         p = splitstr = ucv_string_get(str);
1085         limit = lim ? ucv_uint64_get(lim) : SIZE_MAX;
1086 
1087         if (limit == 0)
1088                 goto out;
1089 
1090         if (ucv_type(sep) == UC_REGEXP) {
1091                 re = (uc_regexp_t *)sep;
1092 
1093                 while (limit > 1) {
1094                         res = regexec(&re->regexp, splitstr, 1, &pmatch, eflags);
1095 
1096                         if (res == REG_NOMATCH)
1097                                 break;
1098 
1099                         if (pmatch.rm_so != pmatch.rm_eo) {
1100                                 ucv_array_push(arr, ucv_string_new_length(splitstr, pmatch.rm_so));
1101                                 splitstr += pmatch.rm_eo;
1102                         }
1103                         else if (*splitstr) {
1104                                 ucv_array_push(arr, ucv_string_new_length(splitstr, 1));
1105                                 splitstr++;
1106                         }
1107                         else {
1108                                 goto out;
1109                         }
1110 
1111                         eflags |= REG_NOTBOL;
1112                         limit--;
1113                 }
1114 
1115                 ucv_array_push(arr, ucv_string_new(splitstr));
1116         }
1117         else if (ucv_type(sep) == UC_STRING) {
1118                 sepstr = ucv_string_get(sep);
1119                 seplen = ucv_string_length(sep);
1120 
1121                 if (splitlen == 0) {
1122                         ucv_array_push(arr, ucv_string_new_length("", 0));
1123                 }
1124                 else if (seplen == 0) {
1125                         while (limit > 1 && splitlen > 0) {
1126                                 ucv_array_push(arr, ucv_string_new_length(p, 1));
1127 
1128                                 limit--;
1129                                 splitlen--;
1130                                 p++;
1131                         }
1132 
1133                         if (splitlen > 0)
1134                                 ucv_array_push(arr, ucv_string_new_length(p, splitlen));
1135                 }
1136                 else {
1137                         while (limit > 1 && splitlen >= seplen) {
1138                                 if (!memcmp(p, sepstr, seplen)) {
1139                                         ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr));
1140 
1141                                         p = splitstr = p + seplen;
1142                                         splitlen -= seplen;
1143                                         limit--;
1144                                         continue;
1145                                 }
1146 
1147                                 splitlen--;
1148                                 p++;
1149                         }
1150 
1151                         ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr + splitlen));
1152                 }
1153         }
1154         else {
1155                 ucv_put(arr);
1156 
1157                 return NULL;
1158         }
1159 
1160 out:
1161         return arr;
1162 }
1163 
1164 static uc_value_t *
1165 uc_substr(uc_vm_t *vm, size_t nargs)
1166 {
1167         uc_value_t *str = uc_fn_arg(0);
1168         int64_t ofs = ucv_to_integer(uc_fn_arg(1));
1169         int64_t sublen = ucv_to_integer(uc_fn_arg(2));
1170         const char *p;
1171         size_t len;
1172 
1173         if (ucv_type(str) != UC_STRING)
1174                 return NULL;
1175 
1176         p = ucv_string_get(str);
1177         len = ucv_string_length(str);
1178 
1179         switch (nargs) {
1180         case 1:
1181                 ofs = 0;
1182                 sublen = len;
1183 
1184                 break;
1185 
1186         case 2:
1187                 if (ofs < 0) {
1188                         ofs = len + ofs;
1189 
1190                         if (ofs < 0)
1191                                 ofs = 0;
1192                 }
1193                 else if ((uint64_t)ofs > len) {
1194                         ofs = len;
1195                 }
1196 
1197                 sublen = len - ofs;
1198 
1199                 break;
1200 
1201         default:
1202                 if (ofs < 0) {
1203                         ofs = len + ofs;
1204 
1205                         if (ofs < 0)
1206                                 ofs = 0;
1207                 }
1208                 else if ((uint64_t)ofs > len) {
1209                         ofs = len;
1210                 }
1211 
1212                 if (sublen < 0) {
1213                         sublen = len - ofs + sublen;
1214 
1215                         if (sublen < 0)
1216                                 sublen = 0;
1217                 }
1218                 else if ((uint64_t)sublen > len - (uint64_t)ofs) {
1219                         sublen = len - ofs;
1220                 }
1221 
1222                 break;
1223         }
1224 
1225         return ucv_string_new_length(p + ofs, sublen);
1226 }
1227 
1228 static uc_value_t *
1229 uc_time(uc_vm_t *vm, size_t nargs)
1230 {
1231         time_t t = time(NULL);
1232 
1233         return ucv_int64_new((int64_t)t);
1234 }
1235 
1236 static uc_value_t *
1237 uc_uc(uc_vm_t *vm, size_t nargs)
1238 {
1239         char *str = ucv_to_string(vm, uc_fn_arg(0));
1240         uc_value_t *rv = NULL;
1241         char *p;
1242 
1243         if (!str)
1244                 return NULL;
1245 
1246         for (p = str; *p; p++)
1247                 if (*p >= 'a' && *p <= 'z')
1248                         *p &= ~32;
1249 
1250         rv = ucv_string_new(str);
1251 
1252         free(str);
1253 
1254         return rv;
1255 }
1256 
1257 static uc_value_t *
1258 uc_uchr(uc_vm_t *vm, size_t nargs)
1259 {
1260         uc_value_t *rv = NULL;
1261         size_t idx, ulen;
1262         char *p, *str;
1263         int64_t n;
1264         int rem;
1265 
1266         for (idx = 0, ulen = 0; idx < nargs; idx++) {
1267                 n = ucv_to_integer(uc_fn_arg(idx));
1268 
1269                 if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
1270                         ulen += 3;
1271                 else if (n <= 0x7F)
1272                         ulen++;
1273                 else if (n <= 0x7FF)
1274                         ulen += 2;
1275                 else if (n <= 0xFFFF)
1276                         ulen += 3;
1277                 else
1278                         ulen += 4;
1279         }
1280 
1281         str = xalloc(ulen);
1282 
1283         for (idx = 0, p = str, rem = ulen; idx < nargs; idx++) {
1284                 n = ucv_to_integer(uc_fn_arg(idx));
1285 
1286                 if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
1287                         n = 0xFFFD;
1288 
1289                 if (!utf8enc(&p, &rem, n))
1290                         break;
1291         }
1292 
1293         rv = ucv_string_new_length(str, ulen);
1294 
1295         free(str);
1296 
1297         return rv;
1298 }
1299 
1300 static uc_value_t *
1301 uc_values(uc_vm_t *vm, size_t nargs)
1302 {
1303         uc_value_t *obj = uc_fn_arg(0);
1304         uc_value_t *arr;
1305 
1306         if (ucv_type(obj) != UC_OBJECT)
1307                 return NULL;
1308 
1309         arr = ucv_array_new(vm);
1310 
1311         ucv_object_foreach(obj, key, val) {
1312                 (void)key;
1313                 ucv_array_push(arr, ucv_get(val));
1314         }
1315 
1316         return arr;
1317 }
1318 
1319 static uc_value_t *
1320 uc_trim_common(uc_vm_t *vm, size_t nargs, bool start, bool end)
1321 {
1322         uc_value_t *str = uc_fn_arg(0);
1323         uc_value_t *chr = uc_fn_arg(1);
1324         const char *p, *c;
1325         size_t len;
1326 
1327         if (ucv_type(str) != UC_STRING ||
1328                 (chr != NULL && ucv_type(chr) != UC_STRING))
1329                 return NULL;
1330 
1331         c = ucv_string_get(chr);
1332         c = c ? c : " \t\r\n";
1333 
1334         p = ucv_string_get(str);
1335         len = ucv_string_length(str);
1336 
1337         if (start) {
1338                 while (*p) {
1339                         if (!strchr(c, *p))
1340                                 break;
1341 
1342                         p++;
1343                         len--;
1344                 }
1345         }
1346 
1347         if (end) {
1348                 while (len > 0) {
1349                         if (!strchr(c, p[len - 1]))
1350                                 break;
1351 
1352                         len--;
1353                 }
1354         }
1355 
1356         return ucv_string_new_length(p, len);
1357 }
1358 
1359 static uc_value_t *
1360 uc_trim(uc_vm_t *vm, size_t nargs)
1361 {
1362         return uc_trim_common(vm, nargs, true, true);
1363 }
1364 
1365 static uc_value_t *
1366 uc_ltrim(uc_vm_t *vm, size_t nargs)
1367 {
1368         return uc_trim_common(vm, nargs, true, false);
1369 }
1370 
1371 static uc_value_t *
1372 uc_rtrim(uc_vm_t *vm, size_t nargs)
1373 {
1374         return uc_trim_common(vm, nargs, false, true);
1375 }
1376 
1377 enum {
1378         FMT_F_ALT   = (1 << 0),
1379         FMT_F_ZERO  = (1 << 1),
1380         FMT_F_LEFT  = (1 << 2),
1381         FMT_F_SPACE = (1 << 3),
1382         FMT_F_SIGN  = (1 << 4),
1383         FMT_F_WIDTH = (1 << 5),
1384         FMT_F_PREC  = (1 << 6),
1385 };
1386 
1387 enum {
1388         FMT_C_NONE = (1 << 0),
1389         FMT_C_INT  = (1 << 1),
1390         FMT_C_UINT = (1 << 2),
1391         FMT_C_DBL  = (1 << 3),
1392         FMT_C_CHR  = (1 << 4),
1393         FMT_C_STR  = (1 << 5),
1394         FMT_C_JSON = (1 << 6),
1395 };
1396 
1397 static void
1398 uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
1399 {
1400         char *s, sfmt[sizeof("%#0- +0123456789.0123456789%")];
1401         uint32_t conv, flags, width, precision;
1402         uc_value_t *fmt = uc_fn_arg(0), *arg;
1403         const char *fstr, *last, *p, *cfmt;
1404         size_t argidx = 1, argpos, sfmtlen;
1405         uint64_t u;
1406         int64_t n;
1407         double d;
1408 
1409         if (ucv_type(fmt) == UC_STRING)
1410                 fstr = ucv_string_get(fmt);
1411         else
1412                 fstr = "";
1413 
1414         for (last = p = fstr; *p; p++) {
1415                 if (*p == '%') {
1416                         ucv_stringbuf_addstr(buf, last, p - last);
1417 
1418                         last = p++;
1419 
1420                         flags = 0;
1421                         width = 0;
1422                         precision = 0;
1423 
1424                         argpos = argidx;
1425 
1426                         if (*p >= '1' && *p <= '9') {
1427                                 while (isdigit(*p))
1428                                         width = width * 10 + (*p++ - '');
1429 
1430                                 /* if a dollar sign follows, this is an argument index */
1431                                 if (*p == '$') {
1432                                         argpos = width;
1433                                         width = 0;
1434                                         p++;
1435                                 }
1436 
1437                                 /* otherwise skip to parsing precision, flags can't possibly follow */
1438                                 else {
1439                                         flags |= FMT_F_WIDTH;
1440                                         goto parse_precision;
1441                                 }
1442                         }
1443 
1444                         while (*p != '\0' && strchr("#0- +", *p)) {
1445                                 switch (*p++) {
1446                                 case '#': flags |= FMT_F_ALT;   break;
1447                                 case '': flags |= FMT_F_ZERO;  break;
1448                                 case '-': flags |= FMT_F_LEFT;  break;
1449                                 case ' ': flags |= FMT_F_SPACE; break;
1450                                 case '+': flags |= FMT_F_SIGN;  break;
1451                                 }
1452                         }
1453 
1454                         if (*p >= '1' && *p <= '9') {
1455                                 while (isdigit(*p))
1456                                         width = width * 10 + (*p++ - '');
1457 
1458                                 flags |= FMT_F_WIDTH;
1459                         }
1460 
1461 parse_precision:
1462                         if (*p == '.') {
1463                                 p++;
1464 
1465                                 if (*p == '-') {
1466                                         p++;
1467 
1468                                         while (isdigit(*p))
1469                                                 p++;
1470                                 }
1471                                 else {
1472                                         while (isdigit(*p))
1473                                                 precision = precision * 10 + (*p++ - '');
1474                                 }
1475 
1476                                 flags |= FMT_F_PREC;
1477                         }
1478 
1479                         switch (*p) {
1480                         case 'd':
1481                         case 'i':
1482                                 conv = FMT_C_INT;
1483                                 flags &= ~FMT_F_PREC;
1484                                 cfmt = PRId64;
1485                                 break;
1486 
1487                         case 'o':
1488                                 conv = FMT_C_UINT;
1489                                 flags &= ~FMT_F_PREC;
1490                                 cfmt = PRIo64;
1491                                 break;
1492 
1493                         case 'u':
1494                                 conv = FMT_C_UINT;
1495                                 flags &= ~FMT_F_PREC;
1496                                 cfmt = PRIu64;
1497                                 break;
1498 
1499                         case 'x':
1500                                 conv = FMT_C_UINT;
1501                                 flags &= ~FMT_F_PREC;
1502                                 cfmt = PRIx64;
1503                                 break;
1504 
1505                         case 'X':
1506                                 conv = FMT_C_UINT;
1507                                 flags &= ~FMT_F_PREC;
1508                                 cfmt = PRIX64;
1509                                 break;
1510 
1511                         case 'e':
1512                                 conv = FMT_C_DBL;
1513                                 cfmt = "e";
1514                                 break;
1515 
1516                         case 'E':
1517                                 conv = FMT_C_DBL;
1518                                 cfmt = "E";
1519                                 break;
1520 
1521                         case 'f':
1522                                 conv = FMT_C_DBL;
1523                                 cfmt = "f";
1524                                 break;
1525 
1526                         case 'F':
1527                                 conv = FMT_C_DBL;
1528                                 cfmt = "F";
1529                                 break;
1530 
1531                         case 'g':
1532                                 conv = FMT_C_DBL;
1533                                 cfmt = "g";
1534                                 break;
1535 
1536                         case 'G':
1537                                 conv = FMT_C_DBL;
1538                                 cfmt = "G";
1539                                 break;
1540 
1541                         case 'c':
1542                                 conv = FMT_C_CHR;
1543                                 flags &= ~FMT_F_PREC;
1544                                 cfmt = "c";
1545                                 break;
1546 
1547                         case 's':
1548                                 conv = FMT_C_STR;
1549                                 flags &= ~FMT_F_ZERO;
1550                                 cfmt = "s";
1551                                 break;
1552 
1553                         case 'J':
1554                                 conv = FMT_C_JSON;
1555 
1556                                 if (flags & FMT_F_PREC) {
1557                                         flags &= ~FMT_F_PREC;
1558                                         precision++;
1559                                 }
1560 
1561                                 cfmt = "s";
1562                                 break;
1563 
1564                         case '%':
1565                                 conv = FMT_C_NONE;
1566                                 flags = 0;
1567                                 cfmt = "%";
1568                                 break;
1569 
1570                         case '\0':
1571                                 p--;
1572                                 /* fall through */
1573 
1574                         default:
1575                                 continue;
1576                         }
1577 
1578                         sfmtlen = 0;
1579                         sfmt[sfmtlen++] = '%';
1580 
1581                         if (flags & FMT_F_ALT)   sfmt[sfmtlen++] = '#';
1582                         if (flags & FMT_F_ZERO)  sfmt[sfmtlen++] = '';
1583                         if (flags & FMT_F_LEFT)  sfmt[sfmtlen++] = '-';
1584                         if (flags & FMT_F_SPACE) sfmt[sfmtlen++] = ' ';
1585                         if (flags & FMT_F_SIGN)  sfmt[sfmtlen++] = '+';
1586 
1587                         if (flags & FMT_F_WIDTH)
1588                                 sfmtlen += snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, "%" PRIu32, width);
1589 
1590                         if (flags & FMT_F_PREC)
1591                                 sfmtlen += snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, ".%" PRIu32, precision);
1592 
1593                         snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, "%s", cfmt);
1594 
1595                         switch (conv) {
1596                         case FMT_C_NONE:
1597                                 ucv_stringbuf_addstr(buf, cfmt, strlen(cfmt));
1598                                 break;
1599 
1600                         case FMT_C_INT:
1601                                 argidx++;
1602                                 arg = uc_fn_arg(argpos);
1603                                 n = ucv_to_integer(arg);
1604 
1605                                 if (errno == ERANGE)
1606                                         n = (int64_t)ucv_to_unsigned(arg);
1607 
1608                                 ucv_stringbuf_printf(buf, sfmt, n);
1609                                 break;
1610 
1611                         case FMT_C_UINT:
1612                                 argidx++;
1613                                 arg = uc_fn_arg(argpos);
1614                                 u = ucv_to_unsigned(arg);
1615 
1616                                 if (errno == ERANGE)
1617                                         u = (uint64_t)ucv_to_integer(arg);
1618 
1619                                 ucv_stringbuf_printf(buf, sfmt, u);
1620                                 break;
1621 
1622                         case FMT_C_DBL:
1623                                 argidx++;
1624                                 d = ucv_to_double(uc_fn_arg(argpos));
1625                                 ucv_stringbuf_printf(buf, sfmt, d);
1626                                 break;
1627 
1628                         case FMT_C_CHR:
1629                                 argidx++;
1630                                 n = ucv_to_integer(uc_fn_arg(argpos));
1631                                 ucv_stringbuf_printf(buf, sfmt, (int)n);
1632                                 break;
1633 
1634                         case FMT_C_STR:
1635                                 argidx++;
1636                                 arg = uc_fn_arg(argpos);
1637 
1638                                 switch (ucv_type(arg)) {
1639                                 case UC_STRING:
1640                                         ucv_stringbuf_printf(buf, sfmt, ucv_string_get(arg));
1641                                         break;
1642 
1643                                 case UC_NULL:
1644                                         ucv_stringbuf_append(buf, "(null)");
1645                                         break;
1646 
1647                                 default:
1648                                         s = ucv_to_string(vm, arg);
1649                                         ucv_stringbuf_printf(buf, sfmt, s ? s : "(null)");
1650                                         free(s);
1651                                 }
1652 
1653                                 break;
1654 
1655                         case FMT_C_JSON:
1656                                 argidx++;
1657                                 s = ucv_to_jsonstring_formatted(vm,
1658                                         uc_fn_arg(argpos),
1659                                         precision > 0 ? (precision > 1 ? ' ' : '\t') : '\0',
1660                                         precision > 0 ? (precision > 1 ? precision - 1 : 1) : 0);
1661 
1662                                 ucv_stringbuf_printf(buf, sfmt, s ? s : "null");
1663                                 free(s);
1664                                 break;
1665                         }
1666 
1667                         last = p + 1;
1668                 }
1669         }
1670 
1671         ucv_stringbuf_addstr(buf, last, p - last);
1672 }
1673 
1674 static uc_value_t *
1675 uc_sprintf(uc_vm_t *vm, size_t nargs)
1676 {
1677         uc_stringbuf_t *buf = ucv_stringbuf_new();
1678 
1679         uc_printf_common(vm, nargs, buf);
1680 
1681         return ucv_stringbuf_finish(buf);
1682 }
1683 
1684 static uc_value_t *
1685 uc_printf(uc_vm_t *vm, size_t nargs)
1686 {
1687         uc_stringbuf_t *buf = xprintbuf_new();
1688         size_t len;
1689 
1690         uc_printf_common(vm, nargs, buf);
1691 
1692         len = fwrite(buf->buf, 1, printbuf_length(buf), vm->output);
1693 
1694         printbuf_free(buf);
1695 
1696         return ucv_int64_new(len);
1697 }
1698 
1699 static bool
1700 uc_require_so(uc_vm_t *vm, const char *path, uc_value_t **res)
1701 {
1702         void (*init)(uc_vm_t *, uc_value_t *);
1703         uc_value_t *scope;
1704         struct stat st;
1705         void *dlh;
1706 
1707         if (stat(path, &st))
1708                 return false;
1709 
1710         dlerror();
1711         dlh = dlopen(path, RTLD_LAZY|RTLD_LOCAL);
1712 
1713         if (!dlh) {
1714                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
1715                                       "Unable to dlopen file '%s': %s", path, dlerror());
1716 
1717                 return true;
1718         }
1719 
1720         *(void **)(&init) = dlsym(dlh, "uc_module_entry");
1721 
1722         if (!init) {
1723                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
1724                                       "Module '%s' provides no 'uc_module_entry' function", path);
1725 
1726                 return true;
1727         }
1728 
1729         scope = ucv_object_new(vm);
1730 
1731         init(vm, scope);
1732 
1733         *res = scope;
1734 
1735         return true;
1736 }
1737 
1738 static uc_value_t *
1739 uc_loadfile(uc_vm_t *vm, size_t nargs);
1740 
1741 static uc_value_t *
1742 uc_callfunc(uc_vm_t *vm, size_t nargs);
1743 
1744 static bool
1745 uc_require_ucode(uc_vm_t *vm, const char *path, uc_value_t *scope, uc_value_t **res, bool raw_mode)
1746 {
1747         uc_parse_config_t config = *vm->config, *prev_config = vm->config;
1748         uc_value_t *closure;
1749         struct stat st;
1750 
1751         if (stat(path, &st))
1752                 return false;
1753 
1754         config.raw_mode = raw_mode;
1755         vm->config = &config;
1756 
1757         uc_vm_stack_push(vm, ucv_string_new(path));
1758 
1759         closure = uc_loadfile(vm, 1);
1760 
1761         ucv_put(uc_vm_stack_pop(vm));
1762 
1763         if (closure) {
1764                 uc_vm_stack_push(vm, closure);
1765                 uc_vm_stack_push(vm, NULL);
1766                 uc_vm_stack_push(vm, scope);
1767 
1768                 *res = uc_callfunc(vm, 3);
1769 
1770                 uc_vm_stack_pop(vm);
1771                 uc_vm_stack_pop(vm);
1772                 uc_vm_stack_pop(vm);
1773         }
1774 
1775         vm->config = prev_config;
1776 
1777         return true;
1778 }
1779 
1780 static bool
1781 uc_require_path(uc_vm_t *vm, const char *path_template, const char *name, uc_value_t **res, bool so_only)
1782 {
1783         uc_stringbuf_t *buf = xprintbuf_new();
1784         const char *p, *q, *last;
1785         uc_value_t *modtable;
1786         bool rv;
1787 
1788         modtable = ucv_property_get(uc_vm_scope_get(vm), "modules");
1789         *res = ucv_get(ucv_object_get(modtable, name, &rv));
1790 
1791         if (rv)
1792                 goto out;
1793 
1794         p = strchr(path_template, '*');
1795 
1796         if (!p)
1797                 goto out;
1798 
1799         ucv_stringbuf_addstr(buf, path_template, p - path_template);
1800 
1801         for (q = last = name;; q++) {
1802                 if (*q == '.' || *q == '\0') {
1803                         ucv_stringbuf_addstr(buf, last, q - last);
1804 
1805                         if (*q)
1806                                 ucv_stringbuf_append(buf, "/");
1807                         else
1808                                 ucv_stringbuf_addstr(buf, p + 1, strlen(p + 1));
1809 
1810                         if (*q == '\0')
1811                                 break;
1812 
1813                         last = q + 1;
1814                 }
1815                 else if (!isalnum(*q) && *q != '_') {
1816                         goto out;
1817                 }
1818         }
1819 
1820         if (!strcmp(p + 1, ".so"))
1821                 rv = uc_require_so(vm, buf->buf, res);
1822         else if (!strcmp(p + 1, ".uc") && !so_only)
1823                 rv = uc_require_ucode(vm, buf->buf, NULL, res, true);
1824 
1825         if (rv)
1826                 ucv_object_add(modtable, name, ucv_get(*res));
1827 
1828 out:
1829         printbuf_free(buf);
1830 
1831         return rv;
1832 }
1833 
1834 uc_value_t *
1835 uc_require_library(uc_vm_t *vm, uc_value_t *nameval, bool so_only)
1836 {
1837         uc_value_t *search, *se, *res;
1838         size_t arridx, arrlen;
1839         const char *name;
1840 
1841         if (ucv_type(nameval) != UC_STRING)
1842                 return NULL;
1843 
1844         name = ucv_string_get(nameval);
1845         search = ucv_property_get(uc_vm_scope_get(vm), "REQUIRE_SEARCH_PATH");
1846 
1847         if (ucv_type(search) != UC_ARRAY) {
1848                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
1849                                       "Global require search path not set");
1850 
1851                 return NULL;
1852         }
1853 
1854         for (arridx = 0, arrlen = ucv_array_length(search); arridx < arrlen; arridx++) {
1855                 se = ucv_array_get(search, arridx);
1856 
1857                 if (ucv_type(se) != UC_STRING)
1858                         continue;
1859 
1860                 if (uc_require_path(vm, ucv_string_get(se), name, &res, so_only))
1861                         return res;
1862         }
1863 
1864         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
1865                               "No module named '%s' could be found", name);
1866 
1867         return NULL;
1868 }
1869 
1870 static uc_value_t *
1871 uc_require(uc_vm_t *vm, size_t nargs)
1872 {
1873         return uc_require_library(vm, uc_fn_arg(0), false);
1874 }
1875 
1876 static uc_value_t *
1877 uc_iptoarr(uc_vm_t *vm, size_t nargs)
1878 {
1879         uc_value_t *ip = uc_fn_arg(0);
1880         uc_value_t *res;
1881         union {
1882                 uint8_t u8[4];
1883                 struct in_addr in;
1884                 struct in6_addr in6;
1885         } a;
1886         int i;
1887 
1888         if (ucv_type(ip) != UC_STRING)
1889                 return NULL;
1890 
1891         if (inet_pton(AF_INET6, ucv_string_get(ip), &a)) {
1892                 res = ucv_array_new(vm);
1893 
1894                 for (i = 0; i < 16; i++)
1895                         ucv_array_push(res, ucv_int64_new(a.in6.s6_addr[i]));
1896 
1897                 return res;
1898         }
1899         else if (inet_pton(AF_INET, ucv_string_get(ip), &a)) {
1900                 res = ucv_array_new(vm);
1901 
1902                 ucv_array_push(res, ucv_int64_new(a.u8[0]));
1903                 ucv_array_push(res, ucv_int64_new(a.u8[1]));
1904                 ucv_array_push(res, ucv_int64_new(a.u8[2]));
1905                 ucv_array_push(res, ucv_int64_new(a.u8[3]));
1906 
1907                 return res;
1908         }
1909 
1910         return NULL;
1911 }
1912 
1913 static int
1914 check_byte(uc_value_t *v)
1915 {
1916         int n;
1917 
1918         if (ucv_type(v) != UC_INTEGER)
1919                 return -1;
1920 
1921         n = ucv_int64_get(v);
1922 
1923         if (n < 0 || n > 255)
1924                 return -1;
1925 
1926         return n;
1927 }
1928 
1929 static uc_value_t *
1930 uc_arrtoip(uc_vm_t *vm, size_t nargs)
1931 {
1932         uc_value_t *arr = uc_fn_arg(0);
1933         union {
1934                 uint8_t u8[4];
1935                 struct in6_addr in6;
1936         } a;
1937         char buf[INET6_ADDRSTRLEN];
1938         int i, n;
1939 
1940         if (ucv_type(arr) != UC_ARRAY)
1941                 return NULL;
1942 
1943         switch (ucv_array_length(arr)) {
1944         case 4:
1945                 for (i = 0; i < 4; i++) {
1946                         n = check_byte(ucv_array_get(arr, i));
1947 
1948                         if (n < 0)
1949                                 return NULL;
1950 
1951                         a.u8[i] = n;
1952                 }
1953 
1954                 inet_ntop(AF_INET, &a, buf, sizeof(buf));
1955 
1956                 return ucv_string_new(buf);
1957 
1958         case 16:
1959                 for (i = 0; i < 16; i++) {
1960                         n = check_byte(ucv_array_get(arr, i));
1961 
1962                         if (n < 0)
1963                                 return NULL;
1964 
1965                         a.in6.s6_addr[i] = n;
1966                 }
1967 
1968                 inet_ntop(AF_INET6, &a, buf, sizeof(buf));
1969 
1970                 return ucv_string_new(buf);
1971 
1972         default:
1973                 return NULL;
1974         }
1975 }
1976 
1977 static uc_value_t *
1978 uc_match(uc_vm_t *vm, size_t nargs)
1979 {
1980         uc_value_t *subject = uc_fn_arg(0);
1981         uc_value_t *pattern = uc_fn_arg(1);
1982         uc_value_t *rv = NULL, *m;
1983         regmatch_t *pmatch = NULL;
1984         int eflags = 0, res;
1985         uc_regexp_t *re;
1986         bool freeable;
1987         char *p;
1988         size_t i;
1989 
1990         if (ucv_type(pattern) != UC_REGEXP || !subject)
1991                 return NULL;
1992 
1993         re = (uc_regexp_t *)pattern;
1994 
1995         pmatch = calloc(1 + re->regexp.re_nsub, sizeof(regmatch_t));
1996 
1997         if (!pmatch)
1998                 return NULL;
1999 
2000         p = uc_cast_string(vm, &subject, &freeable);
2001 
2002         while (true) {
2003                 res = regexec(&re->regexp, p, 1 + re->regexp.re_nsub, pmatch, eflags);
2004 
2005                 if (res == REG_NOMATCH)
2006                         break;
2007 
2008                 m = ucv_array_new(vm);
2009 
2010                 for (i = 0; i < 1 + re->regexp.re_nsub; i++) {
2011                         if (pmatch[i].rm_so != -1)
2012                                 ucv_array_push(m,
2013                                         ucv_string_new_length(p + pmatch[i].rm_so,
2014                                                               pmatch[i].rm_eo - pmatch[i].rm_so));
2015                         else
2016                                 ucv_array_push(m, NULL);
2017                 }
2018 
2019                 if (re->global) {
2020                         if (!rv)
2021                                 rv = ucv_array_new(vm);
2022 
2023                         ucv_array_push(rv, m);
2024 
2025                         if (pmatch[0].rm_so != pmatch[0].rm_eo)
2026                                 p += pmatch[0].rm_eo;
2027                         else if (*p)
2028                                 p++;
2029                         else
2030                                 break;
2031 
2032                         eflags |= REG_NOTBOL;
2033                 }
2034                 else {
2035                         rv = m;
2036                         break;
2037                 }
2038         }
2039 
2040         free(pmatch);
2041 
2042         if (freeable)
2043                 free(p);
2044 
2045         return rv;
2046 }
2047 
2048 static void
2049 uc_replace_cb(uc_vm_t *vm, uc_value_t *func,
2050               const char *subject, regmatch_t *pmatch, size_t plen,
2051               uc_stringbuf_t *resbuf)
2052 {
2053         uc_value_t *rv;
2054         size_t i;
2055 
2056         uc_vm_ctx_push(vm);
2057         uc_vm_stack_push(vm, ucv_get(func));
2058 
2059         for (i = 0; i < plen; i++) {
2060                 if (pmatch[i].rm_so != -1)
2061                         uc_vm_stack_push(vm,
2062                                 ucv_string_new_length(subject + pmatch[i].rm_so,
2063                                                       pmatch[i].rm_eo - pmatch[i].rm_so));
2064                 else
2065                         uc_vm_stack_push(vm, NULL);
2066         }
2067 
2068         if (uc_vm_call(vm, true, i) == EXCEPTION_NONE) {
2069                 rv = uc_vm_stack_pop(vm);
2070 
2071                 ucv_to_stringbuf(vm, resbuf, rv, false);
2072 
2073                 ucv_put(rv);
2074         }
2075 }
2076 
2077 static void
2078 uc_replace_str(uc_vm_t *vm, uc_value_t *str,
2079                const char *subject, regmatch_t *pmatch, size_t plen,
2080                uc_stringbuf_t *resbuf)
2081 {
2082         bool esc = false;
2083         char *p, *r;
2084         uint8_t i;
2085 
2086         for (p = r = ucv_to_string(vm, str); *p; p++) {
2087                 if (esc) {
2088                         switch (*p) {
2089                         case '&':
2090                                 if (pmatch[0].rm_so != -1)
2091                                         ucv_stringbuf_addstr(resbuf,
2092                                                 subject + pmatch[0].rm_so,
2093                                                 pmatch[0].rm_eo - pmatch[0].rm_so);
2094                                 break;
2095 
2096                         case '`':
2097                                 if (pmatch[0].rm_so != -1)
2098                                         ucv_stringbuf_addstr(resbuf, subject, pmatch[0].rm_so);
2099                                 break;
2100 
2101                         case '\'':
2102                                 if (pmatch[0].rm_so != -1)
2103                                         ucv_stringbuf_addstr(resbuf,
2104                                                 subject + pmatch[0].rm_eo,
2105                                                 strlen(subject + pmatch[0].rm_eo));
2106                                 break;
2107 
2108                         case '1':
2109                         case '2':
2110                         case '3':
2111                         case '4':
2112                         case '5':
2113                         case '6':
2114                         case '7':
2115                         case '8':
2116                         case '9':
2117                                 i = *p - '';
2118                                 if (i < plen && pmatch[i].rm_so != -1) {
2119                                         ucv_stringbuf_addstr(resbuf,
2120                                                 subject + pmatch[i].rm_so,
2121                                                 pmatch[i].rm_eo - pmatch[i].rm_so);
2122                                 }
2123                                 else {
2124                                         ucv_stringbuf_append(resbuf, "$");
2125                                         ucv_stringbuf_addstr(resbuf, p, 1);
2126                                 }
2127                                 break;
2128 
2129                         case '$':
2130                                 ucv_stringbuf_append(resbuf, "$");
2131                                 break;
2132 
2133                         default:
2134                                 ucv_stringbuf_append(resbuf, "$");
2135                                 ucv_stringbuf_addstr(resbuf, p, 1);
2136                         }
2137 
2138                         esc = false;
2139                 }
2140                 else if (*p == '$') {
2141                         esc = true;
2142                 }
2143                 else {
2144                         ucv_stringbuf_addstr(resbuf, p, 1);
2145                 }
2146         }
2147 
2148         free(r);
2149 }
2150 
2151 static uc_value_t *
2152 uc_replace(uc_vm_t *vm, size_t nargs)
2153 {
2154         char *sb = NULL, *pt = NULL, *p, *l;
2155         uc_value_t *subject = uc_fn_arg(0);
2156         uc_value_t *pattern = uc_fn_arg(1);
2157         uc_value_t *replace = uc_fn_arg(2);
2158         uc_value_t *limitval = uc_fn_arg(3);
2159         bool sb_freeable, pt_freeable;
2160         regmatch_t *pmatch = NULL;
2161         size_t pl, nmatch, limit;
2162         uc_regexp_t *re = NULL;
2163         uc_stringbuf_t *resbuf;
2164         int eflags = 0, res;
2165 
2166         if (!pattern || !subject || !replace)
2167                 return NULL;
2168 
2169         nmatch = 1;
2170 
2171         if (ucv_type(pattern) == UC_REGEXP) {
2172                 re = (uc_regexp_t *)pattern;
2173                 nmatch += re->regexp.re_nsub;
2174         }
2175 
2176         pmatch = calloc(nmatch, sizeof(regmatch_t));
2177 
2178         if (!pmatch)
2179                 return NULL;
2180 
2181         sb = uc_cast_string(vm, &subject, &sb_freeable);
2182         resbuf = ucv_stringbuf_new();
2183         limit = limitval ? ucv_uint64_get(limitval) : SIZE_MAX;
2184 
2185         if (re) {
2186                 p = sb;
2187 
2188                 while (limit > 0) {
2189                         res = regexec(&re->regexp, p, nmatch, pmatch, eflags);
2190 
2191                         if (res == REG_NOMATCH)
2192                                 break;
2193 
2194                         ucv_stringbuf_addstr(resbuf, p, pmatch[0].rm_so);
2195 
2196                         if (ucv_is_callable(replace))
2197                                 uc_replace_cb(vm, replace, p, pmatch, nmatch, resbuf);
2198                         else
2199                                 uc_replace_str(vm, replace, p, pmatch, nmatch, resbuf);
2200 
2201                         if (pmatch[0].rm_so != pmatch[0].rm_eo)
2202                                 p += pmatch[0].rm_eo;
2203                         else if (*p)
2204                                 ucv_stringbuf_addstr(resbuf, p++, 1);
2205                         else
2206                                 break;
2207 
2208                         if (re->global)
2209                                 eflags |= REG_NOTBOL;
2210                         else
2211                                 break;
2212 
2213                         limit--;
2214                 }
2215 
2216                 ucv_stringbuf_addstr(resbuf, p, strlen(p));
2217         }
2218         else {
2219                 pt = uc_cast_string(vm, &pattern, &pt_freeable);
2220                 pl = strlen(pt);
2221 
2222                 l = p = sb;
2223 
2224                 while (limit > 0) {
2225                         if (pl == 0 || !strncmp(p, pt, pl)) {
2226                                 ucv_stringbuf_addstr(resbuf, l, p - l);
2227 
2228                                 pmatch[0].rm_so = p - l;
2229                                 pmatch[0].rm_eo = pmatch[0].rm_so + pl;
2230 
2231                                 if (ucv_is_callable(replace))
2232                                         uc_replace_cb(vm, replace, l, pmatch, 1, resbuf);
2233                                 else
2234                                         uc_replace_str(vm, replace, l, pmatch, 1, resbuf);
2235 
2236                                 if (pl) {
2237                                         l = p + pl;
2238                                         p += pl - 1;
2239                                 }
2240                                 else {
2241                                         l = p;
2242                                 }
2243 
2244                                 limit--;
2245                         }
2246 
2247                         if (!*p++)
2248                                 break;
2249                 }
2250 
2251                 ucv_stringbuf_addstr(resbuf, l, strlen(l));
2252 
2253                 if (pt_freeable)
2254                         free(pt);
2255         }
2256 
2257         free(pmatch);
2258 
2259         if (sb_freeable)
2260                 free(sb);
2261 
2262         return ucv_stringbuf_finish(resbuf);
2263 }
2264 
2265 static struct json_tokener *
2266 uc_json_from_object(uc_vm_t *vm, uc_value_t *obj, json_object **jso)
2267 {
2268         bool trail = false, eof = false;
2269         enum json_tokener_error err;
2270         struct json_tokener *tok;
2271         uc_value_t *rfn, *rbuf;
2272         uc_stringbuf_t *buf;
2273 
2274         rfn = ucv_property_get(obj, "read");
2275 
2276         if (!ucv_is_callable(rfn)) {
2277                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2278                                       "Input object does not implement read() method");
2279 
2280                 return NULL;
2281         }
2282 
2283         tok = xjs_new_tokener();
2284 
2285         while (true) {
2286                 uc_vm_stack_push(vm, ucv_get(obj));
2287                 uc_vm_stack_push(vm, ucv_get(rfn));
2288                 uc_vm_stack_push(vm, ucv_int64_new(1024));
2289 
2290                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
2291                         json_tokener_free(tok);
2292 
2293                         return NULL;
2294                 }
2295 
2296                 rbuf = uc_vm_stack_pop(vm);
2297 
2298                 /* check EOF */
2299                 eof = (rbuf == NULL || (ucv_type(rbuf) == UC_STRING && ucv_string_length(rbuf) == 0));
2300 
2301                 /* on EOF, stop parsing unless trailing garbage was detected which handled below */
2302                 if (eof && !trail) {
2303                         ucv_put(rbuf);
2304 
2305                         /* Didn't parse a complete object yet, possibly a non-delimitted atomic value
2306                            such as `null`, `true` etc. - nudge parser by sending final zero byte.
2307                            See json-c issue #681 <https://github.com/json-c/json-c/issues/681> */
2308                         if (json_tokener_get_error(tok) == json_tokener_continue)
2309                                 *jso = json_tokener_parse_ex(tok, "\0", 1);
2310 
2311                         break;
2312                 }
2313 
2314                 if (trail || *jso) {
2315                         uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
2316                                               "Trailing garbage after JSON data");
2317 
2318                         json_tokener_free(tok);
2319                         ucv_put(rbuf);
2320 
2321                         return NULL;
2322                 }
2323 
2324                 if (ucv_type(rbuf) != UC_STRING) {
2325                         buf = xprintbuf_new();
2326                         ucv_to_stringbuf_formatted(vm, buf, rbuf, 0, '\0', 0);
2327 
2328                         *jso = json_tokener_parse_ex(tok, buf->buf, printbuf_length(buf));
2329 
2330                         trail = (json_tokener_get_error(tok) == json_tokener_success &&
2331                                  json_tokener_get_parse_end(tok) < (size_t)printbuf_length(buf));
2332 
2333                         printbuf_free(buf);
2334                 }
2335                 else {
2336                         *jso = json_tokener_parse_ex(tok, ucv_string_get(rbuf), ucv_string_length(rbuf));
2337 
2338                         trail = (json_tokener_get_error(tok) == json_tokener_success &&
2339                                  json_tokener_get_parse_end(tok) < ucv_string_length(rbuf));
2340                 }
2341 
2342                 ucv_put(rbuf);
2343 
2344                 err = json_tokener_get_error(tok);
2345 
2346                 if (err != json_tokener_success && err != json_tokener_continue)
2347                         break;
2348         }
2349 
2350         return tok;
2351 }
2352 
2353 static struct json_tokener *
2354 uc_json_from_string(uc_vm_t *vm, uc_value_t *str, json_object **jso)
2355 {
2356         struct json_tokener *tok = xjs_new_tokener();
2357         size_t i;
2358         char *p;
2359 
2360         /* NB: the len + 1 here is intentional to pass the terminating \0 byte
2361          * to the json-c parser. This is required to work-around upstream
2362          * issue #681 <https://github.com/json-c/json-c/issues/681> */
2363         *jso = json_tokener_parse_ex(tok, ucv_string_get(str), ucv_string_length(str) + 1);
2364 
2365         if (json_tokener_get_error(tok) == json_tokener_success) {
2366                 p = ucv_string_get(str);
2367 
2368                 for (i = json_tokener_get_parse_end(tok); i < ucv_string_length(str); i++) {
2369                         if (!isspace(p[i])) {
2370                                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
2371                                                       "Trailing garbage after JSON data");
2372 
2373 
2374                                 json_tokener_free(tok);
2375 
2376                                 return NULL;
2377                         }
2378                 }
2379         }
2380 
2381         return tok;
2382 }
2383 
2384 static uc_value_t *
2385 uc_json(uc_vm_t *vm, size_t nargs)
2386 {
2387         uc_value_t *rv = NULL, *src = uc_fn_arg(0);
2388         struct json_tokener *tok = NULL;
2389         enum json_tokener_error err;
2390         json_object *jso = NULL;
2391 
2392         switch (ucv_type(src)) {
2393         case UC_STRING:
2394                 tok = uc_json_from_string(vm, src, &jso);
2395                 break;
2396 
2397         case UC_RESOURCE:
2398         case UC_OBJECT:
2399         case UC_ARRAY:
2400                 tok = uc_json_from_object(vm, src, &jso);
2401                 break;
2402 
2403         default:
2404                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2405                                       "Passed value is neither a string nor an object");
2406         }
2407 
2408         if (!tok)
2409                 goto out;
2410 
2411         err = json_tokener_get_error(tok);
2412 
2413         if (err == json_tokener_continue) {
2414                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
2415                                       "Unexpected end of string in JSON data");
2416 
2417                 goto out;
2418         }
2419         else if (err != json_tokener_success) {
2420                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
2421                                       "Failed to parse JSON string: %s",
2422                                       json_tokener_error_desc(err));
2423 
2424                 goto out;
2425         }
2426 
2427         rv = ucv_from_json(vm, jso);
2428 
2429 out:
2430         if (tok)
2431                 json_tokener_free(tok);
2432 
2433         json_object_put(jso);
2434 
2435         return rv;
2436 }
2437 
2438 static char *
2439 include_path(const char *curpath, const char *incpath)
2440 {
2441         char *dup, *res;
2442         int len;
2443 
2444         if (*incpath == '/')
2445                 return realpath(incpath, NULL);
2446 
2447         dup = curpath ? strrchr(curpath, '/') : NULL;
2448 
2449         if (dup)
2450                 len = asprintf(&res, "%.*s/%s", (int)(dup - curpath), curpath, incpath);
2451         else
2452                 len = asprintf(&res, "./%s", incpath);
2453 
2454         if (len == -1)
2455                 return NULL;
2456 
2457         dup = realpath(res, NULL);
2458 
2459         free(res);
2460 
2461         return dup;
2462 }
2463 
2464 static uc_value_t *
2465 uc_include_common(uc_vm_t *vm, size_t nargs, bool raw_mode)
2466 {
2467         uc_value_t *path = uc_fn_arg(0);
2468         uc_value_t *scope = uc_fn_arg(1);
2469         uc_value_t *rv = NULL, *sc = NULL;
2470         uc_closure_t *closure = NULL;
2471         size_t i;
2472         char *p;
2473 
2474         if (ucv_type(path) != UC_STRING) {
2475                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2476                                       "Passed filename is not a string");
2477 
2478                 return NULL;
2479         }
2480 
2481         if (scope && ucv_type(scope) != UC_OBJECT) {
2482                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2483                                       "Passed scope value is not an object");
2484 
2485                 return NULL;
2486         }
2487 
2488         /* find calling closure */
2489         for (i = vm->callframes.count; i > 0; i--) {
2490                 closure = vm->callframes.entries[i - 1].closure;
2491 
2492                 if (closure)
2493                         break;
2494         }
2495 
2496         if (!closure)
2497                 return NULL;
2498 
2499         p = include_path(uc_program_function_source(closure->function)->runpath, ucv_string_get(path));
2500 
2501         if (!p) {
2502                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2503                                       "Include file not found");
2504 
2505                 return NULL;
2506         }
2507 
2508         if (ucv_prototype_get(scope)) {
2509                 sc = ucv_get(scope);
2510         }
2511         else if (scope) {
2512                 sc = ucv_object_new(vm);
2513 
2514                 ucv_object_foreach(scope, key, val)
2515                         ucv_object_add(sc, key, ucv_get(val));
2516 
2517                 ucv_prototype_set(sc, ucv_get(uc_vm_scope_get(vm)));
2518         }
2519         else {
2520                 sc = ucv_get(uc_vm_scope_get(vm));
2521         }
2522 
2523         if (uc_require_ucode(vm, p, sc, &rv, raw_mode))
2524                 ucv_put(rv);
2525 
2526         ucv_put(sc);
2527         free(p);
2528 
2529         return NULL;
2530 }
2531 
2532 static uc_value_t *
2533 uc_include(uc_vm_t *vm, size_t nargs)
2534 {
2535         return uc_include_common(vm, nargs, vm->config && vm->config->raw_mode);
2536 }
2537 
2538 static uc_value_t *
2539 uc_render(uc_vm_t *vm, size_t nargs)
2540 {
2541         uc_string_t hdr = { .header = { .type = UC_STRING, .refcount = 1 } };
2542         uc_string_t *ustr = NULL;
2543         FILE *mem, *prev;
2544         size_t len = 0;
2545 
2546         mem = open_memstream((char **)&ustr, &len);
2547 
2548         if (!mem)
2549                 goto out;
2550 
2551         /* reserve space for uc_string_t header... */
2552         if (fwrite(&hdr, 1, sizeof(hdr), mem) != sizeof(hdr))
2553                 goto out;
2554 
2555         /* divert VM output to memory fd */
2556         prev = vm->output;
2557         vm->output = mem;
2558 
2559         /* execute function */
2560         if (ucv_is_callable(uc_fn_arg(0)))
2561                 (void) uc_vm_call(vm, false, nargs - 1);
2562 
2563         /* execute include */
2564         else
2565                 (void) uc_include_common(vm, nargs, false);
2566 
2567         /* restore previous VM output */
2568         vm->output = prev;
2569         fclose(mem);
2570 
2571         /* update uc_string_t length */
2572         ustr->length = len - sizeof(*ustr);
2573 
2574         return &ustr->header;
2575 
2576 out:
2577         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2578                               "Unable to initialize output memory: %s",
2579                               strerror(errno));
2580 
2581         if (mem)
2582                 fclose(mem);
2583 
2584         free(ustr);
2585 
2586         return NULL;
2587 }
2588 
2589 static uc_value_t *
2590 uc_warn(uc_vm_t *vm, size_t nargs)
2591 {
2592         return uc_print_common(vm, nargs, stderr);
2593 }
2594 
2595 #ifdef __APPLE__
2596 /*
2597  * sigtimedwait() implementation based on
2598  * https://comp.unix.programmer.narkive.com/rEDH0sPT/sigtimedwait-implementation
2599  * and
2600  * https://github.com/wahern/lunix/blob/master/src/unix.c
2601  */
2602 static void
2603 sigtimedwait_consume_signal(int signo)
2604 {
2605 }
2606 
2607 static int
2608 sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout)
2609 {
2610         struct timespec elapsed = { 0, 0 }, sleep, rem;
2611         sigset_t pending, unblock, omask;
2612         struct sigaction sa, osa;
2613         int signo;
2614         bool lt;
2615 
2616         while (true) {
2617                 sigemptyset(&pending);
2618                 sigpending(&pending);
2619 
2620                 for (signo = 1; signo < NSIG; signo++) {
2621                         if (!sigismember(set, signo) || !sigismember(&pending, signo))
2622                                 continue;
2623 
2624                         sa.sa_handler = sigtimedwait_consume_signal;
2625                         sa.sa_flags = 0;
2626                         sigfillset(&sa.sa_mask);
2627 
2628                         sigaction(signo, &sa, &osa);
2629 
2630                         sigemptyset(&unblock);
2631                         sigaddset(&unblock, signo);
2632                         sigprocmask(SIG_UNBLOCK, &unblock, &omask);
2633                         sigprocmask(SIG_SETMASK, &omask, NULL);
2634 
2635                         sigaction(signo, &osa, NULL);
2636 
2637                         if (info) {
2638                                 memset(info, 0, sizeof(*info));
2639                                 info->si_signo = signo;
2640                         }
2641 
2642                         return signo;
2643                 }
2644 
2645                 sleep.tv_sec = 0;
2646                 sleep.tv_nsec = 200000000L; /* 2/10th second */
2647                 rem = sleep;
2648 
2649                 if (nanosleep(&sleep, &rem) == 0) {
2650                         elapsed.tv_sec += sleep.tv_sec;
2651                         elapsed.tv_nsec += sleep.tv_nsec;
2652 
2653                         if (elapsed.tv_nsec > 1000000000) {
2654                                 elapsed.tv_sec++;
2655                                 elapsed.tv_nsec -= 1000000000;
2656                         }
2657                 }
2658                 else if (errno == EINTR) {
2659                         sleep.tv_sec -= rem.tv_sec;
2660                         sleep.tv_nsec -= rem.tv_nsec;
2661 
2662                         if (sleep.tv_nsec < 0) {
2663                                 sleep.tv_sec--;
2664                                 sleep.tv_nsec += 1000000000;
2665                         }
2666 
2667                         elapsed.tv_sec += sleep.tv_sec;
2668                         elapsed.tv_nsec += sleep.tv_nsec;
2669 
2670                         if (elapsed.tv_nsec > 1000000000) {
2671                                 elapsed.tv_sec++;
2672                                 elapsed.tv_nsec -= 1000000000;
2673                         }
2674                 }
2675                 else {
2676                         return errno;
2677                 }
2678 
2679                 lt = timeout
2680                         ? ((elapsed.tv_sec == timeout->tv_sec)
2681                                 ? (elapsed.tv_nsec < timeout->tv_nsec)
2682                                 : (elapsed.tv_sec < timeout->tv_sec))
2683                         : true;
2684 
2685                 if (!lt)
2686                         break;
2687         }
2688 
2689         errno = EAGAIN;
2690 
2691         return -1;
2692 }
2693 
2694 #endif
2695 
2696 static uc_value_t *
2697 uc_system(uc_vm_t *vm, size_t nargs)
2698 {
2699         uc_value_t *cmdline = uc_fn_arg(0);
2700         uc_value_t *timeout = uc_fn_arg(1);
2701         const char **arglist, *fn;
2702         sigset_t sigmask, sigomask;
2703         struct timespec ts;
2704         size_t i, len;
2705         int64_t tms;
2706         pid_t cld;
2707         int rc;
2708 
2709         if (timeout && (ucv_type(timeout) != UC_INTEGER || ucv_int64_get(timeout) < 0)) {
2710                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2711                                       "Invalid timeout specified");
2712 
2713                 return NULL;
2714         }
2715 
2716         switch (ucv_type(cmdline)) {
2717         case UC_STRING:
2718                 arglist = xalloc(sizeof(*arglist) * 4);
2719                 arglist[0] = xstrdup("/bin/sh");
2720                 arglist[1] = xstrdup("-c");
2721                 arglist[2] = ucv_to_string(vm, cmdline);
2722                 arglist[3] = NULL;
2723                 break;
2724 
2725         case UC_ARRAY:
2726                 len = ucv_array_length(cmdline);
2727 
2728                 if (len == 0) {
2729                         uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2730                                               "Passed command array is empty");
2731 
2732                         return NULL;
2733                 }
2734 
2735                 arglist = xalloc(sizeof(*arglist) * (len + 1));
2736 
2737                 for (i = 0; i < len; i++)
2738                         arglist[i] = ucv_to_string(vm, ucv_array_get(cmdline, i));
2739 
2740                 arglist[i] = NULL;
2741 
2742                 break;
2743 
2744         default:
2745                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2746                                       "Passed command is neither string nor array");
2747 
2748                 return NULL;
2749         }
2750 
2751         tms = timeout ? ucv_int64_get(timeout) : 0;
2752 
2753         if (tms > 0) {
2754                 sigemptyset(&sigmask);
2755                 sigaddset(&sigmask, SIGCHLD);
2756 
2757                 if (sigprocmask(SIG_BLOCK, &sigmask, &sigomask) < 0) {
2758                         fn = "sigprocmask";
2759                         goto fail;
2760                 }
2761         }
2762 
2763         cld = fork();
2764 
2765         switch (cld) {
2766         case -1:
2767                 fn = "fork";
2768                 goto fail;
2769 
2770         case 0:
2771                 execvp(arglist[0], (char * const *)arglist);
2772                 exit(-1);
2773 
2774                 break;
2775 
2776         default:
2777                 if (tms > 0) {
2778                         ts.tv_sec = tms / 1000;
2779                         ts.tv_nsec = (tms % 1000) * 1000000;
2780 
2781                         while (1) {
2782                                 if (sigtimedwait(&sigmask, NULL, &ts) < 0) {
2783                                         if (errno == EINTR)
2784                                                 continue;
2785 
2786                                         if (errno != EAGAIN) {
2787                                                 fn = "sigtimedwait";
2788                                                 goto fail;
2789                                         }
2790 
2791                                         kill(cld, SIGKILL);
2792                                 }
2793 
2794                                 break;
2795                         }
2796                 }
2797 
2798                 while (waitpid(cld, &rc, 0) < 0) {
2799                         if (errno == EINTR)
2800                                 continue;
2801 
2802                         fn = "waitpid";
2803                         goto fail;
2804                 }
2805 
2806                 if (tms > 0)
2807                         sigprocmask(SIG_SETMASK, &sigomask, NULL);
2808 
2809                 for (i = 0; arglist[i]; i++)
2810                         free((char *)arglist[i]);
2811 
2812                 free(arglist);
2813 
2814                 if (WIFEXITED(rc))
2815                         return ucv_int64_new(WEXITSTATUS(rc));
2816                 else if (WIFSIGNALED(rc))
2817                         return ucv_int64_new(-WTERMSIG(rc));
2818                 else if (WIFSTOPPED(rc))
2819                         return ucv_int64_new(-WSTOPSIG(rc));
2820 
2821                 return NULL;
2822         }
2823 
2824 fail:
2825         if (tms > 0)
2826                 sigprocmask(SIG_SETMASK, &sigomask, NULL);
2827 
2828         for (i = 0; arglist[i]; i++)
2829                 free((char *)arglist[i]);
2830 
2831         free(arglist);
2832 
2833         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2834                               "%s(): %s", fn, strerror(errno));
2835 
2836         return NULL;
2837 }
2838 
2839 static uc_value_t *
2840 uc_trace(uc_vm_t *vm, size_t nargs)
2841 {
2842         uc_value_t *level = uc_fn_arg(0);
2843         uint8_t prev_level;
2844 
2845         if (ucv_type(level) != UC_INTEGER) {
2846                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid level specified");
2847 
2848                 return NULL;
2849         }
2850 
2851         prev_level = vm->trace;
2852         vm->trace = ucv_int64_get(level);
2853 
2854         return ucv_int64_new(prev_level);
2855 }
2856 
2857 static uc_value_t *
2858 uc_proto(uc_vm_t *vm, size_t nargs)
2859 {
2860         uc_value_t *val = uc_fn_arg(0);
2861         uc_value_t *proto = NULL;
2862 
2863         if (nargs < 2)
2864                 return ucv_get(ucv_prototype_get(val));
2865 
2866         proto = uc_fn_arg(1);
2867 
2868         if (!ucv_prototype_set(val, proto))
2869                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed value is neither a prototype, resource or object");
2870 
2871         ucv_get(proto);
2872 
2873         return ucv_get(val);
2874 }
2875 
2876 static uc_value_t *
2877 uc_sleep(uc_vm_t *vm, size_t nargs)
2878 {
2879         uc_value_t *duration = uc_fn_arg(0);
2880         struct timeval tv;
2881         int64_t ms;
2882 
2883         ms = ucv_to_integer(duration);
2884 
2885         if (errno != 0 || ms <= 0)
2886                 return ucv_boolean_new(false);
2887 
2888         tv.tv_sec = ms / 1000;
2889         tv.tv_usec = (ms % 1000) * 1000;
2890 
2891         select(0, NULL, NULL, NULL, &tv);
2892 
2893         return ucv_boolean_new(true);
2894 }
2895 
2896 static uc_value_t *
2897 uc_assert(uc_vm_t *vm, size_t nargs)
2898 {
2899         uc_value_t *cond = uc_fn_arg(0);
2900         uc_value_t *msg = uc_fn_arg(1);
2901         bool freeable = false;
2902         char *s;
2903 
2904         if (!ucv_is_truish(cond)) {
2905                 s = msg ? uc_cast_string(vm, &msg, &freeable) : "Assertion failed";
2906 
2907                 uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s);
2908 
2909                 if (freeable)
2910                         free(s);
2911 
2912                 return NULL;
2913         }
2914 
2915         return ucv_get(cond);
2916 }
2917 
2918 static uc_value_t *
2919 uc_regexp(uc_vm_t *vm, size_t nargs)
2920 {
2921         bool icase = false, newline = false, global = false, freeable;
2922         uc_value_t *source = uc_fn_arg(0);
2923         uc_value_t *flags = uc_fn_arg(1);
2924         uc_value_t *regex = NULL;
2925         char *p, *err = NULL;
2926 
2927         if (flags) {
2928                 if (ucv_type(flags) != UC_STRING) {
2929                         uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Given flags argument is not a string");
2930 
2931                         return NULL;
2932                 }
2933 
2934                 for (p = ucv_string_get(flags); *p; p++) {
2935                         switch (*p) {
2936                         case 'i':
2937                                 icase = true;
2938                                 break;
2939 
2940                         case 's':
2941                                 newline = true;
2942                                 break;
2943 
2944                         case 'g':
2945                                 global = true;
2946                                 break;
2947 
2948                         default:
2949                                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Unrecognized flag character '%c'", *p);
2950 
2951                                 return NULL;
2952                         }
2953                 }
2954         }
2955 
2956         p = uc_cast_string(vm, &source, &freeable);
2957         regex = ucv_regexp_new(p, icase, newline, global, &err);
2958 
2959         if (freeable)
2960                 free(p);
2961 
2962         if (err) {
2963                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "%s", err);
2964                 ucv_put(regex);
2965                 free(err);
2966 
2967                 return NULL;
2968         }
2969 
2970         return regex;
2971 }
2972 
2973 static uc_value_t *
2974 uc_wildcard(uc_vm_t *vm, size_t nargs)
2975 {
2976         uc_value_t *subject = uc_fn_arg(0);
2977         uc_value_t *pattern = uc_fn_arg(1);
2978         uc_value_t *icase = uc_fn_arg(2);
2979         int flags = 0, rv;
2980         bool freeable;
2981         char *s;
2982 
2983         if (!subject || ucv_type(pattern) != UC_STRING)
2984                 return NULL;
2985 
2986         if (ucv_is_truish(icase))
2987                 flags |= FNM_CASEFOLD;
2988 
2989         s = uc_cast_string(vm, &subject, &freeable);
2990         rv = fnmatch(ucv_string_get(pattern), s, flags);
2991 
2992         if (freeable)
2993                 free(s);
2994 
2995         return ucv_boolean_new(rv == 0);
2996 }
2997 
2998 static uc_value_t *
2999 uc_sourcepath(uc_vm_t *vm, size_t nargs)
3000 {
3001         uc_value_t *calldepth = uc_fn_arg(0);
3002         uc_value_t *dironly = uc_fn_arg(1);
3003         uc_value_t *rv = NULL;
3004         uc_callframe_t *frame;
3005         char *path = NULL;
3006         int64_t depth;
3007         size_t i;
3008 
3009         depth = ucv_to_integer(calldepth);
3010 
3011         if (errno)
3012                 depth = 0;
3013 
3014         for (i = vm->callframes.count; i > 0; i--) {
3015                 frame = &vm->callframes.entries[i - 1];
3016 
3017                 if (!frame->closure)
3018                         continue;
3019 
3020                 if (depth > 0) {
3021                         depth--;
3022                         continue;
3023                 }
3024 
3025                 path = realpath(uc_program_function_source(frame->closure->function)->runpath, NULL);
3026                 break;
3027         }
3028 
3029         if (path) {
3030                 if (ucv_is_truish(dironly))
3031                         rv = ucv_string_new(dirname(path));
3032                 else
3033                         rv = ucv_string_new(path);
3034 
3035                 free(path);
3036         }
3037 
3038         return rv;
3039 }
3040 
3041 static uc_value_t *
3042 uc_min_max(uc_vm_t *vm, size_t nargs, int cmp)
3043 {
3044         uc_value_t *rv = NULL, *val;
3045         bool set = false;
3046         size_t i;
3047 
3048         for (i = 0; i < nargs; i++) {
3049                 val = uc_fn_arg(i);
3050 
3051                 if (!set || ucv_compare(cmp, val, rv, NULL)) {
3052                         set = true;
3053                         rv = val;
3054                 }
3055         }
3056 
3057         return ucv_get(rv);
3058 }
3059 
3060 static uc_value_t *
3061 uc_min(uc_vm_t *vm, size_t nargs)
3062 {
3063         return uc_min_max(vm, nargs, I_LT);
3064 }
3065 
3066 static uc_value_t *
3067 uc_max(uc_vm_t *vm, size_t nargs)
3068 {
3069         return uc_min_max(vm, nargs, I_GT);
3070 }
3071 
3072 
3073 /* -------------------------------------------------------------------------
3074  * The following base64 encoding and decoding routines are taken from
3075  * https://git.openwrt.org/?p=project/libubox.git;a=blob;f=base64.c
3076  * and modified for use in ucode.
3077  *
3078  * Original copyright and license statements below.
3079  */
3080 
3081 /*
3082  * base64 - libubox base64 functions
3083  *
3084  * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
3085  *
3086  * Permission to use, copy, modify, and/or distribute this software for any
3087  * purpose with or without fee is hereby granted, provided that the above
3088  * copyright notice and this permission notice appear in all copies.
3089  *
3090  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
3091  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
3092  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
3093  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
3094  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
3095  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
3096  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
3097  */
3098 
3099 /*      $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */
3100 
3101 /*
3102  * Copyright (c) 1996 by Internet Software Consortium.
3103  *
3104  * Permission to use, copy, modify, and distribute this software for any
3105  * purpose with or without fee is hereby granted, provided that the above
3106  * copyright notice and this permission notice appear in all copies.
3107  *
3108  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
3109  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
3110  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
3111  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
3112  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
3113  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
3114  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
3115  * SOFTWARE.
3116  */
3117 
3118 /*
3119  * Portions Copyright (c) 1995 by International Business Machines, Inc.
3120  *
3121  * International Business Machines, Inc. (hereinafter called IBM) grants
3122  * permission under its copyrights to use, copy, modify, and distribute this
3123  * Software with or without fee, provided that the above copyright notice and
3124  * all paragraphs of this notice appear in all copies, and that the name of IBM
3125  * not be used in connection with the marketing of any product incorporating
3126  * the Software or modifications thereof, without specific, written prior
3127  * permission.
3128  *
3129  * To the extent it has a right to do so, IBM grants an immunity from suit
3130  * under its patents, if any, for the use, sale or manufacture of products to
3131  * the extent that such products are used for performing Domain Name System
3132  * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
3133  * granted for any product per se or for any other function of any product.
3134  *
3135  * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
3136  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
3137  * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
3138  * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
3139  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
3140  * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
3141  */
3142 
3143 /* skips all whitespace anywhere.
3144    converts characters, four at a time, starting at (or after)
3145    src from base - 64 numbers into three 8 bit bytes in the target area.
3146    it returns the number of data bytes stored at the target, or -1 on error.
3147  */
3148 
3149 static uc_value_t *
3150 uc_b64dec(uc_vm_t *vm, size_t nargs)
3151 {
3152         enum { BYTE1, BYTE2, BYTE3, BYTE4 } state;
3153         uc_value_t *str = uc_fn_arg(0);
3154         uc_stringbuf_t *buf;
3155         const char *src;
3156         unsigned int ch;
3157         uint8_t val;
3158         size_t off;
3159 
3160         if (ucv_type(str) != UC_STRING)
3161                 return NULL;
3162 
3163         buf = ucv_stringbuf_new();
3164         src = ucv_string_get(str);
3165         off = printbuf_length(buf);
3166 
3167         state = BYTE1;
3168 
3169         /* memset the last expected output char to pre-grow the output buffer */
3170         printbuf_memset(buf, off + (ucv_string_length(str) / 4) * 3, 0, 1);
3171 
3172         while ((ch = (unsigned char)*src++) != '\0') {
3173                 if (isspace(ch))        /* Skip whitespace anywhere. */
3174                         continue;
3175 
3176                 if (ch == '=')
3177                         break;
3178 
3179                 if (ch >= 'A' && ch <= 'Z')
3180                         val = ch - 'A';
3181                 else if (ch >= 'a' && ch <= 'z')
3182                         val = ch - 'a' + 26;
3183                 else if (ch >= '' && ch <= '9')
3184                         val = ch - '' + 52;
3185                 else if (ch == '+')
3186                         val = 62;
3187                 else if (ch == '/')
3188                         val = 63;
3189                 else
3190                         goto err;
3191 
3192                 switch (state) {
3193                 case BYTE1:
3194                         buf->buf[off] = val << 2;
3195                         state = BYTE2;
3196                         break;
3197 
3198                 case BYTE2:
3199                         buf->buf[off++] |= val >> 4;
3200                         buf->buf[off] = (val & 0x0f) << 4;
3201                         state = BYTE3;
3202                         break;
3203 
3204                 case BYTE3:
3205                         buf->buf[off++] |= val >> 2;
3206                         buf->buf[off] = (val & 0x03) << 6;
3207                         state = BYTE4;
3208                         break;
3209 
3210                 case BYTE4:
3211                         buf->buf[off++] |= val;
3212                         state = BYTE1;
3213                         break;
3214                 }
3215         }
3216 
3217         /*
3218          * We are done decoding Base-64 chars.  Let's see if we ended
3219          * on a byte boundary, and/or with erroneous trailing characters.
3220          */
3221 
3222         if (ch == '=') {                        /* We got a pad char. */
3223                 ch = (unsigned char)*src++;     /* Skip it, get next. */
3224                 switch (state) {
3225                 case BYTE1:             /* Invalid = in first position */
3226                 case BYTE2:             /* Invalid = in second position */
3227                         goto err;
3228 
3229                 case BYTE3:             /* Valid, means one byte of info */
3230                         /* Skip any number of spaces. */
3231                         for (; ch != '\0'; ch = (unsigned char)*src++)
3232                                 if (!isspace(ch))
3233                                         break;
3234                         /* Make sure there is another trailing = sign. */
3235                         if (ch != '=')
3236                                 goto err;
3237                         ch = (unsigned char)*src++;             /* Skip the = */
3238                         /* Fall through to "single trailing =" case. */
3239                         /* FALLTHROUGH */
3240 
3241                 case BYTE4:             /* Valid, means two bytes of info */
3242                         /*
3243                          * We know this char is an =.  Is there anything but
3244                          * whitespace after it?
3245                          */
3246                         for (; ch != '\0'; ch = (unsigned char)*src++)
3247                                 if (!isspace(ch))
3248                                         goto err;
3249 
3250                         /*
3251                          * Now make sure for cases BYTE3 and BYTE4 that the "extra"
3252                          * bits that slopped past the last full byte were
3253                          * zeros.  If we don't check them, they become a
3254                          * subliminal channel.
3255                          */
3256                         if (buf->buf[off] != 0)
3257                                 goto err;
3258                 }
3259         } else {
3260                 /*
3261                  * We ended by seeing the end of the string.  Make sure we
3262                  * have no partial bytes lying around.
3263                  */
3264                 if (state != BYTE1)
3265                         goto err;
3266         }
3267 
3268         /* Truncate buffer length to actual output length */
3269         buf->bpos = off;
3270 
3271         return ucv_stringbuf_finish(buf);
3272 
3273 err:
3274         printbuf_free(buf);
3275 
3276         return NULL;
3277 }
3278 
3279 static const char Base64[] =
3280         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3281 
3282 static uc_value_t *
3283 uc_b64enc(uc_vm_t *vm, size_t nargs)
3284 {
3285         uc_value_t *str = uc_fn_arg(0);
3286         unsigned char input[3] = {0};
3287         uc_stringbuf_t *buf;
3288         const char *src;
3289         char output[4];
3290         size_t len, i;
3291 
3292         if (ucv_type(str) != UC_STRING)
3293                 return NULL;
3294 
3295         buf = ucv_stringbuf_new();
3296         src = ucv_string_get(str);
3297         len = ucv_string_length(str);
3298 
3299         while (2 < len) {
3300                 input[0] = (unsigned char)*src++;
3301                 input[1] = (unsigned char)*src++;
3302                 input[2] = (unsigned char)*src++;
3303                 len -= 3;
3304 
3305                 output[0] = Base64[input[0] >> 2];
3306                 output[1] = Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)];
3307                 output[2] = Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)];
3308                 output[3] = Base64[input[2] & 0x3f];
3309 
3310                 ucv_stringbuf_addstr(buf, output, sizeof(output));
3311         }
3312 
3313         /* Now we worry about padding. */
3314         if (0 != len) {
3315                 /* Get what's left. */
3316                 input[0] = input[1] = input[2] = '\0';
3317                 for (i = 0; i < len; i++)
3318                         input[i] = *src++;
3319 
3320                 output[0] = Base64[input[0] >> 2];
3321                 output[1] = Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)];
3322                 output[2] = (len == 1) ? '=' : Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)];
3323                 output[3] = '=';
3324 
3325                 ucv_stringbuf_addstr(buf, output, sizeof(output));
3326         }
3327 
3328         return ucv_stringbuf_finish(buf);
3329 }
3330 
3331 /* End of base64 code.
3332  * -------------------------------------------------------------------------
3333  */
3334 
3335 static unsigned long
3336 uc_uniq_ucv_hash(const void *k)
3337 {
3338         union { double d; int64_t i; uint64_t u; } conv;
3339         uc_value_t *uv = (uc_value_t *)k;
3340         unsigned int h;
3341         uint8_t *u8;
3342         size_t len;
3343 
3344         h = ucv_type(uv);
3345 
3346         switch (h) {
3347         case UC_STRING:
3348                 u8 = (uint8_t *)ucv_string_get(uv);
3349                 len = ucv_string_length(uv);
3350                 break;
3351 
3352         case UC_INTEGER:
3353                 conv.i = ucv_int64_get(uv);
3354 
3355                 if (errno == ERANGE) {
3356                         h *= 2;
3357                         conv.u = ucv_uint64_get(uv);
3358                 }
3359 
3360                 u8 = (uint8_t *)&conv.u;
3361                 len = sizeof(conv.u);
3362                 break;
3363 
3364         case UC_DOUBLE:
3365                 conv.d = ucv_double_get(uv);
3366 
3367                 u8 = (uint8_t *)&conv.u;
3368                 len = sizeof(conv.u);
3369                 break;
3370 
3371         default:
3372                 u8 = (uint8_t *)&uv;
3373                 len = sizeof(uv);
3374                 break;
3375         }
3376 
3377         while (len > 0) {
3378                 h = h * 129 + (*u8++) + LH_PRIME;
3379                 len--;
3380         }
3381 
3382         return h;
3383 }
3384 
3385 static int
3386 uc_uniq_ucv_equal(const void *k1, const void *k2)
3387 {
3388         uc_value_t *uv1 = (uc_value_t *)k1;
3389         uc_value_t *uv2 = (uc_value_t *)k2;
3390 
3391         if (!ucv_is_scalar(uv1) && !ucv_is_scalar(uv2))
3392                 return (uv1 == uv2);
3393 
3394         /* for the sake of array item uniqueness, treat two NaNs as equal */
3395         if (ucv_type(uv1) == UC_DOUBLE && ucv_type(uv2) == UC_DOUBLE &&
3396             isnan(ucv_double_get(uv1)) && isnan(ucv_double_get(uv2)))
3397             return true;
3398 
3399         return ucv_is_equal(uv1, uv2);
3400 }
3401 
3402 static uc_value_t *
3403 uc_uniq(uc_vm_t *vm, size_t nargs)
3404 {
3405         uc_value_t *list = uc_fn_arg(0);
3406         uc_value_t *uniq = NULL;
3407         struct lh_table *seen;
3408         unsigned long hash;
3409         uc_value_t *item;
3410         size_t i, len;
3411 
3412         if (ucv_type(list) != UC_ARRAY)
3413                 return NULL;
3414 
3415         seen = lh_table_new(16, NULL, uc_uniq_ucv_hash, uc_uniq_ucv_equal);
3416         uniq = ucv_array_new(vm);
3417 
3418         assert(seen && uniq);
3419 
3420         for (i = 0, len = ucv_array_length(list); i < len; i++) {
3421                 item = ucv_array_get(list, i);
3422                 hash = lh_get_hash(seen, item);
3423 
3424                 if (!lh_table_lookup_entry_w_hash(seen, item, hash)) {
3425                         lh_table_insert_w_hash(seen, item, NULL, hash, 0);
3426                         ucv_array_push(uniq, ucv_get(item));
3427                 }
3428         }
3429 
3430         lh_table_free(seen);
3431 
3432         return uniq;
3433 }
3434 
3435 static uc_value_t *
3436 uc_gettime_common(uc_vm_t *vm, size_t nargs, bool local)
3437 {
3438         uc_value_t *ts = uc_fn_arg(0), *res;
3439         time_t t = ts ? (time_t)ucv_to_integer(ts) : time(NULL);
3440         struct tm *tm = (local ? localtime : gmtime)(&t);
3441 
3442         if (!tm)
3443                 return NULL;
3444 
3445         res = ucv_object_new(vm);
3446 
3447         ucv_object_add(res, "sec", ucv_int64_new(tm->tm_sec));
3448         ucv_object_add(res, "min", ucv_int64_new(tm->tm_min));
3449         ucv_object_add(res, "hour", ucv_int64_new(tm->tm_hour));
3450         ucv_object_add(res, "mday", ucv_int64_new(tm->tm_mday));
3451         ucv_object_add(res, "mon", ucv_int64_new(tm->tm_mon + 1));
3452         ucv_object_add(res, "year", ucv_int64_new(tm->tm_year + 1900));
3453         ucv_object_add(res, "wday", ucv_int64_new(tm->tm_wday ? tm->tm_wday : 7));
3454         ucv_object_add(res, "yday", ucv_int64_new(tm->tm_yday + 1));
3455         ucv_object_add(res, "isdst", ucv_int64_new(tm->tm_isdst));
3456 
3457         return res;
3458 }
3459 
3460 static uc_value_t *
3461 uc_localtime(uc_vm_t *vm, size_t nargs)
3462 {
3463         return uc_gettime_common(vm, nargs, true);
3464 }
3465 
3466 static uc_value_t *
3467 uc_gmtime(uc_vm_t *vm, size_t nargs)
3468 {
3469         return uc_gettime_common(vm, nargs, false);
3470 }
3471 
3472 static uc_value_t *
3473 uc_mktime_common(uc_vm_t *vm, size_t nargs, bool local)
3474 {
3475 #define FIELD(name, required) \
3476         { #name, required, offsetof(struct tm, tm_##name) }
3477 
3478         const struct {
3479                 const char *name;
3480                 bool required;
3481                 size_t off;
3482         } fields[] = {
3483                 FIELD(sec, false),
3484                 FIELD(min, false),
3485                 FIELD(hour, false),
3486                 FIELD(mday, true),
3487                 FIELD(mon, true),
3488                 FIELD(year, true),
3489                 FIELD(isdst, false)
3490         };
3491 
3492         uc_value_t *to = uc_fn_arg(0), *v;
3493         struct tm tm = { 0 };
3494         bool exists;
3495         time_t t;
3496         size_t i;
3497 
3498         if (ucv_type(to) != UC_OBJECT)
3499                 return NULL;
3500 
3501         for (i = 0; i < ARRAY_SIZE(fields); i++) {
3502                 v = ucv_object_get(to, fields[i].name, &exists);
3503 
3504                 if (!exists && fields[i].required)
3505                         return NULL;
3506 
3507                 *(int *)((char *)&tm + fields[i].off) = (int)ucv_to_integer(v);
3508         }
3509 
3510         if (tm.tm_mon > 0)
3511                 tm.tm_mon--;
3512 
3513         if (tm.tm_year >= 1900)
3514                 tm.tm_year -= 1900;
3515 
3516         t = (local ? mktime : timegm)(&tm);
3517 
3518         return (t != (time_t)-1) ? ucv_int64_new((int64_t)t) : NULL;
3519 }
3520 
3521 static uc_value_t *
3522 uc_timelocal(uc_vm_t *vm, size_t nargs)
3523 {
3524         return uc_mktime_common(vm, nargs, true);
3525 }
3526 
3527 static uc_value_t *
3528 uc_timegm(uc_vm_t *vm, size_t nargs)
3529 {
3530         return uc_mktime_common(vm, nargs, false);
3531 }
3532 
3533 static uc_value_t *
3534 uc_clock(uc_vm_t *vm, size_t nargs)
3535 {
3536         clockid_t id = ucv_is_truish(uc_fn_arg(0)) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
3537         struct timespec ts;
3538         uc_value_t *res;
3539 
3540         if (clock_gettime(id, &ts) == -1)
3541                 return NULL;
3542 
3543         res = ucv_array_new(vm);
3544 
3545         ucv_array_set(res, 0, ucv_int64_new((int64_t)ts.tv_sec));
3546         ucv_array_set(res, 1, ucv_int64_new((int64_t)ts.tv_nsec));
3547 
3548         return res;
3549 }
3550 
3551 static uc_value_t *
3552 uc_hexenc(uc_vm_t *vm, size_t nargs)
3553 {
3554         const char *hex = "0123456789abcdef";
3555         uc_value_t *input = uc_fn_arg(0);
3556         uc_stringbuf_t *buf;
3557         size_t off, len;
3558         uint8_t byte;
3559 
3560         if (!input)
3561                 return NULL;
3562 
3563         buf = ucv_stringbuf_new();
3564         off = printbuf_length(buf);
3565 
3566         ucv_to_stringbuf(vm, buf, input, false);
3567 
3568         len = printbuf_length(buf) - off;
3569 
3570         /* memset the last expected output char to grow the output buffer */
3571         printbuf_memset(buf, off + len * 2, 0, 1);
3572 
3573         /* translate string into hex back to front to reuse the same buffer */
3574         while (len > 0) {
3575                 byte = buf->buf[--len + off];
3576                 buf->buf[off + len * 2 + 0] = hex[byte / 16];
3577                 buf->buf[off + len * 2 + 1] = hex[byte % 16];
3578         }
3579 
3580         /* do not include sentinel `\0` in string length */
3581         buf->bpos--;
3582 
3583         return ucv_stringbuf_finish(buf);
3584 }
3585 
3586 static inline uint8_t
3587 hexval(unsigned char c, bool lo)
3588 {
3589         return ((c > '9') ? (c - 'a') + 10 : c - '') << (lo ? 0 : 4);
3590 }
3591 
3592 static uc_value_t *
3593 uc_hexdec(uc_vm_t *vm, size_t nargs)
3594 {
3595         uc_value_t *input = uc_fn_arg(0);
3596         uc_value_t *skip = uc_fn_arg(1);
3597         size_t len, off, n, i;
3598         uc_stringbuf_t *buf;
3599         unsigned char *p;
3600         const char *s;
3601 
3602         if (ucv_type(input) != UC_STRING)
3603                 return NULL;
3604 
3605         if (skip && ucv_type(skip) != UC_STRING)
3606                 return NULL;
3607 
3608         p = (unsigned char *)ucv_string_get(input);
3609         len = ucv_string_length(input);
3610 
3611         s = skip ? (const char *)ucv_string_get(skip) : " \t\n";
3612 
3613         for (i = 0, n = 0; i < len; i++) {
3614                 if (isxdigit(p[i]))
3615                         n++;
3616                 else if (!s || !strchr(s, p[i]))
3617                         return NULL;
3618         }
3619 
3620         if (n & 1)
3621                 return NULL;
3622 
3623         buf = ucv_stringbuf_new();
3624         off = printbuf_length(buf);
3625 
3626         /* preallocate the output buffer */
3627         printbuf_memset(buf, off, 0, n / 2 + 1);
3628 
3629         for (i = 0, n = 0; i < len; i++) {
3630                 if (!isxdigit(p[i]))
3631                         continue;
3632 
3633                 buf->buf[off + (n >> 1)] |= hexval(p[i] | 32, n & 1);
3634                 n++;
3635         }
3636 
3637         /* do not include sentinel `\0` in string length */
3638         buf->bpos--;
3639 
3640         return ucv_stringbuf_finish(buf);
3641 }
3642 
3643 static uc_value_t *
3644 uc_gc(uc_vm_t *vm, size_t nargs)
3645 {
3646         uc_value_t *operation = uc_fn_arg(0);
3647         uc_value_t *argument = uc_fn_arg(1);
3648         const char *op = NULL;
3649         uc_weakref_t *ref;
3650         int64_t n;
3651 
3652         if (operation != NULL && ucv_type(operation) != UC_STRING)
3653                 return NULL;
3654 
3655         op = ucv_string_get(operation);
3656 
3657         if (!op || !strcmp(op, "collect")) {
3658                 ucv_gc(vm);
3659 
3660                 return ucv_boolean_new(true);
3661         }
3662         else if (!strcmp(op, "start")) {
3663                 n = argument ? ucv_int64_get(argument) : 0;
3664 
3665                 if (errno || n < 0 || n > 0xFFFF)
3666                         return NULL;
3667 
3668                 if (n == 0)
3669                         n = GC_DEFAULT_INTERVAL;
3670 
3671                 return ucv_boolean_new(uc_vm_gc_start(vm, n));
3672         }
3673         else if (!strcmp(op, "stop")) {
3674                 return ucv_boolean_new(uc_vm_gc_stop(vm));
3675         }
3676         else if (!strcmp(op, "count")) {
3677                 for (n = 0, ref = vm->values.next; ref != &vm->values; ref = ref->next)
3678                         n++;
3679 
3680                 return ucv_uint64_new(n);
3681         }
3682 
3683         return NULL;
3684 }
3685 
3686 static void
3687 uc_compile_parse_config(uc_parse_config_t *config, uc_value_t *spec)
3688 {
3689         uc_value_t *v, *p;
3690         size_t i, j;
3691         bool found;
3692 
3693         struct {
3694                 const char *key;
3695                 bool *flag;
3696                 uc_search_path_t *path;
3697         } fields[] = {
3698                 { "lstrip_blocks",       &config->lstrip_blocks,       NULL },
3699                 { "trim_blocks",         &config->trim_blocks,         NULL },
3700                 { "strict_declarations", &config->strict_declarations, NULL },
3701                 { "raw_mode",            &config->raw_mode,            NULL },
3702                 { "module_search_path",  NULL, &config->module_search_path  },
3703                 { "force_dynlink_list",  NULL, &config->force_dynlink_list  }
3704         };
3705 
3706         for (i = 0; i < ARRAY_SIZE(fields); i++) {
3707                 v = ucv_object_get(spec, fields[i].key, &found);
3708 
3709                 if (!found)
3710                         continue;
3711 
3712                 if (fields[i].flag) {
3713                         *fields[i].flag = ucv_is_truish(v);
3714                 }
3715                 else if (fields[i].path) {
3716                         fields[i].path->count = 0;
3717                         fields[i].path->entries = NULL;
3718 
3719                         for (j = 0; j < ucv_array_length(v); j++) {
3720                                 p = ucv_array_get(v, j);
3721 
3722                                 if (ucv_type(p) != UC_STRING)
3723                                         continue;
3724 
3725                                 uc_vector_push(fields[i].path, ucv_string_get(p));
3726                         }
3727                 }
3728         }
3729 }
3730 
3731 static uc_value_t *
3732 uc_load_common(uc_vm_t *vm, size_t nargs, uc_source_t *source)
3733 {
3734         uc_parse_config_t conf = *vm->config;
3735         uc_program_t *program;
3736         uc_value_t *closure;
3737         char *err = NULL;
3738 
3739         uc_compile_parse_config(&conf, uc_fn_arg(1));
3740 
3741         program = uc_compile(&conf, source, &err);
3742         closure = program ? ucv_closure_new(vm, uc_program_entry(program), false) : NULL;
3743 
3744         uc_program_put(program);
3745 
3746         if (!vm->config || conf.module_search_path.entries != vm->config->module_search_path.entries)
3747                 uc_vector_clear(&conf.module_search_path);
3748 
3749         if (!vm->config || conf.force_dynlink_list.entries != vm->config->force_dynlink_list.entries)
3750                 uc_vector_clear(&conf.force_dynlink_list);
3751 
3752         if (!closure) {
3753                 uc_error_message_indent(&err);
3754 
3755                 if (source->buffer)
3756                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3757                                 "Unable to compile source string:\n\n%s", err);
3758                 else
3759                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3760                                 "Unable to compile source file '%s':\n\n%s", source->filename, err);
3761         }
3762 
3763         uc_source_put(source);
3764         free(err);
3765 
3766         return closure;
3767 }
3768 
3769 static uc_value_t *
3770 uc_loadstring(uc_vm_t *vm, size_t nargs)
3771 {
3772         uc_value_t *code = uc_fn_arg(0);
3773         uc_source_t *source;
3774         size_t len;
3775         char *s;
3776 
3777         if (ucv_type(code) == UC_STRING) {
3778                 len = ucv_string_length(code);
3779                 s = xalloc(len);
3780                 memcpy(s, ucv_string_get(code), len);
3781         }
3782         else {
3783                 s = ucv_to_string(vm, code);
3784                 len = strlen(s);
3785         }
3786 
3787         source = uc_source_new_buffer("[loadstring argument]", s, len);
3788 
3789         if (!source) {
3790                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3791                         "Unable to allocate source buffer: %s",
3792                         strerror(errno));
3793 
3794                 return NULL;
3795         }
3796 
3797         return uc_load_common(vm, nargs, source);
3798 }
3799 
3800 static uc_value_t *
3801 uc_loadfile(uc_vm_t *vm, size_t nargs)
3802 {
3803         uc_value_t *path = uc_fn_arg(0);
3804         uc_source_t *source;
3805 
3806         if (ucv_type(path) != UC_STRING)
3807                 return NULL;
3808 
3809         source = uc_source_new_file(ucv_string_get(path));
3810 
3811         if (!source) {
3812                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3813                         "Unable to open source file %s: %s",
3814                         ucv_string_get(path), strerror(errno));
3815 
3816                 return NULL;
3817         }
3818 
3819         return uc_load_common(vm, nargs, source);
3820 }
3821 
3822 static uc_value_t *
3823 uc_callfunc(uc_vm_t *vm, size_t nargs)
3824 {
3825         size_t argoff = vm->stack.count - nargs, i;
3826         uc_value_t *fn_scope, *prev_scope, *res;
3827         uc_value_t *fn = uc_fn_arg(0);
3828         uc_value_t *this = uc_fn_arg(1);
3829         uc_value_t *scope = uc_fn_arg(2);
3830 
3831         if (!ucv_is_callable(fn))
3832                 return NULL;
3833 
3834         if (scope && ucv_type(scope) != UC_OBJECT)
3835                 return NULL;
3836 
3837         if (ucv_prototype_get(scope)) {
3838                 fn_scope = ucv_get(scope);
3839         }
3840         else if (scope) {
3841                 fn_scope = ucv_object_new(vm);
3842 
3843                 ucv_object_foreach(scope, k, v)
3844                         ucv_object_add(fn_scope, k, ucv_get(v));
3845 
3846                 ucv_prototype_set(fn_scope, ucv_get(uc_vm_scope_get(vm)));
3847         }
3848         else {
3849                 fn_scope = NULL;
3850         }
3851 
3852         uc_vm_stack_push(vm, ucv_get(this));
3853         uc_vm_stack_push(vm, ucv_get(fn));
3854 
3855         for (i = 3; i < nargs; i++)
3856                 uc_vm_stack_push(vm, ucv_get(vm->stack.entries[3 + argoff++]));
3857 
3858         if (fn_scope) {
3859                 prev_scope = ucv_get(uc_vm_scope_get(vm));
3860                 uc_vm_scope_set(vm, fn_scope);
3861         }
3862 
3863         if (uc_vm_call(vm, true, i - 3) == EXCEPTION_NONE)
3864                 res = uc_vm_stack_pop(vm);
3865         else
3866                 res = NULL;
3867 
3868         if (fn_scope)
3869                 uc_vm_scope_set(vm, prev_scope);
3870 
3871         return res;
3872 }
3873 
3874 
3875 const uc_function_list_t uc_stdlib_functions[] = {
3876         { "chr",                uc_chr },
3877         { "die",                uc_die },
3878         { "exists",             uc_exists },
3879         { "exit",               uc_exit },
3880         { "filter",             uc_filter },
3881         { "getenv",             uc_getenv },
3882         { "hex",                uc_hex },
3883         { "index",              uc_lindex },
3884         { "int",                uc_int },
3885         { "join",               uc_join },
3886         { "keys",               uc_keys },
3887         { "lc",                 uc_lc },
3888         { "length",             uc_length },
3889         { "ltrim",              uc_ltrim },
3890         { "map",                uc_map },
3891         { "ord",                uc_ord },
3892         { "pop",                uc_pop },
3893         { "print",              uc_print },
3894         { "push",               uc_push },
3895         { "reverse",    uc_reverse },
3896         { "rindex",             uc_rindex },
3897         { "rtrim",              uc_rtrim },
3898         { "shift",              uc_shift },
3899         { "sort",               uc_sort },
3900         { "splice",             uc_splice },
3901         { "slice",              uc_slice },
3902         { "split",              uc_split },
3903         { "substr",             uc_substr },
3904         { "time",               uc_time },
3905         { "trim",               uc_trim },
3906         { "type",               uc_type },
3907         { "uchr",               uc_uchr },
3908         { "uc",                 uc_uc },
3909         { "unshift",    uc_unshift },
3910         { "values",             uc_values },
3911         { "sprintf",    uc_sprintf },
3912         { "printf",             uc_printf },
3913         { "require",    uc_require },
3914         { "iptoarr",    uc_iptoarr },
3915         { "arrtoip",    uc_arrtoip },
3916         { "match",              uc_match },
3917         { "replace",    uc_replace },
3918         { "json",               uc_json },
3919         { "include",    uc_include },
3920         { "warn",               uc_warn },
3921         { "system",             uc_system },
3922         { "trace",              uc_trace },
3923         { "proto",              uc_proto },
3924         { "sleep",              uc_sleep },
3925         { "assert",             uc_assert },
3926         { "render",             uc_render },
3927         { "regexp",             uc_regexp },
3928         { "wildcard",   uc_wildcard },
3929         { "sourcepath", uc_sourcepath },
3930         { "min",                uc_min },
3931         { "max",                uc_max },
3932         { "b64dec",             uc_b64dec },
3933         { "b64enc",             uc_b64enc },
3934         { "uniq",               uc_uniq },
3935         { "localtime",  uc_localtime },
3936         { "gmtime",             uc_gmtime },
3937         { "timelocal",  uc_timelocal },
3938         { "timegm",             uc_timegm },
3939         { "clock",              uc_clock },
3940         { "hexdec",             uc_hexdec },
3941         { "hexenc",             uc_hexenc },
3942         { "gc",                 uc_gc },
3943         { "loadstring", uc_loadstring },
3944         { "loadfile",   uc_loadfile },
3945         { "call",               uc_callfunc },
3946 };
3947 
3948 
3949 void
3950 uc_stdlib_load(uc_value_t *scope)
3951 {
3952         uc_function_list_register(scope, uc_stdlib_functions);
3953 }
3954 
3955 uc_cfn_ptr_t
3956 uc_stdlib_function(const char *name)
3957 {
3958         size_t i;
3959 
3960         for (i = 0; i < ARRAY_SIZE(uc_stdlib_functions); i++)
3961                 if (!strcmp(uc_stdlib_functions[i].name, name))
3962                         return uc_stdlib_functions[i].func;
3963 
3964         return NULL;
3965 }
3966 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt