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