[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 
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 {
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 
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
665  QList<int> idlist = registeredPropertyIdList();
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 {
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 
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 }
QStringList skipFormats() const
List of formats that klfbackend should not attempt to generate.
static QString getUserScriptLogHtml(bool include_head=true)
Return the user script log, formatted in human-readable HTML.
QStringList disableInputs() const
List of user input fields that should be disabled.
QString toString(Qt::DateFormat format) const
QByteArray toByteArray() const
QString toNativeSeparators(const QString &pathName)
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray *> outdatalist)
Actually run the process.
QString toUpper() const
KLF_EXPORT QByteArray klfSaveVariantToText(const QVariant &value, bool saveListAndMapsAsXML, QByteArray *savedType, QByteArray *savedListOrMapType)
#define KLF_PRIVATE_HEAD(ClassName)
XML representation of the category-specific configuration (QByteArray)
Definition: klfuserscript.h:75
QString htmlInfo(const QString &extra_css=QString()) const
Formats most (all?) properties in HTML, suitable for human-readable text display. ...
QList< QByteArray > split(char sep) const
QByteArray trimmed() const
KLFUserScriptFilterProcess(const QString &scriptFileName, const KLFBackend::klfSettings *settings=NULL)
QString attribute(const QString &name, const QString &defValue) const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
int size() const
bool isEmpty() const
bool startsWith(const QByteArray &ba) const
#define klfDbg(streamableItems)
QString userScriptName() const
e.g. "klffeynmf.klfuserscript"
#define KLF_DEBUG_BLOCK(msg)
iterator erase(iterator pos)
void collectStdoutTo(QByteArray *stdoutstore)
QDomElement documentElement() const
#define KLFERR_NOERROR
No Error.
Definition: klfbackend.h:46
NodeType nodeType() const
void registerBuiltInProperty(int propId, const QString &propName) const
QByteArray collectedStderr() const
The collected stderr data of the process that just ran.
virtual QVariant property(const QString &propName) const
QString tr(const char *sourceText, const char *disambiguation, int n)
KLFUserScriptInfo(const QString &userScriptPath)
void collectStderrTo(QByteArray *stderrstore)
QString inputFormUI() const
A UI input form file (Qt designer file) for additional input.
QStringList scriptInfosList() const
A list of Keys (eg. "Name", "Author", ... including custom infos) found in the scriptinfo.
QStringList spitsOut() const
List of formats that this script will generate.
int size() const
QDomNode nextSibling() const
QDomElement toElement() const
#define KLF_DELETE_PRIVATE
QString userScriptPath() const
e.g. "/path/to/klffeynmf.klfuserscript"
int indexOf(char ch, int from) const
QByteArray collectedStdout() const
The collected stdout data of the process that just ran.
QString canonicalFilePath() const
QString fromLocal8Bit(const char *str, int size)
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray *> outdatalist)
QString fromUtf8(const char *str, int size)
QString text() const
bool hasAttribute(const QString &name) const
virtual QString resultErrorString() const
QString fileName() const
#define klfWarning(streamableItems)
QString nodeName() const
bool isEmpty() const
virtual bool setProperty(const QString &propname, const QVariant &value)
const char * key
QByteArray mid(int pos, int len) const
QString userScriptBaseName() const
e.g. "klffeynmf"
QList< int > registeredPropertyIdList() const
QString toHtmlEscaped() const
#define KLF_FUNC_NAME
bool isNull() const
Definition of class KLFBackend.
QString & replace(int position, int n, QChar after)
#define KLF_PRIVATE_INHERIT_HEAD(ClassName, BaseInit)
QByteArray left(int len) const
const_iterator cend() const
QString settingsFormUI() const
A UI widget form file (Qt designer file) to display for setting up the user script.
QDateTime currentDateTime()
void save(QTextStream &stream, int indent, EncodingPolicy encodingPolicy) const
QDomNode firstChild() const
Summary of the info returned by a user script.
Definition: klfuserscript.h:37
QStringList klfMapToEnvironmentList(const QMap< QString, QString > &map)
QByteArray categorySpecificXmlConfig() const
The XML for the category-specific config.
QString fromLatin1(const char *str, int size)
#define KLF_INIT_PRIVATE(ClassName)
virtual QString toString(uint toStringFlags=0) const
KLF_EXPORT QVariant klfLoadVariantFromText(const QByteArray &stringdata, const char *dataTypeName, const char *listOrMapDataTypeName)
int size() const
KLF_EXPORT QVariantMap klfLoadVariantMapFromXML(const QDomElement &xmlNode)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
QString toString() const
int propId
QString baseName() const
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
void setProcessAppEvents(bool processEvents)
QByteArray toUtf8() const

Generated by doxygen 1.8.13