]> git.mdlowis.com Git - projs/tide.git/commitdiff
initial commit of asynchronous command execution
authorMichael D. Lowis <mike@mdlowis.com>
Wed, 12 Jul 2017 00:51:42 +0000 (20:51 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Wed, 12 Jul 2017 00:51:42 +0000 (20:51 -0400)
TODO.md
inc/edit.h
lib/event.c
lib/exec.c
tide.c

diff --git a/TODO.md b/TODO.md
index 3c11d185066f5e3e53a24a5cfe67aa786699572e..b4be0d7729968b036bb23b213f7a2cd23cf2226c 100644 (file)
--- 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
index c01c765e972dd74017c05cdccc02f2429dba9b72..01e535da0917427e258036a25479365552c2eb5f 100644 (file)
@@ -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
  *****************************************************************************/
index 964c3e9b13b7d1a6a775039756fc402b4a01b381..ad6d8e79100d8cf1fe968f106fbeb1ee0b28893b 100644 (file)
@@ -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;
-    }
 }
index bf2408e57013afdaa692ad911c66bfb4b0d7c34d..488fe1ce2ee978c8d225073037f28bf6399b5c7c 100644 (file)
@@ -2,6 +2,7 @@
 #include <stdc.h>
 #include <utf.h>
 #include <edit.h>
+#include <win.h>
 #include <unistd.h>
 #include <sys/wait.h>
 
@@ -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 f794f9047e2dba183688a00adfd3eb2972f22727..9617ae4ce2a6a46379fcc79d2624854ccfe940d0 100644 (file)
--- 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