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

KDECore

  • sources
  • kde-4.14
  • kdelibs
  • kdecore
  • localization
ktranscript.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries Copyright (C) 2007 Chusslove Illich <caslav.ilic@gmx.net>
2 
3  This library is free software; you can redistribute it and/or
4  modify it under the terms of the GNU Library General Public
5  License as published by the Free Software Foundation; either
6  version 2 of the License, or (at your option) any later version.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 */
18 
19 #include <ktranscript_p.h>
20 #include <common_helpers_p.h>
21 
22 #include <config.h>
23 
24 #include <kdecore_export.h>
25 #include <kglobal.h>
26 
27 //#include <unistd.h>
28 
29 #include <kjs/value.h>
30 #include <kjs/object.h>
31 #include <kjs/lookup.h>
32 #include <kjs/function.h>
33 #include <kjs/interpreter.h>
34 #include <kjs/string_object.h>
35 #include <kjs/error_object.h>
36 
37 #include <QVariant>
38 #include <QStringList>
39 #include <QList>
40 #include <QDir>
41 #include <QHash>
42 #include <QPair>
43 #include <QSet>
44 #include <QFile>
45 #include <QIODevice>
46 #include <QTextStream>
47 #include <QRegExp>
48 #include <qendian.h>
49 
50 using namespace KJS;
51 
52 class KTranscriptImp;
53 class Scriptface;
54 
55 typedef QHash<QString, QString> TsConfigGroup;
56 typedef QHash<QString, TsConfigGroup> TsConfig;
57 
58 // Transcript implementation (used as singleton).
59 class KTranscriptImp : public KTranscript
60 {
61  public:
62 
63  KTranscriptImp ();
64  ~KTranscriptImp ();
65 
66  QString eval (const QList<QVariant> &argv,
67  const QString &lang,
68  const QString &ctry,
69  const QString &msgctxt,
70  const QHash<QString, QString> &dynctxt,
71  const QString &msgid,
72  const QStringList &subs,
73  const QList<QVariant> &vals,
74  const QString &final,
75  QList<QStringList> &mods,
76  QString &error,
77  bool &fallback);
78 
79  QStringList postCalls (const QString &lang);
80 
81  // Lexical path of the module for the executing code.
82  QString currentModulePath;
83 
84  private:
85 
86  void loadModules (const QList<QStringList> &mods, QString &error);
87  void setupInterpreter (const QString &lang);
88 
89  TsConfig config;
90 
91  QHash<QString, Scriptface*> m_sface;
92 };
93 
94 // Script-side transcript interface.
95 class Scriptface : public JSObject
96 {
97  public:
98  Scriptface (ExecState *exec, const TsConfigGroup &config);
99  ~Scriptface ();
100 
101  // Interface functions.
102  JSValue *loadf (ExecState *exec, const List &fnames);
103  JSValue *setcallf (ExecState *exec, JSValue *name,
104  JSValue *func, JSValue *fval);
105  JSValue *hascallf (ExecState *exec, JSValue *name);
106  JSValue *acallf (ExecState *exec, const List &argv);
107  JSValue *setcallForallf (ExecState *exec, JSValue *name,
108  JSValue *func, JSValue *fval);
109  JSValue *fallbackf (ExecState *exec);
110  JSValue *nsubsf (ExecState *exec);
111  JSValue *subsf (ExecState *exec, JSValue *index);
112  JSValue *valsf (ExecState *exec, JSValue *index);
113  JSValue *msgctxtf (ExecState *exec);
114  JSValue *dynctxtf (ExecState *exec, JSValue *key);
115  JSValue *msgidf (ExecState *exec);
116  JSValue *msgkeyf (ExecState *exec);
117  JSValue *msgstrff (ExecState *exec);
118  JSValue *dbgputsf (ExecState *exec, JSValue *str);
119  JSValue *warnputsf (ExecState *exec, JSValue *str);
120  JSValue *localeCountryf (ExecState *exec);
121  JSValue *normKeyf (ExecState *exec, JSValue *phrase);
122  JSValue *loadPropsf (ExecState *exec, const List &fnames);
123  JSValue *getPropf (ExecState *exec, JSValue *phrase, JSValue *prop);
124  JSValue *setPropf (ExecState *exec, JSValue *phrase, JSValue *prop, JSValue *value);
125  JSValue *toUpperFirstf (ExecState *exec, JSValue *str, JSValue *nalt);
126  JSValue *toLowerFirstf (ExecState *exec, JSValue *str, JSValue *nalt);
127  JSValue *getConfStringf (ExecState *exec, JSValue *key, JSValue *dval);
128  JSValue *getConfBoolf (ExecState *exec, JSValue *key, JSValue *dval);
129  JSValue *getConfNumberf (ExecState *exec, JSValue *key, JSValue *dval);
130 
131  enum {
132  Load,
133  Setcall,
134  Hascall,
135  Acall,
136  SetcallForall,
137  Fallback,
138  Nsubs,
139  Subs,
140  Vals,
141  Msgctxt,
142  Dynctxt,
143  Msgid,
144  Msgkey,
145  Msgstrf,
146  Dbgputs,
147  Warnputs,
148  LocaleCountry,
149  NormKey,
150  LoadProps,
151  GetProp,
152  SetProp,
153  ToUpperFirst,
154  ToLowerFirst,
155  GetConfString,
156  GetConfBool,
157  GetConfNumber
158  };
159 
160  // Helper methods to interface functions.
161  QString loadProps_text (const QString &fpath);
162  QString loadProps_bin (const QString &fpath);
163  QString loadProps_bin_00 (const QString &fpath);
164  QString loadProps_bin_01 (const QString &fpath);
165 
166  // Virtual implementations.
167  bool getOwnPropertySlot (ExecState *exec, const Identifier& propertyName, PropertySlot& slot);
168  JSValue *getValueProperty (ExecState *exec, int token) const;
169  void put (ExecState *exec, const Identifier &propertyName, JSValue *value, int attr);
170  void putValueProperty (ExecState *exec, int token, JSValue *value, int attr);
171  const ClassInfo* classInfo() const { return &info; }
172 
173  static const ClassInfo info;
174 
175  // Link to its interpreter.
176  // FIXME: Probably accessible without the explicit link.
177  Interpreter *jsi;
178 
179  // Current message data.
180  const QString *msgctxt;
181  const QHash<QString, QString> *dynctxt;
182  const QString *msgid;
183  const QStringList *subs;
184  const QList<QVariant> *vals;
185  const QString *final;
186  const QString *ctry;
187 
188  // Fallback request handle.
189  bool *fallback;
190 
191  // Function register.
192  QHash<QString, JSObject*> funcs;
193  QHash<QString, JSValue*> fvals;
194  QHash<QString, QString> fpaths;
195 
196  // Ordering of those functions which execute for all messages.
197  QList<QString> nameForalls;
198 
199  // Property values per phrase (used by *Prop interface calls).
200  // Not QStrings, in order to avoid conversion from UTF-8 when
201  // loading compiled maps (less latency on startup).
202  QHash<QByteArray, QHash<QByteArray, QByteArray> > phraseProps;
203  // Unresolved property values per phrase,
204  // containing the pointer to compiled pmap file handle and offset in it.
205  QHash<QByteArray, QPair<QFile*, quint64> > phraseUnparsedProps;
206  QHash<QByteArray, QByteArray> resolveUnparsedProps (const QByteArray &phrase);
207  // Set of loaded pmap files by paths and file handle pointers.
208  QSet<QString> loadedPmapPaths;
209  QSet<QFile*> loadedPmapHandles;
210 
211  // User config.
212  TsConfigGroup config;
213 };
214 
215 // ----------------------------------------------------------------------
216 // Custom debug and warning output (kdebug not available)
217 #define DBGP "KTranscript: "
218 void dbgout (const char*str) {
219  #ifndef NDEBUG
220  fprintf(stderr, DBGP"%s\n", str);
221  #else
222  Q_UNUSED(str);
223  #endif
224 }
225 template <typename T1>
226 void dbgout (const char* str, const T1 &a1) {
227  #ifndef NDEBUG
228  fprintf(stderr, DBGP"%s\n", QString::fromUtf8(str).arg(a1).toLocal8Bit().data());
229  #else
230  Q_UNUSED(str); Q_UNUSED(a1);
231  #endif
232 }
233 template <typename T1, typename T2>
234 void dbgout (const char* str, const T1 &a1, const T2 &a2) {
235  #ifndef NDEBUG
236  fprintf(stderr, DBGP"%s\n", QString::fromUtf8(str).arg(a1).arg(a2).toLocal8Bit().data());
237  #else
238  Q_UNUSED(str); Q_UNUSED(a1); Q_UNUSED(a2);
239  #endif
240 }
241 template <typename T1, typename T2, typename T3>
242 void dbgout (const char* str, const T1 &a1, const T2 &a2, const T3 &a3) {
243  #ifndef NDEBUG
244  fprintf(stderr, DBGP"%s\n", QString::fromUtf8(str).arg(a1).arg(a2).arg(a3).toLocal8Bit().data());
245  #else
246  Q_UNUSED(str); Q_UNUSED(a1); Q_UNUSED(a2); Q_UNUSED(a3);
247  #endif
248 }
249 
250 #define WARNP "KTranscript: "
251 void warnout (const char*str) {
252  fprintf(stderr, WARNP"%s\n", str);
253 }
254 template <typename T1>
255 void warnout (const char* str, const T1 &a1) {
256  fprintf(stderr, WARNP"%s\n", QString::fromUtf8(str).arg(a1).toLocal8Bit().data());
257 }
258 
259 // ----------------------------------------------------------------------
260 // Conversions between QString and KJS UString.
261 // Taken from kate.
262 UString::UString(const QString &d)
263 {
264  unsigned int len = d.length();
265  UChar *dat = static_cast<UChar*>(fastMalloc(sizeof(UChar) * len));
266  memcpy(dat, d.unicode(), len * sizeof(UChar));
267  m_rep = UString::Rep::create(dat, len);
268 }
269 QString UString::qstring() const
270 {
271  return QString((QChar*) data(), size());
272 }
273 
274 // ----------------------------------------------------------------------
275 // Produces a string out of a KJS exception.
276 QString expt2str (ExecState *exec)
277 {
278  JSValue *expt = exec->exception();
279  if ( expt->isObject()
280  && expt->getObject()->hasProperty(exec, "message"))
281  {
282  JSValue *msg = expt->getObject()->get(exec, "message");
283  return QString::fromLatin1("Error: %1").arg(msg->getString().qstring());
284  }
285  else
286  {
287  QString strexpt = exec->exception()->toString(exec).qstring();
288  return QString::fromLatin1("Caught exception: %1").arg(strexpt);
289  }
290 }
291 
292 // ----------------------------------------------------------------------
293 // Count number of lines in the string,
294 // up to and excluding the requested position.
295 int countLines (const QString &s, int p)
296 {
297  int n = 1;
298  int len = s.length();
299  for (int i = 0; i < p && i < len; ++i) {
300  if (s[i] == QLatin1Char('\n')) {
301  ++n;
302  }
303  }
304  return n;
305 }
306 
307 // ----------------------------------------------------------------------
308 // Normalize string key for hash lookups,
309 QByteArray normKeystr (const QString &raw, bool mayHaveAcc = true)
310 {
311  // NOTE: Regexes should not be used here for performance reasons.
312  // This function may potentially be called thousands of times
313  // on application startup.
314 
315  QString key = raw;
316 
317  // Strip all whitespace.
318  int len = key.length();
319  QString nkey;
320  for (int i = 0; i < len; ++i) {
321  QChar c = key[i];
322  if (!c.isSpace()) {
323  nkey.append(c);
324  }
325  }
326  key = nkey;
327 
328  // Strip accelerator marker.
329  if (mayHaveAcc) {
330  key = removeAcceleratorMarker(key);
331  }
332 
333  // Convert to lower case.
334  key = key.toLower();
335 
336  return key.toUtf8();
337 }
338 
339 // ----------------------------------------------------------------------
340 // Trim multiline string in a "smart" way:
341 // Remove leading and trailing whitespace up to and including first
342 // newline from that side, if there is one; otherwise, don't touch.
343 QString trimSmart (const QString &raw)
344 {
345  // NOTE: This could be done by a single regex, but is not due to
346  // performance reasons.
347  // This function may potentially be called thousands of times
348  // on application startup.
349 
350  int len = raw.length();
351 
352  int is = 0;
353  while (is < len && raw[is].isSpace() && raw[is] != QLatin1Char('\n')) {
354  ++is;
355  }
356  if (is >= len || raw[is] != QLatin1Char('\n')) {
357  is = -1;
358  }
359 
360  int ie = len - 1;
361  while (ie >= 0 && raw[ie].isSpace() && raw[ie] != QLatin1Char('\n')) {
362  --ie;
363  }
364  if (ie < 0 || raw[ie] != QLatin1Char('\n')) {
365  ie = len;
366  }
367 
368  return raw.mid(is + 1, ie - is - 1);
369 }
370 
371 // ----------------------------------------------------------------------
372 // Produce a JavaScript object out of Qt variant.
373 JSValue *variantToJsValue (const QVariant &val)
374 {
375  QVariant::Type vtype = val.type();
376  if (vtype == QVariant::String)
377  return jsString(val.toString());
378  else if ( vtype == QVariant::Double \
379  || vtype == QVariant::Int || vtype == QVariant::UInt \
380  || vtype == QVariant::LongLong || vtype == QVariant::ULongLong)
381  return jsNumber(val.toDouble());
382  else
383  return jsUndefined();
384 }
385 
386 // ----------------------------------------------------------------------
387 // Parse ini-style config file,
388 // returning content as hash of hashes by group and key.
389 // Parsing is not fussy, it will read what it can.
390 TsConfig readConfig (const QString &fname)
391 {
392  TsConfig config;
393  // Add empty group.
394  TsConfig::iterator configGroup;
395  configGroup = config.insert(QString(), TsConfigGroup());
396 
397  QFile file(fname);
398  if (!file.open(QIODevice::ReadOnly)) {
399  return config;
400  }
401  QTextStream stream(&file);
402  stream.setCodec("UTF-8");
403  while (!stream.atEnd()) {
404  QString line = stream.readLine();
405  int p1, p2;
406 
407  // Remove comment from the line.
408  p1 = line.indexOf(QLatin1Char('#'));
409  if (p1 >= 0) {
410  line = line.left(p1);
411  }
412  line = line.trimmed();
413  if (line.isEmpty()) {
414  continue;
415  }
416 
417  if (line[0] == QLatin1Char('[')) {
418  // Group switch.
419  p1 = 0;
420  p2 = line.indexOf(QLatin1Char(']'), p1 + 1);
421  if (p2 < 0) {
422  continue;
423  }
424  QString group = line.mid(p1 + 1, p2 - p1 - 1).trimmed();
425  configGroup = config.find(group);
426  if (configGroup == config.end()) {
427  // Add new group.
428  configGroup = config.insert(group, TsConfigGroup());
429  }
430  } else {
431  // Field.
432  p1 = line.indexOf(QLatin1Char('='));
433  if (p1 < 0) {
434  continue;
435  }
436  QString field = line.left(p1).trimmed();
437  QString value = line.mid(p1 + 1).trimmed();
438  if (!field.isEmpty()) {
439  (*configGroup)[field] = value;
440  }
441  }
442  }
443  file.close();
444 
445  return config;
446 }
447 
448 // ----------------------------------------------------------------------
449 // Dynamic loading.
450 K_GLOBAL_STATIC(KTranscriptImp, globalKTI)
451 extern "C"
452 {
453  KDE_EXPORT KTranscript *load_transcript ()
454  {
455  return globalKTI;
456  }
457 }
458 
459 // ----------------------------------------------------------------------
460 // KTranscript definitions.
461 
462 KTranscriptImp::KTranscriptImp ()
463 {
464  // Load user configuration.
465  const QString tsConfigPath = QDir::homePath() + QLatin1Char('/') + QLatin1String(".transcriptrc");
466  config = readConfig(tsConfigPath);
467 }
468 
469 KTranscriptImp::~KTranscriptImp ()
470 {
471  // FIXME: vallgrind shows an afwul lot of "invalid read" in WTF:: stuff
472  // when deref is called... Are we leaking somewhere?
473  //foreach (Scriptface *sface, m_sface.values())
474  // sface->jsi->deref();
475 }
476 
477 QString KTranscriptImp::eval (const QList<QVariant> &argv,
478  const QString &lang,
479  const QString &ctry,
480  const QString &msgctxt,
481  const QHash<QString, QString> &dynctxt,
482  const QString &msgid,
483  const QStringList &subs,
484  const QList<QVariant> &vals,
485  const QString &final,
486  QList<QStringList> &mods,
487  QString &error,
488  bool &fallback)
489 {
490  //error = "debug"; return QString();
491 
492  error.clear(); // empty error message means successful evaluation
493  fallback = false; // fallback not requested
494 
495  #if 0
496  // FIXME: Maybe not needed, as KJS has no native outside access?
497  // Unportable (needs unistd.h)?
498 
499  // If effective user id is root and real user id is not root.
500  if (geteuid() == 0 && getuid() != 0)
501  {
502  // Since scripts are user input, and the program is running with
503  // root permissions while real user is not root, do not invoke
504  // scripting at all, to prevent exploits.
505  error = "Security block: trying to execute a script in suid environment.";
506  return QString();
507  }
508  #endif
509 
510  // Load any new modules and clear the list.
511  if (!mods.isEmpty())
512  {
513  loadModules(mods, error);
514  mods.clear();
515  if (!error.isEmpty())
516  return QString();
517  }
518 
519  // Add interpreters for new languages.
520  // (though it should never happen here, but earlier when loading modules;
521  // this also means there are no calls set, so the unregistered call error
522  // below will be reported).
523  if (!m_sface.contains(lang))
524  setupInterpreter(lang);
525 
526  // Shortcuts.
527  Scriptface *sface = m_sface[lang];
528  ExecState *exec = sface->jsi->globalExec();
529  JSObject *gobj = sface->jsi->globalObject();
530 
531  // Link current message data for script-side interface.
532  sface->msgctxt = &msgctxt;
533  sface->dynctxt = &dynctxt;
534  sface->msgid = &msgid;
535  sface->subs = &subs;
536  sface->vals = &vals;
537  sface->final = &final;
538  sface->fallback = &fallback;
539  sface->ctry = &ctry;
540 
541  // Find corresponding JS function.
542  int argc = argv.size();
543  if (argc < 1)
544  {
545  //error = "At least the call name must be supplied.";
546  // Empty interpolation is OK, possibly used just to initialize
547  // at a given point (e.g. for Ts.setForall() to start having effect).
548  return QString();
549  }
550  QString funcName = argv[0].toString();
551  if (!sface->funcs.contains(funcName))
552  {
553  error = QString::fromLatin1("Unregistered call to '%1'.").arg(funcName);
554  return QString();
555  }
556  JSObject *func = sface->funcs[funcName];
557  JSValue *fval = sface->fvals[funcName];
558 
559  // Recover module path from the time of definition of this call,
560  // for possible load calls.
561  currentModulePath = sface->fpaths[funcName];
562 
563  // Execute function.
564  List arglist;
565  for (int i = 1; i < argc; ++i)
566  arglist.append(variantToJsValue(argv[i]));
567  JSValue *val;
568  if (fval->isObject())
569  val = func->callAsFunction(exec, fval->getObject(), arglist);
570  else // no object associated to this function, use global
571  val = func->callAsFunction(exec, gobj, arglist);
572 
573  if (fallback)
574  // Fallback to ordinary translation requested.
575  {
576  // Possibly clear exception state.
577  if (exec->hadException())
578  exec->clearException();
579 
580  return QString();
581  }
582  else if (!exec->hadException())
583  // Evaluation successful.
584  {
585  if (val->isString())
586  // Good to go.
587  {
588  return val->getString().qstring();
589  }
590  else
591  // Accept only strings.
592  {
593  QString strval = val->toString(exec).qstring();
594  error = QString::fromLatin1("Non-string return value: %1").arg(strval);
595  return QString();
596  }
597  }
598  else
599  // Exception raised.
600  {
601  error = expt2str(exec);
602 
603  exec->clearException();
604 
605  return QString();
606  }
607 }
608 
609 QStringList KTranscriptImp::postCalls (const QString &lang)
610 {
611  // Return no calls if scripting was not already set up for this language.
612  // NOTE: This shouldn't happen, as postCalls cannot be called in such case.
613  if (!m_sface.contains(lang))
614  return QStringList();
615 
616  // Shortcuts.
617  Scriptface *sface = m_sface[lang];
618 
619  return sface->nameForalls;
620 }
621 
622 void KTranscriptImp::loadModules (const QList<QStringList> &mods,
623  QString &error)
624 {
625  QList<QString> modErrors;
626 
627  foreach (const QStringList &mod, mods)
628  {
629  QString mpath = mod[0];
630  QString mlang = mod[1];
631 
632  // Add interpreters for new languages.
633  if (!m_sface.contains(mlang))
634  setupInterpreter(mlang);
635 
636  // Setup current module path for loading submodules.
637  // (sort of closure over invocations of loadf)
638  int posls = mpath.lastIndexOf(QLatin1Char('/'));
639  if (posls < 1)
640  {
641  modErrors.append(QString::fromLatin1("Funny module path '%1', skipping.")
642  .arg(mpath));
643  continue;
644  }
645  currentModulePath = mpath.left(posls);
646  QString fname = mpath.mid(posls + 1);
647  // Scriptface::loadf() wants no extension on the filename
648  fname = fname.left(fname.lastIndexOf(QLatin1Char('.')));
649 
650  // Load the module.
651  ExecState *exec = m_sface[mlang]->jsi->globalExec();
652  List alist;
653  alist.append(jsString(fname));
654 
655  m_sface[mlang]->loadf(exec, alist);
656 
657  // Handle any exception.
658  if (exec->hadException())
659  {
660  modErrors.append(expt2str(exec));
661  exec->clearException();
662  }
663  }
664 
665  // Unset module path.
666  currentModulePath.clear();
667 
668  foreach (const QString &merr, modErrors)
669  error.append(merr + QLatin1Char('\n'));
670 }
671 
672 KJS_QT_UNICODE_IMPL
673 
674 #define SFNAME "Ts"
675 void KTranscriptImp::setupInterpreter (const QString &lang)
676 {
677  // Create new interpreter.
678  Interpreter *jsi = new Interpreter;
679  KJS_QT_UNICODE_SET;
680  jsi->initGlobalObject();
681  jsi->ref();
682 
683  // Add scripting interface into the interpreter.
684  // NOTE: Config may not contain an entry for the language, in which case
685  // it is automatically constructed as an empty hash. This is intended.
686  Scriptface *sface = new Scriptface(jsi->globalExec(), config[lang]);
687  jsi->globalObject()->put(jsi->globalExec(), SFNAME, sface,
688  DontDelete|ReadOnly);
689 
690  // Store scriptface and link to its interpreter.
691  sface->jsi = jsi;
692  m_sface[lang] = sface;
693 
694  //dbgout("=====> Created interpreter for '%1'", lang);
695 }
696 
697 // ----------------------------------------------------------------------
698 // Scriptface internal mechanics.
699 #include "ktranscript.lut.h"
700 
701 /* Source for ScriptfaceProtoTable.
702 @begin ScriptfaceProtoTable 2
703  load Scriptface::Load DontDelete|ReadOnly|Function 0
704  setcall Scriptface::Setcall DontDelete|ReadOnly|Function 3
705  hascall Scriptface::Hascall DontDelete|ReadOnly|Function 1
706  acall Scriptface::Acall DontDelete|ReadOnly|Function 0
707  setcallForall Scriptface::SetcallForall DontDelete|ReadOnly|Function 3
708  fallback Scriptface::Fallback DontDelete|ReadOnly|Function 0
709  nsubs Scriptface::Nsubs DontDelete|ReadOnly|Function 0
710  subs Scriptface::Subs DontDelete|ReadOnly|Function 1
711  vals Scriptface::Vals DontDelete|ReadOnly|Function 1
712  msgctxt Scriptface::Msgctxt DontDelete|ReadOnly|Function 0
713  dynctxt Scriptface::Dynctxt DontDelete|ReadOnly|Function 1
714  msgid Scriptface::Msgid DontDelete|ReadOnly|Function 0
715  msgkey Scriptface::Msgkey DontDelete|ReadOnly|Function 0
716  msgstrf Scriptface::Msgstrf DontDelete|ReadOnly|Function 0
717  dbgputs Scriptface::Dbgputs DontDelete|ReadOnly|Function 1
718  warnputs Scriptface::Warnputs DontDelete|ReadOnly|Function 1
719  localeCountry Scriptface::LocaleCountry DontDelete|ReadOnly|Function 0
720  normKey Scriptface::NormKey DontDelete|ReadOnly|Function 1
721  loadProps Scriptface::LoadProps DontDelete|ReadOnly|Function 0
722  getProp Scriptface::GetProp DontDelete|ReadOnly|Function 2
723  setProp Scriptface::SetProp DontDelete|ReadOnly|Function 3
724  toUpperFirst Scriptface::ToUpperFirst DontDelete|ReadOnly|Function 2
725  toLowerFirst Scriptface::ToLowerFirst DontDelete|ReadOnly|Function 2
726  getConfString Scriptface::GetConfString DontDelete|ReadOnly|Function 2
727  getConfBool Scriptface::GetConfBool DontDelete|ReadOnly|Function 2
728  getConfNumber Scriptface::GetConfNumber DontDelete|ReadOnly|Function 2
729 @end
730 */
731 /* Source for ScriptfaceTable.
732 @begin ScriptfaceTable 0
733 @end
734 */
735 
736 KJS_DEFINE_PROTOTYPE(ScriptfaceProto)
737 KJS_IMPLEMENT_PROTOFUNC(ScriptfaceProtoFunc)
738 KJS_IMPLEMENT_PROTOTYPE("Scriptface", ScriptfaceProto, ScriptfaceProtoFunc, ObjectPrototype)
739 
740 const ClassInfo Scriptface::info = {"Scriptface", 0, &ScriptfaceTable, 0};
741 
742 Scriptface::Scriptface (ExecState *exec, const TsConfigGroup &config_)
743 : JSObject(ScriptfaceProto::self(exec)), fallback(NULL), config(config_)
744 {}
745 
746 Scriptface::~Scriptface ()
747 {
748  qDeleteAll(loadedPmapHandles);
749 }
750 
751 bool Scriptface::getOwnPropertySlot (ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
752 {
753  return getStaticValueSlot<Scriptface, JSObject>(exec, &ScriptfaceTable, this, propertyName, slot);
754 }
755 
756 JSValue *Scriptface::getValueProperty (ExecState * /*exec*/, int token) const
757 {
758  switch (token) {
759  default:
760  dbgout("Scriptface::getValueProperty: Unknown property id %1", token);
761  }
762  return jsUndefined();
763 }
764 
765 void Scriptface::put (ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
766 {
767  lookupPut<Scriptface, JSObject>(exec, propertyName, value, attr, &ScriptfaceTable, this);
768 }
769 
770 void Scriptface::putValueProperty (ExecState * /*exec*/, int token, JSValue * /*value*/, int /*attr*/)
771 {
772  switch(token) {
773  default:
774  dbgout("Scriptface::putValueProperty: Unknown property id %1", token);
775  }
776 }
777 
778 #define CALLARG(i) (args.size() > i ? args[i] : jsNull())
779 JSValue *ScriptfaceProtoFunc::callAsFunction (ExecState *exec, JSObject *thisObj, const List &args)
780 {
781  if (!thisObj->inherits(&Scriptface::info)) {
782  return throwError(exec, TypeError);
783  }
784  Scriptface *obj = static_cast<Scriptface*>(thisObj);
785  switch (id) {
786  case Scriptface::Load:
787  return obj->loadf(exec, args);
788  case Scriptface::Setcall:
789  return obj->setcallf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
790  case Scriptface::Hascall:
791  return obj->hascallf(exec, CALLARG(0));
792  case Scriptface::Acall:
793  return obj->acallf(exec, args);
794  case Scriptface::SetcallForall:
795  return obj->setcallForallf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
796  case Scriptface::Fallback:
797  return obj->fallbackf(exec);
798  case Scriptface::Nsubs:
799  return obj->nsubsf(exec);
800  case Scriptface::Subs:
801  return obj->subsf(exec, CALLARG(0));
802  case Scriptface::Vals:
803  return obj->valsf(exec, CALLARG(0));
804  case Scriptface::Msgctxt:
805  return obj->msgctxtf(exec);
806  case Scriptface::Dynctxt:
807  return obj->dynctxtf(exec, CALLARG(0));
808  case Scriptface::Msgid:
809  return obj->msgidf(exec);
810  case Scriptface::Msgkey:
811  return obj->msgkeyf(exec);
812  case Scriptface::Msgstrf:
813  return obj->msgstrff(exec);
814  case Scriptface::Dbgputs:
815  return obj->dbgputsf(exec, CALLARG(0));
816  case Scriptface::Warnputs:
817  return obj->warnputsf(exec, CALLARG(0));
818  case Scriptface::LocaleCountry:
819  return obj->localeCountryf(exec);
820  case Scriptface::NormKey:
821  return obj->normKeyf(exec, CALLARG(0));
822  case Scriptface::LoadProps:
823  return obj->loadPropsf(exec, args);
824  case Scriptface::GetProp:
825  return obj->getPropf(exec, CALLARG(0), CALLARG(1));
826  case Scriptface::SetProp:
827  return obj->setPropf(exec, CALLARG(0), CALLARG(1), CALLARG(2));
828  case Scriptface::ToUpperFirst:
829  return obj->toUpperFirstf(exec, CALLARG(0), CALLARG(1));
830  case Scriptface::ToLowerFirst:
831  return obj->toLowerFirstf(exec, CALLARG(0), CALLARG(1));
832  case Scriptface::GetConfString:
833  return obj->getConfStringf(exec, CALLARG(0), CALLARG(1));
834  case Scriptface::GetConfBool:
835  return obj->getConfBoolf(exec, CALLARG(0), CALLARG(1));
836  case Scriptface::GetConfNumber:
837  return obj->getConfNumberf(exec, CALLARG(0), CALLARG(1));
838  default:
839  return jsUndefined();
840  }
841 }
842 
843 // ----------------------------------------------------------------------
844 // Scriptface interface functions.
845 #define SPREF SFNAME"."
846 
847 JSValue *Scriptface::loadf (ExecState *exec, const List &fnames)
848 {
849  if (globalKTI->currentModulePath.isEmpty())
850  return throwError(exec, GeneralError,
851  SPREF"load: no current module path, aiiie...");
852 
853  for (int i = 0; i < fnames.size(); ++i)
854  if (!fnames[i]->isString())
855  return throwError(exec, TypeError,
856  SPREF"load: expected string as file name");
857 
858  for (int i = 0; i < fnames.size(); ++i)
859  {
860  QString qfname = fnames[i]->getString().qstring();
861  QString qfpath = globalKTI->currentModulePath + QLatin1Char('/') + qfname + QLatin1String(".js");
862 
863  QFile file(qfpath);
864  if (!file.open(QIODevice::ReadOnly))
865  return throwError(exec, GeneralError,
866  QString::fromLatin1(SPREF"load: cannot read file '%1'") \
867  .arg(qfpath));
868 
869  QTextStream stream(&file);
870  stream.setCodec("UTF-8");
871  QString source = stream.readAll();
872  file.close();
873 
874  Completion comp = jsi->evaluate(qfpath, 0, source);
875 
876  if (comp.complType() == Throw)
877  {
878  JSValue *exval = comp.value();
879  ExecState *exec = jsi->globalExec();
880  QString msg = exval->toString(exec).qstring();
881 
882  QString line;
883  if (exval->type() == ObjectType)
884  {
885  JSValue *lval = exval->getObject()->get(exec, "line");
886  if (lval->type() == NumberType)
887  line = QString::number(lval->toInt32(exec));
888  }
889 
890  return throwError(exec, TypeError,
891  QString::fromLatin1("at %1:%2: %3")
892  .arg(qfpath, line, msg));
893  }
894  dbgout("Loaded module: %1", qfpath);
895  }
896 
897  return jsUndefined();
898 }
899 
900 JSValue *Scriptface::setcallf (ExecState *exec, JSValue *name,
901  JSValue *func, JSValue *fval)
902 {
903  if (!name->isString())
904  return throwError(exec, TypeError,
905  SPREF"setcall: expected string as first argument");
906  if ( !func->isObject()
907  || !func->getObject()->implementsCall())
908  return throwError(exec, TypeError,
909  SPREF"setcall: expected function as second argument");
910  if (!(fval->isObject() || fval->isNull()))
911  return throwError(exec, TypeError,
912  SPREF"setcall: expected object or null as third argument");
913 
914  QString qname = name->toString(exec).qstring();
915  funcs[qname] = func->getObject();
916  fvals[qname] = fval;
917 
918  // Register values to keep GC from collecting them. Is this needed?
919  put(exec, Identifier(QString::fromLatin1("#:f<%1>").arg(qname)), func, Internal);
920  put(exec, Identifier(QString::fromLatin1("#:o<%1>").arg(qname)), fval, Internal);
921 
922  // Set current module path as module path for this call,
923  // in case it contains load subcalls.
924  fpaths[qname] = globalKTI->currentModulePath;
925 
926  return jsUndefined();
927 }
928 
929 JSValue *Scriptface::hascallf (ExecState *exec, JSValue *name)
930 {
931  if (!name->isString())
932  return throwError(exec, TypeError,
933  SPREF"hascall: expected string as first argument");
934 
935  QString qname = name->toString(exec).qstring();
936  return jsBoolean(funcs.contains(qname));
937 }
938 
939 JSValue *Scriptface::acallf (ExecState *exec, const List &argv)
940 {
941  if (argv.size() < 1) {
942  return throwError(exec, SyntaxError,
943  SPREF"acall: expected at least one argument (call name)");
944  }
945  if (!argv[0]->isString()) {
946  return throwError(exec, SyntaxError,
947  SPREF"acall: expected string as first argument (call name)");
948  }
949 
950  // Get the function and its context object.
951  QString callname = argv[0]->getString().qstring();
952  if (!funcs.contains(callname)) {
953  return throwError(exec, EvalError,
954  QString::fromLatin1(SPREF"acall: unregistered call to '%1'").arg(callname));
955  }
956  JSObject *func = funcs[callname];
957  JSValue *fval = fvals[callname];
958 
959  // Recover module path from the time of definition of this call,
960  // for possible load calls.
961  globalKTI->currentModulePath = fpaths[callname];
962 
963  // Execute function.
964  List arglist;
965  for (int i = 1; i < argv.size(); ++i)
966  arglist.append(argv[i]);
967  JSValue *val;
968  if (fval->isObject()) {
969  // Call function with the context object.
970  val = func->callAsFunction(exec, fval->getObject(), arglist);
971  }
972  else {
973  // No context object associated to this function, use global.
974  val = func->callAsFunction(exec, jsi->globalObject(), arglist);
975  }
976  return val;
977 }
978 
979 JSValue *Scriptface::setcallForallf (ExecState *exec, JSValue *name,
980  JSValue *func, JSValue *fval)
981 {
982  if (!name->isString())
983  return throwError(exec, TypeError,
984  SPREF"setcallForall: expected string as first argument");
985  if ( !func->isObject()
986  || !func->getObject()->implementsCall())
987  return throwError(exec, TypeError,
988  SPREF"setcallForall: expected function as second argument");
989  if (!(fval->isObject() || fval->isNull()))
990  return throwError(exec, TypeError,
991  SPREF"setcallForall: expected object or null as third argument");
992 
993  QString qname = name->toString(exec).qstring();
994  funcs[qname] = func->getObject();
995  fvals[qname] = fval;
996 
997  // Register values to keep GC from collecting them. Is this needed?
998  put(exec, Identifier(QString::fromLatin1("#:fall<%1>").arg(qname)), func, Internal);
999  put(exec, Identifier(QString::fromLatin1("#:oall<%1>").arg(qname)), fval, Internal);
1000 
1001  // Set current module path as module path for this call,
1002  // in case it contains load subcalls.
1003  fpaths[qname] = globalKTI->currentModulePath;
1004 
1005  // Put in the queue order for execution on all messages.
1006  nameForalls.append(qname);
1007 
1008  return jsUndefined();
1009 }
1010 
1011 JSValue *Scriptface::fallbackf (ExecState *exec)
1012 {
1013  Q_UNUSED(exec);
1014  if (fallback != NULL)
1015  *fallback = true;
1016  return jsUndefined();
1017 }
1018 
1019 JSValue *Scriptface::nsubsf (ExecState *exec)
1020 {
1021  Q_UNUSED(exec);
1022  return jsNumber(subs->size());
1023 }
1024 
1025 JSValue *Scriptface::subsf (ExecState *exec, JSValue *index)
1026 {
1027  if (!index->isNumber())
1028  return throwError(exec, TypeError,
1029  SPREF"subs: expected number as first argument");
1030 
1031  int i = qRound(index->getNumber());
1032  if (i < 0 || i >= subs->size())
1033  return throwError(exec, RangeError,
1034  SPREF"subs: index out of range");
1035 
1036  return jsString(subs->at(i));
1037 }
1038 
1039 JSValue *Scriptface::valsf (ExecState *exec, JSValue *index)
1040 {
1041  if (!index->isNumber())
1042  return throwError(exec, TypeError,
1043  SPREF"vals: expected number as first argument");
1044 
1045  int i = qRound(index->getNumber());
1046  if (i < 0 || i >= vals->size())
1047  return throwError(exec, RangeError,
1048  SPREF"vals: index out of range");
1049 
1050  return variantToJsValue(vals->at(i));
1051 }
1052 
1053 JSValue *Scriptface::msgctxtf (ExecState *exec)
1054 {
1055  Q_UNUSED(exec);
1056  return jsString(*msgctxt);
1057 }
1058 
1059 JSValue *Scriptface::dynctxtf (ExecState *exec, JSValue *key)
1060 {
1061  if (!key->isString())
1062  return throwError(exec, TypeError,
1063  SPREF"dynctxt: expected string as first argument");
1064 
1065  QString qkey = key->getString().qstring();
1066  if (dynctxt->contains(qkey)) {
1067  return jsString(dynctxt->value(qkey));
1068  }
1069  return jsUndefined();
1070 }
1071 
1072 JSValue *Scriptface::msgidf (ExecState *exec)
1073 {
1074  Q_UNUSED(exec);
1075  return jsString(*msgid);
1076 }
1077 
1078 JSValue *Scriptface::msgkeyf (ExecState *exec)
1079 {
1080  Q_UNUSED(exec);
1081  return jsString(QString(*msgctxt + QLatin1Char('|') + *msgid));
1082 }
1083 
1084 JSValue *Scriptface::msgstrff (ExecState *exec)
1085 {
1086  Q_UNUSED(exec);
1087  return jsString(*final);
1088 }
1089 
1090 JSValue *Scriptface::dbgputsf (ExecState *exec, JSValue *str)
1091 {
1092  if (!str->isString())
1093  return throwError(exec, TypeError,
1094  SPREF"dbgputs: expected string as first argument");
1095 
1096  QString qstr = str->getString().qstring();
1097 
1098  dbgout("[JS-debug] %1", qstr);
1099 
1100  return jsUndefined();
1101 }
1102 
1103 JSValue *Scriptface::warnputsf (ExecState *exec, JSValue *str)
1104 {
1105  if (!str->isString())
1106  return throwError(exec, TypeError,
1107  SPREF"warnputs: expected string as first argument");
1108 
1109  QString qstr = str->getString().qstring();
1110 
1111  warnout("[JS-warning] %1", qstr);
1112 
1113  return jsUndefined();
1114 }
1115 
1116 JSValue *Scriptface::localeCountryf (ExecState *exec)
1117 {
1118  Q_UNUSED(exec);
1119  return jsString(*ctry);
1120 }
1121 
1122 JSValue *Scriptface::normKeyf (ExecState *exec, JSValue *phrase)
1123 {
1124  if (!phrase->isString()) {
1125  return throwError(exec, TypeError,
1126  SPREF"normKey: expected string as argument");
1127  }
1128 
1129  QByteArray nqphrase = normKeystr(phrase->toString(exec).qstring());
1130  return jsString(QString::fromUtf8(nqphrase));
1131 }
1132 
1133 JSValue *Scriptface::loadPropsf (ExecState *exec, const List &fnames)
1134 {
1135  if (globalKTI->currentModulePath.isEmpty()) {
1136  return throwError(exec, GeneralError,
1137  SPREF"loadProps: no current module path, aiiie...");
1138  }
1139 
1140  for (int i = 0; i < fnames.size(); ++i) {
1141  if (!fnames[i]->isString()) {
1142  return throwError(exec, TypeError,
1143  SPREF"loadProps: expected string as file name");
1144  }
1145  }
1146 
1147  for (int i = 0; i < fnames.size(); ++i)
1148  {
1149  QString qfname = fnames[i]->getString().qstring();
1150  QString qfpath_base = globalKTI->currentModulePath + QLatin1Char('/') + qfname;
1151 
1152  // Determine which kind of map is available.
1153  // Give preference to compiled map.
1154  QString qfpath = qfpath_base + QLatin1String(".pmapc");
1155  bool haveCompiled = true;
1156  QFile file_check(qfpath);
1157  if (!file_check.open(QIODevice::ReadOnly)) {
1158  haveCompiled = false;
1159  qfpath = qfpath_base + QLatin1String(".pmap");
1160  QFile file_check(qfpath);
1161  if (!file_check.open(QIODevice::ReadOnly)) {
1162  return throwError(exec, GeneralError,
1163  QString::fromLatin1(SPREF"loadProps: cannot read map '%1'")
1164  .arg(qfpath_base));
1165  }
1166  }
1167  file_check.close();
1168 
1169  // Load from appropriate type of map.
1170  if (!loadedPmapPaths.contains(qfpath)) {
1171  QString errorString;
1172  if (haveCompiled) {
1173  errorString = loadProps_bin(qfpath);
1174  }
1175  else {
1176  errorString = loadProps_text(qfpath);
1177  }
1178  if (!errorString.isEmpty()) {
1179  return throwError(exec, SyntaxError, errorString);
1180  }
1181  dbgout("Loaded property map: %1", qfpath);
1182  loadedPmapPaths.insert(qfpath);
1183  }
1184  }
1185 
1186  return jsUndefined();
1187 }
1188 
1189 JSValue *Scriptface::getPropf (ExecState *exec, JSValue *phrase, JSValue *prop)
1190 {
1191  if (!phrase->isString()) {
1192  return throwError(exec, TypeError,
1193  SPREF"getProp: expected string as first argument");
1194  }
1195  if (!prop->isString()) {
1196  return throwError(exec, TypeError,
1197  SPREF"getProp: expected string as second argument");
1198  }
1199 
1200  QByteArray qphrase = normKeystr(phrase->toString(exec).qstring());
1201  QHash<QByteArray, QByteArray> props = phraseProps.value(qphrase);
1202  if (props.isEmpty()) {
1203  props = resolveUnparsedProps(qphrase);
1204  }
1205  if (!props.isEmpty()) {
1206  QByteArray qprop = normKeystr(prop->toString(exec).qstring());
1207  QByteArray qval = props.value(qprop);
1208  if (!qval.isEmpty()) {
1209  return jsString(QString::fromUtf8(qval));
1210  }
1211  }
1212  return jsUndefined();
1213 }
1214 
1215 JSValue *Scriptface::setPropf (ExecState *exec, JSValue *phrase, JSValue *prop, JSValue *value)
1216 {
1217  if (!phrase->isString()) {
1218  return throwError(exec, TypeError,
1219  SPREF"setProp: expected string as first argument");
1220  }
1221  if (!prop->isString()) {
1222  return throwError(exec, TypeError,
1223  SPREF"setProp: expected string as second argument");
1224  }
1225  if (!value->isString()) {
1226  return throwError(exec, TypeError,
1227  SPREF"setProp: expected string as third argument");
1228  }
1229 
1230  QByteArray qphrase = normKeystr(phrase->toString(exec).qstring());
1231  QByteArray qprop = normKeystr(prop->toString(exec).qstring());
1232  QByteArray qvalue = value->toString(exec).qstring().toUtf8();
1233  // Any non-existent key in first or second-level hash will be created.
1234  phraseProps[qphrase][qprop] = qvalue;
1235  return jsUndefined();
1236 }
1237 
1238 static QString toCaseFirst (const QString &qstr, int qnalt, bool toupper)
1239 {
1240  static const QLatin1String head("~@");
1241  static const int hlen = 2; //head.length()
1242 
1243  // If the first letter is found within an alternatives directive,
1244  // change case of the first letter in each of the alternatives.
1245  QString qstrcc = qstr;
1246  int len = qstr.length();
1247  QChar altSep;
1248  int remainingAlts = 0;
1249  bool checkCase = true;
1250  int numChcased = 0;
1251  int i = 0;
1252  while (i < len) {
1253  QChar c = qstr[i];
1254 
1255  if (qnalt && !remainingAlts && qstr.mid(i, hlen) == head) {
1256  // An alternatives directive is just starting.
1257  i += 2;
1258  if (i >= len) break; // malformed directive, bail out
1259  // Record alternatives separator, set number of remaining
1260  // alternatives, reactivate case checking.
1261  altSep = qstrcc[i];
1262  remainingAlts = qnalt;
1263  checkCase = true;
1264  }
1265  else if (remainingAlts && c == altSep) {
1266  // Alternative separator found, reduce number of remaining
1267  // alternatives and reactivate case checking.
1268  --remainingAlts;
1269  checkCase = true;
1270  }
1271  else if (checkCase && c.isLetter()) {
1272  // Case check is active and the character is a letter; change case.
1273  if (toupper) {
1274  qstrcc[i] = c.toUpper();
1275  } else {
1276  qstrcc[i] = c.toLower();
1277  }
1278  ++numChcased;
1279  // No more case checks until next alternatives separator.
1280  checkCase = false;
1281  }
1282 
1283  // If any letter has been changed, and there are no more alternatives
1284  // to be processed, we're done.
1285  if (numChcased > 0 && remainingAlts == 0) {
1286  break;
1287  }
1288 
1289  // Go to next character.
1290  ++i;
1291  }
1292 
1293  return qstrcc;
1294 }
1295 
1296 JSValue *Scriptface::toUpperFirstf (ExecState *exec,
1297  JSValue *str, JSValue *nalt)
1298 {
1299  if (!str->isString()) {
1300  return throwError(exec, TypeError,
1301  SPREF"toUpperFirst: expected string as first argument");
1302  }
1303  if (!(nalt->isNumber() || nalt->isNull())) {
1304  return throwError(exec, TypeError,
1305  SPREF"toUpperFirst: expected number as second argument");
1306  }
1307 
1308  QString qstr = str->toString(exec).qstring();
1309  int qnalt = nalt->isNull() ? 0 : nalt->toInteger(exec);
1310 
1311  QString qstruc = toCaseFirst(qstr, qnalt, true);
1312 
1313  return jsString(qstruc);
1314 }
1315 
1316 JSValue *Scriptface::toLowerFirstf (ExecState *exec,
1317  JSValue *str, JSValue *nalt)
1318 {
1319  if (!str->isString()) {
1320  return throwError(exec, TypeError,
1321  SPREF"toLowerFirst: expected string as first argument");
1322  }
1323  if (!(nalt->isNumber() || nalt->isNull())) {
1324  return throwError(exec, TypeError,
1325  SPREF"toLowerFirst: expected number as second argument");
1326  }
1327 
1328  QString qstr = str->toString(exec).qstring();
1329  int qnalt = nalt->isNull() ? 0 : nalt->toInteger(exec);
1330 
1331  QString qstrlc = toCaseFirst(qstr, qnalt, false);
1332 
1333  return jsString(qstrlc);
1334 }
1335 
1336 JSValue *Scriptface::getConfStringf (ExecState *exec,
1337  JSValue *key, JSValue *dval)
1338 {
1339  if (!key->isString()) {
1340  return throwError(exec, TypeError,
1341  SPREF"getConfString: expected string "
1342  "as first argument");
1343  }
1344  if (!(dval->isString() || dval->isNull())) {
1345  return throwError(exec, TypeError,
1346  SPREF"getConfString: expected string "
1347  "as second argument (when given)");
1348  }
1349 
1350  if (dval->isNull()) {
1351  dval = jsUndefined();
1352  }
1353 
1354  QString qkey = key->getString().qstring();
1355  if (config.contains(qkey)) {
1356  return jsString(config.value(qkey));
1357  }
1358 
1359  return dval;
1360 }
1361 
1362 JSValue *Scriptface::getConfBoolf (ExecState *exec,
1363  JSValue *key, JSValue *dval)
1364 {
1365  if (!key->isString()) {
1366  return throwError(exec, TypeError,
1367  SPREF"getConfBool: expected string as "
1368  "first argument");
1369  }
1370  if (!(dval->isBoolean() || dval->isNull())) {
1371  return throwError(exec, TypeError,
1372  SPREF"getConfBool: expected boolean "
1373  "as second argument (when given)");
1374  }
1375 
1376  static QStringList falsities;
1377  if (falsities.isEmpty()) {
1378  falsities.append(QString(QLatin1Char('0')));
1379  falsities.append(QString::fromLatin1("no"));
1380  falsities.append(QString::fromLatin1("false"));
1381  }
1382 
1383  if (dval->isNull()) {
1384  dval = jsUndefined();
1385  }
1386 
1387  QString qkey = key->getString().qstring();
1388  if (config.contains(qkey)) {
1389  QString qval = config.value(qkey).toLower();
1390  return jsBoolean(!falsities.contains(qval));
1391  }
1392 
1393  return dval;
1394 }
1395 
1396 JSValue *Scriptface::getConfNumberf (ExecState *exec,
1397  JSValue *key, JSValue *dval)
1398 {
1399  if (!key->isString()) {
1400  return throwError(exec, TypeError,
1401  SPREF"getConfNumber: expected string "
1402  "as first argument");
1403  }
1404  if (!(dval->isNumber() || dval->isNull())) {
1405  return throwError(exec, TypeError,
1406  SPREF"getConfNumber: expected number "
1407  "as second argument (when given)");
1408  }
1409 
1410  if (dval->isNull()) {
1411  dval = jsUndefined();
1412  }
1413 
1414  QString qkey = key->getString().qstring();
1415  if (config.contains(qkey)) {
1416  QString qval = config.value(qkey);
1417  bool convOk;
1418  double qnum = qval.toDouble(&convOk);
1419  if (convOk) {
1420  return jsNumber(qnum);
1421  }
1422  }
1423 
1424  return dval;
1425 }
1426 
1427 // ----------------------------------------------------------------------
1428 // Scriptface helpers to interface functions.
1429 
1430 QString Scriptface::loadProps_text (const QString &fpath)
1431 {
1432  QFile file(fpath);
1433  if (!file.open(QIODevice::ReadOnly)) {
1434  return QString::fromLatin1(SPREF"loadProps_text: cannot read file '%1'")
1435  .arg(fpath);
1436  }
1437  QTextStream stream(&file);
1438  stream.setCodec("UTF-8");
1439  QString s = stream.readAll();
1440  file.close();
1441 
1442  // Parse the map.
1443  // Should care about performance: possibly executed on each KDE
1444  // app startup and reading houndreds of thousands of characters.
1445  enum {s_nextEntry, s_nextKey, s_nextValue};
1446  QList<QByteArray> ekeys; // holds keys for current entry
1447  QHash<QByteArray, QByteArray> props; // holds properties for current entry
1448  int slen = s.length();
1449  int state = s_nextEntry;
1450  QByteArray pkey;
1451  QChar prop_sep, key_sep;
1452  int i = 0;
1453  while (1) {
1454  int i_checkpoint = i;
1455 
1456  if (state == s_nextEntry) {
1457  while (s[i].isSpace()) {
1458  ++i;
1459  if (i >= slen) goto END_PROP_PARSE;
1460  }
1461  if (i + 1 >= slen) {
1462  return QString::fromLatin1(SPREF"loadProps_text: unexpected end "
1463  "of file in %1").arg(fpath);
1464  }
1465  if (s[i] != QLatin1Char('#')) {
1466  // Separator characters for this entry.
1467  key_sep = s[i];
1468  prop_sep = s[i + 1];
1469  if (key_sep.isLetter() || prop_sep.isLetter()) {
1470  return QString::fromLatin1(SPREF"loadProps_text: separator "
1471  "characters must not be letters at %1:%2")
1472  .arg(fpath).arg(countLines(s, i));
1473  }
1474 
1475  // Reset all data for current entry.
1476  ekeys.clear();
1477  props.clear();
1478  pkey.clear();
1479 
1480  i += 2;
1481  state = s_nextKey;
1482  }
1483  else {
1484  // This is a comment, skip to EOL, don't change state.
1485  while (s[i] != QLatin1Char('\n')) {
1486  ++i;
1487  if (i >= slen) goto END_PROP_PARSE;
1488  }
1489  }
1490  }
1491  else if (state == s_nextKey) {
1492  int ip = i;
1493  // Proceed up to next key or property separator.
1494  while (s[i] != key_sep && s[i] != prop_sep) {
1495  ++i;
1496  if (i >= slen) goto END_PROP_PARSE;
1497  }
1498  if (s[i] == key_sep) {
1499  // This is a property key,
1500  // record for when the value gets parsed.
1501  pkey = normKeystr(s.mid(ip, i - ip), false);
1502 
1503  i += 1;
1504  state = s_nextValue;
1505  }
1506  else { // if (s[i] == prop_sep) {
1507  // This is an entry key, or end of entry.
1508  QByteArray ekey = normKeystr(s.mid(ip, i - ip), false);
1509  if (!ekey.isEmpty()) {
1510  // An entry key.
1511  ekeys.append(ekey);
1512 
1513  i += 1;
1514  state = s_nextKey;
1515  }
1516  else {
1517  // End of entry.
1518  if (ekeys.size() < 1) {
1519  return QString::fromLatin1(SPREF"loadProps_text: no entry key "
1520  "for entry ending at %1:%2")
1521  .arg(fpath).arg(countLines(s, i));
1522  }
1523 
1524  // Add collected entry into global store,
1525  // once for each entry key (QHash implicitly shared).
1526  foreach (const QByteArray &ekey, ekeys) {
1527  phraseProps[ekey] = props;
1528  }
1529 
1530  i += 1;
1531  state = s_nextEntry;
1532  }
1533  }
1534  }
1535  else if (state == s_nextValue) {
1536  int ip = i;
1537  // Proceed up to next property separator.
1538  while (s[i] != prop_sep) {
1539  ++i;
1540  if (i >= slen) goto END_PROP_PARSE;
1541  if (s[i] == key_sep) {
1542  return QString::fromLatin1(SPREF"loadProps_text: property separator "
1543  "inside property value at %1:%2")
1544  .arg(fpath).arg(countLines(s, i));
1545  }
1546  }
1547  // Extract the property value and store the property.
1548  QByteArray pval = trimSmart(s.mid(ip, i - ip)).toUtf8();
1549  props[pkey] = pval;
1550 
1551  i += 1;
1552  state = s_nextKey;
1553  }
1554  else {
1555  return QString::fromLatin1(SPREF"loadProps: internal error 10 at %1:%2")
1556  .arg(fpath).arg(countLines(s, i));
1557  }
1558 
1559  // To avoid infinite looping and stepping out.
1560  if (i == i_checkpoint || i >= slen) {
1561  return QString::fromLatin1(SPREF"loadProps: internal error 20 at %1:%2")
1562  .arg(fpath).arg(countLines(s, i));
1563  }
1564  }
1565 
1566  END_PROP_PARSE:
1567 
1568  if (state != s_nextEntry) {
1569  return QString::fromLatin1(SPREF"loadProps: unexpected end of file in %1")
1570  .arg(fpath);
1571  }
1572 
1573  return QString();
1574 }
1575 
1576 // Read big-endian integer of nbytes length at position pos
1577 // in character array fc of length len.
1578 // Update position to point after the number.
1579 // In case of error, pos is set to -1.
1580 template <typename T>
1581 static int bin_read_int_nbytes (const char *fc, qlonglong len, qlonglong &pos, int nbytes)
1582 {
1583  if (pos + nbytes > len) {
1584  pos = -1;
1585  return 0;
1586  }
1587  T num = qFromBigEndian<T>((uchar*) fc + pos);
1588  pos += nbytes;
1589  return num;
1590 }
1591 
1592 // Read 64-bit big-endian integer.
1593 static quint64 bin_read_int64 (const char *fc, qlonglong len, qlonglong &pos)
1594 {
1595  return bin_read_int_nbytes<quint64>(fc, len, pos, 8);
1596 }
1597 
1598 // Read 32-bit big-endian integer.
1599 static quint32 bin_read_int (const char *fc, qlonglong len, qlonglong &pos)
1600 {
1601  return bin_read_int_nbytes<quint32>(fc, len, pos, 4);
1602 }
1603 
1604 // Read string at position pos of character array fc of length n.
1605 // String is represented as 32-bit big-endian byte length followed by bytes.
1606 // Update position to point after the string.
1607 // In case of error, pos is set to -1.
1608 static QByteArray bin_read_string (const char *fc, qlonglong len, qlonglong &pos)
1609 {
1610  // Binary format stores strings as length followed by byte sequence.
1611  // No null-termination.
1612  int nbytes = bin_read_int(fc, len, pos);
1613  if (pos < 0) {
1614  return QByteArray();
1615  }
1616  if (nbytes < 0 || pos + nbytes > len) {
1617  pos = -1;
1618  return QByteArray();
1619  }
1620  QByteArray s(fc + pos, nbytes);
1621  pos += nbytes;
1622  return s;
1623 }
1624 
1625 QString Scriptface::loadProps_bin (const QString &fpath)
1626 {
1627  QFile file(fpath);
1628  if (!file.open(QIODevice::ReadOnly)) {
1629  return QString::fromLatin1(SPREF"loadProps: cannot read file '%1'")
1630  .arg(fpath);
1631  }
1632  // Collect header.
1633  QByteArray head(8, '0');
1634  file.read(head.data(), head.size());
1635  file.close();
1636 
1637  // Choose pmap loader based on header.
1638  if (head == "TSPMAP00") {
1639  return loadProps_bin_00(fpath);
1640  } else if (head == "TSPMAP01") {
1641  return loadProps_bin_01(fpath);
1642  }
1643  else {
1644  return QString::fromLatin1(SPREF"loadProps: unknown version of compiled map '%1'")
1645  .arg(fpath);
1646  }
1647 }
1648 
1649 QString Scriptface::loadProps_bin_00 (const QString &fpath)
1650 {
1651  QFile file(fpath);
1652  if (!file.open(QIODevice::ReadOnly)) {
1653  return QString::fromLatin1(SPREF"loadProps: cannot read file '%1'")
1654  .arg(fpath);
1655  }
1656  QByteArray fctmp = file.readAll();
1657  file.close();
1658  const char *fc = fctmp.data();
1659  const int fclen = fctmp.size();
1660 
1661  // Indicates stream state.
1662  qlonglong pos = 0;
1663 
1664  // Match header.
1665  QByteArray head(fc, 8);
1666  pos += 8;
1667  if (head != "TSPMAP00") goto END_PROP_PARSE;
1668 
1669  // Read total number of entries.
1670  int nentries;
1671  nentries = bin_read_int(fc, fclen, pos);
1672  if (pos < 0) goto END_PROP_PARSE;
1673 
1674  // Read all entries.
1675  for (int i = 0; i < nentries; ++i) {
1676 
1677  // Read number of entry keys and all entry keys.
1678  QList<QByteArray> ekeys;
1679  int nekeys = bin_read_int(fc, fclen, pos);
1680  if (pos < 0) goto END_PROP_PARSE;
1681  for (int j = 0; j < nekeys; ++j) {
1682  QByteArray ekey = bin_read_string(fc, fclen, pos);
1683  if (pos < 0) goto END_PROP_PARSE;
1684  ekeys.append(ekey);
1685  }
1686  //dbgout("--------> ekey[0]={%1}", QString::fromUtf8(ekeys[0]));
1687 
1688  // Read number of properties and all properties.
1689  QHash<QByteArray, QByteArray> props;
1690  int nprops = bin_read_int(fc, fclen, pos);
1691  if (pos < 0) goto END_PROP_PARSE;
1692  for (int j = 0; j < nprops; ++j) {
1693  QByteArray pkey = bin_read_string(fc, fclen, pos);
1694  if (pos < 0) goto END_PROP_PARSE;
1695  QByteArray pval = bin_read_string(fc, fclen, pos);
1696  if (pos < 0) goto END_PROP_PARSE;
1697  props[pkey] = pval;
1698  }
1699 
1700  // Add collected entry into global store,
1701  // once for each entry key (QHash implicitly shared).
1702  foreach (const QByteArray &ekey, ekeys) {
1703  phraseProps[ekey] = props;
1704  }
1705  }
1706 
1707  END_PROP_PARSE:
1708 
1709  if (pos < 0) {
1710  return QString::fromLatin1(SPREF"loadProps: corrupt compiled map '%1'")
1711  .arg(fpath);
1712  }
1713 
1714  return QString();
1715 }
1716 
1717 QString Scriptface::loadProps_bin_01 (const QString &fpath)
1718 {
1719  QFile *file = new QFile(fpath);
1720  if (!file->open(QIODevice::ReadOnly)) {
1721  return QString::fromLatin1(SPREF"loadProps: cannot read file '%1'")
1722  .arg(fpath);
1723  }
1724 
1725  QByteArray fstr;
1726  qlonglong pos;
1727 
1728  // Read the header and number and length of entry keys.
1729  fstr = file->read(8 + 4 + 8);
1730  pos = 0;
1731  QByteArray head = fstr.left(8);
1732  pos += 8;
1733  if (head != "TSPMAP01") {
1734  return QString::fromLatin1(SPREF"loadProps: corrupt compiled map '%1'")
1735  .arg(fpath);
1736  }
1737  quint32 numekeys = bin_read_int(fstr, fstr.size(), pos);
1738  quint64 lenekeys = bin_read_int64(fstr, fstr.size(), pos);
1739 
1740  // Read entry keys.
1741  fstr = file->read(lenekeys);
1742  pos = 0;
1743  for (quint32 i = 0; i < numekeys; ++i) {
1744  QByteArray ekey = bin_read_string(fstr, lenekeys, pos);
1745  quint64 offset = bin_read_int64(fstr, lenekeys, pos);
1746  phraseUnparsedProps[ekey] = QPair<QFile*, quint64>(file, offset);
1747  }
1748 
1749  // // Read property keys.
1750  // ...when it becomes necessary
1751 
1752  loadedPmapHandles.insert(file);
1753  return QString();
1754 }
1755 
1756 QHash<QByteArray, QByteArray> Scriptface::resolveUnparsedProps (const QByteArray &phrase)
1757 {
1758  QPair<QFile*, quint64> ref = phraseUnparsedProps.value(phrase);
1759  QFile *file = ref.first;
1760  quint64 offset = ref.second;
1761  QHash<QByteArray, QByteArray> props;
1762  if (file != NULL && file->seek(offset)) {
1763  QByteArray fstr = file->read(4 + 4);
1764  qlonglong pos = 0;
1765  quint32 numpkeys = bin_read_int(fstr, fstr.size(), pos);
1766  quint32 lenpkeys = bin_read_int(fstr, fstr.size(), pos);
1767  fstr = file->read(lenpkeys);
1768  pos = 0;
1769  for (quint32 i = 0; i < numpkeys; ++i) {
1770  QByteArray pkey = bin_read_string(fstr, lenpkeys, pos);
1771  QByteArray pval = bin_read_string(fstr, lenpkeys, pos);
1772  props[pkey] = pval;
1773  }
1774  phraseProps[phrase] = props;
1775  phraseUnparsedProps.remove(phrase);
1776  }
1777  return props;
1778 }
QTextStream::setCodec
void setCodec(QTextCodec *codec)
QList::clear
void clear()
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QFile::seek
virtual bool seek(qint64 pos)
QString::append
QString & append(QChar ch)
QHash::insert
iterator insert(const Key &key, const T &value)
QByteArray::clear
void clear()
KFileSystemType::Type
Type
Definition: kfilesystemtype_p.h:28
QTextStream::readLine
QString readLine(qint64 maxlen)
T
#define T
kdecore_export.h
QByteArray
variantToJsValue
JSValue * variantToJsValue(const QVariant &val)
Definition: ktranscript.cpp:373
KMacroExpander::group
Definition: kmacroexpander_unix.cpp:34
WARNP
#define WARNP
Definition: ktranscript.cpp:250
QChar
K_GLOBAL_STATIC
#define K_GLOBAL_STATIC(TYPE, NAME)
This macro makes it easy to use non-POD types as global statics.
Definition: kglobal.h:221
QList::at
const T & at(int i) const
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QByteArray::isEmpty
bool isEmpty() const
expt2str
QString expt2str(ExecState *exec)
Definition: ktranscript.cpp:276
warnout
void warnout(const char *str)
Definition: ktranscript.cpp:251
quint32
removeAcceleratorMarker
QString removeAcceleratorMarker(const QString &label_)
Definition: common_helpers.cpp:52
common_helpers_p.h
QString::toDouble
double toDouble(bool *ok) const
QDir::homePath
QString homePath()
bin_read_string
static QByteArray bin_read_string(const char *fc, qlonglong len, qlonglong &pos)
Definition: ktranscript.cpp:1608
KGlobal::ref
void ref()
Tells KGlobal about one more operations that should be finished before the application exits...
Definition: kglobal.cpp:321
QFile
QTextStream
QList::size
int size() const
KGlobal::config
KSharedConfigPtr config()
Returns the general config object.
Definition: kglobal.cpp:139
QString::lastIndexOf
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QString::isNull
bool isNull() const
load_transcript
KTranscript * load_transcript()
Definition: ktranscript.cpp:453
QChar::isLetter
bool isLetter() const
QString::clear
void clear()
bin_read_int
static quint32 bin_read_int(const char *fc, qlonglong len, qlonglong &pos)
Definition: ktranscript.cpp:1599
kglobal.h
TsConfig
QHash< QString, TsConfigGroup > TsConfig
Definition: ktranscript.cpp:56
readConfig
TsConfig readConfig(const QString &fname)
Definition: ktranscript.cpp:390
QString::number
QString number(int n, int base)
QList::append
void append(const T &value)
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QChar::isSpace
bool isSpace() const
QTextStream::atEnd
bool atEnd() const
QHash< QString, QString >
toCaseFirst
static QString toCaseFirst(const QString &qstr, int qnalt, bool toupper)
Definition: ktranscript.cpp:1238
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
QString::trimmed
QString trimmed() const
QIODevice::read
qint64 read(char *data, qint64 maxSize)
QSet< QString >
bin_read_int_nbytes
static int bin_read_int_nbytes(const char *fc, qlonglong len, qlonglong &pos, int nbytes)
Definition: ktranscript.cpp:1581
QString
QList< QVariant >
QHash::remove
int remove(const Key &key)
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
CALLARG
#define CALLARG(i)
Definition: ktranscript.cpp:778
QStringList
QPair
QHash::clear
void clear()
QString::toLower
QString toLower() const
QHash::value
const T value(const Key &key) const
QHash::find
iterator find(const Key &key)
QLatin1Char
QFile::close
virtual void close()
QChar::toLower
QChar toLower() const
ktranscript_p.h
normKeystr
QByteArray normKeystr(const QString &raw, bool mayHaveAcc=true)
Definition: ktranscript.cpp:309
QString::unicode
const QChar * unicode() const
DBGP
#define DBGP
Definition: ktranscript.cpp:217
trimSmart
QString trimSmart(const QString &raw)
Definition: ktranscript.cpp:343
QByteArray::left
QByteArray left(int len) const
TsConfigGroup
QHash< QString, QString > TsConfigGroup
Definition: ktranscript.cpp:53
SPREF
#define SPREF
Definition: ktranscript.cpp:845
QString::mid
QString mid(int position, int n) const
QChar::toUpper
QChar toUpper() const
QLatin1String
quint64
countLines
int countLines(const QString &s, int p)
Definition: ktranscript.cpp:295
SFNAME
#define SFNAME
Definition: ktranscript.cpp:674
KTranscript
class for supporting programmable translations
Definition: ktranscript_p.h:38
QString::length
int length() const
QByteArray::data
char * data()
Kuit::Tag::List
Definition: kuitsemantics.cpp:84
QString::left
QString left(int n) const
QString::ref
QChar & ref(uint i)
QString::fromLatin1
QString fromLatin1(const char *str, int size)
QVariant::toDouble
double toDouble(bool *ok) const
QHash::contains
bool contains(const Key &key) const
dbgout
void dbgout(const char *str)
Definition: ktranscript.cpp:218
QHash::end
iterator end()
QVariant::type
Type type() const
QByteArray::size
int size() const
QTextStream::readAll
QString readAll()
bin_read_int64
static quint64 bin_read_int64(const char *fc, qlonglong len, qlonglong &pos)
Definition: ktranscript.cpp:1593
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QVariant::toString
QString toString() const
KDE_EXPORT
#define KDE_EXPORT
The KDE_EXPORT macro marks the symbol of the given variable to be visible, so it can be used from out...
Definition: kdemacros.h.cmake:74
QVariant
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:22:11 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal