diff options
Diffstat (limited to 'examples/dte/spawn.c')
| -rw-r--r-- | examples/dte/spawn.c | 396 |
1 files changed, 0 insertions, 396 deletions
diff --git a/examples/dte/spawn.c b/examples/dte/spawn.c deleted file mode 100644 index 0d9e0d6..0000000 --- a/examples/dte/spawn.c +++ /dev/null @@ -1,396 +0,0 @@ -#include <errno.h> -#include <poll.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include "spawn.h" -#include "error.h" -#include "regexp.h" -#include "terminal/mode.h" -#include "util/debug.h" -#include "util/fd.h" -#include "util/fork-exec.h" -#include "util/ptr-array.h" -#include "util/str-util.h" -#include "util/strtonum.h" -#include "util/xmalloc.h" -#include "util/xreadwrite.h" -#include "util/xstdio.h" - -static void handle_error_msg(const Compiler *c, MessageArray *msgs, char *str) -{ - if (str[0] == '\0' || str[0] == '\n') { - return; - } - - size_t str_len = str_replace_byte(str, '\t', ' '); - if (str[str_len - 1] == '\n') { - str[--str_len] = '\0'; - } - - for (size_t i = 0, n = c->error_formats.count; i < n; i++) { - const ErrorFormat *p = c->error_formats.ptrs[i]; - regmatch_t m[ERRORFMT_CAPTURE_MAX]; - if (!regexp_exec(&p->re, str, str_len, ARRAYLEN(m), m, 0)) { - continue; - } - if (p->ignore) { - return; - } - - int8_t mi = p->capture_index[ERRFMT_MESSAGE]; - if (m[mi].rm_so < 0) { - mi = 0; - } - - Message *msg = new_message(str + m[mi].rm_so, m[mi].rm_eo - m[mi].rm_so); - msg->loc = xnew0(FileLocation, 1); - - int8_t fi = p->capture_index[ERRFMT_FILE]; - if (fi >= 0 && m[fi].rm_so >= 0) { - msg->loc->filename = xstrslice(str, m[fi].rm_so, m[fi].rm_eo); - - unsigned long *const ptrs[] = { - [ERRFMT_LINE] = &msg->loc->line, - [ERRFMT_COLUMN] = &msg->loc->column, - }; - - static_assert(ARRAYLEN(ptrs) == 3); - static_assert(ERRFMT_LINE == 1); - static_assert(ERRFMT_COLUMN == 2); - - for (size_t j = ERRFMT_LINE; j < ARRAYLEN(ptrs); j++) { - int8_t ci = p->capture_index[j]; - if (ci >= 0 && m[ci].rm_so >= 0) { - size_t len = m[ci].rm_eo - m[ci].rm_so; - unsigned long val; - if (len == buf_parse_ulong(str + m[ci].rm_so, len, &val)) { - *ptrs[j] = val; - } - } - } - } - - add_message(msgs, msg); - return; - } - - add_message(msgs, new_message(str, str_len)); -} - -static void read_errors(const Compiler *c, MessageArray *msgs, int fd, bool quiet) -{ - FILE *f = fdopen(fd, "r"); - if (unlikely(!f)) { - return; - } - char line[4096]; - while (xfgets(line, sizeof(line), f)) { - if (!quiet) { - xfputs(line, stderr); - } - handle_error_msg(c, msgs, line); - } - fclose(f); -} - -static void handle_piped_data(int f[3], SpawnContext *ctx) -{ - BUG_ON(f[0] < 0 && f[1] < 0 && f[2] < 0); - BUG_ON(f[0] >= 0 && f[0] <= 2); - BUG_ON(f[1] >= 0 && f[1] <= 2); - BUG_ON(f[2] >= 0 && f[2] <= 2); - - if (ctx->input.length == 0) { - xclose(f[0]); - f[0] = -1; - if (f[1] < 0 && f[2] < 0) { - return; - } - } - - struct pollfd fds[] = { - {.fd = f[0], .events = POLLOUT}, - {.fd = f[1], .events = POLLIN}, - {.fd = f[2], .events = POLLIN}, - }; - - size_t wlen = 0; - while (1) { - if (unlikely(poll(fds, ARRAYLEN(fds), -1) < 0)) { - if (errno == EINTR) { - continue; - } - error_msg_errno("poll"); - return; - } - - for (size_t i = 0; i < ARRAYLEN(ctx->outputs); i++) { - struct pollfd *pfd = fds + i + 1; - if (pfd->revents & POLLIN) { - String *output = &ctx->outputs[i]; - char *buf = string_reserve_space(output, 4096); - ssize_t rc = xread(pfd->fd, buf, output->alloc - output->len); - if (unlikely(rc < 0)) { - error_msg_errno("read"); - return; - } - if (rc == 0) { // EOF - if (xclose(pfd->fd)) { - error_msg_errno("close"); - return; - } - pfd->fd = -1; - continue; - } - output->len += rc; - } - } - - if (fds[0].revents & POLLOUT) { - ssize_t rc = xwrite(fds[0].fd, ctx->input.data + wlen, ctx->input.length - wlen); - if (unlikely(rc < 0)) { - error_msg_errno("write"); - return; - } - wlen += (size_t) rc; - if (wlen == ctx->input.length) { - if (xclose(fds[0].fd)) { - error_msg_errno("close"); - return; - } - fds[0].fd = -1; - } - } - - size_t active_fds = ARRAYLEN(fds); - for (size_t i = 0; i < ARRAYLEN(fds); i++) { - int rev = fds[i].revents; - if (fds[i].fd < 0 || rev & POLLNVAL) { - fds[i].fd = -1; - active_fds--; - continue; - } - if (rev & POLLERR || (rev & (POLLHUP | POLLIN)) == POLLHUP) { - if (xclose(fds[i].fd)) { - error_msg_errno("close"); - } - fds[i].fd = -1; - active_fds--; - } - } - if (active_fds == 0) { - return; - } - } -} - -static int open_dev_null(int flags) -{ - int fd = xopen("/dev/null", flags | O_CLOEXEC, 0); - if (unlikely(fd < 0)) { - error_msg_errno("Error opening /dev/null"); - } - return fd; -} - -static int handle_child_error(pid_t pid) -{ - int ret = wait_child(pid); - if (ret < 0) { - error_msg_errno("waitpid"); - } else if (ret >= 256) { - int sig = ret >> 8; - const char *str = strsignal(sig); - error_msg("Child received signal %d (%s)", sig, str ? str : "??"); - } else if (ret) { - error_msg("Child returned %d", ret); - } - return ret; -} - -static void yield_terminal(EditorState *e, bool quiet) -{ - if (quiet) { - term_raw_isig(); - } else { - e->child_controls_terminal = true; - ui_end(e); - } -} - -static void resume_terminal(EditorState *e, bool quiet, bool prompt) -{ - term_raw(); - if (!quiet && e->child_controls_terminal) { - if (prompt) { - any_key(&e->terminal, e->options.esc_timeout); - } - ui_start(e); - e->child_controls_terminal = false; - } -} - -static void exec_error(const char *argv0) -{ - error_msg("Unable to exec '%s': %s", argv0, strerror(errno)); -} - -bool spawn_compiler(SpawnContext *ctx, const Compiler *c, MessageArray *msgs) -{ - BUG_ON(!ctx->editor); - BUG_ON(!ctx->argv[0]); - - int fd[3]; - fd[0] = open_dev_null(O_RDONLY); - if (fd[0] < 0) { - return false; - } - - int dev_null = open_dev_null(O_WRONLY); - if (dev_null < 0) { - xclose(fd[0]); - return false; - } - - int p[2]; - if (xpipe2(p, O_CLOEXEC) != 0) { - error_msg_errno("pipe"); - xclose(dev_null); - xclose(fd[0]); - return false; - } - - SpawnFlags flags = ctx->flags; - bool read_stdout = !!(flags & SPAWN_READ_STDOUT); - bool quiet = !!(flags & SPAWN_QUIET); - bool prompt = !!(flags & SPAWN_PROMPT); - if (read_stdout) { - fd[1] = p[1]; - fd[2] = quiet ? dev_null : 2; - } else { - fd[1] = quiet ? dev_null : 1; - fd[2] = p[1]; - } - - yield_terminal(ctx->editor, quiet); - pid_t pid = fork_exec(ctx->argv, NULL, fd, quiet); - if (pid == -1) { - exec_error(ctx->argv[0]); - xclose(p[1]); - prompt = false; - } else { - // Must close write end of the pipe before read_errors() or - // the read end never gets EOF! - xclose(p[1]); - read_errors(c, msgs, p[0], quiet); - handle_child_error(pid); - } - resume_terminal(ctx->editor, quiet, prompt); - - xclose(p[0]); - xclose(dev_null); - xclose(fd[0]); - return (pid != -1); -} - -// Close fd only if valid (positive) and not stdin/stdout/stderr -static int safe_xclose(int fd) -{ - return (fd > STDERR_FILENO) ? xclose(fd) : 0; -} - -static void safe_xclose_all(int fds[], size_t nr_fds) -{ - for (size_t i = 0; i < nr_fds; i++) { - safe_xclose(fds[i]); - fds[i] = -1; - } -} - -UNITTEST { - int fds[] = {-2, -3, -4}; - safe_xclose_all(fds, 2); - BUG_ON(fds[0] != -1); - BUG_ON(fds[1] != -1); - BUG_ON(fds[2] != -4); - safe_xclose_all(fds, 3); - BUG_ON(fds[2] != -1); -} - -int spawn(SpawnContext *ctx) -{ - BUG_ON(!ctx->editor); - BUG_ON(!ctx->argv[0]); - - int child_fds[3] = {-1, -1, -1}; - int parent_fds[3] = {-1, -1, -1}; - bool quiet = !!(ctx->flags & SPAWN_QUIET); - size_t nr_pipes = 0; - - for (size_t i = 0; i < ARRAYLEN(child_fds); i++) { - switch (ctx->actions[i]) { - case SPAWN_TTY: - if (!quiet) { - child_fds[i] = i; - break; - } - // Fallthrough - case SPAWN_NULL: - child_fds[i] = open_dev_null(O_RDWR); - if (child_fds[i] < 0) { - goto error_close; - } - break; - case SPAWN_PIPE: { - int p[2]; - if (xpipe2(p, O_CLOEXEC) != 0) { - error_msg_errno("pipe"); - goto error_close; - } - BUG_ON(p[0] <= STDERR_FILENO); - BUG_ON(p[1] <= STDERR_FILENO); - child_fds[i] = i ? p[1] : p[0]; - parent_fds[i] = i ? p[0] : p[1]; - if (!fd_set_nonblock(parent_fds[i], true)) { - error_msg_errno("fcntl"); - goto error_close; - } - nr_pipes++; - break; - } - default: - BUG("unhandled action type"); - goto error_close; - } - } - - yield_terminal(ctx->editor, quiet); - pid_t pid = fork_exec(ctx->argv, ctx->env, child_fds, quiet); - if (pid == -1) { - exec_error(ctx->argv[0]); - goto error_resume; - } - - safe_xclose_all(child_fds, ARRAYLEN(child_fds)); - if (nr_pipes > 0) { - handle_piped_data(parent_fds, ctx); - } - - safe_xclose_all(parent_fds, ARRAYLEN(parent_fds)); - int err = wait_child(pid); - if (err < 0) { - error_msg_errno("waitpid"); - } - - resume_terminal(ctx->editor, quiet, !!(ctx->flags & SPAWN_PROMPT)); - return err; - -error_resume: - resume_terminal(ctx->editor, quiet, false); -error_close: - safe_xclose_all(child_fds, ARRAYLEN(child_fds)); - safe_xclose_all(parent_fds, ARRAYLEN(parent_fds)); - return -1; -} |
