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