[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Re: Enhancement: KLineBufferedProcess
From:       Rolf Eike Beer <kde () opensource ! sf-tec ! de>
Date:       2008-03-11 23:07:20
Message-ID: 200803120007.20935.kde () opensource ! sf-tec ! de
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


Oswald Buddenhagen wrote:
> On Fri, Mar 07, 2008 at 04:13:40PM +0100, Rolf Eike Beer wrote:
> > The idea behind this class is to receive the data from the process and
> > buffer it. A signal is emitted only if a complete line of data is
> > available.
>
> fwiw, in qt 4.4, the combination of kprocess + qtextstream should work
> reliably.

Well, that would not help anyone who could find arbitrary 8 bit characters in 
the output so he needs the result as a QByteArray. For the GPG output I 
should be able to write a QTextCodec that handles this stuff. Thanks for the 
hint.

Nevertheless I have attached a version that actually works (tested with KGpg). 
The other one was a bit hacky since the people at the booth confused me ;)

Eike

["klineprocess.cpp" (text/x-c++src)]

/**
 * Copyright (C) 2008 Rolf Eike Beer <kde@opensource.sf-tec.de>
 */

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "klineprocess.h"

class KLineBufferedProcessPrivate
{
public:
    KLineBufferedProcessPrivate(KLineBufferedProcess *parent);

//private slot implementations
    void _k_receivedStdout();
    void _k_receivedStderr();

    QByteArray m_stdoutBuffer;
    QByteArray m_stderrBuffer;
    int m_newlineInStdout;
    int m_newlineInStderr;
    KLineBufferedProcess *m_parent;
};

KLineBufferedProcessPrivate::KLineBufferedProcessPrivate(KLineBufferedProcess *parent)
{
    m_newlineInStdout = -1;
    m_newlineInStderr = -1;
    m_parent = parent;
}

KLineBufferedProcess::KLineBufferedProcess(QObject *parent)
 : KProcess(parent),
   d(new KLineBufferedProcessPrivate(this))
{
    connect(this, SIGNAL(readyReadStandardOutput()), this, SLOT(_k_receivedStdout()));
    connect(this, SIGNAL(readyReadStandardError()), this, SLOT(_k_receivedStderr()));
}

KLineBufferedProcess::~KLineBufferedProcess()
{
    delete d;
}

#include <KDebug>
void KLineBufferedProcessPrivate::_k_receivedStdout()
{
    QByteArray ndata = m_parent->readAllStandardOutput();
    int oldBufferSize = m_stdoutBuffer.size();
    m_stdoutBuffer.append(ndata);

    if (m_newlineInStdout < 0) {
        m_newlineInStdout = ndata.indexOf('\n');
        if (m_newlineInStdout >= 0) {
            m_newlineInStdout += oldBufferSize;
            emit m_parent->lineReadyStandardOutput();
        }
    }
}

void KLineBufferedProcessPrivate::_k_receivedStderr()
{
    QByteArray ndata = m_parent->readAllStandardError();
    int oldBufferSize = m_stderrBuffer.size();
    m_stderrBuffer.append(ndata);

   if (m_newlineInStderr < 0) {
        m_newlineInStderr = ndata.indexOf('\n');
        if (m_newlineInStderr >= 0) {
            m_newlineInStderr += oldBufferSize;
            emit m_parent->lineReadyStandardError();
        }
    }
}

bool KLineBufferedProcess::readLineStandardOutput(QByteArray *line)
{
    if (d->m_newlineInStdout < 0) {
        return false;
    }

    // don't copy '\n'
    *line = d->m_stdoutBuffer.left(d->m_newlineInStdout);
    d->m_stdoutBuffer.remove(0, d->m_newlineInStdout + 1);

    d->m_newlineInStdout = d->m_stdoutBuffer.indexOf('\n');

    return true;
}

bool KLineBufferedProcess::readLineStandardError(QByteArray *line)
{
    if (d->m_newlineInStderr < 0) {
        return false;
    }

    // don't copy '\n'
    *line = d->m_stderrBuffer.left(d->m_newlineInStderr);
    d->m_stderrBuffer.remove(0, d->m_newlineInStderr + 1);

    d->m_newlineInStderr = d->m_stderrBuffer.indexOf('\n');

    return true;
}

