split into multiple files - 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 2743c784dbce7910cbec895c42a723f06fa7b620
(DIR) parent e24cea688e69bd009ef52ea40891a0a386b881ea
(HTM) Author: Josuah Demangeon <mail@josuah.net>
Date: Sun, 29 Oct 2017 22:05:58 +0100
split into multiple files
Diffstat:
M Makefile | 4 +++-
A buffer.c | 93 +++++++++++++++++++++++++++++++
A buffer.h | 3 +++
A control.c | 169 +++++++++++++++++++++++++++++++
A control.h | 4 ++++
A display.c | 119 +++++++++++++++++++++++++++++++
A display.h | 2 ++
D iomenu.c | 543 -------------------------------
A iomenu.core | 0
A main.c | 123 +++++++++++++++++++++++++++++++
A main.h | 29 +++++++++++++++++++++++++++++
M utf8.c | 13 +++++--------
M utf8.h | 12 ++++++------
13 files changed, 556 insertions(+), 558 deletions(-)
---
(DIR) diff --git a/Makefile b/Makefile
@@ -1,6 +1,6 @@
CFLAGS = -std=c89 -Wpedantic -Wall -Wextra -g -D_POSIX_C_SOURCE=200809L
-OBJ = iomenu.o utf8.o
+OBJ = buffer.o control.o display.o main.o utf8.o
all: iomenu
@@ -15,3 +15,5 @@ install: iomenu
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
@@ -0,0 +1,93 @@
+#include "buffer.h"
+
+static char *
+read_line(FILE *fp)
+{
+ char *line;
+ size_t len;
+
+ line = malloc(LINE_MAX + 1);
+ if (!(fgets(line, LINE_MAX, fp))) {
+ free(line);
+ return NULL;
+ }
+
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+
+ return (line);
+}
+
+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;
+}
+
+void
+free_lines(void)
+{
+ if (linev) {
+ for (; linec > 0; linec--)
+ free(linev[linec - 1]);
+ free(linev);
+ }
+ if (matchv)
+ free(matchv);
+}
+
+void
+read_stdin(void)
+{
+ int size = 0;
+
+ while (1) {
+ if (linec >= size) {
+ size += BUFSIZ;
+ linev = realloc(linev, sizeof (char **) * size);
+ matchv = realloc(matchv, sizeof (char **) * size);
+ if (!linev || !matchv)
+ die("realloc");
+ }
+ if ((linev[linec] = read_line(stdin)) == NULL)
+ break;
+ linec++;
+ matchc++;
+ }
+}
+
+void
+filter(void)
+{
+ int tokc = 0;
+ int n = 0;
+ int i;
+ char **tokv = NULL;
+ char *s;
+ char buffer[sizeof (input)];
+
+ current = offset = next = 0;
+ strcpy(buffer, input);
+ for (s = strtok(buffer, " "); s; s = strtok(NULL, " "), tokc++) {
+ if (tokc >= n) {
+ tokv = realloc(tokv, ++n * sizeof (*tokv));
+ if (tokv == NULL)
+ die("realloc");
+ }
+ tokv[tokc] = s;
+ }
+ matchc = 0;
+ for (i = 0; i < linec; i++)
+ if (match_line(linev[i], tokv, tokc))
+ matchv[matchc++] = linev[i];
+ free(tokv);
+ if (opt['#'] && matchv[current][0] == '#')
+ move(+1);
+}
(DIR) diff --git a/buffer.h b/buffer.h
@@ -0,0 +1,3 @@
+void free_lines (void);
+void read_stdin (void);
+void filter(void);
(DIR) diff --git a/control.c b/control.c
@@ -0,0 +1,169 @@
+#include "control.h"
+
+#define CTL(char) ((char) ^ 0x40)
+#define ALT(char) ((char) + 0x80)
+#define CSI(char) ((char) + 0x80 + 0x80)
+
+static size_t
+width(char *s)
+{
+ int width = 0;
+
+ while (*s) {
+ if (*s++ == '\t')
+ width += 8 - (width % 8);
+ else
+ width++;
+ }
+
+ return width;
+}
+
+int
+prev_page(int pos)
+{
+ int col, cols = ws.ws_col - MARGIN - 4;
+
+ pos -= pos > 0 ? 1 : 0;
+ for (col = 0; pos > 0; pos--)
+ if ((col += width(matchv[pos]) + 2) > cols)
+ return pos + 1;
+ return pos;
+}
+
+int
+next_page(int pos)
+{
+ int col, cols = ws.ws_col - MARGIN - 4;
+
+ for (col = 0; pos < matchc; pos++)
+ if ((col += width(matchv[pos]) + 2) > cols)
+ return pos;
+ return pos;
+}
+
+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;
+
+ if (opt['l'] <= 0) {
+ if (sign > 0) {
+ offset = current = next;
+ next = next_page(next);
+ } else if (sign < 0) {
+ next = offset;
+ offset = current = prev_page(offset);
+ }
+ } else {
+ i = current - current % rows + rows * sign;
+ if (!(0 < i && i < matchc))
+ return;
+ current = i - 1;
+ move(+1);
+ }
+}
+
+static void
+remove_word()
+{
+ 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)
+{
+ int len;
+
+ len = strlen(input);
+ if (isprint(c)) {
+ input[len] = c;
+ input[len + 1] = '\0';
+ }
+ filter();
+}
+
+int
+key(int k)
+{
+top:
+ switch (k) {
+ case CTL('C'):
+ return EXIT_FAILURE;
+ 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 EXIT_SUCCESS;
+ case ALT('['):
+ k = CSI(fgetc(stdin));
+ goto top;
+ case 0x1b: /* escape / alt */
+ k = ALT(fgetc(stdin));
+ goto top;
+ default:
+ add_char((char) k);
+ }
+
+ return CONTINUE;
+}
(DIR) diff --git a/control.h b/control.h
@@ -0,0 +1,4 @@
+int prev_page (int);
+int next_page (int);
+void move (signed int);
+int key (int);
(DIR) diff --git a/display.c b/display.c
@@ -0,0 +1,119 @@
+#include "display.h"
+
+static char *
+format(char *str, int cols)
+{
+ 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) && rune_is_print(rune)) {
+ int i = utf8_len(str);
+ while (i--)
+ *fmt++ = *str++;
+ col++;
+ } else {
+ *fmt++ = '?';
+ col++;
+ str++;
+ }
+ }
+ *fmt = '\0';
+
+ return formatted;
+}
+
+static void
+print_lines(void)
+{
+ int printed = 0, i = current - current % rows;
+
+ for (; printed < rows && i < matchc; i++, printed++) {
+ fprintf(stderr,
+ opt['#'] && matchv[i][0] == '#' ?
+ "\n\x1b[1m\x1b[K %s\x1b[m" :
+ i == current ?
+ "\n\x1b[47;30m\x1b[K %s\x1b[m" :
+ "\n\x1b[K %s",
+ format(matchv[i], ws.ws_col - 1)
+ );
+ }
+ while (printed++ < rows)
+ fputs("\n\x1b[K", stderr);
+ fprintf(stderr, "\x1b[%dA\r\x1b[K", rows);
+}
+
+static void
+print_segments(void)
+{
+ int i;
+
+ if (current < offset) {
+ next = offset;
+ offset = prev_page(offset);
+ } else if (current >= next) {
+ offset = next;
+ next = next_page(offset);
+ }
+ fprintf(stderr, "\r\x1b[K\x1b[%dC", MARGIN);
+ fputs(offset > 0 ? "< " : " ", stderr);
+ for (i = offset; i < next && i < matchc; i++) {
+ fprintf(stderr,
+ opt['#'] && matchv[i][0] == '#' ? "\x1b[1m %s \x1b[m" :
+ i == current ? "\x1b[7m %s \x1b[m" : " %s ",
+ format(matchv[i], ws.ws_col - 1)
+ );
+ }
+ if (next < matchc)
+ fprintf(stderr, "\x1b[%dC\b>", ws.ws_col - MARGIN);
+ fputc('\r', stderr);
+}
+
+void
+print_screen(void)
+{
+ int cols = ws.ws_col - 1;
+
+ if (opt['l'] > 0)
+ print_lines();
+ else
+ print_segments();
+ 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)
+{
+ 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]);
+ fputs("\r\x1b[K", stderr);
+}
(DIR) diff --git a/display.h b/display.h
@@ -0,0 +1,2 @@
+void print_screen (void);
+void print_selection (void);
(DIR) diff --git a/iomenu.c b/iomenu.c
@@ -1,543 +0,0 @@
-#include <ctype.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <locale.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include <sys/ioctl.h>
-
-#include "utf8.h"
-
-#ifndef SIGWINCH
-#define SIGWINCH 28
-#endif
-#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)
-#define CSI(char) (char + 0x80 + 0x80)
-#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
-
-static struct winsize ws;
-static struct termios termios;
-static int ttyfd;
-static int current = 0, offset = 0, next = 0;
-static int linec = 0, matchc = 0;
-static char **linev = NULL, **matchv = NULL;
-static char input[LINE_MAX], formatted[LINE_MAX * 8];
-static int opt[128], rows = 0;
-static char *prompt = "";
-
-static void
-free_lines(void)
-{
- if (linev) {
- for (; linec > 0; linec--)
- free(linev[linec - 1]);
- free(linev);
- }
- if (matchv)
- free(matchv);
-}
-
-static void
-die(const char *s)
-{
- tcsetattr(ttyfd, TCSANOW, &termios);
- close(ttyfd);
- free_lines();
- perror(s);
- exit(EXIT_FAILURE);
-}
-
-static char *
-read_line(FILE *fp)
-{
- char *line;
- size_t len;
-
- line = malloc(LINE_MAX + 1);
- if (!(fgets(line, LINE_MAX, fp))) {
- free(line);
- return NULL;
- }
-
- len = strlen(line);
- if (len > 0 && line[len - 1] == '\n')
- line[len - 1] = '\0';
-
- return (line);
-}
-
-static void
-read_stdin(void)
-{
- int size = 0;
-
- while (1) {
- if (linec >= size) {
- size += BUFSIZ;
- linev = realloc(linev, sizeof (char **) * size);
- matchv = realloc(matchv, sizeof (char **) * size);
- if (!linev || !matchv)
- die("realloc");
- }
- if ((linev[linec] = read_line(stdin)) == NULL)
- break;
- linec++;
- matchc++;
- }
-}
-
-static void
-set_terminal(void)
-{
- struct termios new;
-
- /* save currentsor postition */
- fputs("\x1b[s", stderr);
-
- /* save attributes to `termios` */
- if (tcgetattr(ttyfd, &termios) < 0 || tcgetattr(ttyfd, &new) < 0) {
- perror("tcgetattr");
- exit(EXIT_FAILURE);
- }
-
- /* change to raw mode */
- new.c_lflag &= ~(ICANON | ECHO | IGNBRK | IEXTEN | ISIG);
- tcsetattr(ttyfd, TCSANOW, &new);
-}
-
-static void
-reset_terminal(void)
-{
- int i;
-
- /* clear terminal */
- for (i = 0; i < rows + 1; i++)
- fputs("\r\x1b[K\n", stderr);
-
- /* reset currentsor position */
- fputs("\x1b[u", stderr);
-
- tcsetattr(ttyfd, TCSANOW, &termios);
-}
-
-static size_t
-width(char *s)
-{
- int width = 0;
-
- while (*s) {
- if (*s++ == '\t')
- width += 8 - (width % 8);
- else
- width++;
- }
-
- return width;
-}
-
-static int
-prev_page(int pos)
-{
- int col, cols = ws.ws_col - MARGIN - 4;
-
- pos -= pos > 0 ? 1 : 0;
- for (col = 0; pos > 0; pos--)
- if ((col += width(matchv[pos]) + 2) > cols)
- return pos + 1;
- return pos;
-}
-
-static int
-next_page(int pos)
-{
- int col, cols = ws.ws_col - MARGIN - 4;
-
- for (col = 0; pos < matchc; pos++)
- if ((col += width(matchv[pos]) + 2) > cols)
- return pos;
- return pos;
-}
-
-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;
-
- if (opt['l'] <= 0) {
- if (sign > 0) {
- offset = current = next;
- next = next_page(next);
- } else if (sign < 0) {
- next = offset;
- offset = current = prev_page(offset);
- }
- } else {
- i = current - current % rows + rows * sign;
- if (!(0 < i && i < matchc))
- return;
- current = i - 1;
- move(+1);
- }
-}
-
-static char *
-format(char *str, int cols)
-{
- 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) && rune_is_print(rune)) {
- int i = utf8_len(str);
- while (i--)
- *fmt++ = *str++;
- col++;
- } else {
- *fmt++ = '?';
- col++;
- str++;
- }
- }
- *fmt = '\0';
-
- return formatted;
-}
-
-static void
-print_lines(void)
-{
- int printed = 0, i = current - current % rows;
-
- for (; printed < rows && i < matchc; i++, printed++) {
- fprintf(stderr,
- opt['#'] && matchv[i][0] == '#' ?
- "\n\x1b[1m\x1b[K %s\x1b[m" :
- i == current ?
- "\n\x1b[47;30m\x1b[K %s\x1b[m" :
- "\n\x1b[K %s",
- format(matchv[i], ws.ws_col - 1)
- );
- }
- while (printed++ < rows)
- fputs("\n\x1b[K", stderr);
- fprintf(stderr, "\x1b[%dA\r\x1b[K", rows);
-}
-
-static void
-print_segments(void)
-{
- int i;
-
- if (current < offset) {
- next = offset;
- offset = prev_page(offset);
- } else if (current >= next) {
- offset = next;
- next = next_page(offset);
- }
- fprintf(stderr, "\r\x1b[K\x1b[%dC", MARGIN);
- fputs(offset > 0 ? "< " : " ", stderr);
- for (i = offset; i < next && i < matchc; i++) {
- fprintf(stderr,
- opt['#'] && matchv[i][0] == '#' ? "\x1b[1m %s \x1b[m" :
- i == current ? "\x1b[7m %s \x1b[m" : " %s ",
- format(matchv[i], ws.ws_col - 1)
- );
- }
- if (next < matchc)
- fprintf(stderr, "\x1b[%dC\b>", ws.ws_col - MARGIN);
- fputc('\r', stderr);
-}
-
-static void
-print_screen(void)
-{
- int cols = ws.ws_col - 1;
-
- if (opt['l'] > 0)
- print_lines();
- else
- print_segments();
- 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);
-}
-
-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;
-}
-
-static void
-filter(void)
-{
- int tokc = 0;
- int n = 0;
- int i;
- char **tokv = NULL;
- char *s;
- char buffer[sizeof (input)];
-
- current = offset = next = 0;
- strcpy(buffer, input);
- for (s = strtok(buffer, " "); s; s = strtok(NULL, " "), tokc++) {
- if (tokc >= n) {
- tokv = realloc(tokv, ++n * sizeof (*tokv));
- if (tokv == NULL)
- die("realloc");
- }
- tokv[tokc] = s;
- }
- matchc = 0;
- for (i = 0; i < linec; i++)
- if (match_line(linev[i], tokv, tokc))
- matchv[matchc++] = linev[i];
- free(tokv);
- if (opt['#'] && matchv[current][0] == '#')
- move(+1);
-}
-
-static void
-remove_word()
-{
- 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 key)
-{
- int len;
-
- len = strlen(input);
- if (isprint(key)) {
- input[len] = key;
- input[len + 1] = '\0';
- }
- filter();
-}
-
-static void
-print_selection(void)
-{
- 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]);
- fputs("\r\x1b[K", stderr);
-}
-
-static int
-key(int key)
-{
-top:
- switch (key) {
-
- case CTL('C'):
- return EXIT_FAILURE;
-
- 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 EXIT_SUCCESS;
-
- case ALT('['):
- key = CSI(fgetc(stdin));
- goto top;
-
- case 0x1b: /* escape / alt */
- key = ALT(fgetc(stdin));
- goto top;
-
- default:
- add_char((char) key);
- }
-
- return CONTINUE;
-}
-
-static void
-sigwinch()
-{
- if (ioctl(ttyfd, TIOCGWINSZ, &ws) < 0)
- die("ioctl");
- rows = MIN(opt['l'], ws.ws_row - 1);
- print_screen();
- signal(SIGWINCH, sigwinch);
-}
-
-static void
-usage(void)
-{
- fputs("iomenu [-#] [-l lines] [-p prompt]\n", stderr);
- exit(EXIT_FAILURE);
-}
-
-static void
-parse_opt(int argc, char *argv[])
-{
- memset(opt, 0, 128 * sizeof (int));
- opt['l'] = 255;
- for (argv++, argc--; argc > 0; argv++, argc--) {
- if (argv[0][0] != '-')
- usage();
- switch ((*argv)[1]) {
- case 'l':
- if (!--argc || sscanf(*++argv, "%d", &opt['l']) <= 0)
- usage();
- break;
- case 'p':
- if (!--argc)
- usage();
- prompt = *++argv;
- break;
- case '#':
- opt['#'] = 1;
- break;
- case 's':
- if (!--argc)
- usage();
- opt['s'] = (int) **++argv;
- break;
- default:
- usage();
- }
- }
-}
-
-int
-main(int argc, char *argv[])
-{
- 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))) == CONTINUE)
- print_screen();
- print_screen();
- reset_terminal();
- close(ttyfd);
- free_lines();
-
- return exit_code;
-}
(DIR) diff --git a/iomenu.core b/iomenu.core
Binary files differ.
(DIR) diff --git a/main.c b/main.c
@@ -0,0 +1,123 @@
+#include "main.h"
+
+static struct termios termios;
+static int ttyfd;
+
+void
+die(const char *s)
+{
+ tcsetattr(ttyfd, TCSANOW, &termios);
+ close(ttyfd);
+ free_lines();
+ perror(s);
+ exit(EXIT_FAILURE);
+}
+
+static void
+set_terminal(void)
+{
+ struct termios new;
+
+ /* save currentsor postition */
+ fputs("\x1b[s", stderr);
+
+ /* save attributes to `termios` */
+ if (tcgetattr(ttyfd, &termios) < 0 || tcgetattr(ttyfd, &new) < 0) {
+ perror("tcgetattr");
+ exit(EXIT_FAILURE);
+ }
+
+ /* change to raw mode */
+ new.c_lflag &= ~(ICANON | ECHO | IGNBRK | IEXTEN | ISIG);
+ tcsetattr(ttyfd, TCSANOW, &new);
+}
+
+static void
+reset_terminal(void)
+{
+ int i;
+
+ /* clear terminal */
+ for (i = 0; i < rows + 1; i++)
+ fputs("\r\x1b[K\n", stderr);
+
+ /* reset currentsor position */
+ fputs("\x1b[u", stderr);
+
+ tcsetattr(ttyfd, TCSANOW, &termios);
+}
+
+static void
+sigwinch()
+{
+ if (ioctl(ttyfd, TIOCGWINSZ, &ws) < 0)
+ die("ioctl");
+ rows = MIN(opt['l'], ws.ws_row - 1);
+ print_screen();
+ signal(SIGWINCH, sigwinch);
+}
+
+static void
+usage(void)
+{
+ fputs("iomenu [-#] [-l lines] [-p prompt]\n", stderr);
+ exit(EXIT_FAILURE);
+}
+
+static void
+parse_opt(int argc, char *argv[])
+{
+ memset(opt, 0, 128 * sizeof (int));
+ opt['l'] = 255;
+ for (argv++, argc--; argc > 0; argv++, argc--) {
+ if (argv[0][0] != '-')
+ usage();
+ switch ((*argv)[1]) {
+ case 'l':
+ if (!--argc || sscanf(*++argv, "%d", &opt['l']) <= 0)
+ usage();
+ break;
+ case 'p':
+ if (!--argc)
+ usage();
+ prompt = *++argv;
+ break;
+ case '#':
+ opt['#'] = 1;
+ break;
+ case 's':
+ if (!--argc)
+ usage();
+ opt['s'] = (int) **++argv;
+ break;
+ default:
+ usage();
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ 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))) == CONTINUE)
+ print_screen();
+ print_screen();
+ reset_terminal();
+ close(ttyfd);
+ free_lines();
+
+ return exit_code;
+}
(DIR) diff --git a/main.h b/main.h
@@ -0,0 +1,29 @@
+#ifndef SIGWINCH
+#define SIGWINCH 28
+#endif
+
+#define CONTINUE (EXIT_SUCCESS + EXIT_FAILURE + 1)
+#define MARGIN 30
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+
+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 offset = 0;
+int next = 0;
+int opt[128];
+int rows = 0;
+
+size_t utf8_len (char *);
+size_t rune_len (long);
+size_t utf8_to_rune (long *, char *);
+int utf8_is_unicode (long);
+int utf8_check (char *);
+int utf8_is_print (long);
(DIR) diff --git a/utf8.c b/utf8.c
@@ -1,3 +1,5 @@
+#include "utf8.h"
+
/*
* ASCII all have a leading '0' byte:
*
@@ -28,7 +30,6 @@
#include "utf8.h"
-
/*
* Return the number of bytes in rune for the `n` next char in `s`,
* or 0 if ti is misencoded.
@@ -56,20 +57,18 @@ utf8_len(char *s)
return len;
}
-
/*
* Return the number of bytes required to encode `rune` into UTF-8, or
* 0 if rune is too long.
*/
size_t
-rune_len(long r)
+utf8_rune_len(long r)
{
return (r <= 0x0000007f) ? 1 : (r <= 0x000007ff) ? 2 :
(r <= 0x0000ffff) ? 3 : (r <= 0x001fffff) ? 4 :
(r <= 0x03ffffff) ? 5 : (r <= 0x7fffffff) ? 6 : 0;
}
-
/*
* Sets 'r' to a rune corresponding to the firsts 'n' bytes of 's'.
*
@@ -92,13 +91,12 @@ utf8_to_rune(long *r, char *s)
*r = (*r << 6) | (*s++ & 0x3f); /* 10xxxxxx */
/* overlong sequences */
- if (rune_len(*r) != len)
+ if (utf8_rune_len(*r) != len)
return 0;
return len;
}
-
/*
* Returns 1 if the rune is a valid unicode code point and 0 if not.
*/
@@ -119,7 +117,6 @@ rune_is_unicode(long r)
);
}
-
/*
* Return 1 if '*s' is correctly encoded in UTF-8 with allowed Unicode
* code points.
@@ -146,7 +143,7 @@ utf8_check(char *s)
* Return 1 if the rune is a printable character, and 0 otherwise.
*/
int
-rune_is_print(long r)
+utf8_is_print(long r)
{
return (0x1f < r && r != 0x7f && r < 0x80) || 0x9f < r;
}
(DIR) diff --git a/utf8.h b/utf8.h
@@ -1,6 +1,6 @@
-size_t utf8_len(char *);
-size_t rune_len(long);
-size_t utf8_to_rune(long *, char *);
-int utf8_is_unicode(long);
-int utf8_check(char *);
-int rune_is_print(long);
+size_t utf8_len (char *);
+size_t rune_len (long);
+size_t utf8_to_rune (long *, char *);
+int utf8_is_unicode (long);
+int utf8_check (char *);
+int utf8_is_print (long);