aboutsummaryrefslogtreecommitdiffstats
path: root/pty-unix.c
blob: 1c7ad6694613b3d1f4a8eef320bd4c0544b50736 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#define _XOPEN_SOURCE 600
#include <fcntl.h>
#include <stdlib.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 *shell;

        setsid();
        ioctl(pty->slave, TIOCSCTTY, NULL);

        close(pty->master);

        dup2(pty->slave, 0);
        dup2(pty->slave, 1);
        dup2(pty->slave, 2);

        close(pty->slave);

        shell = getenv("SHELL");
        if (!shell) {
            shell = "/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");

        execl(shell, shell, (char *)NULL);
    }
}

void runes_pty_backend_start_loop(RunesTerm *t)
{
    void *data;

    data = malloc(sizeof(RunesPtyLoopData));
    ((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)
{
    RunesPtyLoopData *data;

    data = (RunesPtyLoopData *)req->data;
    runes_window_backend_request_flush(data->data.t);
    data->len = read(
        data->data.t->pty.master, data->buf, RUNES_PTY_BUFFER_LENGTH);
}

static void runes_pty_backend_got_data(uv_work_t *req, int status)
{
    RunesPtyLoopData *data = req->data;
    RunesTerm *t = data->data.t;

    UNUSED(status);

    if (data->len > 0) {
        runes_parser_process_string(t, data->buf, data->len);
        uv_queue_work(
            t->loop, req, runes_pty_backend_read, runes_pty_backend_got_data);
    }
    else {
        runes_window_backend_request_close(t);
        free(req);
    }
}