source: trunk/tools/neercs/term/pty.cpp @ 2159

Last change on this file since 2159 was 2159, checked in by sam, 10 years ago

neercs: fix our unread() function so that it stops losing characters.

File size: 4.9 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006-2012 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13#if defined HAVE_CONFIG_H
14#   include "config.h"
15#endif
16
17#if defined HAVE_FORKPTY
18#   define _XOPEN_SOURCE
19#   include <stdlib.h>
20#   include <stdio.h>
21#   include <string.h>
22#   include <sys/ioctl.h>
23#   include <sys/types.h>
24#   include <termios.h>
25#   if defined HAVE_PTY_H
26#       include <pty.h>             /* for openpty and forkpty */
27#   elif defined HAVE_UTIL_H
28#       include <util.h>            /* for OS X, OpenBSD and NetBSD */
29#   elif defined HAVE_LIBUTIL_H
30#       include <libutil.h>         /* for FreeBSD */
31#   endif
32#   include <unistd.h>
33#   include <fcntl.h>
34#endif
35
36#include "core.h"
37#include "loldebug.h"
38
39using namespace std;
40using namespace lol;
41
42#include "../neercs.h"
43
44Pty::Pty()
45  : m_fd(-1),
46    m_pid(-1),
47    m_eof(false),
48    m_unread_data(0),
49    m_unread_len(0)
50{
51    ;
52}
53
54Pty::~Pty()
55{
56#if defined HAVE_FORKPTY
57    delete[] m_unread_data;
58
59    if (m_fd >= 0)
60    {
61        close((int)m_fd);
62    }
63#endif
64}
65
66void Pty::Run(char const *command, ivec2 size)
67{
68#if defined HAVE_FORKPTY
69    int fd;
70    pid_t pid;
71
72    m_pid = -1;
73    m_fd = -1;
74
75    pid = forkpty(&fd, NULL, NULL, NULL);
76    if (pid < 0)
77    {
78        fprintf(stderr, "forkpty() error\n");
79        return;
80    }
81    else if (pid == 0)
82    {
83        SetWindowSize(size, 0);
84
85        /* putenv() eats the string, they need to be writable */
86        static char tmp1[] = "CACA_DRIVER=slang";
87        static char tmp2[] = "TERM=xterm";
88        putenv(tmp1);
89        putenv(tmp2);
90
91        m_argv[0] = command;
92        m_argv[1] = NULL;
93        /* The following const cast is valid. The Open Group Base
94         * Specification guarantees that the objects are completely
95         * constant. */
96        execvp(command, const_cast<char * const *>(m_argv));
97        fprintf(stderr, "execvp() error\n");
98        return;
99    }
100
101    fcntl(fd, F_SETFL, O_NDELAY);
102
103    m_pid = pid;
104    m_fd = fd;
105#endif
106}
107
108bool Pty::IsEof() const
109{
110    return m_eof;
111}
112
113size_t Pty::ReadData(char *data, size_t maxlen)
114{
115#if defined HAVE_FORKPTY
116    size_t sent = 0;
117
118    /* Do we have data from previous call? */
119    if (m_unread_len)
120    {
121        size_t tocopy = min(maxlen, m_unread_len);
122
123        memcpy(data, m_unread_data, tocopy);
124
125        data += tocopy;
126        sent += tocopy;
127        maxlen -= tocopy;
128
129        if (tocopy < m_unread_len)
130        {
131            m_unread_len -= tocopy;
132            memmove(m_unread_data, m_unread_data + tocopy, m_unread_len);
133        }
134        else
135        {
136            delete[] m_unread_data;
137            m_unread_data = 0;
138            m_unread_len = 0;
139        }
140    }
141
142    fd_set fdset;
143    int maxfd = -1;
144
145    FD_ZERO(&fdset);
146    if (m_fd >= 0)
147    {
148        FD_SET((int)m_fd, &fdset);
149        maxfd = std::max(maxfd, (int)m_fd);
150    }
151
152    if (maxfd >= 0)
153    {
154        struct timeval tv;
155        tv.tv_sec = 0;
156        tv.tv_usec = 50000;
157        int ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
158
159        if (ret < 0)
160        {
161            Log::Error("cannot read from PTY\n");
162            m_eof = true;
163            return 0;
164        }
165        else if (ret)
166        {
167            if (FD_ISSET((int)m_fd, &fdset))
168            {
169                ssize_t nr = read((int)m_fd, data, maxlen);
170
171                /* Data available but zero-length read: EOF */
172                if (nr <= 0)
173                    m_eof = true;
174                else
175                    sent += nr;
176
177                if (sent >= 0)
178                    return sent;
179            }
180        }
181    }
182#endif
183
184    return 0;
185}
186
187void Pty::UnreadData(char *data, size_t len)
188{
189#if defined HAVE_FORKPTY
190    /* Prepare unread buffer */
191    if (m_unread_data)
192    {
193        char *tmp = new char[m_unread_len + len];
194        memcpy(tmp + len, m_unread_data, m_unread_len);
195        delete[] m_unread_data;
196        m_unread_data = tmp;
197        m_unread_len += len;
198    }
199    else
200    {
201        m_unread_data = new char[len];
202        m_unread_len = len;
203    }
204
205    /* Copy data to the unread buffer */
206    memcpy(m_unread_data, data, len);
207#endif
208}
209
210size_t Pty::WriteData(char const *data, size_t len)
211{
212#if defined HAVE_FORKPTY
213    /* FIXME: can we be more naive than that? */
214    return write((int)m_fd, data, len);
215#endif
216
217    return 0;
218}
219
220void Pty::SetWindowSize(ivec2 size, int64_t fd /* = -1 */)
221{
222#if defined HAVE_FORKPTY
223    if (m_size == size)
224        return;
225
226    if (fd < 0)
227        fd = m_fd;
228
229    m_size = size;
230
231    struct winsize ws;
232
233    memset(&ws, 0, sizeof(ws));
234    ws.ws_row = size.y;
235    ws.ws_col = size.x;
236    ioctl((int)fd, TIOCSWINSZ, (char *)&ws);
237#endif
238}
Note: See TracBrowser for help on using the repository browser.