sfm

Simple file manager
git clone git://git.christosmarg.xyz/sfm.git
Log | Files | Refs | README | LICENSE

commit 0bc5f73af3cf28d63e7db44a587b257d6f7431c8
parent 1eb814ddddbc101ea7deffd2cd184209fd3af4fe
Author: Christos Margiolis <christos@margiolis.net>
Date:   Thu, 29 Oct 2020 14:46:01 +0200

added suckless-style config, simplified code, improved navigation, removed main switch

Diffstat:
MMakefile | 4+++-
Aconfig.h | 27+++++++++++++++++++++++++++
Msfm.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
3 files changed, 187 insertions(+), 55 deletions(-)

diff --git a/Makefile b/Makefile @@ -20,11 +20,13 @@ options: @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" +${OBJ}: config.h config.mk + ${BIN}: ${OBJ} ${CC} ${LDFLAGS} ${OBJ} -o $@ .${EXT}.o: - ${CC} ${CFLAGS} -c $< + ${CC} -c ${CFLAGS} $< dist: clean ${MKDIR} ${DIST} diff --git a/config.h b/config.h @@ -0,0 +1,27 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define SHCMD(cmd) { .f = (const char*[]){"/usr/bin/sh", "-c", cmd, NULL} } + +static unsigned int maxcols = 3; + +static Key keys[] = { + /* mod key func args */ + { 0, KEY_LEFT, nav, {.n = NAV_LEFT} }, + { 0, 'h', nav, {.n = NAV_LEFT} }, + { 0, KEY_RIGHT, nav, {.n = NAV_RIGHT} }, + { 0, 'l', nav, {.n = NAV_RIGHT} }, + { 0, '\n', nav, {.n = NAV_RIGHT} }, + { 0, KEY_UP, nav, {.n = NAV_UP} }, + { 0, KEY_DOWN, nav, {.n = NAV_DOWN} }, + { 'g', 'g', nav, {.n = NAV_TOP} }, + { 0, 'G', nav, {.n = NAV_BOTTOM} }, + { 'g', 'h', cd, {.d = "/home/christos"} }, + { 'g', 'u', cd, {.d = "/usr"} }, + { 'd', 'D', spawn, SHCMD("rm -f %s") }, + { 0, ':', promptget, {.f = NULL} }, + { 0, 'q', quit, {.f = NULL} }, +}; + + +#endif /* CONFIG_H */ diff --git a/sfm.c b/sfm.c @@ -1,7 +1,6 @@ #include <sys/types.h> #include <sys/wait.h> -#include <ctype.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> @@ -18,28 +17,67 @@ #define PATH_MAX 4096 #endif /* PATH_MAX */ -#define ISDIGIT(x) ((unsigned int)(x) - '0' <= 9) -#define SEL_CORRECT() (sel = ((sel < 0) ? 0 : (sel > nfiles - 1) ? nfiles - 1 : sel)) +#define YMAX (getmaxy(stdscr)) +#define XMAX (getmaxx(stdscr)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) +#define ISDIGIT(x) ((unsigned int)(x) - '0' <= 9) +#define SEL_CORRECT (sel = ((sel < 0) ? 0 : (sel > nfiles - 1) ? nfiles - 1 : sel)) -/* structs and enums */ +/* structs, unions and enums */ typedef struct { + // may use just dirent char *name; + char abspath[PATH_MAX]; off_t size; + int nchld; unsigned short nmlen; unsigned char type; } Entry; +typedef union { + int n; + const char *d; + const void *f; +} Arg; + +typedef struct { + int mod; + int key; + void (*func)(const Arg *arg); + const Arg arg; +} Key; + +enum NavFlags { + NAV_LEFT, + NAV_RIGHT, + NAV_UP, + NAV_DOWN, + NAV_TOP, + NAV_BOTTOM +}; + +// add sort flags (by size, name etc) + /* globals */ -static unsigned int maxcols = 3; static unsigned long nfiles = 0; static int sel = 0; +static Entry *entrs = NULL; /* function declarations */ static void cursesinit(void); +static int entriescount(const char *); static Entry *entriesget(const char *); static void pathdraw(const char *); static void dirdraw(const Entry *); -static void spawn(); +static void promptget(const Arg *); +static void nav(const Arg *); +static void cd(const Arg *); +static void spawn(const Arg *); +static void quit(const Arg *); + +#include "config.h" /* function implementations */ void @@ -53,24 +91,46 @@ cursesinit(void) scrollok(stdscr, 1); } +int +entriescount(const char *path) +{ + DIR *dir; + struct dirent *dp; + int nentrs = 0; + + if ((dir = opendir(path)) == NULL) + return -1; // this might cause bugs + // get hidden files flag + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, path) && strcmp(dp->d_name, ".") + && strcmp(dp->d_name, "..")) + nentrs++; + } + closedir(dir); + return nentrs; +} + Entry * -entriesget(const char *dirname) +entriesget(const char *path) { DIR *dir; struct dirent *dp; Entry *entrs; - if ((entrs = malloc(50 * sizeof(Entry))) == NULL) // fix this + if ((entrs = malloc(entriescount(path) * sizeof(Entry))) == NULL) return NULL; - if ((dir = opendir(dirname)) == NULL) + if ((dir = opendir(path)) == NULL) return NULL; nfiles = 0; // get hidden files flag while ((dp = readdir(dir)) != NULL) { - if (strcmp(dp->d_name, dirname) && strcmp(dp->d_name, ".") + if (strcmp(dp->d_name, path) && strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { entrs[nfiles].name = dp->d_name; + entrs[nfiles].nmlen = strlen(dp->d_name); entrs[nfiles].type = dp->d_type; + sprintf(entrs[nfiles].abspath, "%s/%s", path, dp->d_name); + entrs[nfiles].nchld = entriescount(entrs[nfiles].abspath); nfiles++; } } @@ -84,36 +144,106 @@ pathdraw(const char *path) attron(A_BOLD); mvprintw(0, 0, "%s\n", path); attroff(A_BOLD); + /*mvhline(1, 0, ACS_HLINE, XMAX);*/ } void dirdraw(const Entry *entrs) { - // crashes on include, bin and some other dirs int i = 0; - SEL_CORRECT(); + SEL_CORRECT; for (; i < nfiles; i++) { if (i == sel) attron(A_REVERSE); mvprintw(i + 1, 0, "%s\n", entrs[i].name); + // align numbers + if (entrs[i].type == DT_DIR) + mvprintw(i + 1, 50, "%12.ld\n", entrs[i].nchld); attroff(A_REVERSE); } } +// handle user given shell cmds void -spawn(const char *s) +spawn(const Arg *arg) { - /*pid_t pid;*/ - /*char *args[] = {"xdg-open", s, NULL};*/ + // huh? + char buf[BUFSIZ]; + + snprintf(buf, BUFSIZ, ((char **)arg->f)[2], entrs[sel].name); + printw("Confirm action %s (y/n): ", ((char **)arg->f)[0]); + if (getch() == 'y') { + /*execvp(*((char **)arg->f), (char **)arg->f);*/ + printw(" done"); + } + getch(); +} + +void +promptget(const Arg *arg) +{ + char buf[BUFSIZ]; + + move(getmaxy(stdscr) - 1, 0); + echo(); + curs_set(1); + printw(":"); + getnstr(buf, BUFSIZ); + noecho(); + curs_set(0); +} + +void +nav(const Arg *arg) +{ + switch (arg->n) { + case NAV_LEFT: + chdir(".."); + break; + case NAV_RIGHT: + if (entrs[sel].name != NULL) { + // handle symlinks + if (entrs[sel].type == DT_DIR) + chdir(entrs[sel].name); + /*else if (entrs[sel].type == DT_REG)*/ + /*spawn();*/ + } + break; + case NAV_UP: + sel--; + break; + case NAV_DOWN: + sel++; + break; + case NAV_TOP: + sel = 0; + break; + case NAV_BOTTOM: + sel = nfiles - 1; + break; + } +} + +void +cd(const Arg *arg) +{ + chdir(arg->d); +} + +void +quit(const Arg *arg) +{ + endwin(); + exit(0); } int main(int argc, char *argv[]) { - Entry *entrs; + /*Entry *entrs;*/ char cwd[PATH_MAX], *curdir; - int c; + int c, i; if ((curdir = getcwd(cwd, sizeof(cwd))) == NULL) { return -1; @@ -129,48 +259,21 @@ main(int argc, char *argv[]) pathdraw(curdir); dirdraw(entrs); - switch (c = getch()) { - case 'h': /* FALLTHROUGH */ - case KEY_LEFT: - chdir(".."); - break; - case 'l': /* FALLTHROUGH */ - case '\n': - case KEY_RIGHT: - if (entrs[sel].name != NULL) - // handle symlinks - if (entrs[sel].type == DT_DIR) - chdir(entrs[sel].name); - else if (entrs[sel].type == DT_REG) - spawn(entrs[sel].name); - break; - case 'k': /* FALLTHROUGH */ - case KEY_UP: - sel--; - break; - case 'j': /* FALLTHROUGH */ - case KEY_DOWN: - sel++; - break; - case 'g': - c = getch(); - if (c == 'g') - sel = 0; - break; - case 'G': - sel = nfiles - 1; - break; - case 'q': - // a goto! - goto exit; + c = getch(); + for (i = 0; i < ARRLEN(keys); i++) { + // handle same key combinations + // and same key but without mod + if (c == keys[i].mod) { + mvaddch(YMAX - 1, 0, c); + c = getch(); + } + if (c == keys[i].key) + keys[i].func(&(keys[i].arg)); } refresh(); free(entrs); } -exit: - endwin(); - return 0; }