1 | // -*- c-basic-offset: 2 -*- |
2 | /* |
3 | * This file is part of the KDE libraries |
4 | * Copyright (C) 2006 Harri Porten (porten@kde.org) |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #include "JSLock.h" |
24 | #include "interpreter.h" |
25 | #include "object.h" |
26 | #include "package.h" |
27 | #include "function.h" |
28 | |
29 | #include <cstring> |
30 | #include <stdlib.h> |
31 | #include <stdio.h> |
32 | #include <sys/stat.h> |
33 | #include <fcntl.h> |
34 | |
35 | #if PLATFORM(WIN_OS) |
36 | # include <windows.h> |
37 | # include <io.h> |
38 | #else |
39 | # include <unistd.h> |
40 | #endif |
41 | |
42 | #include <kdeversion.h> |
43 | |
44 | enum ExitCode { ErrorNone, |
45 | ErrorUnknownSwitch, |
46 | ErrorMissingArg, |
47 | ErrorReadFile, |
48 | ErrorEval |
49 | }; |
50 | |
51 | using std::strcmp; |
52 | |
53 | using namespace KJS; |
54 | |
55 | static void printUsage(const char *app) |
56 | { |
57 | fprintf(stderr, |
58 | "Usage: %s\n" |
59 | " [ -h | -help | --help ]\n" |
60 | " [ -e <statement> | <script> ]\n" |
61 | " [-v | -version | --version]\n" , |
62 | app); |
63 | } |
64 | |
65 | static UString readFile(const char* fileName) |
66 | { |
67 | int fd = open(fileName, O_RDONLY); |
68 | if (fd < 0) { |
69 | fprintf(stderr, "Error opening %s" , fileName); |
70 | return UString(); |
71 | } |
72 | struct stat buf; |
73 | if (fstat(fd, &buf) == -1) { |
74 | fprintf(stderr, "Error stat'ing %s" , fileName); |
75 | close(fd); |
76 | return UString(); |
77 | } |
78 | int siz = buf.st_size; |
79 | char* c = new char[siz + 1]; |
80 | int dataRead = read(fd, c, siz); |
81 | if (dataRead == -1) { |
82 | fprintf(stderr, "Error reading from %s" , fileName); |
83 | delete[] c; |
84 | close(fd); |
85 | return UString(); |
86 | } |
87 | c[dataRead] = '\0'; |
88 | UString s = c; |
89 | delete[] c; |
90 | return s; |
91 | } |
92 | |
93 | static ExitCode evaluateFile(Interpreter *interp, const char *fileName); |
94 | |
95 | class GlobalImp : public JSGlobalObject { |
96 | public: |
97 | virtual UString className() const { return "global" ; } |
98 | }; |
99 | |
100 | class TestFunctionImp : public JSObject { |
101 | public: |
102 | TestFunctionImp(int i, int length); |
103 | virtual bool implementsCall() const { return true; } |
104 | virtual JSValue* callAsFunction(ExecState* exec, |
105 | JSObject* thisObj, const List &args); |
106 | enum { Print, Quit, Load, GC }; |
107 | |
108 | private: |
109 | int id; |
110 | }; |
111 | |
112 | TestFunctionImp::TestFunctionImp(int i, int length) |
113 | : JSObject(), id(i) |
114 | { |
115 | putDirect(Identifier("length" ), length, DontDelete | ReadOnly | DontEnum); |
116 | } |
117 | |
118 | JSValue* TestFunctionImp::callAsFunction(ExecState* exec, |
119 | JSObject* /* thisObj */, |
120 | const List &args) |
121 | { |
122 | switch (id) { |
123 | case Print: |
124 | printf("%s\n" , args[0]->toString(exec).UTF8String().c_str()); |
125 | return jsUndefined(); |
126 | case Quit: |
127 | exit(0); |
128 | case GC: |
129 | while (Interpreter::collect()) {} |
130 | break; |
131 | case Load: |
132 | evaluateFile(exec->dynamicInterpreter(), args[0]->toString(exec).UTF8String().c_str()); |
133 | break; |
134 | default: |
135 | abort(); |
136 | } |
137 | return jsUndefined(); |
138 | } |
139 | |
140 | static ExitCode evaluateString(Interpreter *interp, const char *fileName, |
141 | const UString& code, |
142 | bool printResult = false) |
143 | { |
144 | ExecState *exec = interp->globalExec(); |
145 | |
146 | Completion res = interp->evaluate(fileName, 0, code); |
147 | |
148 | if (res.complType() == Throw) { |
149 | CString msg = res.value()->toString(exec).UTF8String(); |
150 | JSObject* resObj = res.value()->toObject(exec); |
151 | CString message = resObj->toString(exec).UTF8String(); |
152 | int line = resObj->toObject(exec)->get(exec, "line" )->toUInt32(exec); |
153 | |
154 | if (fileName) |
155 | fprintf(stderr, "%s (line %d): " , fileName, line); |
156 | fprintf(stderr, "%s\n" , msg.c_str()); |
157 | return ErrorEval; |
158 | } else if (printResult) { |
159 | if (res.isValueCompletion() && !res.value()->isUndefined()) { |
160 | CString s8 = res.value()->toString(exec).UTF8String(); |
161 | if (s8.size() != 0) |
162 | fprintf(stdout, "%s\n" , s8.c_str()); |
163 | } |
164 | } |
165 | |
166 | return ErrorNone; |
167 | } |
168 | |
169 | static ExitCode evaluateFile(Interpreter *interp, const char *fileName) |
170 | { |
171 | UString code = readFile(fileName); |
172 | if (code.isNull()) |
173 | return ErrorReadFile; |
174 | |
175 | return evaluateString(interp, fileName, code); |
176 | } |
177 | |
178 | // primitive readline-like function |
179 | static char *readLine(const char *prompt) |
180 | { |
181 | if (prompt) |
182 | fprintf(stdout, "%s" , prompt); |
183 | |
184 | const int bsize = 2 << 10; |
185 | char *buffer = static_cast<char*>(malloc(bsize)); |
186 | char *s = fgets(buffer, bsize, stdin); |
187 | if (s == 0) { |
188 | // EOF |
189 | fprintf(stdout, "\n" ); |
190 | free(buffer); |
191 | } |
192 | return s; |
193 | } |
194 | |
195 | static ExitCode evaluateInteractive(Interpreter *interp) |
196 | { |
197 | char *line; |
198 | while ((line = readLine("JS> " )) != 0) { |
199 | UString code(line); |
200 | free(line); |
201 | evaluateString(interp, 0, code, true); |
202 | } |
203 | |
204 | return ErrorNone; |
205 | } |
206 | |
207 | static ExitCode parseArgs(int argc, char** argv) |
208 | { |
209 | JSLock lock; |
210 | |
211 | GlobalImp* global = new GlobalImp(); |
212 | |
213 | // create interpreter |
214 | RefPtr<Interpreter> interp = new Interpreter(global); |
215 | |
216 | // add some global extension functions |
217 | ExecState *gexec = interp->globalExec(); |
218 | global->put(gexec, "print" , |
219 | new TestFunctionImp(TestFunctionImp::Print, 1)); |
220 | global->put(gexec, "quit" , |
221 | new TestFunctionImp(TestFunctionImp::Quit, 0)); |
222 | global->put(gexec, "load" , |
223 | new TestFunctionImp(TestFunctionImp::Load, 1)); |
224 | global->put(gexec, "gc" , |
225 | new TestFunctionImp(TestFunctionImp::GC, 0)); |
226 | |
227 | // enable package support |
228 | StandardGlobalPackage package; |
229 | interp->setGlobalPackage(&package); |
230 | |
231 | const char *script = 0, *command = 0; |
232 | int ai = 1; |
233 | bool ranOtherScript = false; |
234 | for (ai = 1; ai < argc; ++ai) { |
235 | const char* a = argv[ai]; |
236 | if (strcmp(a, "-v" ) == 0 || strcmp(a, "-version" ) == 0 || |
237 | strcmp(a, "--version" ) == 0) { |
238 | printf("KDE: %s\n" , KDE_VERSION_STRING); |
239 | return ErrorNone; |
240 | } else if (strcmp(a, "-h" ) == 0 || strcmp(a, "-help" ) == 0 || |
241 | strcmp(a, "--help" ) == 0) { |
242 | printUsage(argv[0]); |
243 | return ErrorNone; |
244 | } else if (strcmp(a, "-e" ) == 0) { |
245 | ++ai; |
246 | if (argc <= ai) { |
247 | fprintf(stderr, "Missing -e argument.\n" ); |
248 | return ErrorMissingArg; |
249 | } |
250 | command = argv[ai]; |
251 | ++ai; |
252 | break; |
253 | } else if (strcmp(a, "-f" ) == 0) { // Compatibility mode, for SunSpider |
254 | ++ai; |
255 | if (argc <= ai) { |
256 | fprintf(stderr, "Missing -f argument.\n" ); |
257 | return ErrorMissingArg; |
258 | } |
259 | ExitCode result = evaluateFile(interp.get(), argv[ai]); |
260 | if (result != ErrorNone) |
261 | return result; |
262 | ranOtherScript = true; |
263 | } else if (a[0] == '-') { |
264 | fprintf(stderr, "Unknown switch %s.\n" , a); |
265 | return ErrorUnknownSwitch; |
266 | } else { |
267 | script = a; |
268 | ++ai; |
269 | break; |
270 | } |
271 | } |
272 | |
273 | // ### |
274 | if (argc > ai) |
275 | fprintf(stderr, "Warning: ignoring extra arguments\n" ); |
276 | |
277 | if (script) { |
278 | return evaluateFile(interp.get(), script); |
279 | } else if (command) { |
280 | return evaluateString(interp.get(), "(eval)" , command); |
281 | } else if (!ranOtherScript) { |
282 | return evaluateInteractive(interp.get()); |
283 | } |
284 | return ErrorNone; |
285 | } |
286 | |
287 | int main(int argc, char** argv) |
288 | { |
289 | return int(parseArgs(argc, argv)); |
290 | } |
291 | |