bool KLineBufferedProcess::hasLineStandardOutput()
{
    return d->m_newlineInStdout >= 0;
}

bool KLineBufferedProcess::hasLineStandardError()
{
    return d->m_newlineInStderr >= 0;
}

#include "klineprocess.moc"

["klineprocess.h" (text/x-c++hdr)]

/**
 * Copyright (C) 2008 Rolf Eike Beer <kde@opensource.sf-tec.de>
 */

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef KLINEBUFFEREDPROCESS_H
#define KLINEBUFFEREDPROCESS_H

#include <KProcess>

class QByteArray;
class KLineBufferedProcessPrivate;

/**
 * Read output of a process split into lines
 *
 * This class reads the output of a process and splits it up into lines. This
 * is especially useful if you try to parse the output of a command line tool.
 *
 * \b Usage \n
 *
 * The class is created and set up like a KProcess. After this you can do
 * something like this:
 *
 * \code
 * connect(m_linebufprocess, SIGNAL(lineReadyStandardOutput()), SLOT(dataStdout()));
 * ...
 * void myobj::dataStdout()
 * {
 *   while (m_linebufprocess->hasLineStandardOutput()) {
 *     QByteArray line;
 *     m_linebufprocess->readLineStandardOutput(line);
 *     ...
 *   }
 * }
 * \endcode
 *
 * Never use the read functionality of KProcess with this class. This class
 * needs to read all data from the process into an internal buffer first. If
 * you try to use the read funtions of the parent classes you would normally
 * get no output at all.
 *
 * The write functions of the parent classes are not effected. You can use
 * them exactly the same way as in KProcess.
 *
 * @author Rolf Eike Beer
 */
class KLineBufferedProcess : public KProcess
{
    Q_OBJECT
    friend class KLineBufferedProcessPrivate;

public:
    /**
     * Constructor
     */
    explicit KLineBufferedProcess(QObject *parent = 0);

    /**
     * Destructor
     */
    ~KLineBufferedProcess();

    /**
     * Reads a line of text (excluding '\\n') from stdout.
     *
     * Use readLineStdout() in response to a lineReadyStdout() signal or
     * when hasLineStdout() returns true. You may use it multiple times if
     * more than one line of data is available. If no complete line is
     * available the content of line is undefined and the function returns
     * false.
     *
     * @param line is used to store the line that was read.
     * @return if data was read or not
     */
    bool readLineStandardOutput(QByteArray *line);

    /**
     * Reads a line of text (excluding '\\n') from stderr.
     *
     * Use readLineStderr() in response to a lineReadyStderr() signal or
     * when hasLineStderr() returns true. You may use it multiple times if
     * more than one line of data is available. If no complete line is
     * available the content of line is undefined and the function returns
     * false.
     *
     * @param line is used to store the line that was read.
     * @return if data was read or not
     */
    bool readLineStandardError(QByteArray *line);

    /**
     * Checks if a line is ready on stdout
     *
     * @return true if a complete line can be read
     */
    bool hasLineStandardOutput();

    /**
     * Checks if a line is ready on stdout
     *
     * @return true if a complete line can be read
     */
    bool hasLineStandardError();

signals:
    /**
     * Emitted when there is a line of data available from stdout when there was
     * previously none.
     * There may or may not be more than one line available for reading when this
     * signal is emitted.
     */
    void lineReadyStandardOutput();

    /**
     * Emitted when there is a line of data available from stderr when there was
     * previously none.
     * There may or may not be more than one line available for reading when this
     * signal is emitted.
     */
    void lineReadyStandardError();

private:
    KLineBufferedProcessPrivate* const d;

    Q_PRIVATE_SLOT(d, void _k_receivedStdout())
    Q_PRIVATE_SLOT(d, void _k_receivedStderr())
};

#endif // GPGPROC_H

["signature.asc" (application/pgp-signature)]

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic