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