1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5
6 #include "mujs.h"
7
8 #ifdef HAVE_READLINE
9 #include <readline/readline.h>
10 #include <readline/history.h>
11 #else
using_history(void)12 void using_history(void) { }
add_history(const char * string)13 void add_history(const char *string) { }
rl_bind_key(int key,void (* fun)(void))14 void rl_bind_key(int key, void (*fun)(void)) { }
rl_insert(void)15 void rl_insert(void) { }
readline(const char * prompt)16 char *readline(const char *prompt)
17 {
18 static char line[500], *p;
19 int n;
20 fputs(prompt, stdout);
21 p = fgets(line, sizeof line, stdin);
22 if (p) {
23 n = strlen(line);
24 if (n > 0 && line[n-1] == '\n')
25 line[--n] = 0;
26 p = malloc(n+1);
27 memcpy(p, line, n+1);
28 return p;
29 }
30 return NULL;
31 }
32 #endif
33
34 #define PS1 "> "
35
jsB_gc(js_State * J)36 static void jsB_gc(js_State *J)
37 {
38 int report = js_toboolean(J, 1);
39 js_gc(J, report);
40 js_pushundefined(J);
41 }
42
jsB_load(js_State * J)43 static void jsB_load(js_State *J)
44 {
45 const char *filename = js_tostring(J, 1);
46 int rv = js_dofile(J, filename);
47 js_pushboolean(J, !rv);
48 }
49
jsB_print(js_State * J)50 static void jsB_print(js_State *J)
51 {
52 int i, top = js_gettop(J);
53 for (i = 1; i < top; ++i) {
54 const char *s = js_tostring(J, i);
55 if (i > 1) putchar(' ');
56 fputs(s, stdout);
57 }
58 putchar('\n');
59 js_pushundefined(J);
60 }
61
jsB_write(js_State * J)62 static void jsB_write(js_State *J)
63 {
64 int i, top = js_gettop(J);
65 for (i = 1; i < top; ++i) {
66 const char *s = js_tostring(J, i);
67 if (i > 1) putchar(' ');
68 fputs(s, stdout);
69 }
70 js_pushundefined(J);
71 }
72
jsB_read(js_State * J)73 static void jsB_read(js_State *J)
74 {
75 const char *filename = js_tostring(J, 1);
76 FILE *f;
77 char *s;
78 int n, t;
79
80 f = fopen(filename, "rb");
81 if (!f) {
82 js_error(J, "cannot open file: '%s'", filename);
83 }
84
85 if (fseek(f, 0, SEEK_END) < 0) {
86 fclose(f);
87 js_error(J, "cannot seek in file: '%s'", filename);
88 }
89
90 n = ftell(f);
91 if (n < 0) {
92 fclose(f);
93 js_error(J, "cannot tell in file: '%s'", filename);
94 }
95
96 if (fseek(f, 0, SEEK_SET) < 0) {
97 fclose(f);
98 js_error(J, "cannot seek in file: '%s'", filename);
99 }
100
101 s = malloc(n + 1);
102 if (!s) {
103 fclose(f);
104 js_error(J, "cannot allocate storage for file contents: '%s'", filename);
105 }
106
107 t = fread(s, 1, n, f);
108 if (t != n) {
109 free(s);
110 fclose(f);
111 js_error(J, "cannot read data from file: '%s'", filename);
112 }
113 s[n] = 0;
114
115 js_pushstring(J, s);
116 free(s);
117 fclose(f);
118 }
119
jsB_readline(js_State * J)120 static void jsB_readline(js_State *J)
121 {
122 char *line = readline("");
123 if (!line)
124 js_error(J, "cannot read line from stdin");
125 js_pushstring(J, line);
126 if (*line)
127 add_history(line);
128 free(line);
129 }
130
jsB_quit(js_State * J)131 static void jsB_quit(js_State *J)
132 {
133 exit(js_tonumber(J, 1));
134 }
135
136 static const char *require_js =
137 "function require(name) {\n"
138 "var cache = require.cache;\n"
139 "if (name in cache) return cache[name];\n"
140 "var exports = {};\n"
141 "cache[name] = exports;\n"
142 "Function('exports', read(name+'.js'))(exports);\n"
143 "return exports;\n"
144 "}\n"
145 "require.cache = Object.create(null);\n"
146 ;
147
148 static const char *stacktrace_js =
149 "Error.prototype.toString = function() {\n"
150 "if (this.stackTrace) return this.name + ': ' + this.message + this.stackTrace;\n"
151 "return this.name + ': ' + this.message;\n"
152 "};\n"
153 ;
154
eval_print(js_State * J,const char * source)155 static int eval_print(js_State *J, const char *source)
156 {
157 if (js_ploadstring(J, "[string]", source)) {
158 fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
159 js_pop(J, 1);
160 return 1;
161 }
162 js_pushundefined(J);
163 if (js_pcall(J, 0)) {
164 fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
165 js_pop(J, 1);
166 return 1;
167 }
168 if (js_isdefined(J, -1))
169 printf("%s\n", js_trystring(J, -1, "can't convert to string"));
170 js_pop(J, 1);
171 return 0;
172 }
173
read_stdin(void)174 static char *read_stdin(void)
175 {
176 int n = 0;
177 int t = 512;
178 char *s = NULL;
179
180 for (;;) {
181 char *ss = realloc(s, t);
182 if (!ss) {
183 free(s);
184 fprintf(stderr, "cannot allocate storage for stdin contents\n");
185 return NULL;
186 }
187 s = ss;
188 n += fread(s + n, 1, t - n - 1, stdin);
189 if (n < t - 1)
190 break;
191 t *= 2;
192 }
193
194 if (ferror(stdin)) {
195 free(s);
196 fprintf(stderr, "error reading stdin\n");
197 return NULL;
198 }
199
200 s[n] = 0;
201 return s;
202 }
203
204 int
main(int argc,char ** argv)205 main(int argc, char **argv)
206 {
207 char *input;
208 js_State *J;
209 int i, status = 0;
210
211 J = js_newstate(NULL, NULL, JS_STRICT);
212
213 js_newcfunction(J, jsB_gc, "gc", 0);
214 js_setglobal(J, "gc");
215
216 js_newcfunction(J, jsB_load, "load", 1);
217 js_setglobal(J, "load");
218
219 js_newcfunction(J, jsB_print, "print", 0);
220 js_setglobal(J, "print");
221
222 js_newcfunction(J, jsB_write, "write", 0);
223 js_setglobal(J, "write");
224
225 js_newcfunction(J, jsB_read, "read", 1);
226 js_setglobal(J, "read");
227
228 js_newcfunction(J, jsB_readline, "readline", 0);
229 js_setglobal(J, "readline");
230
231 js_newcfunction(J, jsB_quit, "quit", 1);
232 js_setglobal(J, "quit");
233
234 js_dostring(J, require_js);
235 js_dostring(J, stacktrace_js);
236
237 if (argc > 1) {
238 for (i = 1; i < argc; ++i)
239 if (js_dofile(J, argv[i]))
240 status = 1;
241 } else {
242 if (isatty(0)) {
243 using_history();
244 rl_bind_key('\t', rl_insert);
245 input = readline(PS1);
246 while (input) {
247 eval_print(J, input);
248 if (*input)
249 add_history(input);
250 free(input);
251 input = readline(PS1);
252 }
253 putchar('\n');
254 } else {
255 input = read_stdin();
256 if (!input || !js_dostring(J, input))
257 status = 1;
258 free(input);
259 }
260 }
261
262 js_gc(J, 0);
263 js_freestate(J);
264
265 return status;
266 }
267