rework some things: - sob - simple output bar
 (HTM) git clone git://git.codemadness.org/sob
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 12656a030acf296f0b6299175f982a6eae630646
 (DIR) parent b072bb3bd5fdff06b0a12cd7bc6a8f94962ab97d
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Thu,  2 Oct 2014 20:55:49 +0000
       
       rework some things:
       
       - remove dependency on ncurses.
       - just use stdout for output instead of -f, this makes using it with tee
         trivial. use stderr for the "interface".
       - fix resize bug select() == 1 with errno EINTR.
       
       Diffstat:
         M README                              |      24 ++++++++++++++++++------
         M TODO                                |      25 +++++--------------------
         M config.def.h                        |      74 ++++++++++++++++++-------------
         M sob.1                               |       5 -----
         M sob.c                               |     417 +++++++++++++++++++------------
       
       5 files changed, 318 insertions(+), 227 deletions(-)
       ---
 (DIR) diff --git a/README b/README
       @@ -1,25 +1,37 @@
       -Simple output bar
       -=================
       +sob - simple output bar
       +=======================
        
        
        Dependencies
        ------------
       -- Ncurses (at the moment, alternatives will be investigated).
        - libc
        
        
        Features
        --------
       -- Custom prompt.
       -- Easy to write scripts to pipe input/output.
       +- Small (in size and memory), not much dependencies.
       +- Custom prompt (including color support).
       +- Easy to write custom completion scripts or special actions.
        - Custom action on SIGWINCH (window resize).
        - Familiar and customizable keybinds (in config.h).
        - Example scripts for:
       +        - History list.
       +        - Nickname completion (for IRC).
                - Word completion.
       -        - History
                - Yank line (xsel).
        
        
       +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
       +bug reports or patches!
       +
       +
        License
        -------
        See LICENSE file.
 (DIR) diff --git a/TODO b/TODO
       @@ -1,22 +1,7 @@
       -13:32 <@TLH> Evil_Bob: please support C-p for up in history C-n for down in history C-f for forward cursor C-b for backwards, C-a and 
       -             C-e, C-u, C-k (kill to rest of line), C-y, C-w
       -                         13:32 <@TLH> what else
       -                         13:32 <@TLH> C-j 
       -                         13:32 <@TLH> :P
       -                         13:33 <@TLH> C-m as an alias to C-j
       -
       +- tmux: on newline, sometimes there are render issues.
       +- tmux: arrow up and down dont start history script.
       +- for history, on exit failure, don't clear line.
        - scripts: for word complete, if there is only one match, just print directly.
       -
       -- test draw on wrapped lines.
       -
       -- line_yank doesn't work with xclip, but works with xsel...
       -
        - optimize:
       -        reduce redraws (line_redraw and line_cursor_update).
       -
       -- selections / marks? (delete/pipe etc on selection)?
       -- cycle completions? (with tab for example).
       -- keybind to go to word next and previous (ctrl+arrow left/right).
       -- prompt callback? allow to update prompt?
       -
       -- try libedit, else just use ncurses.
       +  reduce redraws (line_redraw and line_cursor_update).
       +- make example url grab script.
 (DIR) diff --git a/config.def.h b/config.def.h
       @@ -1,4 +1,4 @@
       -static const char *prompt            = "> ";
       +static const char *prompt            = "\x01\x1b[32m\x01> \x01\x1b[0m";
        static const char *completenickcmd[] = { "/bin/sh", "-c", "$HOME/.sob/scripts/complete_nick", NULL };
        static const char *historycmd[]      = { "/bin/sh", "-c", "$HOME/.sob/scripts/history",       NULL };
        static const char *yankcmd[]         = { "/bin/sh", "-c", "/bin/xsel -i -p",                  NULL };
       @@ -22,38 +22,48 @@ complete_nick(void)
                line_wordpipeto((char**)completenickcmd);
        }
        
       +#define CONTROL(ch) ((ch)^0x40)
       +
       +#define KEY_HOME       "\x1b[\x31\x7e"
       +#define KEY_END        "\x1b[\x34\x7e"
       +#define KEY_CTRL_LEFT  "\x1b\x5b\x31\x3b\x35\x44"
       +#define KEY_CTRL_RIGHT "\x1b\x5b\x31\x3b\x35\x43"
       +#define KEY_LEFT       "\x1b\x4f\x44"
       +#define KEY_RIGHT      "\x1b\x4f\x43"
       +#define KEY_DOWN       "\x1b\x4f\x42"
       +#define KEY_UP         "\x1b\x4f\x41"
       +#define KEY_DC         "\x1b\x5b\x33\7e"  /* del */
       +
        static struct keybind {
       -        int key;
       +        unsigned char key[16];
                void (*func)(void);
        } keybinds[] = {
       -        { CONTROL('A'),  line_cursor_begin },
       -        { CONTROL('E'),  line_cursor_end },
       -        { KEY_HOME,      line_cursor_begin },
       -        { KEY_END,       line_cursor_end },
       -        { CONTROL('B'),  line_cursor_prev },
       -        { KEY_LEFT,      line_cursor_prev },
       -        { CONTROL('F'),  line_cursor_next },
       -        { KEY_RIGHT,     line_cursor_next },
       -        { CONTROL('W'),  line_delwordback },
       -        { CONTROL('H'),  line_delcharback },
       -        { CONTROL('U'),  line_clear },
       -        { KEY_DL,        line_clear },
       -        { CONTROL('K'),  line_deltoend },
       -        { KEY_SDC,       line_delcharnext },
       -        { KEY_DC,        line_delcharnext },
       -        { KEY_BACKSPACE, line_delcharback },
       -        { CONTROL('M'),  line_newline },
       -        { CONTROL('J'),  line_newline },
       -        { '\r',          line_newline },
       -        { '\n',          line_newline },
       -        { KEY_ENTER,     line_newline },
       -        { CONTROL('Y'),  line_yank },
       -        { KEY_EXIT,      line_exit },
       -        { 0x04,          line_exit }, /* EOT */
       -        { KEY_EOL,       line_deltoend },
       -        { KEY_UP,        history_menu },
       -        { KEY_DOWN,      history_menu },
       -        { CONTROL('P'),  history_menu },
       -        { CONTROL('N'),  history_menu },
       -        { '\t',          complete_nick },
       +        { { CONTROL('A')   },  line_cursor_begin },
       +        { { CONTROL('E')   },  line_cursor_end },
       +        { { KEY_HOME       },  line_cursor_begin },
       +        { { KEY_END        },  line_cursor_end },
       +        { { CONTROL('B')   },  line_cursor_prev },
       +        { { KEY_LEFT       },  line_cursor_prev },
       +        { { CONTROL('F')   },  line_cursor_next },
       +        { { KEY_RIGHT      },  line_cursor_next },
       +        { { KEY_CTRL_LEFT  },  line_cursor_wordprev },
       +        { { KEY_CTRL_RIGHT },  line_cursor_wordnext },
       +        { { CONTROL('W')   },  line_delwordback },
       +        { { CONTROL('H')   },  line_delcharback },
       +        { { CONTROL('U')   },  line_clear },
       +        { { CONTROL('K')   },  line_deltoend },
       +        { { KEY_DC         },  line_delcharnext },
       +        { { CONTROL('H')   },  line_delcharback },
       +        { { CONTROL('M')   },  line_newline },
       +        { { CONTROL('J')   },  line_newline },
       +        { { '\r'           },  line_newline },
       +        { { '\n'           },  line_newline },
       +        { { CONTROL('Y')   },  line_yank },
       +        { { CONTROL('D')   },  line_exit },
       +        { { CONTROL('E')   },  line_deltoend },
       +        { { KEY_UP         },  history_menu },
       +        { { KEY_DOWN       },  history_menu },
       +        { { CONTROL('P')   },  history_menu },
       +        { { CONTROL('N')   },  history_menu },
       +        { { '\t'           },  complete_nick },
        };
 (DIR) diff --git a/sob.1 b/sob.1
       @@ -3,8 +3,6 @@
        sob \- simple output bar
        .SH SYNOPSIS
        .B sob
       -.RB < \-f
       -.IR outfile >
        .RB [ \-l
        .IR line ]
        .RB [ \-p
       @@ -13,9 +11,6 @@ sob \- simple output bar
        sob is a simple line editor.
        .SH OPTIONS
        .TP
       -.B \-f " outfile"
       -output file.
       -.TP
        .B \-l " line"
        initial input on line.
        .TP
 (DIR) diff --git a/sob.c b/sob.c
       @@ -7,19 +7,19 @@
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
       +#include <sys/ioctl.h>
        #include <sys/select.h>
        #include <unistd.h>
       -
       -#include <ncurses.h>
       +#include <termios.h>
        
        #include "arg.h"
        char *argv0;
        
        #include "util.h"
        
       -#define CONTROL(ch) ((ch)^0x40)
       -#define LEN(x)      (sizeof (x) / sizeof *(x))
       -#define MAX(A, B)   ((A) > (B) ? (A) : (B))
       +#define LEN(x)    (sizeof (x) / sizeof *(x))
       +#define MAX(A, B) ((A) > (B) ? (A) : (B))
       +#define MIN(A, B) ((A) < (B) ? (A) : (B))
        
        struct line {
                char line[BUFSIZ];
       @@ -27,58 +27,54 @@ struct line {
                size_t pos;
        };
        
       +static void   line_clear(void);
       +static void   line_copywordcursor(char *, size_t);
       +static void   line_cursor_begin(void);
       +static void   line_cursor_end(void);
       +static void   line_cursor_move(size_t);
       +static void   line_cursor_next(void);
       +static void   line_cursor_prev(void);
       +static void   line_cursor_wordprev(void);
       +static void   line_cursor_wordnext(void);
       +static void   line_delcharback(void);
       +static void   line_delcharnext(void);
       +static void   line_deltoend(void);
       +static void   line_delwordback(void);
       +static void   line_delwordcursor(void);
       +static void   line_draw(void);
       +static void   line_exit(void);
       +static void   line_getwordpos(size_t *, size_t *);
       +static void   line_inserttext(const char *);
       +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 void   line_set(const char *);
       +static void   line_wordpipeto(char **);
       +static int    pipe_readline(int, int, char *, char *, size_t);
       +static int    pipe_cmd(char *[], char *, char *, size_t);
       +
       +static void   cleanup(void);
       +static void   gettermsize(void);
       +static void   handleinput(const unsigned char *, size_t);
       +static void   resize(void);
       +static int    run(void);
       +static void   setup(void);
       +static void   sighandler(int);
       +static void   usage(void);
       +
       +static struct termios ttystate, ttysave;
       +
        static struct line line;
       -static int isrunning = 1;
       -static char * outname = NULL;
       -static WINDOW * win = NULL;
       -
       -static void line_clear(void);
       -static void line_copywordcursor(char *buf, size_t bufsiz);
       -static void line_cursor_begin(void);
       -static void line_cursor_end(void);
       -static void line_cursor_next(void);
       -static void line_cursor_prev(void);
       -static void line_cursor_update(void);
       -static void line_delcharback(void);
       -static void line_delcharnext(void);
       -static void line_deltoend(void);
       -static void line_delwordback(void);
       -static void line_delwordcursor(void);
       -static void line_exit(void);
       -static void line_getwordpos(unsigned int *start, unsigned int *end);
       -static void line_insertchar(int c);
       -static void line_inserttext(const char *s);
       -static void line_newline(void);
       -static int  line_out(void);
       -static void line_prompt(void);
       -static int  line_pipeto(char **cmd);
       -static void line_redraw(size_t max);
       -static void line_set(const char *s);
       -static void line_wordpipeto(char **cmd);
       -static int  pipe_readline(int fd_in, int fd_out, char *writestr,
       -                          char *outbuf, size_t outbufsiz);
       -static int  pipe_cmd(char *cmd[], char *writestr, char *outbuf,
       -                          size_t outbufsiz);
       -static void resize(void);
       -static void sighandler(int signum);
       -static void setup(void);
       -static void cleanup(void);
       -static void run(void);
       -static void usage(void);
       +static int    cols, rows;
       +static int    isrunning = 1;
       +static FILE * outfp = NULL;
       +static FILE * lineoutfp = NULL;
        
        #include "config.h"
        
        static void
       -line_insertchar(int c)
       -{
       -        char s[2];
       -
       -        s[0] = c;
       -        s[1] = '\0';
       -        line_inserttext(s);
       -}
       -
       -static void
        line_inserttext(const char *s)
        {
                size_t len;
       @@ -94,9 +90,10 @@ line_inserttext(const char *s)
                        memmove(&line.line[line.pos + len], &line.line[line.pos], line.len - line.pos);
                        memcpy(&line.line[line.pos], s, len);
                }
       -        line.pos += len;
                line.len += len;
       +        line.pos += len;
                line.line[line.len + 1] = '\0';
       +        line_draw();
        }
        
        static void
       @@ -107,94 +104,137 @@ line_set(const char *s)
                line.pos = line.len;
        }
        
       +/* like mksh, toggle counting of escape codes in prompt with "\x01" */
       +static int
       +line_promptlen(void)
       +{
       +        size_t i;
       +        int t = 0, n = 0;
       +
       +        for(i = 0; prompt[i]; i++) {
       +                if(prompt[i] == 1)
       +                        t = !t;
       +                else if(!t)
       +                        n++;
       +        }
       +        return n;
       +}
       +
        static void
        line_prompt(void)
        {
                size_t i;
        
       -        wmove(win, 0, 0);
       -        for(i = 0; prompt[i]; i++)
       -                waddch(win, prompt[i]);
       +        for(i = 0; prompt[i]; i++) {
       +                if(prompt[i] != 1)
       +                        fputc(prompt[i], outfp);
       +        }
        }
        
        static void
       -line_redraw(size_t max)
       +line_draw(void)
        {
                size_t n;
        
       +        /* clear */
       +        fprintf(outfp, "\x1b[2J\x1b[H");
       +
                line_prompt();
       +        for(n = 0; line.line[n] && n < line.len; n++)
       +                fputc(line.line[n], outfp);
        
       -        for(n = 0; line.line[n] && n < line.len && n < max; n++)
       -                waddch(win, line.line[n]);
       -        for(; n < max; n++)
       -                waddch(win, ' ');
       -        wrefresh(win);
       +        line_cursor_move(line.pos);
        }
        
       -static int
       +static void
        line_out(void)
        {
       -        FILE *fp;
       -        if(!(fp = fopen(outname, "a"))) {
       -                fprintf(stderr, "fopen: '%s': %s\n", outname, strerror(errno));
       -                return -1;
       +        fprintf(lineoutfp, "%s\n", line.line);
       +        fflush(lineoutfp);
       +}
       +
       +static void
       +line_cursor_move(size_t newpos)
       +{
       +        size_t len, y = 0, x = newpos;
       +
       +        len = line_promptlen();
       +        x += len;
       +
       +        /* linewrap */
       +        if(x > cols - 1) {
       +                x = x % cols;
       +                y = ((newpos + len) - x) / cols;
                }
       -        fprintf(fp, "%s\n", line.line);
       -        fflush(fp);
       -        fclose(fp);
       -        return 0;
       +        fprintf(outfp, "\x1b[%lu;%luH", y + 1, x + 1);
       +        fflush(outfp);
       +        line.pos = newpos;
       +}
       +
       +static void
       +line_cursor_wordprev(void)
       +{
       +        size_t s, e;
       +
       +        line_getwordpos(&s, &e);
       +        if(s == line.pos) {
       +                while(s > 0 && isspace(line.line[s - 1]))
       +                        s--;
       +        }
       +        line_cursor_move(s);
        }
        
        static void
       -line_cursor_update(void)
       +line_cursor_wordnext(void)
        {
       -        wmove(win, 0, line.pos + strlen(prompt));
       +        size_t s, e;
       +
       +        line_getwordpos(&s, &e);
       +        if(e == line.pos) {
       +                while(e < line.len && line.line[e] && isspace(line.line[e]))
       +                        e++;
       +        }
       +        line_cursor_move(e);
        }
        
        static void
        line_cursor_begin(void)
        {
       -        line.pos = 0;
       -        line_cursor_update();
       +        line_cursor_move(0);
        }
        
        static void
        line_cursor_prev(void)
        {
                if(line.pos > 0)
       -                line.pos--;
       -        line_cursor_update();
       +                line_cursor_move(line.pos - 1);
        }
        
        static void
        line_cursor_next(void)
        {
                if(line.pos < line.len)
       -                line.pos++;
       -        line_cursor_update();
       +                line_cursor_move(line.pos + 1);
        }
        
        static void
        line_cursor_end(void)
        {
       -        line.pos = line.len;
       -        line_cursor_update();
       +        line_cursor_move(line.len);
        }
        
        static void
        line_clear(void)
        {
       +        line_cursor_begin();
                line.line[0] = '\0';
       -        line_redraw(line.len);
                line.len = 0;
       -        line_cursor_begin();
       +        line_draw();
        }
        
        static void
        line_delcharnext(void)
        {
       -        size_t oldlen = line.len;
       -
                if(line.pos == line.len || line.len <= 0)
                        return;
        
       @@ -202,41 +242,35 @@ line_delcharnext(void)
                        line.line[line.len - line.pos - 1]);
                line.len--;
                line.line[line.len] = '\0';
       -        line_redraw(oldlen);
       -        line_cursor_update();
       +        line_draw();
        }
        
        static void
        line_delcharback(void)
        {
       -        size_t oldlen = line.len;
       -
                if(line.pos <= 0 || line.len <= 0)
                        return;
                memmove(&line.line[line.pos - 1], &line.line[line.pos],
                        line.line[line.len - line.pos]);
                line.len--;
                line.line[line.len] = '\0';
       -        line_redraw(oldlen);
                line_cursor_prev();
       +        line_draw();
        }
        
        static void
        line_deltoend(void)
        {
       -        size_t oldlen = line.len;
       -
                line.line[line.pos] = '\0';
                line.len = line.pos;
       -        line_redraw(oldlen);
                line_cursor_end();
       +        line_draw();
        }
        
        static void
        line_delwordcursor(void)
        {
       -        unsigned int s, e;
       -        size_t len, oldlen = line.len;
       +        size_t len, s, e;
        
                line_getwordpos(&s, &e);
        
       @@ -245,14 +279,13 @@ line_delwordcursor(void)
                line.len -= len;
                line.pos = s;
                line.line[line.len] = '\0';
       -        line_redraw(MAX(line.len, oldlen));
       -        line_cursor_update();
       +        line_draw();
        }
        
        static void
        line_delwordback(void)
        {
       -        size_t i, len, oldlen = line.len;
       +        size_t i, len;
        
                if(line.pos <= 0 || line.len <= 0)
                        return;
       @@ -271,8 +304,7 @@ line_delwordback(void)
                line.pos = i;
                line.len -= len;
                line.line[line.len] = '\0';
       -        line_redraw(oldlen);
       -        line_cursor_update();
       +        line_draw();
        }
        
        static void
       @@ -280,19 +312,17 @@ line_newline(void)
        {
                line_out();
                line_clear();
       -        line_prompt();
       -        wrefresh(win);
        }
        
        static void
        line_exit(void)
        {
       -        line_newline();
       +        line_out();
                isrunning = 0;
        }
        
        static void
       -line_getwordpos(unsigned int *start, unsigned int *end)
       +line_getwordpos(size_t *start, size_t *end)
        {
                size_t i;
        
       @@ -311,8 +341,7 @@ line_getwordpos(unsigned int *start, unsigned int *end)
        static void
        line_copywordcursor(char *buf, size_t bufsiz)
        {
       -        unsigned int s, e;
       -        size_t len;
       +        size_t s, e, len;
        
                line_getwordpos(&s, &e);
                len = e - s;
       @@ -358,9 +387,11 @@ pipe_readline(int fd_in, int fd_out, char *writestr, char *outbuf,
                                        if((r = read(fd_in, buf, sizeof(buf))) == -1)
                                                goto fini;
                                        buf[r] = '\0';
       -                                if((p = strpbrk(buf, "\r\n")))
       -                                        *p = '\0';
       -                                strlcpy(outbuf, buf, sizeof(outbuf));
       +                                if(outbuf) {
       +                                        if((p = strpbrk(buf, "\r\n")))
       +                                                *p = '\0';
       +                                        strlcpy(outbuf, buf, outbufsiz);
       +                                }
                                        status = 0;
                                        goto fini;
                                }
       @@ -434,21 +465,25 @@ pipe_cmd(char *cmd[], char *writestr, char *outbuf, size_t outbufsiz)
        static int
        line_pipeto(char **cmd)
        {
       -        size_t oldlen = line.len;
       +        char buf[BUFSIZ];
       +        size_t len;
        
       -        if(pipe_cmd(cmd, line.line, line.line, sizeof(line.line)) == -1)
       +        if(pipe_cmd(cmd, line.line, buf, sizeof(buf)) == -1)
                        return -1;
       -        line.len = strlen(line.line);
       -        line_redraw(MAX(line.len, oldlen));
       +        if(buf[0] == '\0')
       +                return -1;
       +        len = strlcpy(line.line, buf, sizeof(line.line));
       +        line.len = len;
                line_cursor_end();
       +        line_draw();
                return 0;
        }
        
       +/* pipe word under cursor and replace it */
        static void
        line_wordpipeto(char **cmd)
        {
                char wordbuf[BUFSIZ], outbuf[BUFSIZ];
       -        size_t oldlen = line.len;
        
                outbuf[0] = '\0';
                wordbuf[0] = '\0';
       @@ -462,8 +497,7 @@ line_wordpipeto(char **cmd)
        
                line_delwordcursor();
                line_inserttext(outbuf);
       -        line_redraw(MAX(line.len, oldlen));
       -        line_cursor_update();
       +        line_draw();
        }
        
        static void
       @@ -472,6 +506,10 @@ sighandler(int signum)
                if(signum == SIGTERM) {
                        isrunning = 0;
                        cleanup();
       +        } else if(signum == SIGWINCH) {
       +                gettermsize();
       +                resize();
       +                line_draw();
                }
        }
        
       @@ -480,79 +518,134 @@ setup(void)
        {
                struct sigaction sa;
        
       -        initscr();
       -        win = stdscr;
       -        cbreak();
       -        noecho();
       -        nonl();
       -        nodelay(win, FALSE);
       -        keypad(win, TRUE);
       -        curs_set(1);
       -        ESCDELAY = 20;
       +        tcgetattr(STDIN_FILENO, &ttystate);
       +        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();
        
                /* signal handling */
                memset(&sa, 0, sizeof(sa));
                sa.sa_flags = SA_RESTART;
                sa.sa_handler = sighandler;
                sigaction(SIGTERM, &sa, NULL);
       +        sigaction(SIGWINCH, &sa, NULL);
       +}
       +
       +static void
       +gettermsize(void)
       +{
       +        struct winsize w;
       +
       +        if(ioctl(STDIN_FILENO, TIOCGWINSZ, &w) == -1)
       +                return;
       +        cols = w.ws_col;
       +        rows = w.ws_row;
        }
        
        static void
        resize(void)
        {
       -        line_pipeto((char **)resizecmd);
       +        pipe_cmd((char **)resizecmd, line.line, NULL, 0);
        }
        
        static void
        cleanup(void)
        {
       -        endwin();
       +        ttystate.c_lflag = ttysave.c_lflag;
       +        /* set the terminal attributes. */
       +        tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
        }
        
       +#if 0
        static void
       -run(void)
       +debuginput(const unsigned char *key, size_t len)
        {
                size_t i;
       -        int c, ismatch = 0;
        
       -        line_redraw(line.len);
       -        while(isrunning) {
       -                c = wgetch(win);
       -                switch(c) {
       -                case ERR:
       -                        isrunning = 0;
       -                        break;
       -                case 0x1b: /* ignore unbinded escape sequences */
       -                        nodelay(win, TRUE);
       -                        while((c = wgetch(win)) != ERR);
       -                        nodelay(win, FALSE);
       -                        break;
       -                case KEY_RESIZE:
       -                        resize();
       +        for(i = 0; i < len; i++)
       +                fprintf(stdout, "\\x%2x", key[i]);
       +        fputc('\n', stdout);
       +        fflush(stdout);
       +}
       +#endif
       +
       +static void
       +handleinput(const unsigned char *key, size_t len)
       +{
       +        size_t i;
       +        int ismatch = 0;
       +
       +        if(key[0] == '\0')
       +                return;
       +        for(i = 0; i < LEN(keybinds); i++) {
       +                if(len == strlen((char*)keybinds[i].key) &&
       +                   memcmp(key, keybinds[i].key, len) == 0) {
       +                        keybinds[i].func();
       +                        ismatch = 1;
                                break;
       -                default:
       -                        ismatch = 0;
       -                        for(i = 0; i < LEN(keybinds); i++) {
       -                                if(keybinds[i].key == c) {
       -                                        ismatch = 1;
       -                                        keybinds[i].func();
       -                                        break;
       -                                }
       -                        }
       -                        if(!ismatch) {
       -                                line_insertchar(c);
       -                                line_redraw(line.len);
       -                                line_cursor_update();
       -                                wrefresh(win);
       +                }
       +        }
       +        if(!ismatch) {
       +                /* ignore unhandled escape sequence */
       +                if(key[0] == '\x1b' || iscntrl(key[0]))
       +                        return;
       +                line_inserttext((char*)key);
       +        }
       +}
       +
       +static int
       +run(void)
       +{
       +        struct timeval tv;
       +        fd_set fdr;
       +        int fd_in, r, status = -1;
       +        unsigned char buf[BUFSIZ];
       +
       +        line_draw();
       +
       +        fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
       +        while(isrunning) {
       +                FD_ZERO(&fdr);
       +                FD_SET(STDIN_FILENO, &fdr);
       +
       +                memset(&tv, 0, sizeof(tv));
       +                tv.tv_sec = 0;
       +                tv.tv_usec = 50000; /* 50 ms */
       +
       +                errno = 0;
       +                if((r = select(STDIN_FILENO + 1, &fdr, NULL, NULL, &tv)) == -1) {
       +                        if(errno != EINTR)
       +                                goto fini; /* E_INTR can happen on SIGWINCH */
       +                } else if(!r) {
       +                        continue; /* timeout */
       +                }
       +
       +                if(FD_ISSET(fd_in, &fdr)) {
       +                        errno = 0;
       +                        if((r = read(STDIN_FILENO, buf, sizeof(buf))) == -1) {
       +                                if(errno != EAGAIN && errno != EWOULDBLOCK)
       +                                        goto fini;
       +                        } else {
       +                                buf[r] = '\0';
       +                                handleinput(buf, r);
                                }
                        }
                }
       +fini:
       +        return status;
        }
        
        static void
        usage(void)
        {
       -        fprintf(stderr, "usage: %s <-f outfile> [-l line] [-p prompt]\n", argv0);
       +        fprintf(stderr, "usage: %s [-l line] [-p prompt]\n", argv0);
                exit(EXIT_FAILURE);
        }
        
       @@ -560,9 +653,6 @@ int
        main(int argc, char **argv)
        {
                ARGBEGIN {
       -        case 'f':
       -                outname = EARGF(usage());
       -                break;
                case 'l':
                        line_set(EARGF(usage()));
                        break;
       @@ -573,9 +663,8 @@ main(int argc, char **argv)
                        usage();
                } ARGEND;
        
       -        if(!outname)
       -                usage();
       -
       +        lineoutfp = stdout;
       +        outfp = stderr;
                setlocale(LC_ALL, "");
                setup();
                run();