]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
1 | /* |
2 | * Main for evloop command line tool. | |
3 | * | |
4 | * Runs a given script from file or stdin inside an eventloop. The | |
5 | * script can then access setTimeout() etc. | |
6 | */ | |
7 | ||
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | #include <string.h> | |
11 | #ifndef NO_SIGNAL | |
12 | #include <signal.h> | |
13 | #endif | |
14 | ||
15 | #include "duktape.h" | |
16 | ||
17 | extern void poll_register(duk_context *ctx); | |
18 | extern void ncurses_register(duk_context *ctx); | |
19 | extern void socket_register(duk_context *ctx); | |
20 | extern void fileio_register(duk_context *ctx); | |
21 | extern void eventloop_register(duk_context *ctx); | |
22 | extern int eventloop_run(duk_context *ctx); /* Duktape/C function, safe called */ | |
23 | ||
24 | static int c_evloop = 0; | |
25 | ||
26 | #ifndef NO_SIGNAL | |
27 | static void my_sighandler(int x) { | |
28 | fprintf(stderr, "Got signal %d\n", x); | |
29 | fflush(stderr); | |
30 | } | |
31 | static void set_sigint_handler(void) { | |
32 | (void) signal(SIGINT, my_sighandler); | |
33 | } | |
34 | #endif /* NO_SIGNAL */ | |
35 | ||
36 | /* Print error to stderr and pop error. */ | |
37 | static void print_error(duk_context *ctx, FILE *f) { | |
38 | if (duk_is_object(ctx, -1) && duk_has_prop_string(ctx, -1, "stack")) { | |
39 | /* XXX: print error objects specially */ | |
40 | /* XXX: pcall the string coercion */ | |
41 | duk_get_prop_string(ctx, -1, "stack"); | |
42 | if (duk_is_string(ctx, -1)) { | |
43 | fprintf(f, "%s\n", duk_get_string(ctx, -1)); | |
44 | fflush(f); | |
45 | duk_pop_2(ctx); | |
46 | return; | |
47 | } else { | |
48 | duk_pop(ctx); | |
49 | } | |
50 | } | |
51 | duk_to_string(ctx, -1); | |
52 | fprintf(f, "%s\n", duk_get_string(ctx, -1)); | |
53 | fflush(f); | |
54 | duk_pop(ctx); | |
55 | } | |
56 | ||
57 | int wrapped_compile_execute(duk_context *ctx) { | |
58 | int comp_flags = 0; | |
59 | int rc; | |
60 | ||
61 | /* Compile input and place it into global _USERCODE */ | |
62 | duk_compile(ctx, comp_flags); | |
63 | duk_push_global_object(ctx); | |
64 | duk_insert(ctx, -2); /* [ ... global func ] */ | |
65 | duk_put_prop_string(ctx, -2, "_USERCODE"); | |
66 | duk_pop(ctx); | |
67 | #if 0 | |
68 | printf("compiled usercode\n"); | |
69 | #endif | |
70 | ||
71 | /* Start a zero timer which will call _USERCODE from within | |
72 | * the event loop. | |
73 | */ | |
74 | fprintf(stderr, "set _USERCODE timer\n"); | |
75 | fflush(stderr); | |
76 | duk_eval_string(ctx, "setTimeout(function() { _USERCODE(); }, 0);"); | |
77 | duk_pop(ctx); | |
78 | ||
79 | /* Finally, launch eventloop. This call only returns after the | |
80 | * eventloop terminates. | |
81 | */ | |
82 | if (c_evloop) { | |
83 | fprintf(stderr, "calling eventloop_run()\n"); | |
84 | fflush(stderr); | |
85 | rc = duk_safe_call(ctx, eventloop_run, 0 /*nargs*/, 1 /*nrets*/); | |
86 | if (rc != 0) { | |
87 | fprintf(stderr, "eventloop_run() failed: %s\n", duk_to_string(ctx, -1)); | |
88 | fflush(stderr); | |
89 | } | |
90 | duk_pop(ctx); | |
91 | } else { | |
92 | fprintf(stderr, "calling EventLoop.run()\n"); | |
93 | fflush(stderr); | |
94 | duk_eval_string(ctx, "EventLoop.run();"); | |
95 | duk_pop(ctx); | |
96 | } | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | int handle_fh(duk_context *ctx, FILE *f, const char *filename) { | |
102 | char *buf = NULL; | |
103 | int len; | |
104 | int got; | |
105 | int rc; | |
106 | int retval = -1; | |
107 | ||
108 | if (fseek(f, 0, SEEK_END) < 0) { | |
109 | goto error; | |
110 | } | |
111 | len = (int) ftell(f); | |
112 | if (fseek(f, 0, SEEK_SET) < 0) { | |
113 | goto error; | |
114 | } | |
115 | buf = (char *) malloc(len); | |
116 | if (!buf) { | |
117 | goto error; | |
118 | } | |
119 | ||
120 | got = fread((void *) buf, (size_t) 1, (size_t) len, f); | |
121 | ||
122 | duk_push_lstring(ctx, buf, got); | |
123 | duk_push_string(ctx, filename); | |
124 | ||
125 | free(buf); | |
126 | buf = NULL; | |
127 | ||
128 | rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/); | |
129 | if (rc != DUK_EXEC_SUCCESS) { | |
130 | print_error(ctx, stderr); | |
131 | goto error; | |
132 | } else { | |
133 | duk_pop(ctx); | |
134 | retval = 0; | |
135 | } | |
136 | /* fall thru */ | |
137 | ||
138 | error: | |
139 | if (buf) { | |
140 | free(buf); | |
141 | } | |
142 | return retval; | |
143 | } | |
144 | ||
145 | int handle_file(duk_context *ctx, const char *filename) { | |
146 | FILE *f = NULL; | |
147 | int retval; | |
148 | ||
149 | f = fopen(filename, "rb"); | |
150 | if (!f) { | |
151 | fprintf(stderr, "failed to open source file: %s\n", filename); | |
152 | fflush(stderr); | |
153 | goto error; | |
154 | } | |
155 | ||
156 | retval = handle_fh(ctx, f, filename); | |
157 | ||
158 | fclose(f); | |
159 | return retval; | |
160 | ||
161 | error: | |
162 | return -1; | |
163 | } | |
164 | ||
165 | int handle_stdin(duk_context *ctx) { | |
166 | int retval; | |
167 | ||
168 | retval = handle_fh(ctx, stdin, "stdin"); | |
169 | ||
170 | return retval; | |
171 | } | |
172 | ||
173 | int main(int argc, char *argv[]) { | |
174 | duk_context *ctx = NULL; | |
175 | int retval = 0; | |
176 | const char *filename = NULL; | |
177 | int i; | |
178 | ||
179 | #ifndef NO_SIGNAL | |
180 | set_sigint_handler(); | |
181 | ||
182 | /* This is useful at the global level; libraries should avoid SIGPIPE though */ | |
183 | /*signal(SIGPIPE, SIG_IGN);*/ | |
184 | #endif | |
185 | ||
186 | for (i = 1; i < argc; i++) { | |
187 | char *arg = argv[i]; | |
188 | if (!arg) { | |
189 | goto usage; | |
190 | } | |
191 | if (strcmp(arg, "-c") == 0) { | |
192 | c_evloop = 1; | |
193 | } else if (strlen(arg) > 1 && arg[0] == '-') { | |
194 | goto usage; | |
195 | } else { | |
196 | if (filename) { | |
197 | goto usage; | |
198 | } | |
199 | filename = arg; | |
200 | } | |
201 | } | |
202 | if (!filename) { | |
203 | goto usage; | |
204 | } | |
205 | ||
206 | ctx = duk_create_heap_default(); | |
207 | ||
208 | poll_register(ctx); | |
209 | ncurses_register(ctx); | |
210 | socket_register(ctx); | |
211 | fileio_register(ctx); | |
212 | ||
213 | if (c_evloop) { | |
214 | fprintf(stderr, "Using C based eventloop (omit -c to use Ecmascript based eventloop)\n"); | |
215 | fflush(stderr); | |
216 | ||
217 | eventloop_register(ctx); | |
218 | duk_eval_file(ctx, "c_eventloop.js"); | |
219 | } else { | |
220 | fprintf(stderr, "Using Ecmascript based eventloop (give -c to use C based eventloop)\n"); | |
221 | fflush(stderr); | |
222 | ||
223 | duk_eval_file(ctx, "ecma_eventloop.js"); | |
224 | } | |
225 | ||
226 | fprintf(stderr, "Executing code from: '%s'\n", filename); | |
227 | fflush(stderr); | |
228 | ||
229 | if (strcmp(filename, "-") == 0) { | |
230 | if (handle_stdin(ctx) != 0) { | |
231 | retval = 1; | |
232 | goto cleanup; | |
233 | } | |
234 | } else { | |
235 | if (handle_file(ctx, filename) != 0) { | |
236 | retval = 1; | |
237 | goto cleanup; | |
238 | } | |
239 | } | |
240 | ||
241 | cleanup: | |
242 | if (ctx) { | |
243 | duk_destroy_heap(ctx); | |
244 | } | |
245 | ||
246 | return retval; | |
247 | ||
248 | usage: | |
249 | fprintf(stderr, "Usage: evloop [-c] <filename>\n"); | |
250 | fprintf(stderr, "\n"); | |
251 | fprintf(stderr, "Uses an Ecmascript based eventloop (ecma_eventloop.js) by default.\n"); | |
252 | fprintf(stderr, "If -c option given, uses a C based eventloop (c_eventloop.{c,js}).\n"); | |
253 | fprintf(stderr, "If <filename> is '-', the entire STDIN executed.\n"); | |
254 | fflush(stderr); | |
255 | exit(1); | |
256 | } |