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