source: ogBrowser-Git/qtermwidget/lib/kptydevice.cpp

jenkins
Last change on this file was 64efc22, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update qtermwidget to modern version

  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 * This file is a part of QTerminal - http://gitorious.org/qterminal
3 *
4 * This file was un-linked from KDE and modified
5 * by Maxim Bourmistrov <maxim@unixconn.com>
6 *
7 */
8
9/*
10
11   This file is part of the KDE libraries
12   Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
13   Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org>
14     Author Adriaan de Groot <groot@kde.org>
15
16   This library is free software; you can redistribute it and/or
17   modify it under the terms of the GNU Library General Public
18   License as published by the Free Software Foundation; either
19   version 2 of the License, or (at your option) any later version.
20
21   This library is distributed in the hope that it will be useful,
22   but WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24   Library General Public License for more details.
25
26   You should have received a copy of the GNU Library General Public License
27   along with this library; see the file COPYING.LIB.  If not, write to
28   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29   Boston, MA 02110-1301, USA.
30*/
31
32#include "kptydevice.h"
33#include "kpty_p.h"
34
35#include <QSocketNotifier>
36
37#include <unistd.h>
38#include <cerrno>
39#include <csignal>
40#include <termios.h>
41#include <fcntl.h>
42#include <sys/ioctl.h>
43#ifdef HAVE_SYS_FILIO_H
44# include <sys/filio.h>
45#endif
46#ifdef HAVE_SYS_TIME_H
47# include <sys/time.h>
48#endif
49
50#if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
51  // "the other end's output queue size" - kinda braindead, huh?
52# define PTY_BYTES_AVAILABLE TIOCOUTQ
53#elif defined(TIOCINQ)
54  // "our end's input queue size"
55# define PTY_BYTES_AVAILABLE TIOCINQ
56#else
57  // likewise. more generic ioctl (theoretically)
58# define PTY_BYTES_AVAILABLE FIONREAD
59#endif
60
61
62
63
64//////////////////
65// private data //
66//////////////////
67
68// Lifted from Qt. I don't think they would mind. ;)
69// Re-lift again from Qt whenever a proper replacement for pthread_once appears
70static void qt_ignore_sigpipe()
71{
72    static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
73    if (atom.testAndSetRelaxed(0, 1)) {
74        struct sigaction noaction;
75        memset(&noaction, 0, sizeof(noaction));
76        noaction.sa_handler = SIG_IGN;
77        sigaction(SIGPIPE, &noaction, nullptr);
78    }
79}
80
81#define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR)
82
83bool KPtyDevicePrivate::_k_canRead()
84{
85    Q_Q(KPtyDevice);
86    qint64 readBytes = 0;
87
88#ifdef Q_OS_IRIX // this should use a config define, but how to check it?
89    size_t available;
90#else
91    int available;
92#endif
93    if (::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (char *) &available) != -1) {
94#ifdef Q_OS_SOLARIS
95        // A Pty is a STREAMS module, and those can be activated
96        // with 0 bytes available. This happens either when ^C is
97        // pressed, or when an application does an explicit write(a,b,0)
98        // which happens in experiments fairly often. When 0 bytes are
99        // available, you must read those 0 bytes to clear the STREAMS
100        // module, but we don't want to hit the !readBytes case further down.
101        if (!available) {
102            char c;
103            // Read the 0-byte STREAMS message
104            NO_INTR(readBytes, read(q->masterFd(), &c, 0));
105            // Should return 0 bytes read; -1 is error
106            if (readBytes < 0) {
107                readNotifier->setEnabled(false);
108                emit q->readEof();
109                return false;
110            }
111            return true;
112        }
113#endif
114
115        char *ptr = readBuffer.reserve(available);
116#ifdef Q_OS_SOLARIS
117        // Even if available > 0, it is possible for read()
118        // to return 0 on Solaris, due to 0-byte writes in the stream.
119        // Ignore them and keep reading until we hit *some* data.
120        // In Solaris it is possible to have 15 bytes available
121        // and to (say) get 0, 0, 6, 0 and 9 bytes in subsequent reads.
122        // Because the stream is set to O_NONBLOCK in finishOpen(),
123        // an EOF read will return -1.
124        readBytes = 0;
125        while (!readBytes)
126#endif
127        // Useless block braces except in Solaris
128        {
129          NO_INTR(readBytes, read(q->masterFd(), ptr, available));
130        }
131        if (readBytes < 0) {
132            readBuffer.unreserve(available);
133            q->setErrorString(QLatin1String("Error reading from PTY"));
134            return false;
135        }
136        readBuffer.unreserve(available - readBytes); // *should* be a no-op
137    }
138
139    if (!readBytes) {
140        readNotifier->setEnabled(false);
141        emit q->readEof();
142        return false;
143    } else {
144        if (!emittedReadyRead) {
145            emittedReadyRead = true;
146            emit q->readyRead();
147            emittedReadyRead = false;
148        }
149        return true;
150    }
151}
152
153bool KPtyDevicePrivate::_k_canWrite()
154{
155    Q_Q(KPtyDevice);
156
157    writeNotifier->setEnabled(false);
158    if (writeBuffer.isEmpty())
159        return false;
160
161    qt_ignore_sigpipe();
162    int wroteBytes;
163    NO_INTR(wroteBytes,
164            write(q->masterFd(),
165                  writeBuffer.readPointer(), writeBuffer.readSize()));
166    if (wroteBytes < 0) {
167        q->setErrorString(QLatin1String("Error writing to PTY"));
168        return false;
169    }
170    writeBuffer.free(wroteBytes);
171
172    if (!emittedBytesWritten) {
173        emittedBytesWritten = true;
174        emit q->bytesWritten(wroteBytes);
175        emittedBytesWritten = false;
176    }
177
178    if (!writeBuffer.isEmpty())
179        writeNotifier->setEnabled(true);
180    return true;
181}
182
183#ifndef timeradd
184// Lifted from GLIBC
185# define timeradd(a, b, result) \
186    do { \
187        (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
188        (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
189        if ((result)->tv_usec >= 1000000) { \
190            ++(result)->tv_sec; \
191            (result)->tv_usec -= 1000000; \
192        } \
193    } while (0)
194# define timersub(a, b, result) \
195    do { \
196        (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
197        (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
198        if ((result)->tv_usec < 0) { \
199            --(result)->tv_sec; \
200            (result)->tv_usec += 1000000; \
201        } \
202    } while (0)
203#endif
204
205bool KPtyDevicePrivate::doWait(int msecs, bool reading)
206{
207    Q_Q(KPtyDevice);
208#ifndef __linux__
209    struct timeval etv;
210#endif
211    struct timeval tv, *tvp;
212
213    if (msecs < 0)
214        tvp = nullptr;
215    else {
216        tv.tv_sec = msecs / 1000;
217        tv.tv_usec = (msecs % 1000) * 1000;
218#ifndef __linux__
219        gettimeofday(&etv, 0);
220        timeradd(&tv, &etv, &etv);
221#endif
222        tvp = &tv;
223    }
224
225    while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) {
226        fd_set rfds;
227        fd_set wfds;
228
229        FD_ZERO(&rfds);
230        FD_ZERO(&wfds);
231
232        if (readNotifier->isEnabled())
233            FD_SET(q->masterFd(), &rfds);
234        if (!writeBuffer.isEmpty())
235            FD_SET(q->masterFd(), &wfds);
236
237#ifndef __linux__
238        if (tvp) {
239            gettimeofday(&tv, 0);
240            timersub(&etv, &tv, &tv);
241            if (tv.tv_sec < 0)
242                tv.tv_sec = tv.tv_usec = 0;
243        }
244#endif
245
246        switch (select(q->masterFd() + 1, &rfds, &wfds, nullptr, tvp)) {
247        case -1:
248            if (errno == EINTR)
249                break;
250            return false;
251        case 0:
252            q->setErrorString(QLatin1String("PTY operation timed out"));
253            return false;
254        default:
255            if (FD_ISSET(q->masterFd(), &rfds)) {
256                bool canRead = _k_canRead();
257                if (reading && canRead)
258                    return true;
259            }
260            if (FD_ISSET(q->masterFd(), &wfds)) {
261                bool canWrite = _k_canWrite();
262                if (!reading)
263                    return canWrite;
264            }
265            break;
266        }
267    }
268    return false;
269}
270
271void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode)
272{
273    Q_Q(KPtyDevice);
274
275    q->QIODevice::open(mode);
276    fcntl(q->masterFd(), F_SETFL, O_NONBLOCK);
277    readBuffer.clear();
278    readNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q);
279    writeNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q);
280    QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_k_canRead()));
281    QObject::connect(writeNotifier, SIGNAL(activated(int)), q, SLOT(_k_canWrite()));
282    readNotifier->setEnabled(true);
283}
284
285/////////////////////////////
286// public member functions //
287/////////////////////////////
288
289KPtyDevice::KPtyDevice(QObject *parent) :
290    QIODevice(parent),
291    KPty(new KPtyDevicePrivate(this))
292{
293}
294
295KPtyDevice::~KPtyDevice()
296{
297    close();
298}
299
300bool KPtyDevice::open(OpenMode mode)
301{
302    Q_D(KPtyDevice);
303
304    if (masterFd() >= 0)
305        return true;
306
307    if (!KPty::open()) {
308        setErrorString(QLatin1String("Error opening PTY"));
309        return false;
310    }
311
312    d->finishOpen(mode);
313
314    return true;
315}
316
317bool KPtyDevice::open(int fd, OpenMode mode)
318{
319    Q_D(KPtyDevice);
320
321    if (!KPty::open(fd)) {
322        setErrorString(QLatin1String("Error opening PTY"));
323        return false;
324    }
325
326    d->finishOpen(mode);
327
328    return true;
329}
330
331void KPtyDevice::close()
332{
333    Q_D(KPtyDevice);
334
335    if (masterFd() < 0)
336        return;
337
338    delete d->readNotifier;
339    delete d->writeNotifier;
340
341    QIODevice::close();
342
343    KPty::close();
344}
345
346bool KPtyDevice::isSequential() const
347{
348    return true;
349}
350
351bool KPtyDevice::canReadLine() const
352{
353    Q_D(const KPtyDevice);
354    return QIODevice::canReadLine() || d->readBuffer.canReadLine();
355}
356
357bool KPtyDevice::atEnd() const
358{
359    Q_D(const KPtyDevice);
360    return QIODevice::atEnd() && d->readBuffer.isEmpty();
361}
362
363qint64 KPtyDevice::bytesAvailable() const
364{
365    Q_D(const KPtyDevice);
366    return QIODevice::bytesAvailable() + d->readBuffer.size();
367}
368
369qint64 KPtyDevice::bytesToWrite() const
370{
371    Q_D(const KPtyDevice);
372    return d->writeBuffer.size();
373}
374
375bool KPtyDevice::waitForReadyRead(int msecs)
376{
377    Q_D(KPtyDevice);
378    return d->doWait(msecs, true);
379}
380
381bool KPtyDevice::waitForBytesWritten(int msecs)
382{
383    Q_D(KPtyDevice);
384    return d->doWait(msecs, false);
385}
386
387void KPtyDevice::setSuspended(bool suspended)
388{
389    Q_D(KPtyDevice);
390    d->readNotifier->setEnabled(!suspended);
391}
392
393bool KPtyDevice::isSuspended() const
394{
395    Q_D(const KPtyDevice);
396    return !d->readNotifier->isEnabled();
397}
398
399// protected
400qint64 KPtyDevice::readData(char *data, qint64 maxlen)
401{
402    Q_D(KPtyDevice);
403    return d->readBuffer.read(data, (int)qMin<qint64>(maxlen, KMAXINT));
404}
405
406// protected
407qint64 KPtyDevice::readLineData(char *data, qint64 maxlen)
408{
409    Q_D(KPtyDevice);
410    return d->readBuffer.readLine(data, (int)qMin<qint64>(maxlen, KMAXINT));
411}
412
413// protected
414qint64 KPtyDevice::writeData(const char *data, qint64 len)
415{
416    Q_D(KPtyDevice);
417    Q_ASSERT(len <= KMAXINT);
418
419    d->writeBuffer.write(data, len);
420    d->writeNotifier->setEnabled(true);
421    return len;
422}
Note: See TracBrowser for help on using the repository browser.