35 # include <KPtyDevice>
36 # include <KPtyProcess>
39 #include <KStandardDirs>
43 #include <QApplication>
57 m_listEmptyLines(false)
62 if (QMetaType::type(
"QProcess::ExitStatus") == 0) {
63 qRegisterMetaType<QProcess::ExitStatus>(
"QProcess::ExitStatus");
67 void CliInterface::cacheParameterList()
84 m_listEmptyLines = emptyLines;
92 QStringList args = m_param.value(
ListArgs).toStringList();
93 substituteListVariables(args);
95 if (!runProcess(m_param.value(
ListProgram).toStringList(), args)) {
106 cacheParameterList();
111 QStringList args = m_param.value(
ExtractArgs).toStringList();
114 for (
int i = 0; i < args.size(); ++i) {
115 QString argument = args.at(i);
116 kDebug() <<
"Processing argument " << argument;
118 if (argument == QLatin1String(
"$Archive" )) {
122 if (argument == QLatin1String(
"$PreservePathSwitch" )) {
124 Q_ASSERT(replacementFlags.size() == 2);
126 bool preservePaths = options.value(QLatin1String(
"PreservePaths" )).toBool();
127 QString theReplacement;
129 theReplacement = replacementFlags.at(0);
131 theReplacement = replacementFlags.at(1);
134 if (theReplacement.isEmpty()) {
140 args[i] = theReplacement;
144 if (argument == QLatin1String(
"$PasswordSwitch" )) {
154 if ((options.value(QLatin1String(
"PasswordProtectedHint")).toBool()) &&
156 kDebug() <<
"Password hint enabled, querying user";
171 if (!pass.isEmpty()) {
172 QStringList theSwitch = m_param.value(
PasswordSwitch).toStringList();
173 for (
int j = 0; j < theSwitch.size(); ++j) {
175 QString newArg = theSwitch.at(j);
178 newArg.replace(QLatin1String(
"$Password" ), pass);
181 args.insert(i + j, newArg);
189 if (argument == QLatin1String(
"$RootNodeSwitch" )) {
198 if (options.contains(QLatin1String(
"RootNode" ))) {
199 rootNode = options.value(QLatin1String(
"RootNode" )).toString();
200 kDebug() <<
"Set root node " << rootNode;
203 if (!rootNode.isEmpty()) {
204 QStringList theSwitch = m_param.value(
RootNodeSwitch).toStringList();
205 for (
int j = 0; j < theSwitch.size(); ++j) {
207 QString newArg = theSwitch.at(j);
210 newArg.replace(QLatin1String(
"$Path" ), rootNode);
213 args.insert(i + j, newArg);
221 if (argument == QLatin1String(
"$Files" )) {
223 for (
int j = 0; j < files.count(); ++j) {
224 args.insert(i + j, escapeFileName(files.at(j).toString()));
231 kDebug() <<
"Setting current dir to " << destinationDirectory;
232 QDir::setCurrent(destinationDirectory);
234 if (!runProcess(m_param.value(
ExtractProgram).toStringList(), args)) {
244 cacheParameterList();
248 const QString globalWorkDir = options.value(QLatin1String(
"GlobalWorkDir" )).toString();
249 const QDir workDir = globalWorkDir.isEmpty() ? QDir::current() : QDir(globalWorkDir);
250 if (!globalWorkDir.isEmpty()) {
251 kDebug() <<
"GlobalWorkDir is set, changing dir to " << globalWorkDir;
252 QDir::setCurrent(globalWorkDir);
256 QStringList args = m_param.value(
AddArgs).toStringList();
259 for (
int i = 0; i < args.size(); ++i) {
260 const QString argument = args.at(i);
261 kDebug() <<
"Processing argument " << argument;
263 if (argument == QLatin1String(
"$Archive" )) {
267 if (argument == QLatin1String(
"$Files" )) {
269 for (
int j = 0; j < files.count(); ++j) {
274 const QString relativeName =
275 workDir.relativeFilePath(files.at(j));
277 args.insert(i + j, relativeName);
284 if (!runProcess(m_param.value(
AddProgram).toStringList(), args)) {
294 cacheParameterList();
298 QStringList args = m_param.value(
DeleteArgs).toStringList();
301 for (
int i = 0; i < args.size(); ++i) {
302 QString argument = args.at(i);
303 kDebug() <<
"Processing argument " << argument;
305 if (argument == QLatin1String(
"$Archive" )) {
307 }
else if (argument == QLatin1String(
"$Files" )) {
309 for (
int j = 0; j < files.count(); ++j) {
310 args.insert(i + j, escapeFileName(files.at(j).toString()));
317 m_removedFiles = files;
319 if (!runProcess(m_param.value(
DeleteProgram).toStringList(), args)) {
327 bool CliInterface::runProcess(
const QStringList& programNames,
const QStringList& arguments)
330 for (
int i = 0; i < programNames.count(); i++) {
331 programPath = KStandardDirs::findExe(programNames.at(i));
332 if (!programPath.isEmpty())
335 if (programPath.isEmpty()) {
336 const QString names = programNames.join(QLatin1String(
", "));
337 emit
error(i18ncp(
"@info",
"Failed to locate program <filename>%2</filename> on disk.",
338 "Failed to locate programs <filename>%2</filename> on disk.", programNames.count(), names));
343 kDebug() <<
"Executing" << programPath << arguments;
346 m_process->waitForFinished();
351 m_process =
new KProcess;
353 m_process =
new KPtyProcess;
354 m_process->setPtyChannels(KPtyProcess::StdinChannel);
357 m_process->setOutputChannelMode(KProcess::MergedChannels);
358 m_process->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered | QIODevice::Text);
359 m_process->setProgram(programPath, arguments);
361 connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(readStdout()), Qt::DirectConnection);
362 connect(m_process, SIGNAL(
finished(
int,QProcess::ExitStatus)), SLOT(processFinished(
int,QProcess::ExitStatus)), Qt::DirectConnection);
364 m_stdOutData.clear();
369 bool ret = m_process->waitForFinished(-1);
372 bool ret = (loop.exec(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents) == 0);
375 Q_ASSERT(!m_process);
380 void CliInterface::processFinished(
int exitCode, QProcess::ExitStatus exitStatus)
382 kDebug() << exitCode << exitStatus;
391 foreach(
const QVariant& v, m_removedFiles) {
413 void CliInterface::failOperation()
420 void CliInterface::readStdout(
bool handleAll)
432 if (!m_process->bytesAvailable()) {
440 Q_ASSERT(QThread::currentThread() != QApplication::instance()->thread());
442 QByteArray dd = m_process->readAllStandardOutput();
445 QList<QByteArray> lines = m_stdOutData.split(
'\n');
456 bool foundErrorMessage =
459 checkForPasswordPromptMessage(QLatin1String(lines.last())) ||
460 checkForFileExistsMessage(QLatin1String( lines.last() )));
462 if (foundErrorMessage) {
471 if (lines.size() == 1 && !handleAll) {
476 m_stdOutData.clear();
481 m_stdOutData = lines.takeLast();
484 foreach(
const QByteArray& line, lines) {
486 handleLine(QString::fromLocal8Bit(line));
491 void CliInterface::handleLine(
const QString& line)
497 int pos = line.indexOf(QLatin1Char(
'%' ));
498 if (pos != -1 && pos > 1) {
499 int percentage = line.mid(pos - 2, 2).toInt();
500 emit
progress(
float(percentage) / 100);
506 if (checkForPasswordPromptMessage(line)) {
507 kDebug() <<
"Found a password prompt";
511 query.waitForResponse();
513 if (query.responseCancelled()) {
520 const QString response(
password() + QLatin1Char(
'\n'));
521 writeToProcess(response.toLocal8Bit());
527 kDebug() <<
"Wrong password!";
528 emit
error(i18n(
"Incorrect password."));
534 kDebug() <<
"Error in extraction!!";
535 emit
error(i18n(
"Extraction failed because of an unexpected error."));
540 if (handleFileExistsMessage(line)) {
546 if (checkForPasswordPromptMessage(line)) {
547 kDebug() <<
"Found a password prompt";
551 query.waitForResponse();
553 if (query.responseCancelled()) {
560 const QString response(
password() + QLatin1Char(
'\n'));
561 writeToProcess(response.toLocal8Bit());
567 kDebug() <<
"Wrong password!";
568 emit
error(i18n(
"Incorrect password."));
574 kDebug() <<
"Error in extraction!!";
575 emit
error(i18n(
"Extraction failed because of an unexpected error."));
580 if (handleFileExistsMessage(line)) {
589 bool CliInterface::checkForPasswordPromptMessage(
const QString& line)
593 if (passwordPromptPattern.isEmpty())
596 if (m_passwordPromptPattern.isEmpty()) {
600 if (m_passwordPromptPattern.indexIn(line) != -1) {
607 bool CliInterface::checkForFileExistsMessage(
const QString& line)
609 if (m_existsPattern.isEmpty()) {
612 if (m_existsPattern.indexIn(line) != -1) {
613 kDebug() <<
"Detected file existing!! Filename " << m_existsPattern.cap(1);
620 bool CliInterface::handleFileExistsMessage(
const QString& line)
622 if (!checkForFileExistsMessage(line)) {
626 const QString
filename = m_existsPattern.cap(1);
629 query.setNoRenameMode(
true);
631 kDebug() <<
"Waiting response";
632 query.waitForResponse();
634 kDebug() <<
"Finished response";
636 QString responseToProcess;
637 const QStringList choices = m_param.value(
FileExistsInput).toStringList();
639 if (query.responseOverwrite()) {
640 responseToProcess = choices.at(0);
641 }
else if (query.responseSkip()) {
642 responseToProcess = choices.at(1);
643 }
else if (query.responseOverwriteAll()) {
644 responseToProcess = choices.at(2);
645 }
else if (query.responseAutoSkip()) {
646 responseToProcess = choices.at(3);
647 }
else if (query.responseCancelled()) {
648 if (choices.count() < 5) {
651 responseToProcess = choices.at(4);
654 Q_ASSERT(!responseToProcess.isEmpty());
656 responseToProcess += QLatin1Char(
'\n' );
658 writeToProcess(responseToProcess.toLocal8Bit());
663 bool CliInterface::checkForErrorMessage(
const QString& line,
int parameterIndex)
665 QList<QRegExp> patterns;
667 if (m_patternCache.contains(parameterIndex)) {
668 patterns = m_patternCache.value(parameterIndex);
670 if (!m_param.contains(parameterIndex)) {
674 foreach(
const QString& rawPattern, m_param.value(parameterIndex).toStringList()) {
675 patterns << QRegExp(rawPattern);
677 m_patternCache[parameterIndex] = patterns;
680 foreach(
const QRegExp& pattern, patterns) {
681 if (pattern.indexIn(line) != -1) {
692 if (!m_process->waitForFinished(5)) {
712 void CliInterface::substituteListVariables(QStringList& params)
714 for (
int i = 0; i < params.size(); ++i) {
715 const QString parameter = params.at(i);
717 if (parameter == QLatin1String(
"$Archive" )) {
723 QString CliInterface::escapeFileName(
const QString& fileName)
const
728 void CliInterface::writeToProcess(
const QByteArray& data)
731 Q_ASSERT(!data.isNull());
733 kDebug() <<
"Writing" << data <<
"to the process";
736 m_process->write(data);
738 m_process->pty()->write(data);
744 #include "cliinterface.moc"
void setWaitForFinishedSignal(bool value)
Setting this option to true will not exit the thread with the exit of the various functions...
QStringList Default: empty A list of regexp patterns that will cause the extraction to exit with a ge...
QStringList The names to the program that will handle extracting of this archive (eg "rar")...
CliInterface(QObject *parent, const QVariantList &args)
void entryRemoved(const QString &path)
QStringList The names to the program that will handle adding in this archive format (eg "rar")...
QStringList The arguments that are passed to the program above for listing the archive.
void userQuery(Query *query)
QString Default: empty A regexp pattern that matches the program's password prompt.
QString filename() const
Returns the filename of the archive currently being handled.
virtual ParameterList parameterList() const =0
void waitForResponse()
Will block until the response have been set.
QStringList This should be a qstringlist with either two elements.
virtual bool readListLine(const QString &line)=0
void setPassword(const QString &password)
QStringList Default: empty A list of regexp patterns that will alert the user that the password was w...
virtual bool copyFiles(const QList< QVariant > &files, const QString &destinationDirectory, ExtractionOptions options)
Extract files from archive.
QStringList The arguments that are passed to the program above for extracting the archive...
QString This is a regexp, defining how to recognize a "File already exists" prompt when extracting...
void finished(bool result)
QHash< QString, QVariant > CompressionOptions
These are the extra options for doing the compression.
Bool (default false) Will look for the %-sign in the stdout while working, in the form of (2%...
QStringList The arguments that are passed to the program above for deleting from the archive...
QStringList (default empty) The format of the root node switch.
QStringList The names to the program that will handle deleting of elements in this archive format (eg...
QHash< QString, QVariant > ExtractionOptions
void setListEmptyLines(bool emptyLines)
Sets if the listing should include empty lines.
QStringList The various responses that can be supplied as a response to the "file exists" prompt...
void error(const QString &message, const QString &details=QString())
OperationMode m_operationMode
QStringList The arguments that are passed to the program above for adding to the archive.
void progress(double progress)
virtual bool list()
List archive contents.
virtual bool addFiles(const QStringList &files, const CompressionOptions &options)
QStringList (default empty) The format of the root node switch.
virtual bool deleteFiles(const QList< QVariant > &files)
QStringList The names to the program that will handle listing of this archive (eg "rar")...