#include "driver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include 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); }