| /* |
| * Main for evloop command line tool. |
| * |
| * Runs a given script from file or stdin inside an eventloop. The |
| * script can then access setTimeout() etc. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #ifndef NO_SIGNAL |
| #include <signal.h> |
| #endif |
| |
| #include "duktape.h" |
| |
| extern void poll_register(duk_context *ctx); |
| extern void ncurses_register(duk_context *ctx); |
| extern void socket_register(duk_context *ctx); |
| extern void fileio_register(duk_context *ctx); |
| extern void eventloop_register(duk_context *ctx); |
| extern int eventloop_run(duk_context *ctx); /* Duktape/C function, safe called */ |
| |
| static int c_evloop = 0; |
| |
| #ifndef NO_SIGNAL |
| static void my_sighandler(int x) { |
| fprintf(stderr, "Got signal %d\n", x); |
| fflush(stderr); |
| } |
| static void set_sigint_handler(void) { |
| (void) signal(SIGINT, my_sighandler); |
| } |
| #endif /* NO_SIGNAL */ |
| |
| /* Print error to stderr and pop error. */ |
| static void print_error(duk_context *ctx, FILE *f) { |
| if (duk_is_object(ctx, -1) && duk_has_prop_string(ctx, -1, "stack")) { |
| /* XXX: print error objects specially */ |
| /* XXX: pcall the string coercion */ |
| duk_get_prop_string(ctx, -1, "stack"); |
| if (duk_is_string(ctx, -1)) { |
| fprintf(f, "%s\n", duk_get_string(ctx, -1)); |
| fflush(f); |
| duk_pop_2(ctx); |
| return; |
| } else { |
| duk_pop(ctx); |
| } |
| } |
| duk_to_string(ctx, -1); |
| fprintf(f, "%s\n", duk_get_string(ctx, -1)); |
| fflush(f); |
| duk_pop(ctx); |
| } |
| |
| int wrapped_compile_execute(duk_context *ctx) { |
| int comp_flags = 0; |
| int rc; |
| |
| /* Compile input and place it into global _USERCODE */ |
| duk_compile(ctx, comp_flags); |
| duk_push_global_object(ctx); |
| duk_insert(ctx, -2); /* [ ... global func ] */ |
| duk_put_prop_string(ctx, -2, "_USERCODE"); |
| duk_pop(ctx); |
| #if 0 |
| printf("compiled usercode\n"); |
| #endif |
| |
| /* Start a zero timer which will call _USERCODE from within |
| * the event loop. |
| */ |
| fprintf(stderr, "set _USERCODE timer\n"); |
| fflush(stderr); |
| duk_eval_string(ctx, "setTimeout(function() { _USERCODE(); }, 0);"); |
| duk_pop(ctx); |
| |
| /* Finally, launch eventloop. This call only returns after the |
| * eventloop terminates. |
| */ |
| if (c_evloop) { |
| fprintf(stderr, "calling eventloop_run()\n"); |
| fflush(stderr); |
| rc = duk_safe_call(ctx, eventloop_run, 0 /*nargs*/, 1 /*nrets*/); |
| if (rc != 0) { |
| fprintf(stderr, "eventloop_run() failed: %s\n", duk_to_string(ctx, -1)); |
| fflush(stderr); |
| } |
| duk_pop(ctx); |
| } else { |
| fprintf(stderr, "calling EventLoop.run()\n"); |
| fflush(stderr); |
| duk_eval_string(ctx, "EventLoop.run();"); |
| duk_pop(ctx); |
| } |
| |
| return 0; |
| } |
| |
| int handle_fh(duk_context *ctx, FILE *f, const char *filename) { |
| char *buf = NULL; |
| int len; |
| int got; |
| int rc; |
| int retval = -1; |
| |
| if (fseek(f, 0, SEEK_END) < 0) { |
| goto error; |
| } |
| len = (int) ftell(f); |
| if (fseek(f, 0, SEEK_SET) < 0) { |
| goto error; |
| } |
| buf = (char *) malloc(len); |
| if (!buf) { |
| goto error; |
| } |
| |
| got = fread((void *) buf, (size_t) 1, (size_t) len, f); |
| |
| duk_push_lstring(ctx, buf, got); |
| duk_push_string(ctx, filename); |
| |
| free(buf); |
| buf = NULL; |
| |
| rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/); |
| if (rc != DUK_EXEC_SUCCESS) { |
| print_error(ctx, stderr); |
| goto error; |
| } else { |
| duk_pop(ctx); |
| retval = 0; |
| } |
| /* fall thru */ |
| |
| error: |
| if (buf) { |
| free(buf); |
| } |
| return retval; |
| } |
| |
| int handle_file(duk_context *ctx, const char *filename) { |
| FILE *f = NULL; |
| int retval; |
| |
| f = fopen(filename, "rb"); |
| if (!f) { |
| fprintf(stderr, "failed to open source file: %s\n", filename); |
| fflush(stderr); |
| goto error; |
| } |
| |
| retval = handle_fh(ctx, f, filename); |
| |
| fclose(f); |
| return retval; |
| |
| error: |
| return -1; |
| } |
| |
| int handle_stdin(duk_context *ctx) { |
| int retval; |
| |
| retval = handle_fh(ctx, stdin, "stdin"); |
| |
| return retval; |
| } |
| |
| int main(int argc, char *argv[]) { |
| duk_context *ctx = NULL; |
| int retval = 0; |
| const char *filename = NULL; |
| int i; |
| |
| #ifndef NO_SIGNAL |
| set_sigint_handler(); |
| |
| /* This is useful at the global level; libraries should avoid SIGPIPE though */ |
| /*signal(SIGPIPE, SIG_IGN);*/ |
| #endif |
| |
| for (i = 1; i < argc; i++) { |
| char *arg = argv[i]; |
| if (!arg) { |
| goto usage; |
| } |
| if (strcmp(arg, "-c") == 0) { |
| c_evloop = 1; |
| } else if (strlen(arg) > 1 && arg[0] == '-') { |
| goto usage; |
| } else { |
| if (filename) { |
| goto usage; |
| } |
| filename = arg; |
| } |
| } |
| if (!filename) { |
| goto usage; |
| } |
| |
| ctx = duk_create_heap_default(); |
| |
| poll_register(ctx); |
| ncurses_register(ctx); |
| socket_register(ctx); |
| fileio_register(ctx); |
| |
| if (c_evloop) { |
| fprintf(stderr, "Using C based eventloop (omit -c to use Ecmascript based eventloop)\n"); |
| fflush(stderr); |
| |
| eventloop_register(ctx); |
| duk_eval_file(ctx, "c_eventloop.js"); |
| } else { |
| fprintf(stderr, "Using Ecmascript based eventloop (give -c to use C based eventloop)\n"); |
| fflush(stderr); |
| |
| duk_eval_file(ctx, "ecma_eventloop.js"); |
| } |
| |
| fprintf(stderr, "Executing code from: '%s'\n", filename); |
| fflush(stderr); |
| |
| if (strcmp(filename, "-") == 0) { |
| if (handle_stdin(ctx) != 0) { |
| retval = 1; |
| goto cleanup; |
| } |
| } else { |
| if (handle_file(ctx, filename) != 0) { |
| retval = 1; |
| goto cleanup; |
| } |
| } |
| |
| cleanup: |
| if (ctx) { |
| duk_destroy_heap(ctx); |
| } |
| |
| return retval; |
| |
| usage: |
| fprintf(stderr, "Usage: evloop [-c] <filename>\n"); |
| fprintf(stderr, "\n"); |
| fprintf(stderr, "Uses an Ecmascript based eventloop (ecma_eventloop.js) by default.\n"); |
| fprintf(stderr, "If -c option given, uses a C based eventloop (c_eventloop.{c,js}).\n"); |
| fprintf(stderr, "If <filename> is '-', the entire STDIN executed.\n"); |
| fflush(stderr); |
| exit(1); |
| } |