00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <ktranscript_p.h>
00020
00021 #include <config.h>
00022
00023 #include <kdecore_export.h>
00024 #include <kglobal.h>
00025
00026
00027
00028 #include <kjs/value.h>
00029 #include <kjs/object.h>
00030 #include <kjs/lookup.h>
00031 #include <kjs/function.h>
00032 #include <kjs/interpreter.h>
00033 #include <kjs/string_object.h>
00034 #include <kjs/error_object.h>
00035
00036 #include <QVariant>
00037 #include <QStringList>
00038 #include <QList>
00039 #include <QHash>
00040 #include <QFile>
00041 #include <QIODevice>
00042 #include <QTextStream>
00043 #include <QRegExp>
00044 #include <qendian.h>
00045
00046 using namespace KJS;
00047
00048 class KTranscriptImp;
00049 class Scriptface;
00050
00051 typedef QHash<QString, QString> TsConfigGroup;
00052 typedef QHash<QString, TsConfigGroup> TsConfig;
00053
00054
00055 class KTranscriptImp : public KTranscript
00056 {
00057 public:
00058
00059 KTranscriptImp ();
00060 ~KTranscriptImp ();
00061
00062 QString eval (const QList<QVariant> &argv,
00063 const QString &lang,
00064 const QString &lscr,
00065 const QString &msgctxt,
00066 const QHash<QString, QString> &dynctxt,
00067 const QString &msgid,
00068 const QStringList &subs,
00069 const QList<QVariant> &vals,
00070 const QString &final,
00071 QList<QStringList> &mods,
00072 QString &error,
00073 bool &fallback);
00074
00075 QStringList postCalls (const QString &lang);
00076
00077
00078 QString currentModulePath;
00079
00080 private:
00081
00082 void loadModules (const QList<QStringList> &mods, QString &error);
00083 void setupInterpreter (const QString &lang);
00084
00085 TsConfig config;
00086
00087 QHash<QString, Scriptface*> m_sface;
00088 };
00089
00090
00091 class Scriptface : public JSObject
00092 {
00093 public:
00094 Scriptface (ExecState *exec, const TsConfigGroup &config);
00095 ~Scriptface ();
00096
00097
00098 JSValue *loadf (ExecState *exec, const List &fnames);
00099 JSValue *setcallf (ExecState *exec, JSValue *name,
00100 JSValue *func, JSValue *fval);
00101 JSValue *hascallf (ExecState *exec, JSValue *name);
00102 JSValue *acallf (ExecState *exec, const List &argv);
00103 JSValue *setcallForallf (ExecState *exec, JSValue *name,
00104 JSValue *func, JSValue *fval);
00105 JSValue *fallbackf (ExecState *exec);
00106 JSValue *nsubsf (ExecState *exec);
00107 JSValue *subsf (ExecState *exec, JSValue *index);
00108 JSValue *valsf (ExecState *exec, JSValue *index);
00109 JSValue *msgctxtf (ExecState *exec);
00110 JSValue *dynctxtf (ExecState *exec, JSValue *key);
00111 JSValue *msgidf (ExecState *exec);
00112 JSValue *msgkeyf (ExecState *exec);
00113 JSValue *msgstrff (ExecState *exec);
00114 JSValue *dbgputsf (ExecState *exec, JSValue *str);
00115 JSValue *lscrf (ExecState *exec);
00116 JSValue *normKeyf (ExecState *exec, JSValue *phrase);
00117 JSValue *loadPropsf (ExecState *exec, const List &fnames);
00118 JSValue *getPropf (ExecState *exec, JSValue *phrase, JSValue *prop);
00119 JSValue *setPropf (ExecState *exec, JSValue *phrase, JSValue *prop, JSValue *value);
00120 JSValue *toUpperFirstf (ExecState *exec, JSValue *str, JSValue *nalt);
00121 JSValue *getConfStringf (ExecState *exec, JSValue *key, JSValue *dval);
00122 JSValue *getConfBoolf (ExecState *exec, JSValue *key, JSValue *dval);
00123 JSValue *getConfNumberf (ExecState *exec, JSValue *key, JSValue *dval);
00124
00125 enum {
00126 Load,
00127 Setcall,
00128 Hascall,
00129 Acall,
00130 SetcallForall,
00131 Fallback,
00132 Nsubs,
00133 Subs,
00134 Vals,
00135 Msgctxt,
00136 Dynctxt,
00137 Msgid,
00138 Msgkey,
00139 Msgstrf,
00140 Dbgputs,
00141 Lscr,
00142 NormKey,
00143 LoadProps,
00144 GetProp,
00145 SetProp,
00146 ToUpperFirst,
00147 GetConfString,
00148 GetConfBool,
00149 GetConfNumber
00150 };
00151
00152
00153 QString loadProps_bin (const QString &fpath);
00154 QString loadProps_text (const QString &fpath);
00155
00156
00157 bool getOwnPropertySlot (ExecState *exec, const Identifier& propertyName, PropertySlot& slot);
00158 JSValue *getValueProperty (ExecState *exec, int token) const;
00159 void put (ExecState *exec, const Identifier &propertyName, JSValue *value, int attr);
00160 void putValueProperty (ExecState *exec, int token, JSValue *value, int attr);
00161 const ClassInfo* classInfo() const { return &info; }
00162
00163 static const ClassInfo info;
00164
00165
00166
00167 Interpreter *jsi;
00168
00169
00170 const QString *msgctxt;
00171 const QHash<QString, QString> *dynctxt;
00172 const QString *msgid;
00173 const QStringList *subs;
00174 const QList<QVariant> *vals;
00175 const QString *final;
00176 const QString *lscr;
00177
00178
00179 bool *fallback;
00180
00181
00182 QHash<QString, JSObject*> funcs;
00183 QHash<QString, JSValue*> fvals;
00184 QHash<QString, QString> fpaths;
00185
00186
00187 QList<QString> nameForalls;
00188
00189
00190
00191
00192 QHash<QByteArray, QHash<QByteArray, QByteArray> > phraseProps;
00193
00194
00195 TsConfigGroup config;
00196 };
00197
00198
00199
00200 #define DBGP "KTranscript: "
00201 void dbgout (const QString &str) {
00202 #ifndef NDEBUG
00203 fprintf(stderr, DBGP"%s\n", str.toLocal8Bit().data());
00204 #else
00205 Q_UNUSED(str);
00206 #endif
00207 }
00208 template <typename T1>
00209 void dbgout (const QString &str, const T1 &a1) {
00210 #ifndef NDEBUG
00211 fprintf(stderr, DBGP"%s\n", str.arg(a1).toLocal8Bit().data());
00212 #else
00213 Q_UNUSED(str); Q_UNUSED(a1);
00214 #endif
00215 }
00216 template <typename T1, typename T2>
00217 void dbgout (const QString &str, const T1 &a1, const T2 &a2) {
00218 #ifndef NDEBUG
00219 fprintf(stderr, DBGP"%s\n", str.arg(a1).arg(a2).toLocal8Bit().data());
00220 #else
00221 Q_UNUSED(str); Q_UNUSED(a1); Q_UNUSED(a2);
00222 #endif
00223 }
00224 template <typename T1, typename T2, typename T3>
00225 void dbgout (const QString &str, const T1 &a1, const T2 &a2, const T3 &a3) {
00226 #ifndef NDEBUG
00227 fprintf(stderr, DBGP"%s\n", str.arg(a1).arg(a2).arg(a3).toLocal8Bit().data());
00228 #else
00229 Q_UNUSED(str); Q_UNUSED(a1); Q_UNUSED(a2); Q_UNUSED(a3);
00230 #endif
00231 }
00232
00233
00234
00235
00236 UString::UString(const QString &d)
00237 {
00238 unsigned int len = d.length();
00239 UChar *dat = static_cast<UChar*>(fastMalloc(sizeof(UChar) * len));
00240 memcpy(dat, d.unicode(), len * sizeof(UChar));
00241 m_rep = UString::Rep::create(dat, len);
00242 }
00243 QString UString::qstring() const
00244 {
00245 return QString((QChar*) data(), size());
00246 }
00247
00248
00249
00250 QString expt2str (ExecState *exec)
00251 {
00252 JSValue *expt = exec->exception();
00253 if ( expt->isObject()
00254 && expt->getObject()->hasProperty(exec, "message"))
00255 {
00256 JSValue *msg = expt->getObject()->get(exec, "message");
00257 return QString("Error: %1").arg(msg->getString().qstring());
00258 }
00259 else
00260 {
00261 QString strexpt = exec->exception()->toString(exec).qstring();
00262 return QString("Caught exception: %1").arg(strexpt);
00263 }
00264 }
00265
00266
00267
00268
00269 int countLines (const QString &s, int p)
00270 {
00271 int n = 1;
00272 int len = s.length();
00273 for (int i = 0; i < p && i < len; ++i) {
00274 if (s[i] == '\n') {
00275 ++n;
00276 }
00277 }
00278 return n;
00279 }
00280
00281
00282
00283 QByteArray normKeystr (const QString &raw)
00284 {
00285
00286
00287
00288
00289 QString key = raw;
00290
00291
00292 int len = key.length();
00293 QString nkey;
00294 for (int i = 0; i < len; ++i) {
00295 QChar c = key[i];
00296 if (!c.isSpace()) {
00297 nkey.append(c);
00298 }
00299 }
00300 key = nkey;
00301
00302
00303 key.remove('&');
00304
00305
00306 key = key.toLower();
00307
00308 return key.toUtf8();
00309 }
00310
00311
00312
00313
00314
00315 QString trimSmart (const QString &raw)
00316 {
00317
00318
00319
00320
00321
00322 int len = raw.length();
00323
00324 int is = 0;
00325 while (is < len && raw[is].isSpace() && raw[is] != '\n') {
00326 ++is;
00327 }
00328 if (is >= len || raw[is] != '\n') {
00329 is = -1;
00330 }
00331
00332 int ie = len - 1;
00333 while (ie >= 0 && raw[ie].isSpace() && raw[ie] != '\n') {
00334 --ie;
00335 }
00336 if (ie < 0 || raw[ie] != '\n') {
00337 ie = len;
00338 }
00339
00340 return raw.mid(is + 1, ie - is - 1);
00341 }
00342
00343
00344
00345 JSValue *variantToJsValue (const QVariant &val)
00346 {
00347 QVariant::Type vtype = val.type();
00348 if (vtype == QVariant::String)
00349 return jsString(val.toString());
00350 else if ( vtype == QVariant::Double \
00351 || vtype == QVariant::Int || vtype == QVariant::UInt \
00352 || vtype == QVariant::LongLong || vtype == QVariant::ULongLong)
00353 return jsNumber(val.toDouble());
00354 else
00355 return jsUndefined();
00356 }
00357
00358
00359
00360
00361
00362 TsConfig readConfig (const QString &fname)
00363 {
00364 TsConfig config;
00365
00366 TsConfig::iterator configGroup;
00367 configGroup = config.insert(QString(), TsConfigGroup());
00368
00369 QFile file(fname);
00370 if (!file.open(QIODevice::ReadOnly)) {
00371 return config;
00372 }
00373 QTextStream stream(&file);
00374 stream.setCodec("UTF-8");
00375 while (!stream.atEnd()) {
00376 QString line = stream.readLine();
00377 int p1, p2;
00378
00379
00380 p1 = line.indexOf('#');
00381 if (p1 >= 0) {
00382 line = line.left(p1);
00383 }
00384 line = line.trimmed();
00385 if (line.isEmpty()) {
00386 continue;
00387 }
00388
00389 if (line[0] == '[') {
00390
00391 p1 = 0;
00392 p2 = line.indexOf(']', p1 + 1);
00393 if (p2 < 0) {
00394 continue;
00395 }
00396 QString group = line.mid(p1 + 1, p2 - p1 - 1).trimmed();
00397 configGroup = config.find(group);
00398 if (configGroup == config.end()) {
00399
00400 configGroup = config.insert(group, TsConfigGroup());
00401 }
00402 } else {
00403
00404 p1 = line.indexOf('=');
00405 if (p1 < 0) {
00406 continue;
00407 }
00408 QString field = line.left(p1).trimmed();
00409 QString value = line.mid(p1 + 1).trimmed();
00410 if (!field.isEmpty()) {
00411 (*configGroup)[field] = value;
00412 }
00413 }
00414 }
00415 file.close();
00416
00417 return config;
00418 }
00419
00420
00421
00422 K_GLOBAL_STATIC(KTranscriptImp, globalKTI)
00423 extern "C"
00424 {
00425 KDE_EXPORT KTranscript *load_transcript ()
00426 {
00427 return globalKTI;
00428 }
00429 }
00430
00431
00432
00433
00434 KTranscriptImp::KTranscriptImp ()
00435 {
00436
00437 QString homeDir = qgetenv("HOME");
00438 QString tsConfigFile = ".transcriptrc";
00439 QString tsConfigPath = homeDir + '/' + tsConfigFile;
00440 config = readConfig(tsConfigPath);
00441 }
00442
00443 KTranscriptImp::~KTranscriptImp ()
00444 {
00445
00446
00447
00448
00449 }
00450
00451 QString KTranscriptImp::eval (const QList<QVariant> &argv,
00452 const QString &lang,
00453 const QString &lscr,
00454 const QString &msgctxt,
00455 const QHash<QString, QString> &dynctxt,
00456 const QString &msgid,
00457 const QStringList &subs,
00458 const QList<QVariant> &vals,
00459 const QString &final,
00460 QList<QStringList> &mods,
00461 QString &error,
00462 bool &fallback)
00463 {
00464
00465
00466 error.clear();
00467 fallback = false;
00468
00469 #if 0
00470
00471
00472
00473
00474 if (geteuid() == 0 && getuid() != 0)
00475 {
00476
00477
00478
00479 error = "Security block: trying to execute a script in suid environment.";
00480 return QString();
00481 }
00482 #endif
00483
00484
00485 if (!mods.isEmpty())
00486 {
00487 loadModules(mods, error);
00488 mods.clear();
00489 if (!error.isEmpty())
00490 return QString();
00491 }
00492
00493
00494
00495
00496
00497 if (!m_sface.contains(lang))
00498 setupInterpreter(lang);
00499
00500
00501 Scriptface *sface = m_sface[lang];
00502 ExecState *exec = sface->jsi->globalExec();
00503 JSObject *gobj = sface->jsi->globalObject();
00504
00505
00506 sface->msgctxt = &msgctxt;
00507 sface->dynctxt = &dynctxt;
00508 sface->msgid = &msgid;
00509 sface->subs = &subs;
00510 sface->vals = &vals;
00511 sface->final = &final;
00512 sface->fallback = &fallback;
00513 sface->lscr = 𝓁
00514
00515
00516 int argc = argv.size();
00517 if (argc < 1)
00518 {
00519
00520
00521
00522 return QString();
00523 }
00524 QString funcName = argv[0].toString();
00525 if (!sface->funcs.contains(funcName))
00526 {
00527 error = QString("Unregistered call to '%1'.").arg(funcName);
00528 return QString();
00529 }
00530 JSObject *func = sface->funcs[funcName];
00531 JSValue *fval = sface->fvals[funcName];
00532
00533
00534
00535 currentModulePath = sface->fpaths[funcName];
00536
00537
00538 List arglist;
00539 for (int i = 1; i < argc; ++i)
00540 arglist.append(variantToJsValue(argv[i]));
00541 JSValue *val;
00542 if (fval->isObject())
00543 val = func->callAsFunction(exec, fval->getObject(), arglist);
00544 else
00545 val = func->callAsFunction(exec, gobj, arglist);
00546
00547 if (fallback)
00548
00549 {
00550
00551 if (exec->hadException())
00552 exec->clearException();
00553
00554 return QString();
00555 }
00556 else if (!exec->hadException())
00557
00558 {
00559 if (val->isString())
00560
00561 {
00562 return val->getString().qstring();
00563 }
00564 else
00565
00566 {
00567 QString strval = val->toString(exec).qstring();
00568 error = QString("Non-string return value: %1").arg(strval);
00569 return QString();
00570 }
00571 }
00572 else
00573
00574 {
00575 error = expt2str(exec);
00576
00577 exec->clearException();
00578
00579 return QString();
00580 }
00581 }
00582
00583 QStringList KTranscriptImp::postCalls (const QString &lang)
00584 {
00585
00586
00587 if (!m_sface.contains(lang))
00588 return QStringList();
00589
00590
00591 Scriptface *sface = m_sface[lang];
00592
00593 return sface->nameForalls;
00594 }
00595
00596 void KTranscriptImp::loadModules (const QList<QStringList> &mods,
00597 QString &error)
00598 {
00599 QList<QString> modErrors;
00600
00601 foreach (const QStringList &mod, mods)
00602 {
00603 QString mpath = mod[0];
00604 QString mlang = mod[1];
00605
00606
00607 if (!m_sface.contains(mlang))
00608 setupInterpreter(mlang);
00609
00610
00611
00612 int posls = mpath.lastIndexOf('/');
00613 if (posls < 1)
00614 {
00615 modErrors.append(QString("Funny module path '%1', skipping.")
00616 .arg(mpath));
00617 continue;
00618 }
00619 currentModulePath = mpath.left(posls);
00620 QString fname = mpath.mid(posls + 1);
00621
00622 fname = fname.left(fname.lastIndexOf('.'));
00623
00624
00625 ExecState *exec = m_sface[mlang]->jsi->globalExec();
00626 List alist;
00627 alist.append(jsString(fname));
00628
00629 m_sface[mlang]->loadf(exec, alist);
00630
00631
00632 if (exec->hadException())
00633 {
00634 modErrors.append(expt2str(exec));
00635 exec->clearException();
00636 }
00637 }
00638
00639
00640 currentModulePath.clear();
00641
00642 foreach (const QString &merr, modErrors)
00643 error.append(merr + '\n');
00644 }
00645
00646 KJS_QT_UNICODE_IMPL
00647
00648 #define SFNAME "Ts"
00649 void KTranscriptImp::setupInterpreter (const QString &lang)
00650 {
00651
00652 Interpreter *jsi = new Interpreter;
00653 KJS_QT_UNICODE_SET;
00654 jsi->initGlobalObject();
00655 jsi->ref();
00656
00657
00658
00659
00660 Scriptface *sface = new Scriptface(jsi->globalExec(), config[lang]);
00661 jsi->globalObject()->put(jsi->globalExec(), SFNAME, sface,
00662 DontDelete|ReadOnly);
00663
00664
00665 sface->jsi = jsi;
00666 m_sface[lang] = sface;
00667
00668
00669 }
00670
00671
00672
00673 #include "ktranscript.lut.h"
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708 KJS_DEFINE_PROTOTYPE(ScriptfaceProto)
00709 KJS_IMPLEMENT_PROTOFUNC(ScriptfaceProtoFunc)
00710 KJS_IMPLEMENT_PROTOTYPE("Scriptface", ScriptfaceProto, ScriptfaceProtoFunc)
00711
00712 const ClassInfo Scriptface::info = {"Scriptface", 0, &ScriptfaceTable, 0};
00713
00714 Scriptface::Scriptface (ExecState *exec, const TsConfigGroup &config_)
00715 : JSObject(ScriptfaceProto::self(exec)), fallback(NULL), config(config_)
00716 {}
00717
00718 Scriptface::~Scriptface ()
00719 {}
00720
00721 bool Scriptface::getOwnPropertySlot (ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
00722 {
00723 return getStaticValueSlot<Scriptface, JSObject>(exec, &ScriptfaceTable, this, propertyName, slot);
00724 }
00725
00726 JSValue *Scriptface::getValueProperty (ExecState * , int token) const
00727 {
00728 switch (token) {
00729 default:
00730 dbgout("Scriptface::getValueProperty: Unknown property id %1", token);
00731 }
00732 return jsUndefined();
00733 }
00734
00735 void Scriptface::put (ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
00736 {
00737 lookupPut<Scriptface, JSObject>(exec, propertyName, value, attr, &ScriptfaceTable, this);
00738 }
00739
00740 void Scriptface::putValueProperty (ExecState * , int token, JSValue * , int )
00741 {
00742 switch(token) {
00743 default:
00744 dbgout("Scriptface::putValueProperty: Unknown property id %1", token);
00745 }
00746 }
00747
00748 #define CALLARG(i) (args.size() > i ? args[i] : jsNull())
00749 JSValue *ScriptfaceProtoFunc::callAsFunction (ExecState *exec, JSObject *thisObj, const List &args)
00750 {
00751 if (!thisObj->inherits(&Scriptface::info)) {
00752 return throwError(exec, TypeError);
00753 }
00754 Scriptface *obj = static_cast<Scriptface*>(thisObj);
00755 switch (id) {
00756 case Scriptface::Load:
00757 return obj->loadf(exec, args);
00758 case Scriptface::Setcall:
00759 return obj->setcallf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
00760 case Scriptface::Hascall:
00761 return obj->hascallf(exec, CALLARG(0));
00762 case Scriptface::Acall:
00763 return obj->acallf(exec, args);
00764 case Scriptface::SetcallForall:
00765 return obj->setcallForallf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
00766 case Scriptface::Fallback:
00767 return obj->fallbackf(exec);
00768 case Scriptface::Nsubs:
00769 return obj->nsubsf(exec);
00770 case Scriptface::Subs:
00771 return obj->subsf(exec, CALLARG(0));
00772 case Scriptface::Vals:
00773 return obj->valsf(exec, CALLARG(0));
00774 case Scriptface::Msgctxt:
00775 return obj->msgctxtf(exec);
00776 case Scriptface::Dynctxt:
00777 return obj->dynctxtf(exec, CALLARG(0));
00778 case Scriptface::Msgid:
00779 return obj->msgidf(exec);
00780 case Scriptface::Msgkey:
00781 return obj->msgkeyf(exec);
00782 case Scriptface::Msgstrf:
00783 return obj->msgstrff(exec);
00784 case Scriptface::Dbgputs:
00785 return obj->dbgputsf(exec, CALLARG(0));
00786 case Scriptface::Lscr:
00787 return obj->lscrf(exec);
00788 case Scriptface::NormKey:
00789 return obj->normKeyf(exec, CALLARG(0));
00790 case Scriptface::LoadProps:
00791 return obj->loadPropsf(exec, args);
00792 case Scriptface::GetProp:
00793 return obj->getPropf(exec, CALLARG(0), CALLARG(1));
00794 case Scriptface::SetProp:
00795 return obj->setPropf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
00796 case Scriptface::ToUpperFirst:
00797 return obj->toUpperFirstf(exec, CALLARG(0), CALLARG(1));
00798 case Scriptface::GetConfString:
00799 return obj->getConfStringf(exec, CALLARG(0), CALLARG(1));
00800 case Scriptface::GetConfBool:
00801 return obj->getConfBoolf(exec, CALLARG(0), CALLARG(1));
00802 case Scriptface::GetConfNumber:
00803 return obj->getConfNumberf(exec, CALLARG(0), CALLARG(1));
00804 default:
00805 return jsUndefined();
00806 }
00807 }
00808
00809
00810
00811 #define SPREF SFNAME"."
00812
00813 JSValue *Scriptface::loadf (ExecState *exec, const List &fnames)
00814 {
00815 if (globalKTI->currentModulePath.isEmpty())
00816 return throwError(exec, GeneralError,
00817 SPREF"load: no current module path, aiiie...");
00818
00819 for (int i = 0; i < fnames.size(); ++i)
00820 if (!fnames[i]->isString())
00821 return throwError(exec, TypeError,
00822 SPREF"load: expected string as file name");
00823
00824 for (int i = 0; i < fnames.size(); ++i)
00825 {
00826 QString qfname = fnames[i]->getString().qstring();
00827 QString qfpath = globalKTI->currentModulePath + '/' + qfname + ".js";
00828
00829 QFile file(qfpath);
00830 if (!file.open(QIODevice::ReadOnly))
00831 return throwError(exec, GeneralError,
00832 QString(SPREF"load: cannot read file '%1'")\
00833 .arg(qfpath));
00834
00835 QTextStream stream(&file);
00836 stream.setCodec("UTF-8");
00837 QString source = stream.readAll();
00838 file.close();
00839
00840 Completion comp = jsi->evaluate(qfpath, 0, source);
00841
00842 if (comp.complType() == Throw)
00843 {
00844 JSValue *exval = comp.value();
00845 ExecState *exec = jsi->globalExec();
00846 QString msg = exval->toString(exec).qstring();
00847
00848 QString line;
00849 if (exval->type() == ObjectType)
00850 {
00851 JSValue *lval = exval->getObject()->get(exec, "line");
00852 if (lval->type() == NumberType)
00853 line = QString::number(lval->toInt32(exec));
00854 }
00855
00856 return throwError(exec, TypeError,
00857 QString("at %1:%2: %3")
00858 .arg(qfpath, line, msg));
00859 }
00860 dbgout("Loaded module: %1", qfpath);
00861 }
00862
00863 return jsUndefined();
00864 }
00865
00866 JSValue *Scriptface::setcallf (ExecState *exec, JSValue *name,
00867 JSValue *func, JSValue *fval)
00868 {
00869 if (!name->isString())
00870 return throwError(exec, TypeError,
00871 SPREF"setcall: expected string as first argument");
00872 if ( !func->isObject()
00873 || !func->getObject()->implementsCall())
00874 return throwError(exec, TypeError,
00875 SPREF"setcall: expected function as second argument");
00876 if (!(fval->isObject() || fval->isNull()))
00877 return throwError(exec, TypeError,
00878 SPREF"setcall: expected object or null as third argument");
00879
00880 QString qname = name->toString(exec).qstring();
00881 funcs[qname] = func->getObject();
00882 fvals[qname] = fval;
00883
00884
00885 put(exec, Identifier(QString("#:f<%1>").arg(qname)), func, Internal);
00886 put(exec, Identifier(QString("#:o<%1>").arg(qname)), fval, Internal);
00887
00888
00889
00890 fpaths[qname] = globalKTI->currentModulePath;
00891
00892 return jsUndefined();
00893 }
00894
00895 JSValue *Scriptface::hascallf (ExecState *exec, JSValue *name)
00896 {
00897 if (!name->isString())
00898 return throwError(exec, TypeError,
00899 SPREF"hascall: expected string as first argument");
00900
00901 QString qname = name->toString(exec).qstring();
00902 return jsBoolean(funcs.contains(qname));
00903 }
00904
00905 JSValue *Scriptface::acallf (ExecState *exec, const List &argv)
00906 {
00907 if (argv.size() < 1) {
00908 return throwError(exec, SyntaxError,
00909 SPREF"acall: expected at least one argument (call name)");
00910 }
00911 if (!argv[0]->isString()) {
00912 return throwError(exec, SyntaxError,
00913 SPREF"acall: expected string as first argument (call name)");
00914 }
00915
00916
00917 QString callname = argv[0]->getString().qstring();
00918 if (!funcs.contains(callname)) {
00919 return throwError(exec, EvalError,
00920 QString(SPREF"acall: unregistered call to '%1'").arg(callname));
00921 }
00922 JSObject *func = funcs[callname];
00923 JSValue *fval = fvals[callname];
00924
00925
00926
00927 globalKTI->currentModulePath = fpaths[callname];
00928
00929
00930 List arglist;
00931 for (int i = 1; i < argv.size(); ++i)
00932 arglist.append(argv[i]);
00933 JSValue *val;
00934 if (fval->isObject()) {
00935
00936 val = func->callAsFunction(exec, fval->getObject(), arglist);
00937 }
00938 else {
00939
00940 val = func->callAsFunction(exec, jsi->globalObject(), arglist);
00941 }
00942 return val;
00943 }
00944
00945 JSValue *Scriptface::setcallForallf (ExecState *exec, JSValue *name,
00946 JSValue *func, JSValue *fval)
00947 {
00948 if (!name->isString())
00949 return throwError(exec, TypeError,
00950 SPREF"setcallForall: expected string as first argument");
00951 if ( !func->isObject()
00952 || !func->getObject()->implementsCall())
00953 return throwError(exec, TypeError,
00954 SPREF"setcallForall: expected function as second argument");
00955 if (!(fval->isObject() || fval->isNull()))
00956 return throwError(exec, TypeError,
00957 SPREF"setcallForall: expected object or null as third argument");
00958
00959 QString qname = name->toString(exec).qstring();
00960 funcs[qname] = func->getObject();
00961 fvals[qname] = fval;
00962
00963
00964 put(exec, Identifier(QString("#:fall<%1>").arg(qname)), func, Internal);
00965 put(exec, Identifier(QString("#:oall<%1>").arg(qname)), fval, Internal);
00966
00967
00968
00969 fpaths[qname] = globalKTI->currentModulePath;
00970
00971
00972 nameForalls.append(qname);
00973
00974 return jsUndefined();
00975 }
00976
00977 JSValue *Scriptface::fallbackf (ExecState *exec)
00978 {
00979 Q_UNUSED(exec);
00980 if (fallback != NULL)
00981 *fallback = true;
00982 return jsUndefined();
00983 }
00984
00985 JSValue *Scriptface::nsubsf (ExecState *exec)
00986 {
00987 Q_UNUSED(exec);
00988 return jsNumber(subs->size());
00989 }
00990
00991 JSValue *Scriptface::subsf (ExecState *exec, JSValue *index)
00992 {
00993 if (!index->isNumber())
00994 return throwError(exec, TypeError,
00995 SPREF"subs: expected number as first argument");
00996
00997 int i = qRound(index->getNumber());
00998 if (i < 0 || i >= subs->size())
00999 return throwError(exec, RangeError,
01000 SPREF"subs: index out of range");
01001
01002 return jsString(subs->at(i));
01003 }
01004
01005 JSValue *Scriptface::valsf (ExecState *exec, JSValue *index)
01006 {
01007 if (!index->isNumber())
01008 return throwError(exec, TypeError,
01009 SPREF"vals: expected number as first argument");
01010
01011 int i = qRound(index->getNumber());
01012 if (i < 0 || i >= vals->size())
01013 return throwError(exec, RangeError,
01014 SPREF"vals: index out of range");
01015
01016 return variantToJsValue(vals->at(i));
01017 }
01018
01019 JSValue *Scriptface::msgctxtf (ExecState *exec)
01020 {
01021 Q_UNUSED(exec);
01022 return jsString(*msgctxt);
01023 }
01024
01025 JSValue *Scriptface::dynctxtf (ExecState *exec, JSValue *key)
01026 {
01027 if (!key->isString())
01028 return throwError(exec, TypeError,
01029 SPREF"dynctxt: expected string as first argument");
01030
01031 QString qkey = key->getString().qstring();
01032 if (dynctxt->contains(qkey)) {
01033 return jsString(dynctxt->value(qkey));
01034 }
01035 return jsUndefined();
01036 }
01037
01038 JSValue *Scriptface::msgidf (ExecState *exec)
01039 {
01040 Q_UNUSED(exec);
01041 return jsString(*msgid);
01042 }
01043
01044 JSValue *Scriptface::msgkeyf (ExecState *exec)
01045 {
01046 Q_UNUSED(exec);
01047 return jsString(*msgctxt + '|' + *msgid);
01048 }
01049
01050 JSValue *Scriptface::msgstrff (ExecState *exec)
01051 {
01052 Q_UNUSED(exec);
01053 return jsString(*final);
01054 }
01055
01056 JSValue *Scriptface::dbgputsf (ExecState *exec, JSValue *str)
01057 {
01058 if (!str->isString())
01059 return throwError(exec, TypeError,
01060 SPREF"dbgputs: expected string as first argument");
01061
01062 QString qstr = str->getString().qstring();
01063
01064 dbgout("(JS) " + qstr);
01065
01066 return jsUndefined();
01067 }
01068
01069 JSValue *Scriptface::lscrf (ExecState *exec)
01070 {
01071 Q_UNUSED(exec);
01072 return jsString(*lscr);
01073 }
01074
01075 JSValue *Scriptface::normKeyf (ExecState *exec, JSValue *phrase)
01076 {
01077 if (!phrase->isString()) {
01078 return throwError(exec, TypeError,
01079 SPREF"normKey: expected string as argument");
01080 }
01081
01082 QByteArray nqphrase = normKeystr(phrase->toString(exec).qstring());
01083 return jsString(QString::fromUtf8(nqphrase));
01084 }
01085
01086 JSValue *Scriptface::loadPropsf (ExecState *exec, const List &fnames)
01087 {
01088 if (globalKTI->currentModulePath.isEmpty()) {
01089 return throwError(exec, GeneralError,
01090 SPREF"loadProps: no current module path, aiiie...");
01091 }
01092
01093 for (int i = 0; i < fnames.size(); ++i) {
01094 if (!fnames[i]->isString()) {
01095 return throwError(exec, TypeError,
01096 SPREF"loadProps: expected string as file name");
01097 }
01098 }
01099
01100 for (int i = 0; i < fnames.size(); ++i)
01101 {
01102 QString qfname = fnames[i]->getString().qstring();
01103 QString qfpath_base = globalKTI->currentModulePath + '/' + qfname;
01104
01105
01106
01107 QString qfpath = qfpath_base + ".pmapc";
01108 bool haveCompiled = true;
01109 QFile file_check(qfpath);
01110 if (!file_check.open(QIODevice::ReadOnly)) {
01111 haveCompiled = false;
01112 qfpath = qfpath_base + ".pmap";
01113 QFile file_check(qfpath);
01114 if (!file_check.open(QIODevice::ReadOnly)) {
01115 return throwError(exec, GeneralError,
01116 QString(SPREF"loadProps: cannot read map '%1'")
01117 .arg(qfpath_base));
01118 }
01119 }
01120 file_check.close();
01121
01122
01123 QString errorString;
01124 if (haveCompiled) {
01125 errorString = loadProps_bin(qfpath);
01126 }
01127 else {
01128 errorString = loadProps_text(qfpath);
01129 }
01130 if (!errorString.isEmpty()) {
01131 return throwError(exec, SyntaxError, errorString);
01132 }
01133 dbgout("Loaded property map: %1", qfpath);
01134 }
01135
01136 return jsUndefined();
01137 }
01138
01139 JSValue *Scriptface::getPropf (ExecState *exec, JSValue *phrase, JSValue *prop)
01140 {
01141 if (!phrase->isString()) {
01142 return throwError(exec, TypeError,
01143 SPREF"getProp: expected string as first argument");
01144 }
01145 if (!prop->isString()) {
01146 return throwError(exec, TypeError,
01147 SPREF"getProp: expected string as second argument");
01148 }
01149
01150 QByteArray qphrase = normKeystr(phrase->toString(exec).qstring());
01151 QHash<QByteArray, QByteArray> props = phraseProps.value(qphrase);
01152 if (!props.isEmpty()) {
01153 QByteArray qprop = normKeystr(prop->toString(exec).qstring());
01154 QByteArray qval = props.value(qprop);
01155 if (!qval.isEmpty()) {
01156 return jsString(QString::fromUtf8(qval));
01157 }
01158 }
01159 return jsUndefined();
01160 }
01161
01162 JSValue *Scriptface::setPropf (ExecState *exec, JSValue *phrase, JSValue *prop, JSValue *value)
01163 {
01164 if (!phrase->isString()) {
01165 return throwError(exec, TypeError,
01166 SPREF"setProp: expected string as first argument");
01167 }
01168 if (!prop->isString()) {
01169 return throwError(exec, TypeError,
01170 SPREF"setProp: expected string as second argument");
01171 }
01172 if (!value->isString()) {
01173 return throwError(exec, TypeError,
01174 SPREF"setProp: expected string as third argument");
01175 }
01176
01177 QByteArray qphrase = normKeystr(phrase->toString(exec).qstring());
01178 QByteArray qprop = normKeystr(prop->toString(exec).qstring());
01179 QByteArray qvalue = value->toString(exec).qstring().toUtf8();
01180
01181 phraseProps[qphrase][qprop] = qvalue;
01182 return jsUndefined();
01183 }
01184
01185 JSValue *Scriptface::toUpperFirstf (ExecState *exec,
01186 JSValue *str, JSValue *nalt)
01187 {
01188 static QString head("~@");
01189 static int hlen = head.length();
01190
01191 if (!str->isString()) {
01192 return throwError(exec, TypeError,
01193 SPREF"toUpperFirst: expected string as first argument");
01194 }
01195 if (!(nalt->isNumber() || nalt->isNull())) {
01196 return throwError(exec, TypeError,
01197 SPREF"toUpperFirst: expected number as second argument");
01198 }
01199
01200 QString qstr = str->toString(exec).qstring();
01201 int qnalt = nalt->isNull() ? 0 : nalt->toInteger(exec);
01202
01203
01204
01205
01206 QString qstruc = qstr;
01207 int len = qstr.length();
01208 QChar altSep;
01209 int remainingAlts = 0;
01210 bool checkCase = true;
01211 int numUpcased = 0;
01212 int i = 0;
01213 while (i < len) {
01214 QChar c = qstr[i];
01215
01216 if (qnalt && !remainingAlts && qstr.mid(i, hlen) == head) {
01217
01218 i += 2;
01219 if (i >= len) break;
01220
01221
01222 altSep = qstruc[i];
01223 remainingAlts = qnalt;
01224 checkCase = true;
01225 }
01226 else if (remainingAlts && c == altSep) {
01227
01228
01229 --remainingAlts;
01230 checkCase = true;
01231 }
01232 else if (checkCase && c.isLetter()) {
01233
01234 qstruc[i] = c.toUpper();
01235 ++numUpcased;
01236
01237 checkCase = false;
01238 }
01239
01240
01241
01242 if (numUpcased > 0 && remainingAlts == 0) {
01243 break;
01244 }
01245
01246
01247 ++i;
01248 }
01249
01250 return jsString(qstruc);
01251 }
01252
01253 JSValue *Scriptface::getConfStringf (ExecState *exec,
01254 JSValue *key, JSValue *dval)
01255 {
01256 if (!key->isString()) {
01257 return throwError(exec, TypeError,
01258 SPREF"getConfString: expected string "
01259 "as first argument");
01260 }
01261 if (!(dval->isString() || dval->isNull())) {
01262 return throwError(exec, TypeError,
01263 SPREF"getConfString: expected string "
01264 "as second argument (when given)");
01265 }
01266
01267 if (dval->isNull()) {
01268 dval = jsUndefined();
01269 }
01270
01271 QString qkey = key->getString().qstring();
01272 if (config.contains(qkey)) {
01273 return jsString(config.value(qkey));
01274 }
01275
01276 return dval;
01277 }
01278
01279 JSValue *Scriptface::getConfBoolf (ExecState *exec,
01280 JSValue *key, JSValue *dval)
01281 {
01282 if (!key->isString()) {
01283 return throwError(exec, TypeError,
01284 SPREF"getConfBool: expected string as "
01285 "first argument");
01286 }
01287 if (!(dval->isBoolean() || dval->isNull())) {
01288 return throwError(exec, TypeError,
01289 SPREF"getConfBool: expected boolean "
01290 "as second argument (when given)");
01291 }
01292
01293 static QStringList falsities;
01294 if (falsities.isEmpty()) {
01295 falsities.append(QString('0'));
01296 falsities.append(QString("no"));
01297 falsities.append(QString("false"));
01298 }
01299
01300 if (dval->isNull()) {
01301 dval = jsUndefined();
01302 }
01303
01304 QString qkey = key->getString().qstring();
01305 if (config.contains(qkey)) {
01306 QString qval = config.value(qkey).toLower();
01307 return jsBoolean(!falsities.contains(qval));
01308 }
01309
01310 return dval;
01311 }
01312
01313 JSValue *Scriptface::getConfNumberf (ExecState *exec,
01314 JSValue *key, JSValue *dval)
01315 {
01316 if (!key->isString()) {
01317 return throwError(exec, TypeError,
01318 SPREF"getConfNumber: expected string "
01319 "as first argument");
01320 }
01321 if (!(dval->isNumber() || dval->isNull())) {
01322 return throwError(exec, TypeError,
01323 SPREF"getConfNumber: expected number "
01324 "as second argument (when given)");
01325 }
01326
01327 if (dval->isNull()) {
01328 dval = jsUndefined();
01329 }
01330
01331 QString qkey = key->getString().qstring();
01332 if (config.contains(qkey)) {
01333 QString qval = config.value(qkey);
01334 bool convOk;
01335 double qnum = qval.toDouble(&convOk);
01336 if (convOk) {
01337 return jsNumber(qnum);
01338 }
01339 }
01340
01341 return dval;
01342 }
01343
01344
01345
01346
01347 QString Scriptface::loadProps_text (const QString &fpath)
01348 {
01349 QFile file(fpath);
01350 if (!file.open(QIODevice::ReadOnly)) {
01351 return QString(SPREF"loadProps_text: cannot read file '%1'")
01352 .arg(fpath);
01353 }
01354 QTextStream stream(&file);
01355 stream.setCodec("UTF-8");
01356 QString s = stream.readAll();
01357 file.close();
01358
01359
01360
01361
01362 enum {s_nextEntry, s_nextKey, s_nextValue};
01363 QList<QByteArray> ekeys;
01364 QHash<QByteArray, QByteArray> props;
01365 int slen = s.length();
01366 int state = s_nextEntry;
01367 QByteArray pkey;
01368 QChar prop_sep, key_sep;
01369 int i = 0;
01370 while (1) {
01371 int i_checkpoint = i;
01372
01373 if (state == s_nextEntry) {
01374 while (s[i].isSpace()) {
01375 ++i;
01376 if (i >= slen) goto END_PROP_PARSE;
01377 }
01378 if (i + 1 >= slen) {
01379 return QString(SPREF"loadProps_text: unexpected end "
01380 "of file in %1").arg(fpath);
01381 }
01382 if (s[i] != '#') {
01383
01384 key_sep = s[i];
01385 prop_sep = s[i + 1];
01386 if (key_sep.isLetter() || prop_sep.isLetter()) {
01387 return QString(SPREF"loadProps_text: separator "
01388 "characters must not be letters at %1:%2")
01389 .arg(fpath).arg(countLines(s, i));
01390 }
01391
01392
01393 ekeys.clear();
01394 props.clear();
01395 pkey.clear();
01396
01397 i += 2;
01398 state = s_nextKey;
01399 }
01400 else {
01401
01402 while (s[i] != '\n') {
01403 ++i;
01404 if (i >= slen) goto END_PROP_PARSE;
01405 }
01406 }
01407 }
01408 else if (state == s_nextKey) {
01409 int ip = i;
01410
01411 while (s[i] != key_sep && s[i] != prop_sep) {
01412 ++i;
01413 if (i >= slen) goto END_PROP_PARSE;
01414 }
01415 if (s[i] == key_sep) {
01416
01417
01418 pkey = normKeystr(s.mid(ip, i - ip));
01419
01420 i += 1;
01421 state = s_nextValue;
01422 }
01423 else {
01424
01425 QByteArray ekey = normKeystr(s.mid(ip, i - ip));
01426 if (!ekey.isEmpty()) {
01427
01428 ekeys.append(ekey);
01429
01430 i += 1;
01431 state = s_nextKey;
01432 }
01433 else {
01434
01435 if (ekeys.size() < 1) {
01436 return QString(SPREF"loadProps_text: no entry key "
01437 "for entry ending at %1:%2")
01438 .arg(fpath).arg(countLines(s, i));
01439 }
01440
01441
01442
01443 foreach (const QByteArray &ekey, ekeys) {
01444 phraseProps[ekey] = props;
01445 }
01446
01447 i += 1;
01448 state = s_nextEntry;
01449 }
01450 }
01451 }
01452 else if (state == s_nextValue) {
01453 int ip = i;
01454
01455 while (s[i] != prop_sep) {
01456 ++i;
01457 if (i >= slen) goto END_PROP_PARSE;
01458 if (s[i] == key_sep) {
01459 return QString(SPREF"loadProps_text: property separator "
01460 "inside property value at %1:%2")
01461 .arg(fpath).arg(countLines(s, i));
01462 }
01463 }
01464
01465 QByteArray pval = trimSmart(s.mid(ip, i - ip)).toUtf8();
01466 props[pkey] = pval;
01467
01468 i += 1;
01469 state = s_nextKey;
01470 }
01471 else {
01472 return QString(SPREF"loadProps: internal error 10 at %1:%2")
01473 .arg(fpath).arg(countLines(s, i));
01474 }
01475
01476
01477 if (i == i_checkpoint || i >= slen) {
01478 return QString(SPREF"loadProps: internal error 20 at %1:%2")
01479 .arg(fpath).arg(countLines(s, i));
01480 }
01481 }
01482
01483 END_PROP_PARSE:
01484
01485 if (state != s_nextEntry) {
01486 return QString(SPREF"loadProps: unexpected end of file in %1")
01487 .arg(fpath);
01488 }
01489
01490 return QString();
01491 }
01492
01493 static int bin_read_int (const char *fc, qlonglong len, qlonglong &pos)
01494 {
01495
01496 static const int nbytes = 4;
01497 if (pos + nbytes > len) {
01498 pos = -1;
01499 return 0;
01500 }
01501 int num = qFromBigEndian<quint32>((uchar*) fc + pos);
01502 pos += nbytes;
01503 return num;
01504 }
01505
01506 static QByteArray bin_read_string (const char *fc, qlonglong len, qlonglong &pos)
01507 {
01508
01509
01510 int nbytes = bin_read_int(fc, len, pos);
01511 if (pos < 0) {
01512 return QByteArray();
01513 }
01514 if (nbytes < 0 || pos + nbytes > len) {
01515 pos = -1;
01516 return QByteArray();
01517 }
01518 QByteArray s(fc + pos, nbytes);
01519 pos += nbytes;
01520 return s;
01521 }
01522
01523 QString Scriptface::loadProps_bin (const QString &fpath)
01524 {
01525 QFile file(fpath);
01526 if (!file.open(QIODevice::ReadOnly)) {
01527 return QString(SPREF"loadProps_bin: cannot read file '%1'")
01528 .arg(fpath);
01529 }
01530 QByteArray fctmp = file.readAll();
01531 file.close();
01532 const char *fc = fctmp.data();
01533 const int fclen = fctmp.size();
01534
01535
01536 qlonglong pos = 0;
01537
01538
01539 QByteArray head(fc, 8);
01540 pos += 8;
01541 if (head != "TSPMAP00") goto END_PROP_PARSE;
01542
01543
01544 int nentries;
01545 nentries = bin_read_int(fc, fclen, pos);
01546 if (pos < 0) goto END_PROP_PARSE;
01547
01548
01549 for (int i = 0; i < nentries; ++i) {
01550
01551
01552 QList<QByteArray> ekeys;
01553 int nekeys = bin_read_int(fc, fclen, pos);
01554 if (pos < 0) goto END_PROP_PARSE;
01555 for (int j = 0; j < nekeys; ++j) {
01556 QByteArray ekey = bin_read_string(fc, fclen, pos);
01557 if (pos < 0) goto END_PROP_PARSE;
01558 ekeys.append(ekey);
01559 }
01560
01561
01562
01563 QHash<QByteArray, QByteArray> props;
01564 int nprops = bin_read_int(fc, fclen, pos);
01565 if (pos < 0) goto END_PROP_PARSE;
01566 for (int j = 0; j < nprops; ++j) {
01567 QByteArray pkey = bin_read_string(fc, fclen, pos);
01568 if (pos < 0) goto END_PROP_PARSE;
01569 QByteArray pval = bin_read_string(fc, fclen, pos);
01570 if (pos < 0) goto END_PROP_PARSE;
01571 props[pkey] = pval;
01572 }
01573
01574
01575
01576 foreach (const QByteArray &ekey, ekeys) {
01577 phraseProps[ekey] = props;
01578 }
01579 }
01580
01581 END_PROP_PARSE:
01582
01583 if (pos < 0) {
01584 return QString(SPREF"loadProps_bin: corrupt compiled map %1")
01585 .arg(fpath);
01586 }
01587
01588 return QString();
01589 }