index 718e6c2d186254f5cca36a5fcee1742d7b9265ed..6ad9c57953182e4d54d4ad3fdca66d61e8dfc4b3 100644 (file)
--- a/tig.c
+++ b/tig.c
*/
#include "tig.h"
+#include "io.h"
static void __NORETURN die(const char *err, ...);
static void warn(const char *msg, ...);
}
-static bool
-argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
-{
- int valuelen;
-
- while (*cmd && *argc < SIZEOF_ARG && (valuelen = strcspn(cmd, " \t"))) {
- bool advance = cmd[valuelen] != 0;
-
- cmd[valuelen] = 0;
- argv[(*argc)++] = chomp_string(cmd);
- cmd = chomp_string(cmd + valuelen + advance);
- }
-
- if (*argc < SIZEOF_ARG)
- argv[*argc] = NULL;
- return *argc < SIZEOF_ARG;
-}
-
-static bool
-argv_from_env(const char **argv, const char *name)
-{
- char *env = argv ? getenv(name) : NULL;
- int argc = 0;
-
- if (env && *env)
- env = strdup(env);
- return !env || argv_from_string(argv, &argc, env);
-}
-
-static void
-argv_free(const char *argv[])
-{
- int argc;
-
- if (!argv)
- return;
- for (argc = 0; argv[argc]; argc++)
- free((void *) argv[argc]);
- argv[0] = NULL;
-}
-
-static size_t
-argv_size(const char **argv)
-{
- int argc = 0;
-
- while (argv && argv[argc])
- argc++;
-
- return argc;
-}
-
-DEFINE_ALLOCATOR(argv_realloc, const char *, SIZEOF_ARG)
-
-static bool
-argv_append(const char ***argv, const char *arg)
-{
- size_t argc = argv_size(*argv);
-
- if (!argv_realloc(argv, argc, 2))
- return FALSE;
-
- (*argv)[argc++] = strdup(arg);
- (*argv)[argc] = NULL;
- return TRUE;
-}
-
-static bool
-argv_append_array(const char ***dst_argv, const char *src_argv[])
-{
- int i;
-
- for (i = 0; src_argv && src_argv[i]; i++)
- if (!argv_append(dst_argv, src_argv[i]))
- return FALSE;
- return TRUE;
-}
-
-static bool
-argv_copy(const char ***dst, const char *src[])
-{
- int argc;
-
- for (argc = 0; src[argc]; argc++)
- if (!argv_append(dst, src[argc]))
- return FALSE;
- return TRUE;
-}
-
-
-/*
- * Executing external commands.
- */
-
-enum io_type {
- IO_FD, /* File descriptor based IO. */
- IO_BG, /* Execute command in the background. */
- IO_FG, /* Execute command with same std{in,out,err}. */
- IO_RD, /* Read only fork+exec IO. */
- IO_WR, /* Write only fork+exec IO. */
- IO_AP, /* Append fork+exec output to file. */
-};
-
-struct io {
- int pipe; /* Pipe end for reading or writing. */
- pid_t pid; /* PID of spawned process. */
- int error; /* Error status. */
- char *buf; /* Read buffer. */
- size_t bufalloc; /* Allocated buffer size. */
- size_t bufsize; /* Buffer content size. */
- char *bufpos; /* Current buffer position. */
- unsigned int eof:1; /* Has end of file been reached. */
-};
-
-static void
-io_init(struct io *io)
-{
- memset(io, 0, sizeof(*io));
- io->pipe = -1;
-}
-
-static bool
-io_open(struct io *io, const char *fmt, ...)
-{
- char name[SIZEOF_STR] = "";
- bool fits;
- va_list args;
-
- io_init(io);
-
- va_start(args, fmt);
- fits = vsnprintf(name, sizeof(name), fmt, args) < sizeof(name);
- va_end(args);
-
- if (!fits) {
- io->error = ENAMETOOLONG;
- return FALSE;
- }
- io->pipe = *name ? open(name, O_RDONLY) : STDIN_FILENO;
- if (io->pipe == -1)
- io->error = errno;
- return io->pipe != -1;
-}
-
-static bool
-io_kill(struct io *io)
-{
- return io->pid == 0 || kill(io->pid, SIGKILL) != -1;
-}
-
-static bool
-io_done(struct io *io)
-{
- pid_t pid = io->pid;
-
- if (io->pipe != -1)
- close(io->pipe);
- free(io->buf);
- io_init(io);
-
- while (pid > 0) {
- int status;
- pid_t waiting = waitpid(pid, &status, 0);
-
- if (waiting < 0) {
- if (errno == EINTR)
- continue;
- io->error = errno;
- return FALSE;
- }
-
- return waiting == pid &&
- !WIFSIGNALED(status) &&
- WIFEXITED(status) &&
- !WEXITSTATUS(status);
- }
-
- return TRUE;
-}
-
-static bool
-io_run(struct io *io, enum io_type type, const char *dir, const char *argv[], ...)
-{
- int pipefds[2] = { -1, -1 };
- va_list args;
-
- io_init(io);
-
- if ((type == IO_RD || type == IO_WR) && pipe(pipefds) < 0) {
- io->error = errno;
- return FALSE;
- } else if (type == IO_AP) {
- va_start(args, argv);
- pipefds[1] = va_arg(args, int);
- va_end(args);
- }
-
- if ((io->pid = fork())) {
- if (io->pid == -1)
- io->error = errno;
- if (pipefds[!(type == IO_WR)] != -1)
- close(pipefds[!(type == IO_WR)]);
- if (io->pid != -1) {
- io->pipe = pipefds[!!(type == IO_WR)];
- return TRUE;
- }
-
- } else {
- if (type != IO_FG) {
- int devnull = open("/dev/null", O_RDWR);
- int readfd = type == IO_WR ? pipefds[0] : devnull;
- int writefd = (type == IO_RD || type == IO_AP)
- ? pipefds[1] : devnull;
-
- dup2(readfd, STDIN_FILENO);
- dup2(writefd, STDOUT_FILENO);
- dup2(devnull, STDERR_FILENO);
-
- close(devnull);
- if (pipefds[0] != -1)
- close(pipefds[0]);
- if (pipefds[1] != -1)
- close(pipefds[1]);
- }
-
- if (dir && *dir && chdir(dir) == -1)
- exit(errno);
-
- execvp(argv[0], (char *const*) argv);
- exit(errno);
- }
-
- if (pipefds[!!(type == IO_WR)] != -1)
- close(pipefds[!!(type == IO_WR)]);
- return FALSE;
-}
-
-static bool
-io_complete(enum io_type type, const char **argv, const char *dir, int fd)
-{
- struct io io;
-
- return io_run(&io, type, dir, argv, fd) && io_done(&io);
-}
-
-static bool
-io_run_bg(const char **argv)
-{
- return io_complete(IO_BG, argv, NULL, -1);
-}
-
-static bool
-io_run_fg(const char **argv, const char *dir)
-{
- return io_complete(IO_FG, argv, dir, -1);
-}
-
-static bool
-io_run_append(const char **argv, int fd)
-{
- return io_complete(IO_AP, argv, NULL, fd);
-}
-
-static bool
-io_eof(struct io *io)
-{
- return io->eof;
-}
-
-static int
-io_error(struct io *io)
-{
- return io->error;
-}
-
-static char *
-io_strerror(struct io *io)
-{
- return strerror(io->error);
-}
-
-static bool
-io_can_read(struct io *io)
-{
- struct timeval tv = { 0, 500 };
- fd_set fds;
-
- FD_ZERO(&fds);
- FD_SET(io->pipe, &fds);
-
- return select(io->pipe + 1, &fds, NULL, NULL, &tv) > 0;
-}
-
-static ssize_t
-io_read(struct io *io, void *buf, size_t bufsize)
-{
- do {
- ssize_t readsize = read(io->pipe, buf, bufsize);
-
- if (readsize < 0 && (errno == EAGAIN || errno == EINTR))
- continue;
- else if (readsize == -1)
- io->error = errno;
- else if (readsize == 0)
- io->eof = 1;
- return readsize;
- } while (1);
-}
-
-DEFINE_ALLOCATOR(io_realloc_buf, char, BUFSIZ)
-
-static char *
-io_get(struct io *io, int c, bool can_read)
-{
- char *eol;
- ssize_t readsize;
-
- while (TRUE) {
- if (io->bufsize > 0) {
- eol = memchr(io->bufpos, c, io->bufsize);
- if (eol) {
- char *line = io->bufpos;
-
- *eol = 0;
- io->bufpos = eol + 1;
- io->bufsize -= io->bufpos - line;
- return line;
- }
- }
-
- if (io_eof(io)) {
- if (io->bufsize) {
- io->bufpos[io->bufsize] = 0;
- io->bufsize = 0;
- return io->bufpos;
- }
- return NULL;
- }
-
- if (!can_read)
- return NULL;
-
- if (io->bufsize > 0 && io->bufpos > io->buf)
- memmove(io->buf, io->bufpos, io->bufsize);
-
- if (io->bufalloc == io->bufsize) {
- if (!io_realloc_buf(&io->buf, io->bufalloc, BUFSIZ))
- return NULL;
- io->bufalloc += BUFSIZ;
- }
-
- io->bufpos = io->buf;
- readsize = io_read(io, io->buf + io->bufsize, io->bufalloc - io->bufsize);
- if (io_error(io))
- return NULL;
- io->bufsize += readsize;
- }
-}
-
-static bool
-io_write(struct io *io, const void *buf, size_t bufsize)
-{
- size_t written = 0;
-
- while (!io_error(io) && written < bufsize) {
- ssize_t size;
-
- size = write(io->pipe, buf + written, bufsize - written);
- if (size < 0 && (errno == EAGAIN || errno == EINTR))
- continue;
- else if (size == -1)
- io->error = errno;
- else
- written += size;
- }
-
- return written == bufsize;
-}
-
-static bool
-io_read_buf(struct io *io, char buf[], size_t bufsize)
-{
- char *result = io_get(io, '\n', TRUE);
-
- if (result) {
- result = chomp_string(result);
- string_ncopy_do(buf, bufsize, result, strlen(result));
- }
-
- return io_done(io) && result;
-}
-
-static bool
-io_run_buf(const char **argv, char buf[], size_t bufsize)
-{
- struct io io;
-
- return io_run(&io, IO_RD, NULL, argv) && io_read_buf(&io, buf, bufsize);
-}
-
-typedef int (*io_read_fn)(char *, size_t, char *, size_t, void *data);
-
-static int
-io_load(struct io *io, const char *separators,
- io_read_fn read_property, void *data)
-{
- char *name;
- int state = OK;
-
- while (state == OK && (name = io_get(io, '\n', TRUE))) {
- char *value;
- size_t namelen;
- size_t valuelen;
-
- name = chomp_string(name);
- namelen = strcspn(name, separators);
-
- if (name[namelen]) {
- name[namelen] = 0;
- value = chomp_string(name + namelen + 1);
- valuelen = strlen(value);
-
- } else {
- value = "";
- valuelen = 0;
- }
-
- state = read_property(name, namelen, value, valuelen, data);
- }
-
- if (state != ERR && io_error(io))
- state = ERR;
- io_done(io);
-
- return state;
-}
-
-static int
-io_run_load(const char **argv, const char *separators,
- io_read_fn read_property, void *data)
-{
- struct io io;
-
- if (!io_run(&io, IO_RD, NULL, argv))
- return ERR;
- return io_load(&io, separators, read_property, data);
-}
-
-
/*
* User requests
*/