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
my_sighandler(int x)27 static void my_sighandler(int x) {
28 	fprintf(stderr, "Got signal %d\n", x);
29 	fflush(stderr);
30 }
set_sigint_handler(void)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. */
print_error(duk_context * ctx,FILE * f)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 
wrapped_compile_execute(duk_context * ctx)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 
handle_fh(duk_context * ctx,FILE * f,const char * filename)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 
handle_file(duk_context * ctx,const char * filename)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 
handle_stdin(duk_context * ctx)165 int handle_stdin(duk_context *ctx) {
166 	int retval;
167 
168 	retval = handle_fh(ctx, stdin, "stdin");
169 
170 	return retval;
171 }
172 
main(int argc,char * argv[])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 }
257