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