# # # patch "guitone/src/monotone/Monotone.cpp" # from [3d0d845b9d79a048207d19a2fd60afa531dd415e] # to [a3b9439e5c0f368d32d2834b59f8e5a50ef9877e] # # patch "guitone/src/monotone/Monotone.h" # from [cd194dcafc2094075e90a5ccdb2b76b90b57fb09] # to [1439a757f7fb5008780d0a76de2dbe86ccad1154] # ============================================================ --- guitone/src/monotone/Monotone.cpp 3d0d845b9d79a048207d19a2fd60afa531dd415e +++ guitone/src/monotone/Monotone.cpp a3b9439e5c0f368d32d2834b59f8e5a50ef9877e @@ -56,7 +56,7 @@ void Monotone::setup(QDir *workingDirect // read & parse mtn's output as soon as it gets available connect( process, SIGNAL(readyReadStandardOutput()), - this, SLOT(readAndParseStdout()) + this, SLOT(readStdout()) ); // monitor if the process is exited unexpectedly @@ -113,10 +113,65 @@ void Monotone::processTerminated(int cod emit criticalError(tr("The connection to the monotone process was terminated (Code %1). Check your configuration and reload the current workspace afterwards.").arg(code)); } +bool Monotone::executeCommand(const QStringList & command) +{ + return executeCommand(command, QStringList()); +} + +bool Monotone::executeCommand(const QStringList & command, const QStringList & options) +{ + if (!writeStdin(command, options)) + { + return false; + } + + while (true) + { + if (!process->waitForReadyRead(-1)) + { + qDebug("A error occured while waiting for incoming data."); + return false; + } + + // read all data from stdout of the process and + // append it to previous output which couldn't be processed yet + input.append(process->readAllStandardOutput()); + + int retCode = 0; + + // check if we already could parse the complete stdio output + if (!parseStdio(retCode)) + { + qWarning("Monotone::readStdout: Contents incomplete/invalid."); + continue; + } + // parsing successful + return true; + } +} + +bool Monotone::triggerCommand(AutomateCommand * caller, const QStringList & command) +{ + return triggerCommand(caller, command, QStringList()); +} + +bool Monotone::triggerCommand(AutomateCommand * caller, const QStringList & command, const QStringList & options) +{ + // connect the caller with us so he knows when the command is finished + connect( + this, SIGNAL(commandFinished(AutomateCommand*)), + caller, SLOT(parseOutput(AutomateCommand*)) + ); + + lastCaller = caller; + + return writeStdin(command, options); +} + // TODO: we might want to implement a Queue for that some time so commands // are executed sequentially in the order they've come in and don't need to // wait for each other -bool Monotone::triggerCommand(AutomateCommand *caller, QStringList parts) +bool Monotone::writeStdin(const QStringList & command, const QStringList & options) { // wait until the process has been started up if it is not already // running. Notice that if waitForStartup times out after 15 seconds @@ -134,65 +189,104 @@ bool Monotone::triggerCommand(AutomateCo return false; } - isProcessingData = true; - lastCaller = caller; - cmdParts = parts; - input.clear(); + isProcessingData = true; + input.clear(); output.clear(); + commandLine = ""; - QString finalCmd; + if (options.size() > 0) + { + // currently mtn can only understand key => value option pairs + Q_ASSERT(options.size() % 2 == 0); + + commandLine += "o"; + for (int i=0, c=options.size(); i 0); + + commandLine += "l"; + for (int i=0, c=command.size(); ireadAllStandardOutput()); + int retCode = 0; + + // check if we already could parse the complete stdio output + if (!parseStdio(retCode)) + { + qWarning("Monotone::readStdout: Contents incomplete/invalid."); + return; + } + + // apparently everything has been parsed properly + isProcessingData = false; + + // transfer the data to the command for parsing + lastCaller->setData(output); + + // check if there was an error during execution + if (retCode > 0 && !lastCaller->handleError(retCode)) + { + // the error could not be handled by the caller + // so we have no other choice but try a clean exit here + + // FIXME: further actions needed here? + isCleanExit = true; + process->close(); + + // disconnect the event sink from any further signals + disconnect(lastCaller); + + // emit a critical error which closes the app later + emit criticalError( + tr("Unable to process command '%1': %2") + .arg(commandLine) + .arg(output) + ); + return; + } + + emit commandFinished(lastCaller); + + // disconnect the event sink from any further signals + disconnect(lastCaller); +} + +bool Monotone::parseStdio(int & retCode) +{ // parse out the contents of each line QRegExp regex("^(\\d+):(\\d+):([ml]):(\\d+):"); - while (true) + while (regex.indexIn(input) != -1) { - // apparently we can't parse the string, this could be because not - // enough data have been flushed or the format is somehow screwed up - if (regex.indexIn(input) == -1) - { - qWarning("Monotone::readAndParseStdout: Can't parse data header, waiting for next flush."); - return; - } - QStringList list = regex.capturedTexts(); Q_ASSERT(input.length() >= regex.matchedLength()); @@ -214,7 +308,7 @@ void Monotone::readAndParseStdout() .arg(list[3]) .arg(list[4]) ); - return; + return false; } // add the byte amount to the output string @@ -227,51 +321,22 @@ void Monotone::readAndParseStdout() if (list[3].compare("l") == 0) { Q_ASSERT(input.length() == 0); - - isProcessingData = false; - - int retCode = list[2].toInt(); - - // transfer the data to the command for parsing - lastCaller->setData(output); - - // check if there was an error during execution - if (retCode > 0 && !lastCaller->handleError(retCode)) - { - // the error could not be handled by the caller - // so we have no other choice but try a clean exit here - - // FIXME: further actions needed here? - isCleanExit = true; - process->close(); - - // disconnect the event sink from any further signals - disconnect(lastCaller); - - // emit a critical error which closes the app later - emit criticalError( - tr("Unable to process command '%1': %2") - .arg(cmdParts.join(" ")) - .arg(output) - ); - return; - } - - emit commandFinished(lastCaller); - - // disconnect the event sink from any further signals - disconnect(lastCaller); - - return; + retCode = list[2].toInt(); + // command successfully parsed + return true; } - - // if this was not the last output, but there are no contents left, + + // if this was not the last output, but there are no bytes left, // we need to wait for further data if (input.size() == 0) { - return; + return false; } } + + // apparently we couldn't parse the string, this could be because not + // enough data have been flushed or the format is somehow screwed up + return false; } bool Monotone::checkBinaryVersion(const QString & path) ============================================================ --- guitone/src/monotone/Monotone.h cd194dcafc2094075e90a5ccdb2b76b90b57fb09 +++ guitone/src/monotone/Monotone.h 1439a757f7fb5008780d0a76de2dbe86ccad1154 @@ -36,13 +36,22 @@ class Monotone : public QObject void setup(QDir*); virtual ~Monotone(); - bool triggerCommand(AutomateCommand*, QStringList); + bool triggerCommand(AutomateCommand*, const QStringList &); + bool triggerCommand(AutomateCommand*, const QStringList &, const QStringList &); + + bool executeCommand(const QStringList &); + bool executeCommand(const QStringList &, const QStringList &); + + inline QString getOutput() { return output; } private: + bool writeStdin(const QStringList &, const QStringList &); + bool parseStdio(int &); + Monotone(QObject *); QString input; QString output; - QStringList cmdParts; + QString commandLine; AutomateCommand* lastCaller; bool isProcessingData; bool isCleanExit; @@ -50,7 +59,7 @@ class Monotone : public QObject QProcess * process; private slots: - void readAndParseStdout(); + void readStdout(); void processTerminated(int, QProcess::ExitStatus); void startupError(QProcess::ProcessError);