various improvements and cleanup: - sob - simple output bar
 (HTM) git clone git://git.codemadness.org/sob
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 6d27363d00b6e9901126df2f4a70b8ba93bc1602
 (DIR) parent 7c32499ad075426acaa9f44813059b630aa52a12
 (HTM) 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(-)
       ---
 (DIR) 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
 (DIR) 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)
 (DIR) 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;
        }