• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/main.c

  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