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
44enum ExitCode { ErrorNone,
45 ErrorUnknownSwitch,
46 ErrorMissingArg,
47 ErrorReadFile,
48 ErrorEval
49};
50
51using std::strcmp;
52
53using namespace KJS;
54
55static 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
65static 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
93static ExitCode evaluateFile(Interpreter *interp, const char *fileName);
94
95class GlobalImp : public JSGlobalObject {
96public:
97 virtual UString className() const { return "global"; }
98};
99
100class TestFunctionImp : public JSObject {
101public:
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
108private:
109 int id;
110};
111
112TestFunctionImp::TestFunctionImp(int i, int length)
113 : JSObject(), id(i)
114{
115 putDirect(Identifier("length"), length, DontDelete | ReadOnly | DontEnum);
116}
117
118JSValue* 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
140static 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
169static 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
179static 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
195static 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
207static 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
287int main(int argc, char** argv)
288{
289 return int(parseArgs(argc, argv));
290}
291