commit 247044fb9f76f2298a44a9985bb3a950b7fa13ad
Author: rani <clagv.randomgames@gmail.com>
Date: Thu Jan 15 13:12:00 2026 +0000
diff --git a/Makefile b/Makefile
index b7d8a73..406894a 100644
--- a/Makefile
+++ b/Makefile
@@ -15 +15 @@
BIN = cscroll
-SRC = src/config.c src/dir.c src/ui.c src/main.c
+SRC = src/hashmap.c src/config.c src/dir.c src/ui.c src/main.c
OBJ = ${SRC:.c=.o}
CC ?= cc
diff --git a/include/dir.h b/include/dir.h
index 9de5e89..7f8d3da 100644
--- a/include/dir.h
+++ b/include/dir.h
@@ -246 +249 @@
#define REGSEARCH_BAD_REGEX 1
#define REGSEARCH_NOT_FOUND 2
+#define TOGGLEMARK_PARENT_MARKED 1
+#define TOGGLEMARK_DIRENT_UNKNOWN 2
+
enum size_unit {
SIZE_B,
SIZE_KB,
@@ -5813 +6115 @@ typedef struct {
char * linkname;
enum de_type linktype;
+
+ bool marked;
} dirent_t;
typedef struct {
size_t len;
cvector(dirent_t) entries;
size_t nodots_len;
- cvector(dirent_t) nodots;
+ cvector(dirent_t*) nodots;
size_t longest_uname;
size_t longest_gname;
@@ -736 +789 @@ typedef struct {
size_t longest_size_unit;
} dir_t;
+void dir_init(void);
+void dir_deinit(void);
+
int dir_list(const char * path, dir_t * dir);
void dir_free(dir_t * dir);
const char * dir_get_cwd(void);
@@ -829 +9011 @@ int dir_search_name(const dir_t * dir, const char * name, size_t * idx);
int dir_search_regex(const dir_t * dir, const char * regexstr, size_t * idx);
const char * dir_basename(const char * path);
size_t dir_len(const dir_t * dir);
+dirent_t * dir_get_entry(const dir_t * dir, size_t cursor);
cvector(dirent_t) dir_entries(const dir_t * dir);
-void dir_sort(const dir_t * dir);
+void dir_sort(dir_t * dir);
const char * dir_get_home(void);
+size_t dir_get_total_marked(void);
char dirent_crepr(const dirent_t * de); // character representing dir entry
char dirent_creprl(const dirent_t * de); // like above, but for the link
@@ -955 +1056 @@ const char * dirent_size_unit(const dirent_t * de);
size_t dirent_subfiles(const dirent_t * de); // number of sub entries in a dir
int dirent_delete(const dirent_t * de);
int dirent_open(const dirent_t * de);
+int dirent_togglemark(dirent_t * de);
#endif /* DIR_H */
diff --git a/include/hashmap.h b/include/hashmap.h
new file mode 100644
index 0000000..73f7f46
--- /dev/null
+++ b/include/hashmap.h
@@ -00 +149 @@
+#ifndef HASHMAP_H
+#define HASHMAP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+
+// collision list
+struct hashmap_col_list {
+ struct hashmap_col_list * next;
+ struct hashmap_col_list * prev;
+
+ unsigned long hash;
+ char * key;
+ void * val;
+};
+
+
+struct hashmap_bucket {
+ struct hashmap_col_list * entries;
+ struct hashmap_col_list * last;
+};
+
+
+typedef struct {
+ void (*destroyer)(void*);
+ struct hashmap_bucket ** buckets;
+ size_t len; // total entries
+ size_t max;
+} hashmap;
+
+
+typedef struct {
+ size_t curbuck;
+ struct hashmap_col_list * col;
+
+ char * key;
+ void * val;
+} hashmap_walk_state;
+
+
+hashmap * hashmap_new(void (*destroyer)(void*));
+void hashmap_insert(hashmap *, char *, void *);
+void * hashmap_get(const hashmap *, const char *);
+void hashmap_remove(hashmap *, char *);
+int hashmap_walk(hashmap *, hashmap_walk_state *);
+void hashmap_destroy(hashmap *);
+
+#endif /* HASHMAP_H */
diff --git a/include/ui.h b/include/ui.h
index e2fa624..1735fa4 100644
--- a/include/ui.h
+++ b/include/ui.h
@@ -627 +627 @@ 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);
+void ui_print_cursor(size_t cursor, size_t total, 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);
diff --git a/src/dir.c b/src/dir.c
index 56b30eb..7dd3748 100644
--- a/src/dir.c
+++ b/src/dir.c
@@ -196 +197 @@
#include "dir.h"
#include "config.h"
#include "cvector.h"
+#include "hashmap.h"
// S_ISVTX requires _XOPEN_SOURCE >= 500
// I guess sticky bit isn't POSIX
@@ -498 +5013 @@
static char cwd_buf[CWDSZ];
static char * cwd = NULL;
+// marks is a map of sets
+// keys are directories and values are sets of marked files in that directory
+static hashmap * marks = NULL;
+static size_t n_marks = 0;
-static void dir_entry(int dirfd, const char * name, dirent_t * dirent);
+static void dir_entry(int dirfd, const char * name, dirent_t * dirent, const hashmap * dirmarks);
+static void dir_fill_nodots(dir_t * dir);
static char de_crepr(enum de_type de_type);
static void free_dirent(void * p) {
dirent_t * de = (dirent_t*)p;
@@ -686 +7426 @@ 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);
+}
+
+void dir_init(void) {
+ // read cwd
+ getcwd(cwd_buf, CWDSZ);
+ cwd = cwd_buf;
+ char * p = strrchr(cwd, '/');
+ if (*(p + 1) == 0) *p = 0;
+
+ marks = hashmap_new(marks_destroyer);
+}
+
+void dir_deinit(void) {
+ hashmap_destroy(marks);
+}
int dir_list(const char * path, dir_t * dir) {
dir->len = 0;
@@ -8721 +11318 @@ int dir_list(const char * path, dir_t * dir) {
return -errno;
}
+ hashmap * dirmarks = hashmap_get(marks, path);
+
while ((de = readdir(dp))) {
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
continue;
}
dirent_t dirent;
- dir_entry(dirfd(dp), de->d_name, &dirent);
+ dir_entry(dirfd(dp), de->d_name, &dirent, dirmarks);
cvector_push_back(dir->entries, dirent);
dir->len++;
- if (*de->d_name != '.') {
- cvector_push_back(dir->nodots, dirent);
- dir->nodots_len++;
- }
-
size_t uname_len = 0;
size_t gname_len = 0;
size_t size_len = ilen(dirent.size, 10);
@@ -11911 +14225 @@ int dir_list(const char * path, dir_t * dir) {
}
closedir(dp);
+ dir_fill_nodots(dir);
dir_sort(dir);
return 0;
}
-void dir_entry(int dirfd, const char * name, dirent_t * dirent) {
+static void dir_fill_nodots(dir_t * dir) {
+ cvector_clear(dir->nodots);
+ dir->nodots_len = 0;
+
+ for (size_t i = 0; i < dir->len; i++) {
+ dirent_t * dirent = &dir->entries[i];
+ if (*dirent->name != '.') {
+ cvector_push_back(dir->nodots, dirent);
+ dir->nodots_len++;
+ }
+ }
+}
+
+void dir_entry(int dirfd, const char * name, dirent_t * dirent, const hashmap * dirmarks) {
memset(dirent, 0, sizeof(dirent_t));
dirent->name = strdup(name);
dirent->type = DE_UNKNOWN;
@@ -1346 +1718 @@ void dir_entry(int dirfd, const char * name, dirent_t * dirent) {
return;
}
+ if (dirmarks && hashmap_get(dirmarks, name)) dirent->marked = true;
+
// convert the ugly st_mode to a nice de_type
switch (statbuf.st_mode & S_IFMT) {
case S_IFSOCK: dirent->type = DE_SOCKET; break;
@@ -1997 +2389 @@ void dir_entry(int dirfd, const char * name, dirent_t * dirent) {
void dir_free(dir_t * dir) {
cvector_free(dir->entries);
+ dir->len = 0;
cvector_free(dir->nodots);
+ dir->nodots_len = 0;
}
char de_crepr(enum de_type de_type) {
@@ -27512 +3166 @@ const char * dirent_prettymode(const dirent_t * de) {
}
const char * dir_get_cwd(void) {
- if (!cwd) {
- getcwd(cwd_buf, CWDSZ);
- cwd = cwd_buf;
- char * p = strrchr(cwd, '/');
- if (*(p + 1) == 0) *p = 0;
- }
return cwd;
}
@@ -33610 +37110 @@ const char * dir_basename(const char * path) {
int dir_search_name(const dir_t * dir, const char * name, size_t * idx) {
size_t dirlen = dir_len(dir);
- cvector(dirent_t) entries = dir_entries(dir);
for (size_t i = 0; i < dirlen; i++) {
- if (!strcmp(entries[i].name, name)) {
+ dirent_t * de = dir_get_entry(dir, i);
+ if (!strcmp(de->name, name)) {
*idx = i;
return 0;
}
@@ -35014 +38513 @@ int dir_search_name(const dir_t * dir, const char * name, size_t * idx) {
int dir_search_regex(const dir_t * dir, const char * regexstr, size_t * idx) {
size_t dirlen = dir_len(dir);
- cvector(dirent_t) entries = dir_entries(dir);
regex_t regex;
int ret = regcomp(®ex, regexstr, REG_EXTENDED);
if (!!ret) return -REGSEARCH_BAD_REGEX;
for (size_t i = 0; i < dirlen; i++) {
- dirent_t * de = &entries[i];
+ dirent_t * de = dir_get_entry(dir, i);
ret = regexec(®ex, de->name, 0, NULL, 0);
if (!ret) {
*idx = i;
@@ -3759 +40911 @@ size_t dir_len(const dir_t * dir) {
else return dir->nodots_len;
}
-cvector(dirent_t) dir_entries(const dir_t * dir) {
- if (config.dots) return dir->entries;
- else return dir->nodots;
+dirent_t * dir_get_entry(const dir_t * dir, size_t cursor) {
+ if (dir_len(dir) == 0) return NULL;
+
+ if (config.dots) return &dir->entries[cursor];
+ else return dir->nodots[cursor];
}
static int dir_sort_cmp(const void * inpa, const void * inpb) {
@@ -40912 +44512 @@ static int dir_sort_cmp(const void * inpa, const void * inpb) {
}
}
-void dir_sort(const dir_t * dir) {
+
+void dir_sort(dir_t * dir) {
if (config.dir_sort == DIR_SORT_UNSORTED) return;
if (dir->entries && dir->len > 0)
qsort(dir->entries, dir->len, sizeof(dir->entries[0]), dir_sort_cmp);
- if (dir->nodots && dir->nodots_len > 0)
- qsort(dir->nodots, dir->nodots_len, sizeof(dir->entries[0]), dir_sort_cmp);
+ dir_fill_nodots(dir);
}
const char * dirent_size_unit(const dirent_t * de) {
@@ -6273 +663101 @@ const char * dir_get_home(void) {
}
return pw->pw_dir;
}
+
+size_t dir_get_total_marked(void) {
+ return n_marks;
+}
+
+static size_t dir_get_total_marked_(void) {
+ size_t n_marks = 0;
+
+ hashmap_walk_state marksws = {0};
+ while (hashmap_walk(marks, &marksws)) {
+ hashmap * dirmarks = marksws.val;
+ hashmap_walk_state dirmarksws = {0};
+ while (hashmap_walk(dirmarks, &dirmarksws))
+ n_marks++;
+ }
+
+ return n_marks;
+}
+
+int dirent_togglemark(dirent_t * de) {
+ if (de->type == DE_UNKNOWN) return -TOGGLEMARK_DIRENT_UNKNOWN;
+
+ if (de->marked) {
+ hashmap * dirmarks = hashmap_get(marks, cwd);
+ de->marked = false;
+ hashmap_remove(dirmarks, de->name);
+ if (dirmarks->len == 0) hashmap_remove(marks, cwd);
+ n_marks--;
+ } else {
+ // do not allow marking if an ancestor is already marked
+ bool bad = false;
+ char * ancestor = strdup(cwd);
+ do {
+ char * sep = strrchr(ancestor, '/');
+ *sep = 0;
+ char * base = sep + 1;
+
+ hashmap * ancestor_marks;
+ if (sep == ancestor) ancestor_marks = hashmap_get(marks, "/");
+ else ancestor_marks = hashmap_get(marks, ancestor);
+
+ if (!ancestor_marks) continue;
+ if (hashmap_get(ancestor_marks, base)) {
+ bad = true;
+ break;
+ }
+ } while (*ancestor);
+ free(ancestor);
+ if (bad) return -TOGGLEMARK_PARENT_MARKED;
+
+ de->marked = true;
+ hashmap * dirmarks = hashmap_get(marks, cwd);
+ if (!dirmarks) {
+ dirmarks = hashmap_new(marks_set_destroyer);
+ hashmap_insert(marks, cwd, dirmarks);
+ }
+ hashmap_insert(dirmarks, de->name, (void*)true);
+
+ // unmark all children if marking a directory
+ if (de->type == DE_DIR) {
+ const char * fmt;
+ if (!strcmp(cwd, "/")) fmt = "%s%s";
+ else fmt = "%s/%s";
+
+ size_t dirpathlen = snprintf(NULL, 0, fmt, cwd, de->name);
+ // extra byte for trailing slash
+ char * dirpath = malloc(dirpathlen + 1 + 1);
+ sprintf(dirpath, fmt, cwd, de->name);
+
+ // check any direct children
+ if (hashmap_get(marks, dirpath)) hashmap_remove(marks, dirpath);
+
+ // check any subchildren
+ strcat(dirpath, "/");
+ dirpathlen++;
+ bool deleted;
+ do {
+ deleted = false;
+
+ // this is a horrible solution
+ hashmap_walk_state ws = {0};
+ while (hashmap_walk(marks, &ws)) {
+ if (!strncmp(ws.key, dirpath, dirpathlen)) {
+ hashmap_remove(marks, ws.key);
+ deleted = true;
+ break;
+ }
+ }
+ } while (deleted);
+
+ free(dirpath);
+ }
+
+ n_marks = dir_get_total_marked_();
+ }
+
+ return 0;
+}
diff --git a/src/hashmap.c b/src/hashmap.c
new file mode 100644
index 0000000..34549a5
--- /dev/null
+++ b/src/hashmap.c
@@ -00 +1234 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "hashmap.h"
+
+#define HASHMAP_RESIZE_LEN 128
+#define HASHMAP_RESIZE_FACT 75 // percent full
+
+// FNV-1a
+static unsigned long hashs(const char * s) {
+ unsigned long hash = 2166136261UL;
+ int c;
+ while ((c = *s++)) {
+ hash *= 2166136261UL;
+ hash ^= c;
+ }
+ return hash;
+}
+
+
+static struct hashmap_bucket * new_bucket(void) {
+ struct hashmap_bucket * b = malloc(sizeof(struct hashmap_bucket));
+ b->entries = NULL;
+ b->last = NULL;
+ return b;
+}
+
+
+static struct hashmap_col_list * new_col_list(void) {
+ struct hashmap_col_list * cl = malloc(sizeof(struct hashmap_col_list));
+ cl->next = NULL;
+ cl->prev = NULL;
+ cl->hash = 0;
+ cl->key = NULL;
+ cl->val = NULL;
+ return cl;
+}
+
+
+static void destroy_col(struct hashmap_col_list * cl) {
+ free(cl->key);
+ free(cl);
+}
+
+
+static struct hashmap_col_list * append_col(struct hashmap_bucket * b) {
+ if (!b->entries) {
+ b->entries = new_col_list();
+ b->last = b->entries;
+ return b->entries;
+ }
+
+ b->last->next = new_col_list();
+ b->last->next->prev = b->last;
+ b->last = b->last->next;
+ return b->last;
+}
+
+
+static struct hashmap_bucket * hashmap_get_bucket(const hashmap * hm, const char * key) {
+ unsigned long hash = hashs(key);
+ size_t index = hash % hm->max;
+ return hm->buckets[index];
+}
+
+
+static struct hashmap_col_list * search_col_list(struct hashmap_bucket * b, const char * key) {
+ for (struct hashmap_col_list * cl = b->entries; cl; cl = cl->next) {
+ if (!strcmp(key, cl->key)) return cl;
+ }
+ return NULL;
+}
+
+
+hashmap * hashmap_new(void (*destroyer)(void*)) {
+ hashmap * hm = malloc(sizeof(hashmap));
+ hm->destroyer = destroyer;
+ hm->buckets = malloc(sizeof(struct hashmap_bucket*) * HASHMAP_RESIZE_LEN);
+ hm->len = 0;
+ hm->max = HASHMAP_RESIZE_LEN;
+
+ for (size_t i = 0; i < hm->max; i++) {
+ hm->buckets[i] = NULL;
+ }
+
+ return hm;
+}
+
+
+static void hashmap_resize(hashmap * hm) {
+ size_t nmax = hm->max * 2;
+ struct hashmap_bucket ** nb = malloc(sizeof(struct hashmap_bucket*) * nmax);
+
+ for (size_t i = 0; i < nmax; i++) {
+ nb[i] = NULL;
+ }
+
+ for (size_t i = 0; i < hm->max; i++) {
+ struct hashmap_bucket * ob = hm->buckets[i];
+ if (!ob || !ob->entries) continue;
+
+ for (struct hashmap_col_list * cl = ob->entries; cl;) {
+ struct hashmap_col_list * next = cl->next;
+
+ size_t nindex = cl->hash % nmax;
+ if (!nb[nindex]) {
+ nb[nindex] = new_bucket();
+ nb[nindex]->entries = cl;
+ nb[nindex]->last = cl;
+ cl->prev = NULL;
+ cl->next = NULL;
+ } else {
+ struct hashmap_col_list * last = nb[nindex]->last;
+ last->next = cl;
+ cl->next = NULL;
+ cl->prev = last;
+ nb[nindex]->last = cl;
+ }
+
+ cl = next;
+ }
+
+ free(ob);
+ }
+
+ free(hm->buckets);
+ hm->buckets = nb;
+ hm->max = nmax;
+}
+
+
+void hashmap_insert(hashmap * hm, char * key, void * val) {
+ if ((hm->len * 100) / hm->max >= HASHMAP_RESIZE_FACT) {
+ hashmap_resize(hm);
+ }
+
+ struct hashmap_col_list * cl = NULL;
+
+ unsigned long hash = hashs(key);
+ size_t index = hash % hm->max;
+ if (!hm->buckets[index]) {
+ hm->buckets[index] = new_bucket();
+ } else {
+ for (struct hashmap_col_list * l = hm->buckets[index]->entries; l; l = l->next) {
+ // overrite the old collision entry
+ if (!strcmp(l->key, key)) {
+ cl = l;
+ if (hm->destroyer) hm->destroyer(cl->val);
+ cl->val = val;
+ }
+ }
+ }
+
+ if (!cl) {
+ cl = append_col(hm->buckets[index]);
+ cl->hash = hash;
+ cl->key = strdup(key);
+ cl->val = val;
+
+ hm->len++;
+ }
+}
+
+
+void * hashmap_get(const hashmap * hm, const char * key) {
+ struct hashmap_bucket * b = hashmap_get_bucket(hm, key);
+ if (!b) return NULL;
+ struct hashmap_col_list * cl = search_col_list(b, key);
+ if (!cl) return NULL;
+ return cl->val;
+}
+
+
+void hashmap_remove(hashmap * hm, char * key) {
+ struct hashmap_bucket * b = hashmap_get_bucket(hm, key);
+ if (!b) return;
+
+ struct hashmap_col_list * cl = search_col_list(b, key);
+ if (!cl) return;
+
+ struct hashmap_col_list * prev = cl->prev;
+ struct hashmap_col_list * next = cl->next;
+
+ if (!prev) b->entries = next;
+ else prev->next = next;
+ if (next) next->prev = prev;
+
+ if (hm->destroyer) hm->destroyer(cl->val);
+ destroy_col(cl);
+}
+
+
+int hashmap_walk(hashmap * hm, hashmap_walk_state * state) {
+ for (size_t i = state->curbuck; i < hm->max; i++) {
+ struct hashmap_bucket * b = hm->buckets[i];
+ if (!b || !b->entries) continue;
+
+ if (!state->col) state->col = b->entries;
+ for (struct hashmap_col_list * cl = state->col; cl; cl = cl->next) {
+ state->col = cl->next;
+ state->curbuck = i;
+ if (!state->col) state->curbuck++;
+
+ state->key = cl->key;
+ state->val = cl->val;
+ return 1;
+ }
+ state->col = NULL;
+ }
+
+ return 0;
+}
+
+
+void hashmap_destroy(hashmap * hm) {
+ for (size_t i = 0; i < hm->max; i++) {
+ struct hashmap_bucket * b = hm->buckets[i];
+ if (!b) continue;
+
+ // free each collision in the bucket
+ for (struct hashmap_col_list * cl = b->entries; cl;) {
+ struct hashmap_col_list * next = cl->next;
+ if (hm->destroyer) hm->destroyer(cl->val);
+ destroy_col(cl);
+ cl = next;
+ }
+
+ free(b);
+ }
+
+ free(hm->buckets);
+ free(hm);
+}
diff --git a/src/main.c b/src/main.c
index 5abdfa7..1bad0ab 100644
--- a/src/main.c
+++ b/src/main.c
@@ -116 +117 @@
#include "cvector.h"
int main(int argc, char ** argv) {
+ dir_init();
const char * cwd = dir_get_cwd();
// dir_get_home may return a static field so it must be copied here
char * home = (char*)dir_get_home();
@@ -2626 +2723 @@ int main(int argc, char ** argv) {
ui_erase();
size_t dirlen = dir_len(&dir);
- cvector(dirent_t) entries = dir_entries(&dir);
ui_set_title(cwd, home);
ui_print_dir(&dir, cursor);
- ui_print_cursor(cursor, dirlen);
+ ui_print_cursor(cursor, dirlen, dir_get_total_marked());
ui_refresh();
- dirent_t * cur_de;
- if (dirlen == 0) cur_de = NULL;
- else cur_de = &entries[cursor];
+ dirent_t * cur_de = dir_get_entry(&dir, cursor);
- switch (getch()) {
+ int inputc = getch();
+ if (inputc != KEY_RESIZE) ui_status_info("");
+ switch (inputc) {
UP_KEYS:
if (cursor > 0) cursor--;
- ui_status_info("");
break;
DOWN_KEYS:
if (cursor < dirlen - 1) cursor++;
- ui_status_info("");
break;
LEFT_KEYS: {
// the string from dir_basename(cwd) may be a static field
@@ -6113 +5910 @@ int main(int argc, char ** argv) {
ret = dir_list(cwd, &dir);
if (ret < 0) {
ui_status_error(strerror(-ret));
- } else {
- if (basename) {
- size_t idx;
- int i = dir_search_name(&dir, basename, &idx);
- if (i >= 0) cursor = idx;
- }
- ui_status_info("");
+ } else if (basename) {
+ size_t idx;
+ int i = dir_search_name(&dir, basename, &idx);
+ if (i >= 0) cursor = idx;
}
} else {
ui_status_error(strerror(-ret));
@@ -767 +716 @@ int main(int argc, char ** argv) {
break;
}
RIGHT_KEYS: {
- ui_status_info("");
if (!cur_de) break;
if (cur_de->type == DE_DIR || (cur_de->type == DE_LINK
&& cur_de->linktype == DE_DIR)) {
@@ -10116 +9513 @@ int main(int argc, char ** argv) {
case KEY_HOME:
case 'g':
cursor = 0;
- ui_status_info("");
break;
case KEY_END:
case 'G':
if (dirlen == 0) cursor = 0;
else cursor = dirlen - 1;
- ui_status_info("");
break;
case '.': {
- ui_status_info("");
config.dots = !config.dots;
if (!cur_de) cursor = 0;
else {
@@ -1307 +1216 @@ int main(int argc, char ** argv) {
config.longmode = true;
config.longinline = true;
}
- ui_status_info("");
break;
case '/': {
const char * input = ui_readline("/");
@@ -1397 +1296 @@ int main(int argc, char ** argv) {
int i = dir_search_regex(&dir, input, &idx);
if (i >= 0) {
cursor = idx;
- ui_status_info("");
} else switch (-i) {
case REGSEARCH_BAD_REGEX:
ui_status_error("Bad RegEx");
@@ -1547 +1436 @@ int main(int argc, char ** argv) {
break;
}
case 'd': {
- ui_status_info("");
ui_refresh();
bool del = ui_prompt_deletion(cur_de);
if (!del) {
@@ -1806 +16822 @@ int main(int argc, char ** argv) {
else if (cursor > dirlen - 1) cursor = dirlen - 1;
break;
}
+ case 'm': {
+ if (!cur_de) break;
+ int ret = dirent_togglemark(cur_de);
+ if (ret < 0) switch (-ret) {
+ case TOGGLEMARK_PARENT_MARKED:
+ ui_status_error("Ancestor Already Marked");
+ break;
+ case TOGGLEMARK_DIRENT_UNKNOWN:
+ ui_status_error("Cannot Mark Unknown Dirent");
+ break;
+ default:
+ ui_status_error("Mark Failed");
+ break;
+ }
+ break;
+ }
case 'q':
goto finished;
case KEY_RESIZE:
@@ -1934 +1975 @@ finished:
free(home);
dir_free(&dir);
ui_deinit();
+ dir_deinit();
}
diff --git a/src/ui.c b/src/ui.c
index 261d245..798f871 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -2207 +2208 @@ void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, const dir_t
int color = COLOR_PAIR(ui_dirent_color(de));
if (selected) color |= A_REVERSE;
- waddch(filewin, ' ');
+ if (de->marked) waddch(filewin, '+');
+ else waddch(filewin, ' ');
wattron(filewin, color);
waddstr(filewin, de->name);
wattroff(filewin, color);
@@ -35316 +35416 @@ void ui_print_dir(const dir_t * dir, size_t cursor) {
size_t first;
size_t last;
size_t len = dir_len(dir);
- cvector(dirent_t) entries = dir_entries(dir);
ui_get_first_last(len, cursor, &first, &last);
for (size_t i = first; i < last; i++) {
size_t pos = i - first;
- ui_print_dirent(&entries[i], pos, cursor == i, dir);
+ dirent_t * de = dir_get_entry(dir, i);
+ ui_print_dirent(de, pos, cursor == i, dir);
}
}
-void ui_print_cursor(size_t cursor, size_t total) {
+void ui_print_cursor(size_t cursor, size_t total, size_t total_marked) {
size_t lines, cols;
getmaxyx(filewin, lines, cols);
if (config.longmode && !config.longinline) {
@@ -3786 +37910 @@ void ui_print_cursor(size_t cursor, size_t total) {
} else {
waddstr(filewin, "0/0");
}
+
+ if (total_marked > 0) {
+ wprintw(filewin, " | %zu Marked", total_marked);
+ }
}
static void ui_get_first_last(size_t dir_len, size_t cursor, size_t * first, size_t * last) {