fe
terminal file explorer and picker
git clone https://9o.is/git/fe.git
commit e56fd1e39e4401896ed5244396fe1b53a9e6023b parent ca95bad8a50f50dd7e71ff3926df0bc75208a458 Author: Jul <jul@9o.is> Date: Tue, 27 Jan 2026 23:22:16 -0500 add 'x' keybinding for cut/paste moving files Diffstat:
| M | config.def.h | | | 1 | + |
| M | entries.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | entries.h | | | 1 | + |
| M | tty_interface.c | | | 47 | ++++++++++++++++++++++++++++++++++++++++++----- |
| M | tty_interface.h | | | 2 | ++ |
5 files changed, 113 insertions(+), 5 deletions(-)
diff --git a/config.def.h b/config.def.h @@ -41,6 +41,7 @@ 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('x'), action_cut, NULL}, /* x (cut) */ {KEY('y'), action_copy, NULL}, /* y */ {KEY('v'), action_paste, NULL}, /* v */ {KEY('p'), action_run, "less %s/%s"}, /* p (preview) */ diff --git a/entries.c b/entries.c @@ -528,3 +528,70 @@ void entries_reload(entries_t *entries) { int index = entries_find_file(entries, name); entries_position(entries, (size_t)index); } + +int entries_move_file(const char *src, const char *dst) { + if (filetype(src) != S_IFDIR) { + return rename(src, dst); + } + + if (rename(src, dst) == 0) { + return 0; + } + + if (errno != EXDEV) { + return -1; + } + + if (entries_copy_file(src, dst) == 0) { + char stack[PATH_MAX][1024]; + int stack_top = 0; + strlcpy(stack[stack_top], src, PATH_MAX); + stack_top++; + + while (stack_top > 0) { + stack_top--; + char current_path[PATH_MAX]; + strlcpy(current_path, stack[stack_top], PATH_MAX); + + DIR *dirp = opendir(current_path); + if (dirp == NULL) { + continue; + } + + int is_empty = 1; + struct dirent *dp; + + while ((dp = readdir(dirp)) != NULL) { + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; + + char child_path[PATH_MAX]; + mkpath(current_path, dp->d_name, child_path, sizeof(child_path)); + + if (filetype(child_path) == S_IFDIR) { + if (stack_top < 1024) { + strlcpy(stack[stack_top], child_path, PATH_MAX); + stack_top++; + } + is_empty = 0; + } else { + if (unlink(child_path) == -1) { + perror("unlink"); + } + } + } + + closedir(dirp); + + if (is_empty) { + if (rmdir(current_path) == -1) { + perror("rmdir"); + } + } + } + + return 0; + } + + return -1; +} diff --git a/entries.h b/entries.h @@ -40,5 +40,6 @@ 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); +int entries_move_file(const char *src, const char *dst); #endif diff --git a/tty_interface.c b/tty_interface.c @@ -406,6 +406,7 @@ void action_copy(tty_interface_t *state, const char *argv) { strlcat(state->copy_buffer, entry->name, sizeof(state->copy_buffer)); } state->has_copied = 1; + state->is_cut = 0; tty_setcol(state->tty, 0); tty_clearline(state->tty); @@ -413,6 +414,28 @@ void action_copy(tty_interface_t *state, const char *argv) { tty_flush(state->tty); } +void action_cut(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; + state->is_cut = 1; + + tty_setcol(state->tty, 0); + tty_clearline(state->tty); + tty_printf(state->tty, "Cut %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; @@ -432,21 +455,34 @@ void action_paste(tty_interface_t *state, const char *argv) { } int result = 0; - if (entries_copy_file(state->copy_buffer, dst_path) == 0) { + if (state->is_cut) { + result = entries_move_file(state->copy_buffer, dst_path); + if (result == 0) { + state->has_copied = 0; + state->is_cut = 0; + } + } else { + result = entries_copy_file(state->copy_buffer, dst_path); + } + + if (result == 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); +draw(state); - if (result) { + if (result == 0) { tty_setcol(state->tty, 0); tty_clearline(state->tty); - tty_printf(state->tty, "Pasted %s", state->copy_buffer); + if (state->is_cut) { + tty_printf(state->tty, "Moved %s", state->copy_buffer); + } else { + tty_printf(state->tty, "Pasted %s", state->copy_buffer); + } tty_flush(state->tty); } } @@ -470,6 +506,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, entries_t *entries, state->ambiguous_key_pending = 0; state->exit = -1; state->has_copied = 0; + state->is_cut = 0; strcpy(state->input, ""); tty_hide_cursor(tty); diff --git a/tty_interface.h b/tty_interface.h @@ -14,6 +14,7 @@ typedef struct tty_interface { int exit; char copy_buffer[PATH_MAX]; int has_copied; + int is_cut; /* 0 = copy, 1 = cut */ } tty_interface_t; void tty_interface_init(tty_interface_t *state, tty_t *tty, entries_t *entries, options_t *options); @@ -39,6 +40,7 @@ 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_cut(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);