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

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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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