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

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

[Attachment #2 (multipart/mixed)]


Hi,

I'd like to introduce my example of KLineBufferedProcess, which should ease 
the handling of line oriented output of processes. Users would be for example 
KGpg where the interaction with gpg is line based. Also K3b could be a 
potential user as it parses the output of several different command line 
tools.

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. 
Everything else (program arguments, start, finished signal, writing data to 
the process' stdin) is unmodified from KProcess.

Please have a look at the code and give me feedback.

Greetings,

Eike

P.S.: please CC me on replies, I'm not on the list.

["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();

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

    QByteArray m_stdoutBuffer;
    QByteArray m_stderrBuffer;
    int m_newlineInStdout;
    int m_newlineInStderr;
};

KLineBufferedProcessPrivate::KLineBufferedProcessPrivate()
{
    m_newlineInStdout = -1;
    m_newlineInStderr = -1;
}

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

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

void KLineBufferedProcessPrivate::_k_receivedStdout()
{
    QByteArray ndata = 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 lineReadyStandardOutput();
        }
    }
}

void KLineBufferedProcessPrivate::_k_receivedStderr()
{
    QByteArray ndata = 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 lineReadyStandardError();
        }
    }
}

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

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

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

    return true;
}

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

    // don't copy '\n'
    *line = d->m_stderrBuffer.left(len);
    d->m_stderrBuffer.remove(0, len + 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 KLineProcessPrivate;

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

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 slots:
    Q_PRIVATE_SLOT(d, void _k_receivedStdout())
    Q_PRIVATE_SLOT(d, void _k_receivedStderr())

private:
    KLineProcessPrivate* const d;
};

#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