commit 7b1661244d58353d4b6d5a3d27450f5757845f06
Author: rani <clagv.randomgames@gmail.com>
Date: Wed Dec 31 20:34:20 2025 +0000
diff --git a/Makefile b/Makefile
index 37cbb01..b7d8a73 100644
--- a/Makefile
+++ b/Makefile
@@ -15 +15 @@
BIN = cscroll
-SRC = src/dir.c src/ui.c src/main.c
+SRC = src/config.c src/dir.c src/ui.c src/main.c
OBJ = ${SRC:.c=.o}
CC ?= cc
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..8c9102a
--- /dev/null
+++ b/include/config.h
@@ -00 +135 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdbool.h>
+
+enum dir_sort_dirs {
+ DIR_SORT_DIRS_FIRST,
+ DIR_SORT_DIRS_LAST,
+ DIR_SORT_DIRS_UNSORTED,
+};
+
+enum dir_sortby {
+ DIR_SORTBY_NAME,
+ DIR_SORTBY_TIME,
+ DIR_SORTBY_SIZE,
+};
+
+enum dir_sort {
+ DIR_SORT_INCREASING,
+ DIR_SORT_DECREASING,
+ DIR_SORT_UNSORTED,
+};
+
+typedef struct {
+ bool longmode;
+ bool longinline;
+ bool dots;
+ enum dir_sort_dirs dir_sort_dirs;
+ enum dir_sortby dir_sortby;
+ enum dir_sort dir_sort;
+} config_t;
+
+extern config_t config;
+
+#endif /* CONFIG_H */
diff --git a/include/dir.h b/include/dir.h
index 9551cb0..3d46d07 100644
--- a/include/dir.h
+++ b/include/dir.h
@@ -526 +528 @@ typedef struct {
typedef struct {
size_t len;
cvector(dirent_t) entries;
+ size_t nodots_len;
+ cvector(dirent_t) nodots;
size_t longest_uname;
size_t longest_gname;
@@ -666 +689 @@ int dir_cd(const char * cwd, const char * next);
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);
+cvector(dirent_t) dir_entries(const dir_t * dir);
+void dir_sort(const dir_t * dir);
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/ui.h b/include/ui.h
index a2e07c7..01d3665 100644
--- a/include/ui.h
+++ b/include/ui.h
@@ -357 +357 @@ void ui_status_info(const char * status);
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, bool longmode);
+void ui_print_dir(const dir_t * dir, size_t cursor);
void ui_print_cursor(size_t cursor, size_t total);
void ui_resize(void);
const char * ui_readline(const char * prompt);
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..8353340
--- /dev/null
+++ b/src/config.c
@@ -00 +113 @@
+#include <stdbool.h>
+
+#include "config.h"
+
+config_t config = {
+ .longmode = true,
+ .longinline = true,
+ .dots = true,
+
+ .dir_sortby = DIR_SORTBY_NAME,
+ .dir_sort = DIR_SORT_INCREASING,
+ .dir_sort_dirs = DIR_SORT_DIRS_FIRST,
+};
diff --git a/src/dir.c b/src/dir.c
index a480701..1b7da66 100644
--- a/src/dir.c
+++ b/src/dir.c
@@ -15 +16 @@
#include <sys/stat.h>
#include <stdbool.h>
+#include <strings.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
@@ -116 +127 @@
#include <grp.h>
#include "dir.h"
+#include "config.h"
#include "cvector.h"
#define CWDSZ (PATH_MAX + 1)
@@ -426 +448 @@ int dir_list(const char * path, dir_t * dir) {
dir->len = 0;
dir->entries = NULL;
cvector_init(dir->entries, 1, free_dirent);
+ dir->nodots_len = 0;
+ dir->nodots = NULL;
dir->longest_uname = 0;
dir->longest_gname = 0;
dir->longest_size = 0;
@@ -626 +6611 @@ int dir_list(const char * path, dir_t * dir) {
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);
@@ -1466 +1557 @@ void dir_entry(int dirfd, const char * name, dirent_t * dirent) {
void dir_free(dir_t * dir) {
cvector_free(dir->entries);
+ cvector_free(dir->nodots);
}
char de_crepr(enum de_type de_type) {
@@ -2818 +29111 @@ const char * dir_basename(const char * path) {
}
int dir_search_name(const dir_t * dir, const char * name, size_t * idx) {
- for (size_t i = 0; i < dir->len; i++) {
- if (!strcmp(dir->entries[i].name, name)) {
+ 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)) {
*idx = i;
return 0;
}
@@ -29212 +30515 @@ 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 < dir->len; i++) {
- dirent_t * de = &dir->entries[i];
+ for (size_t i = 0; i < dirlen; i++) {
+ dirent_t * de = &entries[i];
ret = regexec(®ex, de->name, 0, NULL, 0);
if (!ret) {
*idx = i;
@@ -3093 +32548 @@ int dir_search_regex(const dir_t * dir, const char * regexstr, size_t * idx) {
regfree(®ex);
return -REGSEARCH_NOT_FOUND;
}
+
+size_t dir_len(const dir_t * dir) {
+ if (config.dots) return dir->len;
+ else return dir->nodots_len;
+}
+
+cvector(dirent_t) dir_entries(const dir_t * dir) {
+ if (config.dots) return dir->entries;
+ else return dir->nodots;
+}
+
+static int dir_sort_cmp(const void * inpa, const void * inpb) {
+ const dirent_t * a = (const dirent_t*)inpa;
+ const dirent_t * b = (const dirent_t*)inpb;
+
+ if (config.dir_sort_dirs != DIR_SORT_DIRS_UNSORTED
+ && ((a->type == DE_DIR) != (b->type == DE_DIR))) {
+ if (config.dir_sort_dirs == DIR_SORT_DIRS_FIRST)
+ return (a->type == DE_DIR) ? -1 : 1;
+ if (config.dir_sort_dirs == DIR_SORT_DIRS_LAST)
+ return (b->type == DE_DIR) ? -1 : 1;
+ }
+
+ if (config.dir_sort == DIR_SORT_DECREASING) {
+ const dirent_t * tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ switch (config.dir_sortby) {
+ case DIR_SORTBY_NAME:
+ return strcasecmp(a->name, b->name);
+ case DIR_SORTBY_TIME:
+ return (a->mtime < b->mtime) ? -1 : 1;
+ case DIR_SORTBY_SIZE:
+ return (a->size < b->size) ? -1 : 1;
+ default: return 0;
+ }
+}
+
+void dir_sort(const dir_t * dir) {
+ if (config.dir_sort == DIR_SORT_UNSORTED) return;
+ qsort(dir->entries, dir->len, sizeof(dir->entries[0]), dir_sort_cmp);
+ qsort(dir->nodots, dir->nodots_len, sizeof(dir->entries[0]), dir_sort_cmp);
+}
diff --git a/src/main.c b/src/main.c
index 58a169a..109b50f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -512 +515 @@
#include "ui.h"
#include "dir.h"
+#include "config.h"
+#include "cvector.h"
int main(int argc, char ** argv) {
const char * cwd = dir_get_cwd();
dir_t dir;
dir_list(cwd, &dir);
+ dir_sort(&dir);
ui_init();
size_t cursor = 0;
@@ -1822 +2126 @@ int main(int argc, char ** argv) {
for (;;) {
ui_erase();
+ size_t dirlen = dir_len(&dir);
+ cvector(dirent_t) entries = dir_entries(&dir);
+
ui_set_title(cwd);
- ui_print_dir(&dir, cursor, true);
- ui_print_cursor(cursor, dir.len);
+ ui_print_dir(&dir, cursor);
+ ui_print_cursor(cursor, dirlen);
ui_refresh();
dirent_t * cur_de;
- if (dir.len == 0) cur_de = NULL;
- else cur_de = &dir.entries[cursor];
+ if (dirlen == 0) cur_de = NULL;
+ else cur_de = &entries[cursor];
+
switch (getch()) {
case KEY_UP:
if (cursor > 0) cursor--;
ui_status_info("");
break;
case KEY_DOWN:
- if (cursor < dir.len - 1) cursor++;
+ if (cursor < dirlen - 1) cursor++;
ui_status_info("");
break;
case KEY_LEFT: {
@@ -4912 +5613 @@ 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 {
+ dir_sort(&dir);
+ if (basename) {
+ size_t idx;
+ int i = dir_search_name(&dir, basename, &idx);
+ if (i >= 0) cursor = idx;
+ }
ui_status_info("");
}
} else {
@@ -727 +808 @@ int main(int argc, char ** argv) {
cwd = dir_get_cwd();
dir_free(&dir);
dir_list(cwd, &dir);
- if (dir.len == 0) ui_status_info("Empty Directory");
+ dir_sort(&dir);
+ if (dirlen == 0) ui_status_info("Empty Directory");
} else {
ui_status_error(strerror(-ret));
}
@@ -879 +9627 @@ int main(int argc, char ** argv) {
break;
case KEY_END:
case 'G':
- cursor = dir.len - 1;
+ cursor = dirlen - 1;
ui_status_info("");
break;
+ case '.': {
+ config.dots = !config.dots;
+ size_t idx;
+ int ret = dir_search_name(&dir, cur_de->name, &idx);
+ if (ret < 0) cursor = 0;
+ else cursor = idx;
+ break;
+ }
+ case 'l':
+ // cycle through long mode options
+ if (config.longmode) {
+ if (config.longinline) config.longinline = false;
+ else config.longmode = false;
+ } else {
+ config.longmode = true;
+ config.longinline = true;
+ }
+ break;
case '/': {
const char * input = ui_readline("/");
if (!input) break;
diff --git a/src/ui.c b/src/ui.c
index 1042da9..ae49f45 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -86 +88 @@
#include "ui.h"
#include "dir.h"
+#include "config.h"
+#include "cvector.h"
#define TITLEWINSTARTY 0
#define TITLEWINLINES 1
@@ -357 +377 @@ static WINDOW * inputwin = NULL;
static void win_set(WINDOW * win, const char * str, int attrs);
static int ui_dirent_color(const dirent_t * de);
static int ui_link_color(const dirent_t * de);
-static void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, bool longmode, const dir_t * dir);
+static void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, const dir_t * dir);
static void ui_wlpadstr(WINDOW * win, const char * s, size_t len);
static void ui_get_first_last(size_t n_dirents, size_t cursor, size_t * first, size_t * last);
@@ -1207 +1227 @@ static void ui_wlpadstr(WINDOW * win, const char * s, size_t len) {
waddstr(win, s);
}
-void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, bool longmode, const dir_t * dir) {
+void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, const dir_t * dir) {
static const enum ui_color mode_colors[] = {
['r'] = RED, ['w'] = MAGENTA, ['x'] = COLOR_EXEC,
['s'] = YELLOW, ['S'] = YELLOW, ['t'] = RED,
@@ -1349 +13619 @@ void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, bool longmo
static char stime[128];
static char isbuf[40]; // should be big enough to hold any integer
- wmove(filewin, pos, 0);
+ size_t lines, cols;
+ getmaxyx(filewin, lines, cols);
+
+ size_t dirlen = dir_len(dir);
+
+ if (config.longmode && (config.longinline || (!config.longinline && selected))) {
+ if (config.longinline) wmove(filewin, pos, 0);
+ else {
+ // place the longmode info right on top of the "cursor" info
+ if (dirlen < lines - 3) wmove(filewin, dirlen + 1, 0);
+ else wmove(filewin, lines - 2, 0);
+ }
- if (longmode) {
const char * pmode = dirent_prettymode(de);
for (const char * p = pmode; *p; p++) {
int color = COLOR_PAIR(mode_colors[(unsigned)*p]);
@@ -1596 +1719 @@ void ui_print_dirent(const dirent_t * de, size_t pos, bool selected, bool longmo
wprintw(filewin, " %s ", stime);
}
+ if (!config.longmode || (config.longmode && !config.longinline))
+ wmove(filewin, pos, 0);
+
int color = COLOR_PAIR(ui_dirent_color(de));
if (selected) color |= A_REVERSE;
wattron(filewin, color);
@@ -21422 +22929 @@ static int ui_link_color(const dirent_t * de) {
}
}
-void ui_print_dir(const dir_t * dir, size_t cursor, bool longmode) {
+void ui_print_dir(const dir_t * dir, size_t cursor) {
size_t first;
size_t last;
- ui_get_first_last(dir->len, cursor, &first, &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(&dir->entries[i], pos, cursor == i, longmode, dir);
+ ui_print_dirent(&entries[i], pos, cursor == i, dir);
}
}
void ui_print_cursor(size_t cursor, size_t total) {
size_t lines, cols;
getmaxyx(filewin, lines, cols);
- if (total < lines - 2) wmove(filewin, total + 1, 0);
- else wmove(filewin, lines - 1, 0);
+ if (config.longmode && !config.longinline) {
+ if (total < lines - 3) wmove(filewin, total + 2, 0);
+ else wmove(filewin, lines - 1, 0);
+ } else {
+ if (total < lines - 2) wmove(filewin, total + 1, 0);
+ else wmove(filewin, lines - 1, 0);
+ }
if (total > 0) {
wprintw(filewin, "%zu/%zu", cursor + 1, total);
@@ -2426 +2647 @@ static void ui_get_first_last(size_t dir_len, size_t cursor, size_t * first, siz
size_t lines, cols;
getmaxyx(filewin, lines, cols);
lines -= 2;
+ if (config.longmode && !config.longinline) lines--;
// try to keep cursor in the middle of the window
if (dir_len < lines) {
*first = 0;