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