/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

/****************************************************************************
**
** Copyright (C) 2023-2026 Linaro Limited (or its affiliates). All rights reserved.
** Copyright (C) 2009-2023 Arm Limited (or its affiliates).
**
****************************************************************************/

//#define QPIPEREADER_DEBUG

#include <QFile>
#include <QSocketNotifier>
#include <errno.h>
#define BSD_COMP                // gets solaris fionread
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined (Q_OS_WIN)
#include <io.h>
#else
#include <unistd.h>
#endif
#include "qpipereader_p.h"

#if defined QPIPEREADER_DEBUG
static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
{
    if (!data) return "(null)";
    QByteArray out;
    for (int i = 0; i < len; ++i) {
        char c = data[i];
        if (isprint(c)) {
            out += c;
        } else switch (c) {
        case '\n': out += "\\n"; break;
        case '\r': out += "\\r"; break;
        case '\t': out += "\\t"; break;
        default:
            QString tmp;
            tmp = QString::asprintf("\\%o", c);
            out += tmp.toLocal8Bit();
        }
    }

    if (len < maxSize)
        out += "...";

    return out;
}
#endif

QPipeReaderPrivate::QPipeReaderPrivate()
{
    q_ptr = 0;
    eof = false;
    notifier = 0;
    pipe = INVALID_Q_PIPE;
    blocking = false;
    logFile = 0;
    enabled = true;
}

QPipeReaderPrivate::~QPipeReaderPrivate()
{
    delete notifier;
}

qint64 QPipeReaderPrivate::read(char *data, qint64 maxlen)
{
    extern qint64 qt_native_read(Q_PIPE pipe, char* data, qint64 maxlen);

    qint64 bytesRead = qt_native_read(pipe, data, maxlen);
#if defined QPIPEREADER_DEBUG
    qDebug("QPipeReaderPrivate::read(%p \"%s\", %lld) == %lld",
           data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);
#endif
    return bytesRead;
}

void QPipeReaderPrivate::setPipeDescriptor(Q_PIPE pipe, bool blocking)
{
    this->pipe = pipe;
    this->blocking = blocking;
    eof = false;
    createNotifier();
}

void QPipeReaderPrivate::close()
{
    closePipe();
    eof = false;
    buffer.clear();
    delete notifier;
    notifier = 0;
}

QPipeReader::QPipeReader( QObject *parent ) :
    QIODevice(parent), d_ptr(new QPipeReaderPrivate)
{
    d_ptr->q_ptr = this;
}

QPipeReader::~QPipeReader() { }

qint64 QPipeReader::readData ( char * data, qint64 maxlen )
{
    Q_D(QPipeReader);
    qint64 bytesRead = d->buffer.readData(data, maxlen);
    if (!bytesRead && d->eof)
        return -1;              // EOF
    return bytesRead;
}

qint64 QPipeReader::writeData ( const char *, qint64 )
{
    return -1;
}

const DDTRingBuffer *QPipeReader::buffer() const
{
    Q_D(const QPipeReader);
    return &d->buffer;
}

DDTRingBuffer *QPipeReader::buffer()
{
    Q_D(QPipeReader);
    return &d->buffer;
}

bool QPipeReader::open (Q_PIPE pipe, OpenMode mode)
{
    Q_D(QPipeReader);
    if (isOpen()) {
        qWarning("QPipeReader::open: Pipe already open");
        return false;
    }
    if (mode & WriteOnly) {
        qWarning("QPipeReader::open: Pipe must be opened read-only");
        return false;
    }
    if (pipe == INVALID_Q_PIPE) {
        qWarning("QPipeReader::open: Invalid file descriptor");
        return false;
    }
    d->setPipeDescriptor(pipe);
    QIODevice::open(mode);
    return true;
}

bool QPipeReader::open(const QString& filename, OpenMode mode)
{
    Q_D(QPipeReader);
    if (isOpen()) {
        qWarning("QPipeReader::open: Pipe already open");
        return false;
    }
    if (mode & WriteOnly) {
        qWarning("QPipeReader::open: Pipe must be opened read-only");
        return false;
    }
    if (filename.isEmpty()) {
        qWarning("QPipeReader::open: No filename specified");
        return false;
    }

    QByteArray nativeFilename = QFile::encodeName(filename);
    extern Q_PIPE qt_open_read_pipe(const char* pathname);

    Q_PIPE pipe;
    do {
        pipe = qt_open_read_pipe(nativeFilename.constData());
    } while (pipe == INVALID_Q_PIPE && errno == EINTR);

    if (pipe == INVALID_Q_PIPE)
    {
        return false;
    }
    d->setPipeDescriptor(pipe, false);
    QIODevice::open(mode);
    return true;
}

void QPipeReader::close()
{
    Q_D(QPipeReader);
    emit aboutToClose();
    d->close();
    QIODevice::close();
}

/*! \reimp
*/
bool QPipeReader::canReadLine() const
{
    Q_D(const QPipeReader);
    return d->buffer.canReadLine() || QIODevice::canReadLine();
}

/*! \reimp
*/
bool QPipeReader::atEnd() const
{
    Q_D(const QPipeReader);
    return QIODevice::atEnd() && (!isOpen() || d->buffer.isEmpty());
}

/*! \reimp
*/
bool QPipeReader::isSequential() const
{
    return true;
}

/*! \reimp
*/
qint64 QPipeReader::bytesAvailable() const
{
    Q_D(const QPipeReader);
#if defined QPIPEREADER_DEBUG
    qDebug("QPipeReader::bytesAvailable() == %i", d->buffer.size());
#endif
    return d->buffer.size() + QIODevice::bytesAvailable();
}

bool QPipeReader::isEof() const
{
    Q_D(const QPipeReader);
    return d->eof;
}

void QPipeReader::drain()
{
    Q_D(QPipeReader);
#if defined QPIPEREADER_DEBUG
    qDebug("QPipeReader::drain()");
#endif
    d->_q_canRead(true);
}

void QPipeReader::setLogFile(QFile *logFile)
{
    Q_D(QPipeReader);
    d->logFile = logFile;
}

Q_PIPE QPipeReader::fd() const
{
    Q_D(const QPipeReader);
    return d->pipe;
}

QFile *QPipeReader::logFile() const
{
    Q_D(const QPipeReader);
    return d->logFile;
}

void QPipeReader::setEnabled(bool enabled)
{
    Q_D(QPipeReader);
    d->enabled = enabled;
    if (enabled) {
        if (d->notifier)
            d->notifier->setEnabled(true);
        d->_q_canRead();
    } else {
        if (d->notifier)
            d->notifier->setEnabled(false);
    }
}

bool QPipeReader::isEnabled() const
{
    Q_D(const QPipeReader);
    return d->enabled;
}

#include "moc_qpipereader.cpp"
