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

KDECore

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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