28 #include <config-pty.h>
30 #include <QSocketNotifier>
39 #include <sys/ioctl.h>
40 #ifdef HAVE_SYS_FILIO_H
41 # include <sys/filio.h>
43 #ifdef HAVE_SYS_TIME_H
44 # include <sys/time.h>
47 #if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
49 # define PTY_BYTES_AVAILABLE TIOCOUTQ
50 #elif defined(TIOCINQ)
52 # define PTY_BYTES_AVAILABLE TIOCINQ
55 # define PTY_BYTES_AVAILABLE FIONREAD
58 #define KMAXINT ((int)(~0U >> 1))
64 #include <QtCore/qbytearray.h>
65 #include <QtCore/qlinkedlist.h>
67 #define CHUNKSIZE 4096
87 inline bool isEmpty()
const
89 return buffers.count() == 1 && !tail;
92 inline int size()
const
97 inline int readSize()
const
99 return (buffers.count() == 1 ? tail : buffers.first().size()) - head;
102 inline const char *readPointer()
const
104 Q_ASSERT(totalSize > 0);
105 return buffers.first().constData() + head;
111 Q_ASSERT(totalSize >= 0);
114 int nbs = readSize();
118 if (head == tail && buffers.count() == 1) {
126 if (buffers.count() == 1) {
132 buffers.removeFirst();
137 char *reserve(
int bytes)
142 if (tail + bytes <= buffers.last().size()) {
143 ptr = buffers.last().data() + tail;
146 buffers.last().resize(tail);
157 inline void unreserve(
int bytes)
163 inline void write(
const char *data,
int len)
165 memcpy(reserve(len), data, len);
171 int indexAfter(
char c,
int maxLength =
KMAXINT)
const
175 QLinkedList<QByteArray>::ConstIterator it = buffers.begin();
181 const QByteArray &buf = *it;
183 int len = qMin((it == buffers.end() ? tail : buf.size()) - start,
185 const char *ptr = buf.data() + start;
186 if (
const char *rptr = (
const char *)memchr(ptr, c, len))
187 return index + (rptr - ptr) + 1;
194 inline int lineSize(
int maxLength =
KMAXINT)
const
196 return indexAfter(
'\n', maxLength);
199 inline bool canReadLine()
const
201 return lineSize() != -1;
204 int read(
char *data,
int maxLength)
206 int bytesToRead = qMin(size(), maxLength);
208 while (readSoFar < bytesToRead) {
209 const char *ptr = readPointer();
210 int bs = qMin(bytesToRead - readSoFar, readSize());
211 memcpy(data + readSoFar, ptr, bs);
218 int readLine(
char *data,
int maxLength)
220 return read(data, lineSize(qMin(maxLength, size())));
224 QLinkedList<QByteArray> buffers;
237 static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
238 if (atom.testAndSetRelaxed(0, 1)) {
239 struct sigaction noaction;
240 memset(&noaction, 0,
sizeof(noaction));
241 noaction.sa_handler = SIG_IGN;
242 sigaction(SIGPIPE, &noaction, 0);
246 #define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR)
251 KPtyDevicePrivate(
KPty* parent) :
253 emittedReadyRead(
false), emittedBytesWritten(
false),
254 readNotifier(0), writeNotifier(0)
261 bool doWait(
int msecs,
bool reading);
262 void finishOpen(QIODevice::OpenMode mode);
264 bool emittedReadyRead;
265 bool emittedBytesWritten;
266 QSocketNotifier *readNotifier;
267 QSocketNotifier *writeNotifier;
268 KRingBuffer readBuffer;
269 KRingBuffer writeBuffer;
272 bool KPtyDevicePrivate::_k_canRead()
277 #ifdef Q_OS_IRIX // this should use a config define, but how to check it?
293 NO_INTR(readBytes, read(q->masterFd(), &c, 0));
296 readNotifier->setEnabled(
false);
304 char *ptr = readBuffer.reserve(available);
318 NO_INTR(readBytes, read(q->masterFd(), ptr, available));
321 readBuffer.unreserve(available);
322 q->setErrorString(
i18n(
"Error reading from PTY"));
325 readBuffer.unreserve(available - readBytes);
329 readNotifier->setEnabled(
false);
333 if (!emittedReadyRead) {
334 emittedReadyRead =
true;
336 emittedReadyRead =
false;
342 bool KPtyDevicePrivate::_k_canWrite()
346 writeNotifier->setEnabled(
false);
347 if (writeBuffer.isEmpty())
354 writeBuffer.readPointer(), writeBuffer.readSize()));
355 if (wroteBytes < 0) {
356 q->setErrorString(
i18n(
"Error writing to PTY"));
359 writeBuffer.free(wroteBytes);
361 if (!emittedBytesWritten) {
362 emittedBytesWritten =
true;
363 emit q->bytesWritten(wroteBytes);
364 emittedBytesWritten =
false;
367 if (!writeBuffer.isEmpty())
368 writeNotifier->setEnabled(
true);
374 # define timeradd(a, b, result) \
376 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
377 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
378 if ((result)->tv_usec >= 1000000) { \
379 ++(result)->tv_sec; \
380 (result)->tv_usec -= 1000000; \
383 # define timersub(a, b, result) \
385 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
386 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
387 if ((result)->tv_usec < 0) { \
388 --(result)->tv_sec; \
389 (result)->tv_usec += 1000000; \
394 bool KPtyDevicePrivate::doWait(
int msecs,
bool reading)
400 struct timeval tv, *tvp;
405 tv.tv_sec = msecs / 1000;
406 tv.tv_usec = (msecs % 1000) * 1000;
408 gettimeofday(&etv, 0);
414 while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
421 if (readNotifier->isEnabled())
422 FD_SET(q->masterFd(), &rfds);
423 if (!writeBuffer.isEmpty())
424 FD_SET(q->masterFd(), &wfds);
428 gettimeofday(&tv, 0);
431 tv.tv_sec = tv.tv_usec = 0;
435 switch (select(q->masterFd() + 1, &rfds, &wfds, 0, tvp)) {
441 q->setErrorString(
i18n(
"PTY operation timed out"));
444 if (FD_ISSET(q->masterFd(), &rfds)) {
445 bool canRead = _k_canRead();
446 if (reading && canRead)
449 if (FD_ISSET(q->masterFd(), &wfds)) {
450 bool canWrite = _k_canWrite();
460 void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode)
464 q->QIODevice::open(mode);
465 fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
467 readNotifier =
new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q);
468 writeNotifier =
new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q);
469 QObject::connect(readNotifier, SIGNAL(activated(
int)), q, SLOT(_k_canRead()));
470 QObject::connect(writeNotifier, SIGNAL(activated(
int)), q, SLOT(_k_canWrite()));
471 readNotifier->setEnabled(
true);
480 KPty(new KPtyDevicePrivate(this))
497 setErrorString(
i18n(
"Error opening PTY"));
511 setErrorString(
i18n(
"Error opening PTY"));
527 delete d->readNotifier;
528 delete d->writeNotifier;
543 return QIODevice::canReadLine() || d->readBuffer.canReadLine();
549 return QIODevice::atEnd() && d->readBuffer.isEmpty();
555 return QIODevice::bytesAvailable() + d->readBuffer.size();
561 return d->writeBuffer.size();
567 return d->doWait(msecs,
true);
573 return d->doWait(msecs,
false);
579 d->readNotifier->setEnabled(!suspended);
585 return !d->readNotifier->isEnabled();
592 return d->readBuffer.read(data, (
int)qMin<qint64>(maxlen,
KMAXINT));
599 return d->readBuffer.readLine(data, (
int)qMin<qint64>(maxlen,
KMAXINT));
608 d->writeBuffer.write(data, len);
609 d->writeNotifier->setEnabled(
true);
613 #include "kptydevice.moc"
QString i18n(const char *text)
virtual ~KPtyDevice()
Destructor:
virtual bool isSequential() const
qint64 bytesAvailable() const
#define timeradd(a, b, result)
#define timersub(a, b, result)
bool open()
Create a pty master/slave pair.
static void qt_ignore_sigpipe()
virtual qint64 readLineData(char *data, qint64 maxSize)
virtual void close()
Close the pty master/slave pair.
void setSuspended(bool suspended)
Sets whether the KPtyDevice monitors the pty for incoming data.
virtual qint64 readData(char *data, qint64 maxSize)
#define NO_INTR(ret, func)
bool waitForReadyRead(int msecs=-1)
virtual qint64 writeData(const char *data, qint64 maxSize)
qint64 bytesToWrite() const
#define PTY_BYTES_AVAILABLE
bool isSuspended() const
Returns true if the KPtyDevice is not monitoring the pty for incoming data.
void close()
Close the pty master/slave pair.
KPtyDevice(QObject *parent=0)
Constructor.
KPtyPrivate(KPty *parent)
Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc.
Provides primitives for opening & closing a pseudo TTY pair, assigning the controlling TTY...
bool waitForBytesWritten(int msecs=-1)