180 lines
3.9 KiB
C
180 lines
3.9 KiB
C
#include "driver.h"
|
|
#include <camellia.h>
|
|
#include <camellia/compat.h>
|
|
#include <camellia/flags.h>
|
|
#include <camellia/fs/dir.h>
|
|
#include <camellia/fs/misc.h>
|
|
#include <camellia/syscalls.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <thread.h>
|
|
#include <unistd.h>
|
|
|
|
enum tstate {
|
|
Normal,
|
|
Esc,
|
|
CSI,
|
|
};
|
|
|
|
enum handles {
|
|
Hroot,
|
|
Htokill,
|
|
};
|
|
|
|
typedef struct Tokill Tokill;
|
|
struct Tokill {
|
|
Tokill *next;
|
|
char path[]; /* NUL-terminated */
|
|
};
|
|
static Tokill *tokill = NULL;
|
|
static hid_t stdin_pipe[2];
|
|
|
|
static void w_output(hid_t output, const char *buf, size_t len) {
|
|
size_t pos = 0;
|
|
while (pos < len) {
|
|
int ret = _sys_write(output, buf + pos, len - pos, pos, 0);
|
|
if (ret < 0) break;
|
|
pos += ret;
|
|
}
|
|
}
|
|
|
|
static void send_intr(char *intr) {
|
|
for (Tokill *it = tokill; it; it = it->next) {
|
|
int fd = camellia_open(it->path, OPEN_WRITE);
|
|
// TODO remove dead from list
|
|
if (fd < 0) continue;
|
|
_sys_write(fd, intr, strlen(intr), -1, 0);
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
static void line_editor(void *) {
|
|
char readbuf[16], linebuf[256];
|
|
size_t linepos = 0;
|
|
enum tstate state = Normal;
|
|
hid_t input = 0;
|
|
hid_t output = stdin_pipe[1];
|
|
for (;;) {
|
|
int readlen = _sys_read(input, readbuf, sizeof readbuf, -1);
|
|
if (readlen < 0) {
|
|
errno = -readlen;
|
|
err(1, "read");
|
|
return;
|
|
}
|
|
for (int i = 0; i < readlen; i++) {
|
|
char c = readbuf[i];
|
|
switch (state) {
|
|
case Normal:
|
|
switch (c) {
|
|
case '\b':
|
|
case 0x7f:
|
|
if (linepos != 0) {
|
|
printf("\b \b");
|
|
linepos--;
|
|
}
|
|
break;
|
|
case 3: /* C-c */
|
|
send_intr("");
|
|
break;
|
|
case 0x1c: /* C-\ */
|
|
send_intr("kill");
|
|
break;
|
|
case 4: /* EOT, C-d */
|
|
if (linepos > 0) {
|
|
w_output(output, linebuf, linepos);
|
|
linepos = 0;
|
|
} else {
|
|
_sys_write(output, NULL, 0, 0, 0); /* EOF */
|
|
}
|
|
break;
|
|
case '\n':
|
|
case '\r':
|
|
printf("\n");
|
|
if (linepos < sizeof linebuf)
|
|
linebuf[linepos++] = '\n';
|
|
w_output(output, linebuf, linepos);
|
|
linepos = 0;
|
|
break;
|
|
case '\e':
|
|
state = Esc;
|
|
break;
|
|
case '\t':
|
|
break;
|
|
default:
|
|
if (linepos < sizeof linebuf) {
|
|
linebuf[linepos++] = c;
|
|
printf("%c", c);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case Esc:
|
|
if (c == '[') state = CSI;
|
|
else state = Normal;
|
|
break;
|
|
case CSI:
|
|
if (0x40 <= c && c <= 0x7E) state = Normal;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fs(void *) {
|
|
const size_t buflen = 1024;
|
|
char *buf = malloc(buflen);
|
|
int pipefd = stdin_pipe[0];
|
|
if (!buf) err(1, "malloc");
|
|
for (;;) {
|
|
struct ufs_request req;
|
|
hid_t reqh = ufs_wait(buf, buflen, &req);
|
|
int id = (int)req.id;
|
|
if (reqh < 0) errx(1, "ufs_wait error");
|
|
|
|
if (req.op == VFSOP_OPEN) {
|
|
if (strcmp(buf, "/") == 0) {
|
|
_sys_fs_respond(reqh, (void*)Hroot, 0, 0);
|
|
} else if (strcmp(buf, "/tokill") == 0) {
|
|
_sys_fs_respond(reqh, (void*)Htokill, 0, 0);
|
|
} else if (strcmp(buf, "/stdin") == 0) {
|
|
_sys_fs_respond(reqh, NULL, pipefd, FSR_DELEGATE);
|
|
} else {
|
|
_sys_fs_respond(reqh, NULL, -ENOENT, 0);
|
|
}
|
|
} else if (id == Hroot && (req.op == VFSOP_READ || req.op == VFSOP_GETSIZE)) {
|
|
struct dirbuild db;
|
|
char *target = req.op == VFSOP_READ ? buf : NULL;
|
|
dir_start(&db, req.offset, target, buflen);
|
|
dir_append(&db, "stdin");
|
|
_sys_fs_respond(reqh, target, dir_finish(&db), 0);
|
|
} else if (id == Htokill && req.op == VFSOP_WRITE) {
|
|
// alternatively, pass fd?
|
|
Tokill *head = calloc(sizeof(Tokill) + req.len + 1, 1);
|
|
if (!head) {
|
|
_sys_fs_respond(reqh, NULL, -ENOMEM, 0);
|
|
continue;
|
|
}
|
|
memcpy(head->path, buf, req.len);
|
|
head->next = tokill;
|
|
tokill = head;
|
|
_sys_fs_respond(reqh, NULL, req.len, 0);
|
|
} else {
|
|
_sys_fs_respond(reqh, NULL, -ENOSYS, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO turn into a separate binary
|
|
void termcook(void) {
|
|
if (_sys_pipe(stdin_pipe, 0) < 0)
|
|
err(1, "pipe");
|
|
|
|
thread_create(0, fs, NULL);
|
|
thread_create(0, line_editor, NULL);
|
|
_sys_await();
|
|
exit(1);
|
|
}
|