* 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
char* strmcat(char* first, ...);
-enum { INPUT, OUTPUT, NOTIFY };
+enum { INPUT, OUTPUT };
typedef void (*event_cbfn_t)(int fd, void* data);
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
*****************************************************************************/
}
/* 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;
}
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;
- }
}
#include <stdc.h>
#include <utf.h>
#include <edit.h>
+#include <win.h>
#include <unistd.h>
#include <sys/wait.h>
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));
+ }
+}
}
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++;
free(input);
free(output);
free(error);
+#endif
}
static void exec(char* cmd) {
win_setruler(0);
CmdFD = pty_spawn(*cmd ? cmd : shellcmd);
event_watchfd(CmdFD, INPUT, pty_update, NULL);
-
}
#ifndef TEST