File indexing completed on 2024-05-19 05:05:34
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2022 Thomas Fischer <fischer@unix-ag.uni-kl.de> 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, see <https://www.gnu.org/licenses/>. * 0018 ***************************************************************************/ 0019 0020 #include "fileexportertoolchain.h" 0021 0022 #include <QCoreApplication> 0023 #include <QStringList> 0024 #include <QFile> 0025 #include <QDir> 0026 #include <QHash> 0027 #include <QTextStream> 0028 #include <QProcess> 0029 #include <QProcessEnvironment> 0030 #include <QVector> 0031 0032 #ifdef HAVE_KFI18N 0033 #include <KLocalizedString> 0034 #else // HAVE_KFI18N 0035 #include <QObject> 0036 #define i18n(text) QObject::tr(text) 0037 #endif // HAVE_KFI18N 0038 0039 #include "logging_io.h" 0040 0041 FileExporterToolchain::FileExporterToolchain(QObject *parent) 0042 : FileExporter(parent) 0043 { 0044 tempDir.setAutoRemove(true); 0045 } 0046 0047 bool FileExporterToolchain::runProcesses(const QStringList &progs, bool doEmitProcessOutput) 0048 { 0049 bool result = true; 0050 int i = 0; 0051 0052 Q_EMIT progress(0, progs.size()); 0053 for (QStringList::ConstIterator it = progs.constBegin(); result && it != progs.constEnd(); ++it) { 0054 QCoreApplication::instance()->processEvents(); 0055 QStringList args = (*it).split(QStringLiteral(" ")); 0056 QString cmd = args.first(); 0057 args.erase(args.begin()); 0058 result &= runProcess(cmd, args, doEmitProcessOutput); 0059 Q_EMIT progress(i++, progs.size()); 0060 } 0061 QCoreApplication::instance()->processEvents(); 0062 return result; 0063 } 0064 0065 bool FileExporterToolchain::runProcess(const QString &cmd, const QStringList &args, bool doEmitProcessOutput) 0066 { 0067 QProcess process(this); 0068 QProcessEnvironment processEnvironment = QProcessEnvironment::systemEnvironment(); 0069 /// Avoid some paranoid security settings in BibTeX 0070 processEnvironment.insert(QStringLiteral("openout_any"), QStringLiteral("r")); 0071 /// Make applications use working directory as temporary directory 0072 processEnvironment.insert(QStringLiteral("TMPDIR"), tempDir.path()); 0073 processEnvironment.insert(QStringLiteral("TEMPDIR"), tempDir.path()); 0074 process.setProcessEnvironment(processEnvironment); 0075 process.setWorkingDirectory(tempDir.path()); 0076 /// Assemble the full command line (program name + arguments) 0077 /// for use in log messages and debug output 0078 const QString fullCommandLine = cmd + QStringLiteral(" ") + args.join(QStringLiteral(" ")); 0079 0080 qCInfo(LOG_KBIBTEX_IO) << "Running command" << fullCommandLine << "using working directory" << process.workingDirectory(); 0081 process.start(cmd, args); 0082 0083 connect(&process, &QProcess::readyReadStandardOutput, this, [this, &doEmitProcessOutput, &process] { 0084 QTextStream ts(process.readAllStandardOutput()); 0085 while (!ts.atEnd()) { 0086 const QString line = ts.readLine(); 0087 qCWarning(LOG_KBIBTEX_IO) << line; 0088 if (doEmitProcessOutput) 0089 Q_EMIT processStandardOut(line); 0090 } 0091 }); 0092 connect(&process, &QProcess::readyReadStandardError, this, [this, &doEmitProcessOutput, &process] { 0093 QTextStream ts(process.readAllStandardError()); 0094 while (!ts.atEnd()) { 0095 const QString line = ts.readLine(); 0096 qCDebug(LOG_KBIBTEX_IO) << line; 0097 if (doEmitProcessOutput) 0098 Q_EMIT processStandardError(line); 0099 } 0100 }); 0101 0102 if (process.waitForStarted(3000)) { 0103 if (process.waitForFinished(30000)) { 0104 if (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0) 0105 qCInfo(LOG_KBIBTEX_IO) << "Command" << fullCommandLine << "succeeded"; 0106 else { 0107 qCWarning(LOG_KBIBTEX_IO) << "Command" << fullCommandLine << "failed with exit code" << process.exitCode() << ":" << process.errorString(); 0108 return false; 0109 } 0110 } else { 0111 qCWarning(LOG_KBIBTEX_IO) << "Timeout waiting for command" << fullCommandLine << "failed:" << process.errorString(); 0112 return false; 0113 } 0114 } else { 0115 qCWarning(LOG_KBIBTEX_IO) << "Starting command" << fullCommandLine << "failed:" << process.errorString(); 0116 return false; 0117 } 0118 0119 return true; 0120 } 0121 0122 bool FileExporterToolchain::writeFileToIODevice(const QString &filename, QIODevice *device) 0123 { 0124 bool result = false; 0125 QFile file(filename); 0126 if (file.open(QIODevice::ReadOnly)) { 0127 static const qint64 buffersize = 0x10000; 0128 char buffer[buffersize]; 0129 result = true; 0130 while (result) { 0131 const qint64 indata_size = file.read(buffer, buffersize); 0132 result &= indata_size >= 0; 0133 if (!result || indata_size == 0) break; //< on error or end of data 0134 0135 qint64 remainingdata_size = buffersize; 0136 qint64 outdata_size = 0; 0137 int buffer_offset = 0; 0138 while (result && outdata_size < remainingdata_size) { 0139 buffer_offset += outdata_size; 0140 remainingdata_size -= outdata_size; 0141 outdata_size = device->write(buffer + buffer_offset, remainingdata_size); 0142 result &= outdata_size >= 0; 0143 } 0144 } 0145 0146 file.close(); 0147 } 0148 0149 if (!result) 0150 qCWarning(LOG_KBIBTEX_IO) << "Writing to file" << filename << "failed"; 0151 return result; 0152 } 0153 0154 QString FileExporterToolchain::pageSizeToLaTeXName(const Preferences::PageSize pageSize) const 0155 { 0156 for (const auto &dbItem : Preferences::availablePageSizes) 0157 if (dbItem.first == pageSize) 0158 return dbItem.second; 0159 return pageSizeToLaTeXName(Preferences::defaultPageSize); 0160 } 0161 0162 bool FileExporterToolchain::kpsewhich(const QString &filename) 0163 { 0164 static QHash<QString, bool> kpsewhichMap; 0165 if (kpsewhichMap.contains(filename)) 0166 return kpsewhichMap.value(filename, false); 0167 0168 bool result = false; 0169 QProcess kpsewhich; 0170 const QStringList param {filename}; 0171 kpsewhich.start(QStringLiteral("kpsewhich"), param); 0172 if (kpsewhich.waitForStarted(3000) && kpsewhich.waitForFinished(30000)) { 0173 const QString standardOut = QString::fromUtf8(kpsewhich.readAllStandardOutput()); 0174 result = kpsewhich.exitStatus() == QProcess::NormalExit && kpsewhich.exitCode() == 0 && standardOut.endsWith(QDir::separator() + filename + QStringLiteral("\n")); 0175 kpsewhichMap.insert(filename, result); 0176 } 0177 0178 return result; 0179 }