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