diff options
Diffstat (limited to 'src/pty-unix.c')
-rw-r--r-- | src/pty-unix.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/pty-unix.c b/src/pty-unix.c new file mode 100644 index 0000000..643c5ba --- /dev/null +++ b/src/pty-unix.c @@ -0,0 +1,138 @@ +#define _XOPEN_SOURCE 600 +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "runes.h" + +static void runes_pty_backend_read(uv_work_t *req); +static void runes_pty_backend_got_data(uv_work_t *req, int status); + +void runes_pty_backend_spawn_subprocess(RunesTerm *t) +{ + RunesPtyBackend *pty = &t->pty; + + pty->master = posix_openpt(O_RDWR); + grantpt(pty->master); + unlockpt(pty->master); + pty->slave = open(ptsname(pty->master), O_RDWR); + + pty->child_pid = fork(); + if (pty->child_pid) { + close(pty->slave); + pty->slave = -1; + } + else { + char *cmd; + int old_stderr_fd; + FILE *old_stderr; + + old_stderr_fd = dup(2); + fcntl(old_stderr_fd, F_SETFD, FD_CLOEXEC); + old_stderr = fdopen(old_stderr_fd, "w"); + + setsid(); + ioctl(pty->slave, TIOCSCTTY, NULL); + + close(pty->master); + + dup2(pty->slave, 0); + dup2(pty->slave, 1); + dup2(pty->slave, 2); + + close(pty->slave); + + cmd = t->cmd; + if (!cmd) { + cmd = getenv("SHELL"); + } + if (!cmd) { + cmd = "/bin/sh"; + } + + /* XXX should use a different TERM value eventually, but for right now + * screen is compatible enough */ + setenv("TERM", "screen", 1); + unsetenv("LINES"); + unsetenv("COLUMNS"); + + execlp(cmd, cmd, (char *)NULL); + + fprintf(old_stderr, "Couldn't run %s: %s\n", cmd, strerror(errno)); + exit(1); + } +} + +void runes_pty_backend_start_loop(RunesTerm *t) +{ + void *data; + + data = malloc(sizeof(RunesLoopData)); + ((RunesLoopData *)data)->req.data = data; + ((RunesLoopData *)data)->t = t; + + uv_queue_work( + t->loop, data, runes_pty_backend_read, runes_pty_backend_got_data); +} + +void runes_pty_backend_set_window_size(RunesTerm *t) +{ + struct winsize size; + + size.ws_row = t->rows; + size.ws_col = t->cols; + size.ws_xpixel = t->xpixel; + size.ws_ypixel = t->ypixel; + ioctl(t->pty.master, TIOCSWINSZ, &size); +} + +void runes_pty_backend_write(RunesTerm *t, char *buf, size_t len) +{ + write(t->pty.master, buf, len); +} + +void runes_pty_backend_request_close(RunesTerm *t) +{ + RunesPtyBackend *pty = &t->pty; + + kill(pty->child_pid, SIGHUP); +} + +void runes_pty_backend_cleanup(RunesTerm *t) +{ + RunesPtyBackend *pty = &t->pty; + + close(pty->master); +} + +static void runes_pty_backend_read(uv_work_t *req) +{ + RunesLoopData *data = req->data; + RunesTerm *t = data->t; + + runes_window_backend_request_flush(t); + t->readlen = read( + t->pty.master, t->readbuf + t->remaininglen, + RUNES_READ_BUFFER_LENGTH - t->remaininglen); +} + +static void runes_pty_backend_got_data(uv_work_t *req, int status) +{ + RunesLoopData *data = req->data; + RunesTerm *t = data->t; + + UNUSED(status); + + if (t->readlen > 0) { + runes_parser_process_string( + t, t->readbuf, t->readlen + t->remaininglen); + uv_queue_work( + t->loop, req, runes_pty_backend_read, runes_pty_backend_got_data); + } + else { + runes_window_backend_request_close(t); + free(req); + } +} |