10#include "kptydevice.h"
13#include <config-pty.h>
15#include <QSocketNotifier>
17#include <KLocalizedString>
32#if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
34#define PTY_BYTES_AVAILABLE TIOCOUTQ
37#define PTY_BYTES_AVAILABLE TIOCINQ
40#define PTY_BYTES_AVAILABLE FIONREAD
43#define KMAXINT ((int)(~0U >> 1))
72 inline bool isEmpty()
const
74 return buffers.
count() == 1 && !tail;
77 inline int size()
const
82 inline int readSize()
const
84 return (buffers.count() == 1 ? tail : buffers.first().size()) - head;
87 inline const char *readPointer()
const
89 Q_ASSERT(totalSize > 0);
90 return buffers.first().constData() + head;
96 Q_ASSERT(totalSize >= 0);
103 if (head == tail && buffers.count() == 1) {
104 buffers.first().resize(CHUNKSIZE);
111 if (buffers.count() == 1) {
112 buffers.first().resize(CHUNKSIZE);
117 buffers.removeFirst();
122 char *reserve(
int bytes)
127 if (tail + bytes <= buffers.last().size()) {
128 ptr = buffers.last().data() + tail;
131 buffers.last().resize(tail);
133 tmp.
resize(qMax(CHUNKSIZE, bytes));
142 inline void unreserve(
int bytes)
148 inline void write(
const char *data,
int len)
150 memcpy(reserve(len), data, len);
156 int indexAfter(
char c,
int maxLength = KMAXINT)
const
165 if (index == size()) {
168 const QByteArray &buf = *it;
170 int len = qMin((it == buffers.end() ? tail : buf.
size()) -
start, maxLength);
172 if (
const char *rptr = (
const char *)memchr(ptr, c, len)) {
173 return index + (rptr - ptr) + 1;
181 inline int lineSize(
int maxLength = KMAXINT)
const
183 return indexAfter(
'\n', maxLength);
186 inline bool canReadLine()
const
188 return lineSize() != -1;
191 int read(
char *data,
int maxLength)
193 int bytesToRead = qMin(size(), maxLength);
195 while (readSoFar < bytesToRead) {
196 const char *ptr = readPointer();
197 int bs = qMin(bytesToRead - readSoFar, readSize());
198 memcpy(data + readSoFar, ptr, bs);
205 int readLine(
char *data,
int maxLength)
207 return read(data, lineSize(qMin(maxLength, size())));
211 QList<QByteArray> buffers;
222static void qt_ignore_sigpipe()
224 static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
225 if (atom.testAndSetRelaxed(0, 1)) {
226 struct sigaction noaction;
227 memset(&noaction, 0,
sizeof(noaction));
228 noaction.sa_handler = SIG_IGN;
229 sigaction(SIGPIPE, &noaction,
nullptr);
234#define NO_INTR(ret, func) \
237 } while (ret < 0 && errno == EINTR)
240class KPtyDevicePrivate :
public KPtyPrivate
242 Q_DECLARE_PUBLIC(KPtyDevice)
244 KPtyDevicePrivate(KPty *parent)
245 : KPtyPrivate(parent)
246 , emittedReadyRead(false)
247 , emittedBytesWritten(false)
248 , readNotifier(nullptr)
249 , writeNotifier(nullptr)
256 bool doWait(
int msecs,
bool reading);
259 bool emittedReadyRead;
260 bool emittedBytesWritten;
261 QSocketNotifier *readNotifier;
262 QSocketNotifier *writeNotifier;
263 KRingBuffer readBuffer;
264 KRingBuffer writeBuffer;
267bool KPtyDevicePrivate::_k_canRead()
270 qint64 readBytes = 0;
273 if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (
char *)&available)) {
274 char *ptr = readBuffer.reserve(available);
275 NO_INTR(readBytes,
read(q->masterFd(), ptr, available));
277 readBuffer.unreserve(available);
278 q->setErrorString(
i18n(
"Error reading from PTY"));
281 readBuffer.unreserve(available - readBytes);
285 readNotifier->setEnabled(
false);
289 if (!emittedReadyRead) {
290 emittedReadyRead =
true;
291 Q_EMIT q->readyRead();
292 emittedReadyRead =
false;
298bool KPtyDevicePrivate::_k_canWrite()
302 writeNotifier->setEnabled(
false);
303 if (writeBuffer.isEmpty()) {
309 NO_INTR(wroteBytes, write(q->masterFd(), writeBuffer.readPointer(), writeBuffer.readSize()));
310 if (wroteBytes < 0) {
311 q->setErrorString(
i18n(
"Error writing to PTY"));
314 writeBuffer.free(wroteBytes);
316 if (!emittedBytesWritten) {
317 emittedBytesWritten =
true;
318 Q_EMIT q->bytesWritten(wroteBytes);
319 emittedBytesWritten =
false;
322 if (!writeBuffer.isEmpty()) {
323 writeNotifier->setEnabled(
true);
331#define timeradd(a, b, result) \
333 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
334 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
335 if ((result)->tv_usec >= 1000000) { \
336 ++(result)->tv_sec; \
337 (result)->tv_usec -= 1000000; \
341#define timersub(a, b, result) \
343 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
344 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
345 if ((result)->tv_usec < 0) { \
346 --(result)->tv_sec; \
347 (result)->tv_usec += 1000000; \
353bool KPtyDevicePrivate::doWait(
int msecs,
bool reading)
365 tv.tv_sec = msecs / 1000;
366 tv.tv_usec = (msecs % 1000) * 1000;
368 gettimeofday(&etv,
nullptr);
369 timeradd(&tv, &etv, &etv);
374 while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
381 if (readNotifier->isEnabled()) {
382 FD_SET(q->masterFd(), &rfds);
384 if (!writeBuffer.isEmpty()) {
385 FD_SET(q->masterFd(), &wfds);
390 gettimeofday(&tv,
nullptr);
391 timersub(&etv, &tv, &tv);
393 tv.tv_sec = tv.tv_usec = 0;
398 switch (select(q->masterFd() + 1, &rfds, &wfds,
nullptr, tvp)) {
400 if (errno == EINTR) {
405 q->setErrorString(
i18n(
"PTY operation timed out"));
408 if (FD_ISSET(q->masterFd(), &rfds)) {
409 bool canRead = _k_canRead();
410 if (reading && canRead) {
414 if (FD_ISSET(q->masterFd(), &wfds)) {
415 bool canWrite = _k_canWrite();
430 q->QIODevice::open(mode);
431 fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
441 readNotifier->setEnabled(
true);
450 ,
KPty(new KPtyDevicePrivate(this))
499 delete d->readNotifier;
500 delete d->writeNotifier;
533 return d->writeBuffer.size();
536bool KPtyDevice::waitForReadyRead(
int msecs)
539 return d->doWait(msecs,
true);
542bool KPtyDevice::waitForBytesWritten(
int msecs)
545 return d->doWait(msecs,
false);
551 d->readNotifier->setEnabled(!suspended);
557 return !d->readNotifier->isEnabled();
561qint64 KPtyDevice::readData(
char *data, qint64 maxlen)
564 return d->readBuffer.read(data, (
int)qMin<qint64>(maxlen, KMAXINT));
568qint64 KPtyDevice::readLineData(
char *data, qint64 maxlen)
571 return d->readBuffer.readLine(data, (
int)qMin<qint64>(maxlen, KMAXINT));
575qint64 KPtyDevice::writeData(
const char *data, qint64 len)
578 Q_ASSERT(len <= KMAXINT);
580 d->writeBuffer.write(data, len);
581 d->writeNotifier->setEnabled(
true);
585#include "moc_kptydevice.cpp"
Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
void setSuspended(bool suspended)
Sets whether the KPtyDevice monitors the pty for incoming data.
qint64 bytesAvailable() const override
bool isSequential() const override
bool canReadLine() const override
KPtyDevice(QObject *parent=nullptr)
Constructor.
qint64 bytesToWrite() const override
bool isSuspended() const
Returns true if the KPtyDevice is not monitoring the pty for incoming data.
void close() override
Close the pty master/slave pair.
bool atEnd() const override
~KPtyDevice() override
Destructor:
bool open()
Create a pty master/slave pair.
void close()
Close the pty master/slave pair.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
QVariant read(const QByteArray &data, int versionOverride=0)
qsizetype count() const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
virtual bool atEnd() const const
virtual qint64 bytesAvailable() const const
virtual bool canReadLine() const const
void setErrorString(const QString &str)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)