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