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