URI: 
       it is not that big, a single file will do - iomenu - interactive terminal-based selection menu
  HTML git clone git://bitreich.org/iomenu git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/iomenu
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
   DIR commit 251f7a6436516116e1dc7c9e674a9be1f58eb1b7
   DIR parent b69d6f5982f4c2b96f8dee1369c48180ed31b035
  HTML Author: Josuah Demangeon <mail@josuah.net>
       Date:   Thu, 16 Nov 2017 11:09:25 +0100
       
       it is not that big, a single file will do
       
       Diffstat:
         M Makefile                            |      20 +++++++++++---------
         D buffer.c                            |     155 -------------------------------
         D buffer.h                            |       3 ---
         D control.c                           |     152 -------------------------------
         D control.h                           |       4 ----
         D display.c                           |     120 -------------------------------
         D display.h                           |       2 --
         A iomenu.c                            |     532 +++++++++++++++++++++++++++++++
         D iomenu.h                            |      18 ------------------
         D main.c                              |     151 -------------------------------
         D main.h                              |       1 -
       
       11 files changed, 543 insertions(+), 615 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -1,22 +1,24 @@
        CFLAGS = -std=c89 -pedantic -Wall -Wextra -g -D_POSIX_C_SOURCE=200809L
        
       -OBJ = buffer.o control.o display.o main.o utf8.o
       -INC = buffer.h control.h display.h main.h utf8.h iomenu.h
       +OBJ = iomenu.o utf8.o
        
        all: iomenu
        
       -iomenu: $(OBJ)
       -        $(CC) $(LDFLAGS) $(OBJ) -o $@
       +.c.o:
       +        ${CC} -c -o $@ ${CFLAGS} $<
        
       -$(OBJ): $(INC)
       +iomenu: ${OBJ}
       +        ${CC} -o $@ ${LDFLAGS} ${OBJ}
       +
       +${OBJ}: utf8.h
        
        clean:
                rm -f *.o *.core iomenu
        
        install: iomenu
       -        mkdir -p  $(PREFIX)/share/man/man1
       -        cp *.1    $(PREFIX)/share/man/man1
       -        mkdir -p  $(PREFIX)/bin
       -        cp iomenu $(PREFIX)/bin
       +        mkdir -p  ${PREFIX}/share/man/man1
       +        cp *.1    ${PREFIX}/share/man/man1
       +        mkdir -p  ${PREFIX}/bin
       +        cp iomenu ${PREFIX}/bin
        
        .PHONY: all clean install
   DIR diff --git a/buffer.c b/buffer.c
       @@ -1,155 +0,0 @@
       -#include <sys/ioctl.h>
       -
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <string.h>
       -#include <limits.h>
       -#include <unistd.h>
       -
       -#include "iomenu.h"
       -#include "buffer.h"
       -#include "main.h"
       -#include "control.h"
       -
       -/*
       - * Keep the line if it match every token (in no particular order, and allowed to
       - * be overlapping).
       - */
       -static int
       -match_line(char *line, char **tokv, int tokc)
       -{
       -        if (opt['#'] && line[0] == '#')
       -                return 2;
       -        while (tokc-- > 0)
       -                if (strstr(line, tokv[tokc]) == NULL)
       -                        return 0;
       -        return 1;
       -}
       -
       -/*
       - * As we use a single buffer for the whole stdin, we only need to free it once
       - * and it will free all the lines.
       - */
       -void
       -free_lines(void)
       -{
       -        extern        char        **linev;
       -        extern        char        **matchv;
       -
       -        if (linev) {
       -                free(linev[0]);
       -                free(linev);
       -        }
       -        if (matchv)
       -                free(matchv);
       -}
       -
       -/*
       - * Split a buffer into an array of lines, without allocating memory for every
       - * line, but using the input buffer and replacing characters.
       - */
       -void
       -split_lines(char *buf)
       -{
       -        extern        char        **linev;
       -        extern        char        **matchv;
       -        extern        int          linec;
       -
       -        char         *b;
       -        char        **lv;
       -        char        **mv;
       -
       -        linec = 0;
       -        b = buf;
       -        while ((b = strchr(b + 1, '\n')))
       -                linec++;
       -        if (!linec)
       -                linec = 1;
       -        if (!(lv = linev = calloc(linec + 1, sizeof (char **))))
       -                die("calloc");
       -        if (!(mv = matchv = calloc(linec + 1, sizeof (char **)))) {
       -                free(linev);
       -                die("calloc");
       -        }
       -        *mv = *lv = b = buf;
       -        while ((b = strchr(b, '\n'))) {
       -                *b = '\0';
       -                mv++, lv++;
       -                *mv = *lv = ++b;
       -        }
       -}
       -
       -/*
       - * Read stdin in a single malloc-ed buffer, realloc-ed to twice its size every
       - * time the previous buffer is filled.
       - */
       -void
       -read_stdin(void)
       -{
       -        size_t         size;
       -        size_t         len;
       -        size_t         off;
       -        char        *buf;
       -        char        *b;
       -
       -        size = BUFSIZ;
       -        off = 0;
       -        buf = malloc(size);
       -        while ((len = read(STDIN_FILENO, buf + off, size - off)) > 0) {
       -                off += len;
       -                if (off >= size >> 1) {
       -                        size <<= 1;
       -                        if (!(b = realloc(buf, size + 1))) {
       -                                free(buf);
       -                                die("realloc");
       -                        }
       -                        buf = b;
       -                }
       -        }
       -        buf[off] = '\0';
       -        split_lines(buf);
       -}
       -
       -/*
       - * First split input into token, then match every token independently against
       - * every line.  The matching lines fills matchv.
       - */
       -void
       -filter(void)
       -{
       -        extern        char        **linev;
       -        extern        char        **matchv;
       -        extern        int          linec;
       -        extern        int          matchc;
       -        extern        int          current;
       -
       -        int          tokc;
       -        int          n;
       -        char        **tokv;
       -        char         *s;
       -        char          buf[sizeof (input)];
       -
       -        tokv = NULL;
       -        current = 0;
       -        strcpy(buf, input);
       -        tokc = 0;
       -        n = 0;
       -        s = strtok(buf, " ");
       -        while (s) {
       -                if (tokc >= n) {
       -                        tokv = realloc(tokv, ++n * sizeof (*tokv));
       -                        if (tokv == NULL)
       -                                die("realloc");
       -                }
       -                tokv[tokc] = s;
       -                s = strtok(NULL, " ");
       -                tokc++;
       -        }
       -        matchc = 0;
       -        for (n = 0; n < linec; n++)
       -                if (match_line(linev[n], tokv, tokc))
       -                        matchv[matchc++] = linev[n];
       -        free(tokv);
       -        if (opt['#'] && matchv[current][0] == '#')
       -                move(+1);
       -}
   DIR diff --git a/buffer.h b/buffer.h
       @@ -1,3 +0,0 @@
       -void        free_lines(void);
       -void        read_stdin(void);
       -void        filter(void);
   DIR diff --git a/control.c b/control.c
       @@ -1,152 +0,0 @@
       -#include <sys/ioctl.h>
       -
       -#include <stddef.h>
       -#include <limits.h>
       -#include <string.h>
       -#include <ctype.h>
       -#include <stdio.h>
       -
       -#include "iomenu.h"
       -#include "buffer.h"
       -#include "control.h"
       -#include "display.h"
       -
       -#define CTL(char)        ((char) ^ 0x40)
       -#define ALT(char)        ((char) + 0x80)
       -#define CSI(char)        ((char) + 0x80 + 0x80)
       -
       -void
       -move(signed int sign)
       -{
       -        extern        char        **matchv;
       -        extern        int          matchc;
       -
       -        int        i;
       -
       -        for (i = current + sign; 0 <= i && i < matchc; i += sign) {
       -                if (!opt['#'] || matchv[i][0] != '#') {
       -                        current = i;
       -                        break;
       -                }
       -        }
       -}
       -
       -static void
       -move_page(signed int sign)
       -{
       -        extern        struct        winsize ws;
       -        extern        int        matchc;
       -
       -        int        i;
       -        int        rows;
       -
       -        rows = ws.ws_row - 1;
       -        i = current - current % rows + rows * sign;
       -        if (!(0 <= i && i < matchc))
       -                return;
       -        current = i - 1;
       -        move(+1);
       -}
       -
       -static void
       -remove_word()
       -{
       -        extern        char        input[LINE_MAX];
       -
       -        int len;
       -        int i;
       -
       -        len = strlen(input) - 1;
       -        for (i = len; i >= 0 && isspace(input[i]); i--)
       -                input[i] = '\0';
       -        len = strlen(input) - 1;
       -        for (i = len; i >= 0 && !isspace(input[i]); i--)
       -                input[i] = '\0';
       -        filter();
       -}
       -
       -static void
       -add_char(char c)
       -{
       -        extern        char        input[LINE_MAX];
       -
       -        int len;
       -
       -        len = strlen(input);
       -        if (isprint(c)) {
       -                input[len]     = c;
       -                input[len + 1] = '\0';
       -        }
       -        filter();
       -}
       -
       -/*
       - * Big case table, that calls itself back for with ALT (aka ESC), CSI
       - * (aka ESC + [).  These last two have values above the range of ASCII.
       - */
       -int
       -key(int k)
       -{
       -        extern        char        **matchv;
       -        extern        char          input[LINE_MAX];
       -        extern        int          linec;
       -
       -top:
       -        switch (k) {
       -        case CTL('C'):
       -                return -1;
       -        case CTL('U'):
       -                input[0] = '\0';
       -                filter();
       -                break;
       -        case CTL('W'):
       -                remove_word();
       -                break;
       -        case 127:
       -        case CTL('H'):  /* backspace */
       -                input[strlen(input) - 1] = '\0';
       -                filter();
       -                break;
       -        case CSI('A'):  /* up */
       -        case CTL('P'):
       -                move(-1);
       -                break;
       -        case CSI('B'):  /* down */
       -        case CTL('N'):
       -                move(+1);
       -                break;
       -        case CSI('5'):  /* page up */
       -                if (fgetc(stdin) != '~')
       -                        break;
       -                /* FALLTHROUGH */
       -        case ALT('v'):
       -                move_page(-1);
       -                break;
       -        case CSI('6'):  /* page down */
       -                if (fgetc(stdin) != '~')
       -                        break;
       -                /* FALLTHROUGH */
       -        case CTL('V'):
       -                move_page(+1);
       -                break;
       -        case CTL('I'):  /* tab */
       -                if (linec > 0)
       -                        strcpy(input, matchv[current]);
       -                filter();
       -                break;
       -        case CTL('J'):  /* enter */
       -        case CTL('M'):
       -                print_selection();
       -                return 0;
       -        case ALT('['):
       -                k = CSI(fgetc(stdin));
       -                goto top;
       -        case 0x1b: /* escape / alt */
       -                k = ALT(fgetc(stdin));
       -                goto top;
       -        default:
       -                add_char((char) k);
       -        }
       -
       -        return 1;
       -}
   DIR diff --git a/control.h b/control.h
       @@ -1,4 +0,0 @@
       -int        prev_page(int);
       -int        next_page(int);
       -void        move(signed int);
       -int        key(int);
   DIR diff --git a/display.c b/display.c
       @@ -1,120 +0,0 @@
       -#include <sys/ioctl.h>
       -#include <string.h>
       -#include <stdio.h>
       -#include <limits.h>
       -
       -#include "iomenu.h"
       -#include "utf8.h"
       -#include "control.h"
       -#include "display.h"
       -
       -static char *
       -format(char *str, int cols)
       -{
       -        extern        struct        winsize ws;
       -
       -        int   col = 0;
       -        long  rune = 0;
       -        char *fmt = formatted;
       -
       -        while (*str && col < cols) {
       -                if (*str == '\t') {
       -                        int t = 8 - col % 8;
       -                        while (t-- && col < cols) {
       -                                *fmt++ = ' ';
       -                                col++;
       -                        }
       -                        str++;
       -                } else if (utf8_to_rune(&rune, str) && utf8_is_print(rune)) {
       -                        int i = utf8_len(str);
       -                        while (i--)
       -                                *fmt++ = *str++;
       -                        col++;
       -                } else {
       -                        *fmt++ = '?';
       -                        col++;
       -                        str++;
       -                }
       -        }
       -        *fmt = '\0';
       -
       -        return formatted;
       -}
       -
       -static void
       -print_line(char *line, int cur)
       -{
       -        extern        struct        winsize ws;
       -
       -        if (opt['#'] && line[0] == '#') {
       -                format(line + 1, ws.ws_col - 1);
       -                fprintf(stderr, "\n\x1b[1m %s\x1b[m", formatted);
       -        } else if (cur) {
       -                format(line, ws.ws_col - 1);
       -                fprintf(stderr, "\n\x1b[47;30m\x1b[K %s\x1b[m", formatted);
       -        } else {
       -                format(line, ws.ws_col - 1);
       -                fprintf(stderr, "\n %s", formatted);
       -        }
       -}
       -
       -void
       -print_screen(void)
       -{
       -        extern        struct        winsize ws;
       -        extern        char        **matchv;
       -        extern        char         *prompt;
       -        extern        char          input[LINE_MAX];
       -        extern        int          matchc;
       -
       -        char        **m;
       -        int          p;
       -        int          i;
       -        int          cols;
       -        int          rows;
       -
       -        cols = ws.ws_col - 1;
       -        rows = ws.ws_row - 1;
       -        p = 0;
       -        i = current - current % rows;
       -        m = matchv + i;
       -        fputs("\x1b[2J", stderr);
       -        while (p < rows && i < matchc) {
       -                print_line(*m, i == current);
       -                p++, i++, m++;
       -        }
       -        fputs("\x1b[H", stderr);
       -        if (*prompt) {
       -                format(prompt, cols - 2);
       -                fprintf(stderr, "\x1b[30;47m %s \x1b[m", formatted);
       -                cols -= strlen(formatted) + 2;
       -        }
       -        fputc(' ', stderr);
       -        fputs(format(input, cols), stderr);
       -        fflush(stderr);
       -}
       -
       -void
       -print_selection(void)
       -{
       -        extern        char        **matchv;
       -        extern        char          input[LINE_MAX];
       -        extern        int          matchc;
       -
       -        char **match;
       -
       -        if (opt['#']) {
       -                match = matchv + current;
       -                while (--match >= matchv) {
       -                        if ((*match)[0] == '#') {
       -                                fputs(*match + 1, stdout);
       -                                break;
       -                        }
       -                }
       -                putchar('\t');
       -        }
       -        if (matchc == 0 || (opt['#'] && matchv[current][0] == '#'))
       -                puts(input);
       -        else
       -                puts(matchv[current]);
       -}
   DIR diff --git a/display.h b/display.h
       @@ -1,2 +0,0 @@
       -void        print_screen(void);
       -void        print_selection(void);
   DIR diff --git a/iomenu.c b/iomenu.c
       @@ -0,0 +1,532 @@
       +#include <sys/ioctl.h>
       +
       +#include <ctype.h>
       +#include <fcntl.h>
       +#include <limits.h>
       +#include <signal.h>
       +#include <stddef.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <termios.h>
       +#include <unistd.h>
       +
       +#include "utf8.h"
       +
       +#ifndef SIGWINCH
       +#define SIGWINCH        28
       +#endif
       +
       +#define MIN(X, Y)        (((X) < (Y)) ? (X) : (Y))
       +#define CTL(char)        ((char) ^ 0x40)
       +#define ALT(char)        ((char) + 0x80)
       +#define CSI(char)        ((char) + 0x80 + 0x80)
       +
       +static        struct        termios termios;
       +static        int        ttyfd;
       +
       +struct winsize        ws;
       +int                linec = 0, matchc = 0, current = 0;
       +char                **linev = NULL, **matchv = NULL;
       +char                input[LINE_MAX], formatted[LINE_MAX * 8];
       +
       +char        *flag_p = "";
       +int        flag_hs = 0;
       +
       +/*
       + * Keep the line if it match every token (in no particular order, and allowed to
       + * be overlapping).
       + */
       +static int
       +match_line(char *line, char **tokv, int tokc)
       +{
       +        if (flag_hs && line[0] == '#')
       +                return 2;
       +        while (tokc-- > 0)
       +                if (strstr(line, tokv[tokc]) == NULL)
       +                        return 0;
       +        return 1;
       +}
       +
       +/*
       + * As we use a single buffer for the whole stdin, we only need to free it once
       + * and it will free all the lines.
       + */
       +static void
       +free_lines(void)
       +{
       +        extern        char        **linev;
       +        extern        char        **matchv;
       +
       +        if (linev) {
       +                free(linev[0]);
       +                free(linev);
       +        }
       +        if (matchv)
       +                free(matchv);
       +}
       +
       +/*
       + * Free the structures, reset the terminal state and exit with an error message.
       + */
       +static void
       +die(const char *s)
       +{
       +        tcsetattr(ttyfd, TCSANOW, &termios);
       +        close(ttyfd);
       +        free_lines();
       +        perror(s);
       +        exit(EXIT_FAILURE);
       +}
       +
       +/*
       + * Split a buffer into an array of lines, without allocating memory for every
       + * line, but using the input buffer and replacing characters.
       + */
       +static void
       +split_lines(char *buf)
       +{
       +        extern char        **linev, **matchv;
       +        extern int        linec;
       +
       +        char        *b, **lv, **mv;
       +
       +        linec = 0;
       +        b = buf;
       +        while ((b = strchr(b + 1, '\n')))
       +                linec++;
       +        if (!linec)
       +                linec = 1;
       +        if (!(lv = linev = calloc(linec + 1, sizeof (char **))))
       +                die("calloc");
       +        if (!(mv = matchv = calloc(linec + 1, sizeof (char **)))) {
       +                free(linev);
       +                die("calloc");
       +        }
       +        *mv = *lv = b = buf;
       +        while ((b = strchr(b, '\n'))) {
       +                *b = '\0';
       +                mv++, lv++;
       +                *mv = *lv = ++b;
       +        }
       +}
       +
       +/*
       + * Read stdin in a single malloc-ed buffer, realloc-ed to twice its size every
       + * time the previous buffer is filled.
       + */
       +static void
       +read_stdin(void)
       +{
       +        size_t        size, len, off;
       +        char        *buf, *b;
       +
       +        size = BUFSIZ;
       +        off = 0;
       +        buf = malloc(size);
       +        while ((len = read(STDIN_FILENO, buf + off, size - off)) > 0) {
       +                off += len;
       +                if (off >= size >> 1) {
       +                        size <<= 1;
       +                        if (!(b = realloc(buf, size + 1))) {
       +                                free(buf);
       +                                die("realloc");
       +                        }
       +                        buf = b;
       +                }
       +        }
       +        buf[off] = '\0';
       +        split_lines(buf);
       +}
       +
       +static void
       +move(signed int sign)
       +{
       +        extern        char        **matchv;
       +        extern        int          matchc;
       +
       +        int        i;
       +
       +        for (i = current + sign; 0 <= i && i < matchc; i += sign) {
       +                if (!flag_hs || matchv[i][0] != '#') {
       +                        current = i;
       +                        break;
       +                }
       +        }
       +}
       +
       +/*
       + * First split input into token, then match every token independently against
       + * every line.  The matching lines fills matchv.
       + */
       +static void
       +filter(void)
       +{
       +        extern char        **linev, **matchv;
       +        extern int        linec, matchc, current;
       +
       +        int        tokc, n;
       +        char        **tokv, *s, buf[sizeof (input)];
       +
       +        tokv = NULL;
       +        current = 0;
       +        strcpy(buf, input);
       +        tokc = 0;
       +        n = 0;
       +        s = strtok(buf, " ");
       +        while (s) {
       +                if (tokc >= n) {
       +                        tokv = realloc(tokv, ++n * sizeof (*tokv));
       +                        if (tokv == NULL)
       +                                die("realloc");
       +                }
       +                tokv[tokc] = s;
       +                s = strtok(NULL, " ");
       +                tokc++;
       +        }
       +        matchc = 0;
       +        for (n = 0; n < linec; n++)
       +                if (match_line(linev[n], tokv, tokc))
       +                        matchv[matchc++] = linev[n];
       +        free(tokv);
       +        if (flag_hs && matchv[current][0] == '#')
       +                move(+1);
       +}
       +
       +static void
       +move_page(signed int sign)
       +{
       +        extern        struct        winsize ws;
       +        extern        int        matchc;
       +
       +        int        i;
       +        int        rows;
       +
       +        rows = ws.ws_row - 1;
       +        i = current - current % rows + rows * sign;
       +        if (!(0 <= i && i < matchc))
       +                return;
       +        current = i - 1;
       +        move(+1);
       +}
       +
       +static void
       +remove_word()
       +{
       +        extern        char        input[LINE_MAX];
       +
       +        int len;
       +        int i;
       +
       +        len = strlen(input) - 1;
       +        for (i = len; i >= 0 && isspace(input[i]); i--)
       +                input[i] = '\0';
       +        len = strlen(input) - 1;
       +        for (i = len; i >= 0 && !isspace(input[i]); i--)
       +                input[i] = '\0';
       +        filter();
       +}
       +
       +static void
       +add_char(char c)
       +{
       +        extern        char        input[LINE_MAX];
       +
       +        int len;
       +
       +        len = strlen(input);
       +        if (isprint(c)) {
       +                input[len]     = c;
       +                input[len + 1] = '\0';
       +        }
       +        filter();
       +}
       +
       +static void
       +print_selection(void)
       +{
       +        extern        char        **matchv;
       +        extern        char          input[LINE_MAX];
       +        extern        int          matchc;
       +
       +        char **match;
       +
       +        if (flag_hs) {
       +                match = matchv + current;
       +                while (--match >= matchv) {
       +                        if ((*match)[0] == '#') {
       +                                fputs(*match + 1, stdout);
       +                                break;
       +                        }
       +                }
       +                putchar('\t');
       +        }
       +        if (matchc == 0 || (flag_hs && matchv[current][0] == '#'))
       +                puts(input);
       +        else
       +                puts(matchv[current]);
       +}
       +
       +/*
       + * Big case table, that calls itself back for with ALT (aka ESC), CSI
       + * (aka ESC + [).  These last two have values above the range of ASCII.
       + */
       +int
       +key(int k)
       +{
       +        extern        char        **matchv;
       +        extern        char          input[LINE_MAX];
       +        extern        int          linec;
       +
       +top:
       +        switch (k) {
       +        case CTL('C'):
       +                return -1;
       +        case CTL('U'):
       +                input[0] = '\0';
       +                filter();
       +                break;
       +        case CTL('W'):
       +                remove_word();
       +                break;
       +        case 127:
       +        case CTL('H'):  /* backspace */
       +                input[strlen(input) - 1] = '\0';
       +                filter();
       +                break;
       +        case CSI('A'):  /* up */
       +        case CTL('P'):
       +                move(-1);
       +                break;
       +        case CSI('B'):  /* down */
       +        case CTL('N'):
       +                move(+1);
       +                break;
       +        case CSI('5'):  /* page up */
       +                if (fgetc(stdin) != '~')
       +                        break;
       +                /* FALLTHROUGH */
       +        case ALT('v'):
       +                move_page(-1);
       +                break;
       +        case CSI('6'):  /* page down */
       +                if (fgetc(stdin) != '~')
       +                        break;
       +                /* FALLTHROUGH */
       +        case CTL('V'):
       +                move_page(+1);
       +                break;
       +        case CTL('I'):  /* tab */
       +                if (linec > 0)
       +                        strcpy(input, matchv[current]);
       +                filter();
       +                break;
       +        case CTL('J'):  /* enter */
       +        case CTL('M'):
       +                print_selection();
       +                return 0;
       +        case ALT('['):
       +                k = CSI(fgetc(stdin));
       +                goto top;
       +        case 0x1b: /* escape / alt */
       +                k = ALT(fgetc(stdin));
       +                goto top;
       +        default:
       +                add_char((char) k);
       +        }
       +
       +        return 1;
       +}
       +
       +static char *
       +format(char *str, int cols)
       +{
       +        extern        struct        winsize ws;
       +
       +        int   col = 0;
       +        long  rune = 0;
       +        char *fmt = formatted;
       +
       +        while (*str && col < cols) {
       +                if (*str == '\t') {
       +                        int t = 8 - col % 8;
       +                        while (t-- && col < cols) {
       +                                *fmt++ = ' ';
       +                                col++;
       +                        }
       +                        str++;
       +                } else if (utf8_to_rune(&rune, str) && utf8_is_print(rune)) {
       +                        int i = utf8_len(str);
       +                        while (i--)
       +                                *fmt++ = *str++;
       +                        col++;
       +                } else {
       +                        *fmt++ = '?';
       +                        col++;
       +                        str++;
       +                }
       +        }
       +        *fmt = '\0';
       +
       +        return formatted;
       +}
       +
       +static void
       +print_line(char *line, int cur)
       +{
       +        extern        struct        winsize ws;
       +
       +        if (flag_hs && line[0] == '#') {
       +                format(line + 1, ws.ws_col - 1);
       +                fprintf(stderr, "\n\x1b[1m %s\x1b[m", formatted);
       +        } else if (cur) {
       +                format(line, ws.ws_col - 1);
       +                fprintf(stderr, "\n\x1b[47;30m\x1b[K %s\x1b[m", formatted);
       +        } else {
       +                format(line, ws.ws_col - 1);
       +                fprintf(stderr, "\n %s", formatted);
       +        }
       +}
       +
       +static void
       +print_screen(void)
       +{
       +        extern        struct        winsize ws;
       +        extern        char        **matchv;
       +        extern        char         *flag_p;
       +        extern        char          input[LINE_MAX];
       +        extern        int          matchc;
       +
       +        char        **m;
       +        int          p;
       +        int          i;
       +        int          cols;
       +        int          rows;
       +
       +        cols = ws.ws_col - 1;
       +        rows = ws.ws_row - 1;
       +        p = 0;
       +        i = current - current % rows;
       +        m = matchv + i;
       +        fputs("\x1b[2J", stderr);
       +        while (p < rows && i < matchc) {
       +                print_line(*m, i == current);
       +                p++, i++, m++;
       +        }
       +        fputs("\x1b[H", stderr);
       +        if (*flag_p) {
       +                format(flag_p, cols - 2);
       +                fprintf(stderr, "\x1b[30;47m %s \x1b[m", formatted);
       +                cols -= strlen(formatted) + 2;
       +        }
       +        fputc(' ', stderr);
       +        fputs(format(input, cols), stderr);
       +        fflush(stderr);
       +}
       +
       +/*
       + * Set terminal in raw mode.
       + */
       +static void
       +set_terminal(void)
       +{
       +        struct        termios new;
       +
       +        fputs("\x1b[s\x1b[?1049h\x1b[H", stderr);
       +        if (tcgetattr(ttyfd, &termios) < 0 || tcgetattr(ttyfd, &new) < 0) {
       +                perror("tcgetattr");
       +                exit(EXIT_FAILURE);
       +        }
       +        new.c_lflag &= ~(ICANON | ECHO | IGNBRK | IEXTEN | ISIG);
       +        tcsetattr(ttyfd, TCSANOW, &new);
       +}
       +
       +/*
       + * Take terminal out of raw mode.
       + */
       +static void
       +reset_terminal(void)
       +{
       +        fputs("\x1b[u\033[?1049l", stderr);
       +        tcsetattr(ttyfd, TCSANOW, &termios);
       +}
       +
       +/*
       + * Redraw the whole screen on window resize.
       + */
       +static void
       +sigwinch()
       +{
       +        extern struct winsize        ws;
       +
       +        if (ioctl(ttyfd, TIOCGWINSZ, &ws) < 0)
       +                die("ioctl");
       +        print_screen();
       +        signal(SIGWINCH, sigwinch);
       +}
       +
       +static void
       +usage(void)
       +{
       +        fputs("usage: iomenu [-#] [-p flag_p]\n", stderr);
       +        exit(EXIT_FAILURE);
       +}
       +
       +/*
       + * XXX: switch to getopt.
       + */
       +static void
       +parse_opt(int argc, char *argv[])
       +{
       +        extern char        *flag_p;
       +
       +        for (argv++, argc--; argc > 0; argv++, argc--) {
       +                if (argv[0][0] != '-')
       +                        usage();
       +                switch ((*argv)[1]) {
       +                case 'p':
       +                        if (!--argc)
       +                                usage();
       +                        flag_p = *++argv;
       +                        break;
       +                case '#':
       +                        flag_hs = 1;
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +}
       +
       +/*
       + * Read stdin in a buffer, filling a table of lines, then re-open stdin to
       + * /dev/tty for an interactive (raw) session to let the user filter and select
       + * one line by searching words within stdin.  This was inspired from dmenu.
       + */
       +int
       +main(int argc, char *argv[])
       +{
       +        extern char        input[LINE_MAX];
       +
       +        int                exit_code;
       +
       +        parse_opt(argc, argv);
       +        read_stdin();
       +        filter();
       +        if (!freopen("/dev/tty", "r", stdin))
       +                die("freopen /dev/tty");
       +        if (!freopen("/dev/tty", "w", stderr))
       +                die("freopen /dev/tty");
       +        ttyfd =  open("/dev/tty", O_RDWR);
       +        set_terminal();
       +        sigwinch();
       +        input[0] = '\0';
       +        while ((exit_code = key(fgetc(stdin))) > 0)
       +                print_screen();
       +        print_screen();
       +        reset_terminal();
       +        close(ttyfd);
       +        free_lines();
       +
       +        return exit_code;
       +}
   DIR diff --git a/iomenu.h b/iomenu.h
       @@ -1,18 +0,0 @@
       -#ifndef        SIGWINCH
       -#define        SIGWINCH        28
       -#endif
       -
       -#define        MARGIN        30
       -
       -#define        MIN(X, Y)        (((X) < (Y)) ? (X) : (Y))
       -
       -extern        struct          winsize ws;
       -extern        char        **linev;
       -extern        int          linec;
       -extern        char        **matchv;
       -extern        int          matchc;
       -extern        char         *prompt;
       -extern        char          input[LINE_MAX];
       -extern        char          formatted[LINE_MAX * 8];
       -extern        int          current;
       -extern        int          opt[128];
   DIR diff --git a/main.c b/main.c
       @@ -1,151 +0,0 @@
       -#include <sys/ioctl.h>
       -
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <termios.h>
       -#include <signal.h>
       -#include <string.h>
       -#include <unistd.h>
       -#include <fcntl.h>
       -#include <limits.h>
       -
       -#include "iomenu.h"
       -#include "main.h"
       -#include "buffer.h"
       -#include "control.h"
       -#include "display.h"
       -
       -static        struct        termios termios;
       -static        int        ttyfd;
       -
       -struct        winsize ws;
       -char        **linev = NULL;
       -int          linec = 0;
       -char        **matchv = NULL;
       -int          matchc = 0;
       -char         *prompt = "";
       -char          input[LINE_MAX];
       -char          formatted[LINE_MAX * 8];
       -int          current = 0;
       -int          opt[128];
       -
       -/*
       - * Free the structures, reset the terminal state and exit with an error message.
       - */
       -void
       -die(const char *s)
       -{
       -        tcsetattr(ttyfd, TCSANOW, &termios);
       -        close(ttyfd);
       -        free_lines();
       -        perror(s);
       -        exit(EXIT_FAILURE);
       -}
       -
       -/*
       - * Set terminal in raw mode.
       - */
       -static void
       -set_terminal(void)
       -{
       -        struct        termios new;
       -
       -        fputs("\x1b[s\x1b[?1049h\x1b[H", stderr);
       -        if (tcgetattr(ttyfd, &termios) < 0 || tcgetattr(ttyfd, &new) < 0) {
       -                perror("tcgetattr");
       -                exit(EXIT_FAILURE);
       -        }
       -        new.c_lflag &= ~(ICANON | ECHO | IGNBRK | IEXTEN | ISIG);
       -        tcsetattr(ttyfd, TCSANOW, &new);
       -}
       -
       -/*
       - * Take terminal out of raw mode.
       - */
       -static void
       -reset_terminal(void)
       -{
       -        fputs("\x1b[u\033[?1049l", stderr);
       -        tcsetattr(ttyfd, TCSANOW, &termios);
       -}
       -
       -/*
       - * Redraw the whole screen on window resize.
       - */
       -static void
       -sigwinch()
       -{
       -        extern        struct        winsize ws;
       -
       -        if (ioctl(ttyfd, TIOCGWINSZ, &ws) < 0)
       -                die("ioctl");
       -        print_screen();
       -        signal(SIGWINCH, sigwinch);
       -}
       -
       -static void
       -usage(void)
       -{
       -        fputs("usage: iomenu [-#] [-p prompt]\n", stderr);
       -        exit(EXIT_FAILURE);
       -}
       -
       -/*
       - * XXX: switch to getopt.
       - */
       -static void
       -parse_opt(int argc, char *argv[])
       -{
       -        extern        char        *prompt;
       -
       -        memset(opt, 0, 128 * sizeof (int));
       -        for (argv++, argc--; argc > 0; argv++, argc--) {
       -                if (argv[0][0] != '-')
       -                        usage();
       -                switch ((*argv)[1]) {
       -                case 'p':
       -                        if (!--argc)
       -                                usage();
       -                        prompt = *++argv;
       -                        break;
       -                case '#':
       -                        opt['#'] = 1;
       -                        break;
       -                default:
       -                        usage();
       -                }
       -        }
       -}
       -
       -/*
       - * Read stdin in a buffer, filling a table of lines, then re-open stdin to
       - * /dev/tty for an interactive (raw) session to let the user filter and select
       - * one line by searching words within stdin.  This was inspired from dmenu.
       - */
       -int
       -main(int argc, char *argv[])
       -{
       -        extern        char        input[LINE_MAX];
       -
       -        int        exit_code;
       -
       -        parse_opt(argc, argv);
       -        read_stdin();
       -        filter();
       -        if (!freopen("/dev/tty", "r", stdin))
       -                die("freopen /dev/tty");
       -        if (!freopen("/dev/tty", "w", stderr))
       -                die("freopen /dev/tty");
       -        ttyfd =  open("/dev/tty", O_RDWR);
       -        set_terminal();
       -        sigwinch();
       -        input[0] = '\0';
       -        while ((exit_code = key(fgetc(stdin))) > 0)
       -                print_screen();
       -        print_screen();
       -        reset_terminal();
       -        close(ttyfd);
       -        free_lines();
       -
       -        return exit_code;
       -}
   DIR diff --git a/main.h b/main.h
       @@ -1 +0,0 @@
       -void        die(const char *);