fe

terminal file explorer and picker

git clone https://9o.is/git/fe.git

commit 24c6b472ded585c4e779f6a4e5f8bef64f9ef9a2
parent 1ab215102959e9cb22de7c0ca8056a19f08d78b1
Author: Jul <jul@9o.is>
Date:   Tue, 20 Jan 2026 12:46:09 -0500

merge actions with tty_interface

Diffstat:
MMakefile | 1-
Dactions.c | 210-------------------------------------------------------------------------------
Dactions.h | 16----------------
Mconfig.def.h | 4+---
Moptions.h | 1+
Mtty_interface.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mtty_interface.h | 19+++++++++++++++++--
7 files changed, 232 insertions(+), 236 deletions(-)

diff --git a/Makefile b/Makefile @@ -19,7 +19,6 @@ OBJS = fe.o \ entries.o \ tty_interface.o \ tty.o \ - actions.o \ compats.o all: fe diff --git a/actions.c b/actions.c @@ -1,210 +0,0 @@ -#include <sys/types.h> -#include <sys/wait.h> -#include <stdlib.h> -#include <errno.h> -#include <stdarg.h> -#include <unistd.h> -#include <stdio.h> -#include <string.h> -#include "config.h" - -#define EXEC_REPLACE 1 -#define EXEC_SHELL 2 -#define EXEC_CAPTURE 4 - -static int x_spawn(const char *cmd, char *const argv[], int flags, char *out_buf, size_t out_sz) { - int pipefd[2]; - - if (flags & EXEC_CAPTURE) { - if (pipe(pipefd) == -1) return -1; - } - - pid_t pid = (flags & EXEC_REPLACE) ? 0 : fork(); - - if (pid == -1) return -1; - - if (pid == 0) { - if (flags & EXEC_CAPTURE) { - dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[0]); - close(pipefd[1]); - } - - if (flags & EXEC_SHELL) { - execlp("sh", "sh", "-c", cmd, (char *)NULL); - } else { - execvp(cmd, argv); - } - - perror("exec failed"); - if (flags & EXEC_REPLACE) return -1; - _exit(EXIT_FAILURE); - } - - if (flags & EXEC_CAPTURE) { - close(pipefd[1]); - read(pipefd[0], out_buf, out_sz - 1); - close(pipefd[0]); - } - - int status; - waitpid(pid, &status, 0); - return WEXITSTATUS(status); -} - -void action_ignore(tty_interface_t *state, const char *argv) { - (void)state; - (void)argv; -} - -void action_select(tty_interface_t *state, const char *argv) { - (void)argv; - int done = entries_select(state->entries); - - if (!done) { - draw(state); - } else { - clear(state); - tty_close(state->tty); - const struct entry *selection = entries_selected(state->entries); - - if (state->options->run == NULL) { - printf("%s/%s\n", state->entries->path, selection->name); - state->exit = EXIT_SUCCESS; - } else { - char fullpath[2*PATH_MAX]; - snprintf(fullpath, sizeof(fullpath), "%s/%s", state->entries->path, selection->name); - - char *const sargv[] = {(char *)state->options->run, fullpath, NULL}; - x_spawn(state->options->run, sargv, EXEC_REPLACE, NULL, 0); - state->exit = EXIT_FAILURE; - } - } -} - -void action_parent(tty_interface_t *state, const char *argv) { - (void)argv; - entries_parent(state->entries); - draw(state); -} - -void action_prev(tty_interface_t *state, const char *argv) { - (void)argv; - entries_prev(state->entries); - draw(state); -} - -void action_next(tty_interface_t *state, const char *argv) { - (void)argv; - entries_next(state->entries); - draw(state); -} - -void action_halfpageup(tty_interface_t *state, const char *argv) { - (void)argv; - for (size_t i = 0; i < (state->options->num_files / 2) && state->entries->selection > 0; i++) - entries_prev(state->entries); - - draw(state); -} - -void action_halfpagedown(tty_interface_t *state, const char *argv) { - (void)argv; - for (size_t i = 0; i < (state->options->num_files / 2) && state->entries->selection < state->entries->size - 1; i++) - entries_next(state->entries); - - draw(state); -} - -void action_pageup(tty_interface_t *state, const char *argv) { - (void)argv; - for (size_t i = 0; i < state->options->num_files && state->entries->selection > 0; i++) - entries_prev(state->entries); - - draw(state); -} - -void action_pagedown(tty_interface_t *state, const char *argv) { - (void)argv; - for (size_t i = 0; i < state->options->num_files && state->entries->selection < state->entries->size - 1; i++) - entries_next(state->entries); - - draw(state); -} - -void action_first(tty_interface_t *state, const char *argv) { - (void)argv; - entries_position(state->entries, 0); - draw(state); -} - -void action_last(tty_interface_t *state, const char *argv) { - (void)argv; - entries_position(state->entries, state->entries->size - 1); - draw(state); -} - -void action_home(tty_interface_t *state, const char *argv) { - (void)argv; - entries_setpath(state->entries, getenv("HOME")); - draw(state); -} - -void action_togglehidden(tty_interface_t *state, const char *argv) { - (void)argv; - entries_togglehidden(); - draw(state); -} - -void action_run(tty_interface_t *state, const char *argv) { - (void)argv; - - const struct entry *entry = entries_item(state->entries, state->entries->selection); - if (!entry) return; - - size_t input_len = strlen(argv) + PATH_MAX; - char *input = malloc(input_len); - - if (input == NULL) { - perror("Failed to allocate memory"); - return; - } - - snprintf(input, input_len, argv, state->entries->path, entry->name); - - clear(state); - x_spawn(input, NULL, EXEC_SHELL, NULL, 0); - - free(input); - tty_hide_cursor(state->tty); - draw(state); -} - -void action_setpath(tty_interface_t *state, const char *argv) { - size_t input_len = strlen(argv) + PATH_MAX; - char *input = malloc(input_len); - - if (input == NULL) { - perror("Failed to allocate memory"); - return; - } - - snprintf(input, input_len, argv, state->entries->path); - - char output[PATH_MAX]; - clear(state); - - if (0 == x_spawn(input, NULL, EXEC_SHELL | EXEC_CAPTURE, output, sizeof(output))) - entries_setpath(state->entries, output); - - free(input); - tty_hide_cursor(state->tty); - draw(state); -} - -void action_exit(tty_interface_t *state, const char *argv) { - (void)argv; - clear(state); - tty_close(state->tty); - state->exit = EXIT_FAILURE; -} diff --git a/actions.h b/actions.h @@ -1,16 +0,0 @@ -void action_ignore(tty_interface_t *state, const char *argv); -void action_select(tty_interface_t *state, const char *argv); -void action_parent(tty_interface_t *state, const char *argv); -void action_prev(tty_interface_t *state, const char *argv); -void action_next(tty_interface_t *state, const char *argv); -void action_halfpageup(tty_interface_t *state, const char *argv); -void action_halfpagedown(tty_interface_t *state, const char *argv); -void action_pageup(tty_interface_t *state, const char *argv); -void action_pagedown(tty_interface_t *state, const char *argv); -void action_first(tty_interface_t *state, const char *argv); -void action_last(tty_interface_t *state, const char *argv); -void action_home(tty_interface_t *state, const char *argv); -void action_togglehidden(tty_interface_t *state, const char *argv); -void action_run(tty_interface_t *state, const char *argv); -void action_setpath(tty_interface_t *state, const char *argv); -void action_exit(tty_interface_t *state, const char *argv); diff --git a/config.def.h b/config.def.h @@ -1,6 +1,5 @@ #include "tty.h" #include "tty_interface.h" -#include "actions.h" #include "options.h" #define TTY_COLOR_BLACK 0 @@ -20,8 +19,6 @@ #define COLOR_EXEC TTY_COLOR_GREEN #define COLOR_REGULAR TTY_COLOR_NORMAL -#define KEYTIMEOUT 25 - #define KEY(key) ((const char[]){key, '\0'}) #define KEY_CTRL(key) ((const char[]){((key) - ('@')), '\0'}) @@ -60,6 +57,7 @@ static const keybinding_t keybindings[] = { static const options_t default_options = { .num_files = 25, .tty_filename = "/dev/tty", + .keytimeout = 25, .sort_dir = 0, .sort_icase = 0, .sort_mtime = 0, diff --git a/options.h b/options.h @@ -14,6 +14,7 @@ typedef struct { const char *tty_filename; const char *run; const char *path; + int keytimeout; int sort_dir; int sort_icase; int sort_mtime; diff --git a/tty_interface.c b/tty_interface.c @@ -1,7 +1,14 @@ +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdio.h> #include <string.h> -#include "config.h" +#include "tty_interface.h" -void clear(tty_interface_t *state) { +static void clear(tty_interface_t *state) { tty_t *tty = state->tty; tty_setcol(tty, 0); @@ -18,7 +25,7 @@ void clear(tty_interface_t *state) { tty_flush(tty); } -void draw(tty_interface_t *state) { +static void draw(tty_interface_t *state) { tty_t *tty = state->tty; entries_t *entries = state->entries; const options_t *options = state->options; @@ -70,6 +77,208 @@ void draw(tty_interface_t *state) { tty_flush(tty); } +#define EXEC_REPLACE 1 +#define EXEC_SHELL 2 +#define EXEC_CAPTURE 4 + +static int x_spawn(const char *cmd, char *const argv[], int flags, char *out_buf, size_t out_sz) { + int pipefd[2]; + + if (flags & EXEC_CAPTURE) { + if (pipe(pipefd) == -1) return -1; + } + + pid_t pid = (flags & EXEC_REPLACE) ? 0 : fork(); + + if (pid == -1) return -1; + + if (pid == 0) { + if (flags & EXEC_CAPTURE) { + dup2(pipefd[1], STDOUT_FILENO); + close(pipefd[0]); + close(pipefd[1]); + } + + if (flags & EXEC_SHELL) { + execlp("sh", "sh", "-c", cmd, (char *)NULL); + } else { + execvp(cmd, argv); + } + + perror("exec failed"); + if (flags & EXEC_REPLACE) return -1; + _exit(EXIT_FAILURE); + } + + if (flags & EXEC_CAPTURE) { + close(pipefd[1]); + read(pipefd[0], out_buf, out_sz - 1); + close(pipefd[0]); + } + + int status; + waitpid(pid, &status, 0); + return WEXITSTATUS(status); +} + +void action_ignore(tty_interface_t *state, const char *argv) { + (void)state; + (void)argv; +} + +void action_select(tty_interface_t *state, const char *argv) { + (void)argv; + int done = entries_select(state->entries); + + if (!done) { + draw(state); + } else { + clear(state); + const struct entry *selection = entries_selected(state->entries); + + if (state->options->run == NULL) { + tty_printf(state->tty, "%s/%s\n", state->entries->path, selection->name); + state->exit = EXIT_SUCCESS; + } else { + char fullpath[2*PATH_MAX]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", state->entries->path, selection->name); + + char *const sargv[] = {(char *)state->options->run, fullpath, NULL}; + x_spawn(state->options->run, sargv, EXEC_REPLACE, NULL, 0); + state->exit = EXIT_FAILURE; + } + tty_close(state->tty); + } +} + +void action_parent(tty_interface_t *state, const char *argv) { + (void)argv; + entries_parent(state->entries); + draw(state); +} + +void action_prev(tty_interface_t *state, const char *argv) { + (void)argv; + entries_prev(state->entries); + draw(state); +} + +void action_next(tty_interface_t *state, const char *argv) { + (void)argv; + entries_next(state->entries); + draw(state); +} + +void action_halfpageup(tty_interface_t *state, const char *argv) { + (void)argv; + for (size_t i = 0; i < (state->options->num_files / 2) && state->entries->selection > 0; i++) + entries_prev(state->entries); + + draw(state); +} + +void action_halfpagedown(tty_interface_t *state, const char *argv) { + (void)argv; + for (size_t i = 0; i < (state->options->num_files / 2) && state->entries->selection < state->entries->size - 1; i++) + entries_next(state->entries); + + draw(state); +} + +void action_pageup(tty_interface_t *state, const char *argv) { + (void)argv; + for (size_t i = 0; i < state->options->num_files && state->entries->selection > 0; i++) + entries_prev(state->entries); + + draw(state); +} + +void action_pagedown(tty_interface_t *state, const char *argv) { + (void)argv; + for (size_t i = 0; i < state->options->num_files && state->entries->selection < state->entries->size - 1; i++) + entries_next(state->entries); + + draw(state); +} + +void action_first(tty_interface_t *state, const char *argv) { + (void)argv; + entries_position(state->entries, 0); + draw(state); +} + +void action_last(tty_interface_t *state, const char *argv) { + (void)argv; + entries_position(state->entries, state->entries->size - 1); + draw(state); +} + +void action_home(tty_interface_t *state, const char *argv) { + (void)argv; + entries_setpath(state->entries, getenv("HOME")); + draw(state); +} + +void action_togglehidden(tty_interface_t *state, const char *argv) { + (void)argv; + entries_togglehidden(); + draw(state); +} + +void action_run(tty_interface_t *state, const char *argv) { + (void)argv; + + const struct entry *entry = entries_item(state->entries, state->entries->selection); + if (!entry) return; + + size_t input_len = strlen(argv) + PATH_MAX; + char *input = malloc(input_len); + + if (input == NULL) { + perror("Failed to allocate memory"); + return; + } + + snprintf(input, input_len, argv, state->entries->path, entry->name); + + clear(state); + x_spawn(input, NULL, EXEC_SHELL, NULL, 0); + + free(input); + tty_hide_cursor(state->tty); + draw(state); +} + +void action_setpath(tty_interface_t *state, const char *argv) { + size_t input_len = strlen(argv) + PATH_MAX; + char *input = malloc(input_len); + + if (input == NULL) { + perror("Failed to allocate memory"); + return; + } + + snprintf(input, input_len, argv, state->entries->path); + + char output[PATH_MAX]; + clear(state); + + if (0 == x_spawn(input, NULL, EXEC_SHELL | EXEC_CAPTURE, output, sizeof(output))) + entries_setpath(state->entries, output); + + free(input); + tty_hide_cursor(state->tty); + draw(state); +} + +void action_exit(tty_interface_t *state, const char *argv) { + (void)argv; + clear(state); + tty_close(state->tty); + state->exit = EXIT_FAILURE; +} + + void tty_interface_init(tty_interface_t *state, tty_t *tty, entries_t *entries, options_t *options) { if (options->num_files + 1 > tty_getheight(tty)) { options->num_files = tty_getheight(tty) - 3; @@ -134,7 +343,7 @@ int tty_interface_run(tty_interface_t *state) { if (state->exit >= 0) return state->exit; - } while (tty_input_ready(state->tty, state->ambiguous_key_pending ? KEYTIMEOUT : 0, 0)); + } while (tty_input_ready(state->tty, state->ambiguous_key_pending ? state->options->keytimeout : 0, 0)); if (state->ambiguous_key_pending) { const char s[1] = ""; diff --git a/tty_interface.h b/tty_interface.h @@ -16,7 +16,22 @@ typedef struct tty_interface { void tty_interface_init(tty_interface_t *state, tty_t *tty, entries_t *entries, options_t *options); int tty_interface_run(tty_interface_t *state); -void draw(tty_interface_t *state); -void clear(tty_interface_t *state); + +void action_ignore(tty_interface_t *state, const char *argv); +void action_select(tty_interface_t *state, const char *argv); +void action_parent(tty_interface_t *state, const char *argv); +void action_prev(tty_interface_t *state, const char *argv); +void action_next(tty_interface_t *state, const char *argv); +void action_halfpageup(tty_interface_t *state, const char *argv); +void action_halfpagedown(tty_interface_t *state, const char *argv); +void action_pageup(tty_interface_t *state, const char *argv); +void action_pagedown(tty_interface_t *state, const char *argv); +void action_first(tty_interface_t *state, const char *argv); +void action_last(tty_interface_t *state, const char *argv); +void action_home(tty_interface_t *state, const char *argv); +void action_togglehidden(tty_interface_t *state, const char *argv); +void action_run(tty_interface_t *state, const char *argv); +void action_setpath(tty_interface_t *state, const char *argv); +void action_exit(tty_interface_t *state, const char *argv); #endif