1 /* 2 * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <stdio.h> 18 #include <stdbool.h> 19 #include <stdint.h> 20 #include <unistd.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <getopt.h> 24 25 26 #include "ucode/compiler.h" 27 #include "ucode/lib.h" 28 #include "ucode/vm.h" 29 #include "ucode/types.h" 30 #include "ucode/source.h" 31 #include "ucode/program.h" 32 33 #include "ucode/internal/json-c-compat.h" 34 35 #ifdef __GLIBC__ 36 # define POSIXLY_CORRECT_FLAG "+" 37 #else 38 # define POSIXLY_CORRECT_FLAG "" 39 #endif 40 41 static FILE *stdin_unused; 42 43 static void 44 print_usage(const char *app) 45 { 46 printf( 47 "Usage:\n" 48 " %1$s -h\n" 49 " %1$s -e \"expression\"\n" 50 " %1$s input.uc [input2.uc ...]\n" 51 " %1$s -c [-s] [-o output.uc] input.uc [input2.uc ...]\n\n" 52 53 "-h\n" 54 " Help display this help.\n\n" 55 56 "-e \"expression\"\n" 57 " Execute the given expression as ucode program.\n\n" 58 59 "-p \"expression\"\n" 60 " Like `-e` but print the result of expression.\n\n" 61 62 "-t\n" 63 " Enable VM execution tracing.\n\n" 64 65 "-g interval\n" 66 " Perform periodic garbage collection every `interval` object\n" 67 " allocations.\n\n" 68 69 "-S\n" 70 " Enable strict mode.\n\n" 71 72 "-R\n" 73 " Process source file(s) as raw script code (default).\n\n" 74 75 "-T[flag,flag,...]\n" 76 " Process the source file(s) as templates, not as raw script code.\n" 77 " Supported flags: no-lstrip (don't strip leading whitespace before\n" 78 " block tags), no-rtrim (don't strip trailing newline after block tags).\n\n" 79 80 "-D [name=]value\n" 81 " Define global variable. If `name` is omitted, a JSON dictionary is\n" 82 " expected with each property becoming a global variable set to the\n" 83 " corresponding value. If `name` is specified, it is defined as global\n" 84 " variable set to `value` parsed as JSON (or the literal `value` string\n" 85 " if JSON parsing fails).\n\n" 86 87 "-F [name=]path\n" 88 " Like `-D` but reading the value from the file in `path`. The given\n" 89 " file must contain a single, well-formed JSON dictionary.\n\n" 90 91 "-U name\n" 92 " Undefine the given global variable name.\n\n" 93 94 "-l [name=]library\n" 95 " Preload the given `library`, optionally aliased to `name`.\n\n" 96 97 "-L pattern\n" 98 " Prepend given `pattern` to default library search paths. If the pattern\n" 99 " contains no `*`, it is added twice, once with `/*.so` and once with\n" 100 " `/*.uc` appended to it.\n\n" 101 102 "-c[flag,flag,...]\n" 103 " Compile the given source file(s) to bytecode instead of executing them.\n" 104 " Supported flags: no-interp (omit interpreter line), interp=... (over-\n" 105 " ride interpreter line with ...), dynlink=... (force import from ... to\n" 106 " be treated as shared extensions loaded at runtime), module (build load-\n" 107 " able library).\n\n" 108 109 "-o path\n" 110 " Output file path when compiling. If omitted, the compiled byte code\n" 111 " is written to `./uc.out`. Only meaningful in conjunction with `-c`.\n\n" 112 113 "-s\n" 114 " Omit (strip) debug information when compiling files.\n" 115 " Only meaningful in conjunction with `-c`.\n\n" 116 117 "-x[expr]\n" 118 " Start program in interactive debugger. If given, stop at the location\n" 119 " described by `expr` (a function name, `path:line[:offset]`, or a ucode\n" 120 " expression evaluating to a function - the same grammar the `break`\n" 121 " debugger CLI command accepts) instead of the first instruction.\n\n" 122 "-X[expr]\n" 123 " Enable debugger infrastructure (SIGUSR1 break, uloop) without\n" 124 " launching the interactive debugger automatically. If given, `expr` is\n" 125 " resolved the same way as for `-x` and a breakpoint is installed at\n" 126 " that location; once hit, execution pauses and waits for a remote\n" 127 " debugger to attach, the same way the SIGUSR1 break does.\n\n", 128 app); 129 } 130 131 static bool 132 parse_library_load(char *opt, uc_vm_t *vm); 133 134 static uc_value_t * 135 debug_lookup_fn(uc_vm_t *vm, const char *name) 136 { 137 uc_value_t *dbgmod = ucv_object_get(uc_vm_scope_get(vm), "debug", NULL); 138 uc_value_t *fn = ucv_object_get(dbgmod, name, NULL); 139 140 if (ucv_type(fn) != UC_CFUNCTION) { 141 fprintf(stderr, "Unable to locate debug.%s() function\n", name); 142 143 return NULL; 144 } 145 146 return fn; 147 } 148 149 typedef enum { 150 DEBUG_MODE_NONE, /* neither -x nor -X given */ 151 DEBUG_MODE_LOCAL, /* -x: launch local interactive debugger */ 152 DEBUG_MODE_REMOTE, /* -X: enable break infrastructure for a remote debugger */ 153 } debug_mode_t; 154 155 typedef struct { 156 bool strip; 157 char *interpreter; 158 bool autoprint; 159 debug_mode_t debug; 160 char *breakpoint; 161 } compile_opts_t; 162 163 static int 164 compile(uc_vm_t *vm, uc_source_t *src, FILE *precompile, const compile_opts_t *opts) 165 { 166 uc_value_t *res = NULL; 167 bool strip = opts->strip, autoprint = opts->autoprint; 168 debug_mode_t debug = opts->debug; 169 char *interp = opts->interpreter, *breakpoint = opts->breakpoint; 170 uc_program_t *program; 171 int rc = 0; 172 char *err; 173 174 program = uc_compile(vm->config, src, &err); 175 176 if (!program) { 177 fprintf(stderr, "%s", err); 178 free(err); 179 rc = -1; 180 goto out; 181 } 182 183 if (precompile) { 184 if (interp) 185 fprintf(precompile, "#!%s\n", interp); 186 187 uc_program_write(program, precompile, !strip); 188 fclose(precompile); 189 goto out; 190 } 191 192 if (vm->gc_interval) 193 uc_vm_gc_start(vm, vm->gc_interval); 194 195 if (debug != DEBUG_MODE_NONE) { 196 uc_value_t *entryfn; 197 198 if (!parse_library_load("debug", vm)) { 199 fprintf(stderr, "Unable to load debug module\n"); 200 rc = -2; 201 goto out; 202 } 203 204 entryfn = uc_program_main(vm, program); 205 206 /* -x: launch local debugger, breaking at `breakpoint` if given, 207 * else at the first instruction. 208 * -X: arm attach mode (wait for a remote debugger on the PID-derived 209 * socket). With `breakpoint` given, break at that location; without 210 * one, break at the first instruction of the entry function - the 211 * remote mirror of `-x` with no argument. Either way this is the 212 * remote equivalent of the local `-x` flow above. */ 213 if (debug == DEBUG_MODE_LOCAL) { 214 uc_value_t *dbgfn = debug_lookup_fn(vm, "debugger"); 215 216 if (!dbgfn) { 217 ucv_put(entryfn); 218 rc = -2; 219 goto out; 220 } 221 222 uc_vm_stack_push(vm, ucv_get(dbgfn)); 223 uc_vm_stack_push(vm, breakpoint ? NULL : ucv_get(entryfn)); 224 225 if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) 226 ucv_put(uc_vm_stack_pop(vm)); 227 } 228 else { 229 /* -X: arm attach mode. Pass entryfn so that, when no explicit 230 * `breakpoint` is given, debug.attach() installs its step 231 * breakpoint at the entry function's first instruction (breaking 232 * at the start of main, mirroring `-x` with no argument). When a 233 * `breakpoint` is given, the explicit breakpoint below is what 234 * pauses first; the entry step breakpoint is then simply 235 * superseded. debug.attach() ignores a non-closure arg, so 236 * passing NULL in that case is harmless. */ 237 uc_value_t *attachfn = debug_lookup_fn(vm, "attach"); 238 239 if (!attachfn) { 240 ucv_put(entryfn); 241 rc = -2; 242 goto out; 243 } 244 245 uc_vm_stack_push(vm, ucv_get(attachfn)); 246 uc_vm_stack_push(vm, breakpoint ? NULL : ucv_get(entryfn)); 247 248 if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) 249 ucv_put(uc_vm_stack_pop(vm)); 250 } 251 252 if (breakpoint) { 253 uc_value_t *bkfn = debug_lookup_fn(vm, "breakpoint"); 254 uc_value_t *id; 255 256 if (!bkfn) { 257 ucv_put(entryfn); 258 rc = -2; 259 goto out; 260 } 261 262 uc_vm_stack_push(vm, ucv_get(bkfn)); 263 uc_vm_stack_push(vm, ucv_string_new(breakpoint)); 264 uc_vm_stack_push(vm, ucv_get(entryfn)); 265 266 if (uc_vm_call(vm, false, 2) == EXCEPTION_NONE) { 267 id = uc_vm_stack_pop(vm); 268 269 if (!ucv_is_truish(id)) { 270 fprintf(stderr, 271 "Unable to resolve breakpoint location '%s'\n", 272 breakpoint); 273 ucv_put(id); 274 ucv_put(entryfn); 275 rc = -2; 276 goto out; 277 } 278 279 ucv_put(id); 280 } 281 } 282 283 ucv_put(entryfn); 284 } 285 286 uc_vm_status_t status = uc_vm_execute(vm, program, &res); 287 288 rc = status; 289 290 switch (rc) { 291 case STATUS_OK: 292 if (autoprint) { 293 if (ucv_type(res) == UC_STRING) { 294 fwrite(ucv_string_get(res), ucv_string_length(res), 1, stdout); 295 } 296 else { 297 uc_stringbuf_t *pb = xprintbuf_new(); 298 299 ucv_to_stringbuf_formatted(vm, pb, res, 0, '\t', 1); 300 fwrite(pb->buf, pb->bpos, 1, stdout); 301 printbuf_free(pb); 302 } 303 } 304 305 rc = 0; 306 break; 307 308 case STATUS_EXIT: 309 rc = (int)ucv_int64_get(res); 310 break; 311 312 case STATUS_BREAK: 313 /* Break requested - in remote debug mode, continue running */ 314 if (debug == DEBUG_MODE_REMOTE) 315 rc = 0; 316 else 317 rc = -2; 318 break; 319 320 case ERROR_COMPILE: 321 rc = -1; 322 break; 323 324 case ERROR_RUNTIME: 325 rc = -2; 326 break; 327 } 328 329 /* Let an attached remote debugger client know the target is going away 330 * and why, instead of it only finding out once the connection drops. 331 * Pass the raw VM status rather than the CLI's own exit-code 332 * translation above (which flattens both error kinds to the same -2 333 * and loses the actual exception), plus the exit code / a full 334 * exception object (same {type, message, stacktrace} shape script 335 * code sees via try/catch) it corresponds to. 336 * 337 * These have to be snapshotted into locals *before* the uc_vm_call() 338 * below, not read from vm->exception/vm->arg by the callee once 339 * inside it: uc_vm_call() unconditionally calls 340 * uc_vm_clear_exception() as its very first action (a normal safety 341 * reset for ordinary calls, which also frees vm->exception.message/ 342 * ->stacktrace), which would wipe vm->exception out from under us 343 * before debug.notifyExit() ever got to look at it. 344 * 345 * vm->output (stdout) is fully block-buffered once it's a socket 346 * rather than a tty, while the notification itself goes out via a raw 347 * write() - flush first, or the event can overtake not-yet-flushed 348 * script output that was already written earlier. */ 349 if (debug != DEBUG_MODE_NONE) { 350 uc_value_t *notifyfn = debug_lookup_fn(vm, "notifyExit"); 351 int32_t exit_code = vm->arg.s32; 352 uc_value_t *exception_obj = (status == ERROR_COMPILE || status == ERROR_RUNTIME) 353 ? uc_vm_exception_object(vm) : NULL; 354 355 fflush(vm->output); 356 357 if (notifyfn) { 358 uc_vm_stack_push(vm, ucv_get(notifyfn)); 359 uc_vm_stack_push(vm, ucv_int64_new(status)); 360 uc_vm_stack_push(vm, ucv_int64_new(exit_code)); 361 uc_vm_stack_push(vm, exception_obj); 362 363 if (uc_vm_call(vm, false, 3) == EXCEPTION_NONE) 364 ucv_put(uc_vm_stack_pop(vm)); 365 } 366 else { 367 ucv_put(exception_obj); 368 } 369 } 370 371 out: 372 uc_program_put(program); 373 ucv_put(res); 374 375 return rc; 376 } 377 378 static uc_source_t * 379 read_stdin(void) 380 { 381 size_t rlen = 0, tlen = 0; 382 char buf[128], *p = NULL; 383 384 if (!stdin_unused) { 385 fprintf(stderr, "The stdin can only be read once\n"); 386 errno = EINVAL; 387 388 return NULL; 389 } 390 391 while (true) { 392 rlen = fread(buf, 1, sizeof(buf), stdin_unused); 393 394 if (rlen == 0) 395 break; 396 397 p = xrealloc(p, tlen + rlen); 398 memcpy(p + tlen, buf, rlen); 399 tlen += rlen; 400 } 401 402 stdin_unused = NULL; 403 404 /* On empty stdin, provide a dummy buffer and ensure that it is 405 * at least one byte long, due to 406 * https://github.com/google/sanitizers/issues/627 */ 407 if (p == NULL) { 408 p = xstrdup("\n"); 409 tlen = 1; 410 } 411 412 return uc_source_new_buffer("[stdin]", p, tlen); 413 } 414 415 static void 416 parse_template_modeflags(char *opt, uc_parse_config_t *config) 417 { 418 char *p; 419 420 if (!opt) 421 return; 422 423 for (p = strtok(opt, ", "); p; p = strtok(NULL, ", ")) { 424 if (!strcmp(p, "no-lstrip")) 425 config->lstrip_blocks = false; 426 else if (!strcmp(p, "no-rtrim")) 427 config->trim_blocks = false; 428 else 429 fprintf(stderr, "Unrecognized -T flag \"%s\", ignoring\n", p); 430 } 431 } 432 433 static void 434 parse_compile_flags(char *opt, char **interp, uc_search_path_t *dynlink_list, 435 bool *module) 436 { 437 char *p, *k, *v; 438 439 if (!opt) 440 return; 441 442 for (p = strtok(opt, ","); p; p = strtok(NULL, ",")) { 443 k = p; 444 v = strchr(p, '='); 445 446 if (v) 447 *v++ = 0; 448 449 if (!strcmp(k, "no-interp")) { 450 if (v) 451 fprintf(stderr, "Compile flag \"%s\" takes no value, ignoring\n", k); 452 453 *interp = NULL; 454 } 455 else if (!strcmp(k, "interp")) { 456 if (!v) 457 fprintf(stderr, "Compile flag \"%s\" requires a value, ignoring\n", k); 458 else 459 *interp = v; 460 } 461 else if (!strcmp(k, "dynlink")) { 462 if (!v) 463 fprintf(stderr, "Compile flag \"%s\" requires a value, ignoring\n", k); 464 else 465 uc_vector_push(dynlink_list, v); 466 } 467 else if (!strcmp(k, "module")) { 468 if (v) 469 fprintf(stderr, "Compile flag \"%s\" takes no value, ignoring\n", k); 470 471 *module = true; 472 } 473 else { 474 fprintf(stderr, "Unrecognized -c flag \"%s\", ignoring\n", k); 475 } 476 } 477 } 478 479 static bool 480 parse_define_file(char *opt, uc_value_t *globals) 481 { 482 enum json_tokener_error err = json_tokener_continue; 483 char buf[128], *name = NULL, *p; 484 struct json_tokener *tok; 485 json_object *jso = NULL; 486 size_t rlen; 487 FILE *fp; 488 489 p = strchr(opt, '='); 490 491 if (p) { 492 name = opt; 493 *p++ = 0; 494 } 495 else { 496 p = opt; 497 } 498 499 if (!strcmp(p, "-")) { 500 if (!stdin_unused) { 501 fprintf(stderr, "The stdin can only be read once\n"); 502 503 return false; 504 } 505 506 fp = stdin_unused; 507 stdin_unused = NULL; 508 } 509 else 510 fp = fopen(p, "r"); 511 512 if (!fp) { 513 fprintf(stderr, "Unable to open definition file \"%s\": %s\n", 514 p, strerror(errno)); 515 516 return true; 517 } 518 519 tok = xjs_new_tokener(); 520 521 while (true) { 522 rlen = fread(buf, 1, sizeof(buf), fp); 523 524 if (rlen == 0) 525 break; 526 527 jso = json_tokener_parse_ex(tok, buf, rlen); 528 err = json_tokener_get_error(tok); 529 530 if (err != json_tokener_continue) 531 break; 532 } 533 534 json_tokener_free(tok); 535 fclose(fp); 536 537 if (err != json_tokener_success || !json_object_is_type(jso, json_type_object)) { 538 json_object_put(jso); 539 540 fprintf(stderr, "Invalid definition file \"%s\": %s\n", 541 p, (err != json_tokener_success) 542 ? "JSON parse failure" : "Not a valid JSON object"); 543 544 return false; 545 } 546 547 if (name && *name) { 548 ucv_object_add(globals, name, ucv_from_json(NULL, jso)); 549 } 550 else { 551 json_object_object_foreach(jso, key, val) 552 ucv_object_add(globals, key, ucv_from_json(NULL, val)); 553 } 554 555 json_object_put(jso); 556 557 return true; 558 } 559 560 static bool 561 parse_define_string(char *opt, uc_value_t *globals) 562 { 563 enum json_tokener_error err; 564 struct json_tokener *tok; 565 json_object *jso = NULL; 566 char *name = NULL, *p; 567 bool rv = false; 568 size_t len; 569 570 p = strchr(opt, '='); 571 572 if (p) { 573 name = opt; 574 *p++ = 0; 575 } 576 else { 577 p = opt; 578 } 579 580 len = strlen(p); 581 tok = xjs_new_tokener(); 582 583 /* NB: the len + 1 here is intentional to pass the terminating \0 byte 584 * to the json-c parser. This is required to work-around upstream 585 * issue #681 <https://github.com/json-c/json-c/issues/681> */ 586 jso = json_tokener_parse_ex(tok, p, len + 1); 587 588 err = json_tokener_get_error(tok); 589 590 /* Treat trailing bytes after a parsed value as error */ 591 if (err == json_tokener_success && json_tokener_get_parse_end(tok) < len) 592 err = json_tokener_error_parse_unexpected; 593 594 json_tokener_free(tok); 595 596 if (err != json_tokener_success) { 597 json_object_put(jso); 598 599 if (!name || !*name) { 600 fprintf(stderr, "Invalid -D option value \"%s\": %s\n", 601 p, json_tokener_error_desc(err)); 602 603 return false; 604 } 605 606 ucv_object_add(globals, name, ucv_string_new(p)); 607 608 return true; 609 } 610 611 if (name && *name) { 612 ucv_object_add(globals, name, ucv_from_json(NULL, jso)); 613 rv = true; 614 } 615 else if (json_object_is_type(jso, json_type_object)) { 616 json_object_object_foreach(jso, key, val) 617 ucv_object_add(globals, key, ucv_from_json(NULL, val)); 618 rv = true; 619 } 620 else { 621 fprintf(stderr, "Invalid -D option value \"%s\": Not a valid JSON object\n", p); 622 } 623 624 json_object_put(jso); 625 626 return rv; 627 } 628 629 static void 630 parse_search_path(char *pattern, uc_parse_config_t *config) 631 { 632 size_t len; 633 char *p; 634 635 if (strchr(pattern, '*')) { 636 uc_search_path_add(&config->module_search_path, pattern); 637 return; 638 } 639 640 len = strlen(pattern); 641 642 if (!len) 643 return; 644 645 while (pattern[len-1] == '/') 646 pattern[--len] = 0; 647 648 xasprintf(&p, "%s/*.so", pattern); 649 uc_search_path_add(&config->module_search_path, p); 650 free(p); 651 652 xasprintf(&p, "%s/*.uc", pattern); 653 uc_search_path_add(&config->module_search_path, p); 654 free(p); 655 } 656 657 static bool 658 parse_library_load(char *opt, uc_vm_t *vm) 659 { 660 char *name = NULL, *p; 661 uc_value_t *lib, *ctx; 662 663 p = strchr(opt, '='); 664 665 if (p) { 666 name = opt; 667 *p++ = 0; 668 } 669 else { 670 p = opt; 671 } 672 673 lib = ucv_string_new(p); 674 ctx = uc_vm_invoke(vm, "require", 1, lib); 675 ucv_put(lib); 676 677 if (!ctx) 678 return vm->exception.type == EXCEPTION_NONE; 679 680 ucv_object_add(uc_vm_scope_get(vm), name ? name : p, ctx); 681 682 return true; 683 } 684 685 static const char * 686 appname(const char *argv0) 687 { 688 const char *p; 689 690 if (!argv0) 691 return "ucode"; 692 693 p = strrchr(argv0, '/'); 694 695 if (p) 696 return p + 1; 697 698 return argv0; 699 } 700 701 int 702 main(int argc, char **argv) 703 { 704 const char *optspec = POSIXLY_CORRECT_FLAG "he:p:tg:ST::RD:F:U:l:L:c::o:sx::X::"; 705 bool strip = false, print_result = false; 706 debug_mode_t debug = DEBUG_MODE_NONE; 707 char *breakpoint = NULL; 708 char *interp = "/usr/bin/env ucode"; 709 uc_source_t *source = NULL; 710 FILE *precompile = NULL; 711 char *outfile = NULL; 712 uc_vm_t vm = { 0 }; 713 const char *argv0; 714 int opt, rv = 0; 715 const char *app; 716 uc_value_t *o; 717 int fd; 718 719 uc_parse_config_t config = { 720 .strict_declarations = false, 721 .lstrip_blocks = true, 722 .trim_blocks = true, 723 .raw_mode = true, 724 .setup_signal_handlers = true 725 }; 726 727 app = appname(argv[0]); 728 729 if (argc == 1) { 730 print_usage(app); 731 goto out; 732 } 733 734 if (!strcmp(app, "utpl")) 735 config.raw_mode = false; 736 else if (!strcmp(app, "ucc")) 737 outfile = "./uc.out"; 738 739 stdin_unused = stdin; 740 741 /* parse options iteration 1: parse config related options */ 742 while ((opt = getopt(argc, argv, optspec)) != -1) 743 { 744 switch (opt) { 745 case 'L': 746 parse_search_path(optarg, &config); 747 break; 748 749 case 'S': 750 config.strict_declarations = true; 751 break; 752 753 case 'R': 754 config.raw_mode = true; 755 break; 756 757 case 'T': 758 config.raw_mode = false; 759 parse_template_modeflags(optarg, &config); 760 break; 761 } 762 } 763 764 uc_search_path_init(&config.module_search_path); 765 766 argv0 = argv[optind]; 767 optind = 1; 768 769 uc_vm_init(&vm, &config); 770 771 /* load std functions into global scope */ 772 uc_stdlib_load(uc_vm_scope_get(&vm)); 773 774 /* register ARGV array but populate it later (to allow for -U ARGV) */ 775 o = ucv_array_new(&vm); 776 777 ucv_object_add(uc_vm_scope_get(&vm), "ARGV", ucv_get(o)); 778 if (argv0) 779 ucv_object_add(uc_vm_scope_get(&vm), "SCRIPT_NAME", ucv_string_new(argv0)); 780 781 /* parse options iteration 2: process remaining options */ 782 while ((opt = getopt(argc, argv, optspec)) != -1) 783 { 784 switch (opt) { 785 case 'h': 786 print_usage(argv[0]); 787 goto out; 788 789 case 'e': 790 source = uc_source_new_buffer("[-e argument]", xstrdup(optarg), strlen(optarg)); 791 break; 792 793 case 'p': 794 source = uc_source_new_buffer("[-p argument]", xstrdup(optarg), strlen(optarg)); 795 print_result = true; 796 break; 797 798 case 't': 799 uc_vm_trace_set(&vm, 1); 800 break; 801 802 case 'g': 803 vm.gc_interval = atoi(optarg); 804 break; 805 806 case 'D': 807 if (!parse_define_string(optarg, uc_vm_scope_get(&vm))) { 808 rv = 1; 809 goto out; 810 } 811 812 break; 813 814 case 'F': 815 if (!parse_define_file(optarg, uc_vm_scope_get(&vm))) { 816 rv = 1; 817 goto out; 818 } 819 820 break; 821 822 case 'U': 823 ucv_object_delete(uc_vm_scope_get(&vm), optarg); 824 break; 825 826 case 'l': 827 if (!parse_library_load(optarg, &vm)) { 828 rv = 1; 829 goto out; 830 } 831 832 break; 833 834 case 'c': 835 outfile = "./uc.out"; 836 parse_compile_flags(optarg, &interp, &config.force_dynlink_list, 837 &config.compile_module); 838 break; 839 840 case 's': 841 strip = true; 842 break; 843 844 case 'o': 845 outfile = optarg; 846 break; 847 848 case 'x': 849 debug = DEBUG_MODE_LOCAL; 850 breakpoint = optarg; 851 break; 852 853 case 'X': 854 debug = DEBUG_MODE_REMOTE; 855 breakpoint = optarg; 856 break; 857 } 858 } 859 860 if (!source && argv[optind] != NULL) { 861 if (!strcmp(argv[optind], "-")) 862 source = read_stdin(); 863 else 864 source = uc_source_new_file(argv[optind]); 865 866 if (!source) { 867 fprintf(stderr, "Failed to open \"%s\": %s\n", argv[optind], strerror(errno)); 868 rv = 1; 869 goto out; 870 } 871 872 optind++; 873 } 874 875 if (!source) { 876 fprintf(stderr, "Require either -e/-p expression or source file\n"); 877 rv = 1; 878 goto out; 879 } 880 881 if (outfile) { 882 if (!strcmp(outfile, "-")) { 883 precompile = stdout; 884 } 885 else { 886 fd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0777); 887 888 if (fd == -1) { 889 fprintf(stderr, "Unable to open output file %s: %s\n", 890 outfile, strerror(errno)); 891 892 rv = 1; 893 goto out; 894 } 895 896 precompile = fdopen(fd, "wb"); 897 } 898 } 899 900 /* populate ARGV array */ 901 for (; optind < argc; optind++) 902 ucv_array_push(o, ucv_string_new(argv[optind])); 903 904 ucv_put(o); 905 906 rv = compile(&vm, source, precompile, &((compile_opts_t){ 907 .strip = strip, 908 .interpreter = interp, 909 .autoprint = print_result, 910 .debug = debug, 911 .breakpoint = breakpoint, 912 })); 913 914 out: 915 uc_search_path_free(&config.module_search_path); 916 uc_vector_clear(&config.force_dynlink_list); 917 918 uc_source_put(source); 919 920 uc_vm_free(&vm); 921 922 return rv; 923 } 924
This page was automatically generated by LXR 0.3.1. • OpenWrt