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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt