fe
terminal file explorer and picker
git clone https://9o.is/git/fe.git
commit 7425f960ebff80875ad5f4492c1fef67f42cb4ea parent a746585ada39bfc628f3a371644482701cfb0491 Author: Jul <jul@9o.is> Date: Tue, 27 Jan 2026 06:59:44 -0500 add 'y' 'v' keybindings to copy paste files Diffstat:
| M | config.def.h | | | 2 | ++ |
| M | entries.c | | | 29 | +++++++++++++++++++++++++++++ |
| M | entries.h | | | 1 | + |
| M | tty_interface.c | | | 59 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | tty_interface.h | | | 4 | ++++ |
5 files changed, 95 insertions(+), 0 deletions(-)
diff --git a/config.def.h b/config.def.h @@ -41,6 +41,8 @@ static const keybinding_t keybindings[] = { {KEY('%'), action_create, NULL}, /* % (create) */ {KEY('D'), action_mkdir, NULL}, /* D (mkdir) */ {KEY('d'), action_remove, NULL}, /* d (delete/remove) */ + {KEY('y'), action_copy, NULL}, /* y */ + {KEY('v'), action_paste, NULL}, /* v */ {KEY('p'), action_run, "less %s/%s"}, /* p (preview) */ {KEY('t'), action_run, "tmux new-window -b 'vis %s/%s'"}, /* edit new tab */ {KEY('f'), action_setpath, "ag -g . %s | fzy"}, /* s (search) */ diff --git a/entries.c b/entries.c @@ -430,6 +430,35 @@ int entries_find_file(entries_t *entries, const char *filename) { return -1; } +int entries_copy_file(const char *src, const char *dst) { + int src_fd, dst_fd; + char buffer[4096]; + ssize_t bytes_read; + + src_fd = open(src, O_RDONLY); + if (src_fd == -1) { + return -1; + } + + dst_fd = open(dst, O_CREAT | O_WRONLY | O_TRUNC, 0644); + if (dst_fd == -1) { + close(src_fd); + return -1; + } + + while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) { + if (write(dst_fd, buffer, bytes_read) != bytes_read) { + close(src_fd); + close(dst_fd); + return -1; + } + } + + close(src_fd); + close(dst_fd); + return 0; +} + void entries_reload(entries_t *entries) { char *name = entries->dents[entries->selection].name; set_directory(entries, entries->path); diff --git a/entries.h b/entries.h @@ -39,5 +39,6 @@ int entries_remove(entries_t *entries); int entries_create_file(entries_t *entries, const char *filename); int entries_create_dir(entries_t *entries, const char *dirname); int entries_find_file(entries_t *entries, const char *filename); +int entries_copy_file(const char *src, const char *dst); #endif diff --git a/tty_interface.c b/tty_interface.c @@ -392,6 +392,64 @@ void action_mkdir(tty_interface_t *state, const char *argv) { draw(state); } +void action_copy(tty_interface_t *state, const char *argv) { + (void)argv; + const struct entry *entry = entries_selected(state->entries); + if (!entry) return; + + if (strcmp(state->entries->path, "/") == 0) { + strlcpy(state->copy_buffer, "/", sizeof(state->copy_buffer)); + strlcat(state->copy_buffer, entry->name, sizeof(state->copy_buffer)); + } else { + strlcpy(state->copy_buffer, state->entries->path, sizeof(state->copy_buffer)); + strlcat(state->copy_buffer, "/", sizeof(state->copy_buffer)); + strlcat(state->copy_buffer, entry->name, sizeof(state->copy_buffer)); + } + state->has_copied = 1; + + tty_setcol(state->tty, 0); + tty_clearline(state->tty); + tty_printf(state->tty, "Copied %s", state->copy_buffer); + tty_flush(state->tty); +} + +void action_paste(tty_interface_t *state, const char *argv) { + (void)argv; + if (!state->has_copied) return; + + char *filename = strrchr(state->copy_buffer, '/'); + if (!filename) filename = state->copy_buffer; + else filename++; + + char dst_path[PATH_MAX]; + if (strcmp(state->entries->path, "/") == 0) { + strlcpy(dst_path, "/", sizeof(dst_path)); + strlcat(dst_path, filename, sizeof(dst_path)); + } else { + strlcpy(dst_path, state->entries->path, sizeof(dst_path)); + strlcat(dst_path, "/", sizeof(dst_path)); + strlcat(dst_path, filename, sizeof(dst_path)); + } + + int result = 0; + if (entries_copy_file(state->copy_buffer, dst_path) == 0) { + entries_setpath(state->entries, state->entries->path); + int index = entries_find_file(state->entries, filename); + if (index != -1) { + entries_position(state->entries, (size_t)index); + } + result = 1; + } + + draw(state); + + if (result) { + tty_setcol(state->tty, 0); + tty_clearline(state->tty); + tty_printf(state->tty, "Pasted %s", state->copy_buffer); + tty_flush(state->tty); + } +} void action_exit(tty_interface_t *state, const char *argv) { (void)argv; @@ -411,6 +469,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, entries_t *entries, state->options = options; state->ambiguous_key_pending = 0; state->exit = -1; + state->has_copied = 0; strcpy(state->input, ""); tty_hide_cursor(tty); diff --git a/tty_interface.h b/tty_interface.h @@ -12,6 +12,8 @@ typedef struct tty_interface { int ambiguous_key_pending; char input[32]; int exit; + char copy_buffer[PATH_MAX]; + int has_copied; } tty_interface_t; void tty_interface_init(tty_interface_t *state, tty_t *tty, entries_t *entries, options_t *options); @@ -36,6 +38,8 @@ void action_setpath(tty_interface_t *state, const char *argv); void action_remove(tty_interface_t *state, const char *argv); void action_create(tty_interface_t *state, const char *argv); void action_mkdir(tty_interface_t *state, const char *argv); +void action_copy(tty_interface_t *state, const char *argv); +void action_paste(tty_interface_t *state, const char *argv); void action_exit(tty_interface_t *state, const char *argv); #endif