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