[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
src/klfbackend/klfuserscript.cpp
00001 /***************************************************************************
00002  *   file klfuserscript.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2012 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id$ */
00023 
00024 #include <QFileInfo>
00025 #include <QDir>
00026 #include <QDateTime>
00027 #include <QByteArray>
00028 
00029 #include <klfdefs.h>
00030 #include <klfdebug.h>
00031 #include <klfpobj.h>
00032 #include <klfdatautil.h>
00033 
00034 #include "klfbackend.h"
00035 #include "klfbackend_p.h"
00036 #include "klfuserscript.h"
00037 
00038 
00049 /*
00050 static int read_spec_section(const QString& str, int fromindex, const QRegExp& seprx, QString * extractedPart)
00051 {
00052   int i = fromindex;
00053   bool in_quote = false;
00054 
00055   QString s;
00056 
00057   while (i < str.length() && (in_quote || seprx.indexIn(str, i) != i)) {
00058     if (str[i] == '\\') {
00059       s.append(str[i]);
00060       if (i+1 < str.length())
00061         s.append(str[i+1]);
00062       i += 2; // skip next char, too. The actual escaping will be done with klfEscapedToData()
00063       continue;
00064     }
00065     if (str[i] == '"') {
00066       in_quote = !in_quote;
00067       ++i;
00068       continue;
00069     }
00070     s.append(str[i]);
00071     ++i;
00072   }
00073 
00074   *extractedPart = QString::fromLocal8Bit(klfEscapedToData(s.toLocal8Bit()));
00075 
00076   return i; // the position of the last char separator
00077 }
00078 
00079 */
00080 
00081 
00082 
00083 
00084 
00085 struct KLFUserScriptInfo::Private : public KLFPropertizedObject
00086 {
00087   Private()
00088     : KLFPropertizedObject("KLFUserScriptInfo")
00089   {
00090     refcount = 0;
00091     scriptInfoError = KLFERR_NOERROR;
00092 
00093     registerBuiltInProperty(ExeScript, QLatin1String("ExeScript"));
00094     registerBuiltInProperty(Category, QLatin1String("Category"));
00095     registerBuiltInProperty(Name, QLatin1String("Name"));
00096     registerBuiltInProperty(Author, QLatin1String("Author"));
00097     registerBuiltInProperty(Version, QLatin1String("Version"));
00098     registerBuiltInProperty(License, QLatin1String("License"));
00099     registerBuiltInProperty(KLFMinVersion, QLatin1String("KLFMinVersion"));
00100     registerBuiltInProperty(KLFMaxVersion, QLatin1String("KLFMaxVersion"));
00101     registerBuiltInProperty(SettingsFormUI, QLatin1String("SettingsFormUI"));
00102     registerBuiltInProperty(CanProvideDefaultSettings, QLatin1String("CanProvideDefaultSettings"));
00103     registerBuiltInProperty(CategorySpecificXmlConfig, QLatin1String("CategorySpecificXmlConfig"));
00104   }
00105 
00106   void clear()
00107   {
00108     // clear all properties
00109     QList<int> idlist = registeredPropertyIdList();
00110     for (int k = 0; k < idlist.size(); ++k) {
00111       setProperty(idlist[k], QVariant());
00112     }
00113   }
00114 
00115   int refcount;
00116   inline int ref() { return ++refcount; }
00117   inline int deref() { return --refcount; }
00118 
00119   QString uspath;
00120   QString normalizedfname;
00121   QString sname;
00122   QString basename;
00123   int scriptInfoError;
00124   QString scriptInfoErrorString;
00125 
00126   QStringList notices;
00127   QStringList warnings;
00128   QStringList errors;
00129 
00130 
00131   void _set_xml_read_error(const QString& fullerrmsg)
00132   {
00133     scriptInfoError = 999;
00134     scriptInfoErrorString = fullerrmsg;
00135   }
00136   void _set_xml_parsing_error(const QString& xmlfname, const QString& errmsg)
00137   {
00138     scriptInfoError = 999;
00139     scriptInfoErrorString = QString("Error parsing scriptinfo XML contents: %1: %2")
00140       .arg(xmlfname).arg(errmsg);
00141   }
00142 
00143   void read_script_info()
00144   {
00145     scriptInfoError = KLFERR_NOERROR;
00146     scriptInfoErrorString = QString();
00147 
00148     QString xmlfname = QDir::toNativeSeparators(uspath + "/scriptinfo.xml");
00149     QFile fxml(xmlfname);
00150     if ( ! fxml.open(QIODevice::ReadOnly) ) {
00151       _set_xml_read_error(QString("Can't open XML file %1: %2").arg(xmlfname).arg(fxml.errorString()));
00152       return;
00153     }
00154 
00155     QDomDocument doc("klfuserscript-info");
00156     QString errMsg; int errLine, errCol;
00157     bool r = doc.setContent(&fxml, false, &errMsg, &errLine, &errCol);
00158     if (!r) {
00159       _set_xml_read_error(QString("XML parse error: %1 (file %2 line %3 col %4)")
00160                           .arg(errMsg).arg(xmlfname).arg(errLine).arg(errCol));
00161       return;
00162     }
00163     fxml.close();
00164 
00165     QDomElement root = doc.documentElement();
00166     if (root.nodeName() != "klfuserscript-info") {
00167       _set_xml_parsing_error(xmlfname, QString("expected <klfuserscript-info> as root document element"));
00168       return;
00169     }
00170     
00171     // clear all properties
00172     clear();
00173 
00174     setProperty(CanProvideDefaultSettings, false);
00175 
00176     // read XML contents
00177     QDomNode n;
00178     for (n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
00179       // try to convert the node to an element; ignore non-elements
00180       if ( n.nodeType() != QDomNode::ElementNode ) {
00181         continue;
00182       }
00183       QDomElement e = n.toElement();
00184       if ( e.isNull() ) {
00185         continue;
00186       }
00187       // parse the elements.
00188       QString val = e.text();
00189       if (val.isEmpty()) {
00190         val = QString(); // empty value is null string
00191       }
00192       if (e.nodeName() == "exe-script") {
00193         if (!property(ExeScript).toString().isEmpty()) {
00194           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <exe-script> element"));
00195           return;
00196         }
00197         setProperty(ExeScript, val);
00198       } else if (e.nodeName() == "name") {
00199         if (!property(Name).toString().isEmpty()) {
00200           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <name> element"));
00201           return;
00202         }
00203         setProperty(Name, val);
00204       } else if (e.nodeName() == "author") {
00205         setProperty(Author, property(Author).toStringList() + (QStringList()<<val));
00206       } else if (e.nodeName() == "version") {
00207         if (!property(Version).toString().isEmpty()) {
00208           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <version> element"));
00209           return;
00210         }
00211         setProperty(Version, val);
00212       } else if (e.nodeName() == "license") {
00213         if (!property(License).toString().isEmpty()) {
00214           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <license> element"));
00215           return;
00216         }
00217         setProperty(License, val);
00218       } else if (e.nodeName() == "klf-min-version") {
00219         if (!property(KLFMinVersion).toString().isEmpty()) {
00220           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <klf-min-version> element"));
00221           return;
00222         }
00223         setProperty(KLFMinVersion, val);
00224       } else if (e.nodeName() == "klf-max-version") {
00225         if (!property(KLFMaxVersion).toString().isEmpty()) {
00226           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <klf-max-version> element"));
00227           return;
00228         }
00229         setProperty(KLFMaxVersion, val);
00230       } else if (e.nodeName() == "category") {
00231         if (!property(Category).toString().isEmpty()) {
00232           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <category> element"));
00233           return;
00234         }
00235         setProperty(Category, val);
00236       } else if (e.nodeName() == "settings-form-ui") {
00237         if (!property(SettingsFormUI).toString().isEmpty()) {
00238           _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <settings-form-ui> element"));
00239           return;
00240         }
00241         setProperty(SettingsFormUI, val);
00242       } else if (e.nodeName() == "can-provide-default-settings") {
00243         setProperty(CanProvideDefaultSettings, klfLoadVariantFromText(val.toUtf8(), "bool").toBool());
00244       } else {
00245         const QString category = property(Category).toString();
00246         if (e.nodeName() == category) {
00247           if (!property(CategorySpecificXmlConfig).toByteArray().isEmpty()) {
00248             _set_xml_parsing_error(xmlfname, QString::fromLatin1("duplicate <%1> element")
00249                                    .arg(category));
00250             return;
00251           }
00252           // element node matching the category -- keep category-specific config as XML
00253           QByteArray xmlrepr;
00254           { QTextStream tstream(&xmlrepr);
00255             e.save(tstream, 2); }
00256           klfDbg("Read category-specific XML: " << xmlrepr);
00257           setProperty(CategorySpecificXmlConfig, xmlrepr);
00258         } else {
00259           _set_xml_parsing_error(xmlfname, QString::fromLatin1("Unexpected element: %1").arg(e.nodeName()));
00260           return;
00261         }
00262       }
00263     } // for all elements
00264 
00265     klfDbg("All properties read: \n" << qPrintable(toString()));
00266   } // read_script_info()
00267 
00268 
00269   static QMap<QString,KLFRefPtr<Private> > userScriptInfoCache;
00270   
00271 private:
00272   /* no copy constructor */
00273   Private(const Private& /*other*/) : KLFPropertizedObject("KLFUserScriptInfo") { }
00274 };
00275 
00276 
00277 // static
00278 QMap<QString,KLFRefPtr<KLFUserScriptInfo::Private> > KLFUserScriptInfo::Private::userScriptInfoCache;
00279 
00280 static QString normalizedFn(const QString& userScriptFileName)
00281 {
00282   return QFileInfo(userScriptFileName).canonicalFilePath();
00283 }
00284 
00285 // static
00286 KLFUserScriptInfo KLFUserScriptInfo::forceReloadScriptInfo(const QString& userScriptFileName)
00287 {
00288   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00289 
00290   QString normalizedfn = normalizedFn(userScriptFileName);
00291   Private::userScriptInfoCache.remove(normalizedfn);
00292 
00293   KLFUserScriptInfo usinfo(userScriptFileName) ;
00294   if (usinfo.scriptInfoError() != KLFERR_NOERROR) {
00295     klfWarning(qPrintable(usinfo.scriptInfoErrorString()));
00296   }
00297 
00298   return usinfo;
00299 }
00300 // static
00301 void KLFUserScriptInfo::clearCacheAll()
00302 {
00303   // will decrease the refcounts if needed automatically (KLFRefPtr)
00304   Private::userScriptInfoCache.clear();
00305 }
00306 
00307 
00308 // static
00309 bool KLFUserScriptInfo::hasScriptInfoInCache(const QString& userScriptFileName)
00310 {
00311   QString normalizedfn = normalizedFn(userScriptFileName);
00312   klfDbg("userScriptFileName = " << userScriptFileName << "; normalizedfn = " << normalizedfn) ;
00313   klfDbg("cache: " << Private::userScriptInfoCache) ;
00314   return Private::userScriptInfoCache.contains(normalizedfn);
00315 }
00316 
00317 KLFUserScriptInfo::KLFUserScriptInfo(const QString& userScriptFileName)
00318 {
00319   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00320 
00321   QFileInfo fi(userScriptFileName);
00322   QString normalizedfn = fi.canonicalFilePath();
00323   if (Private::userScriptInfoCache.contains(normalizedfn)) {
00324     d = Private::userScriptInfoCache[normalizedfn];
00325   } else {
00326     d = new KLFUserScriptInfo::Private;
00327 
00328     d()->uspath = normalizedfn;//userScriptFileName;
00329     d()->normalizedfname = normalizedfn;
00330     d()->sname = fi.fileName();
00331     d()->basename = fi.baseName();
00332 
00333     d()->read_script_info();
00334 
00335     if (d()->scriptInfoError == KLFERR_NOERROR) {
00336       Private::userScriptInfoCache[normalizedfn] = d();
00337     }
00338   }
00339 }
00340 
00341 KLFUserScriptInfo::KLFUserScriptInfo(const KLFUserScriptInfo& copy)
00342   : KLFAbstractPropertizedObject(copy)
00343 {
00344   // will increase the refcount (thanks to KLFRefPtr)
00345   d = copy.d;
00346 }
00347 
00348 KLFUserScriptInfo::~KLFUserScriptInfo()
00349 {
00350   d.setNull(); // will delete the data if refcount reaches zero (see KLFRefPtr)
00351 }
00352 
00353 QString KLFUserScriptInfo::userScriptPath() const
00354 {
00355   return d()->uspath;
00356 }
00357 QString KLFUserScriptInfo::userScriptName() const
00358 {
00359   return d()->sname;
00360 }
00361 QString KLFUserScriptInfo::userScriptBaseName() const
00362 {
00363   return d()->basename;
00364 }
00365 
00366 int KLFUserScriptInfo::scriptInfoError() const
00367 {
00368   return d()->scriptInfoError;
00369 }
00370 QString KLFUserScriptInfo::scriptInfoErrorString() const
00371 {
00372   return d()->scriptInfoErrorString;
00373 }
00374 
00375 //protected
00376 void KLFUserScriptInfo::setScriptInfoError(int code, const QString & msg)
00377 {
00378   d()->scriptInfoError = code;
00379   d()->scriptInfoErrorString = msg;
00380 }
00381 
00382 QString KLFUserScriptInfo::relativeFile(const QString& fname) const
00383 {
00384   return QDir::toNativeSeparators(userScriptPath()+"/"+fname);
00385 }
00386 
00387 QString KLFUserScriptInfo::exeScript() const { return scriptInfo(ExeScript).toString(); }
00388 QString KLFUserScriptInfo::exeScriptFullPath() const
00389 {
00390   return relativeFile(exeScript());
00391 }
00392 
00393 QString KLFUserScriptInfo::category() const { return scriptInfo(Category).toString(); }
00394 QString KLFUserScriptInfo::name() const { return scriptInfo(Name).toString(); }
00395 QString KLFUserScriptInfo::author() const { return scriptInfo(Author).toStringList().join("; "); }
00396 QStringList KLFUserScriptInfo::authorList() const { return scriptInfo(Author).toStringList(); }
00397 QString KLFUserScriptInfo::version() const { return scriptInfo(Version).toString(); }
00398 QString KLFUserScriptInfo::license() const { return scriptInfo(License).toString(); }
00399 QString KLFUserScriptInfo::klfMinVersion() const { return scriptInfo(KLFMinVersion).toString(); }
00400 QString KLFUserScriptInfo::klfMaxVersion() const { return scriptInfo(KLFMaxVersion).toString(); }
00401 QString KLFUserScriptInfo::settingsFormUI() const { return scriptInfo(SettingsFormUI).toString(); }
00402 
00403 bool KLFUserScriptInfo::canProvideDefaultSettings() const { return scriptInfo(CanProvideDefaultSettings).toBool(); }
00404 
00405 QMap<QString,QVariant> KLFUserScriptInfo::queryDefaultSettings(const KLFBackend::klfSettings * settings) const
00406 {
00407   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00408 
00409   KLFUserScriptFilterProcess proc(userScriptPath(), settings);
00410 
00411   // since this may be called on start-up, processing app events may lead to process
00412   // hanging on mac os x (why???)
00413   proc.setProcessAppEvents(false);
00414 
00415   proc.addArgv(QStringList() << QLatin1String("--query-default-settings"));
00416 
00417   // buffers to collect output
00418   QByteArray stdoutdata;
00419   QByteArray stderrdata;
00420   proc.collectStdoutTo(&stdoutdata);
00421   proc.collectStderrTo(&stderrdata);
00422 
00423   bool ok = proc.run();
00424   if (!ok) {
00425     klfWarning("Error querying default config for user script "<<userScriptBaseName()<<": "
00426                << qPrintable(proc.resultErrorString())) ;
00427     return QMap<QString,QVariant>();
00428   }
00429 
00430   klfDbg("stdoutdata = " << stdoutdata) ;
00431   klfDbg("stderrdata = " << stderrdata) ;
00432 
00433   klfDbg("Ran script "<<userScriptPath()<<": stdout="<<stdoutdata<<"\n\tstderr="<<stderrdata) ;
00434 
00435 
00436   // the output may be one of two formats:
00437   //  - XML compatible with klf{Save|Load}VariantMap{To|From}XML()
00438   //  - simple key=value pairs on separate lines
00439   // If the output starts with <?xml then we go for XML, otherwise we try to parse
00440   // key=value pairs.
00441 
00442   QByteArray trimmedstdoutdata = stdoutdata.trimmed();
00443   if (trimmedstdoutdata.startsWith("<?xml")) {
00444     QDomDocument doc("klfuserscript-default-settings");
00445     QString errMsg; int errLine, errCol;
00446     bool r = doc.setContent(trimmedstdoutdata, false, &errMsg, &errLine, &errCol);
00447     if (!r) {
00448       klfWarning("XML parse error: "<<qPrintable(errMsg)
00449                  <<" ("<<qPrintable(userScriptBaseName())<<" default-settings, line "
00450                  <<errLine<<" col "<<errCol<<")") ;
00451       return QVariantMap();
00452     }
00453 
00454     QDomElement root = doc.documentElement();
00455     if (root.nodeName() != "klfuserscript-default-settings") {
00456       klfWarning("expected <klfuserscript-default-settings> as root document element");
00457       return QVariantMap();
00458     }
00459     
00460     QVariantMap config = klfLoadVariantMapFromXML(root);
00461     return config;
00462   }
00463 
00464   // otherwise, parse key=value pairs
00465 
00466   // get variables
00467   QMap<QString,QVariant> config;
00468   foreach (QByteArray line, trimmedstdoutdata.split('\n')) {
00469     if (!line.size()) {
00470       continue;
00471     }
00472     int idxeq = line.indexOf('=');
00473     if (idxeq == -1) {
00474       klfWarning("Invalid line in reported userscript default config: " << line) ;
00475       continue;
00476     }
00477     config[QString::fromUtf8(line.left(idxeq)).trimmed()] = line.mid(idxeq+1).trimmed();
00478   }
00479 
00480   return config;
00481 }
00482 
00483 
00484 
00485 QByteArray KLFUserScriptInfo::categorySpecificXmlConfig() const
00486 {
00487   return scriptInfo(CategorySpecificXmlConfig).toByteArray();
00488 }
00489 
00490 
00491 bool KLFUserScriptInfo::hasNotices() const
00492 {
00493   return d->notices.size();
00494 }
00495 bool KLFUserScriptInfo::hasWarnings() const
00496 {
00497   return d->warnings.size();
00498 }
00499 bool KLFUserScriptInfo::hasErrors() const
00500 {
00501   return d->errors.size();
00502 }
00503 
00504 KLF_DEFINE_PROPERTY_GET(KLFUserScriptInfo, QStringList, notices) ;
00505 
00506 KLF_DEFINE_PROPERTY_GET(KLFUserScriptInfo, QStringList, warnings) ;
00507 
00508 KLF_DEFINE_PROPERTY_GET(KLFUserScriptInfo, QStringList, errors) ;
00509 
00510 
00511 
00512 QVariant KLFUserScriptInfo::scriptInfo(int propId) const
00513 {
00514   return d()->property(propId);
00515 }
00516 
00517 QVariant KLFUserScriptInfo::scriptInfo(const QString& field) const
00518 {
00519   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00520   QString x = field;
00521 
00522   if (x == QLatin1String("Authors")) {
00523     x = QLatin1String("Author");
00524   }
00525 
00526   klfDbg("x="<<x) ;
00527   int id = d()->propertyIdForName(x);
00528   if (id < 0) {
00529     klfDbg("KLFUserScriptInfo for "<<userScriptName()<<" does not have any information about "
00530            <<field<<" ("<<x<<")") ;
00531     return QVariant();
00532   }
00533   return scriptInfo(id);
00534 }
00535 
00536 QStringList KLFUserScriptInfo::scriptInfosList() const
00537 {
00538   return d()->propertyNameList();
00539 }
00540 
00541 QString KLFUserScriptInfo::objectKind() const { return d()->objectKind(); }
00542 
00543 
00544 // protected. Used by eg. KLFExportTypeUserScriptInfo to normalize list property values.
00545 void KLFUserScriptInfo::internalSetProperty(const QString& key, const QVariant &val)
00546 {
00547   d()->setProperty(key, val);
00548 }
00549 
00550 const KLFPropertizedObject * KLFUserScriptInfo::pobj()
00551 {
00552   return d();
00553 }
00554 
00555 
00556 static QString escapeListIntoTags(const QStringList& list, const QString& starttag, const QString& endtag)
00557 {
00558   QString html;
00559   foreach (QString s, list) {
00560     html += starttag + s.toHtmlEscaped() + endtag;
00561   }
00562   return html;
00563 }
00564 
00565 QString KLFUserScriptInfo::htmlInfo(const QString& extra_css) const
00566 {
00567   QString txt =
00568     "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
00569     "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
00570     "p, li { white-space: pre-wrap; }\n"
00571     "p.msgnotice { color: blue; font-weight: bold; margin: 2px 0px; }\n"
00572     "p.msgwarning { color: #a06000; font-weight: bold; margin: 2px 0px; }\n"
00573     "p.msgerror { color: #a00000; font-weight: bold; margin: 2px 0px; }\n"
00574     ".scriptinfokey { }\n"
00575     ".scriptinfovalue { font-weight: bold; }\n"
00576     + extra_css + "\n"
00577     "</style></head>\n"
00578     "<body>\n";
00579 
00580   // any notices/warnings/errors go first
00581   if (hasNotices()) {
00582     txt += escapeListIntoTags(notices(), "<p class=\"msgnotice\">", "</p>\n");
00583   }
00584   if (hasWarnings()) {
00585     txt += escapeListIntoTags(warnings(), "<p class=\"msgwarning\">", "</p>\n");
00586   }
00587   if (hasErrors()) {
00588     txt += escapeListIntoTags(errors(), "<p class=\"msgerror\">", "</p>\n");
00589   }
00590 
00591   // the user script name (incl ".klfuserscript")
00592   txt +=
00593     "<p style=\"-qt-block-indent: 0; text-indent: 0px; margin-top: 8px; margin-bottom: 0px\">\n"
00594     "<span class=\"scriptinfokey\">" + QObject::tr("Script Name:", "[[user script info text]]")
00595     + "</span>&nbsp;&nbsp;"
00596     "<span class=\"scriptinfovalue\">" + userScriptName().toHtmlEscaped() + "</span><br />\n";
00597 
00598   // the category
00599   txt += "<span class=\"scriptinfokey\">" + QObject::tr("Category:", "[[user script info text]]")
00600     + "</span>&nbsp;&nbsp;"
00601     "<span class=\"scriptinfovalue\">" + category().toHtmlEscaped() + "</span><br />\n";
00602 
00603   if (!version().isEmpty()) {
00604     // the version
00605     txt += "<span class=\"scriptinfokey\">" + QObject::tr("Version:", "[[user script info text]]")
00606       + "</span>&nbsp;&nbsp;"
00607       "<span class=\"scriptinfovalue\">" + version().toHtmlEscaped() + "</span><br />\n";
00608   }
00609   if (!author().isEmpty()) {
00610     // the author
00611     txt += "<span class=\"scriptinfokey\">" + QObject::tr("Author:", "[[user script info text]]")
00612       + "</span>&nbsp;&nbsp;"
00613       "<span class=\"scriptinfovalue\">" + author().toHtmlEscaped() + "</span><br />\n";
00614   }
00615 
00616   if (!license().isEmpty()) {
00617     // the license
00618     txt += "<span class=\"scriptinfokey\">" + QObject::tr("License:", "[[user script info text]]")
00619       + "</span>&nbsp;&nbsp;"
00620       "<span class=\"scriptinfovalue\">" + license().toHtmlEscaped() + "</span><br />\n";
00621   }
00622 
00623   return txt;
00624 }
00625 
00626 
00627 
00628 // static
00629 QMap<QString,QString> KLFUserScriptInfo::usConfigToStrMap(const QVariantMap& usconfig)
00630 {
00631   QMap<QString,QString> mdata;
00632   for (QVariantMap::const_iterator it = usconfig.begin(); it != usconfig.end(); ++it)
00633     mdata[QLatin1String("KLF_USCONFIG_") + it.key()] = klfSaveVariantToText(it.value(), true);
00634   return mdata;
00635 }
00636 
00637 // static
00638 QStringList KLFUserScriptInfo::usConfigToEnvList(const QVariantMap& usconfig)
00639 {
00640   return klfMapToEnvironmentList(KLFUserScriptInfo::usConfigToStrMap(usconfig));
00641 }
00642 
00643 
00644 inline QStringList space_sep_values(const QString& val)
00645 {
00646   return val.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00647 }
00648 
00649 
00650 
00651 
00652 struct KLFBackendEngineUserScriptInfoPrivate : public KLFPropertizedObject
00653 {
00654   KLF_PRIVATE_INHERIT_HEAD(KLFBackendEngineUserScriptInfo,
00655                            : KLFPropertizedObject("KLFBackendEngineUserScriptInfo"))
00656   {
00657     registerBuiltInProperty(KLFBackendEngineUserScriptInfo::SpitsOut, QLatin1String("SpitsOut"));
00658     registerBuiltInProperty(KLFBackendEngineUserScriptInfo::SkipFormats, QLatin1String("SkipFormats"));
00659     registerBuiltInProperty(KLFBackendEngineUserScriptInfo::DisableInputs, QLatin1String("DisableInputs"));
00660     registerBuiltInProperty(KLFBackendEngineUserScriptInfo::InputFormUI, QLatin1String("InputFormUI"));
00661   }
00662   void clear()
00663   {
00664     // clear all properties
00665     QList<int> idlist = registeredPropertyIdList();
00666     for (int k = 0; k < idlist.size(); ++k) {
00667       setProperty(idlist[k], QVariant());
00668     }
00669   }
00670 
00671   void _set_xml_parsing_error(const QString& errmsg)
00672   {
00673     K->setScriptInfoError(1001, QString("Error parsing klf-backend-engine XML config: %1: %2")
00674                           .arg(K->userScriptBaseName()).arg(errmsg));
00675   }
00676   void parse_category_config(const QByteArray & ba)
00677   {
00678     QDomDocument doc("klf-backend-engine");
00679     QString errMsg; int errLine, errCol;
00680     bool r = doc.setContent(ba, false, &errMsg, &errLine, &errCol);
00681     if (!r) {
00682       K->setScriptInfoError(
00683           1001,
00684           QString("XML parse error: %1 (klf-backend-engine in %2, relative line %3 col %4)")
00685           .arg(errMsg).arg(K->userScriptBaseName()).arg(errLine).arg(errCol));
00686       return;
00687     }
00688 
00689     QDomElement root = doc.documentElement();
00690     if (root.nodeName() != "klf-backend-engine") {
00691       _set_xml_parsing_error(QString("expected <klf-backend-engine> element"));
00692       return;
00693     }
00694     
00695     // clear all properties
00696     clear();
00697 
00698     // read XML contents
00699     QDomNode n;
00700     for (n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
00701       // try to convert the node to an element; ignore non-elements
00702       if ( n.nodeType() != QDomNode::ElementNode ) {
00703         continue;
00704       }
00705       QDomElement e = n.toElement();
00706       if ( e.isNull() ) {
00707         continue;
00708       }
00709       // parse the elements.
00710       QString val = e.text();
00711       if (val.isEmpty()) {
00712         val = QString(); // empty value is null string
00713       }
00714       if (e.nodeName() == "spits-out") {
00715         if (!property(KLFBackendEngineUserScriptInfo::SpitsOut).toStringList().isEmpty()) {
00716           _set_xml_parsing_error(QString("duplicate <spits-out> element"));
00717           return;
00718         }
00719         setProperty(KLFBackendEngineUserScriptInfo::SpitsOut, space_sep_values(val));
00720       } else if (e.nodeName() == "skip-formats") {
00721         if (!property(KLFBackendEngineUserScriptInfo::SkipFormats).toString().isEmpty()) {
00722           _set_xml_parsing_error(QString("duplicate <skip-formats> element"));
00723           return;
00724         }
00725         QStringList lst;
00726         if (e.hasAttribute("selector")) {
00727           // all-except -> ALL_EXCEPT
00728           QString s = e.attribute("selector").toUpper();
00729           lst << space_sep_values(s.replace('-', '_'));
00730         }
00731         lst << space_sep_values(val);
00732         setProperty(KLFBackendEngineUserScriptInfo::SkipFormats, lst);
00733       } else if (e.nodeName() == "disable-inputs") {
00734         if (!property(KLFBackendEngineUserScriptInfo::DisableInputs).toStringList().isEmpty()) {
00735           _set_xml_parsing_error(QString("duplicate <disable-inputs> element"));
00736           return;
00737         }
00738         QStringList lst;
00739         if (e.hasAttribute("selector")) {
00740           // all-except -> ALL_EXCEPT
00741           QString s = e.attribute("selector").toUpper();
00742           lst << space_sep_values(s.replace('-', '_'));
00743         }
00744         lst << space_sep_values(val);
00745         setProperty(KLFBackendEngineUserScriptInfo::DisableInputs, lst);
00746       } else if (e.nodeName() == "input-form-ui") {
00747         if (!property(KLFBackendEngineUserScriptInfo::InputFormUI).toStringList().isEmpty()) {
00748           _set_xml_parsing_error(QString("duplicate <input-form-ui> element"));
00749           return;
00750         }
00751         setProperty(KLFBackendEngineUserScriptInfo::InputFormUI, val);
00752       } else {
00753         _set_xml_parsing_error(QString("Found unexpected element: %1").arg(e.nodeName()));
00754         return;
00755       }
00756     }
00757 
00758     klfDbg("Read all klfbackend-engine properties:\n" << qPrintable(toString()));
00759   }
00760 };
00761 
00762 
00763 
00764 KLFBackendEngineUserScriptInfo::KLFBackendEngineUserScriptInfo(const QString& uspath)
00765   : KLFUserScriptInfo(uspath)
00766 {
00767   KLF_INIT_PRIVATE(KLFBackendEngineUserScriptInfo) ;
00768 
00769   if (category() != "klf-backend-engine") {
00770     klfWarning("KLFBackendEngineUserScriptInfo instantiated for user script "
00771                << uspath << ", which is of category " << category()) ;
00772   } else {
00773     d->parse_category_config(categorySpecificXmlConfig());
00774   }
00775 }
00776 
00777 KLFBackendEngineUserScriptInfo::~KLFBackendEngineUserScriptInfo()
00778 {
00779   KLF_DELETE_PRIVATE ;
00780 }
00781 
00782 
00783 
00784 QStringList KLFBackendEngineUserScriptInfo::spitsOut() const
00785 {
00786   return klfBackendEngineInfo(SpitsOut).toStringList();
00787 }
00788 QStringList KLFBackendEngineUserScriptInfo::skipFormats() const
00789 {
00790   return klfBackendEngineInfo(SkipFormats).toStringList();
00791 }
00792 QStringList KLFBackendEngineUserScriptInfo::disableInputs() const
00793 {
00794   return klfBackendEngineInfo(DisableInputs).toStringList();
00795 }
00796 QString KLFBackendEngineUserScriptInfo::inputFormUI() const
00797 {
00798   return klfBackendEngineInfo(InputFormUI).toString();
00799 }
00800 
00801 
00802 QVariant KLFBackendEngineUserScriptInfo::klfBackendEngineInfo(int propId) const
00803 {
00804   return d->property(propId);
00805 }
00806 
00807 QVariant KLFBackendEngineUserScriptInfo::klfBackendEngineInfo(const QString& field) const
00808 {
00809   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00810   QString x = field;
00811 
00812   klfDbg("x="<<x) ;
00813   int id = d->propertyIdForName(x);
00814   if (id < 0) {
00815     klfDbg("KLFBackendEngineUserScriptInfo for "<<userScriptName()<<" does not have any information about "
00816            <<field<<" ("<<x<<")") ;
00817     return QVariant();
00818   }
00819   return scriptInfo(id);
00820 }
00821 
00822 QStringList KLFBackendEngineUserScriptInfo::klfBackendEngineInfosList() const
00823 {
00824   return d->propertyNameList();
00825 }
00826 
00827 
00828 
00829 
00830 
00831 
00832 
00833 
00834 
00835 
00836 // ----------------------------------------
00837 
00838 struct KLFUserScriptFilterProcessPrivate
00839 {
00840   KLF_PRIVATE_HEAD(KLFUserScriptFilterProcess)
00841   {
00842     usinfo = NULL;
00843   }
00844 
00845   KLFUserScriptInfo * usinfo;
00846 
00847   // log of user script output
00848   static QStringList log;
00849 };
00850 
00851 // static
00852 QStringList KLFUserScriptFilterProcessPrivate::log = QStringList();
00853 
00854 
00855 KLFUserScriptFilterProcess::KLFUserScriptFilterProcess(const QString& userScriptFileName,
00856                                                        const KLFBackend::klfSettings * settings)
00857   : KLFFilterProcess("User Script " + userScriptFileName, settings, QString(), true)
00858 {
00859   KLF_DEBUG_BLOCK(KLF_FUNC_NAME);
00860   klfDbg("userScriptFileName= "<<userScriptFileName) ;
00861 
00862   KLF_INIT_PRIVATE(KLFUserScriptFilterProcess) ;
00863 
00864   d->usinfo = new KLFUserScriptInfo(userScriptFileName);
00865 
00866   QString exescript = d->usinfo->exeScriptFullPath();
00867   klfDbg("exescript = " << exescript) ;
00868   setArgv(QStringList() << exescript);
00869 }
00870 
00871 
00872 KLFUserScriptFilterProcess::~KLFUserScriptFilterProcess()
00873 {
00874   delete d->usinfo;
00875   KLF_DELETE_PRIVATE ;
00876 }
00877 
00878 void KLFUserScriptFilterProcess::addUserScriptConfig(const QVariantMap& usconfig)
00879 {
00880   QStringList envlist = KLFUserScriptInfo::usConfigToEnvList(usconfig);
00881   addExecEnviron(envlist);
00882 }
00883 
00884 
00885 bool KLFUserScriptFilterProcess::do_run(const QByteArray& indata, const QMap<QString, QByteArray*> outdatalist)
00886 {
00887   bool ret = KLFFilterProcess::do_run(indata, outdatalist);
00888 
00889   // for user script debugging
00890   QString thislog = QString::fromLatin1("<h1 class=\"userscript-run\">")
00891     + QObject::tr("Output from %1", "KLFUserScriptFilterProcess").arg(QLatin1String("<span class=\"userscriptname\">")
00892                                                                       +d->usinfo->userScriptBaseName().toHtmlEscaped()
00893                                                                       +QLatin1String("</span>")) +
00894     QLatin1String("</h1>\n") +
00895     QLatin1String("<p class=\"userscript-run-datetime\">") +
00896     QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate).toHtmlEscaped()
00897     + QLatin1String("</p>") ;
00898   
00899   // error message, if any
00900   QString errstr = resultErrorString();
00901   if (errstr.size()) {
00902     thislog += QString::fromLatin1("<div class=\"userscript-error\">%1</div>").arg(errstr); // errstr is already HTML
00903   }
00904 
00905   QString templ = QString::fromLatin1("<p><span class=\"output-type\">%1</span>\n"
00906                                       "<pre class=\"output\">%2</pre></p>\n") ;
00907 
00908   QByteArray bstdout = collectedStdout();
00909   if (bstdout.size()) {
00910     thislog += templ.arg("STDOUT").arg(QString::fromLocal8Bit(bstdout).toHtmlEscaped());
00911   }
00912   QByteArray bstderr = collectedStderr();
00913   if (bstderr.size()) {
00914     thislog += templ.arg("STDERR").arg(QString::fromLocal8Bit(bstderr).toHtmlEscaped());
00915   }
00916 
00917   // start discarding old logs after 255 entries
00918   if (KLFUserScriptFilterProcessPrivate::log.size() > 255) {
00919     KLFUserScriptFilterProcessPrivate::log.erase(KLFUserScriptFilterProcessPrivate::log.begin());
00920   }
00921 
00922   KLFUserScriptFilterProcessPrivate::log << thislog;
00923 
00924   return ret;
00925 }
00926 
00927 
00928 QString KLFUserScriptFilterProcess::getUserScriptLogHtml(bool include_head)
00929 {
00930   QString loghtml;
00931   QStringList::const_iterator it = KLFUserScriptFilterProcessPrivate::log.cend();
00932   while (it != KLFUserScriptFilterProcessPrivate::log.cbegin()) {
00933     --it;
00934     loghtml += *it;
00935   }
00936   if (!include_head) {
00937     return loghtml;
00938   }
00939   return QLatin1String("<html><head>"
00940                        "<meta charset=\"utf-8\">"
00941                        "<title>User Script Log</title>"
00942                        "<style type=\"text/css\">"
00943                        ".userscript-run { font-weight: bold; font-size: 2em; } "
00944                        ".userscriptname { font: monospace; } "
00945                        ".output-type { font-weight: bold; } "
00946                        "</style>"
00947                        "</head>"
00948                        "<body>") + loghtml + QLatin1String("</body></html>") ;
00949 }

Generated by doxygen 1.7.6.1. The KLatexFormula website is hosted on sourceforge.net