sob

Simple output bar
git clone https://git.sinitax.com/codemadness/sob
Log | Files | Refs | README | LICENSE | Upstream | sfeed.txt

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:
MREADME | 5-----
Mconfig.def.h | 6+++---
Msob.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; }