Mercurial > octave-nkf
changeset 13647:ac3d9480292d
Renamed file.
author | Jacob Dawid <jacob.dawid@googlemail.com> |
---|---|
date | Tue, 23 Aug 2011 18:22:27 +0200 |
parents | 2e1f54803758 |
children | da69cec2459f |
files | gui/octave-gui.pro gui/src/terminal/KPty.cpp gui/src/terminal/KPty.h gui/src/terminal/KPtyDevice.cpp gui/src/terminal/KPtyDevice.h gui/src/terminal/LinuxTerminalEmulation.h gui/src/terminal/kpty.cpp gui/src/terminal/kpty.h gui/src/terminal/kpty_p.h gui/src/terminal/kptydevice.cpp gui/src/terminal/kptydevice.h |
diffstat | 11 files changed, 1222 insertions(+), 1270 deletions(-) [+] |
line wrap: on
line diff
--- a/gui/octave-gui.pro Tue Aug 23 18:06:45 2011 +0200 +++ b/gui/octave-gui.pro Tue Aug 23 18:22:27 2011 +0200 @@ -58,8 +58,6 @@ # Files associated with the project: SOURCES +=\ src/lexer/lexeroctavegui.cpp \ - src/terminal/kpty.cpp \ - src/terminal/kptydevice.cpp \ src/MainWindow.cpp \ src/OctaveTerminal.cpp \ src/VariablesDockWidget.cpp \ @@ -79,13 +77,12 @@ src/qirc/IRCClientImpl.cpp \ src/terminal/TerminalEmulation.cpp \ src/terminal/LinuxTerminalEmulation.cpp \ - src/backend/ReadlineAdapter.cpp + src/backend/ReadlineAdapter.cpp \ + src/terminal/KPty.cpp \ + src/terminal/KPtyDevice.cpp HEADERS += \ src/lexer/lexeroctavegui.h \ - src/terminal/kpty.h \ - src/terminal/kpty_p.h \ - src/terminal/kptydevice.h \ src/MainWindow.h \ src/OctaveTerminal.h \ src/VariablesDockWidget.h \ @@ -105,7 +102,9 @@ src/qirc/IRCClientImpl.h \ src/terminal/TerminalEmulation.h \ src/terminal/LinuxTerminalEmulation.h \ - src/backend/ReadlineAdapter.h + src/backend/ReadlineAdapter.h \ + src/terminal/KPtyDevice.h \ + src/terminal/KPty.h FORMS += \ src/SettingsDialog.ui
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui/src/terminal/KPty.cpp Tue Aug 23 18:22:27 2011 +0200 @@ -0,0 +1,342 @@ +/* + + This file is part of the KDE libraries + Copyright (C) 2002 Waldo Bastian <bastian@kde.org> + Copyright (C) 2002-2003,2007-2008 Oswald Buddenhagen <ossi@kde.org> + Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org> + Author Adriaan de Groot <groot@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KPty.h" +#include <fcntl.h> +#include <stdio.h> + +#include <pty.h> +#include <utmp.h> + + +/* for HP-UX (some versions) the extern C is needed, and for other + platforms it doesn't hurt */ +extern "C" +{ +#include <termios.h> +} + +#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) +#define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode) +#else +#if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__) || defined(__sun) +#define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode) +#else +#define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode) +#endif +#endif + +#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) +#define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode) +#else +#if defined(_HPUX_SOURCE) || defined(__CYGWIN__) || defined(__sun) +#define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode) +#else +#define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode) +#endif +#endif + +#include <QtCore/Q_PID> + +#define TTY_GROUP "tty" + +KPtyPrivate::KPtyPrivate (KPty * parent): +masterFd (-1), +slaveFd (-1), +ownMaster (true), +q_ptr (parent) +{ +} + +KPtyPrivate::~KPtyPrivate () +{ +} + +KPty::KPty (): +d_ptr (new KPtyPrivate (this)) +{ +} + +KPty::KPty (KPtyPrivate * d): +d_ptr (d) +{ + d_ptr->q_ptr = this; +} + +KPty::~KPty () +{ + close (); + delete d_ptr; +} + +bool +KPty::open () +{ + Q_D (KPty); + + if (d->masterFd >= 0) + return true; + + d->ownMaster = true; + + QByteArray ptyName; + + // Find a master pty that we can open //////////////////////////////// + + // Because not all the pty animals are created equal, they want to + // be opened by several different methods. + + // We try, as we know them, one by one. + + char ptsn[PATH_MAX]; + if (::openpty (&d->masterFd, &d->slaveFd, ptsn, 0, 0)) + { + d->masterFd = -1; + d->slaveFd = -1; + return false; + } + d->ttyName = ptsn; + + fcntl (d->masterFd, F_SETFD, FD_CLOEXEC); + fcntl (d->slaveFd, F_SETFD, FD_CLOEXEC); + + return true; +} + +bool +KPty::open (int fd) +{ + Q_D (KPty); + + if (d->masterFd >= 0) + { + return false; + } + + d->ownMaster = false; + + int ptyno; + if (!ioctl (fd, TIOCGPTN, &ptyno)) + { + char buf[32]; + sprintf (buf, "/dev/pts/%d", ptyno); + d->ttyName = buf; + } + else + { + return false; + } + + d->masterFd = fd; + if (!openSlave ()) + { + d->masterFd = -1; + return false; + } + + return true; +} + +void +KPty::closeSlave () +{ + Q_D (KPty); + + if (d->slaveFd < 0) + return; + ::close (d->slaveFd); + d->slaveFd = -1; +} + +bool +KPty::openSlave () +{ + Q_D (KPty); + + if (d->slaveFd >= 0) + return true; + if (d->masterFd < 0) + { + return false; + } + d->slaveFd =::open (d->ttyName.data (), O_RDWR | O_NOCTTY); + if (d->slaveFd < 0) + { + return false; + } + fcntl (d->slaveFd, F_SETFD, FD_CLOEXEC); + return true; +} + +void +KPty::close () +{ + Q_D (KPty); + + if (d->masterFd < 0) + return; + closeSlave (); + if (d->ownMaster) + { + ::close (d->masterFd); + } + d->masterFd = -1; +} + +void +KPty::setCTty () +{ + Q_D (KPty); + + // Setup job control ////////////////////////////////// + + // Become session leader, process group leader, + // and get rid of the old controlling terminal. + setsid (); + + // make our slave pty the new controlling terminal. + ioctl (d->slaveFd, TIOCSCTTY, 0); + + // make our new process group the foreground group on the pty + int pgrp = getpid (); + tcsetpgrp (d->slaveFd, pgrp); +} + +void +KPty::login (const char *user, const char *remotehost) +{ + struct utmp l_struct; + + memset (&l_struct, 0, sizeof (l_struct)); + // note: strncpy without terminators _is_ correct here. man 4 utmp + + if (user) + strncpy (l_struct.ut_name, user, sizeof (l_struct.ut_name)); + + if (remotehost) + { + strncpy (l_struct.ut_host, remotehost, sizeof (l_struct.ut_host)); + } + + l_struct.ut_time = time (0); + utmpname (_PATH_UTMP); + setutent (); + pututline (&l_struct); + endutent (); + updwtmp (_PATH_WTMP, &l_struct); +} + +void +KPty::logout () +{ + Q_D (KPty); + + const char *str_ptr = d->ttyName.data (); + if (!memcmp (str_ptr, "/dev/", 5)) + str_ptr += 5; + else + { + const char *sl_ptr = strrchr (str_ptr, '/'); + if (sl_ptr) + str_ptr = sl_ptr + 1; + } + + struct utmp l_struct, *ut; + + memset (&l_struct, 0, sizeof (l_struct)); + strncpy (l_struct.ut_line, str_ptr, sizeof (l_struct.ut_line)); + utmpname (_PATH_UTMP); + setutent (); + if ((ut = getutline (&l_struct))) + { + memset (ut->ut_name, 0, sizeof (*ut->ut_name)); + memset (ut->ut_host, 0, sizeof (*ut->ut_host)); + ut->ut_time = time (0); + pututline (ut); + } + endutent (); +} + +bool +KPty::tcGetAttr (struct::termios * ttmode) const +{ + Q_D (const KPty); + return _tcgetattr (d->masterFd, ttmode) == 0; +} + +bool +KPty::tcSetAttr (struct::termios * ttmode) +{ + Q_D (KPty); + return _tcsetattr (d->masterFd, ttmode) == 0; +} + +bool +KPty::setWinSize (int lines, int columns) +{ + Q_D (KPty); + + struct winsize winSize; + memset (&winSize, 0, sizeof (winSize)); + winSize.ws_row = (unsigned short) lines; + winSize.ws_col = (unsigned short) columns; + return ioctl (d->masterFd, TIOCSWINSZ, (char *) &winSize) == 0; +} + +bool +KPty::setEcho (bool echo) +{ + struct::termios ttmode; + if (!tcGetAttr (&ttmode)) + return false; + if (!echo) + ttmode.c_lflag &= ~ECHO; + else + ttmode.c_lflag |= ECHO; + return tcSetAttr (&ttmode); +} + +const char * +KPty::ttyName () const +{ + Q_D (const KPty); + + return d->ttyName.data (); +} + +int +KPty::masterFd () const +{ + Q_D (const KPty); + + return d->masterFd; +} + +int +KPty::slaveFd () const +{ + Q_D (const KPty); + + return d->slaveFd; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui/src/terminal/KPty.h Tue Aug 23 18:22:27 2011 +0200 @@ -0,0 +1,219 @@ +/* This file is part of the KDE libraries + + Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kpty_h +#define kpty_h + +#include <QtCore/qglobal.h> +#include <QByteArray> + +class KPty; +struct KPtyPrivate +{ + Q_DECLARE_PUBLIC (KPty) KPtyPrivate (KPty * parent); + virtual ~ KPtyPrivate (); + bool chownpty (bool grant); + + int masterFd; + int slaveFd; + bool ownMaster:1; + + QByteArray ttyName; + + KPty *q_ptr; +}; + +struct termios; + +/** + * Provides primitives for opening & closing a pseudo TTY pair, assigning the + * controlling TTY, utmp registration and setting various terminal attributes. + */ +class KPty +{ +Q_DECLARE_PRIVATE (KPty) public: + + /** + * Constructor + */ + KPty (); + + /** + * Destructor: + * + * If the pty is still open, it will be closed. Note, however, that + * an utmp registration is @em not undone. + */ + ~KPty (); + + /** + * Create a pty master/slave pair. + * + * @return true if a pty pair was successfully opened + */ + bool open (); + + /** + * Open using an existing pty master. + * + * @param fd an open pty master file descriptor. + * The ownership of the fd remains with the caller; + * it will not be automatically closed at any point. + * @return true if a pty pair was successfully opened + */ + bool open (int fd); + + /** + * Close the pty master/slave pair. + */ + void close (); + + /** + * Close the pty slave descriptor. + * + * When creating the pty, KPty also opens the slave and keeps it open. + * Consequently the master will never receive an EOF notification. + * Usually this is the desired behavior, as a closed pty slave can be + * reopened any time - unlike a pipe or socket. However, in some cases + * pipe-alike behavior might be desired. + * + * After this function was called, slaveFd() and setCTty() cannot be + * used. + */ + void closeSlave (); + + /** + * Open the pty slave descriptor. + * + * This undoes the effect of closeSlave(). + * + * @return true if the pty slave was successfully opened + */ + bool openSlave (); + + /** + * Creates a new session and process group and makes this pty the + * controlling tty. + */ + void setCTty (); + + /** + * Creates an utmp entry for the tty. + * This function must be called after calling setCTty and + * making this pty the stdin. + * @param user the user to be logged on + * @param remotehost the host from which the login is coming. This is + * @em not the local host. For remote logins it should be the hostname + * of the client. For local logins from inside an X session it should + * be the name of the X display. Otherwise it should be empty. + */ + void login (const char *user = 0, const char *remotehost = 0); + + /** + * Removes the utmp entry for this tty. + */ + void logout (); + + /** + * Wrapper around tcgetattr(3). + * + * This function can be used only while the PTY is open. + * You will need an #include <termios.h> to do anything useful + * with it. + * + * @param ttmode a pointer to a termios structure. + * Note: when declaring ttmode, @c struct @c ::termios must be used - + * without the '::' some version of HP-UX thinks, this declares + * the struct in your class, in your method. + * @return @c true on success, false otherwise + */ + bool tcGetAttr (struct::termios * ttmode) const; + + /** + * Wrapper around tcsetattr(3) with mode TCSANOW. + * + * This function can be used only while the PTY is open. + * + * @param ttmode a pointer to a termios structure. + * @return @c true on success, false otherwise. Note that success means + * that @em at @em least @em one attribute could be set. + */ + bool tcSetAttr (struct::termios * ttmode); + + /** + * Change the logical (screen) size of the pty. + * The default is 24 lines by 80 columns. + * + * This function can be used only while the PTY is open. + * + * @param lines the number of rows + * @param columns the number of columns + * @return @c true on success, false otherwise + */ + bool setWinSize (int lines, int columns); + + /** + * Set whether the pty should echo input. + * + * Echo is on by default. + * If the output of automatically fed (non-interactive) PTY clients + * needs to be parsed, disabling echo often makes it much simpler. + * + * This function can be used only while the PTY is open. + * + * @param echo true if input should be echoed. + * @return @c true on success, false otherwise + */ + bool setEcho (bool echo); + + /** + * @return the name of the slave pty device. + * + * This function should be called only while the pty is open. + */ + const char *ttyName () const; + + /** + * @return the file descriptor of the master pty + * + * This function should be called only while the pty is open. + */ + int masterFd () const; + + /** + * @return the file descriptor of the slave pty + * + * This function should be called only while the pty slave is open. + */ + int slaveFd () const; + +protected: + /** + * @internal + */ + KPty (KPtyPrivate * d); + + /** + * @internal + */ + KPtyPrivate *const d_ptr; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui/src/terminal/KPtyDevice.cpp Tue Aug 23 18:22:27 2011 +0200 @@ -0,0 +1,340 @@ +/* + + This file is part of the KDE libraries + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org> + Author Adriaan de Groot <groot@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "KPtyDevice.h" + +#include <QtCore/QSocketNotifier> + +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <termios.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#define PTY_BYTES_AVAILABLE TIOCINQ + +////////////////// +// private data // +////////////////// + +// Lifted from Qt. I don't think they would mind. ;) +// Re-lift again from Qt whenever a proper replacement for pthread_once appears +static void +qt_ignore_sigpipe () +{ + static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER (0); + if (atom.testAndSetRelaxed (0, 1)) + { + struct sigaction noaction; + memset (&noaction, 0, sizeof (noaction)); + noaction.sa_handler = SIG_IGN; + sigaction (SIGPIPE, &noaction, 0); + } +} + +#define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR) + +bool +KPtyDevicePrivate::_k_canRead () +{ + Q_Q (KPtyDevice); + qint64 readBytes = 0; + + + int available; + if (!::ioctl (q->masterFd (), PTY_BYTES_AVAILABLE, (char *) &available)) + { + char *ptr = readBuffer.reserve (available); + // Useless block braces except in Solaris + { + NO_INTR (readBytes, read (q->masterFd (), ptr, available)); + } + if (readBytes < 0) + { + readBuffer.unreserve (available); + return false; + } + readBuffer.unreserve (available - readBytes); // *should* be a no-op + } + + if (!readBytes) + { + readNotifier->setEnabled (false); + return false; + } + else + { + if (!emittedReadyRead) + { + emittedReadyRead = true; + emit q->readyRead (); + emittedReadyRead = false; + } + return true; + } +} + +bool +KPtyDevicePrivate::_k_canWrite () +{ + Q_Q (KPtyDevice); + + writeNotifier->setEnabled (false); + if (writeBuffer.isEmpty ()) + return false; + + qt_ignore_sigpipe (); + int wroteBytes; + NO_INTR (wroteBytes, + write (q->masterFd (), + writeBuffer.readPointer (), writeBuffer.readSize ())); + if (wroteBytes < 0) + { + return false; + } + writeBuffer.free (wroteBytes); + + if (!emittedBytesWritten) + { + emittedBytesWritten = true; + emit q->bytesWritten (wroteBytes); + emittedBytesWritten = false; + } + + if (!writeBuffer.isEmpty ()) + writeNotifier->setEnabled (true); + return true; +} + +#ifndef timeradd +// Lifted from GLIBC +#define timeradd(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } while (0) +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +bool +KPtyDevicePrivate::doWait (int msecs, bool reading) +{ + Q_Q (KPtyDevice); + struct timeval tv, *tvp; + + if (msecs < 0) + tvp = 0; + else + { + tv.tv_sec = msecs / 1000; + tv.tv_usec = (msecs % 1000) * 1000; + tvp = &tv; + } + + while (reading ? readNotifier->isEnabled () : !writeBuffer.isEmpty ()) + { + fd_set rfds; + fd_set wfds; + + FD_ZERO (&rfds); + FD_ZERO (&wfds); + + if (readNotifier->isEnabled ()) + FD_SET (q->masterFd (), &rfds); + if (!writeBuffer.isEmpty ()) + FD_SET (q->masterFd (), &wfds); + + switch (select (q->masterFd () + 1, &rfds, &wfds, 0, tvp)) + { + case -1: + if (errno == EINTR) + break; + return false; + case 0: + return false; + default: + if (FD_ISSET (q->masterFd (), &rfds)) + { + bool canRead = _k_canRead (); + if (reading && canRead) + return true; + } + if (FD_ISSET (q->masterFd (), &wfds)) + { + bool canWrite = _k_canWrite (); + if (!reading) + return canWrite; + } + break; + } + } + return false; +} + +void +KPtyDevicePrivate::finishOpen (QIODevice::OpenMode mode) +{ + Q_Q (KPtyDevice); + + q->QIODevice::open (mode); + fcntl (q->masterFd (), F_SETFL, O_NONBLOCK); + readBuffer.clear (); + readNotifier = + new QSocketNotifier (q->masterFd (), QSocketNotifier::Read, q); + writeNotifier = + new QSocketNotifier (q->masterFd (), QSocketNotifier::Write, q); + QObject::connect (readNotifier, SIGNAL (activated (int)), q, + SLOT (_k_canRead ())); + QObject::connect (writeNotifier, SIGNAL (activated (int)), q, + SLOT (_k_canWrite ())); + readNotifier->setEnabled (true); +} + +KPtyDevice::KPtyDevice (QObject * parent): +QIODevice (parent), KPty (new KPtyDevicePrivate (this)) +{ +} + +KPtyDevice::~KPtyDevice () +{ + close (); +} + +bool +KPtyDevice::open (OpenMode mode) +{ + Q_D (KPtyDevice); + + if (masterFd () >= 0) + return true; + + if (!KPty::open ()) + { + return false; + } + + d->finishOpen (mode); + + return true; +} + +bool +KPtyDevice::open (int fd, OpenMode mode) +{ + Q_D (KPtyDevice); + + if (!KPty::open (fd)) + { + return false; + } + + d->finishOpen (mode); + + return true; +} + +void +KPtyDevice::close () +{ + Q_D (KPtyDevice); + + if (masterFd () < 0) + return; + + delete d->readNotifier; + delete d->writeNotifier; + + QIODevice::close (); + + KPty::close (); +} + +bool +KPtyDevice::canReadLine () const +{ + Q_D (const KPtyDevice); + return QIODevice::canReadLine () || d->readBuffer.canReadLine (); +} + +bool +KPtyDevice::atEnd () const +{ + Q_D (const KPtyDevice); + return QIODevice::atEnd () && d->readBuffer.isEmpty (); +} + +qint64 +KPtyDevice::bytesAvailable () const +{ + Q_D (const KPtyDevice); + return QIODevice::bytesAvailable () + d->readBuffer.size (); +} + +qint64 +KPtyDevice::bytesToWrite () const +{ + Q_D (const KPtyDevice); + return d->writeBuffer.size (); +} + +// protected +qint64 +KPtyDevice::readData (char *data, qint64 maxlen) +{ + Q_D (KPtyDevice); + return d->readBuffer.read (data, (int) qMin < qint64 > (maxlen, KMAXINT)); +} + +// protected +qint64 +KPtyDevice::readLineData (char *data, qint64 maxlen) +{ + Q_D (KPtyDevice); + return d->readBuffer.readLine (data, + (int) qMin < qint64 > (maxlen, KMAXINT)); +} + +// protected +qint64 +KPtyDevice::writeData (const char *data, qint64 len) +{ + Q_D (KPtyDevice); + Q_ASSERT (len <= KMAXINT); + + d->writeBuffer.write (data, len); + d->writeNotifier->setEnabled (true); + return len; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui/src/terminal/KPtyDevice.h Tue Aug 23 18:22:27 2011 +0200 @@ -0,0 +1,314 @@ +/* This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kptydev_h +#define kptydev_h + +struct KPtyDevicePrivate; + +#include "KPty.h" +#include <QtCore/QIODevice> +#include <QSocketNotifier> + +#define Q_DECLARE_PRIVATE_MI(Class, SuperClass) \ + inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(SuperClass::d_ptr); } \ + inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(SuperClass::d_ptr); } \ + friend class Class##Private; + +/** + * Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc. + */ +class KPtyDevice:public QIODevice, public KPty +{ +Q_OBJECT Q_DECLARE_PRIVATE_MI (KPtyDevice, KPty) public: + + /** + * Constructor + */ + KPtyDevice (QObject * parent = 0); + + /** + * Destructor: + * + * If the pty is still open, it will be closed. Note, however, that + * an utmp registration is @em not undone. + */ + virtual ~ KPtyDevice (); + + /** + * Create a pty master/slave pair. + * + * @return true if a pty pair was successfully opened + */ + virtual bool open (OpenMode mode = ReadWrite | Unbuffered); + + /** + * Open using an existing pty master. The ownership of the fd + * remains with the caller, i.e., close() will not close the fd. + * + * This is useful if you wish to attach a secondary "controller" to an + * existing pty device such as a terminal widget. + * Note that you will need to use setSuspended() on both devices to + * control which one gets the incoming data from the pty. + * + * @param fd an open pty master file descriptor. + * @param mode the device mode to open the pty with. + * @return true if a pty pair was successfully opened + */ + bool open (int fd, OpenMode mode = ReadWrite | Unbuffered); + + /** + * Close the pty master/slave pair. + */ + virtual void close (); + + /** + * @reimp + */ + bool canReadLine () const; + + /** + * @reimp + */ + bool atEnd () const; + + /** + * @reimp + */ + qint64 bytesAvailable () const; + + /** + * @reimp + */ + qint64 bytesToWrite () const; + +protected: + virtual qint64 readData (char *data, qint64 maxSize); + virtual qint64 readLineData (char *data, qint64 maxSize); + virtual qint64 writeData (const char *data, qint64 maxSize); + +private: + Q_PRIVATE_SLOT (d_func (), bool _k_canRead ()) + Q_PRIVATE_SLOT (d_func (), bool _k_canWrite ())}; + +#define KMAXINT ((int)(~0U >> 1)) + +///////////////////////////////////////////////////// +// Helper. Remove when QRingBuffer becomes public. // +///////////////////////////////////////////////////// + +#include <QtCore/qbytearray.h> +#include <QtCore/qlinkedlist.h> + +#define CHUNKSIZE 4096 + +class KRingBuffer +{ +public: + KRingBuffer () + { + clear (); + } + + void clear () + { + buffers.clear (); + QByteArray tmp; + tmp.resize (CHUNKSIZE); + buffers << tmp; + head = tail = 0; + totalSize = 0; + } + + inline bool isEmpty () const + { + return buffers.count () == 1 && !tail; + } + + inline int size () const + { + return totalSize; + } + + inline int readSize () const + { + return (buffers.count () == 1 ? tail : buffers.first ().size ()) - head; + } + + inline const char *readPointer () const + { + Q_ASSERT (totalSize > 0); + return buffers.first ().constData () + head; + } + + void free (int bytes) + { + totalSize -= bytes; + Q_ASSERT (totalSize >= 0); + + forever + { + int nbs = readSize (); + + if (bytes < nbs) + { + head += bytes; + if (head == tail && buffers.count () == 1) + { + buffers.first ().resize (CHUNKSIZE); + head = tail = 0; + } + break; + } + + bytes -= nbs; + if (buffers.count () == 1) + { + buffers.first ().resize (CHUNKSIZE); + head = tail = 0; + break; + } + + buffers.removeFirst (); + head = 0; + } + } + + char *reserve (int bytes) + { + totalSize += bytes; + + char *ptr; + if (tail + bytes <= buffers.last ().size ()) + { + ptr = buffers.last ().data () + tail; + tail += bytes; + } + else + { + buffers.last ().resize (tail); + QByteArray tmp; + tmp.resize (qMax (CHUNKSIZE, bytes)); + ptr = tmp.data (); + buffers << tmp; + tail = bytes; + } + return ptr; + } + + // release a trailing part of the last reservation + inline void unreserve (int bytes) + { + totalSize -= bytes; + tail -= bytes; + } + + inline void write (const char *data, int len) + { + memcpy (reserve (len), data, len); + } + + // Find the first occurrence of c and return the index after it. + // If c is not found until maxLength, maxLength is returned, provided + // it is smaller than the buffer size. Otherwise -1 is returned. + int indexAfter (char c, int maxLength = KMAXINT) const + { + int index = 0; + int start = head; + QLinkedList < QByteArray >::ConstIterator it = buffers.begin (); + forever + { + if (!maxLength) + return index; + if (index == size ()) + return -1; + const QByteArray & buf = *it; + ++it; + int len = qMin ((it == buffers.end ()? tail : buf.size ()) - start, + maxLength); + const char *ptr = buf.data () + start; + if (const char *rptr = (const char *)memchr (ptr, c, len)) + return index + (rptr - ptr) + 1; + index += len; + maxLength -= len; + start = 0; + } + } + + inline int lineSize (int maxLength = KMAXINT) const + { + return indexAfter ('\n', maxLength); + } + + inline bool canReadLine () const + { + return lineSize () != -1; + } + + int read (char *data, int maxLength) + { + int bytesToRead = qMin (size (), maxLength); + int readSoFar = 0; + while (readSoFar < bytesToRead) + { + const char *ptr = readPointer (); + int bs = qMin (bytesToRead - readSoFar, readSize ()); + memcpy (data + readSoFar, ptr, bs); + readSoFar += bs; + free (bs); + } + return readSoFar; + } + + int readLine (char *data, int maxLength) + { + return read (data, lineSize (qMin (maxLength, size ()))); + } + +private: + QLinkedList < QByteArray > buffers; + int head, tail; + int totalSize; +}; + +struct KPtyDevicePrivate:public KPtyPrivate +{ + Q_DECLARE_PUBLIC (KPtyDevice) + KPtyDevicePrivate (KPty * parent):KPtyPrivate (parent), + emittedReadyRead (false), emittedBytesWritten (false), + readNotifier (0), writeNotifier (0) + { + } + + bool _k_canRead (); + bool _k_canWrite (); + + bool doWait (int msecs, bool reading); + void finishOpen (QIODevice::OpenMode mode); + + bool emittedReadyRead; + bool emittedBytesWritten; + QSocketNotifier *readNotifier; + QSocketNotifier *writeNotifier; + KRingBuffer readBuffer; + KRingBuffer writeBuffer; +}; + +#endif
--- a/gui/src/terminal/LinuxTerminalEmulation.h Tue Aug 23 18:06:45 2011 +0200 +++ b/gui/src/terminal/LinuxTerminalEmulation.h Tue Aug 23 18:22:27 2011 +0200 @@ -6,7 +6,7 @@ #include "unistd.h" #include <assert.h> #include <cstdio> -#include "kptydevice.h" +#include "KPtyDevice.h" class LinuxTerminalEmulation : public TerminalEmulation {
--- a/gui/src/terminal/kpty.cpp Tue Aug 23 18:06:45 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,340 +0,0 @@ -/* - - This file is part of the KDE libraries - Copyright (C) 2002 Waldo Bastian <bastian@kde.org> - Copyright (C) 2002-2003,2007-2008 Oswald Buddenhagen <ossi@kde.org> - Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org> - Author Adriaan de Groot <groot@kde.org> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "kpty_p.h" -#include <fcntl.h> -#include <stdio.h> -#include <pty.h> -#include <utmp.h> - -/* for HP-UX (some versions) the extern C is needed, and for other - platforms it doesn't hurt */ -extern "C" -{ -#include <termios.h> -} - -#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) -#define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode) -#else -#if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__) || defined(__sun) -#define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode) -#else -#define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode) -#endif -#endif - -#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) -#define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode) -#else -#if defined(_HPUX_SOURCE) || defined(__CYGWIN__) || defined(__sun) -#define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode) -#else -#define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode) -#endif -#endif - -#include <QtCore/Q_PID> - -#define TTY_GROUP "tty" - -KPtyPrivate::KPtyPrivate (KPty * parent): -masterFd (-1), -slaveFd (-1), -ownMaster (true), -q_ptr (parent) -{ -} - -KPtyPrivate::~KPtyPrivate () -{ -} - -KPty::KPty (): -d_ptr (new KPtyPrivate (this)) -{ -} - -KPty::KPty (KPtyPrivate * d): -d_ptr (d) -{ - d_ptr->q_ptr = this; -} - -KPty::~KPty () -{ - close (); - delete d_ptr; -} - -bool -KPty::open () -{ - Q_D (KPty); - - if (d->masterFd >= 0) - return true; - - d->ownMaster = true; - - QByteArray ptyName; - - // Find a master pty that we can open //////////////////////////////// - - // Because not all the pty animals are created equal, they want to - // be opened by several different methods. - - // We try, as we know them, one by one. - - char ptsn[PATH_MAX]; - if (::openpty (&d->masterFd, &d->slaveFd, ptsn, 0, 0)) - { - d->masterFd = -1; - d->slaveFd = -1; - return false; - } - d->ttyName = ptsn; - - fcntl (d->masterFd, F_SETFD, FD_CLOEXEC); - fcntl (d->slaveFd, F_SETFD, FD_CLOEXEC); - - return true; -} - -bool -KPty::open (int fd) -{ - Q_D (KPty); - - if (d->masterFd >= 0) - { - return false; - } - - d->ownMaster = false; - - int ptyno; - if (!ioctl (fd, TIOCGPTN, &ptyno)) - { - char buf[32]; - sprintf (buf, "/dev/pts/%d", ptyno); - d->ttyName = buf; - } - else - { - return false; - } - - d->masterFd = fd; - if (!openSlave ()) - { - d->masterFd = -1; - return false; - } - - return true; -} - -void -KPty::closeSlave () -{ - Q_D (KPty); - - if (d->slaveFd < 0) - return; - ::close (d->slaveFd); - d->slaveFd = -1; -} - -bool -KPty::openSlave () -{ - Q_D (KPty); - - if (d->slaveFd >= 0) - return true; - if (d->masterFd < 0) - { - return false; - } - d->slaveFd =::open (d->ttyName.data (), O_RDWR | O_NOCTTY); - if (d->slaveFd < 0) - { - return false; - } - fcntl (d->slaveFd, F_SETFD, FD_CLOEXEC); - return true; -} - -void -KPty::close () -{ - Q_D (KPty); - - if (d->masterFd < 0) - return; - closeSlave (); - if (d->ownMaster) - { - ::close (d->masterFd); - } - d->masterFd = -1; -} - -void -KPty::setCTty () -{ - Q_D (KPty); - - // Setup job control ////////////////////////////////// - - // Become session leader, process group leader, - // and get rid of the old controlling terminal. - setsid (); - - // make our slave pty the new controlling terminal. - ioctl (d->slaveFd, TIOCSCTTY, 0); - - // make our new process group the foreground group on the pty - int pgrp = getpid (); - tcsetpgrp (d->slaveFd, pgrp); -} - -void -KPty::login (const char *user, const char *remotehost) -{ - struct utmp l_struct; - - memset (&l_struct, 0, sizeof (l_struct)); - // note: strncpy without terminators _is_ correct here. man 4 utmp - - if (user) - strncpy (l_struct.ut_name, user, sizeof (l_struct.ut_name)); - - if (remotehost) - { - strncpy (l_struct.ut_host, remotehost, sizeof (l_struct.ut_host)); - } - - l_struct.ut_time = time (0); - utmpname (_PATH_UTMP); - setutent (); - pututline (&l_struct); - endutent (); - updwtmp (_PATH_WTMP, &l_struct); -} - -void -KPty::logout () -{ - Q_D (KPty); - - const char *str_ptr = d->ttyName.data (); - if (!memcmp (str_ptr, "/dev/", 5)) - str_ptr += 5; - else - { - const char *sl_ptr = strrchr (str_ptr, '/'); - if (sl_ptr) - str_ptr = sl_ptr + 1; - } - - struct utmp l_struct, *ut; - - memset (&l_struct, 0, sizeof (l_struct)); - strncpy (l_struct.ut_line, str_ptr, sizeof (l_struct.ut_line)); - utmpname (_PATH_UTMP); - setutent (); - if ((ut = getutline (&l_struct))) - { - memset (ut->ut_name, 0, sizeof (*ut->ut_name)); - memset (ut->ut_host, 0, sizeof (*ut->ut_host)); - ut->ut_time = time (0); - pututline (ut); - } - endutent (); -} - -bool -KPty::tcGetAttr (struct::termios * ttmode) const -{ - Q_D (const KPty); - return _tcgetattr (d->masterFd, ttmode) == 0; -} - -bool -KPty::tcSetAttr (struct::termios * ttmode) -{ - Q_D (KPty); - return _tcsetattr (d->masterFd, ttmode) == 0; -} - -bool -KPty::setWinSize (int lines, int columns) -{ - Q_D (KPty); - - struct winsize winSize; - memset (&winSize, 0, sizeof (winSize)); - winSize.ws_row = (unsigned short) lines; - winSize.ws_col = (unsigned short) columns; - return ioctl (d->masterFd, TIOCSWINSZ, (char *) &winSize) == 0; -} - -bool -KPty::setEcho (bool echo) -{ - struct::termios ttmode; - if (!tcGetAttr (&ttmode)) - return false; - if (!echo) - ttmode.c_lflag &= ~ECHO; - else - ttmode.c_lflag |= ECHO; - return tcSetAttr (&ttmode); -} - -const char * -KPty::ttyName () const -{ - Q_D (const KPty); - - return d->ttyName.data (); -} - -int -KPty::masterFd () const -{ - Q_D (const KPty); - - return d->masterFd; -} - -int -KPty::slaveFd () const -{ - Q_D (const KPty); - - return d->slaveFd; -}
--- a/gui/src/terminal/kpty.h Tue Aug 23 18:06:45 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/* This file is part of the KDE libraries - - Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef kpty_h -#define kpty_h -#include <QtCore/qglobal.h> - -struct KPtyPrivate; -struct termios; - -/** - * Provides primitives for opening & closing a pseudo TTY pair, assigning the - * controlling TTY, utmp registration and setting various terminal attributes. - */ -class KPty -{ -Q_DECLARE_PRIVATE (KPty) public: - - /** - * Constructor - */ - KPty (); - - /** - * Destructor: - * - * If the pty is still open, it will be closed. Note, however, that - * an utmp registration is @em not undone. - */ - ~KPty (); - - /** - * Create a pty master/slave pair. - * - * @return true if a pty pair was successfully opened - */ - bool open (); - - /** - * Open using an existing pty master. - * - * @param fd an open pty master file descriptor. - * The ownership of the fd remains with the caller; - * it will not be automatically closed at any point. - * @return true if a pty pair was successfully opened - */ - bool open (int fd); - - /** - * Close the pty master/slave pair. - */ - void close (); - - /** - * Close the pty slave descriptor. - * - * When creating the pty, KPty also opens the slave and keeps it open. - * Consequently the master will never receive an EOF notification. - * Usually this is the desired behavior, as a closed pty slave can be - * reopened any time - unlike a pipe or socket. However, in some cases - * pipe-alike behavior might be desired. - * - * After this function was called, slaveFd() and setCTty() cannot be - * used. - */ - void closeSlave (); - - /** - * Open the pty slave descriptor. - * - * This undoes the effect of closeSlave(). - * - * @return true if the pty slave was successfully opened - */ - bool openSlave (); - - /** - * Creates a new session and process group and makes this pty the - * controlling tty. - */ - void setCTty (); - - /** - * Creates an utmp entry for the tty. - * This function must be called after calling setCTty and - * making this pty the stdin. - * @param user the user to be logged on - * @param remotehost the host from which the login is coming. This is - * @em not the local host. For remote logins it should be the hostname - * of the client. For local logins from inside an X session it should - * be the name of the X display. Otherwise it should be empty. - */ - void login (const char *user = 0, const char *remotehost = 0); - - /** - * Removes the utmp entry for this tty. - */ - void logout (); - - /** - * Wrapper around tcgetattr(3). - * - * This function can be used only while the PTY is open. - * You will need an #include <termios.h> to do anything useful - * with it. - * - * @param ttmode a pointer to a termios structure. - * Note: when declaring ttmode, @c struct @c ::termios must be used - - * without the '::' some version of HP-UX thinks, this declares - * the struct in your class, in your method. - * @return @c true on success, false otherwise - */ - bool tcGetAttr (struct::termios * ttmode) const; - - /** - * Wrapper around tcsetattr(3) with mode TCSANOW. - * - * This function can be used only while the PTY is open. - * - * @param ttmode a pointer to a termios structure. - * @return @c true on success, false otherwise. Note that success means - * that @em at @em least @em one attribute could be set. - */ - bool tcSetAttr (struct::termios * ttmode); - - /** - * Change the logical (screen) size of the pty. - * The default is 24 lines by 80 columns. - * - * This function can be used only while the PTY is open. - * - * @param lines the number of rows - * @param columns the number of columns - * @return @c true on success, false otherwise - */ - bool setWinSize (int lines, int columns); - - /** - * Set whether the pty should echo input. - * - * Echo is on by default. - * If the output of automatically fed (non-interactive) PTY clients - * needs to be parsed, disabling echo often makes it much simpler. - * - * This function can be used only while the PTY is open. - * - * @param echo true if input should be echoed. - * @return @c true on success, false otherwise - */ - bool setEcho (bool echo); - - /** - * @return the name of the slave pty device. - * - * This function should be called only while the pty is open. - */ - const char *ttyName () const; - - /** - * @return the file descriptor of the master pty - * - * This function should be called only while the pty is open. - */ - int masterFd () const; - - /** - * @return the file descriptor of the slave pty - * - * This function should be called only while the pty slave is open. - */ - int slaveFd () const; - -protected: - /** - * @internal - */ - KPty (KPtyPrivate * d); - - /** - * @internal - */ - KPtyPrivate *const d_ptr; -}; - -#endif
--- a/gui/src/terminal/kpty_p.h Tue Aug 23 18:06:45 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* This file is part of the KDE libraries - - Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef kpty_p_h -#define kpty_p_h - -#include "kpty.h" - -#if defined(Q_OS_MAC) -#define HAVE_UTIL_H -#define HAVE_UTMPX -#define _UTMPX_COMPAT -#define HAVE_PTSNAME -#define HAVE_SYS_TIME_H -#else -#define HAVE_PTY_H -#endif - -#define HAVE_OPENPTY - -#include <QtCore/QByteArray> - -struct KPtyPrivate -{ - Q_DECLARE_PUBLIC (KPty) KPtyPrivate (KPty * parent); - virtual ~ KPtyPrivate (); -#ifndef HAVE_OPENPTY - bool chownpty (bool grant); -#endif - - int masterFd; - int slaveFd; - bool ownMaster:1; - - QByteArray ttyName; - - KPty *q_ptr; -}; - -#endif
--- a/gui/src/terminal/kptydevice.cpp Tue Aug 23 18:06:45 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,347 +0,0 @@ -/* - - This file is part of the KDE libraries - Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> - Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org> - Author Adriaan de Groot <groot@kde.org> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "kptydevice.h" -#include "kpty_p.h" -#define i18n - -#include <QtCore/QSocketNotifier> - -#include <unistd.h> -#include <errno.h> -#include <signal.h> -#include <termios.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#define PTY_BYTES_AVAILABLE TIOCINQ - -////////////////// -// private data // -////////////////// - -// Lifted from Qt. I don't think they would mind. ;) -// Re-lift again from Qt whenever a proper replacement for pthread_once appears -static void -qt_ignore_sigpipe () -{ - static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER (0); - if (atom.testAndSetRelaxed (0, 1)) - { - struct sigaction noaction; - memset (&noaction, 0, sizeof (noaction)); - noaction.sa_handler = SIG_IGN; - sigaction (SIGPIPE, &noaction, 0); - } -} - -#define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR) - -bool -KPtyDevicePrivate::_k_canRead () -{ - Q_Q (KPtyDevice); - qint64 readBytes = 0; - - - int available; - if (!::ioctl (q->masterFd (), PTY_BYTES_AVAILABLE, (char *) &available)) - { - char *ptr = readBuffer.reserve (available); - // Useless block braces except in Solaris - { - NO_INTR (readBytes, read (q->masterFd (), ptr, available)); - } - if (readBytes < 0) - { - readBuffer.unreserve (available); - q->setErrorString (i18n ("Error reading from PTY")); - return false; - } - readBuffer.unreserve (available - readBytes); // *should* be a no-op - } - - if (!readBytes) - { - readNotifier->setEnabled (false); - return false; - } - else - { - if (!emittedReadyRead) - { - emittedReadyRead = true; - emit q->readyRead (); - emittedReadyRead = false; - } - return true; - } -} - -bool -KPtyDevicePrivate::_k_canWrite () -{ - Q_Q (KPtyDevice); - - writeNotifier->setEnabled (false); - if (writeBuffer.isEmpty ()) - return false; - - qt_ignore_sigpipe (); - int wroteBytes; - NO_INTR (wroteBytes, - write (q->masterFd (), - writeBuffer.readPointer (), writeBuffer.readSize ())); - if (wroteBytes < 0) - { - q->setErrorString (i18n ("Error writing to PTY")); - return false; - } - writeBuffer.free (wroteBytes); - - if (!emittedBytesWritten) - { - emittedBytesWritten = true; - emit q->bytesWritten (wroteBytes); - emittedBytesWritten = false; - } - - if (!writeBuffer.isEmpty ()) - writeNotifier->setEnabled (true); - return true; -} - -#ifndef timeradd -// Lifted from GLIBC -#define timeradd(a, b, result) \ - do { \ - (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ - if ((result)->tv_usec >= 1000000) { \ - ++(result)->tv_sec; \ - (result)->tv_usec -= 1000000; \ - } \ - } while (0) -#define timersub(a, b, result) \ - do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ - } \ - } while (0) -#endif - -bool -KPtyDevicePrivate::doWait (int msecs, bool reading) -{ - Q_Q (KPtyDevice); - struct timeval tv, *tvp; - - if (msecs < 0) - tvp = 0; - else - { - tv.tv_sec = msecs / 1000; - tv.tv_usec = (msecs % 1000) * 1000; - tvp = &tv; - } - - while (reading ? readNotifier->isEnabled () : !writeBuffer.isEmpty ()) - { - fd_set rfds; - fd_set wfds; - - FD_ZERO (&rfds); - FD_ZERO (&wfds); - - if (readNotifier->isEnabled ()) - FD_SET (q->masterFd (), &rfds); - if (!writeBuffer.isEmpty ()) - FD_SET (q->masterFd (), &wfds); - - switch (select (q->masterFd () + 1, &rfds, &wfds, 0, tvp)) - { - case -1: - if (errno == EINTR) - break; - return false; - case 0: - q->setErrorString (i18n ("PTY operation timed out")); - return false; - default: - if (FD_ISSET (q->masterFd (), &rfds)) - { - bool canRead = _k_canRead (); - if (reading && canRead) - return true; - } - if (FD_ISSET (q->masterFd (), &wfds)) - { - bool canWrite = _k_canWrite (); - if (!reading) - return canWrite; - } - break; - } - } - return false; -} - -void -KPtyDevicePrivate::finishOpen (QIODevice::OpenMode mode) -{ - Q_Q (KPtyDevice); - - q->QIODevice::open (mode); - fcntl (q->masterFd (), F_SETFL, O_NONBLOCK); - readBuffer.clear (); - readNotifier = - new QSocketNotifier (q->masterFd (), QSocketNotifier::Read, q); - writeNotifier = - new QSocketNotifier (q->masterFd (), QSocketNotifier::Write, q); - QObject::connect (readNotifier, SIGNAL (activated (int)), q, - SLOT (_k_canRead ())); - QObject::connect (writeNotifier, SIGNAL (activated (int)), q, - SLOT (_k_canWrite ())); - readNotifier->setEnabled (true); -} - -KPtyDevice::KPtyDevice (QObject * parent): -QIODevice (parent), KPty (new KPtyDevicePrivate (this)) -{ -} - -KPtyDevice::~KPtyDevice () -{ - close (); -} - -bool -KPtyDevice::open (OpenMode mode) -{ - Q_D (KPtyDevice); - - if (masterFd () >= 0) - return true; - - if (!KPty::open ()) - { - setErrorString (i18n ("Error opening PTY")); - return false; - } - - d->finishOpen (mode); - - return true; -} - -bool -KPtyDevice::open (int fd, OpenMode mode) -{ - Q_D (KPtyDevice); - - if (!KPty::open (fd)) - { - setErrorString (i18n ("Error opening PTY")); - return false; - } - - d->finishOpen (mode); - - return true; -} - -void -KPtyDevice::close () -{ - Q_D (KPtyDevice); - - if (masterFd () < 0) - return; - - delete d->readNotifier; - delete d->writeNotifier; - - QIODevice::close (); - - KPty::close (); -} - -bool -KPtyDevice::canReadLine () const -{ - Q_D (const KPtyDevice); - return QIODevice::canReadLine () || d->readBuffer.canReadLine (); -} - -bool -KPtyDevice::atEnd () const -{ - Q_D (const KPtyDevice); - return QIODevice::atEnd () && d->readBuffer.isEmpty (); -} - -qint64 -KPtyDevice::bytesAvailable () const -{ - Q_D (const KPtyDevice); - return QIODevice::bytesAvailable () + d->readBuffer.size (); -} - -qint64 -KPtyDevice::bytesToWrite () const -{ - Q_D (const KPtyDevice); - return d->writeBuffer.size (); -} - -// protected -qint64 -KPtyDevice::readData (char *data, qint64 maxlen) -{ - Q_D (KPtyDevice); - return d->readBuffer.read (data, (int) qMin < qint64 > (maxlen, KMAXINT)); -} - -// protected -qint64 -KPtyDevice::readLineData (char *data, qint64 maxlen) -{ - Q_D (KPtyDevice); - return d->readBuffer.readLine (data, - (int) qMin < qint64 > (maxlen, KMAXINT)); -} - -// protected -qint64 -KPtyDevice::writeData (const char *data, qint64 len) -{ - Q_D (KPtyDevice); - Q_ASSERT (len <= KMAXINT); - - d->writeBuffer.write (data, len); - d->writeNotifier->setEnabled (true); - return len; -}
--- a/gui/src/terminal/kptydevice.h Tue Aug 23 18:06:45 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,316 +0,0 @@ -/* This file is part of the KDE libraries - - Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef kptydev_h -#define kptydev_h - -struct KPtyPrivate; -struct KPtyDevicePrivate; - -#include "kpty.h" -#include "kpty_p.h" -#include <QtCore/QIODevice> -#include <QSocketNotifier> - -#define Q_DECLARE_PRIVATE_MI(Class, SuperClass) \ - inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(SuperClass::d_ptr); } \ - inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(SuperClass::d_ptr); } \ - friend class Class##Private; - -/** - * Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc. - */ -class KPtyDevice:public QIODevice, public KPty -{ -Q_OBJECT Q_DECLARE_PRIVATE_MI (KPtyDevice, KPty) public: - - /** - * Constructor - */ - KPtyDevice (QObject * parent = 0); - - /** - * Destructor: - * - * If the pty is still open, it will be closed. Note, however, that - * an utmp registration is @em not undone. - */ - virtual ~ KPtyDevice (); - - /** - * Create a pty master/slave pair. - * - * @return true if a pty pair was successfully opened - */ - virtual bool open (OpenMode mode = ReadWrite | Unbuffered); - - /** - * Open using an existing pty master. The ownership of the fd - * remains with the caller, i.e., close() will not close the fd. - * - * This is useful if you wish to attach a secondary "controller" to an - * existing pty device such as a terminal widget. - * Note that you will need to use setSuspended() on both devices to - * control which one gets the incoming data from the pty. - * - * @param fd an open pty master file descriptor. - * @param mode the device mode to open the pty with. - * @return true if a pty pair was successfully opened - */ - bool open (int fd, OpenMode mode = ReadWrite | Unbuffered); - - /** - * Close the pty master/slave pair. - */ - virtual void close (); - - /** - * @reimp - */ - bool canReadLine () const; - - /** - * @reimp - */ - bool atEnd () const; - - /** - * @reimp - */ - qint64 bytesAvailable () const; - - /** - * @reimp - */ - qint64 bytesToWrite () const; - -protected: - virtual qint64 readData (char *data, qint64 maxSize); - virtual qint64 readLineData (char *data, qint64 maxSize); - virtual qint64 writeData (const char *data, qint64 maxSize); - -private: - Q_PRIVATE_SLOT (d_func (), bool _k_canRead ()) - Q_PRIVATE_SLOT (d_func (), bool _k_canWrite ())}; - -#define KMAXINT ((int)(~0U >> 1)) - -///////////////////////////////////////////////////// -// Helper. Remove when QRingBuffer becomes public. // -///////////////////////////////////////////////////// - -#include <QtCore/qbytearray.h> -#include <QtCore/qlinkedlist.h> - -#define CHUNKSIZE 4096 - -class KRingBuffer -{ -public: - KRingBuffer () - { - clear (); - } - - void clear () - { - buffers.clear (); - QByteArray tmp; - tmp.resize (CHUNKSIZE); - buffers << tmp; - head = tail = 0; - totalSize = 0; - } - - inline bool isEmpty () const - { - return buffers.count () == 1 && !tail; - } - - inline int size () const - { - return totalSize; - } - - inline int readSize () const - { - return (buffers.count () == 1 ? tail : buffers.first ().size ()) - head; - } - - inline const char *readPointer () const - { - Q_ASSERT (totalSize > 0); - return buffers.first ().constData () + head; - } - - void free (int bytes) - { - totalSize -= bytes; - Q_ASSERT (totalSize >= 0); - - forever - { - int nbs = readSize (); - - if (bytes < nbs) - { - head += bytes; - if (head == tail && buffers.count () == 1) - { - buffers.first ().resize (CHUNKSIZE); - head = tail = 0; - } - break; - } - - bytes -= nbs; - if (buffers.count () == 1) - { - buffers.first ().resize (CHUNKSIZE); - head = tail = 0; - break; - } - - buffers.removeFirst (); - head = 0; - } - } - - char *reserve (int bytes) - { - totalSize += bytes; - - char *ptr; - if (tail + bytes <= buffers.last ().size ()) - { - ptr = buffers.last ().data () + tail; - tail += bytes; - } - else - { - buffers.last ().resize (tail); - QByteArray tmp; - tmp.resize (qMax (CHUNKSIZE, bytes)); - ptr = tmp.data (); - buffers << tmp; - tail = bytes; - } - return ptr; - } - - // release a trailing part of the last reservation - inline void unreserve (int bytes) - { - totalSize -= bytes; - tail -= bytes; - } - - inline void write (const char *data, int len) - { - memcpy (reserve (len), data, len); - } - - // Find the first occurrence of c and return the index after it. - // If c is not found until maxLength, maxLength is returned, provided - // it is smaller than the buffer size. Otherwise -1 is returned. - int indexAfter (char c, int maxLength = KMAXINT) const - { - int index = 0; - int start = head; - QLinkedList < QByteArray >::ConstIterator it = buffers.begin (); - forever - { - if (!maxLength) - return index; - if (index == size ()) - return -1; - const QByteArray & buf = *it; - ++it; - int len = qMin ((it == buffers.end ()? tail : buf.size ()) - start, - maxLength); - const char *ptr = buf.data () + start; - if (const char *rptr = (const char *)memchr (ptr, c, len)) - return index + (rptr - ptr) + 1; - index += len; - maxLength -= len; - start = 0; - } - } - - inline int lineSize (int maxLength = KMAXINT) const - { - return indexAfter ('\n', maxLength); - } - - inline bool canReadLine () const - { - return lineSize () != -1; - } - - int read (char *data, int maxLength) - { - int bytesToRead = qMin (size (), maxLength); - int readSoFar = 0; - while (readSoFar < bytesToRead) - { - const char *ptr = readPointer (); - int bs = qMin (bytesToRead - readSoFar, readSize ()); - memcpy (data + readSoFar, ptr, bs); - readSoFar += bs; - free (bs); - } - return readSoFar; - } - - int readLine (char *data, int maxLength) - { - return read (data, lineSize (qMin (maxLength, size ()))); - } - -private: - QLinkedList < QByteArray > buffers; - int head, tail; - int totalSize; -}; - -struct KPtyDevicePrivate:public KPtyPrivate -{ - Q_DECLARE_PUBLIC (KPtyDevice) - KPtyDevicePrivate (KPty * parent):KPtyPrivate (parent), - emittedReadyRead (false), emittedBytesWritten (false), - readNotifier (0), writeNotifier (0) - { - } - - bool _k_canRead (); - bool _k_canWrite (); - - bool doWait (int msecs, bool reading); - void finishOpen (QIODevice::OpenMode mode); - - bool emittedReadyRead; - bool emittedBytesWritten; - QSocketNotifier *readNotifier; - QSocketNotifier *writeNotifier; - KRingBuffer readBuffer; - KRingBuffer writeBuffer; -}; - -#endif