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 *);