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

KDECore

  • 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
50using namespace KJS;
51
52class KTranscriptImp;
53class Scriptface;
54
55typedef QHash<QString, QString> TsConfigGroup;
56typedef QHash<QString, TsConfigGroup> TsConfig;
57
58// Transcript implementation (used as singleton).
59class 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.
95class 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: "
218void dbgout (const char*str) {
219 #ifndef NDEBUG
220 fprintf(stderr, DBGP"%s\n", str);
221 #else
222 Q_UNUSED(str);
223 #endif
224}
225template <typename T1>
226void 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}
233template <typename T1, typename T2>
234void 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}
241template <typename T1, typename T2, typename T3>
242void 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: "
251void warnout (const char*str) {
252 fprintf(stderr, WARNP"%s\n", str);
253}
254template <typename T1>
255void 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.
262UString::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}
269QString UString::qstring() const
270{
271 return QString((QChar*) data(), size());
272}
273
274// ----------------------------------------------------------------------
275// Produces a string out of a KJS exception.
276QString 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.
295int 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,
309QByteArray 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.
343QString 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.
373JSValue *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.
390TsConfig 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.
450K_GLOBAL_STATIC(KTranscriptImp, globalKTI)
451extern "C"
452{
453 KDE_EXPORT KTranscript *load_transcript ()
454 {
455 return globalKTI;
456 }
457}
458
459// ----------------------------------------------------------------------
460// KTranscript definitions.
461
462KTranscriptImp::KTranscriptImp ()
463{
464 // Load user configuration.
465 const QString tsConfigPath = QDir::homePath() + QLatin1Char('/') + QLatin1String(".transcriptrc");
466 config = readConfig(tsConfigPath);
467}
468
469KTranscriptImp::~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
477QString 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
609QStringList 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
622void 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
672KJS_QT_UNICODE_IMPL
673
674#define SFNAME "Ts"
675void 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
736KJS_DEFINE_PROTOTYPE(ScriptfaceProto)
737KJS_IMPLEMENT_PROTOFUNC(ScriptfaceProtoFunc)
738KJS_IMPLEMENT_PROTOTYPE("Scriptface", ScriptfaceProto, ScriptfaceProtoFunc, ObjectPrototype)
739
740const ClassInfo Scriptface::info = {"Scriptface", 0, &ScriptfaceTable, 0};
741
742Scriptface::Scriptface (ExecState *exec, const TsConfigGroup &config_)
743: JSObject(ScriptfaceProto::self(exec)), fallback(NULL), config(config_)
744{}
745
746Scriptface::~Scriptface ()
747{
748 qDeleteAll(loadedPmapHandles);
749}
750
751bool Scriptface::getOwnPropertySlot (ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
752{
753 return getStaticValueSlot<Scriptface, JSObject>(exec, &ScriptfaceTable, this, propertyName, slot);
754}
755
756JSValue *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
765void Scriptface::put (ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
766{
767 lookupPut<Scriptface, JSObject>(exec, propertyName, value, attr, &ScriptfaceTable, this);
768}
769
770void 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())
779JSValue *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
847JSValue *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
900JSValue *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
929JSValue *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
939JSValue *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
979JSValue *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
1011JSValue *Scriptface::fallbackf (ExecState *exec)
1012{
1013 Q_UNUSED(exec);
1014 if (fallback != NULL)
1015 *fallback = true;
1016 return jsUndefined();
1017}
1018
1019JSValue *Scriptface::nsubsf (ExecState *exec)
1020{
1021 Q_UNUSED(exec);
1022 return jsNumber(subs->size());
1023}
1024
1025JSValue *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
1039JSValue *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
1053JSValue *Scriptface::msgctxtf (ExecState *exec)
1054{
1055 Q_UNUSED(exec);
1056 return jsString(*msgctxt);
1057}
1058
1059JSValue *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
1072JSValue *Scriptface::msgidf (ExecState *exec)
1073{
1074 Q_UNUSED(exec);
1075 return jsString(*msgid);
1076}
1077
1078JSValue *Scriptface::msgkeyf (ExecState *exec)
1079{
1080 Q_UNUSED(exec);
1081 return jsString(QString(*msgctxt + QLatin1Char('|') + *msgid));
1082}
1083
1084JSValue *Scriptface::msgstrff (ExecState *exec)
1085{
1086 Q_UNUSED(exec);
1087 return jsString(*final);
1088}
1089
1090JSValue *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
1103JSValue *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
1116JSValue *Scriptface::localeCountryf (ExecState *exec)
1117{
1118 Q_UNUSED(exec);
1119 return jsString(*ctry);
1120}
1121
1122JSValue *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
1133JSValue *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
1189JSValue *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
1215JSValue *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
1238static 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
1296JSValue *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
1316JSValue *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
1336JSValue *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
1362JSValue *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
1396JSValue *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
1430QString 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.
1580template <typename T>
1581static 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.
1593static 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.
1599static 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.
1608static 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
1625QString 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
1649QString 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
1717QString 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
1756QHash<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}
KTranscript
class for supporting programmable translations
Definition: ktranscript_p.h:39
KTranscript::postCalls
virtual QStringList postCalls(const QString &lang)=0
Returns the list of calls to execute an all messages after the interpolations are done,...
KTranscript::eval
virtual QString eval(const QList< QVariant > &argv, const QString &lang, const QString &ctry, const QString &msgctxt, const QHash< QString, QString > &dynctxt, const QString &msgid, const QStringList &subs, const QList< QVariant > &vals, const QString &final, QList< QStringList > &mods, QString &error, bool &fallback)=0
Evaluates interpolation.
QHash
Definition: ksycocafactory.h:28
QList
Definition: kaboutdata.h:33
QPair
QSet
Definition: k3resolver.h:41
QStringList
QString
QVariant
quint32
quint64
removeAcceleratorMarker
QString removeAcceleratorMarker(const QString &label_)
Definition: common_helpers.cpp:52
common_helpers_p.h
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
kdecore_export.h
kglobal.h
T
#define T
readConfig
TsConfig readConfig(const QString &fname)
Definition: ktranscript.cpp:390
countLines
int countLines(const QString &s, int p)
Definition: ktranscript.cpp:295
normKeystr
QByteArray normKeystr(const QString &raw, bool mayHaveAcc=true)
Definition: ktranscript.cpp:309
TsConfig
QHash< QString, TsConfigGroup > TsConfig
Definition: ktranscript.cpp:56
TsConfigGroup
QHash< QString, QString > TsConfigGroup
Definition: ktranscript.cpp:55
WARNP
#define WARNP
Definition: ktranscript.cpp:250
expt2str
QString expt2str(ExecState *exec)
Definition: ktranscript.cpp:276
SFNAME
#define SFNAME
Definition: ktranscript.cpp:674
toCaseFirst
static QString toCaseFirst(const QString &qstr, int qnalt, bool toupper)
Definition: ktranscript.cpp:1238
variantToJsValue
JSValue * variantToJsValue(const QVariant &val)
Definition: ktranscript.cpp:373
bin_read_int64
static quint64 bin_read_int64(const char *fc, qlonglong len, qlonglong &pos)
Definition: ktranscript.cpp:1593
dbgout
void dbgout(const char *str)
Definition: ktranscript.cpp:218
bin_read_string
static QByteArray bin_read_string(const char *fc, qlonglong len, qlonglong &pos)
Definition: ktranscript.cpp:1608
DBGP
#define DBGP
Definition: ktranscript.cpp:217
CALLARG
#define CALLARG(i)
Definition: ktranscript.cpp:778
bin_read_int_nbytes
static int bin_read_int_nbytes(const char *fc, qlonglong len, qlonglong &pos, int nbytes)
Definition: ktranscript.cpp:1581
load_transcript
KTranscript * load_transcript()
Definition: ktranscript.cpp:453
bin_read_int
static quint32 bin_read_int(const char *fc, qlonglong len, qlonglong &pos)
Definition: ktranscript.cpp:1599
trimSmart
QString trimSmart(const QString &raw)
Definition: ktranscript.cpp:343
SPREF
#define SPREF
Definition: ktranscript.cpp:845
warnout
void warnout(const char *str)
Definition: ktranscript.cpp:251
ktranscript_p.h
KGlobal::ref
void ref()
Tells KGlobal about one more operations that should be finished before the application exits.
Definition: kglobal.cpp:321
KGlobal::config
KSharedConfigPtr config()
Returns the general config object.
Definition: kglobal.cpp:139
Kuit::Tag::List
@ List
Definition: kuitsemantics.cpp:84
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 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-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 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
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