iomenu.c: horizontal mode with -l 0 - iomenu - interactive terminal-based selection menu
 (HTM) git clone git://bitreich.org/iomenu git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/iomenu
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 695c25f7dc0613e9aa3e9057b333ec22c0e580f9
 (DIR) parent 5ac10691e817e4bfb903449a99614a1184d03a7e
 (HTM) Author: Josuah Demangeon <josuah.demangeon@gandi.net>
       Date:   Sun,  3 Sep 2017 20:09:12 +0200
       
       iomenu.c: horizontal mode with -l 0
       
       Diffstat:
         M iomenu.c                            |     215 +++++++++++++++++++------------
       
       1 file changed, 135 insertions(+), 80 deletions(-)
       ---
 (DIR) diff --git a/iomenu.c b/iomenu.c
       @@ -14,6 +14,7 @@
        #include "utf8.h"
        
        #define CONTINUE  2   /* as opposed to EXIT_SUCCESS and EXIT_FAILURE */
       +#define MARGIN    30  /* space for the input before the horizontal list */
        
        #define  CTL(char) (char ^ 0x40)
        #define  ALT(char) (char + 0x80)
       @@ -53,6 +54,34 @@ die(const char *s)
        }
        
        static void
       +read_lines(void)
       +{
       +        int    size = 0;
       +        size_t len;
       +
       +        do {
       +                if (linec >= size) {
       +                        size += BUFSIZ;
       +                        linev  = realloc(linev,  sizeof (char **) * size);
       +                        matchv = realloc(matchv, sizeof (char **) * size);
       +                        if (!linev || !matchv)
       +                                die("realloc");
       +                }
       +
       +                linev[linec] = malloc(LINE_MAX + 1);
       +                if (!(fgets(linev[linec], LINE_MAX, stdin))) {
       +                        free(linev[linec]);
       +                        break;
       +                }
       +
       +                len = strlen(linev[linec]);
       +                if (len > 0 && linec[linev][len - 1] == '\n')
       +                        linev[linec][len - 1] = '\0';
       +
       +        } while (++linec, ++matchc);
       +}
       +
       +static void
        set_terminal(void)
        {
                struct termios new;
       @@ -83,48 +112,73 @@ reset_terminal(void)
                /* reset cursor position */
                fputs("\033[u", stderr);
        
       -        /* set terminal back to normal mode */
                tcsetattr(ttyfd, TCSANOW, &termios);
        }
        
       -static void
       -read_lines(void)
       +static size_t
       +str_width(char *s)
        {
       -        int    size = 0;
       -        size_t len;
       +        int width = 0;
        
       -        do {
       -                if (linec >= size) {
       -                        size += BUFSIZ;
       -                        linev  = realloc(linev,  sizeof (char **) * size);
       -                        matchv = realloc(matchv, sizeof (char **) * size);
       -                        if (!linev || !matchv)
       -                                die("realloc");
       -                }
       +        while (*s) {
       +                if (*s++ == '\t')
       +                        width += (width + 7) % 8;
       +                else
       +                        width++;
       +        }
        
       -                linev[linec] = malloc(LINE_MAX + 1);
       -                if (!(fgets(linev[linec], LINE_MAX, stdin))) {
       -                        free(linev[linec]);
       -                        break;
       -                }
       +        return width;
       +}
        
       -                len = strlen(linev[linec]);
       -                if (len > 0 && linec[linev][len - 1] == '\n')
       -                        linev[linec][len - 1] = '\0';
       +static int
       +prev_page(int pos, int cols)
       +{
       +        int col;
        
       -        } while (++linec, ++matchc);
       +        pos -= pos > 0 ? 1 : 0;
       +        for (col = 0; pos > 0; pos--)
       +                if ((col += str_width(matchv[pos]) + 2) > cols)
       +                        return pos + 1;
       +        return pos;
        }
        
       -static size_t
       -string_width(char *s)
       +static int
       +next_page(int pos, int cols)
        {
       -        int width = 0;
       +        int col;
        
       -        while (*s)
       -                if (*s++ == '\t')
       -                        width += (width + 7) % 8;
       +        for (col = 0; pos < matchc; pos++)
       +                if ((col += str_width(matchv[pos]) + 2) > cols)
       +                        return pos;
       +        return pos;
       +}
        
       -        return width;
       +static void
       +move(signed int sign)
       +{
       +        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)
       +{
       +        int i = current - current % rows + rows * sign;
       +
       +        if (!opt['l'])
       +                return;
       +
       +        if (0 > i || i > matchc)
       +                return;
       +
       +        current = i - 1;
       +        move(+1);
        }
        
        static char *
       @@ -149,10 +203,10 @@ format(char *str, int cols)
                                        *fmt++ = *str++;
                                col++;
        
       -        } else {
       -                *fmt++ = '?';
       -                col++;
       -                str++;
       +                } else {
       +                        *fmt++ = '?';
       +                        col++;
       +                        str++;
                        }
                }
                *fmt = '\0';
       @@ -165,22 +219,51 @@ print_lines(void)
        {
                int printed = 0, i = current - current % rows;
        
       -        while (printed < rows && i < matchc) {
       +        for (; printed < rows && i < matchc; i++, printed++) {
       +                fprintf(stderr,
       +                        opt['#'] && matchv[i][0] == '#' ?
       +                        "\n\033[1m\033[K %s\033[m"      :
       +                        i == current                    ?
       +                        "\n\033[47;30m\033[K %s\033[m"      :
       +                        "\n\033[K %s",
       +                        format(matchv[i], ws.ws_col - 1)
       +                );
       +        }
       +
       +        while (printed++ < rows)
       +                fputs("\n\033[K", stderr);
       +}
        
       -                char *s = format(matchv[i], ws.ws_col - 1);
       +static void
       +print_segments(void)
       +{
       +        int i;
        
       -                if (opt['#'] && matchv[i][0] == '#')
       -                        fprintf(stderr, "\n\033[1m\033[K %s\033[m",     s);
       -                else if (i == current)
       -                        fprintf(stderr, "\n\033[30;47m\033[K %s\033[m", s);
       -                else
       -                        fprintf(stderr, "\n\033[K %s",                  s);
       +        if (current < offset) {
       +                next   = offset;
       +                offset = prev_page(offset, ws.ws_col - MARGIN - 4);
        
       -                i++; printed++;
       +        } else if (current >= next) {
       +                offset = next;
       +                next   = next_page(offset, ws.ws_col - MARGIN - 4);
                }
        
       -        while (printed++ < rows)
       -                fputs("\n\033[K", stderr);
       +        fprintf(stderr, "\r\033[K\033[%dC", MARGIN);
       +        fputs(offset > 0 ? "< " : "  ", stderr);
       +
       +        for (i = offset; i < next && i < matchc; i++) {
       +                fprintf(stderr,
       +                        opt['#'] && matchv[i][0] == '#' ? "\033[1m %s \033[m" :
       +                        i == current                    ? "\033[7m %s \033[m" :
       +                                                          " %s ",
       +                        format(matchv[i], ws.ws_col - 1)
       +                );
       +        }
       +
       +        if (next < matchc)
       +                fprintf(stderr, "\033[%dC\b>", ws.ws_col - MARGIN);
       +
       +        fputc('\r', stderr);
        }
        
        static void
       @@ -188,10 +271,12 @@ print_screen(void)
        {
                int cols = ws.ws_col - 1;
        
       -        fputs("\r\033[K", stderr);
       -
       -        print_lines();
       -        fprintf(stderr, "\033[%dA\r", rows);
       +        if (opt['l'] > 0) {
       +                print_lines();
       +                fprintf(stderr, "\033[%dA\r", rows);
       +        } else {
       +                print_segments();
       +        }
        
                if (*prompt) {
                        format(prompt, cols - 2);
       @@ -219,34 +304,6 @@ match_line(char *line, char **tokv, int tokc)
        }
        
        static void
       -move(signed int sign)
       -{
       -        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)
       -{
       -        int i = current - current % rows + rows * sign;
       -
       -        if (!opt['l'])
       -                return;
       -
       -        if (0 > i || i > matchc)
       -                return;
       -
       -        current = i - 1;
       -        move(+1);
       -}
       -
       -static void
        filter(void)
        {
                char **tokv = NULL, *s, buffer[sizeof (input)];
       @@ -333,10 +390,8 @@ print_selection(void)
        }
        
        static int
       -key(void)
       +key(int key)
        {
       -        int key = fgetc(stdin);
       -
        top:
                switch (key) {
        
       @@ -485,7 +540,7 @@ main(int argc, char *argv[])
                sigwinch();
        
                input[0] = '\0';
       -        while ((exit_code = key()) == CONTINUE)
       +        while ((exit_code = key(fgetc(stdin))) == CONTINUE)
                        print_screen();
                print_screen();