[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
klfuserscript.cpp
1 /***************************************************************************
2  * file klfuserscript.cpp
3  * This file is part of the KLatexFormula Project.
4  * Copyright (C) 2012 by Philippe Faist
5  * philippe.faist at bluewin.ch
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21  ***************************************************************************/
22 /* $Id: klfuserscript.cpp 1019 2017-02-07 06:20:02Z phfaist $ */
23 
24 #include <QFileInfo>
25 #include <QDir>
26 #include <QDateTime>
27 #include <QByteArray>
28 
29 #include <klfdefs.h>
30 #include <klfdebug.h>
31 #include <klfpobj.h>
32 #include <klfdatautil.h>
33 
34 #include "klfbackend.h"
35 #include "klfbackend_p.h"
36 #include "klfuserscript.h"
37 
38 
49 /*
50 static int read_spec_section(const QString& str, int fromindex, const QRegExp& seprx, QString * extractedPart)
51 {
52  int i = fromindex;
53  bool in_quote = false;
54 
55  QString s;
56 
57  while (i < str.length() && (in_quote || seprx.indexIn(str, i) != i)) {
58  if (str[i] == '\\') {
59  s.append(str[i]);
60  if (i+1 < str.length())
61  s.append(str[i+1]);
62  i += 2; // skip next char, too. The actual escaping will be done with klfEscapedToData()
63  continue;
64  }
65  if (str[i] == '"') {
66  in_quote = !in_quote;
67  ++i;
68  continue;
69  }
70  s.append(str[i]);
71  ++i;
72  }
73 
74  *extractedPart = QString::fromLocal8Bit(klfEscapedToData(s.toLocal8Bit()));
75 
76  return i; // the position of the last char separator
77 }
78 
79 */
80 
81 
82 
83 
84 
85 struct KLFUserScriptInfo::Private : public KLFPropertizedObject
86 {
87  Private()
88  : KLFPropertizedObject("KLFUserScriptInfo")
89  {
90  refcount = 0;
91  scriptInfoError = KLFERR_NOERROR;
92 
93  registerBuiltInProperty(ExeScript, QLatin1String("ExeScript"));
94  registerBuiltInProperty(Category, QLatin1String("Category"));
96  registerBuiltInProperty(Author, QLatin1String("Author"));
97  registerBuiltInProperty(Version, QLatin1String("Version"));
98  registerBuiltInProperty(License, QLatin1String("License"));
99  registerBuiltInProperty(KLFMinVersion, QLatin1String("KLFMinVersion"));
100  registerBuiltInProperty(KLFMaxVersion, QLatin1String("KLFMaxVersion"));
101  registerBuiltInProperty(SettingsFormUI, QLatin1String("SettingsFormUI"));
102  registerBuiltInProperty(CanProvideDefaultSettings, QLatin1String("CanProvideDefaultSettings"));
103  registerBuiltInProperty(CategorySpecificXmlConfig, QLatin1String("CategorySpecificXmlConfig"));
104  }
105 
106  void clear()
107  {
108  // clear all properties
110  for (int k = 0; k < idlist.size(); ++k) {
111  setProperty(idlist[k], QVariant());
112  }
113  }
114 
115  int refcount;
116  inline int ref() { return ++refcount; }
117  inline int deref() { return --refcount; }
118 
119  QString uspath;
120  QString normalizedfname;
121  QString sname;
122  QString basename;
123  int scriptInfoError;
124  QString scriptInfoErrorString;
125 
126  QStringList notices;
127  QStringList warnings;
128  QStringList errors;
129 
130 
131  void _set_xml_read_error(const QString& fullerrmsg)
132  {
133  scriptInfoError = 999;
134  scriptInfoErrorString = fullerrmsg;
135  }
136  void _set_xml_parsing_error(const QString& xmlfname, const QString& errmsg)
137  {
138  scriptInfoError = 999;
139  scriptInfoErrorString = QString("Error parsing scriptinfo XML contents: %1: %2")
140  .arg(xmlfname).arg(errmsg);
141  }
142 
143  void read_script_info()
144  {
145  scriptInfoError = KLFERR_NOERROR;
146  scriptInfoErrorString = QString();
147 
148  QString xmlfname = QDir::toNativeSeparators(uspath + "/scriptinfo.xml");
149  QFile fxml(xmlfname);
150  if ( ! fxml.open(QIODevice::ReadOnly) ) {
151  _set_xml_read_error(QString("Can't open XML file %1: %2").arg(xmlfname).arg(fxml.errorString()));
152  return;
153  }
154 
155  QDomDocument doc("klfuserscript-info");
156  QString errMsg; int errLine, errCol;
157  bool r = doc.setContent(&fxml, false, &errMsg, &errLine, &errCol);
158  if (!r) {
159  _set_xml_read_error(QString("XML parse error: %1 (file %2 line %3 col %4)")
160  .arg(errMsg).arg(xmlfname).arg(errLine).arg(errCol));
161  return;
162  }
163  fxml.close();
164 
165  QDomElement root = doc.documentElement();
166  if (root.nodeName() != "klfuserscript-info") {
167  _set_xml_parsing_error(xmlfname, QString("expected <klfuserscript-info> as root document element"));
168  return;
169  }
170 
171  // clear all properties
172  clear();
173 
174  setProperty(CanProvideDefaultSettings, false);
175 
176  // read XML contents
177  QDomNode n;
178  for (n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
179  // try to convert the node to an element; ignore non-elements
180  if ( n.nodeType() != QDomNode::ElementNode ) {
181  continue;
182  }
183  QDomElement e = n.toElement();
184  if ( e.isNull() ) {
185  continue;
186  }
187  // parse the elements.
188  QString val = e.text();
189  if (val.isEmpty()) {
190  val = QString(); // empty value is null string
191  }
192  if (e.nodeName() == "exe-script") {
193  if (!property(ExeScript).toString().isEmpty()) {
194  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <exe-script> element"));
195  return;
196  }
197  setProperty(ExeScript, val);
198  } else if (e.nodeName() == "name") {
199  if (!property(Name).toString().isEmpty()) {
200  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <name> element"));
201  return;
202  }
203  setProperty(Name, val);
204  } else if (e.nodeName() == "author") {
205  setProperty(Author, property(Author).toStringList() + (QStringList()<<val));
206  } else if (e.nodeName() == "version") {
207  if (!property(Version).toString().isEmpty()) {
208  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <version> element"));
209  return;
210  }
211  setProperty(Version, val);
212  } else if (e.nodeName() == "license") {
213  if (!property(License).toString().isEmpty()) {
214  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <license> element"));
215  return;
216  }
217  setProperty(License, val);
218  } else if (e.nodeName() == "klf-min-version") {
219  if (!property(KLFMinVersion).toString().isEmpty()) {
220  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <klf-min-version> element"));
221  return;
222  }
223  setProperty(KLFMinVersion, val);
224  } else if (e.nodeName() == "klf-max-version") {
225  if (!property(KLFMaxVersion).toString().isEmpty()) {
226  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <klf-max-version> element"));
227  return;
228  }
229  setProperty(KLFMaxVersion, val);
230  } else if (e.nodeName() == "category") {
231  if (!property(Category).toString().isEmpty()) {
232  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <category> element"));
233  return;
234  }
235  setProperty(Category, val);
236  } else if (e.nodeName() == "settings-form-ui") {
237  if (!property(SettingsFormUI).toString().isEmpty()) {
238  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <settings-form-ui> element"));
239  return;
240  }
241  setProperty(SettingsFormUI, val);
242  } else if (e.nodeName() == "can-provide-default-settings") {
243  setProperty(CanProvideDefaultSettings, klfLoadVariantFromText(val.toUtf8(), "bool").toBool());
244  } else {
245  const QString category = property(Category).toString();
246  if (e.nodeName() == category) {
248  _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <%1> element")
249  .arg(category));
250  return;
251  }
252  // element node matching the category -- keep category-specific config as XML
253  QByteArray xmlrepr;
254  { QTextStream tstream(&xmlrepr);
255  e.save(tstream, 2); }
256  klfDbg("Read category-specific XML: " << xmlrepr);
258  } else {
259  _set_xml_parsing_error(xmlfname, QString::fromLatin1("Unexpected element: %1").arg(e.nodeName()));
260  return;
261  }
262  }
263  } // for all elements
264 
265  klfDbg("All properties read: \n" << qPrintable(toString()));
266  } // read_script_info()
267 
268 
269  static QMap<QString,KLFRefPtr<Private> > userScriptInfoCache;
270 
271 private:
272  /* no copy constructor */
273  Private(const Private& /*other*/) : KLFPropertizedObject("KLFUserScriptInfo") { }
274 };
275 
276 
277 // static
278 QMap<QString,KLFRefPtr<KLFUserScriptInfo::Private> > KLFUserScriptInfo::Private::userScriptInfoCache;
279 
280 static QString normalizedFn(const QString& userScriptFileName)
281 {
282  return QFileInfo(userScriptFileName).canonicalFilePath();
283 }
284 
285 // static
286 KLFUserScriptInfo KLFUserScriptInfo::forceReloadScriptInfo(const QString& userScriptFileName)
287 {
288  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
289 
290  QString normalizedfn = normalizedFn(userScriptFileName);
291  Private::userScriptInfoCache.remove(normalizedfn);
292 
293  KLFUserScriptInfo usinfo(userScriptFileName) ;
294  if (usinfo.scriptInfoError() != KLFERR_NOERROR) {
295  klfWarning(qPrintable(usinfo.scriptInfoErrorString()));
296  }
297 
298  return usinfo;
299 }
300 // static
301 void KLFUserScriptInfo::clearCacheAll()
302 {
303  // will decrease the refcounts if needed automatically (KLFRefPtr)
304  Private::userScriptInfoCache.clear();
305 }
306 
307 
308 // static
309 bool KLFUserScriptInfo::hasScriptInfoInCache(const QString& userScriptFileName)
310 {
311  QString normalizedfn = normalizedFn(userScriptFileName);
312  klfDbg("userScriptFileName = " << userScriptFileName << "; normalizedfn = " << normalizedfn) ;
313  klfDbg("cache: " << Private::userScriptInfoCache) ;
314  return Private::userScriptInfoCache.contains(normalizedfn);
315 }
316 
317 KLFUserScriptInfo::KLFUserScriptInfo(const QString& userScriptFileName)
318 {
320 
321  QFileInfo fi(userScriptFileName);
322  QString normalizedfn = fi.canonicalFilePath();
323  if (Private::userScriptInfoCache.contains(normalizedfn)) {
324  d = Private::userScriptInfoCache[normalizedfn];
325  } else {
326  d = new KLFUserScriptInfo::Private;
327 
328  d()->uspath = normalizedfn;//userScriptFileName;
329  d()->normalizedfname = normalizedfn;
330  d()->sname = fi.fileName();
331  d()->basename = fi.baseName();
332 
333  d()->read_script_info();
334 
335  if (d()->scriptInfoError == KLFERR_NOERROR) {
336  Private::userScriptInfoCache[normalizedfn] = d();
337  }
338  }
339 }
340 
343 {
344  // will increase the refcount (thanks to KLFRefPtr)
345  d = copy.d;
346 }
347 
348 KLFUserScriptInfo::~KLFUserScriptInfo()
349 {
350  d.setNull(); // will delete the data if refcount reaches zero (see KLFRefPtr)
351 }
352 
354 {
355  return d()->uspath;
356 }
358 {
359  return d()->sname;
360 }
362 {
363  return d()->basename;
364 }
365 
366 int KLFUserScriptInfo::scriptInfoError() const
367 {
368  return d()->scriptInfoError;
369 }
370 QString KLFUserScriptInfo::scriptInfoErrorString() const
371 {
372  return d()->scriptInfoErrorString;
373 }
374 
375 //protected
376 void KLFUserScriptInfo::setScriptInfoError(int code, const QString & msg)
377 {
378  d()->scriptInfoError = code;
379  d()->scriptInfoErrorString = msg;
380 }
381 
382 QString KLFUserScriptInfo::relativeFile(const QString& fname) const
383 {
384  return QDir::toNativeSeparators(userScriptPath()+"/"+fname);
385 }
386 
387 QString KLFUserScriptInfo::exeScript() const { return scriptInfo(ExeScript).toString(); }
388 QString KLFUserScriptInfo::exeScriptFullPath() const
389 {
390  return relativeFile(exeScript());
391 }
392 
393 QString KLFUserScriptInfo::category() const { return scriptInfo(Category).toString(); }
394 QString KLFUserScriptInfo::name() const { return scriptInfo(Name).toString(); }
395 QString KLFUserScriptInfo::author() const { return scriptInfo(Author).toStringList().join("; "); }
396 QStringList KLFUserScriptInfo::authorList() const { return scriptInfo(Author).toStringList(); }
397 QString KLFUserScriptInfo::version() const { return scriptInfo(Version).toString(); }
398 QString KLFUserScriptInfo::license() const { return scriptInfo(License).toString(); }
399 QString KLFUserScriptInfo::klfMinVersion() const { return scriptInfo(KLFMinVersion).toString(); }
400 QString KLFUserScriptInfo::klfMaxVersion() const { return scriptInfo(KLFMaxVersion).toString(); }
401 QString KLFUserScriptInfo::settingsFormUI() const { return scriptInfo(SettingsFormUI).toString(); }
402 
403 bool KLFUserScriptInfo::canProvideDefaultSettings() const { return scriptInfo(CanProvideDefaultSettings).toBool(); }
404 
405 QMap<QString,QVariant> KLFUserScriptInfo::queryDefaultSettings(const KLFBackend::klfSettings * settings) const
406 {
407  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
408 
409  KLFUserScriptFilterProcess proc(userScriptPath(), settings);
410 
411  // since this may be called on start-up, processing app events may lead to process
412  // hanging on mac os x (why???)
413  proc.setProcessAppEvents(false);
414 
415  proc.addArgv(QStringList() << QLatin1String("--query-default-settings"));
416 
417  // buffers to collect output
418  QByteArray stdoutdata;
419  QByteArray stderrdata;
420  proc.collectStdoutTo(&stdoutdata);
421  proc.collectStderrTo(&stderrdata);
422 
423  bool ok = proc.run();
424  if (!ok) {
425  klfWarning("Error querying default config for user script "<<userScriptBaseName()<<": "
426  << qPrintable(proc.resultErrorString())) ;
427  return QMap<QString,QVariant>();
428  }
429 
430  klfDbg("stdoutdata = " << stdoutdata) ;
431  klfDbg("stderrdata = " << stderrdata) ;
432 
433  klfDbg("Ran script "<<userScriptPath()<<": stdout="<<stdoutdata<<"\n\tstderr="<<stderrdata) ;
434 
435 
436  // the output may be one of two formats:
437  // - XML compatible with klf{Save|Load}VariantMap{To|From}XML()
438  // - simple key=value pairs on separate lines
439  // If the output starts with <?xml then we go for XML, otherwise we try to parse
440  // key=value pairs.
441 
442  QByteArray trimmedstdoutdata = stdoutdata.trimmed();
443  if (trimmedstdoutdata.startsWith("<?xml")) {
444  QDomDocument doc("klfuserscript-default-settings");
445  QString errMsg; int errLine, errCol;
446  bool r = doc.setContent(trimmedstdoutdata, false, &errMsg, &errLine, &errCol);
447  if (!r) {
448  klfWarning("XML parse error: "<<qPrintable(errMsg)
449  <<" ("<<qPrintable(userScriptBaseName())<<" default-settings, line "
450  <<errLine<<" col "<<errCol<<")") ;
451  return QVariantMap();
452  }
453 
454  QDomElement root = doc.documentElement();
455  if (root.nodeName() != "klfuserscript-default-settings") {
456  klfWarning("expected <klfuserscript-default-settings> as root document element");
457  return QVariantMap();
458  }
459 
460  QVariantMap config = klfLoadVariantMapFromXML(root);
461  return config;
462  }
463 
464  // otherwise, parse key=value pairs
465 
466  // get variables
467  QMap<QString,QVariant> config;
468  foreach (QByteArray line, trimmedstdoutdata.split('\n')) {
469  if (!line.size()) {
470  continue;
471  }
472  int idxeq = line.indexOf('=');
473  if (idxeq == -1) {
474  klfWarning("Invalid line in reported userscript default config: " << line) ;
475  continue;
476  }
477  config[QString::fromUtf8(line.left(idxeq)).trimmed()] = line.mid(idxeq+1).trimmed();
478  }
479 
480  return config;
481 }
482 
483 
484 
486 {
487  return scriptInfo(CategorySpecificXmlConfig).toByteArray();
488 }
489 
490 
491 bool KLFUserScriptInfo::hasNotices() const
492 {
493  return d->notices.size();
494 }
495 bool KLFUserScriptInfo::hasWarnings() const
496 {
497  return d->warnings.size();
498 }
499 bool KLFUserScriptInfo::hasErrors() const
500 {
501  return d->errors.size();
502 }
503 
504 KLF_DEFINE_PROPERTY_GET(KLFUserScriptInfo, QStringList, notices) ;
505 
506 KLF_DEFINE_PROPERTY_GET(KLFUserScriptInfo, QStringList, warnings) ;
507 
508 KLF_DEFINE_PROPERTY_GET(KLFUserScriptInfo, QStringList, errors) ;
509 
510 
511 
512 QVariant KLFUserScriptInfo::scriptInfo(int propId) const
513 {
514  return d()->property(propId);
515 }
516 
517 QVariant KLFUserScriptInfo::scriptInfo(const QString& field) const
518 {
520  QString x = field;
521 
522  if (x == QLatin1String("Authors")) {
523  x = QLatin1String("Author");
524  }
525 
526  klfDbg("x="<<x) ;
527  int id = d()->propertyIdForName(x);
528  if (id < 0) {
529  klfDbg("KLFUserScriptInfo for "<<userScriptName()<<" does not have any information about "
530  <<field<<" ("<<x<<")") ;
531  return QVariant();
532  }
533  return scriptInfo(id);
534 }
535 
537 {
538  return d()->propertyNameList();
539 }
540 
541 QString KLFUserScriptInfo::objectKind() const { return d()->objectKind(); }
542 
543 
544 // protected. Used by eg. KLFExportTypeUserScriptInfo to normalize list property values.
545 void KLFUserScriptInfo::internalSetProperty(const QString& key, const QVariant &val)
546 {
547  d()->setProperty(key, val);
548 }
549 
550 const KLFPropertizedObject * KLFUserScriptInfo::pobj()
551 {
552  return d();
553 }
554 
555 
556 static QString escapeListIntoTags(const QStringList& list, const QString& starttag, const QString& endtag)
557 {
558  QString html;
559  foreach (QString s, list) {
560  html += starttag + s.toHtmlEscaped() + endtag;
561  }
562  return html;
563 }
564 
565 QString KLFUserScriptInfo::htmlInfo(const QString& extra_css) const
566 {
567  QString txt =
568  "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
569  "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
570  "p, li { white-space: pre-wrap; }\n"
571  "p.msgnotice { color: blue; font-weight: bold; margin: 2px 0px; }\n"
572  "p.msgwarning { color: #a06000; font-weight: bold; margin: 2px 0px; }\n"
573  "p.msgerror { color: #a00000; font-weight: bold; margin: 2px 0px; }\n"
574  ".scriptinfokey { }\n"
575  ".scriptinfovalue { font-weight: bold; }\n"
576  + extra_css + "\n"
577  "</style></head>\n"
578  "<body>\n";
579 
580  // any notices/warnings/errors go first
581  if (hasNotices()) {
582  txt += escapeListIntoTags(notices(), "<p class=\"msgnotice\">", "</p>\n");
583  }
584  if (hasWarnings()) {
585  txt += escapeListIntoTags(warnings(), "<p class=\"msgwarning\">", "</p>\n");
586  }
587  if (hasErrors()) {
588  txt += escapeListIntoTags(errors(), "<p class=\"msgerror\">", "</p>\n");
589  }
590 
591  // the user script name (incl ".klfuserscript")
592  txt +=
593  "<p style=\"-qt-block-indent: 0; text-indent: 0px; margin-top: 8px; margin-bottom: 0px\">\n"
594  "<span class=\"scriptinfokey\">" + QObject::tr("Script Name:", "[[user script info text]]")
595  + "</span>&nbsp;&nbsp;"
596  "<span class=\"scriptinfovalue\">" + userScriptName().toHtmlEscaped() + "</span><br />\n";
597 
598  // the category
599  txt += "<span class=\"scriptinfokey\">" + QObject::tr("Category:", "[[user script info text]]")
600  + "</span>&nbsp;&nbsp;"
601  "<span class=\"scriptinfovalue\">" + category().toHtmlEscaped() + "</span><br />\n";
602 
603  if (!version().isEmpty()) {
604  // the version
605  txt += "<span class=\"scriptinfokey\">" + QObject::tr("Version:", "[[user script info text]]")
606  + "</span>&nbsp;&nbsp;"
607  "<span class=\"scriptinfovalue\">" + version().toHtmlEscaped() + "</span><br />\n";
608  }
609  if (!author().isEmpty()) {
610  // the author
611  txt += "<span class=\"scriptinfokey\">" + QObject::tr("Author:", "[[user script info text]]")
612  + "</span>&nbsp;&nbsp;"
613  "<span class=\"scriptinfovalue\">" + author().toHtmlEscaped() + "</span><br />\n";
614  }
615 
616  if (!license().isEmpty()) {
617  // the license
618  txt += "<span class=\"scriptinfokey\">" + QObject::tr("License:", "[[user script info text]]")
619  + "</span>&nbsp;&nbsp;"
620  "<span class=\"scriptinfovalue\">" + license().toHtmlEscaped() + "</span><br />\n";
621  }
622 
623  return txt;
624 }
625 
626 
627 
628 // static
629 QMap<QString,QString> KLFUserScriptInfo::usConfigToStrMap(const QVariantMap& usconfig)
630 {
631  QMap<QString,QString> mdata;
632  for (QVariantMap::const_iterator it = usconfig.begin(); it != usconfig.end(); ++it)
633  mdata[QLatin1String("KLF_USCONFIG_") + it.key()] = klfSaveVariantToText(it.value(), true);
634  return mdata;
635 }
636 
637 // static
638 QStringList KLFUserScriptInfo::usConfigToEnvList(const QVariantMap& usconfig)
639 {
640  return klfMapToEnvironmentList(KLFUserScriptInfo::usConfigToStrMap(usconfig));
641 }
642 
643 
644 inline QStringList space_sep_values(const QString& val)
645 {
646  return val.split(QRegExp("\\s+"), QString::SkipEmptyParts);
647 }
648 
649 
650 
651 
652 struct KLFBackendEngineUserScriptInfoPrivate : public KLFPropertizedObject
653 {
655  : KLFPropertizedObject("KLFBackendEngineUserScriptInfo"))
656  {
657  registerBuiltInProperty(KLFBackendEngineUserScriptInfo::SpitsOut, QLatin1String("SpitsOut"));
658  registerBuiltInProperty(KLFBackendEngineUserScriptInfo::SkipFormats, QLatin1String("SkipFormats"));
659  registerBuiltInProperty(KLFBackendEngineUserScriptInfo::DisableInputs, QLatin1String("DisableInputs"));
660  registerBuiltInProperty(KLFBackendEngineUserScriptInfo::InputFormUI, QLatin1String("InputFormUI"));
661  }
662  void clear()
663  {
664  // clear all properties
666  for (int k = 0; k < idlist.size(); ++k) {
667  setProperty(idlist[k], QVariant());
668  }
669  }
670 
671  void _set_xml_parsing_error(const QString& errmsg)
672  {
673  K->setScriptInfoError(1001, QString("Error parsing klf-backend-engine XML config: %1: %2")
674  .arg(K->userScriptBaseName()).arg(errmsg));
675  }
676  void parse_category_config(const QByteArray & ba)
677  {
678  QDomDocument doc("klf-backend-engine");
679  QString errMsg; int errLine, errCol;
680  bool r = doc.setContent(ba, false, &errMsg, &errLine, &errCol);
681  if (!r) {
682  K->setScriptInfoError(
683  1001,
684  QString("XML parse error: %1 (klf-backend-engine in %2, relative line %3 col %4)")
685  .arg(errMsg).arg(K->userScriptBaseName()).arg(errLine).arg(errCol));
686  return;
687  }
688 
689  QDomElement root = doc.documentElement();
690  if (root.nodeName() != "klf-backend-engine") {
691  _set_xml_parsing_error(QString("expected <klf-backend-engine> element"));
692  return;
693  }
694 
695  // clear all properties
696  clear();
697 
698  // read XML contents
699  QDomNode n;
700  for (n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
701  // try to convert the node to an element; ignore non-elements
702  if ( n.nodeType() != QDomNode::ElementNode ) {
703  continue;
704  }
705  QDomElement e = n.toElement();
706  if ( e.isNull() ) {
707  continue;
708  }
709  // parse the elements.
710  QString val = e.text();
711  if (val.isEmpty()) {
712  val = QString(); // empty value is null string
713  }
714  if (e.nodeName() == "spits-out") {
715  if (!property(KLFBackendEngineUserScriptInfo::SpitsOut).toStringList().isEmpty()) {
716  _set_xml_parsing_error(QString("duplicate <spits-out> element"));
717  return;
718  }
719  setProperty(KLFBackendEngineUserScriptInfo::SpitsOut, space_sep_values(val));
720  } else if (e.nodeName() == "skip-formats") {
721  if (!property(KLFBackendEngineUserScriptInfo::SkipFormats).toString().isEmpty()) {
722  _set_xml_parsing_error(QString("duplicate <skip-formats> element"));
723  return;
724  }
725  QStringList lst;
726  if (e.hasAttribute("selector")) {
727  // all-except -> ALL_EXCEPT
728  QString s = e.attribute("selector").toUpper();
729  lst << space_sep_values(s.replace('-', '_'));
730  }
731  lst << space_sep_values(val);
732  setProperty(KLFBackendEngineUserScriptInfo::SkipFormats, lst);
733  } else if (e.nodeName() == "disable-inputs") {
734  if (!property(KLFBackendEngineUserScriptInfo::DisableInputs).toStringList().isEmpty()) {
735  _set_xml_parsing_error(QString("duplicate <disable-inputs> element"));
736  return;
737  }
738  QStringList lst;
739  if (e.hasAttribute("selector")) {
740  // all-except -> ALL_EXCEPT
741  QString s = e.attribute("selector").toUpper();
742  lst << space_sep_values(s.replace('-', '_'));
743  }
744  lst << space_sep_values(val);
745  setProperty(KLFBackendEngineUserScriptInfo::DisableInputs, lst);
746  } else if (e.nodeName() == "input-form-ui") {
747  if (!property(KLFBackendEngineUserScriptInfo::InputFormUI).toStringList().isEmpty()) {
748  _set_xml_parsing_error(QString("duplicate <input-form-ui> element"));
749  return;
750  }
751  setProperty(KLFBackendEngineUserScriptInfo::InputFormUI, val);
752  } else {
753  _set_xml_parsing_error(QString("Found unexpected element: %1").arg(e.nodeName()));
754  return;
755  }
756  }
757 
758  klfDbg("Read all klfbackend-engine properties:\n" << qPrintable(toString()));
759  }
760 };
761 
762 
763 
764 KLFBackendEngineUserScriptInfo::KLFBackendEngineUserScriptInfo(const QString& uspath)
765  : KLFUserScriptInfo(uspath)
766 {
768 
769  if (category() != "klf-backend-engine") {
770  klfWarning("KLFBackendEngineUserScriptInfo instantiated for user script "
771  << uspath << ", which is of category " << category()) ;
772  } else {
773  d->parse_category_config(categorySpecificXmlConfig());
774  }
775 }
776 
777 KLFBackendEngineUserScriptInfo::~KLFBackendEngineUserScriptInfo()
778 {
780 }
781 
782 
783 
785 {
786  return klfBackendEngineInfo(SpitsOut).toStringList();
787 }
789 {
790  return klfBackendEngineInfo(SkipFormats).toStringList();
791 }
793 {
794  return klfBackendEngineInfo(DisableInputs).toStringList();
795 }
797 {
798  return klfBackendEngineInfo(InputFormUI).toString();
799 }
800 
801 
802 QVariant KLFBackendEngineUserScriptInfo::klfBackendEngineInfo(int propId) const
803 {
804  return d->property(propId);
805 }
806 
807 QVariant KLFBackendEngineUserScriptInfo::klfBackendEngineInfo(const QString& field) const
808 {
809  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
810  QString x = field;
811 
812  klfDbg("x="<<x) ;
813  int id = d->propertyIdForName(x);
814  if (id < 0) {
815  klfDbg("KLFBackendEngineUserScriptInfo for "<<userScriptName()<<" does not have any information about "
816  <<field<<" ("<<x<<")") ;
817  return QVariant();
818  }
819  return scriptInfo(id);
820 }
821 
822 QStringList KLFBackendEngineUserScriptInfo::klfBackendEngineInfosList() const
823 {
824  return d->propertyNameList();
825 }
826 
827 
828 
829 
830 
831 
832 
833 
834 
835 
836 // ----------------------------------------
837 
838 struct KLFUserScriptFilterProcessPrivate
839 {
841  {
842  usinfo = NULL;
843  }
844 
845  KLFUserScriptInfo * usinfo;
846 
847  static QStringList log;
848 };
849 
850 // static
851 QStringList KLFUserScriptFilterProcessPrivate::log = QStringList();
852 
853 
855  const KLFBackend::klfSettings * settings)
856  : KLFFilterProcess("User Script " + userScriptFileName, settings)
857 {
859  klfDbg("userScriptFileName= "<<userScriptFileName) ;
860 
862 
863  d->usinfo = new KLFUserScriptInfo(userScriptFileName);
864 
865  QString exescript = d->usinfo->exeScriptFullPath();
866  klfDbg("exescript = " << exescript) ;
867  setArgv(QStringList() << exescript);
868 }
869 
870 
871 KLFUserScriptFilterProcess::~KLFUserScriptFilterProcess()
872 {
873  delete d->usinfo;
875 }
876 
877 void KLFUserScriptFilterProcess::addUserScriptConfig(const QVariantMap& usconfig)
878 {
879  QStringList envlist = KLFUserScriptInfo::usConfigToEnvList(usconfig);
880  addExecEnviron(envlist);
881 }
882 
883 
884 bool KLFUserScriptFilterProcess::do_run(const QByteArray& indata, const QMap<QString, QByteArray*> outdatalist)
885 {
886  bool ret = KLFFilterProcess::do_run(indata, outdatalist);
887 
888  // for user script debugging
889  QString thislog = QString::fromLatin1("<h1 class=\"userscript-run\">")
890  + QObject::tr("Output from %1", "KLFUserScriptFilterProcess").arg(QLatin1String("<span class=\"userscriptname\">")
891  +d->usinfo->userScriptBaseName().toHtmlEscaped()
892  +QLatin1String("</span>")) +
893  QLatin1String("</h1>\n") +
894  QLatin1String("<p class=\"userscript-run-datetime\">") +
895  QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate).toHtmlEscaped()
896  + QLatin1String("</p>") ;
897 
898  // error message, if any
899  QString errstr = resultErrorString();
900  if (errstr.size()) {
901  thislog += QString::fromLatin1("<div class=\"userscript-error\">%1</div>").arg(errstr); // errstr is already HTML
902  }
903 
904  QString templ = QString::fromLatin1("<p><span class=\"output-type\">%1</span>\n"
905  "<pre class=\"output\">%2</pre></p>\n") ;
906 
907  QByteArray bstdout = collectedStdout();
908  if (bstdout.size()) {
909  thislog += templ.arg("STDOUT").arg(QString::fromLocal8Bit(bstdout).toHtmlEscaped());
910  }
911  QByteArray bstderr = collectedStderr();
912  if (bstderr.size()) {
913  thislog += templ.arg("STDERR").arg(QString::fromLocal8Bit(bstderr).toHtmlEscaped());
914  }
915 
916  // start discarding old logs after 255 entries
917  if (KLFUserScriptFilterProcessPrivate::log.size() > 255) {
918  KLFUserScriptFilterProcessPrivate::log.erase(KLFUserScriptFilterProcessPrivate::log.begin());
919  }
920 
921  KLFUserScriptFilterProcessPrivate::log << thislog;
922 
923  return ret;
924 }
925 
926 
928 {
929  QString loghtml;
930  QStringList::const_iterator it = KLFUserScriptFilterProcessPrivate::log.cend();
931  while (it != KLFUserScriptFilterProcessPrivate::log.cbegin()) {
932  --it;
933  loghtml += *it;
934  }
935  if (!include_head) {
936  return loghtml;
937  }
938  return QLatin1String("<html><head>"
939  "<meta charset=\"utf-8\">"
940  "<title>User Script Log</title>"
941  "<style type=\"text/css\">"
942  ".userscript-run { font-weight: bold; font-size: 2em; } "
943  ".userscriptname { font: monospace; } "
944  ".output-type { font-weight: bold; } "
945  "</style>"
946  "</head>"
947  "<body>") + loghtml + QLatin1String("</body></html>") ;
948 }
klfSaveVariantToText
KLF_EXPORT QByteArray klfSaveVariantToText(const QVariant &value, bool saveListAndMapsAsXML, QByteArray *savedType, QByteArray *savedListOrMapType)
KLFBackendEngineUserScriptInfo::spitsOut
QStringList spitsOut() const
List of formats that this script will generate.
Definition: klfuserscript.cpp:783
QDomElement::text
QString text() const
klfMapToEnvironmentList
QStringList klfMapToEnvironmentList(const QMap< QString, QString > &map)
QString::split
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QDomNode::firstChild
QDomNode firstChild() const
KLFUserScriptInfo::userScriptName
QString userScriptName() const
e.g. "klffeynmf.klfuserscript"
Definition: klfuserscript.cpp:356
QDomNode::toElement
QDomElement toElement() const
QString::toUpper
QString toUpper() const
KLFUserScriptInfo
Summary of the info returned by a user script.
Definition: klfuserscript.h:36
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::size
int size() const
QString::toHtmlEscaped
QString toHtmlEscaped() const
KLFRefPtr::setNull
void setNull()
QDomNode
KLFFilterProcess::resultErrorString
virtual QString resultErrorString() const
Definition: klffilterprocess.cpp:278
QByteArray::indexOf
int indexOf(char ch, int from) const
QDateTime::currentDateTime
QDateTime currentDateTime()
KLFPropertizedObject::KLFPropertizedObject
KLFPropertizedObject(const QString &propertyNameSpace)
QDomNode::isNull
bool isNull() const
KLFUserScriptInfo::userScriptPath
QString userScriptPath() const
e.g. "/path/to/klffeynmf.klfuserscript"
Definition: klfuserscript.cpp:352
QByteArray::split
QList< QByteArray > split(char sep) const
QByteArray::trimmed
QByteArray trimmed() const
KLFBackendEngineUserScriptInfo::skipFormats
QStringList skipFormats() const
List of formats that klfbackend should not attempt to generate.
Definition: klfuserscript.cpp:787
KLF_PRIVATE_HEAD
#define KLF_PRIVATE_HEAD(ClassName)
QVariant::toByteArray
QByteArray toByteArray() const
KLFBackendEngineUserScriptInfo::inputFormUI
QString inputFormUI() const
A UI input form file (Qt designer file) for additional input.
Definition: klfuserscript.cpp:795
KLF_FUNC_NAME
#define KLF_FUNC_NAME
QList
KLFBackendEngineUserScriptInfo
Definition: klfuserscript.h:153
KLFUserScriptFilterProcess::KLFUserScriptFilterProcess
KLFUserScriptFilterProcess(const QString &scriptFileName, const KLFBackend::klfSettings *settings=NULL)
Definition: klfuserscript.cpp:853
KLFUserScriptInfo::KLFUserScriptInfo
KLFUserScriptInfo(const QString &userScriptPath)
Definition: klfuserscript.cpp:316
KLFUserScriptInfo::scriptInfosList
QStringList scriptInfosList() const
A list of Keys (eg. "Name", "Author", ... including custom infos) found in the scriptinfo.
Definition: klfuserscript.cpp:535
QList::size
int size() const
klfDbg
#define klfDbg(streamableItems)
QString::fromLocal8Bit
QString fromLocal8Bit(const char *str, int size)
KLF_DEBUG_BLOCK
#define KLF_DEBUG_BLOCK(msg)
QString
klfLoadVariantFromText
KLF_EXPORT QVariant klfLoadVariantFromText(const QByteArray &stringdata, const char *dataTypeName, const char *listOrMapDataTypeName)
QTextStream
KLFPropertizedObject::registeredPropertyIdList
QList< int > registeredPropertyIdList() const
QFileInfo::baseName
QString baseName() const
QByteArray::mid
QByteArray mid(int pos, int len) const
QString::isEmpty
bool isEmpty() const
QDir::toNativeSeparators
QString toNativeSeparators(const QString &pathName)
QString::toUtf8
QByteArray toUtf8() const
KLF_DELETE_PRIVATE
#define KLF_DELETE_PRIVATE
QDomNode::nodeType
NodeType nodeType() const
klfdatautil.h
klfLoadVariantMapFromXML
KLF_EXPORT QVariantMap klfLoadVariantMapFromXML(const QDomElement &xmlNode)
QByteArray::startsWith
bool startsWith(const QByteArray &ba) const
KLFUserScriptInfo::settingsFormUI
QString settingsFormUI() const
A UI widget form file (Qt designer file) to display for setting up the user script.
Definition: klfuserscript.cpp:400
QList::isEmpty
bool isEmpty() const
QList::cend
const_iterator cend() const
KLFPropertizedObject::registerBuiltInProperty
void registerBuiltInProperty(int propId, const QString &propName) const
QList::const_iterator
QStringList::join
QString join(const QString &separator) const
KLFFilterProcess
Definition: klffilterprocess.h:46
QLatin1String
QFileInfo::fileName
QString fileName() const
KLFUserScriptInfo::categorySpecificXmlConfig
QByteArray categorySpecificXmlConfig() const
The XML for the category-specific config.
Definition: klfuserscript.cpp:484
QDomNode::nodeName
QString nodeName() const
QVariant::toBool
bool toBool() const
QString::replace
QString & replace(int position, int n, QChar after)
KLF_PRIVATE_INHERIT_HEAD
#define KLF_PRIVATE_INHERIT_HEAD(ClassName, BaseInit)
klfbackend.h
Definition of class KLFBackend.
QMap
QDomElement::hasAttribute
bool hasAttribute(const QString &name) const
QByteArray::left
QByteArray left(int len) const
QByteArray::isEmpty
bool isEmpty() const
klfWarning
#define klfWarning(streamableItems)
QList::erase
iterator erase(iterator pos)
QDomElement
KLFUserScriptInfo::userScriptBaseName
QString userScriptBaseName() const
e.g. "klffeynmf"
Definition: klfuserscript.cpp:360
QString::fromLatin1
QString fromLatin1(const char *str, int size)
QFileInfo
KLFFilterProcess::collectedStdout
QByteArray collectedStdout() const
The collected stdout data of the process that just ran.
Definition: klffilterprocess.cpp:406
KLFAbstractPropertizedObject
QDomNode::save
void save(QTextStream &stream, int indent, EncodingPolicy encodingPolicy) const
QDomNode::nextSibling
QDomNode nextSibling() const
KLFBackend::klfSettings
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:217
klfdebug.h
KLFUserScriptFilterProcess::do_run
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray * > outdatalist)
Definition: klfuserscript.cpp:883
QRegExp
KLFPropertizedObject::toString
virtual QString toString(uint toStringFlags=0) const
QByteArray::size
int size() const
KLFFilterProcess::do_run
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray * > outdatalist)
Actually run the process.
Definition: klffilterprocess.cpp:288
KLF_INIT_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
KLFUserScriptInfo::CategorySpecificXmlConfig
XML representation of the category-specific configuration (QByteArray)
Definition: klfuserscript.h:93
klfpobj.h
KLFPropertizedObject
QVariant::toStringList
QStringList toStringList() const
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
QDomDocument
QDomElement::attribute
QString attribute(const QString &name, const QString &defValue) const
KLFPropertizedObject::setProperty
virtual bool setProperty(const QString &propname, const QVariant &value)
QVariant
KLFUserScriptFilterProcess
Definition: klfuserscript.h:198
QDateTime::toString
QString toString(Qt::DateFormat format) const
KLFERR_NOERROR
#define KLFERR_NOERROR
No Error.
Definition: klfbackend.h:45
KLFFilterProcess::collectedStderr
QByteArray collectedStderr() const
The collected stderr data of the process that just ran.
Definition: klffilterprocess.cpp:413
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
KLFBackendEngineUserScriptInfo::disableInputs
QStringList disableInputs() const
List of user input fields that should be disabled.
Definition: klfuserscript.cpp:791
klfdefs.h
QFile
KLFUserScriptFilterProcess::getUserScriptLogHtml
static QString getUserScriptLogHtml(bool include_head=true)
Return the user script log, formatted in human-readable HTML.
Definition: klfuserscript.cpp:926
KLFUserScriptInfo::htmlInfo
QString htmlInfo(const QString &extra_css=QString()) const
Formats most (all?) properties in HTML, suitable for human-readable text display.
Definition: klfuserscript.cpp:564
KLFPropertizedObject::property
virtual QVariant property(const QString &propName) const
QByteArray
QFileInfo::canonicalFilePath
QString canonicalFilePath() const
QStringList
QVariant::toString
QString toString() const

Generated by doxygen 1.8.16