• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

ktranscript.cpp
Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries    Copyright (C) 2007 Chusslove Illich <caslav.ilic@gmx.net>
00002 
00003     This library is free software; you can redistribute it and/or
00004     modify it under the terms of the GNU Library General Public
00005     License as published by the Free Software Foundation; either
00006     version 2 of the License, or (at your option) any later version.
00007 
00008     This library is distributed in the hope that it will be useful,
00009     but WITHOUT ANY WARRANTY; without even the implied warranty of
00010     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011     Library General Public License for more details.
00012 
00013     You should have received a copy of the GNU Library General Public License
00014     along with this library; see the file COPYING.LIB.  If not, write to
00015     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016     Boston, MA 02110-1301, USA.
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 //#include <unistd.h>
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 <QDir>
00041 #include <QHash>
00042 #include <QPair>
00043 #include <QSet>
00044 #include <QFile>
00045 #include <QIODevice>
00046 #include <QTextStream>
00047 #include <QRegExp>
00048 #include <qendian.h>
00049 
00050 using namespace KJS;
00051 
00052 class KTranscriptImp;
00053 class Scriptface;
00054 
00055 typedef QHash<QString, QString> TsConfigGroup;
00056 typedef QHash<QString, TsConfigGroup> TsConfig;
00057 
00058 // Transcript implementation (used as singleton).
00059 class KTranscriptImp : public KTranscript
00060 {
00061     public:
00062 
00063     KTranscriptImp ();
00064     ~KTranscriptImp ();
00065 
00066     QString eval (const QList<QVariant> &argv,
00067                   const QString &lang,
00068                   const QString &ctry,
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     // Lexical path of the module for the executing code.
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 // Script-side transcript interface.
00095 class Scriptface : public JSObject
00096 {
00097     public:
00098     Scriptface (ExecState *exec, const TsConfigGroup &config);
00099     ~Scriptface ();
00100 
00101     // Interface functions.
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 *warnputsf (ExecState *exec, JSValue *str);
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         Warnputs,
00148         LocaleCountry,
00149         NormKey,
00150         LoadProps,
00151         GetProp,
00152         SetProp,
00153         ToUpperFirst,
00154         ToLowerFirst,
00155         GetConfString,
00156         GetConfBool,
00157         GetConfNumber
00158     };
00159 
00160     // Helper methods to interface functions.
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     // Virtual implementations.
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     // Link to its interpreter.
00176     // FIXME: Probably accessible without the explicit link.
00177     Interpreter *jsi;
00178 
00179     // Current message data.
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 
00188     // Fallback request handle.
00189     bool *fallback;
00190 
00191     // Function register.
00192     QHash<QString, JSObject*> funcs;
00193     QHash<QString, JSValue*> fvals;
00194     QHash<QString, QString> fpaths;
00195 
00196     // Ordering of those functions which execute for all messages.
00197     QList<QString> nameForalls;
00198 
00199     // Property values per phrase (used by *Prop interface calls).
00200     // Not QStrings, in order to avoid conversion from UTF-8 when
00201     // loading compiled maps (less latency on startup).
00202     QHash<QByteArray, QHash<QByteArray, QByteArray> > phraseProps;
00203     // Unresolved property values per phrase,
00204     // containing the pointer to compiled pmap file handle and offset in it.
00205     QHash<QByteArray, QPair<QFile*, quint64> > phraseUnparsedProps;
00206     QHash<QByteArray, QByteArray> resolveUnparsedProps (const QByteArray &phrase);
00207     // Set of loaded pmap files by paths and file handle pointers.
00208     QSet<QString> loadedPmapPaths;
00209     QSet<QFile*> loadedPmapHandles;
00210 
00211     // User config.
00212     TsConfigGroup config;
00213 };
00214 
00215 // ----------------------------------------------------------------------
00216 // Custom debug and warning output (kdebug not available)
00217 #define DBGP "KTranscript: "
00218 void dbgout (const char*str) {
00219     #ifndef NDEBUG
00220     fprintf(stderr, DBGP"%s\n", str);
00221     #else
00222     Q_UNUSED(str);
00223     #endif
00224 }
00225 template <typename T1>
00226 void dbgout (const char* str, const T1 &a1) {
00227     #ifndef NDEBUG
00228     fprintf(stderr, DBGP"%s\n", QString::fromUtf8(str).arg(a1).toLocal8Bit().data());
00229     #else
00230     Q_UNUSED(str); Q_UNUSED(a1);
00231     #endif
00232 }
00233 template <typename T1, typename T2>
00234 void dbgout (const char* str, const T1 &a1, const T2 &a2) {
00235     #ifndef NDEBUG
00236     fprintf(stderr, DBGP"%s\n", QString::fromUtf8(str).arg(a1).arg(a2).toLocal8Bit().data());
00237     #else
00238     Q_UNUSED(str); Q_UNUSED(a1); Q_UNUSED(a2);
00239     #endif
00240 }
00241 template <typename T1, typename T2, typename T3>
00242 void dbgout (const char* str, const T1 &a1, const T2 &a2, const T3 &a3) {
00243     #ifndef NDEBUG
00244     fprintf(stderr, DBGP"%s\n", QString::fromUtf8(str).arg(a1).arg(a2).arg(a3).toLocal8Bit().data());
00245     #else
00246     Q_UNUSED(str); Q_UNUSED(a1); Q_UNUSED(a2); Q_UNUSED(a3);
00247     #endif
00248 }
00249 
00250 #define WARNP "KTranscript: "
00251 void warnout (const char*str) {
00252     fprintf(stderr, WARNP"%s\n", str);
00253 }
00254 template <typename T1>
00255 void warnout (const char* str, const T1 &a1) {
00256     fprintf(stderr, WARNP"%s\n", QString::fromUtf8(str).arg(a1).toLocal8Bit().data());
00257 }
00258 
00259 // ----------------------------------------------------------------------
00260 // Conversions between QString and KJS UString.
00261 // Taken from kate.
00262 UString::UString(const QString &d)
00263 {
00264     unsigned int len = d.length();
00265     UChar *dat = static_cast<UChar*>(fastMalloc(sizeof(UChar) * len));
00266     memcpy(dat, d.unicode(), len * sizeof(UChar));
00267     m_rep = UString::Rep::create(dat, len);
00268 }
00269 QString UString::qstring() const
00270 {
00271     return QString((QChar*) data(), size());
00272 }
00273 
00274 // ----------------------------------------------------------------------
00275 // Produces a string out of a KJS exception.
00276 QString expt2str (ExecState *exec)
00277 {
00278     JSValue *expt = exec->exception();
00279     if (   expt->isObject()
00280         && expt->getObject()->hasProperty(exec, "message"))
00281     {
00282         JSValue *msg = expt->getObject()->get(exec, "message");
00283         return QString::fromLatin1("Error: %1").arg(msg->getString().qstring());
00284     }
00285     else
00286     {
00287         QString strexpt = exec->exception()->toString(exec).qstring();
00288         return QString::fromLatin1("Caught exception: %1").arg(strexpt);
00289     }
00290 }
00291 
00292 // ----------------------------------------------------------------------
00293 // Count number of lines in the string,
00294 // up to and excluding the requested position.
00295 int countLines (const QString &s, int p)
00296 {
00297     int n = 1;
00298     int len = s.length();
00299     for (int i = 0; i < p && i < len; ++i) {
00300         if (s[i] == QLatin1Char('\n')) {
00301             ++n;
00302         }
00303     }
00304     return n;
00305 }
00306 
00307 // ----------------------------------------------------------------------
00308 // Normalize string key for hash lookups,
00309 QByteArray normKeystr (const QString &raw, bool mayHaveAcc = true)
00310 {
00311     // NOTE: Regexes should not be used here for performance reasons.
00312     // This function may potentially be called thousands of times
00313     // on application startup.
00314 
00315     QString key = raw;
00316 
00317     // Strip all whitespace.
00318     int len = key.length();
00319     QString nkey;
00320     for (int i = 0; i < len; ++i) {
00321         QChar c = key[i];
00322         if (!c.isSpace()) {
00323             nkey.append(c);
00324         }
00325     }
00326     key = nkey;
00327 
00328     // Strip accelerator marker.
00329     if (mayHaveAcc) {
00330         key = removeAcceleratorMarker(key);
00331     }
00332 
00333     // Convert to lower case.
00334     key = key.toLower();
00335 
00336     return key.toUtf8();
00337 }
00338 
00339 // ----------------------------------------------------------------------
00340 // Trim multiline string in a "smart" way:
00341 // Remove leading and trailing whitespace up to and including first
00342 // newline from that side, if there is one; otherwise, don't touch.
00343 QString trimSmart (const QString &raw)
00344 {
00345     // NOTE: This could be done by a single regex, but is not due to
00346     // performance reasons.
00347     // This function may potentially be called thousands of times
00348     // on application startup.
00349 
00350     int len = raw.length();
00351 
00352     int is = 0;
00353     while (is < len && raw[is].isSpace() && raw[is] != QLatin1Char('\n')) {
00354         ++is;
00355     }
00356     if (is >= len || raw[is] != QLatin1Char('\n')) {
00357         is = -1;
00358     }
00359 
00360     int ie = len - 1;
00361     while (ie >= 0 && raw[ie].isSpace() && raw[ie] != QLatin1Char('\n')) {
00362         --ie;
00363     }
00364     if (ie < 0 || raw[ie] != QLatin1Char('\n')) {
00365         ie = len;
00366     }
00367 
00368     return raw.mid(is + 1, ie - is - 1);
00369 }
00370 
00371 // ----------------------------------------------------------------------
00372 // Produce a JavaScript object out of Qt variant.
00373 JSValue *variantToJsValue (const QVariant &val)
00374 {
00375     QVariant::Type vtype = val.type();
00376     if (vtype == QVariant::String)
00377         return jsString(val.toString());
00378     else if (   vtype == QVariant::Double \
00379              || vtype == QVariant::Int || vtype == QVariant::UInt \
00380              || vtype == QVariant::LongLong || vtype == QVariant::ULongLong)
00381         return jsNumber(val.toDouble());
00382     else
00383         return jsUndefined();
00384 }
00385 
00386 // ----------------------------------------------------------------------
00387 // Parse ini-style config file,
00388 // returning content as hash of hashes by group and key.
00389 // Parsing is not fussy, it will read what it can.
00390 TsConfig readConfig (const QString &fname)
00391 {
00392     TsConfig config;
00393     // Add empty group.
00394     TsConfig::iterator configGroup;
00395     configGroup = config.insert(QString(), TsConfigGroup());
00396 
00397     QFile file(fname);
00398     if (!file.open(QIODevice::ReadOnly)) {
00399         return config;
00400     }
00401     QTextStream stream(&file);
00402     stream.setCodec("UTF-8");
00403     while (!stream.atEnd()) {
00404         QString line = stream.readLine();
00405         int p1, p2;
00406 
00407         // Remove comment from the line.
00408         p1 = line.indexOf(QLatin1Char('#'));
00409         if (p1 >= 0) {
00410             line = line.left(p1);
00411         }
00412         line = line.trimmed();
00413         if (line.isEmpty()) {
00414             continue;
00415         }
00416 
00417         if (line[0] == QLatin1Char('[')) {
00418             // Group switch.
00419             p1 = 0;
00420             p2 = line.indexOf(QLatin1Char(']'), p1 + 1);
00421             if (p2 < 0) {
00422                 continue;
00423             }
00424             QString group = line.mid(p1 + 1, p2 - p1 - 1).trimmed();
00425             configGroup = config.find(group);
00426             if (configGroup == config.end()) {
00427                 // Add new group.
00428                 configGroup = config.insert(group, TsConfigGroup());
00429             }
00430         } else {
00431             // Field.
00432             p1 = line.indexOf(QLatin1Char('='));
00433             if (p1 < 0) {
00434                 continue;
00435             }
00436             QString field = line.left(p1).trimmed();
00437             QString value = line.mid(p1 + 1).trimmed();
00438             if (!field.isEmpty()) {
00439                 (*configGroup)[field] = value;
00440             }
00441         }
00442     }
00443     file.close();
00444 
00445     return config;
00446 }
00447 
00448 // ----------------------------------------------------------------------
00449 // Dynamic loading.
00450 K_GLOBAL_STATIC(KTranscriptImp, globalKTI)
00451 extern "C"
00452 {
00453     KDE_EXPORT KTranscript *load_transcript ()
00454     {
00455         return globalKTI;
00456     }
00457 }
00458 
00459 // ----------------------------------------------------------------------
00460 // KTranscript definitions.
00461 
00462 KTranscriptImp::KTranscriptImp ()
00463 {
00464     // Load user configuration.
00465     const QString tsConfigPath = QDir::homePath() + QLatin1Char('/') + QLatin1String(".transcriptrc");
00466     config = readConfig(tsConfigPath);
00467 }
00468 
00469 KTranscriptImp::~KTranscriptImp ()
00470 {
00471     // FIXME: vallgrind shows an afwul lot of "invalid read" in WTF:: stuff
00472     // when deref is called... Are we leaking somewhere?
00473     //foreach (Scriptface *sface, m_sface.values())
00474     //    sface->jsi->deref();
00475 }
00476 
00477 QString KTranscriptImp::eval (const QList<QVariant> &argv,
00478                               const QString &lang,
00479                               const QString &ctry,
00480                               const QString &msgctxt,
00481                               const QHash<QString, QString> &dynctxt,
00482                               const QString &msgid,
00483                               const QStringList &subs,
00484                               const QList<QVariant> &vals,
00485                               const QString &final,
00486                               QList<QStringList> &mods,
00487                               QString &error,
00488                               bool &fallback)
00489 {
00490     //error = "debug"; return QString();
00491 
00492     error.clear(); // empty error message means successful evaluation
00493     fallback = false; // fallback not requested
00494 
00495     #if 0
00496     // FIXME: Maybe not needed, as KJS has no native outside access?
00497     // Unportable (needs unistd.h)?
00498 
00499     // If effective user id is root and real user id is not root.
00500     if (geteuid() == 0 && getuid() != 0)
00501     {
00502         // Since scripts are user input, and the program is running with
00503         // root permissions while real user is not root, do not invoke
00504         // scripting at all, to prevent exploits.
00505         error = "Security block: trying to execute a script in suid environment.";
00506         return QString();
00507     }
00508     #endif
00509 
00510     // Load any new modules and clear the list.
00511     if (!mods.isEmpty())
00512     {
00513         loadModules(mods, error);
00514         mods.clear();
00515         if (!error.isEmpty())
00516             return QString();
00517     }
00518 
00519     // Add interpreters for new languages.
00520     // (though it should never happen here, but earlier when loading modules;
00521     // this also means there are no calls set, so the unregistered call error
00522     // below will be reported).
00523     if (!m_sface.contains(lang))
00524         setupInterpreter(lang);
00525 
00526     // Shortcuts.
00527     Scriptface *sface = m_sface[lang];
00528     ExecState *exec = sface->jsi->globalExec();
00529     JSObject *gobj = sface->jsi->globalObject();
00530 
00531     // Link current message data for script-side interface.
00532     sface->msgctxt = &msgctxt;
00533     sface->dynctxt = &dynctxt;
00534     sface->msgid = &msgid;
00535     sface->subs = &subs;
00536     sface->vals = &vals;
00537     sface->final = &final;
00538     sface->fallback = &fallback;
00539     sface->ctry = &ctry;
00540 
00541     // Find corresponding JS function.
00542     int argc = argv.size();
00543     if (argc < 1)
00544     {
00545         //error = "At least the call name must be supplied.";
00546         // Empty interpolation is OK, possibly used just to initialize
00547         // at a given point (e.g. for Ts.setForall() to start having effect).
00548         return QString();
00549     }
00550     QString funcName = argv[0].toString();
00551     if (!sface->funcs.contains(funcName))
00552     {
00553         error = QString::fromLatin1("Unregistered call to '%1'.").arg(funcName);
00554         return QString();
00555     }
00556     JSObject *func = sface->funcs[funcName];
00557     JSValue *fval = sface->fvals[funcName];
00558 
00559     // Recover module path from the time of definition of this call,
00560     // for possible load calls.
00561     currentModulePath = sface->fpaths[funcName];
00562 
00563     // Execute function.
00564     List arglist;
00565     for (int i = 1; i < argc; ++i)
00566         arglist.append(variantToJsValue(argv[i]));
00567     JSValue *val;
00568     if (fval->isObject())
00569         val = func->callAsFunction(exec, fval->getObject(), arglist);
00570     else // no object associated to this function, use global
00571         val = func->callAsFunction(exec, gobj, arglist);
00572 
00573     if (fallback)
00574     // Fallback to ordinary translation requested.
00575     {
00576         // Possibly clear exception state.
00577         if (exec->hadException())
00578             exec->clearException();
00579 
00580         return QString();
00581     }
00582     else if (!exec->hadException())
00583     // Evaluation successful.
00584     {
00585         if (val->isString())
00586         // Good to go.
00587         {
00588             return val->getString().qstring();
00589         }
00590         else
00591         // Accept only strings.
00592         {
00593             QString strval = val->toString(exec).qstring();
00594             error = QString::fromLatin1("Non-string return value: %1").arg(strval);
00595             return QString();
00596         }
00597     }
00598     else
00599     // Exception raised.
00600     {
00601         error = expt2str(exec);
00602 
00603         exec->clearException();
00604 
00605         return QString();
00606     }
00607 }
00608 
00609 QStringList KTranscriptImp::postCalls (const QString &lang)
00610 {
00611     // Return no calls if scripting was not already set up for this language.
00612     // NOTE: This shouldn't happen, as postCalls cannot be called in such case.
00613     if (!m_sface.contains(lang))
00614         return QStringList();
00615 
00616     // Shortcuts.
00617     Scriptface *sface = m_sface[lang];
00618 
00619     return sface->nameForalls;
00620 }
00621 
00622 void KTranscriptImp::loadModules (const QList<QStringList> &mods,
00623                                   QString &error)
00624 {
00625     QList<QString> modErrors;
00626 
00627     foreach (const QStringList &mod, mods)
00628     {
00629         QString mpath = mod[0];
00630         QString mlang = mod[1];
00631 
00632         // Add interpreters for new languages.
00633         if (!m_sface.contains(mlang))
00634             setupInterpreter(mlang);
00635 
00636         // Setup current module path for loading submodules.
00637         // (sort of closure over invocations of loadf)
00638         int posls = mpath.lastIndexOf(QLatin1Char('/'));
00639         if (posls < 1)
00640         {
00641             modErrors.append(QString::fromLatin1("Funny module path '%1', skipping.")
00642                                     .arg(mpath));
00643             continue;
00644         }
00645         currentModulePath = mpath.left(posls);
00646         QString fname = mpath.mid(posls + 1);
00647         // Scriptface::loadf() wants no extension on the filename
00648         fname = fname.left(fname.lastIndexOf(QLatin1Char('.')));
00649 
00650         // Load the module.
00651         ExecState *exec = m_sface[mlang]->jsi->globalExec();
00652         List alist;
00653         alist.append(jsString(fname));
00654 
00655         m_sface[mlang]->loadf(exec, alist);
00656 
00657         // Handle any exception.
00658         if (exec->hadException())
00659         {
00660             modErrors.append(expt2str(exec));
00661             exec->clearException();
00662         }
00663     }
00664 
00665     // Unset module path.
00666     currentModulePath.clear();
00667 
00668     foreach (const QString &merr, modErrors)
00669         error.append(merr + QLatin1Char('\n'));
00670 }
00671 
00672 KJS_QT_UNICODE_IMPL
00673 
00674 #define SFNAME "Ts"
00675 void KTranscriptImp::setupInterpreter (const QString &lang)
00676 {
00677     // Create new interpreter.
00678     Interpreter *jsi = new Interpreter;
00679     KJS_QT_UNICODE_SET;
00680     jsi->initGlobalObject();
00681     jsi->ref();
00682 
00683     // Add scripting interface into the interpreter.
00684     // NOTE: Config may not contain an entry for the language, in which case
00685     // it is automatically constructed as an empty hash. This is intended.
00686     Scriptface *sface = new Scriptface(jsi->globalExec(), config[lang]);
00687     jsi->globalObject()->put(jsi->globalExec(), SFNAME, sface,
00688                              DontDelete|ReadOnly);
00689 
00690     // Store scriptface and link to its interpreter.
00691     sface->jsi = jsi;
00692     m_sface[lang] = sface;
00693 
00694     //dbgout("=====> Created interpreter for '%1'", lang);
00695 }
00696 
00697 // ----------------------------------------------------------------------
00698 // Scriptface internal mechanics.
00699 #include "ktranscript.lut.h"
00700 
00701 /* Source for ScriptfaceProtoTable.
00702 @begin ScriptfaceProtoTable 2
00703     load            Scriptface::Load            DontDelete|ReadOnly|Function 0
00704     setcall         Scriptface::Setcall         DontDelete|ReadOnly|Function 3
00705     hascall         Scriptface::Hascall         DontDelete|ReadOnly|Function 1
00706     acall           Scriptface::Acall           DontDelete|ReadOnly|Function 0
00707     setcallForall   Scriptface::SetcallForall   DontDelete|ReadOnly|Function 3
00708     fallback        Scriptface::Fallback        DontDelete|ReadOnly|Function 0
00709     nsubs           Scriptface::Nsubs           DontDelete|ReadOnly|Function 0
00710     subs            Scriptface::Subs            DontDelete|ReadOnly|Function 1
00711     vals            Scriptface::Vals            DontDelete|ReadOnly|Function 1
00712     msgctxt         Scriptface::Msgctxt         DontDelete|ReadOnly|Function 0
00713     dynctxt         Scriptface::Dynctxt         DontDelete|ReadOnly|Function 1
00714     msgid           Scriptface::Msgid           DontDelete|ReadOnly|Function 0
00715     msgkey          Scriptface::Msgkey          DontDelete|ReadOnly|Function 0
00716     msgstrf         Scriptface::Msgstrf         DontDelete|ReadOnly|Function 0
00717     dbgputs         Scriptface::Dbgputs         DontDelete|ReadOnly|Function 1
00718     warnputs        Scriptface::Warnputs        DontDelete|ReadOnly|Function 1
00719     localeCountry   Scriptface::LocaleCountry   DontDelete|ReadOnly|Function 0
00720     normKey         Scriptface::NormKey         DontDelete|ReadOnly|Function 1
00721     loadProps       Scriptface::LoadProps       DontDelete|ReadOnly|Function 0
00722     getProp         Scriptface::GetProp         DontDelete|ReadOnly|Function 2
00723     setProp         Scriptface::SetProp         DontDelete|ReadOnly|Function 3
00724     toUpperFirst    Scriptface::ToUpperFirst    DontDelete|ReadOnly|Function 2
00725     toLowerFirst    Scriptface::ToLowerFirst    DontDelete|ReadOnly|Function 2
00726     getConfString   Scriptface::GetConfString   DontDelete|ReadOnly|Function 2
00727     getConfBool     Scriptface::GetConfBool     DontDelete|ReadOnly|Function 2
00728     getConfNumber   Scriptface::GetConfNumber   DontDelete|ReadOnly|Function 2
00729 @end
00730 */
00731 /* Source for ScriptfaceTable.
00732 @begin ScriptfaceTable 0
00733 @end
00734 */
00735 
00736 KJS_DEFINE_PROTOTYPE(ScriptfaceProto)
00737 KJS_IMPLEMENT_PROTOFUNC(ScriptfaceProtoFunc)
00738 KJS_IMPLEMENT_PROTOTYPE("Scriptface", ScriptfaceProto, ScriptfaceProtoFunc, ObjectPrototype)
00739 
00740 const ClassInfo Scriptface::info = {"Scriptface", 0, &ScriptfaceTable, 0};
00741 
00742 Scriptface::Scriptface (ExecState *exec, const TsConfigGroup &config_)
00743 : JSObject(ScriptfaceProto::self(exec)), fallback(NULL), config(config_)
00744 {}
00745 
00746 Scriptface::~Scriptface ()
00747 {
00748     qDeleteAll(loadedPmapHandles);
00749 }
00750 
00751 bool Scriptface::getOwnPropertySlot (ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
00752 {
00753     return getStaticValueSlot<Scriptface, JSObject>(exec, &ScriptfaceTable, this, propertyName, slot);
00754 }
00755 
00756 JSValue *Scriptface::getValueProperty (ExecState * /*exec*/, int token) const
00757 {
00758     switch (token) {
00759         default:
00760             dbgout("Scriptface::getValueProperty: Unknown property id %1", token);
00761     }
00762     return jsUndefined();
00763 }
00764 
00765 void Scriptface::put (ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
00766 {
00767     lookupPut<Scriptface, JSObject>(exec, propertyName, value, attr, &ScriptfaceTable, this);
00768 }
00769 
00770 void Scriptface::putValueProperty (ExecState * /*exec*/, int token, JSValue * /*value*/, int /*attr*/)
00771 {
00772     switch(token) {
00773         default:
00774             dbgout("Scriptface::putValueProperty: Unknown property id %1", token);
00775     }
00776 }
00777 
00778 #define CALLARG(i) (args.size() > i ? args[i] : jsNull())
00779 JSValue *ScriptfaceProtoFunc::callAsFunction (ExecState *exec, JSObject *thisObj, const List &args)
00780 {
00781     if (!thisObj->inherits(&Scriptface::info)) {
00782         return throwError(exec, TypeError);
00783     }
00784     Scriptface *obj = static_cast<Scriptface*>(thisObj);
00785     switch (id) {
00786         case Scriptface::Load:
00787             return obj->loadf(exec, args);
00788         case Scriptface::Setcall:
00789             return obj->setcallf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
00790         case Scriptface::Hascall:
00791             return obj->hascallf(exec, CALLARG(0));
00792         case Scriptface::Acall:
00793             return obj->acallf(exec, args);
00794         case Scriptface::SetcallForall:
00795             return obj->setcallForallf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
00796         case Scriptface::Fallback:
00797             return obj->fallbackf(exec);
00798         case Scriptface::Nsubs:
00799             return obj->nsubsf(exec);
00800         case Scriptface::Subs:
00801             return obj->subsf(exec, CALLARG(0));
00802         case Scriptface::Vals:
00803             return obj->valsf(exec, CALLARG(0));
00804         case Scriptface::Msgctxt:
00805             return obj->msgctxtf(exec);
00806         case Scriptface::Dynctxt:
00807             return obj->dynctxtf(exec, CALLARG(0));
00808         case Scriptface::Msgid:
00809             return obj->msgidf(exec);
00810         case Scriptface::Msgkey:
00811             return obj->msgkeyf(exec);
00812         case Scriptface::Msgstrf:
00813             return obj->msgstrff(exec);
00814         case Scriptface::Dbgputs:
00815             return obj->dbgputsf(exec, CALLARG(0));
00816         case Scriptface::Warnputs:
00817             return obj->warnputsf(exec, CALLARG(0));
00818         case Scriptface::LocaleCountry:
00819             return obj->localeCountryf(exec);
00820         case Scriptface::NormKey:
00821             return obj->normKeyf(exec, CALLARG(0));
00822         case Scriptface::LoadProps:
00823             return obj->loadPropsf(exec, args);
00824         case Scriptface::GetProp:
00825             return obj->getPropf(exec, CALLARG(0), CALLARG(1));
00826         case Scriptface::SetProp:
00827             return obj->setPropf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
00828         case Scriptface::ToUpperFirst:
00829             return obj->toUpperFirstf(exec, CALLARG(0), CALLARG(1));
00830         case Scriptface::ToLowerFirst:
00831             return obj->toLowerFirstf(exec, CALLARG(0), CALLARG(1));
00832         case Scriptface::GetConfString:
00833             return obj->getConfStringf(exec, CALLARG(0), CALLARG(1));
00834         case Scriptface::GetConfBool:
00835             return obj->getConfBoolf(exec, CALLARG(0), CALLARG(1));
00836         case Scriptface::GetConfNumber:
00837             return obj->getConfNumberf(exec, CALLARG(0), CALLARG(1));
00838         default:
00839             return jsUndefined();
00840     }
00841 }
00842 
00843 // ----------------------------------------------------------------------
00844 // Scriptface interface functions.
00845 #define SPREF SFNAME"."
00846 
00847 JSValue *Scriptface::loadf (ExecState *exec, const List &fnames)
00848 {
00849     if (globalKTI->currentModulePath.isEmpty())
00850         return throwError(exec, GeneralError,
00851                           SPREF"load: no current module path, aiiie...");
00852 
00853     for (int i = 0; i < fnames.size(); ++i)
00854         if (!fnames[i]->isString())
00855             return throwError(exec, TypeError,
00856                               SPREF"load: expected string as file name");
00857 
00858     for (int i = 0; i < fnames.size(); ++i)
00859     {
00860         QString qfname = fnames[i]->getString().qstring();
00861         QString qfpath = globalKTI->currentModulePath + QLatin1Char('/') + qfname + QLatin1String(".js");
00862 
00863         QFile file(qfpath);
00864         if (!file.open(QIODevice::ReadOnly))
00865             return throwError(exec, GeneralError,
00866                               QString::fromLatin1(SPREF"load: cannot read file '%1'") \
00867                                      .arg(qfpath));
00868 
00869         QTextStream stream(&file);
00870         stream.setCodec("UTF-8");
00871         QString source = stream.readAll();
00872         file.close();
00873 
00874         Completion comp = jsi->evaluate(qfpath, 0, source);
00875 
00876         if (comp.complType() == Throw)
00877         {
00878             JSValue *exval = comp.value();
00879             ExecState *exec = jsi->globalExec();
00880             QString msg = exval->toString(exec).qstring();
00881 
00882             QString line;
00883             if (exval->type() == ObjectType)
00884             {
00885                 JSValue *lval = exval->getObject()->get(exec, "line");
00886                 if (lval->type() == NumberType)
00887                     line = QString::number(lval->toInt32(exec));
00888             }
00889 
00890             return throwError(exec, TypeError,
00891                               QString::fromLatin1("at %1:%2: %3")
00892                                      .arg(qfpath, line, msg));
00893         }
00894         dbgout("Loaded module: %1", qfpath);
00895     }
00896 
00897     return jsUndefined();
00898 }
00899 
00900 JSValue *Scriptface::setcallf (ExecState *exec, JSValue *name,
00901                                JSValue *func, JSValue *fval)
00902 {
00903     if (!name->isString())
00904         return throwError(exec, TypeError,
00905                           SPREF"setcall: expected string as first argument");
00906     if (   !func->isObject()
00907         || !func->getObject()->implementsCall())
00908         return throwError(exec, TypeError,
00909                           SPREF"setcall: expected function as second argument");
00910     if (!(fval->isObject() || fval->isNull()))
00911         return throwError(exec, TypeError,
00912                           SPREF"setcall: expected object or null as third argument");
00913 
00914     QString qname = name->toString(exec).qstring();
00915     funcs[qname] = func->getObject();
00916     fvals[qname] = fval;
00917 
00918     // Register values to keep GC from collecting them. Is this needed?
00919     put(exec, Identifier(QString::fromLatin1("#:f<%1>").arg(qname)), func, Internal);
00920     put(exec, Identifier(QString::fromLatin1("#:o<%1>").arg(qname)), fval, Internal);
00921 
00922     // Set current module path as module path for this call,
00923     // in case it contains load subcalls.
00924     fpaths[qname] = globalKTI->currentModulePath;
00925 
00926     return jsUndefined();
00927 }
00928 
00929 JSValue *Scriptface::hascallf (ExecState *exec, JSValue *name)
00930 {
00931     if (!name->isString())
00932         return throwError(exec, TypeError,
00933                           SPREF"hascall: expected string as first argument");
00934 
00935     QString qname = name->toString(exec).qstring();
00936     return jsBoolean(funcs.contains(qname));
00937 }
00938 
00939 JSValue *Scriptface::acallf (ExecState *exec, const List &argv)
00940 {
00941     if (argv.size() < 1) {
00942         return throwError(exec, SyntaxError,
00943                           SPREF"acall: expected at least one argument (call name)");
00944     }
00945     if (!argv[0]->isString()) {
00946         return throwError(exec, SyntaxError,
00947                           SPREF"acall: expected string as first argument (call name)");
00948     }
00949 
00950     // Get the function and its context object.
00951     QString callname = argv[0]->getString().qstring();
00952     if (!funcs.contains(callname)) {
00953         return throwError(exec, EvalError,
00954                           QString::fromLatin1(SPREF"acall: unregistered call to '%1'").arg(callname));
00955     }
00956     JSObject *func = funcs[callname];
00957     JSValue *fval = fvals[callname];
00958 
00959     // Recover module path from the time of definition of this call,
00960     // for possible load calls.
00961     globalKTI->currentModulePath = fpaths[callname];
00962 
00963     // Execute function.
00964     List arglist;
00965     for (int i = 1; i < argv.size(); ++i)
00966         arglist.append(argv[i]);
00967     JSValue *val;
00968     if (fval->isObject()) {
00969         // Call function with the context object.
00970         val = func->callAsFunction(exec, fval->getObject(), arglist);
00971     }
00972     else {
00973         // No context object associated to this function, use global.
00974         val = func->callAsFunction(exec, jsi->globalObject(), arglist);
00975     }
00976     return val;
00977 }
00978 
00979 JSValue *Scriptface::setcallForallf (ExecState *exec, JSValue *name,
00980                                      JSValue *func, JSValue *fval)
00981 {
00982     if (!name->isString())
00983         return throwError(exec, TypeError,
00984                           SPREF"setcallForall: expected string as first argument");
00985     if (   !func->isObject()
00986         || !func->getObject()->implementsCall())
00987         return throwError(exec, TypeError,
00988                           SPREF"setcallForall: expected function as second argument");
00989     if (!(fval->isObject() || fval->isNull()))
00990         return throwError(exec, TypeError,
00991                           SPREF"setcallForall: expected object or null as third argument");
00992 
00993     QString qname = name->toString(exec).qstring();
00994     funcs[qname] = func->getObject();
00995     fvals[qname] = fval;
00996 
00997     // Register values to keep GC from collecting them. Is this needed?
00998     put(exec, Identifier(QString::fromLatin1("#:fall<%1>").arg(qname)), func, Internal);
00999     put(exec, Identifier(QString::fromLatin1("#:oall<%1>").arg(qname)), fval, Internal);
01000 
01001     // Set current module path as module path for this call,
01002     // in case it contains load subcalls.
01003     fpaths[qname] = globalKTI->currentModulePath;
01004 
01005     // Put in the queue order for execution on all messages.
01006     nameForalls.append(qname);
01007 
01008     return jsUndefined();
01009 }
01010 
01011 JSValue *Scriptface::fallbackf (ExecState *exec)
01012 {
01013     Q_UNUSED(exec);
01014     if (fallback != NULL)
01015         *fallback = true;
01016     return jsUndefined();
01017 }
01018 
01019 JSValue *Scriptface::nsubsf (ExecState *exec)
01020 {
01021     Q_UNUSED(exec);
01022     return jsNumber(subs->size());
01023 }
01024 
01025 JSValue *Scriptface::subsf (ExecState *exec, JSValue *index)
01026 {
01027     if (!index->isNumber())
01028         return throwError(exec, TypeError,
01029                           SPREF"subs: expected number as first argument");
01030 
01031     int i = qRound(index->getNumber());
01032     if (i < 0 || i >= subs->size())
01033         return throwError(exec, RangeError,
01034                           SPREF"subs: index out of range");
01035 
01036     return jsString(subs->at(i));
01037 }
01038 
01039 JSValue *Scriptface::valsf (ExecState *exec, JSValue *index)
01040 {
01041     if (!index->isNumber())
01042         return throwError(exec, TypeError,
01043                           SPREF"vals: expected number as first argument");
01044 
01045     int i = qRound(index->getNumber());
01046     if (i < 0 || i >= vals->size())
01047         return throwError(exec, RangeError,
01048                           SPREF"vals: index out of range");
01049 
01050     return variantToJsValue(vals->at(i));
01051 }
01052 
01053 JSValue *Scriptface::msgctxtf (ExecState *exec)
01054 {
01055     Q_UNUSED(exec);
01056     return jsString(*msgctxt);
01057 }
01058 
01059 JSValue *Scriptface::dynctxtf (ExecState *exec, JSValue *key)
01060 {
01061     if (!key->isString())
01062         return throwError(exec, TypeError,
01063                           SPREF"dynctxt: expected string as first argument");
01064 
01065     QString qkey = key->getString().qstring();
01066     if (dynctxt->contains(qkey)) {
01067         return jsString(dynctxt->value(qkey));
01068     }
01069     return jsUndefined();
01070 }
01071 
01072 JSValue *Scriptface::msgidf (ExecState *exec)
01073 {
01074     Q_UNUSED(exec);
01075     return jsString(*msgid);
01076 }
01077 
01078 JSValue *Scriptface::msgkeyf (ExecState *exec)
01079 {
01080     Q_UNUSED(exec);
01081     return jsString(QString(*msgctxt + QLatin1Char('|') + *msgid));
01082 }
01083 
01084 JSValue *Scriptface::msgstrff (ExecState *exec)
01085 {
01086     Q_UNUSED(exec);
01087     return jsString(*final);
01088 }
01089 
01090 JSValue *Scriptface::dbgputsf (ExecState *exec, JSValue *str)
01091 {
01092     if (!str->isString())
01093         return throwError(exec, TypeError,
01094                           SPREF"dbgputs: expected string as first argument");
01095 
01096     QString qstr = str->getString().qstring();
01097 
01098     dbgout("[JS-debug] %1", qstr);
01099 
01100     return jsUndefined();
01101 }
01102 
01103 JSValue *Scriptface::warnputsf (ExecState *exec, JSValue *str)
01104 {
01105     if (!str->isString())
01106         return throwError(exec, TypeError,
01107                           SPREF"warnputs: expected string as first argument");
01108 
01109     QString qstr = str->getString().qstring();
01110 
01111     warnout("[JS-warning] %1", qstr);
01112 
01113     return jsUndefined();
01114 }
01115 
01116 JSValue *Scriptface::localeCountryf (ExecState *exec)
01117 {
01118     Q_UNUSED(exec);
01119     return jsString(*ctry);
01120 }
01121 
01122 JSValue *Scriptface::normKeyf (ExecState *exec, JSValue *phrase)
01123 {
01124     if (!phrase->isString()) {
01125         return throwError(exec, TypeError,
01126                           SPREF"normKey: expected string as argument");
01127     }
01128 
01129     QByteArray nqphrase = normKeystr(phrase->toString(exec).qstring());
01130     return jsString(QString::fromUtf8(nqphrase));
01131 }
01132 
01133 JSValue *Scriptface::loadPropsf (ExecState *exec, const List &fnames)
01134 {
01135     if (globalKTI->currentModulePath.isEmpty()) {
01136         return throwError(exec, GeneralError,
01137                           SPREF"loadProps: no current module path, aiiie...");
01138     }
01139 
01140     for (int i = 0; i < fnames.size(); ++i) {
01141         if (!fnames[i]->isString()) {
01142             return throwError(exec, TypeError,
01143                               SPREF"loadProps: expected string as file name");
01144         }
01145     }
01146 
01147     for (int i = 0; i < fnames.size(); ++i)
01148     {
01149         QString qfname = fnames[i]->getString().qstring();
01150         QString qfpath_base = globalKTI->currentModulePath + QLatin1Char('/') + qfname;
01151 
01152         // Determine which kind of map is available.
01153         // Give preference to compiled map.
01154         QString qfpath = qfpath_base + QLatin1String(".pmapc");
01155         bool haveCompiled = true;
01156         QFile file_check(qfpath);
01157         if (!file_check.open(QIODevice::ReadOnly)) {
01158             haveCompiled = false;
01159             qfpath = qfpath_base + QLatin1String(".pmap");
01160             QFile file_check(qfpath);
01161             if (!file_check.open(QIODevice::ReadOnly)) {
01162                 return throwError(exec, GeneralError,
01163                                   QString::fromLatin1(SPREF"loadProps: cannot read map '%1'")
01164                                      .arg(qfpath_base));
01165             }
01166         }
01167         file_check.close();
01168 
01169         // Load from appropriate type of map.
01170         if (!loadedPmapPaths.contains(qfpath)) {
01171             QString errorString;
01172             if (haveCompiled) {
01173                 errorString = loadProps_bin(qfpath);
01174             }
01175             else {
01176                 errorString = loadProps_text(qfpath);
01177             }
01178             if (!errorString.isEmpty()) {
01179                 return throwError(exec, SyntaxError, errorString);
01180             }
01181             dbgout("Loaded property map: %1", qfpath);
01182             loadedPmapPaths.insert(qfpath);
01183         }
01184     }
01185 
01186     return jsUndefined();
01187 }
01188 
01189 JSValue *Scriptface::getPropf (ExecState *exec, JSValue *phrase, JSValue *prop)
01190 {
01191     if (!phrase->isString()) {
01192         return throwError(exec, TypeError,
01193                           SPREF"getProp: expected string as first argument");
01194     }
01195     if (!prop->isString()) {
01196         return throwError(exec, TypeError,
01197                           SPREF"getProp: expected string as second argument");
01198     }
01199 
01200     QByteArray qphrase = normKeystr(phrase->toString(exec).qstring());
01201     QHash<QByteArray, QByteArray> props = phraseProps.value(qphrase);
01202     if (props.isEmpty()) {
01203         props = resolveUnparsedProps(qphrase);
01204     }
01205     if (!props.isEmpty()) {
01206         QByteArray qprop = normKeystr(prop->toString(exec).qstring());
01207         QByteArray qval = props.value(qprop);
01208         if (!qval.isEmpty()) {
01209             return jsString(QString::fromUtf8(qval));
01210         }
01211     }
01212     return jsUndefined();
01213 }
01214 
01215 JSValue *Scriptface::setPropf (ExecState *exec, JSValue *phrase, JSValue *prop, JSValue *value)
01216 {
01217     if (!phrase->isString()) {
01218         return throwError(exec, TypeError,
01219                           SPREF"setProp: expected string as first argument");
01220     }
01221     if (!prop->isString()) {
01222         return throwError(exec, TypeError,
01223                           SPREF"setProp: expected string as second argument");
01224     }
01225     if (!value->isString()) {
01226         return throwError(exec, TypeError,
01227                           SPREF"setProp: expected string as third argument");
01228     }
01229 
01230     QByteArray qphrase = normKeystr(phrase->toString(exec).qstring());
01231     QByteArray qprop = normKeystr(prop->toString(exec).qstring());
01232     QByteArray qvalue = value->toString(exec).qstring().toUtf8();
01233     // Any non-existent key in first or second-level hash will be created.
01234     phraseProps[qphrase][qprop] = qvalue;
01235     return jsUndefined();
01236 }
01237 
01238 static QString toCaseFirst (const QString &qstr, int qnalt, bool toupper)
01239 {
01240     static const QLatin1String head("~@");
01241     static const int hlen = 2; //head.length()
01242 
01243     // If the first letter is found within an alternatives directive,
01244     // change case of the first letter in each of the alternatives.
01245     QString qstrcc = qstr;
01246     int len = qstr.length();
01247     QChar altSep;
01248     int remainingAlts = 0;
01249     bool checkCase = true;
01250     int numChcased = 0;
01251     int i = 0;
01252     while (i < len) {
01253         QChar c = qstr[i];
01254 
01255         if (qnalt && !remainingAlts && qstr.mid(i, hlen) == head) {
01256             // An alternatives directive is just starting.
01257             i += 2;
01258             if (i >= len) break; // malformed directive, bail out
01259             // Record alternatives separator, set number of remaining
01260             // alternatives, reactivate case checking.
01261             altSep = qstrcc[i];
01262             remainingAlts = qnalt;
01263             checkCase = true;
01264         }
01265         else if (remainingAlts && c == altSep) {
01266             // Alternative separator found, reduce number of remaining
01267             // alternatives and reactivate case checking.
01268             --remainingAlts;
01269             checkCase = true;
01270         }
01271         else if (checkCase && c.isLetter()) {
01272             // Case check is active and the character is a letter; change case.
01273             if (toupper) {
01274                 qstrcc[i] = c.toUpper();
01275             } else {
01276                 qstrcc[i] = c.toLower();
01277             }
01278             ++numChcased;
01279             // No more case checks until next alternatives separator.
01280             checkCase = false;
01281         }
01282 
01283         // If any letter has been changed, and there are no more alternatives
01284         // to be processed, we're done.
01285         if (numChcased > 0 && remainingAlts == 0) {
01286             break;
01287         }
01288 
01289         // Go to next character.
01290         ++i;
01291     }
01292 
01293     return qstrcc;
01294 }
01295 
01296 JSValue *Scriptface::toUpperFirstf (ExecState *exec,
01297                                     JSValue *str, JSValue *nalt)
01298 {
01299     if (!str->isString()) {
01300         return throwError(exec, TypeError,
01301                           SPREF"toUpperFirst: expected string as first argument");
01302     }
01303     if (!(nalt->isNumber() || nalt->isNull())) {
01304         return throwError(exec, TypeError,
01305                           SPREF"toUpperFirst: expected number as second argument");
01306     }
01307 
01308     QString qstr = str->toString(exec).qstring();
01309     int qnalt = nalt->isNull() ? 0 : nalt->toInteger(exec);
01310 
01311     QString qstruc = toCaseFirst(qstr, qnalt, true);
01312 
01313     return jsString(qstruc);
01314 }
01315 
01316 JSValue *Scriptface::toLowerFirstf (ExecState *exec,
01317                                     JSValue *str, JSValue *nalt)
01318 {
01319     if (!str->isString()) {
01320         return throwError(exec, TypeError,
01321                           SPREF"toLowerFirst: expected string as first argument");
01322     }
01323     if (!(nalt->isNumber() || nalt->isNull())) {
01324         return throwError(exec, TypeError,
01325                           SPREF"toLowerFirst: expected number as second argument");
01326     }
01327 
01328     QString qstr = str->toString(exec).qstring();
01329     int qnalt = nalt->isNull() ? 0 : nalt->toInteger(exec);
01330 
01331     QString qstrlc = toCaseFirst(qstr, qnalt, false);
01332 
01333     return jsString(qstrlc);
01334 }
01335 
01336 JSValue *Scriptface::getConfStringf (ExecState *exec,
01337                                      JSValue *key, JSValue *dval)
01338 {
01339     if (!key->isString()) {
01340         return throwError(exec, TypeError,
01341                           SPREF"getConfString: expected string "
01342                           "as first argument");
01343     }
01344     if (!(dval->isString() || dval->isNull())) {
01345         return throwError(exec, TypeError,
01346                           SPREF"getConfString: expected string "
01347                           "as second argument (when given)");
01348     }
01349 
01350     if (dval->isNull()) {
01351         dval = jsUndefined();
01352     }
01353 
01354     QString qkey = key->getString().qstring();
01355     if (config.contains(qkey)) {
01356         return jsString(config.value(qkey));
01357     }
01358 
01359     return dval;
01360 }
01361 
01362 JSValue *Scriptface::getConfBoolf (ExecState *exec,
01363                                    JSValue *key, JSValue *dval)
01364 {
01365     if (!key->isString()) {
01366         return throwError(exec, TypeError,
01367                           SPREF"getConfBool: expected string as "
01368                           "first argument");
01369     }
01370     if (!(dval->isBoolean() || dval->isNull())) {
01371         return throwError(exec, TypeError,
01372                           SPREF"getConfBool: expected boolean "
01373                           "as second argument (when given)");
01374     }
01375 
01376     static QStringList falsities;
01377     if (falsities.isEmpty()) {
01378         falsities.append(QString(QLatin1Char('0')));
01379         falsities.append(QString::fromLatin1("no"));
01380         falsities.append(QString::fromLatin1("false"));
01381     }
01382 
01383     if (dval->isNull()) {
01384         dval = jsUndefined();
01385     }
01386 
01387     QString qkey = key->getString().qstring();
01388     if (config.contains(qkey)) {
01389         QString qval = config.value(qkey).toLower();
01390         return jsBoolean(!falsities.contains(qval));
01391     }
01392 
01393     return dval;
01394 }
01395 
01396 JSValue *Scriptface::getConfNumberf (ExecState *exec,
01397                                      JSValue *key, JSValue *dval)
01398 {
01399     if (!key->isString()) {
01400         return throwError(exec, TypeError,
01401                           SPREF"getConfNumber: expected string "
01402                           "as first argument");
01403     }
01404     if (!(dval->isNumber() || dval->isNull())) {
01405         return throwError(exec, TypeError,
01406                           SPREF"getConfNumber: expected number "
01407                           "as second argument (when given)");
01408     }
01409 
01410     if (dval->isNull()) {
01411         dval = jsUndefined();
01412     }
01413 
01414     QString qkey = key->getString().qstring();
01415     if (config.contains(qkey)) {
01416         QString qval = config.value(qkey);
01417         bool convOk;
01418         double qnum = qval.toDouble(&convOk);
01419         if (convOk) {
01420             return jsNumber(qnum);
01421         }
01422     }
01423 
01424     return dval;
01425 }
01426 
01427 // ----------------------------------------------------------------------
01428 // Scriptface helpers to interface functions.
01429 
01430 QString Scriptface::loadProps_text (const QString &fpath)
01431 {
01432     QFile file(fpath);
01433     if (!file.open(QIODevice::ReadOnly)) {
01434         return QString::fromLatin1(SPREF"loadProps_text: cannot read file '%1'")
01435                       .arg(fpath);
01436     }
01437     QTextStream stream(&file);
01438     stream.setCodec("UTF-8");
01439     QString s = stream.readAll();
01440     file.close();
01441 
01442     // Parse the map.
01443     // Should care about performance: possibly executed on each KDE
01444     // app startup and reading houndreds of thousands of characters.
01445     enum {s_nextEntry, s_nextKey, s_nextValue};
01446     QList<QByteArray> ekeys; // holds keys for current entry
01447     QHash<QByteArray, QByteArray> props; // holds properties for current entry
01448     int slen = s.length();
01449     int state = s_nextEntry;
01450     QByteArray pkey;
01451     QChar prop_sep, key_sep;
01452     int i = 0;
01453     while (1) {
01454         int i_checkpoint = i;
01455 
01456         if (state == s_nextEntry) {
01457             while (s[i].isSpace()) {
01458                 ++i;
01459                 if (i >= slen) goto END_PROP_PARSE;
01460             }
01461             if (i + 1 >= slen) {
01462                 return QString::fromLatin1(SPREF"loadProps_text: unexpected end "
01463                                "of file in %1").arg(fpath);
01464             }
01465             if (s[i] != QLatin1Char('#')) {
01466                 // Separator characters for this entry.
01467                 key_sep = s[i];
01468                 prop_sep = s[i + 1];
01469                 if (key_sep.isLetter() || prop_sep.isLetter()) {
01470                     return  QString::fromLatin1(SPREF"loadProps_text: separator "
01471                                     "characters must not be letters at %1:%2")
01472                                    .arg(fpath).arg(countLines(s, i));
01473                 }
01474 
01475                 // Reset all data for current entry.
01476                 ekeys.clear();
01477                 props.clear();
01478                 pkey.clear();
01479 
01480                 i += 2;
01481                 state = s_nextKey;
01482             }
01483             else {
01484                 // This is a comment, skip to EOL, don't change state.
01485                 while (s[i] != QLatin1Char('\n')) {
01486                     ++i;
01487                     if (i >= slen) goto END_PROP_PARSE;
01488                 }
01489             }
01490         }
01491         else if (state == s_nextKey) {
01492             int ip = i;
01493             // Proceed up to next key or property separator.
01494             while (s[i] != key_sep && s[i] != prop_sep) {
01495                 ++i;
01496                 if (i >= slen) goto END_PROP_PARSE;
01497             }
01498             if (s[i] == key_sep) {
01499                 // This is a property key,
01500                 // record for when the value gets parsed.
01501                 pkey = normKeystr(s.mid(ip, i - ip), false);
01502 
01503                 i += 1;
01504                 state = s_nextValue;
01505             }
01506             else { // if (s[i] == prop_sep) {
01507                 // This is an entry key, or end of entry.
01508                 QByteArray ekey = normKeystr(s.mid(ip, i - ip), false);
01509                 if (!ekey.isEmpty()) {
01510                     // An entry key.
01511                     ekeys.append(ekey);
01512 
01513                     i += 1;
01514                     state = s_nextKey;
01515                 }
01516                 else {
01517                     // End of entry.
01518                     if (ekeys.size() < 1) {
01519                         return QString::fromLatin1(SPREF"loadProps_text: no entry key "
01520                                        "for entry ending at %1:%2")
01521                                        .arg(fpath).arg(countLines(s, i));
01522                     }
01523 
01524                     // Add collected entry into global store,
01525                     // once for each entry key (QHash implicitly shared).
01526                     foreach (const QByteArray &ekey, ekeys) {
01527                         phraseProps[ekey] = props;
01528                     }
01529 
01530                     i += 1;
01531                     state = s_nextEntry;
01532                 }
01533             }
01534         }
01535         else if (state == s_nextValue) {
01536             int ip = i;
01537             // Proceed up to next property separator.
01538             while (s[i] != prop_sep) {
01539                 ++i;
01540                 if (i >= slen) goto END_PROP_PARSE;
01541                 if (s[i] == key_sep) {
01542                     return QString::fromLatin1(SPREF"loadProps_text: property separator "
01543                                    "inside property value at %1:%2")
01544                                   .arg(fpath).arg(countLines(s, i));
01545                 }
01546             }
01547             // Extract the property value and store the property.
01548             QByteArray pval = trimSmart(s.mid(ip, i - ip)).toUtf8();
01549             props[pkey] = pval;
01550 
01551             i += 1;
01552             state = s_nextKey;
01553         }
01554         else {
01555             return QString::fromLatin1(SPREF"loadProps: internal error 10 at %1:%2")
01556                           .arg(fpath).arg(countLines(s, i));
01557         }
01558 
01559         // To avoid infinite looping and stepping out.
01560         if (i == i_checkpoint || i >= slen) {
01561             return QString::fromLatin1(SPREF"loadProps: internal error 20 at %1:%2")
01562                           .arg(fpath).arg(countLines(s, i));
01563         }
01564     }
01565 
01566     END_PROP_PARSE:
01567 
01568     if (state != s_nextEntry) {
01569         return QString::fromLatin1(SPREF"loadProps: unexpected end of file in %1")
01570                       .arg(fpath);
01571     }
01572 
01573     return QString();
01574 }
01575 
01576 // Read big-endian integer of nbytes length at position pos
01577 // in character array fc of length len.
01578 // Update position to point after the number.
01579 // In case of error, pos is set to -1.
01580 template <typename T>
01581 static int bin_read_int_nbytes (const char *fc, qlonglong len, qlonglong &pos, int nbytes)
01582 {
01583     if (pos + nbytes > len) {
01584         pos = -1;
01585         return 0;
01586     }
01587     T num = qFromBigEndian<T>((uchar*) fc + pos);
01588     pos += nbytes;
01589     return num;
01590 }
01591 
01592 // Read 64-bit big-endian integer.
01593 static quint64 bin_read_int64 (const char *fc, qlonglong len, qlonglong &pos)
01594 {
01595     return bin_read_int_nbytes<quint64>(fc, len, pos, 8);
01596 }
01597 
01598 // Read 32-bit big-endian integer.
01599 static quint32 bin_read_int (const char *fc, qlonglong len, qlonglong &pos)
01600 {
01601     return bin_read_int_nbytes<quint32>(fc, len, pos, 4);
01602 }
01603 
01604 // Read string at position pos of character array fc of length n.
01605 // String is represented as 32-bit big-endian byte length followed by bytes.
01606 // Update position to point after the string.
01607 // In case of error, pos is set to -1.
01608 static QByteArray bin_read_string (const char *fc, qlonglong len, qlonglong &pos)
01609 {
01610     // Binary format stores strings as length followed by byte sequence.
01611     // No null-termination.
01612     int nbytes = bin_read_int(fc, len, pos);
01613     if (pos < 0) {
01614         return QByteArray();
01615     }
01616     if (nbytes < 0 || pos + nbytes > len) {
01617         pos = -1;
01618         return QByteArray();
01619     }
01620     QByteArray s(fc + pos, nbytes);
01621     pos += nbytes;
01622     return s;
01623 }
01624 
01625 QString Scriptface::loadProps_bin (const QString &fpath)
01626 {
01627     QFile file(fpath);
01628     if (!file.open(QIODevice::ReadOnly)) {
01629         return QString::fromLatin1(SPREF"loadProps: cannot read file '%1'")
01630                       .arg(fpath);
01631     }
01632     // Collect header.
01633     QByteArray head(8, '0');
01634     file.read(head.data(), head.size());
01635     file.close();
01636 
01637     // Choose pmap loader based on header.
01638     if (head == "TSPMAP00") {
01639         return loadProps_bin_00(fpath);
01640     } else if (head == "TSPMAP01") {
01641         return loadProps_bin_01(fpath);
01642     }
01643     else {
01644         return QString::fromLatin1(SPREF"loadProps: unknown version of compiled map '%1'")
01645                       .arg(fpath);
01646     }
01647 }
01648 
01649 QString Scriptface::loadProps_bin_00 (const QString &fpath)
01650 {
01651     QFile file(fpath);
01652     if (!file.open(QIODevice::ReadOnly)) {
01653         return QString::fromLatin1(SPREF"loadProps: cannot read file '%1'")
01654                       .arg(fpath);
01655     }
01656     QByteArray fctmp = file.readAll();
01657     file.close();
01658     const char *fc = fctmp.data();
01659     const int fclen = fctmp.size();
01660 
01661     // Indicates stream state.
01662     qlonglong pos = 0;
01663 
01664     // Match header.
01665     QByteArray head(fc, 8);
01666     pos += 8;
01667     if (head != "TSPMAP00") goto END_PROP_PARSE;
01668 
01669     // Read total number of entries.
01670     int nentries;
01671     nentries = bin_read_int(fc, fclen, pos);
01672     if (pos < 0) goto END_PROP_PARSE;
01673 
01674     // Read all entries.
01675     for (int i = 0; i < nentries; ++i) {
01676 
01677         // Read number of entry keys and all entry keys.
01678         QList<QByteArray> ekeys;
01679         int nekeys = bin_read_int(fc, fclen, pos);
01680         if (pos < 0) goto END_PROP_PARSE;
01681         for (int j = 0; j < nekeys; ++j) {
01682             QByteArray ekey = bin_read_string(fc, fclen, pos);
01683             if (pos < 0) goto END_PROP_PARSE;
01684             ekeys.append(ekey);
01685         }
01686         //dbgout("--------> ekey[0]={%1}", QString::fromUtf8(ekeys[0]));
01687 
01688         // Read number of properties and all properties.
01689         QHash<QByteArray, QByteArray> props;
01690         int nprops = bin_read_int(fc, fclen, pos);
01691         if (pos < 0) goto END_PROP_PARSE;
01692         for (int j = 0; j < nprops; ++j) {
01693             QByteArray pkey = bin_read_string(fc, fclen, pos);
01694             if (pos < 0) goto END_PROP_PARSE;
01695             QByteArray pval = bin_read_string(fc, fclen, pos);
01696             if (pos < 0) goto END_PROP_PARSE;
01697             props[pkey] = pval;
01698         }
01699 
01700         // Add collected entry into global store,
01701         // once for each entry key (QHash implicitly shared).
01702         foreach (const QByteArray &ekey, ekeys) {
01703             phraseProps[ekey] = props;
01704         }
01705     }
01706 
01707     END_PROP_PARSE:
01708 
01709     if (pos < 0) {
01710         return QString::fromLatin1(SPREF"loadProps: corrupt compiled map '%1'")
01711                       .arg(fpath);
01712     }
01713 
01714     return QString();
01715 }
01716 
01717 QString Scriptface::loadProps_bin_01 (const QString &fpath)
01718 {
01719     QFile *file = new QFile(fpath);
01720     if (!file->open(QIODevice::ReadOnly)) {
01721         return QString::fromLatin1(SPREF"loadProps: cannot read file '%1'")
01722                       .arg(fpath);
01723     }
01724 
01725     QByteArray fstr;
01726     qlonglong pos;
01727 
01728     // Read the header and number and length of entry keys.
01729     fstr = file->read(8 + 4 + 8);
01730     pos = 0;
01731     QByteArray head = fstr.left(8);
01732     pos += 8;
01733     if (head != "TSPMAP01") {
01734         return QString::fromLatin1(SPREF"loadProps: corrupt compiled map '%1'")
01735                       .arg(fpath);
01736     }
01737     quint32 numekeys = bin_read_int(fstr, fstr.size(), pos);
01738     quint64 lenekeys = bin_read_int64(fstr, fstr.size(), pos);
01739 
01740     // Read entry keys.
01741     fstr = file->read(lenekeys);
01742     pos = 0;
01743     for (quint32 i = 0; i < numekeys; ++i) {
01744         QByteArray ekey = bin_read_string(fstr, lenekeys, pos);
01745         quint64 offset = bin_read_int64(fstr, lenekeys, pos);
01746         phraseUnparsedProps[ekey] = QPair<QFile*, quint64>(file, offset);
01747     }
01748 
01749     // // Read property keys.
01750     // ...when it becomes necessary
01751 
01752     loadedPmapHandles.insert(file);
01753     return QString();
01754 }
01755 
01756 QHash<QByteArray, QByteArray> Scriptface::resolveUnparsedProps (const QByteArray &phrase)
01757 {
01758     QPair<QFile*, quint64> ref = phraseUnparsedProps.value(phrase);
01759     QFile *file = ref.first;
01760     quint64 offset = ref.second;
01761     QHash<QByteArray, QByteArray> props;
01762     if (file != NULL && file->seek(offset)) {
01763         QByteArray fstr = file->read(4 + 4);
01764         qlonglong pos = 0;
01765         quint32 numpkeys = bin_read_int(fstr, fstr.size(), pos);
01766         quint32 lenpkeys = bin_read_int(fstr, fstr.size(), pos);
01767         fstr = file->read(lenpkeys);
01768         pos = 0;
01769         for (quint32 i = 0; i < numpkeys; ++i) {
01770             QByteArray pkey = bin_read_string(fstr, lenpkeys, pos);
01771             QByteArray pval = bin_read_string(fstr, lenpkeys, pos);
01772             props[pkey] = pval;
01773         }
01774         phraseProps[phrase] = props;
01775         phraseUnparsedProps.remove(phrase);
01776     }
01777     return props;
01778 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal