sfm

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

commit 53cea22c0ddeb3efd3ad864c36b41610e92b25da
parent 57778a1845e21c3066fd32df03df5a2a46676c67
Author: Christos Margiolis <christos@margiolis.net>
Date:   Fri, 18 Dec 2020 02:46:28 +0200

implementing scrolling

Diffstat:
Mconfig.h | 104++++++++++++++++++++++++-------------------------------------------------------
Mconfig.mk | 5++---
Msfm.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
3 files changed, 121 insertions(+), 105 deletions(-)

diff --git a/config.h b/config.h @@ -5,82 +5,40 @@ #define CD(dir) {.s = (dir)} #define SHCMD(cmd) {.s = (cmd)} -//static int dircolor = CYAN; -//static int filecolor = GREEN; -//static int linkcolor = YELLOW; +static int colors[] = { + COLOR_CYAN, /* directories */ + COLOR_GREEN, /* regular files */ + COLOR_YELLOW, /* symlinks */ +}; -/* - * when mod is set to 0, it means there's no mod - * - * key can be any ascii character or ncurses key macro - * - * available functions: - * nav: handles navigation - * available flags: - * 1. NAV_LEFT - * 2. NAV_RIGHT - * 3. NAV_UP - * 4. NAV_DOWN - * 5. NAV_TOP - * 6. NAV_BOTTOM - * 7. NAV_SELECT - * 8. NAV_SHOWALL - * 9. NAV_INFO - * 10. NAV_REDRAW - * 11. NAV_EXIT - * - * cd: go to specified directory - * - * run: run a predefined shell command. the selected items - * will act as input to the command - * - * builtinrun: runs a specific command that is already built inside - * inside the file manager. The command is run on the - * currently selected entry. - * available commands: - * 1. RUN_PAGER - * 2. RUN_EDITOR - * 3. RUN_OPENWITH - * 4. RUN_RENAME - * - * prompt: execute any shell command on the fly - * - * selectitem: select item - * - * args: - * n: used for arguments that need a numeric value - * s: used for cd - * v: used for commands, NULL when no command is needed - */ static Key keys[] = { - /* mod key func arg */ - { 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, 'k', nav, {.n = NAV_UP} }, - { 0, KEY_DOWN, nav, {.n = NAV_DOWN} }, - { 0, 'j', nav, {.n = NAV_DOWN} }, - { 0, 'g', nav, {.n = NAV_TOP} }, - { 0, 'G', nav, {.n = NAV_BOTTOM} }, - { 0, ' ', nav, {.n = NAV_SELECT} }, - { 0, '.', nav, {.n = NAV_SHOWALL} }, - { 0, 'i', nav, {.n = NAV_INFO} }, - { 0, CTRL('r'), nav, {.n = NAV_REDRAW} }, - { 0, 'q', nav, {.n = NAV_EXIT} }, - { 0, '~', cd, CD("/home/christos") }, - { 0, CTRL('n'), cd, CD("/mnt/christos_ntfs/christos") }, + /* key func arg */ + { KEY_LEFT, nav, {.n = NAV_LEFT} }, + { 'h', nav, {.n = NAV_LEFT} }, + { KEY_RIGHT, nav, {.n = NAV_RIGHT} }, + { 'l', nav, {.n = NAV_RIGHT} }, + { '\n', nav, {.n = NAV_RIGHT} }, + { KEY_UP, nav, {.n = NAV_UP} }, + { 'k', nav, {.n = NAV_UP} }, + { KEY_DOWN, nav, {.n = NAV_DOWN} }, + { 'j', nav, {.n = NAV_DOWN} }, + { 'g', nav, {.n = NAV_TOP} }, + { 'G', nav, {.n = NAV_BOTTOM} }, + { ' ', nav, {.n = NAV_SELECT} }, + { '.', nav, {.n = NAV_SHOWALL} }, + { 'i', nav, {.n = NAV_INFO} }, + { CTRL('r'), nav, {.n = NAV_REDRAW} }, + { 'q', nav, {.n = NAV_EXIT} }, + { '~', cd, CD("/home/christos") }, + { CTRL('n'), cd, CD("/mnt/christos_ntfs/christos") }, /* TODO: get rid of builtinrun */ - { 0, 'p', builtinrun, {.n = RUN_PAGER} }, - { 0, 'e', builtinrun, {.n = RUN_EDITOR} }, - { 0, 'o', builtinrun, {.n = RUN_OPENWITH} }, - { 0, 'r', builtinrun, {.n = RUN_RENAME} }, - { 0, 'x', run, SHCMD("rm -rf") }, - { 0, 'w', run, SHCMD("mpv") }, - { 0, 's', sort, {.v = NULL} }, - { 0, ':', prompt, {.v = NULL} }, + { 'p', builtinrun, {.n = RUN_PAGER} }, + { 'e', builtinrun, {.n = RUN_EDITOR} }, + { 'o', builtinrun, {.n = RUN_OPENWITH} }, + { 'r', builtinrun, {.n = RUN_RENAME} }, + { 'x', run, SHCMD("rm -rf") }, + { 's', sort, {.v = NULL} }, + { ':', prompt, {.v = NULL} }, }; #endif /* CONFIG_H */ diff --git a/config.mk b/config.mk @@ -13,9 +13,8 @@ LIBS = -Llib -lncurses # add ncursesw # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \ - -D_XOPEN_SOURCE=600 -DVERSION=\"${VERSION}\" -CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations \ - -O3 ${INCS} ${CPPFLAGS} + -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} # utils diff --git a/sfm.c b/sfm.c @@ -42,6 +42,7 @@ #endif /* DEL */ #define DELAY_MS 350000 +#define SCROLLOFF 4 #define CTRL(x) ((x) & 0x1f) #define YMAX (getmaxy(stdscr)) @@ -52,10 +53,6 @@ #define ISDIGIT(x) ((unsigned int)(x) - '0' <= 9) #define ENTSORT(e, n) (qsort((e), (n), sizeof(*(e)), sortfn)) -#define SEL_CORRECT \ - win->sel = ((win->sel < 0) ? 0 : (win->sel > win->nents - 1) \ - ? win->nents - 1 : win->sel); - /* type definitions */ typedef unsigned char uchar; typedef unsigned short ushort; @@ -91,7 +88,6 @@ typedef union { } Arg; typedef struct { - int mod; int key; void (*func)(const Arg *arg); const Arg arg; @@ -142,7 +138,7 @@ enum { static void cursesinit(void); static ulong entcount(char *); static Entry *entget(char *, ulong); -static void entprint(void); +static void entprint(int); static void notify(int, const char *); static char *promptstr(const char *); static int confirmact(const char *); @@ -153,6 +149,7 @@ static void run(const Arg *); static void builtinrun(const Arg *); static void sort(const Arg *); static void prompt(const Arg *); +static void selcorrect(void); static void entcleanup(Entry *); static void escape(char *, const char *); static void xdelay(useconds_t); @@ -186,6 +183,7 @@ static const char *msgs[] = { /* globals variables */ static Win *win = NULL; /* main display */ static char *curdir = NULL; /* current directory */ +static int curscroll = 0; /* cursor scroll */ /* flags */ static uchar f_showall = 0; /* show hidden files */ @@ -229,6 +227,8 @@ revsizecmp(const void *x, const void *y) static void cursesinit(void) { + int i = 0; + if (!initscr()) die("sfm: initscr failed"); @@ -236,18 +236,13 @@ cursesinit(void) cbreak(); curs_set(0); keypad(stdscr, 1); + /*timeout(1000);*/ + /*set_escdelay(25);*/ start_color(); - /* define constants for each color */ - init_pair(1, COLOR_CYAN, COLOR_BLACK); - init_pair(2, COLOR_GREEN, COLOR_BLACK); - init_pair(3, COLOR_YELLOW, COLOR_BLACK); - - /* TODO: move away from here */ - win = emalloc(sizeof(Win)); - win->ents = NULL; - win->sel = win->nsel = win->nents = 0; + for (; i < ARRLEN(colors); i++) + init_pair(i + 1, colors[i], COLOR_BLACK); } static ulong @@ -314,7 +309,12 @@ entget(char *path, ulong n) break; case DT_LNK: ents[i].attrs |= COLOR_PAIR(3); + if (S_ISDIR(ents[i].stat.st_mode)) { + ents[i].flags |= DT_DIR; + ents[i].attrs |= A_BOLD; + } break; + /* TODO: handle more modes from stat(3) */ } sprintf(ents[i].statstr, "%c%c%c%c%c%c%c%c%c%c %ldB %s", @@ -330,8 +330,9 @@ entget(char *path, ulong n) ents[i].stat.st_mode & S_IXOTH ? 'x' : '-', ents[i].stat.st_size, ents[i].atime); - /* remove newline from statstr */ + /* remove newlines */ ents[i].statstr[strlen(ents[i].statstr) - 1] = '\0'; + ents[i].atime[strlen(ents[i].atime) - 1] = '\0'; i++; @@ -342,11 +343,13 @@ entget(char *path, ulong n) return ents; } -/*TODO: handle f_info*/ +/*TODO: add offset for scrolling */ static void -entprint(void) +entprint(int off) { + Entry *ent; int i = 0; + char ind; attron(A_BOLD); addstr(curdir); @@ -354,15 +357,55 @@ entprint(void) mvhline(1, 0, ACS_HLINE, XMAX); for (; i < win->nents && i < YMAX; i++) { + ent = &win->ents[i + off]; + move(i + 2, 0); - addch(win->ents[i].selected ? '+' : ' '); + addch(ent->selected ? '+' : ' '); if (i == win->sel) attron(A_REVERSE); - attron(win->ents[i].attrs); - addstr(win->ents[i].name); - attroff(A_REVERSE | win->ents[i].attrs); + if (f_info) + printw("%s %c%c%c %10ldB ", + ent->atime, + '0' + ((ent->stat.st_mode >> 6) & 7), + '0' + ((ent->stat.st_mode >> 3) & 7), + '0' + (ent->stat.st_mode & 7), + ent->stat.st_size); + + attron(ent->attrs); + addstr(ent->name); + attroff(A_REVERSE | ent->attrs); + + switch (ent->stat.st_mode & S_IFMT) { + /* FIXME: for some reason regular files fall here too */ + case S_IFDIR: + ind = '/'; + break; + case S_IFREG: + if (ent->stat.st_mode & 0100) + ind = '*'; + break; + case S_IFLNK: + ind = (ent->flags & DT_DIR) ? '/' : '@'; + break; + case S_IFSOCK: + ind = '='; + break; + case S_IFIFO: + ind = '|'; + break; + case S_IFBLK: + ind = '%'; + break; + case S_IFCHR: + ind = '#'; + break; + default: + ind = '?'; + } + + addch(ind); } mvprintw(YMAX - 1, 0, "%ld/%ld %s", win->sel + 1, win->nents, @@ -481,6 +524,7 @@ nav(const Arg *arg) if (ent->flags & DT_REG) { sprintf(buf, "%s %s", cmds[CMD_OPEN], win->ents[win->sel].name); + /* TODO: escape this buf! */ if (!spawn(buf)) notify(MSG_FAIL, NULL); } @@ -584,7 +628,7 @@ builtinrun(const Arg *arg) return; } - f_noconfirm = 1; + f_noconfirm = 0; run(&prog); f_redraw = 1; } @@ -623,6 +667,15 @@ prompt(const Arg *arg) f_redraw = 1; } } +static void +selcorrect(void) +{ + if (win->sel < 0) + win->sel = 0; + else if (win->sel > win->nents - 1) + win->sel = win->nents - 1; + +} static void entcleanup(Entry *ent) @@ -719,14 +772,15 @@ sfmrun(void) refresh(); } - SEL_CORRECT; - entprint(); + selcorrect(); + curscroll = win->sel > YMAX - 4 ? SCROLLOFF : 0; + entprint(curscroll); /*TODO: signal/timeout */ - c = getch(); - for (i = 0; i < ARRLEN(keys); i++) - if (c == keys[i].key) - keys[i].func(&(keys[i].arg)); + if ((c = getch()) != ERR) + for (i = 0; i < ARRLEN(keys); i++) + if (c == keys[i].key) + keys[i].func(&(keys[i].arg)); } } @@ -746,6 +800,11 @@ main(int argc, char *argv[]) sortfn = namecmp; cursesinit(); + + win = emalloc(sizeof(Win)); + win->ents = NULL; + win->sel = win->nsel = win->nents = 0; + sfmrun(); cleanup();