Extend copyright to 2018.
[kdbg.git] / kdbg / ttywnd.cpp
blobb03e14d4930b7f45a7beb5bc85ae4219e90b975d
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "ttywnd.h"
8 #include <QSocketNotifier>
9 #include <QContextMenuEvent>
10 #include <QFontDatabase>
11 #include <QMenu>
12 #include <klocalizedstring.h>
14 #include "config.h"
15 #include <fcntl.h>
16 #include <unistd.h> /* open, close, etc. */
17 #include <sys/ioctl.h>
18 #include <sys/stat.h>
19 #ifdef HAVE_PTY_H
20 #include <pty.h> /* openpty on Linux */
21 #endif
22 #ifdef HAVE_LIBUTIL_H
23 #include <libutil.h> /* openpty on FreeBSD */
24 #endif
25 #ifdef HAVE_UTIL_H /* openpty on NetBSD, OpenBSD */
26 #include <util.h>
27 #endif
28 #include <errno.h>
30 #include "mydebug.h"
33 STTY::STTY() :
34 QObject(),
35 m_masterfd(-1),
36 m_slavefd(-1),
37 m_outNotifier(0)
39 if (findTTY())
41 ::fcntl(m_masterfd, F_SETFL, O_NDELAY);
42 m_outNotifier = new QSocketNotifier(m_masterfd, QSocketNotifier::Read);
43 connect(m_outNotifier, SIGNAL(activated(int)), SLOT(outReceived(int)));
44 } else {
45 m_slavetty = QString();
49 STTY::~STTY()
51 if (m_outNotifier) {
52 ::close(m_masterfd);
53 if (m_slavefd >= 0)
54 ::close(m_slavefd);
55 delete m_outNotifier;
59 bool STTY::findTTY()
61 m_masterfd = -1;
63 #ifdef HAVE_FUNC_OPENPTY
64 /* use glibc2's openpty */
65 if (m_masterfd < 0)
67 if (::openpty(&m_masterfd, &m_slavefd, 0, 0, 0) == 0) {
68 const char* tname = ::ttyname(m_slavefd);
69 if (tname != 0) {
70 m_slavetty = tname;
71 } else {
72 ::close(m_slavefd);
73 ::close(m_masterfd);
74 m_masterfd = m_slavefd = -1;
78 #endif
80 // resort to BSD-style terminals
81 if (m_masterfd < 0)
83 const char* s3;
84 const char* s4;
86 char ptynam[] = "/dev/ptyxx";
87 char ttynam[] = "/dev/ttyxx";
88 static const char ptyc3[] = "pqrstuvwxyzabcde";
89 static const char ptyc4[] = "0123456789abcdef";
91 // Find a master pty that we can open
92 for (s3 = ptyc3; *s3 != 0 && m_masterfd < 0; s3++)
94 for (s4 = ptyc4; *s4 != 0; s4++)
96 ptynam[8] = ttynam[8] = *s3;
97 ptynam[9] = ttynam[9] = *s4;
98 if ((m_masterfd = ::open(ptynam,O_RDWR)) >= 0)
100 if (::geteuid() == 0 || ::access(ttynam,R_OK|W_OK) == 0)
102 m_slavetty = ttynam;
103 break;
105 ::close(m_masterfd);
106 m_masterfd = -1;
112 return m_masterfd >= 0;
115 void STTY::outReceived(int f)
117 for (;;) {
118 char buf[1024];
119 int n = ::read(f, buf, sizeof(buf));
120 if (n < 0) {
121 if (errno != EAGAIN) { /* this is not an error */
122 // ugh! error! somebody disconnect this signal please!
124 break;
126 emit output(buf, n);
127 if (n == 0)
128 break;
134 TTYWindow::TTYWindow(QWidget* parent) :
135 QPlainTextEdit(parent),
136 m_tty(0),
137 m_pos(document())
139 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
140 setReadOnly(true);
141 setWordWrapMode(QTextOption::NoWrap);
142 setUndoRedoEnabled(false);
143 m_pos.setPosition(0);
146 TTYWindow::~TTYWindow()
148 if (m_tty)
149 deactivate();
153 QString TTYWindow::activate()
155 // allocate a pseudo terminal
156 m_tty = new STTY;
158 QString ttyName = m_tty->slaveTTY();
159 if (ttyName.isEmpty()) {
160 // failed to allocate terminal
161 delete m_tty;
162 m_tty = 0;
163 return QString();
164 } else {
165 connect(m_tty, SIGNAL(output(char*,int)), SLOT(slotAppend(char*,int)));
166 return ttyName;
170 void TTYWindow::deactivate()
172 delete m_tty;
173 m_tty = 0;
176 void TTYWindow::slotAppend(char* buffer, int count)
178 // parse off lines
179 char* start = buffer;
180 while (count > 0) {
181 int len = 0;
182 while (count > 0 && start[len] != '\n' && start[len] != '\r') {
183 --count;
184 ++len;
186 if (len > 0) {
187 QString str = QString::fromLatin1(start, len);
188 // replace text in the last line
189 // this selection is non-empty only after a '\r' that was not
190 // followed by a '\n'
191 m_pos.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, len);
192 m_pos.insertText(str);
193 start += len;
194 len = 0;
196 if (count > 0 && *start == '\r') {
197 ++start;
198 --count;
199 m_pos.movePosition(QTextCursor::StartOfLine);
201 if (count > 0 && *start == '\n') {
202 ++start;
203 --count;
204 m_pos.movePosition(QTextCursor::End);
205 m_pos.insertText(QString('\n'));
210 void TTYWindow::contextMenuEvent(QContextMenuEvent *event)
212 QMenu* menu = createStandardContextMenu();
213 menu->addSeparator();
214 menu->addAction(i18n("&Clear"),this, SLOT(slotClear()));
215 menu->exec(event->globalPos());
216 delete menu;
219 void TTYWindow::slotClear()
221 clear();
222 m_pos.movePosition(QTextCursor::End);
225 #include "ttywnd.moc"