commit cd1cdaafbe1a57c6e64fffee8bb208648e1b758c
Author: rani <clagv.randomgames@gmail.com>
Date: Thu Jan 15 14:34:25 2026 +0000
diff --git a/include/dir.h b/include/dir.h
index 7f8d3da..eea10af 100644
--- a/include/dir.h
+++ b/include/dir.h
@@ -956 +957 @@ cvector(dirent_t) dir_entries(const dir_t * dir);
void dir_sort(dir_t * dir);
const char * dir_get_home(void);
size_t dir_get_total_marked(void);
+int dir_paste_marks(const char * cwd, size_t * total_pastes);
char dirent_crepr(const dirent_t * de); // character representing dir entry
char dirent_creprl(const dirent_t * de); // like above, but for the link
diff --git a/include/main.h b/include/main.h
new file mode 100644
index 0000000..acb6417
--- /dev/null
+++ b/include/main.h
@@ -00 +19 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+enum prog_mode {
+ MODE_NORMAL,
+ MODE_CUT,
+};
+
+#endif /* MAIN_H */
diff --git a/include/ui.h b/include/ui.h
index 1735fa4..92bcf37 100644
--- a/include/ui.h
+++ b/include/ui.h
@@ -66 +67 @@
#include <stddef.h>
#include "dir.h"
+#include "main.h"
#define CTRL(k) ((k) & 0x1F)
@@ -6210 +6311 @@ void ui_status_error(const char * status);
void ui_erase(void);
void ui_refresh(void);
void ui_print_dir(const dir_t * dir, size_t cursor);
-void ui_print_cursor(size_t cursor, size_t total, size_t total_marked);
+void ui_print_cursor(size_t cursor, size_t total, enum prog_mode mode, size_t total_marked);
void ui_resize(void);
const char * ui_readline(const char * prompt);
const char * ui_prompt(const char * prompt, prompt_opts_t opts);
bool ui_prompt_deletion(const dirent_t * de);
+bool ui_prompt_paste(size_t total_marked);
#endif /* UI_H */
diff --git a/src/dir.c b/src/dir.c
index 7dd3748..77c4b1e 100644
--- a/src/dir.c
+++ b/src/dir.c
@@ -749 +746 @@ static size_t ilen(size_t i, int base) {
}
return r;
}
-static void marks_set_destroyer(void * p) {
- ;
-}
static void marks_destroyer(void * p) {
hashmap_destroy((hashmap*)p);
}
@@ -7167 +7137 @@ int dirent_togglemark(dirent_t * de) {
de->marked = true;
hashmap * dirmarks = hashmap_get(marks, cwd);
if (!dirmarks) {
- dirmarks = hashmap_new(marks_set_destroyer);
+ dirmarks = hashmap_new(NULL);
hashmap_insert(marks, cwd, dirmarks);
}
hashmap_insert(dirmarks, de->name, (void*)true);
@@ -7613 +75843 @@ int dirent_togglemark(dirent_t * de) {
return 0;
}
+
+int dir_paste_marks(const char * cwd, size_t * total_pastes) {
+ *total_pastes = 0;
+
+ DIR * pastedir = opendir(cwd);
+ if (!pastedir) return -errno;
+ int pastefd = dirfd(pastedir);
+
+ int retval = 0;
+
+ hashmap_walk_state marksws = {0};
+ while (hashmap_walk(marks, &marksws)) {
+ const char * dirpath = marksws.key;
+ hashmap * dirmarks = marksws.val;
+
+ DIR * sourcedir = opendir(dirpath);
+ if (!sourcedir) continue;
+ int sourcefd = dirfd(sourcedir);
+
+ hashmap_walk_state dirmarksws = {0};
+ while (hashmap_walk(dirmarks, &dirmarksws)) {
+ const char * fname = dirmarksws.key;
+ // make sure the new file name doesn't already exist
+ struct stat statbuf;
+ int ret = fstatat(pastefd, fname, &statbuf, AT_SYMLINK_NOFOLLOW);
+ if (ret >= 0) continue;
+ ret = renameat(sourcefd, fname, pastefd, fname);
+ if (ret < 0) continue;
+ (*total_pastes)++;
+ }
+
+ closedir(sourcedir);
+ }
+
+ closedir(pastedir);
+ hashmap_destroy(marks);
+ marks = hashmap_new(marks_destroyer);
+ n_marks = 0;
+ return retval;
+}
diff --git a/src/main.c b/src/main.c
index 1bad0ab..1955f4b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -216 +217 @@ int main(int argc, char ** argv) {
dir_list(cwd, &dir);
ui_init();
+ enum prog_mode mode = MODE_NORMAL;
size_t cursor = 0;
ui_status_info("");
for (;;) {
@@ -307 +317 @@ int main(int argc, char ** argv) {
ui_set_title(cwd, home);
ui_print_dir(&dir, cursor);
- ui_print_cursor(cursor, dirlen, dir_get_total_marked());
+ ui_print_cursor(cursor, dirlen, mode, dir_get_total_marked());
ui_refresh();
@@ -1436 +14410 @@ int main(int argc, char ** argv) {
break;
}
case 'd': {
+ if (mode == MODE_CUT) {
+ ui_status_info("Cannot Delete While Cutting");
+ break;
+ }
ui_refresh();
bool del = ui_prompt_deletion(cur_de);
if (!del) {
@@ -17010 +17514 @@ int main(int argc, char ** argv) {
}
case 'm': {
if (!cur_de) break;
+ if (mode == MODE_CUT) {
+ ui_status_info("Cannot Mark While Cutting");
+ break;
+ }
int ret = dirent_togglemark(cur_de);
if (ret < 0) switch (-ret) {
case TOGGLEMARK_PARENT_MARKED:
- ui_status_error("Ancestor Already Marked");
+ ui_status_info("Ancestor Already Marked");
break;
case TOGGLEMARK_DIRENT_UNKNOWN:
ui_status_error("Cannot Mark Unknown Dirent");
@@ -1846 +19337 @@ int main(int argc, char ** argv) {
}
break;
}
+ case 'c':
+ if (mode == MODE_NORMAL && dir_get_total_marked() > 0) mode = MODE_CUT;
+ else if (mode == MODE_CUT) mode = MODE_NORMAL;
+ break;
+ case 'p': {
+ if (mode != MODE_CUT) {
+ ui_status_info("Cannot Paste if Not Cutting");
+ break;
+ }
+ bool paste = ui_prompt_paste(dir_get_total_marked());
+ if (!paste) {
+ ui_status_info("Not Pasting");
+ break;
+ }
+ mode = MODE_NORMAL;
+ ui_status_info("Pasting");
+ ui_refresh();
+
+ size_t total_marks, total_pastes;
+ total_marks = dir_get_total_marked();
+ int ret = dir_paste_marks(cwd, &total_pastes);
+ if (ret < 0) ui_status_error(strerror(-ret));
+ else if (total_pastes == 0)
+ ui_status_error("Nothing Pasted");
+ else if (total_marks != total_pastes)
+ ui_status_error("Not All Marks Pasted");
+ else ui_status_info("Pasted");
+ dir_free(&dir);
+ dir_list(cwd, &dir);
+ break;
+ }
case 'q':
goto finished;
case KEY_RESIZE:
diff --git a/src/ui.c b/src/ui.c
index 798f871..5697d26 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -116 +117 @@
#include "ui.h"
#include "dir.h"
+#include "main.h"
#include "config.h"
#include "cvector.h"
@@ -3637 +3647 @@ void ui_print_dir(const dir_t * dir, size_t cursor) {
}
}
-void ui_print_cursor(size_t cursor, size_t total, size_t total_marked) {
+void ui_print_cursor(size_t cursor, size_t total, enum prog_mode mode, size_t total_marked) {
size_t lines, cols;
getmaxyx(filewin, lines, cols);
if (config.longmode && !config.longinline) {
@@ -3808 +38114 @@ void ui_print_cursor(size_t cursor, size_t total, size_t total_marked) {
waddstr(filewin, "0/0");
}
- if (total_marked > 0) {
- wprintw(filewin, " | %zu Marked", total_marked);
+ if (total_marked > 0) switch (mode) {
+ case MODE_NORMAL:
+ wprintw(filewin, " | %zu Marked", total_marked);
+ break;
+ case MODE_CUT:
+ wprintw(filewin, " | Cutting %zu", total_marked);
+ break;
+ default: break;
}
}
@@ -6013 +60818 @@ bool ui_prompt_deletion(const dirent_t * de) {
return true;
}
+
+bool ui_prompt_paste(size_t total_marked) {
+ static const char * no = "No";
+ static const char * yes = "Yes";
+ static const char * fstr = "Paste %zu files into this directory?";
+
+ size_t len = snprintf(NULL, 0, fstr, total_marked);
+ char * prompt = malloc(len + 1);
+ sprintf(prompt, fstr, total_marked);
+
+ const char * ret = ui_prompt(prompt, (prompt_opts_t){no, yes, NULL});
+ free(prompt);
+ if (ret != yes) return false;
+ return true;
+}