From c53dac8aa756bd7003732204570e3a675478970f Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Tue, 11 Jul 2017 20:51:42 -0400 Subject: [PATCH] initial commit of asynchronous command execution --- TODO.md | 1 + inc/edit.h | 3 ++- lib/event.c | 25 +++++++++-------- lib/exec.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tide.c | 33 ++++++++++++++++++++++- 5 files changed, 126 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index 3c11d18..b4be0d7 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,7 @@ Up Next: * Send tag to send commands or data to the subprocess * Middle click executes send when in command mode and edit region +* AutoSave feature? * moving from tags to the gutter does not transfer focus to edit region * implement transaction control in buf.c diff --git a/inc/edit.h b/inc/edit.h index c01c765..01e535d 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -22,7 +22,7 @@ bool file_exists(char* path); char* strmcat(char* first, ...); -enum { INPUT, OUTPUT, NOTIFY }; +enum { INPUT, OUTPUT }; typedef void (*event_cbfn_t)(int fd, void* data); @@ -230,6 +230,7 @@ int cmdrun(char** cmd, char** err); char* cmdread(char** cmd, char** err); void cmdwrite(char** cmd, char* text, char** err); char* cmdwriteread(char** cmd, char* text, char** err); +void exec_job(char** cmd, char* data, size_t ndata, View* dest); /* Configuration Data *****************************************************************************/ diff --git a/lib/event.c b/lib/event.c index 964c3e9..ad6d8e7 100644 --- a/lib/event.c +++ b/lib/event.c @@ -33,16 +33,22 @@ bool event_poll(int ms) { } /* if the desriptor is done or errored, throw it out */ - if (Descriptors[i].revents & (POLLERR|POLLHUP)) { + if (Descriptors[i].revents & (POLLNVAL|POLLERR|POLLHUP)) { close(Descriptors[i].fd); - for (int x = i+1; x < NumDescriptors; x++) { - Descriptors[x-1] = Descriptors[x]; - EventData[x-1] = EventData[x]; - } - NumDescriptors--; + Descriptors[i].fd = -1; } } + /* remove any closed or invalid descriptors */ + size_t nfds = 0; + for (int i = 0; i < NumDescriptors; i++) { + if (Descriptors[i].fd != -1) { + Descriptors[nfds] = Descriptors[i]; + EventData[nfds++] = EventData[i]; + } + } + NumDescriptors = nfds; + return true; } @@ -53,11 +59,8 @@ void event_watchfd(int fd, int iodir, event_cbfn_t fn, void* data) { if (!Descriptors || !EventData) die("event_Watchfd() : out of memory\n"); Descriptors[idx].fd = fd; + Descriptors[idx].revents = 0; + Descriptors[idx].events = (iodir == INPUT ? POLLIN : POLLOUT); EventData[idx].data = data; EventData[idx].fn = fn; - switch (iodir) { - case INPUT: Descriptors[idx].events = POLLIN; break; - case OUTPUT: Descriptors[idx].events = POLLOUT; break; - case NOTIFY: Descriptors[idx].events = (POLLIN|POLLOUT); break; - } } diff --git a/lib/exec.c b/lib/exec.c index bf2408e..488fe1c 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -133,3 +134,79 @@ char* cmdwriteread(char** cmd, char* text, char** err) { waitpid(proc.pid, NULL, 0); return str; } + +/******************************************************************************/ + +typedef struct Job Job; + +typedef struct { + View* view; + char nbytes; + char bytes[6]; +} Recvr; + +struct Job { + Job* next; /* Pointer to previous job in the job list */ + Job* prev; /* Pointer to next job in the job list */ + size_t ndata; /* number of bytes to write to stdout */ + size_t nwrite; /* number of bytes written to stdout so far */ + char* data; /* data to write to stdout */ + Recvr err_recvr; /* view in which the error output will be placed */ + Recvr out_recvr; /* view in which the output will be placed */ + int pid; /* process id of the running job */ +}; + +static Job* JobList = NULL; + +static void send_data(int fd, void* data) { + Job* job = data; + long nwrite = write(fd, (job->data + job->nwrite), job->ndata); + if (nwrite < 0) { free(job->data); close(fd); return; } + job->ndata -= nwrite; + job->nwrite += nwrite; + if (job->ndata <= 0) { free(job->data); close(fd); return; } +} + +static void recv_data(int fd, void* data) { + static char buf[4096]; + long i = 0, nread = read(fd, buf, sizeof(buf)); + Recvr* rcvr = data; + if (nread <= 0) { close(fd); return; } + for (; i < nread;) { + Rune r; + size_t len = 0; + while (!utf8decode(&r, &len, buf[i++])); + view_insert(rcvr->view, false, r); + } +} + +static void watch_or_close(bool valid, int dir, int fd, void* data) { + event_cbfn_t fn = (dir == OUTPUT ? send_data : recv_data); + if (valid) + event_watchfd(fd, dir, fn, data); + else + close(fd); +} + +void exec_job(char** cmd, char* data, size_t ndata, View* dest) { + Proc proc; + Job* job = calloc(1, sizeof(Job)); + job->pid = execute(cmd, &proc); + if (job->pid < 0) { + die("job_start() :"); + } else { + /* add the job to the job list */ + job->err_recvr.view = win_view(TAGS); + job->out_recvr.view = dest; + job->ndata = ndata; + job->nwrite = 0; + job->data = data; + job->next = JobList; + if (JobList) JobList->prev = job; + JobList = job; + /* register watch events for file descriptors */ + watch_or_close((job->data != NULL), OUTPUT, proc.in, job); + watch_or_close((dest != NULL), INPUT, proc.out, &(job->out_recvr)); + watch_or_close(true, INPUT, proc.err, &(job->err_recvr)); + } +} diff --git a/tide.c b/tide.c index f794f90..9617ae4 100644 --- a/tide.c +++ b/tide.c @@ -67,6 +67,37 @@ static void tag_exec(Tag* tag, char* arg) { } static void cmd_exec(char* cmd) { +#if 1 + + /* parse the command sigils */ + char op = '\0', **execcmd = NULL; + if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>') + op = *cmd, cmd++; + execcmd = (op == ':' ? SedCmd : ShellCmd); + execcmd[2] = cmd; + + /* get the selection that the command will operate on */ + if (op && op != '<' && op != '!' && 0 == view_selsize(win_view(EDIT))) + win_view(EDIT)->selection = (Sel){ .beg = 0, .end = buf_end(win_buf(EDIT)) }; + char* input = view_getstr(win_view(EDIT), NULL); + size_t len = (input ? strlen(input) : 0); + View *tags = win_view(TAGS), *edit = win_view(EDIT), *curr = win_view(FOCUSED); + + /* execute the job */ + printf("sigil: '%c'\n", op); + printf("data: %d '%s'\n", len, input); + if (op == '!') { + free(input); + exec_job(execcmd, NULL, 0, NULL); + } else if (op == '>') { + exec_job(execcmd, input, len, tags); + } else if (op == '|' || op == ':') { + exec_job(execcmd, input, len, edit); + } else { + exec_job(execcmd, input, len, (op != '<' ? curr : edit)); + } + +#else char op = '\0'; if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>') op = *cmd, cmd++; @@ -107,6 +138,7 @@ static void cmd_exec(char* cmd) { free(input); free(output); free(error); +#endif } static void exec(char* cmd) { @@ -674,7 +706,6 @@ void edit_command(char** cmd) { win_setruler(0); CmdFD = pty_spawn(*cmd ? cmd : shellcmd); event_watchfd(CmdFD, INPUT, pty_update, NULL); - } #ifndef TEST -- 2.49.0