1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #ifdef _MSC_VER
5 #include <io.h>
6 #else
7 #include <unistd.h>
8 #endif
9 #include <errno.h>
10 
11 #include "mujs.h"
12 
13 static char *xoptarg; /* Global argument pointer. */
14 static int xoptind = 0; /* Global argv index. */
xgetopt(int argc,char * argv[],char * optstring)15 static int xgetopt(int argc, char *argv[], char *optstring)
16 {
17 	static char *scan = NULL; /* Private scan pointer. */
18 
19 	char c;
20 	char *place;
21 
22 	xoptarg = NULL;
23 
24 	if (!scan || *scan == '\0') {
25 		if (xoptind == 0)
26 			xoptind++;
27 
28 		if (xoptind >= argc || argv[xoptind][0] != '-' || argv[xoptind][1] == '\0')
29 			return EOF;
30 		if (argv[xoptind][1] == '-' && argv[xoptind][2] == '\0') {
31 			xoptind++;
32 			return EOF;
33 		}
34 
35 		scan = argv[xoptind]+1;
36 		xoptind++;
37 	}
38 
39 	c = *scan++;
40 	place = strchr(optstring, c);
41 
42 	if (!place || c == ':') {
43 		fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
44 		return '?';
45 	}
46 
47 	place++;
48 	if (*place == ':') {
49 		if (*scan != '\0') {
50 			xoptarg = scan;
51 			scan = NULL;
52 		} else if (xoptind < argc) {
53 			xoptarg = argv[xoptind];
54 			xoptind++;
55 		} else {
56 			fprintf(stderr, "%s: option requires argument -%c\n", argv[0], c);
57 			return ':';
58 		}
59 	}
60 
61 	return c;
62 }
63 
64 #ifdef HAVE_READLINE
65 #include <readline/readline.h>
66 #include <readline/history.h>
67 #else
using_history(void)68 void using_history(void) { }
add_history(const char * string)69 void add_history(const char *string) { }
rl_bind_key(int key,void (* fun)(void))70 void rl_bind_key(int key, void (*fun)(void)) { }
rl_insert(void)71 void rl_insert(void) { }
readline(const char * prompt)72 char *readline(const char *prompt)
73 {
74 	static char line[500], *p;
75 	int n;
76 	fputs(prompt, stdout);
77 	p = fgets(line, sizeof line, stdin);
78 	if (p) {
79 		n = strlen(line);
80 		if (n > 0 && line[n-1] == '\n')
81 			line[--n] = 0;
82 		p = malloc(n+1);
83 		memcpy(p, line, n+1);
84 		return p;
85 	}
86 	return NULL;
87 }
88 #endif
89 
90 #define PS1 "> "
91 
jsB_gc(js_State * J)92 static void jsB_gc(js_State *J)
93 {
94 	int report = js_toboolean(J, 1);
95 	js_gc(J, report);
96 	js_pushundefined(J);
97 }
98 
jsB_load(js_State * J)99 static void jsB_load(js_State *J)
100 {
101 	int i, n = js_gettop(J);
102 	for (i = 1; i < n; ++i) {
103 		js_loadfile(J, js_tostring(J, i));
104 		js_pushundefined(J);
105 		js_call(J, 0);
106 		js_pop(J, 1);
107 	}
108 	js_pushundefined(J);
109 }
110 
jsB_compile(js_State * J)111 static void jsB_compile(js_State *J)
112 {
113 	const char *source = js_tostring(J, 1);
114 	const char *filename = js_isdefined(J, 2) ? js_tostring(J, 2) : "[string]";
115 	js_loadstring(J, filename, source);
116 }
117 
jsB_print(js_State * J)118 static void jsB_print(js_State *J)
119 {
120 	int i, top = js_gettop(J);
121 	for (i = 1; i < top; ++i) {
122 		const char *s = js_tostring(J, i);
123 		if (i > 1) putchar(' ');
124 		fputs(s, stdout);
125 	}
126 	putchar('\n');
127 	js_pushundefined(J);
128 }
129 
jsB_write(js_State * J)130 static void jsB_write(js_State *J)
131 {
132 	int i, top = js_gettop(J);
133 	for (i = 1; i < top; ++i) {
134 		const char *s = js_tostring(J, i);
135 		if (i > 1) putchar(' ');
136 		fputs(s, stdout);
137 	}
138 	js_pushundefined(J);
139 }
140 
jsB_read(js_State * J)141 static void jsB_read(js_State *J)
142 {
143 	const char *filename = js_tostring(J, 1);
144 	FILE *f;
145 	char *s;
146 	int n, t;
147 
148 	f = fopen(filename, "rb");
149 	if (!f) {
150 		js_error(J, "cannot open file '%s': %s", filename, strerror(errno));
151 	}
152 
153 	if (fseek(f, 0, SEEK_END) < 0) {
154 		fclose(f);
155 		js_error(J, "cannot seek in file '%s': %s", filename, strerror(errno));
156 	}
157 
158 	n = ftell(f);
159 	if (n < 0) {
160 		fclose(f);
161 		js_error(J, "cannot tell in file '%s': %s", filename, strerror(errno));
162 	}
163 
164 	if (fseek(f, 0, SEEK_SET) < 0) {
165 		fclose(f);
166 		js_error(J, "cannot seek in file '%s': %s", filename, strerror(errno));
167 	}
168 
169 	s = malloc(n + 1);
170 	if (!s) {
171 		fclose(f);
172 		js_error(J, "out of memory");
173 	}
174 
175 	t = fread(s, 1, n, f);
176 	if (t != n) {
177 		free(s);
178 		fclose(f);
179 		js_error(J, "cannot read data from file '%s': %s", filename, strerror(errno));
180 	}
181 	s[n] = 0;
182 
183 	js_pushstring(J, s);
184 	free(s);
185 	fclose(f);
186 }
187 
jsB_readline(js_State * J)188 static void jsB_readline(js_State *J)
189 {
190 	char *line = readline("");
191 	if (!line) {
192 		js_pushnull(J);
193 		return;
194 	}
195 	js_pushstring(J, line);
196 	if (*line)
197 		add_history(line);
198 	free(line);
199 }
200 
jsB_quit(js_State * J)201 static void jsB_quit(js_State *J)
202 {
203 	exit(js_tonumber(J, 1));
204 }
205 
jsB_repr(js_State * J)206 static void jsB_repr(js_State *J)
207 {
208 	js_repr(J, 1);
209 }
210 
211 static const char *require_js =
212 	"function require(name) {\n"
213 	"var cache = require.cache;\n"
214 	"if (name in cache) return cache[name];\n"
215 	"var exports = {};\n"
216 	"cache[name] = exports;\n"
217 	"Function('exports', read(name+'.js'))(exports);\n"
218 	"return exports;\n"
219 	"}\n"
220 	"require.cache = Object.create(null);\n"
221 ;
222 
223 
224 static const char *stacktrace_js =
225 	"Error.prototype.toString = function() {\n"
226 	"var s = this.name;\n"
227 	"if ('message' in this) s += ': ' + this.message;\n"
228 	"if ('stackTrace' in this) s += this.stackTrace;\n"
229 	"return s;\n"
230 	"};\n"
231 ;
232 
eval_print(js_State * J,const char * source)233 static int eval_print(js_State *J, const char *source)
234 {
235 	if (js_ploadstring(J, "[stdin]", source)) {
236 		fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
237 		js_pop(J, 1);
238 		return 1;
239 	}
240 	js_pushundefined(J);
241 	if (js_pcall(J, 0)) {
242 		fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
243 		js_pop(J, 1);
244 		return 1;
245 	}
246 	if (js_isdefined(J, -1)) {
247 		printf("%s\n", js_tryrepr(J, -1, "can't convert to string"));
248 	}
249 	js_pop(J, 1);
250 	return 0;
251 }
252 
read_stdin(void)253 static char *read_stdin(void)
254 {
255 	int n = 0;
256 	int t = 512;
257 	char *s = NULL;
258 
259 	for (;;) {
260 		char *ss = realloc(s, t);
261 		if (!ss) {
262 			free(s);
263 			fprintf(stderr, "cannot allocate storage for stdin contents\n");
264 			return NULL;
265 		}
266 		s = ss;
267 		n += fread(s + n, 1, t - n - 1, stdin);
268 		if (n < t - 1)
269 			break;
270 		t *= 2;
271 	}
272 
273 	if (ferror(stdin)) {
274 		free(s);
275 		fprintf(stderr, "error reading stdin\n");
276 		return NULL;
277 	}
278 
279 	s[n] = 0;
280 	return s;
281 }
282 
usage(void)283 static void usage(void)
284 {
285 	fprintf(stderr, "Usage: mujs [options] [script [scriptArgs*]]\n");
286 	fprintf(stderr, "\t-i: Enter interactive prompt after running code.\n");
287 	fprintf(stderr, "\t-s: Check strictness.\n");
288 	exit(1);
289 }
290 
291 int
main(int argc,char ** argv)292 main(int argc, char **argv)
293 {
294 	char *input;
295 	js_State *J;
296 	int status = 0;
297 	int strict = 0;
298 	int interactive = 0;
299 	int i, c;
300 
301 	while ((c = xgetopt(argc, argv, "is")) != -1) {
302 		switch (c) {
303 		default: usage(); break;
304 		case 'i': interactive = 1; break;
305 		case 's': strict = 1; break;
306 		}
307 	}
308 
309 	J = js_newstate(NULL, NULL, strict ? JS_STRICT : 0);
310 
311 	js_newcfunction(J, jsB_gc, "gc", 0);
312 	js_setglobal(J, "gc");
313 
314 	js_newcfunction(J, jsB_load, "load", 1);
315 	js_setglobal(J, "load");
316 
317 	js_newcfunction(J, jsB_compile, "compile", 2);
318 	js_setglobal(J, "compile");
319 
320 	js_newcfunction(J, jsB_print, "print", 0);
321 	js_setglobal(J, "print");
322 
323 	js_newcfunction(J, jsB_write, "write", 0);
324 	js_setglobal(J, "write");
325 
326 	js_newcfunction(J, jsB_read, "read", 1);
327 	js_setglobal(J, "read");
328 
329 	js_newcfunction(J, jsB_readline, "readline", 0);
330 	js_setglobal(J, "readline");
331 
332 	js_newcfunction(J, jsB_repr, "repr", 0);
333 	js_setglobal(J, "repr");
334 
335 	js_newcfunction(J, jsB_quit, "quit", 1);
336 	js_setglobal(J, "quit");
337 
338 	js_dostring(J, require_js);
339 	js_dostring(J, stacktrace_js);
340 
341 	if (xoptind == argc) {
342 		interactive = 1;
343 	} else {
344 		c = xoptind++;
345 
346 		js_newarray(J);
347 		i = 0;
348 		while (xoptind < argc) {
349 			js_pushstring(J, argv[xoptind++]);
350 			js_setindex(J, -2, i++);
351 		}
352 		js_setglobal(J, "scriptArgs");
353 
354 		if (js_dofile(J, argv[c]))
355 			status = 1;
356 	}
357 
358 	if (interactive) {
359 		printf("Welcome to MuJS %d.%d.%d.\n",
360 			JS_VERSION_MAJOR, JS_VERSION_MINOR, JS_VERSION_PATCH);
361 		if (isatty(0)) {
362 			using_history();
363 			rl_bind_key('\t', rl_insert);
364 			input = readline(PS1);
365 			while (input) {
366 				eval_print(J, input);
367 				if (*input)
368 					add_history(input);
369 				free(input);
370 				input = readline(PS1);
371 			}
372 			putchar('\n');
373 		} else {
374 			input = read_stdin();
375 			if (!input || !js_dostring(J, input))
376 				status = 1;
377 			free(input);
378 		}
379 	}
380 
381 	js_gc(J, 0);
382 	js_freestate(J);
383 
384 	return status;
385 }
386