commit 6d27363d00b6e9901126df2f4a70b8ba93bc1602
parent 7c32499ad075426acaa9f44813059b630aa52a12
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Sun, 26 Oct 2014 19:05:06 +0000
various improvements and cleanup:
- rework read and write from pipe.
- unify pipe_read and normal input / output handling.
- allow to specify to not read from the pipe, this fixes an issue with
xclip / xsel which forks to the background and blocks reads forever.
- cleanup setup() code a bit.
Diffstat:
M | README | | | 5 | ----- |
M | config.def.h | | | 6 | +++--- |
M | sob.c | | | 343 | +++++++++++++++++++++++++++++++------------------------------------------------ |
3 files changed, 139 insertions(+), 215 deletions(-)
diff --git a/README b/README
@@ -62,11 +62,6 @@ $SOBLINE the current text of the line.
$SOBWRITE the string also written to the process stdin.
-Known issues
-------------
-- Line yank doesn't work with xclip, but does work with xsel.
-
-
Author
------
Hiltjo Posthuma <hiltjo@codemadness.nl>, don't hesitate to contact me for
diff --git a/config.def.h b/config.def.h
@@ -9,19 +9,19 @@ static const char *resizecmd[] = { "/bin/sh", "-c", "$HOME/.sob/scripts/re
static void
line_yank(void)
{
- line_pipeto((char**)yankcmd);
+ line_pipeto((char**)yankcmd, NULL);
}
static void
history_menu(void)
{
- line_pipeto((char**)historycmd);
+ line_pipeto((char**)historycmd, cb_pipe_insert);
}
static void
complete_nick(void)
{
- line_wordpipeto((char**)completenickcmd);
+ line_wordpipeto((char**)completenickcmd, cb_pipe_replaceword);
}
#define CONTROL(ch) ((ch)^0x40)
diff --git a/sob.c b/sob.c
@@ -30,6 +30,7 @@ struct line {
size_t collen; /* total length (in columns) */
};
+static void cb_handleinput(const char *, size_t, size_t);
static void cb_pipe_insert(const char *, size_t, size_t);
static void cb_pipe_replaceword(const char *, size_t, size_t);
@@ -58,10 +59,11 @@ static void line_newline(void);
static void line_out(void);
static void line_prompt(void);
static int line_promptlen(void);
-static int line_pipeto(char **);
-static int line_wordpipeto(char **);
+static int line_pipeto(char **, void (*)(const char *, size_t, size_t));
+static int line_wordpipeto(char **,
+ void (*)(const char *, size_t, size_t));
-static int pipe_read(int, int, char *,
+static int pipe_rw(int, int, char *,
void (*)(const char *, size_t, size_t));
static int pipe_cmd(char *[], char *,
void (*)(const char *, size_t, size_t));
@@ -71,14 +73,11 @@ static void gettermsize(void);
static void handleinput(const unsigned char *, size_t);
static void initialinput(void);
static void resize(void);
-static int run(void);
+static void run(void);
static void setup(void);
static void sighandler(int);
static void usage(void);
-static ssize_t readfd(int, char *, size_t);
-static ssize_t writefd(int, char *, size_t);
-
static size_t colw(const char *, size_t);
static int nonspace(int c);
static size_t utf8len(const char *);
@@ -248,6 +247,14 @@ line_prompt(void)
}
static void
+clear(void)
+{
+ /* clear screen, move cursor to (0, 0) */
+ fprintf(outfp, "\x1b[2J\x1b[H");
+ fflush(outfp);
+}
+
+static void
line_draw(void)
{
size_t i;
@@ -422,7 +429,6 @@ line_deltoend(void)
line.utfpos = line.utflen;
line.collen = colw(line.line, line.bytesiz);
line.colpos = line.collen;
-
line_draw();
}
@@ -445,7 +451,6 @@ line_delwordcursor(void)
line.utflen -= len;
line.collen = colw(line.line, line.bytesiz);
line.colpos = colw(line.line, bs);
-
line_draw();
}
@@ -472,7 +477,6 @@ line_delwordprev(void)
line.utfpos = us;
line.utflen -= len;
line.collen = colw(line.line, line.bytesiz);
-
line_draw();
}
@@ -549,54 +553,12 @@ line_copywordcursor(char *buf, size_t bufsiz)
buf[len] = '\0';
}
-static ssize_t
-readfd(int fd, char *buf, size_t len) {
- ssize_t r, i = 0;
-
- while(len > 0) {
- if((r = read(fd, &buf[i], len)) == -1) {
- if(errno == EINTR)
- continue;
- if(errno == EWOULDBLOCK || errno == EAGAIN)
- return -1;
- fprintf(stderr, "read: %s\n", strerror(errno));
- fflush(stderr);
- return -1;
- } else if(r == 0) {
- return i;
- }
- i += r;
- len -= r;
- }
- return i;
-}
-
-static ssize_t
-writefd(int fd, char *buf, size_t len) {
- ssize_t w, i = 0;
-
- while(len > 0) {
- if((w = write(fd, &buf[i], len)) == -1) {
- if(errno == EINTR)
- continue;
- if(errno == EPIPE)
- return i;
- if(errno == EWOULDBLOCK || errno == EAGAIN)
- continue;
- fprintf(stderr, "write: %s\n", strerror(errno));
- fflush(stderr);
- return -1;
- } else if(w == 0) {
- return i;
- }
- i += w;
- len -= w;
- }
- return i;
-}
-
+/* It will be tried first to write to the pipe and then read.
+ * if fd_in == -1 don't read, if fd_out == -1 or writestr == NULL don't write.
+ * f is the read callback() on the data (buf, read_size, total_read).
+ * if f is NULL no data is read from the pipe. */
static int
-pipe_read(int fd_in, int fd_out, char *writestr,
+pipe_rw(int fd_in, int fd_out, char *writestr,
void (*f)(const char *, size_t, size_t))
{
char buf[PIPE_BUF];
@@ -606,10 +568,15 @@ pipe_read(int fd_in, int fd_out, char *writestr,
ssize_t r;
int maxfd, status = -1, haswritten = 0;
- for(;;) {
+ if(fd_out == -1 || writestr == NULL)
+ haswritten = 1;
+
+ while(isrunning) {
FD_ZERO(&fdr);
FD_ZERO(&fdw);
if(haswritten) {
+ if(!f || fd_in == -1)
+ break;
FD_SET(fd_in, &fdr);
maxfd = fd_in;
} else {
@@ -617,7 +584,7 @@ pipe_read(int fd_in, int fd_out, char *writestr,
maxfd = fd_out;
}
memset(&tv, 0, sizeof(tv));
- tv.tv_sec = 0;
+ tv.tv_usec = 0;
tv.tv_usec = 50000; /* 50 ms */
if((r = select(maxfd + 1, haswritten ? &fdr : NULL,
@@ -627,37 +594,42 @@ pipe_read(int fd_in, int fd_out, char *writestr,
} else if(!r) { /* timeout */
continue;
}
- if(FD_ISSET(fd_out, &fdw)) {
- if(writefd(fd_out, writestr, strlen(writestr)) == -1)
+ if(fd_out != -1 && FD_ISSET(fd_out, &fdw)) {
+ if(write(fd_out, writestr, strlen(writestr)) == -1) {
+ if(errno == EWOULDBLOCK || errno == EAGAIN ||
+ errno == EINTR)
+ continue;
+ else if(errno == EPIPE)
+ goto fini;
goto fini;
- close(fd_out); /* sends EOF */
+ }
+ if(fd_out != STDOUT_FILENO)
+ close(fd_out); /* sends EOF */
fd_out = -1;
haswritten = 1;
}
- if(FD_ISSET(fd_in, &fdr) && haswritten) {
- while(1) {
- r = readfd(fd_in, buf, sizeof(buf));
- if(r == -1) {
- if(errno == EWOULDBLOCK || errno == EAGAIN)
- continue;
- goto fini;
- }
- if(!r) {
- status = 0;
- goto fini;
- }
- if(f) {
- buf[r] = '\0';
- total += (size_t)r;
+ if(haswritten && fd_in != -1 && FD_ISSET(fd_in, &fdr)) {
+ r = read(fd_in, buf, sizeof(buf));
+ if(r == -1) {
+ if(errno == EWOULDBLOCK || errno == EAGAIN)
+ continue;
+ goto fini;
+ }
+ if(r > 0) {
+ buf[r] = '\0';
+ total += (size_t)r;
+ if(f)
f(buf, r, total);
- }
+ } else if(!r) {
+ status = 0;
+ goto fini;
}
}
}
fini:
- if(fd_in != -1)
+ if(fd_in != -1 && fd_in != STDIN_FILENO)
close(fd_in);
- if(fd_out != -1)
+ if(fd_out != -1 && fd_out != STDOUT_FILENO)
close(fd_out);
return status;
}
@@ -669,7 +641,7 @@ pipe_cmd(char *cmd[], char *writestr, void (*f)(const char *, size_t, size_t))
pid_t pid;
int pc[2], cp[2];
- if ((pipe(pc) == -1) || (pipe(cp) == -1)) {
+ if (pipe(pc) == -1 || pipe(cp) == -1) {
perror("pipe");
return -1;
}
@@ -689,6 +661,7 @@ pipe_cmd(char *cmd[], char *writestr, void (*f)(const char *, size_t, size_t))
perror("dup2");
return -1;
}
+
if(execv(cmd[0], (char**)cmd) == -1) {
perror("execv");
_exit(EXIT_FAILURE); /* NOTE: must be _exit */
@@ -705,13 +678,21 @@ pipe_cmd(char *cmd[], char *writestr, void (*f)(const char *, size_t, size_t))
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
- if(pipe_read(cp[0], pc[1], writestr, f) == -1)
+ if(pipe_rw(cp[0], pc[1], writestr, f) == -1)
return -1;
}
return 0;
}
static void
+cb_handleinput(const char *buf, size_t len, size_t total)
+{
+ if(!len || !total)
+ return;
+ handleinput((unsigned char *)buf, len);
+}
+
+static void
cb_pipe_insert(const char *buf, size_t len, size_t total)
{
if(!len || !total)
@@ -732,21 +713,21 @@ cb_pipe_replaceword(const char *buf, size_t len, size_t total)
}
static int
-line_pipeto(char **cmd)
+line_pipeto(char **cmd, void (*f)(const char *, size_t, size_t))
{
- return pipe_cmd(cmd, line.line, cb_pipe_insert);
+ return pipe_cmd(cmd, line.line, f);
}
/* pipe word under cursor and replace it */
static int
-line_wordpipeto(char **cmd)
+line_wordpipeto(char **cmd, void (*f)(const char *, size_t, size_t))
{
char wordbuf[BUFSIZ];
wordbuf[0] = '\0';
line_copywordcursor(wordbuf, sizeof(wordbuf));
- return pipe_cmd((char**)cmd, wordbuf, cb_pipe_replaceword);
+ return pipe_cmd((char**)cmd, wordbuf, f);
}
static void
@@ -767,36 +748,6 @@ sighandler(int signum)
}
static void
-setup(void)
-{
- if(tcgetattr(STDIN_FILENO, &ttystate) == 0) {
- termattrset = 1;
- ttysave = ttystate;
- /* turn off canonical mode and echo */
- ttystate.c_lflag &= ~(ICANON | ECHO);
- ttystate.c_cc[VMIN] = 1;
- /* set the terminal attributes */
- tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
- } else {
- /* not a tty */
- initialinput();
- /* setup tty again because we (re)open "/dev/tty" */
- termattrset = 0;
- if(tcgetattr(STDIN_FILENO, &ttystate) == 0) {
- termattrset = 1;
- ttysave = ttystate;
- /* turn off canonical mode and echo */
- ttystate.c_lflag &= ~(ICANON | ECHO);
- ttystate.c_cc[VMIN] = 1;
- /* set the terminal attributes */
- tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
- }
- }
- /* get terminal window size */
- gettermsize();
-}
-
-static void
gettermsize(void)
{
struct winsize w;
@@ -855,94 +806,92 @@ handleinput(const unsigned char *input, size_t len)
}
}
-static int
-run(void)
+static void
+setup(void)
{
- struct timeval tv;
- struct timespec ts;
- fd_set fdr;
- int r, status = -1;
- unsigned char buf[BUFSIZ];
+ struct sigaction sa;
- if(isrunning) {
- fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
- line_draw();
- }
- while(isrunning) {
- FD_ZERO(&fdr);
- FD_SET(STDIN_FILENO, &fdr);
+ lineoutfp = stdout;
+ outfp = stderr;
+ setlocale(LC_ALL, "");
- memset(&tv, 0, sizeof(tv));
- tv.tv_sec = 0;
- tv.tv_usec = 32000; /* 32 ms */
+ /* signal handling */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sighandler;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGWINCH, &sa, NULL);
+ /* reap zombie childs >=) */
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ /* ignore SIGPIPE, we handle this for write(). */
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
- if((r = select(STDIN_FILENO + 1, &fdr, NULL, NULL, &tv)) == -1) {
- if(errno != EINTR)
- goto fini; /* E_INTR can happen on a signal like SIGWINCH */
- } else if(!r) {
- continue; /* timeout */
- }
- if(FD_ISSET(STDIN_FILENO, &fdr)) {
- r = readfd(STDIN_FILENO, (char *)buf, sizeof(buf));
- if(r == -1) {
- if(errno == EWOULDBLOCK || errno == EAGAIN)
- continue;
- goto fini;
- }
- if(r > 0) {
- buf[r] = '\0';
- handleinput(buf, r);
- } else if(r == 0) {
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
- nanosleep(&ts, NULL);
- }
+ if(tcgetattr(STDIN_FILENO, &ttystate) == 0) {
+ termattrset = 1;
+ ttysave = ttystate;
+ /* turn off canonical mode and echo */
+ ttystate.c_lflag &= ~(ICANON | ECHO);
+ ttystate.c_cc[VMIN] = 1;
+ /* set the terminal attributes */
+ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
+ } else {
+ /* not a tty */
+ initialinput();
+ /* setup tty again because we (re)open "/dev/tty" */
+ termattrset = 0;
+ if(tcgetattr(STDIN_FILENO, &ttystate) == 0) {
+ termattrset = 1;
+ ttysave = ttystate;
+ /* turn off canonical mode and echo */
+ ttystate.c_lflag &= ~(ICANON | ECHO);
+ ttystate.c_cc[VMIN] = 1;
+ /* set the terminal attributes */
+ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
}
-fini:
- return status;
+ /* get terminal window size */
+ gettermsize();
}
static void
-usage(void)
+run(void)
{
- fprintf(stderr, "usage: %s [-p prompt]\n", argv0);
- exit(EXIT_FAILURE);
-}
+ if(!isrunning)
+ return;
-static void
-clear(void)
-{
- /* clear screen, move cursor to (0, 0) */
- fprintf(outfp, "\x1b[2J\x1b[H");
- fflush(outfp);
+ /* clear screen */
+ clear();
+
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
+ line_draw();
+
+ pipe_rw(STDIN_FILENO, -1, NULL, cb_handleinput);
+
+ /* cleanup: restore terminal attributes */
+ if(termattrset) {
+ ttystate.c_lflag = ttysave.c_lflag;
+ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
+ }
}
static void
initialinput(void)
{
- unsigned char buf[BUFSIZ];
- int fd, r;
+ int fd;
/* read initial input from stdin */
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
- while(1) {
- r = readfd(STDIN_FILENO, (char *)buf, sizeof(buf));
- if(r == -1) {
- if(errno == EWOULDBLOCK || errno == EAGAIN)
- continue;
- break;
- }
- if(r == 0)
- break;
- buf[r] = '\0';
- handleinput(buf, r);
- }
+ pipe_rw(STDIN_FILENO, -1, NULL, cb_handleinput);
/* restore terminal attributes */
if(termattrset) {
ttystate.c_lflag = ttysave.c_lflag;
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
+ if(!isrunning)
+ return;
/* close and re-attach to stdin */
close(STDIN_FILENO);
if((fd = open("/dev/tty", O_RDONLY | O_NONBLOCK)) == -1) {
@@ -955,11 +904,16 @@ initialinput(void)
}
}
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-p prompt]\n", argv0);
+ exit(EXIT_FAILURE);
+}
+
int
main(int argc, char **argv)
{
- struct sigaction sa;
-
ARGBEGIN {
case 'p':
prompt = EARGF(usage());
@@ -968,33 +922,8 @@ main(int argc, char **argv)
usage();
} ARGEND;
- lineoutfp = stdout;
- outfp = stderr;
- setlocale(LC_ALL, "");
-
- /* signal handling */
- memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = sighandler;
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGWINCH, &sa, NULL);
- /* reap zombie childs >=) */
- sa.sa_handler = SIG_IGN;
- sigaction(SIGCHLD, &sa, NULL);
- /* ignore SIGPIPE, we handle this for write(). */
- sigaction(SIGPIPE, &sa, NULL);
-
setup();
-
- /* clear screen */
- clear();
run();
- /* cleanup: restore terminal attributes */
- if(termattrset) {
- ttystate.c_lflag = ttysave.c_lflag;
- tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
- }
return EXIT_SUCCESS;
}