File indexing completed on 2024-04-14 15:17:36
0001 /**************************************************************************************** 0002 Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) 0003 2008-2018 by Michel Ludwig (michel.ludwig@kdemail.net) 0004 ****************************************************************************************/ 0005 0006 /*************************************************************************** 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 #include "kilelauncher.h" 0016 0017 #include <config.h> 0018 0019 #include "kileconfig.h" 0020 #include "kileinfo.h" 0021 #include "kiletool.h" 0022 #include "kiletoolmanager.h" 0023 #include "kiletool_enums.h" 0024 #include "kileviewmanager.h" 0025 #include "livepreview.h" 0026 0027 #include <QStackedWidget> 0028 #include <QFileInfo> 0029 0030 #include "kiledebug.h" 0031 #include <KIO/DesktopExecParser> 0032 #include <KProcess> 0033 #include <KLocalizedString> 0034 #include <KShell> 0035 0036 #include <KParts/Part> 0037 #include <KParts/PartManager> 0038 0039 namespace KileTool { 0040 0041 Launcher::Launcher() : 0042 m_tool(Q_NULLPTR) 0043 { 0044 } 0045 0046 Launcher::~ Launcher() 0047 { 0048 KILE_DEBUG_MAIN << "DELETING launcher"; 0049 } 0050 0051 ProcessLauncher::ProcessLauncher() : 0052 m_changeTo(true) 0053 { 0054 KILE_DEBUG_MAIN << "==KileTool::ProcessLauncher::ProcessLauncher()=============="; 0055 0056 m_proc = new KProcess(this); 0057 0058 m_proc->setOutputChannelMode(KProcess::MergedChannels); 0059 m_proc->setReadChannel(QProcess::StandardOutput); 0060 0061 connect(m_proc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotProcessOutput())); 0062 connect(m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessExited(int,QProcess::ExitStatus))); 0063 connect(m_proc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(slotProcessError(QProcess::ProcessError))); 0064 } 0065 0066 ProcessLauncher::~ProcessLauncher() 0067 { 0068 KILE_DEBUG_MAIN << "DELETING ProcessLauncher"; 0069 0070 if(m_proc) { 0071 // we don't want it to emit any signals as we are being deleted 0072 m_proc->disconnect(); 0073 kill(false); 0074 delete m_proc; 0075 } 0076 } 0077 0078 void ProcessLauncher::setWorkingDirectory(const QString &wd) 0079 { 0080 m_wd = wd; 0081 } 0082 0083 void ProcessLauncher::changeToWorkingDirectory(bool change) 0084 { 0085 m_changeTo = change; 0086 } 0087 0088 void ProcessLauncher::setCommand(const QString& cmd) 0089 { 0090 m_cmd = cmd; 0091 } 0092 0093 void ProcessLauncher::setOptions(const QString& opt) 0094 { 0095 m_options = opt; 0096 } 0097 0098 bool ProcessLauncher::launch() 0099 { 0100 if(tool() == Q_NULLPTR) { 0101 qWarning() << "tool() is Q_NULLPTR which is a BUG"; 0102 return false; 0103 } 0104 if(m_proc == Q_NULLPTR) { 0105 qWarning() << "m_proc is Q_NULLPTR which is a BUG"; 0106 return false; 0107 } 0108 0109 QString msg; 0110 QString out = "*****\n***** " + tool()->name() + i18n(" output: \n"); 0111 0112 if(m_cmd.isEmpty()) { 0113 m_cmd = tool()->readEntry("command"); 0114 KILE_DEBUG_MAIN << "readEntry('command'): " << m_cmd; 0115 } 0116 0117 if(m_options.isEmpty()) { 0118 m_options = tool()->readEntry("options"); 0119 KILE_DEBUG_MAIN << "readEntry('option'):" << m_options; 0120 } 0121 0122 if(m_changeTo && (!m_wd.isEmpty())) { 0123 m_proc->setWorkingDirectory(m_wd); 0124 KILE_DEBUG_MAIN << "changed to " << m_wd; 0125 out += QString("***** cd \"") + m_wd + QString("\"\n"); 0126 } 0127 0128 tool()->translate(m_cmd); 0129 tool()->translate(m_options, true); // quote the substituted strings using 'KShell::quoteArg' 0130 // (see bug 314109) 0131 KILE_DEBUG_MAIN << "after translate: m_cmd=" << m_cmd << ", m_options=" << m_options; 0132 0133 if(m_cmd.isEmpty()) { 0134 return false; 0135 } 0136 0137 KShell::Errors err; 0138 QStringList arguments = KShell::splitArgs(m_options, KShell::AbortOnMeta | KShell::TildeExpand, &err); 0139 if(err == KShell::BadQuoting || err == KShell::FoundMeta) { 0140 return false; 0141 } 0142 0143 // we cannot use 'KProcess::setShellCommand' here as that method uses 'KStandardDirs::findExe' 0144 // which doesn't respect the path preferences given by the user, i.e. 'KStandardDirs::findExe' is happy 0145 // to return the first executable it finds (for example, in '/usr/bin' although the user maybe didn't 0146 // want to use that directory) 0147 // BUG: 204397 0148 m_proc->setProgram(m_cmd, arguments); 0149 0150 KILE_DEBUG_MAIN << "sent " << m_cmd << ' ' << arguments; 0151 0152 out += QString("***** ") + m_cmd + ' ' + arguments.join(" ") + '\n'; 0153 0154 QString src = tool()->source(false); 0155 QString trgt = tool()->target(); 0156 if(src == trgt) { 0157 msg = src; 0158 } 0159 else { 0160 msg = src + " => " + trgt; 0161 } 0162 0163 msg += " (" + m_cmd + ')'; 0164 0165 emit(message(Info, msg)); 0166 0167 QString teXInputPaths = tool()->teXInputPaths(); 0168 QString bibInputPaths = tool()->bibInputPaths(); 0169 QString bstInputPaths = tool()->bstInputPaths(); 0170 0171 // QuickView tools need a special TEXINPUTS environment variable 0172 if(tool()->isQuickie()) { 0173 teXInputPaths = KileConfig::previewTeXPaths(); 0174 bibInputPaths = KileConfig::previewBibInputPaths(); 0175 } 0176 0177 KILE_DEBUG_MAIN << "$PATH=" << tool()->manager()->info()->expandEnvironmentVars("$PATH"); 0178 KILE_DEBUG_MAIN << "$TEXINPUTS=" << tool()->manager()->info()->expandEnvironmentVars(teXInputPaths + LIST_SEPARATOR + "$TEXINPUTS"); 0179 KILE_DEBUG_MAIN << "$BIBINPUTS=" << tool()->manager()->info()->expandEnvironmentVars(bibInputPaths + LIST_SEPARATOR + "$BIBINPUTS"); 0180 KILE_DEBUG_MAIN << "$BSTINPUTS=" << tool()->manager()->info()->expandEnvironmentVars(bstInputPaths + LIST_SEPARATOR + "$BSTINPUTS"); 0181 KILE_DEBUG_MAIN << "Tool name is "<< tool()->name(); 0182 0183 m_proc->setEnv("PATH", tool()->manager()->info()->expandEnvironmentVars("$PATH")); 0184 0185 if(!teXInputPaths.isEmpty()) { 0186 m_proc->setEnv("TEXINPUTS", tool()->manager()->info()->expandEnvironmentVars(teXInputPaths + LIST_SEPARATOR + "$TEXINPUTS")); 0187 } 0188 if(!bibInputPaths.isEmpty()) { 0189 m_proc->setEnv("BIBINPUTS", tool()->manager()->info()->expandEnvironmentVars(bibInputPaths + LIST_SEPARATOR + "$BIBINPUTS")); 0190 } 0191 if(!bstInputPaths.isEmpty()) { 0192 m_proc->setEnv("BSTINPUTS", tool()->manager()->info()->expandEnvironmentVars(bstInputPaths + LIST_SEPARATOR + "$BSTINPUTS")); 0193 } 0194 0195 out += "*****\n"; 0196 emit(output(out)); 0197 0198 if(tool()->manager()->shouldBlock()) { 0199 KILE_DEBUG_MAIN << "About to execute: " << m_proc->program(); 0200 m_proc->execute(); 0201 } 0202 else { 0203 KILE_DEBUG_MAIN << "About to start: " << m_proc->program(); 0204 m_proc->start(); 0205 } 0206 return true; 0207 } 0208 0209 void ProcessLauncher::kill(bool emitSignals) 0210 { 0211 KILE_DEBUG_MAIN << "==KileTool::ProcessLauncher::kill()=============="; 0212 if(m_proc && m_proc->state() == QProcess::Running) { 0213 KILE_DEBUG_MAIN << "\tkilling"; 0214 m_proc->kill(); 0215 m_proc->waitForFinished(-1); 0216 } 0217 else { 0218 KILE_DEBUG_MAIN << "\tno process or process not running"; 0219 if(emitSignals) { 0220 emit(message(Error, i18n("terminated"))); 0221 emit(done(AbnormalExit)); 0222 } 0223 } 0224 } 0225 0226 // FIXME: this should be done in the 'launch()' method itself 0227 bool ProcessLauncher::selfCheck() 0228 { 0229 emit(message(Error, i18n("Launching failed, diagnostics:"))); 0230 0231 KShell::Errors err; 0232 QStringList arguments = KShell::splitArgs(m_options, KShell::AbortOnMeta | KShell::TildeExpand, &err); 0233 if(err == KShell::BadQuoting) { 0234 emit(message(Error, i18n("An error occurred while parsing the options given to the tool."))); 0235 return false; 0236 } 0237 else if(err == KShell::FoundMeta) { 0238 emit(message(Error, i18n("Shell meta characters that cannot be handled are present in the options given to the tool."))); 0239 return false; 0240 } 0241 0242 0243 QString exe = KIO::DesktopExecParser::executablePath(tool()->readEntry("command")); 0244 QString path = QStandardPaths::findExecutable(exe); 0245 0246 if(path.isEmpty()) { 0247 emit(message(Error, i18n("There is no executable named \"%1\" in your path.", exe))); 0248 return false; 0249 } 0250 else { 0251 QFileInfo fi(path); 0252 if(!fi.isExecutable()) { 0253 emit(message(Error, i18n("You do not have permission to run %1.", path))); 0254 return false; 0255 } 0256 } 0257 0258 emit(message(Info, i18n("Diagnostics could not find any obvious problems."))); 0259 return true; 0260 } 0261 0262 void ProcessLauncher::slotProcessOutput() 0263 { 0264 QByteArray buf = m_proc->readAllStandardOutput(); 0265 emit output(QString::fromLocal8Bit(buf, buf.size())); 0266 } 0267 0268 void ProcessLauncher::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) 0269 { 0270 KILE_DEBUG_MAIN << "==KileTool::ProcessLauncher::slotProcessExited============="; 0271 KILE_DEBUG_MAIN << "\t" << tool()->name(); 0272 0273 if(m_proc) { 0274 if(exitStatus == QProcess::NormalExit) { 0275 KILE_DEBUG_MAIN << "\tnormal exit"; 0276 int type = Info; 0277 if(exitCode != 0) { 0278 type = Error; 0279 emit(message(type, i18n("finished with exit code %1", exitCode))); 0280 } 0281 0282 if (type == Info) { 0283 emit(done(Success)); 0284 } 0285 else { 0286 emit(done(Failed)); 0287 } 0288 } 0289 else { 0290 KILE_DEBUG_MAIN << "\tabnormal exit"; 0291 emit(message(Error, i18n("finished abruptly"))); 0292 emit(done(AbnormalExit)); 0293 } 0294 } 0295 else { 0296 qWarning() << "\tNO PROCESS, emitting done"; 0297 emit(done(Success)); 0298 } 0299 } 0300 0301 void ProcessLauncher::slotProcessError(QProcess::ProcessError error) 0302 { 0303 KILE_DEBUG_MAIN << "error =" << error << "tool = " << tool()->name(); 0304 QString errorString; 0305 switch(error) { 0306 case QProcess::FailedToStart: 0307 errorString = i18n("failed to start"); 0308 break; 0309 case QProcess::Crashed: 0310 errorString = i18n("crashed"); 0311 break; 0312 default: 0313 errorString = i18n("failed (error code %1)", error); 0314 break; 0315 } 0316 emit(message(Error, errorString)); 0317 emit(done(AbnormalExit)); 0318 } 0319 0320 KonsoleLauncher::KonsoleLauncher() : ProcessLauncher() 0321 { 0322 } 0323 0324 bool KonsoleLauncher::launch() 0325 { 0326 QString cmd = tool()->readEntry("command"); 0327 QString noclose = (tool()->readEntry("close") == "no") ? "--noclose" : ""; 0328 setCommand("konsole"); 0329 setOptions(noclose + " -e " + cmd + ' ' + tool()->readEntry("options")); 0330 if(QStandardPaths::findExecutable(KIO::DesktopExecParser::executablePath(cmd)).isEmpty()) { 0331 return false; 0332 } 0333 0334 return ProcessLauncher::launch(); 0335 } 0336 0337 DocumentViewerLauncher::DocumentViewerLauncher() 0338 { 0339 } 0340 0341 DocumentViewerLauncher::~DocumentViewerLauncher() 0342 { 0343 KILE_DEBUG_MAIN << "DELETING DocumentViewerLauncher"; 0344 } 0345 0346 bool DocumentViewerLauncher::selfCheck() 0347 { 0348 return true; //no additional self-checks, all of them are done in launch() 0349 } 0350 0351 bool DocumentViewerLauncher::launch() 0352 { 0353 if(!tool()->manager()->viewManager()->viewerPart()) { 0354 emit(message(Error, i18n("The document viewer is not available"))); 0355 return false; 0356 } 0357 if(tool()->manager()->livePreviewManager() && tool()->manager()->livePreviewManager()->isLivePreviewActive()) { 0358 emit(message(Error, i18n("Please disable the live preview before launching this tool"))); 0359 return false; 0360 } 0361 const QString fileName = tool()->paramDict()["%dir_target"] + '/' + tool()->paramDict()["%target"]; 0362 tool()->manager()->viewManager()->openInDocumentViewer(QUrl::fromLocalFile(fileName)); 0363 if(tool()->paramDict().contains("%sourceFileName") 0364 && tool()->paramDict().contains("%sourceLine")) { 0365 const QString sourceFileName = tool()->paramDict()["%sourceFileName"]; 0366 const QString lineString = tool()->paramDict()["%sourceLine"]; 0367 tool()->manager()->viewManager()->showSourceLocationInDocumentViewer(sourceFileName, lineString.toInt(), 0); 0368 } 0369 emit(done(Success)); 0370 0371 return true; 0372 } 0373 0374 void DocumentViewerLauncher::kill(bool emitSignals) 0375 { 0376 Q_UNUSED(emitSignals); 0377 } 0378 0379 } 0380