diff options
| author | frosty <gabriel@bwaaa.monster> | 2026-02-23 00:37:47 -0500 |
|---|---|---|
| committer | frosty <gabriel@bwaaa.monster> | 2026-02-23 00:37:47 -0500 |
| commit | 3add5ed8899a8f4a442c249913602de6b7d60732 (patch) | |
| tree | e4f864b1f4d41aa258c8b678b00a7d9ed69640f7 /src | |
| download | insel-3add5ed8899a8f4a442c249913602de6b7d60732.tar.gz | |
billions must commit
Diffstat (limited to 'src')
| -rw-r--r-- | src/Draw.c | 49 | ||||
| -rw-r--r-- | src/Draw.h | 6 | ||||
| -rw-r--r-- | src/Globals.h | 34 | ||||
| -rw-r--r-- | src/Input.c | 112 | ||||
| -rw-r--r-- | src/Input.h | 7 | ||||
| -rw-r--r-- | src/Main.c | 112 | ||||
| -rw-r--r-- | src/Terminal.c | 75 | ||||
| -rw-r--r-- | src/Terminal.h | 12 |
8 files changed, 407 insertions, 0 deletions
diff --git a/src/Draw.c b/src/Draw.c new file mode 100644 index 0000000..1132f10 --- /dev/null +++ b/src/Draw.c @@ -0,0 +1,49 @@ +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdio.h> + +#include "Globals.h" +#include "Terminal.h" +#include "Draw.h" + +void draw(void) { + int rows, cols; + struct winsize w; + ioctl(tty_out_fd, TIOCGWINSZ, &w); + rows = w.ws_row; + cols = w.ws_col; + + if (rows < 1) rows = 24; + if (cols < 1) cols = 80; + + int prompt_len = strlen(opts.prompt); + int input_pos = prompt_len + 2; + int filter_line = rows; + int item_end = rows - 1; + + dprintf(tty_out_fd, "\033[H\033[J"); + + dprintf(tty_out_fd, "\033[%d;1H", filter_line); + dprintf(tty_out_fd, "\033[7m"); + dprintf(tty_out_fd, "%-*s", cols, ""); + dprintf(tty_out_fd, "\033[%d;1H", filter_line); + dprintf(tty_out_fd, "%s", opts.prompt); + if (input_len > 0) { + dprintf(tty_out_fd, " %s", input); + } + dprintf(tty_out_fd, "\033[0m"); + + for (int i = 1; i < item_end; i++) { + int idx = i - 1 + scroll; + dprintf(tty_out_fd, "\033[%d;1H", i); + if (idx < (int)filtered.count) { + if (idx == cursor) { + dprintf(tty_out_fd, "\033[7m"); + } + dprintf(tty_out_fd, "%s\033[0m", filtered.items[idx]); + } + } + dprintf(tty_out_fd, "\033[%d;%dH", filter_line, input_pos); + fsync(tty_out_fd); +} diff --git a/src/Draw.h b/src/Draw.h new file mode 100644 index 0000000..82c0b7f --- /dev/null +++ b/src/Draw.h @@ -0,0 +1,6 @@ +#ifndef INSEL_DRAW_H +#define INSEL_DRAW_H + +void draw(void); + +#endif diff --git a/src/Globals.h b/src/Globals.h new file mode 100644 index 0000000..95a61ea --- /dev/null +++ b/src/Globals.h @@ -0,0 +1,34 @@ +#ifndef INSEL_GLOBALS_H +#define INSEL_GLOBALS_H + +#include <stddef.h> + +#define INITIAL_CAPACITY 64 +#define DEFAULT_LINES 10 + +typedef struct { + char **items; + size_t count; + size_t capacity; +} ItemList; + +typedef struct { + char *prompt; + int insensitive; +} Options; + +extern Options opts; +extern ItemList all_items; +extern ItemList filtered; +extern char *input; +extern size_t input_len; +extern size_t input_capacity; +extern int cursor; +extern int scroll; +extern int needs_redraw; +extern int tty_fd; +extern int tty_out_fd; +extern int orig_stdout; +extern int stdin_is_tty; + +#endif diff --git a/src/Input.c b/src/Input.c new file mode 100644 index 0000000..23a474d --- /dev/null +++ b/src/Input.c @@ -0,0 +1,112 @@ +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> + +#include "Globals.h" +#include "Terminal.h" +#include "Input.h" + +int matches(const char *s) { + if (input_len == 0) return 1; + const char *p = s; + if (opts.insensitive) { + while (*p) { + if (strncasecmp(p, input, input_len) == 0) return 1; + p++; + } + } else { + while (*p) { + if (strncmp(p, input, input_len) == 0) return 1; + p++; + } + } + return 0; +} + +void filter_items(void) { + filtered.count = 0; + for (size_t i = 0; i < all_items.count; i++) { + if (matches(all_items.items[i])) { + if (filtered.count >= filtered.capacity) { + size_t new_cap = filtered.capacity == 0 ? INITIAL_CAPACITY : filtered.capacity * 2; + filtered.items = realloc(filtered.items, new_cap * sizeof(char *)); + filtered.capacity = new_cap; + } + filtered.items[filtered.count++] = all_items.items[i]; + } + } + if ((size_t)cursor >= filtered.count) cursor = filtered.count > 0 ? (int)filtered.count - 1 : 0; + if (scroll > cursor) scroll = cursor; + if (scroll < cursor - DEFAULT_LINES + 1) scroll = cursor - DEFAULT_LINES + 1; + if (scroll < 0) scroll = 0; + needs_redraw = 1; +} + +void handle_input(void) { + char c; + if (read(tty_fd, &c, 1) <= 0) return; + if (c == 27) { + char seq[3]; + ssize_t n = read(tty_fd, &seq[0], 1); + if (n > 0 && seq[0] == '[') { + if (read(tty_fd, &seq[1], 1) > 0) { + switch (seq[1]) { + case 'A': + if (cursor > 0) cursor--; + break; + case 'B': + if (cursor < (int)filtered.count - 1) cursor++; + break; + case 'H': + case 'F': + cursor = 0; + break; + case 'G': + cursor = filtered.count > 0 ? filtered.count - 1 : 0; + break; + case '5': + case '6': + if (read(tty_fd, &seq[2], 1) > 0 && seq[2] == '~') { + int jump = DEFAULT_LINES; + if (seq[1] == '5') { + cursor -= jump; + if (cursor < 0) cursor = 0; + } else { + cursor += jump; + if (cursor >= (int)filtered.count) cursor = filtered.count > 0 ? (int)filtered.count - 1 : 0; + } + } + break; + } + } + } else { + exit(1); + } + if (cursor < scroll) scroll = cursor; + if (cursor > scroll + DEFAULT_LINES - 1) scroll = cursor - DEFAULT_LINES + 1; + needs_redraw = 1; + } else if (c == 127 || c == 8) { + if (input_len > 0) input[--input_len] = '\0'; + filter_items(); + } else if (c == '\n' || c == '\r') { + if (filtered.count > 0) { + dprintf(tty_out_fd, "\033[?1049l\033[2J\033[?25h\033[0m"); + dprintf(orig_stdout, "%s\n", filtered.items[cursor]); + exit(0); + } + } else if (c == 4) { + exit(1); + } else if (c >= 32 && c < 127) { + if ((int)input_len + 2 > (int)input_capacity) { + size_t new_cap = input_capacity == 0 ? INITIAL_CAPACITY : input_capacity * 2; + input = realloc(input, new_cap); + memset(input + input_capacity, 0, new_cap - input_capacity); + input_capacity = new_cap; + } + input[input_len++] = c; + input[input_len] = '\0'; + filter_items(); + } +} diff --git a/src/Input.h b/src/Input.h new file mode 100644 index 0000000..eefd6d0 --- /dev/null +++ b/src/Input.h @@ -0,0 +1,7 @@ +#ifndef INSEL_INPUT_H +#define INSEL_INPUT_H + +void filter_items(void); +void handle_input(void); + +#endif diff --git a/src/Main.c b/src/Main.c new file mode 100644 index 0000000..4ecd4f3 --- /dev/null +++ b/src/Main.c @@ -0,0 +1,112 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/select.h> + +#include "Globals.h" +#include "Terminal.h" +#include "Draw.h" +#include "Input.h" + +Options opts = { + .prompt = "", + .insensitive = 0 +}; + +ItemList all_items = {0}; +ItemList filtered = {0}; +char *input = NULL; +size_t input_len = 0; +size_t input_capacity = 0; +int cursor = 0; +int scroll = 0; +int needs_redraw = 1; + +static void ensure_item_capacity(ItemList *list) { + if (list->count >= list->capacity) { + size_t new_cap = list->capacity == 0 ? INITIAL_CAPACITY : list->capacity * 2; + list->items = realloc(list->items, new_cap * sizeof(char *)); + list->capacity = new_cap; + } +} + +void read_items(void) { + char *line = NULL; + size_t len = 0; + ssize_t n; + while ((n = getline(&line, &len, stdin)) != -1) { + if (n > 0 && line[n-1] == '\n') line[n-1] = '\0'; + ensure_item_capacity(&all_items); + all_items.items[all_items.count++] = strdup(line); + } + free(line); + filter_items(); +} + +void version(void) { + puts("insel 1.0"); + puts("'those who select their inputs'\n"); + puts("GNU GPL version 2 <https://gnu.org/licenses/old-licenses/gpl-2.0.html>.\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law."); + exit(0); +} + +void help(void) { + puts("Usage: insel [-i] [-p prompt]" + "\n" + "Options:" + "\n" + " -i Case-insensitive matching" + "\n" + " -v Show version" + "\n" + " -p prompt Prompt to display" + "\n" + " --help Show this help message"); + exit(0); +} + +int main(int argc, char *argv[]) { + stdin_is_tty = isatty(STDIN_FILENO); + orig_stdout = dup(STDOUT_FILENO); + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-v") == 0) version(); + else if (strcmp(argv[i], "--help") == 0) help(); + else if (strcmp(argv[i], "-i") == 0) opts.insensitive = 1; + else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) { + if (opts.prompt[0] != '\0') free(opts.prompt); + opts.prompt = strdup(argv[++i]); + } + else if (strcmp(argv[i], "-nb") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-nf") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-sb") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-sf") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-fn") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-m") == 0 && i + 1 < argc) i++; + else if (strcmp(argv[i], "-w") == 0 && i + 1 < argc) i++; + } + + read_items(); + setup_terminal(); + + while (1) { + if (needs_redraw) { + draw(); + needs_redraw = 0; + } + fd_set fds; + FD_ZERO(&fds); + FD_SET(tty_fd, &fds); + struct timeval tv = { .tv_sec = 0, .tv_usec = 10000 }; + if (select(tty_fd + 1, &fds, NULL, NULL, &tv) > 0) { + handle_input(); + } + } + return 0; +} diff --git a/src/Terminal.c b/src/Terminal.c new file mode 100644 index 0000000..6cc24db --- /dev/null +++ b/src/Terminal.c @@ -0,0 +1,75 @@ +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> + +#include "Globals.h" +#include "Terminal.h" + +static struct termios orig_term; + +int tty_fd = -1; +int tty_out_fd = -1; +int orig_stdout = -1; +int stdin_is_tty = 0; + +void restore_terminal(void) { + if (tty_fd >= 0) { + tcsetattr(tty_fd, TCSAFLUSH, &orig_term); + } + + int restore_fd = tty_out_fd >= 0 ? tty_out_fd : STDOUT_FILENO; + dprintf(restore_fd, "\033[?1049l\033[2J\033[?25h\033[0m"); + + if (tty_fd >= 0) { + close(tty_fd); + } + if (tty_out_fd >= 0 && tty_out_fd != STDOUT_FILENO) { + close(tty_out_fd); + } + if (orig_stdout >= 0 && orig_stdout != STDOUT_FILENO) { + close(orig_stdout); + } +} + +static void handle_signal(int sig) { + restore_terminal(); + _exit(128 + sig); +} + +void setup_terminal(void) { + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); + + if (isatty(STDOUT_FILENO)) { + tty_out_fd = STDOUT_FILENO; + } else { + tty_out_fd = open("/dev/tty", O_WRONLY); + if (tty_out_fd < 0) { + fprintf(stderr, "Error: cannot open terminal for output\n"); + exit(1); + } + } + + tty_fd = open("/dev/tty", O_RDWR); + if (tty_fd < 0) { + tty_fd = dup(STDIN_FILENO); + } + + if (tty_fd < 0) { + dprintf(tty_out_fd, "\033[?1049l\033[2J\033[?25h"); + close(tty_out_fd); + fprintf(stderr, "Error: cannot open terminal for keyboard input\n"); + exit(1); + } + + struct termios t; + tcgetattr(tty_fd, &orig_term); + atexit(restore_terminal); + tcgetattr(tty_fd, &t); + t.c_lflag &= ~(ICANON | ECHO); + tcsetattr(tty_fd, TCSAFLUSH, &t); + dprintf(tty_out_fd, "\033[?1049l\033[2J\033[?25l"); +} diff --git a/src/Terminal.h b/src/Terminal.h new file mode 100644 index 0000000..3b5fb0b --- /dev/null +++ b/src/Terminal.h @@ -0,0 +1,12 @@ +#ifndef INSEL_TERMINAL_H +#define INSEL_TERMINAL_H + +void setup_terminal(void); +void restore_terminal(void); + +extern int tty_fd; +extern int tty_out_fd; +extern int orig_stdout; +extern int stdin_is_tty; + +#endif |
