# # add_dir "" # # add_file ".mtn-ignore" # content [3ce0f58af08773143db89e255d88a112eb0b8a25] # # add_file "AbstractParser.cpp" # content [07d457cc3931cd2f4f384b8313f50555f2048692] # # add_file "AbstractParser.h" # content [5d83ade859fbf057e5326f2bdcf8b0f6560a5be2] # # add_file "MonotoneThread.cpp" # content [4f8b7ce09075fb1f46e046f7fe4bea66d489a1eb] # # add_file "MonotoneThread.h" # content [b8475556bd836dc28167ac42a37a10a6c87aef80] # # add_file "StdioParser.cpp" # content [f187157c2f8946f58676798fe8d3f52ed70e5d49] # # add_file "StdioParser.h" # content [ed0742f484843b709c946a2c030cca934432335d] # # add_file "main.cpp" # content [cc269121a88bcdcd1428b462ab24913193c524aa] # # add_file "test.cpp" # content [581c6d829be0253d7383a62eecf94effdb74b08a] # # add_file "test.h" # content [076086d24821d8abca53acb9e312a15e6b201727] # # add_file "test.pro" # content [e83cf036f401d2119b3b3c6faafc4f7928770002] # # add_file "test.ui" # content [b2a46ced3e70c2fa341ae1e2e52ab100f9572c5d] --- .mtn-ignore +++ .mtn-ignore @@ -0,0 +1,4 @@ +^moc_ +^ui_ +\.app$ +Makefile --- AbstractParser.cpp +++ AbstractParser.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +* Copyright (C) 2007 by Thomas Keller * +* address@hidden * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include "AbstractParser.h" + +AbstractParser::AbstractParser(const QString & in) : input(in.toUtf8()) {} + +AbstractParser::AbstractParser(const QByteArray & in) : input(in) {} + +AbstractParser::~AbstractParser() {} + +void AbstractParser::eatSpaces() +{ + while (whatsNext() == ' ') { advance(); } +} + +char AbstractParser::whatsNext(int count /* = 0 */) +{ + if (count > (input.size() - 1)) return '\0'; + return input.at(count); +} + +bool AbstractParser::advance(int count /* = 1 */) +{ + if (count > input.size()) return false; + input.remove(0, count); + return true; +} + +char AbstractParser::getNext() +{ + char ch = input.at(0); + advance(); + return ch; +} + --- AbstractParser.h +++ AbstractParser.h @@ -0,0 +1,47 @@ +/*************************************************************************** +* Copyright (C) 2007 by Thomas Keller * +* address@hidden * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef ABSTRACT_PARSER_H +#define ABSTRACT_PARSER_H + +#include +#include + +class AbstractParser +{ +public: + AbstractParser(const QByteArray &); + AbstractParser(const QString &); + virtual ~AbstractParser(); + + virtual bool parse() = 0; + inline QByteArray getLeftBytes() const { return input; } + +protected: + char whatsNext(int count = 0); + char getNext(); + void eatSpaces(); + bool advance(int count = 1); + + QByteArray input; +}; + +#endif + --- MonotoneThread.cpp +++ MonotoneThread.cpp @@ -0,0 +1,276 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "MonotoneThread.h" +#include "StdioParser.h" + +#include +#include + +MonotoneTask::MonotoneTask() +{ + init(QList(), QList()); +} + +MonotoneTask::MonotoneTask(const QStringList & args) +{ + init(stringToByteArrayList(args), QList()); +} + +MonotoneTask::MonotoneTask(const QStringList & args, const QStringList & opts) +{ + init(stringToByteArrayList(args), stringToByteArrayList(opts)); +} + +MonotoneTask::MonotoneTask(const QList & args) +{ + init(args, QList()); +} + +MonotoneTask::MonotoneTask(const QList & args, const QList & opts) +{ + init(args, opts); +} + +void MonotoneTask::init(const QList & args, const QList & opts) +{ + arguments = args; + options = opts; + returnCode = -1; +} + +QList MonotoneTask::stringToByteArrayList(const QStringList & list) +{ + QList byteArrayList; + foreach (QString entry, list) + { + byteArrayList.append(entry.toUtf8()); + } + return byteArrayList; +} + +QByteArray MonotoneTask::getEncodedInput() const +{ + QByteArray commandLine; + QTextStream streamCmdLine(&commandLine); + + if (options.size() > 0) + { + // currently mtn can only understand key => value option pairs + if (options.size() % 2 != 0) qFatal("%s: %d", __FILE__, __LINE__); + + streamCmdLine << "o"; + for (int i=0, c=options.size(); i("MonotoneTask"); + + process = new QProcess(); + + QStringList args; + args << "automate"; + args << "stdio"; + args << QString("--automate-stdio-size=%1").arg(StdioBufferSize); + args << "--db" << database; + + if (!workspace.isEmpty()) + { + process->setWorkingDirectory(workspace); + } + + qDebug(QString("starting %1 %2").arg(mtn).arg(args.join(" ")).toLatin1()); + process->start(mtn, args); +} + +MonotoneThread::~MonotoneThread() +{ + cleanup(); + delete process; +} + +void MonotoneThread::enqueueTask(const MonotoneTask & task) +{ + queue.enqueue(task); +} + +void MonotoneThread::run() +{ + if (!process->waitForStarted()) + { + qDebug("not started"); + emit error(processErrorToString()); + cleanup(); + return; + } + + QTextStream streamProcess(process); + + QByteArray buffer; + QByteArray output; + + bool processingTask = false; + + while (!doAbort) + { + if (process->state() != QProcess::Running) + { + qDebug("not running"); + QString err; + if (process->exitStatus() == QProcess::CrashExit) + { + err = processErrorToString(); + } + + // read anything we get from stderr + err.append(QString::fromUtf8(process->readAllStandardError())); + + emit error(err); + cleanup(); + return; + } + + if (queue.size() == 0) continue; + + if (!processingTask) + { + qDebug("starting new task"); + MonotoneTask task = queue.head(); + streamProcess << task.getEncodedInput(); + streamProcess.flush(); + processingTask = true; + } + else + { + qDebug("continue on last task"); + } + + if (!process->waitForReadyRead(-1)) + { + qDebug("timeout waiting for ready read"); + QString err(processErrorToString()); + err.append(QString::fromUtf8(process->readAllStandardError())); + emit error(err); + cleanup(); + return; + } + + qDebug("got new data"); + + // FIXME: what about stderr output here? + buffer.append(process->readAllStandardOutput()); + StdioParser parser(buffer); + + // if the chunk is not yet complete, try again later + if (!parser.parse()) + { + qDebug("output incomplete / not parsable"); + continue; + } + + buffer = parser.getLeftBytes(); + output.append(parser.getPayload()); + int returnCode = parser.getErrorCode(); + + // FIXME: support for other chunk types here + if (parser.getChunkType() == 'm') + { + qDebug("more data to come"); + continue; + } + + MonotoneTask task = queue.dequeue(); + task.setOutput(output); + task.setReturnCode(returnCode); + processingTask = false; + output.clear(); + + emit taskFinished(task); + } + cleanup(); +} + +QString MonotoneThread::processErrorToString() +{ + QString desc; + switch (process->error()) + { + case QProcess::FailedToStart: desc = tr("failed to start"); + case QProcess::Crashed: desc = tr("crashed"); + case QProcess::Timedout: desc = tr("timed out"); + case QProcess::WriteError: desc = tr("write error"); + case QProcess::ReadError: desc = tr("read error"); + default: desc = tr("unknown or no error"); + } + return desc; +} + +void MonotoneThread::abort() +{ + doAbort = true; +} + +void MonotoneThread::cleanup() +{ + if (queue.size() > 0) + { + foreach (MonotoneTask task, queue) + { + emit taskAborted(task); + } + queue.clear(); + } + + // close the pipes + process->close(); + // send SIGTERM + process->terminate(); + // block until the process has really been finished + process->waitForFinished(); +} + --- MonotoneThread.h +++ MonotoneThread.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef MONOTONE_THREAD_H +#define MONOTONE_THREAD_H + +#include +#include +#include + +class MonotoneTask +{ +public: + MonotoneTask(); + MonotoneTask(const QStringList &); + MonotoneTask(const QStringList &, const QStringList &); + MonotoneTask(const QList &); + MonotoneTask(const QList &, const QList &); + + QByteArray getEncodedInput() const; + + void setOutput(const QByteArray & out) { output = out; } + void setReturnCode(int code) { returnCode = code; } + + QByteArray getOutput() const { return output; } + QString getOutputUtf8() const { return QString::fromUtf8(output); } + bool getReturnCode() const { return returnCode; } + +private: + void init(const QList &, const QList &); + QList stringToByteArrayList(const QStringList &); + + int returnCode; + + QList arguments; + QList options; + QByteArray output; +}; + +class MonotoneThread : public QThread +{ + Q_OBJECT + +public: + MonotoneThread(const QString &, const QString &, const QString & workspace = QString()); + ~MonotoneThread(); + +protected: + void run(); + +public slots: + void enqueueTask(const MonotoneTask &); + void abort(); + +signals: + void taskFinished(const MonotoneTask &); + void taskAborted(const MonotoneTask &); + void error(const QString &); + +private: + QString processErrorToString(); + void cleanup(); + + static const int StdioBufferSize; + bool doAbort; + QProcess * process; + QQueue queue; +}; + +#endif --- StdioParser.cpp +++ StdioParser.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** +* Copyright (C) 2007 by Thomas Keller * +* address@hidden * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include "StdioParser.h" + +StdioParser::StdioParser(const QByteArray & input) : AbstractParser(input) {} + +bool StdioParser::parse() +{ + if (input.size() == 0) return false; + + // chunk format: :::: + commandNumber = getNumber(); + if (getNext() != ':') qFatal("%s: %d", __FILE__, __LINE__); + errorCode = getNumber(); + if(getNext() != ':') qFatal("%s: %d", __FILE__, __LINE__); + chunkType = getNext(); + if (!(chunkType == 'm' || chunkType == 'l')) qFatal("%s: %d", __FILE__, __LINE__); + if (getNext() != ':') qFatal("%s: %d", __FILE__, __LINE__); + chunkSize = getNumber(); + if (getNext() != ':') qFatal("%s: %d", __FILE__, __LINE__); + + int charsLeft = input.size(); + if (chunkSize > charsLeft) + { + return false; + } + + payload = input.mid(0, chunkSize); + advance(chunkSize); + + return true; +} + +int StdioParser::getNumber() +{ + int number = 0; + int processedChars = 0; + + while (char ch = whatsNext()) + { + if (ch < '0' || ch > '9') + { + // ensure that we've read at least one char + if (processedChars == 0) qFatal("%s: %d", __FILE__, __LINE__); + break; + } + number *= 10; + number += (ch - '0'); + advance(); + processedChars++; + } + + return number; +} + --- StdioParser.h +++ StdioParser.h @@ -0,0 +1,49 @@ +/*************************************************************************** +* Copyright (C) 2007 by Thomas Keller * +* address@hidden * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef STDIO_PARSER_H +#define STDIO_PARSER_H + +#include "AbstractParser.h" + +class StdioParser : public AbstractParser +{ +public: + StdioParser(const QByteArray &); + + bool parse(); + inline int getCommandNumber() const { return commandNumber; } + inline int getErrorCode() const { return errorCode; } + inline char getChunkType() const { return chunkType; } + inline int getChunkSize() const { return chunkSize; } + inline QByteArray getPayload() const { return payload; } + +private: + int getNumber(); + + int commandNumber; + int errorCode; + char chunkType; + int chunkSize; + QByteArray payload; +}; + +#endif + --- main.cpp +++ main.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2006 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "test.h" +#include + +int main(int argc, char** argv) +{ + QApplication app(argc, argv); + + TestDlg dlg; + dlg.show(); + + return app.exec(); +} + --- test.cpp +++ test.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2006 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "test.h" + +TestDlg::TestDlg() : QDialog(0) +{ + setupUi(this); + + mtn = new MonotoneThread("mtn", "~/Entwicklung/guitone.mtn", "~/Entwicklung/guitone"); + mtn->start(); + + connect( + doExec, SIGNAL(clicked()), + this, SLOT(execute()) + ); + + connect( + mtn, SIGNAL(taskFinished(const MonotoneTask &)), + this, SLOT(finished(const MonotoneTask &)) + ); + + connect( + mtn, SIGNAL(error(const QString &)), + this, SLOT(mtnerror(const QString &)) + ); +} + +TestDlg::~TestDlg() +{ + mtn->terminate(); + mtn->wait(); + delete mtn; +} + +void TestDlg::execute() +{ + QStringList in = input->text().split(" "); + MonotoneTask task(in); + mtn->enqueueTask(in); +} + +void TestDlg::finished(const MonotoneTask & task) +{ + output->setText(task.getOutputUtf8()); +} + +void TestDlg::mtnerror(const QString & err) +{ + error->setText(err); +} + --- test.h +++ test.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2006 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef TEST_H +#define TEST_H + +#include "ui_test.h" +#include "MonotoneThread.h" + +#include + +class TestDlg : public QDialog, private Ui::TestDialog +{ + Q_OBJECT + +public: + TestDlg(); + ~TestDlg(); + +private: + MonotoneThread * mtn; + +private slots: + void execute(); + void finished(const MonotoneTask &); + void mtnerror(const QString &); +}; + +#endif --- test.pro +++ test.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +TARGET = test +INCLUDEPATH = . +HEADERS = MonotoneThread.h \ + StdioParser.h \ + AbstractParser.h \ + test.h +SOURCES = MonotoneThread.cpp \ + StdioParser.cpp \ + AbstractParser.cpp \ + test.cpp \ + main.cpp +FORMS = test.ui + --- test.ui +++ test.ui @@ -0,0 +1,118 @@ + + TestDialog + + + + 0 + 0 + 534 + 454 + + + + Dialog + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + 0 + + + 6 + + + + + + + + exec + + + true + + + + + + + + + + + + + + + 0 + + + 6 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + close + + + + + + + + + + + input + doExec + output + error + + + + + pushButton + clicked() + TestDialog + accept() + + + 436 + 394 + + + 301 + 398 + + + + +