sfm

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

commit eaf954058a70cfd162a35e473ab3653df8369462
parent 742c94a216c17e59b45f7ff7b247f59cfd4298b5
Author: Christos Margiolis <christos@margiolis.net>
Date:   Fri, 30 Apr 2021 16:13:24 +0300

fixed scrolling (I think), removed , fixed minor bugs

Diffstat:
MMakefile | 10+++++-----
Mconfig.h | 20++++++++++----------
Msfm.c | 257+++++++++++++++++++++++++++++++------------------------------------------------
3 files changed, 117 insertions(+), 170 deletions(-)

diff --git a/Makefile b/Makefile @@ -15,9 +15,9 @@ all: options ${BIN} options: @echo ${BIN} build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" ${OBJ}: config.h config.mk @@ -38,7 +38,7 @@ run: ./${BIN} install: all - #${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR} + ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR} ${MKDIR} ${DESTDIR}${BIN_DIR} ${CP} ${BIN} ${BIN_DIR} #${CP} ${MAN1} ${DESTDIR}${MAN_DIR} @@ -48,7 +48,7 @@ install: all uninstall: ${RM} ${DESTDIR}${BIN_DIR}/${BIN} - #${RM} ${DESTDIR}${MAN_DIR}/${MAN1} + ${RM} ${DESTDIR}${MAN_DIR}/${MAN1} clean: ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz diff --git a/config.h b/config.h @@ -1,10 +1,12 @@ #ifndef CONFIG_H #define CONFIG_H -#define DELAY_MS 350000 +#define DELAY_MS 350 #define SCROLLOFF 4 -static uchar npanes = 4; /* Clumps to 1 if < 1 and to 9 if > 9 */ +/* Clumps to 1 if < 1 and to 9 if > 9 */ +static uint npanes = 4; + static char *datefmt = "%F"; /* c1 e2 27 2e 00 60 33 f7 c6 d6 ab c4 */ @@ -38,22 +40,20 @@ static struct key keys[] = { { 'k', scrollpane, {.i = -1} }, { KEY_DOWN, scrollpane, {.i = +1} }, { 'j', scrollpane, {.i = +1} }, - //{ 'g', scrollpane, {.i = 0} }, - //{ 'G', scrollpane, {.i = INT_MAX} }, + { 'g', scrollpane, {.i = 0} }, + //{ 'G', scrollpane, {.i = S_BOTTOM} }, { '\t', cyclepane, {0} }, { ' ', select, {.i = 0} }, - { 'a', select, {.i = 1} }, + { CTRL('a'), select, {.i = 1} }, { '.', setflag, {.i = F_SHOWALL} }, { 'i', setflag, {.i = F_INFO} }, { CTRL('r'), setflag, {.i = F_REDRAW} }, { 'q', setflag, {.i = F_QUIT} }, { '~', cd, {.s = "/home/christos"} }, { CTRL('b'), cd, {.s = "/storage"} }, - /* TODO: get rid of builtinrun */ - { 'p', builtinrun, {.i = RUN_PAGER} }, - { 'e', builtinrun, {.i = RUN_EDITOR} }, - { 'o', builtinrun, {.i = RUN_OPENWITH} }, - { 'r', builtinrun, {.i = RUN_RENAME} }, + { CTRL('l'), cd, {.s = "/root/.local"} }, + { 'p', run, {.s = "PAGER"} }, + { 'e', run, {.s = "EDITOR"} }, { 'x', run, {.s = "rm -rf"} }, { 's', sort, {0} }, { ':', prompt, {0} }, diff --git a/sfm.c b/sfm.c @@ -56,22 +56,17 @@ #define ISDIGIT(x) ((unsigned int)(x) - '0' <= 9) #define ENTSORT(e, n, f) (qsort((e), (n), sizeof(*(e)), f)) -/* type definitions */ typedef unsigned char uchar; -typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; -typedef long long ll; -typedef unsigned long long ull; -/* structs, unions and enums */ struct entry { struct stat stat; - char statstr[36]; /* XXX no? */ + char statstr[36]; char date[12]; - char sizestr[12]; /* XXX: too hardcoded? */ + char sizestr[12]; char *name; - ushort nlen; + uint nlen; uchar flags; uchar selected; }; @@ -79,13 +74,13 @@ struct entry { struct pane { struct entry *ents; char *curdir; - long nsel; /* XXX: shouldn't be long? */ - ulong nents; + uint nsel; + uint nents; int sel; int (*sortfn)(const void *, const void *); }; -typedef union { +typedef union arg { int i; const char *s; const void *v; @@ -104,30 +99,11 @@ enum { F_QUIT = 1 << 4, }; -/* XXX: do I need these? */ -enum { - RUN_EDITOR, - RUN_PAGER, - RUN_OPENWITH, - RUN_RENAME, -}; - -/* XXX: do I need these? */ enum { CMD_OPEN, - CMD_MV, -}; - -/* XXX: do I need these? */ -enum { - ENV_SHELL, - ENV_EDITOR, - ENV_PAGER, }; enum { - MSG_OPENWITH, - MSG_RENAME, MSG_EXEC, MSG_SORT, MSG_PROMPT, @@ -151,19 +127,18 @@ enum { C_INF, /* Information */ }; -/* function declarations */ static void initcurses(void); static struct entry *entget(const char *); static void entprint(struct pane *); static int spawn(char *); -static void notify(int, const char *); +static void notify(const char *, ...); static int confirmact(const char *); static int appendbuf(char *, size_t *, const char *); static char *escape(const char *); static char *fmtsize(size_t); -static char *promptstr(const char *); +static char *promptread(const char *); static void selclump(struct pane *); -static void xdelay(useconds_t); +static void xdelay(ulong); static void chlevel(const arg *); static void scrollpane(const arg *); static void cyclepane(const arg *); @@ -172,7 +147,6 @@ static void _select(struct pane *, int); static void select(const arg *); static void cd(const arg *); static void run(const arg *); -static void builtinrun(const arg *); static void sort(const arg *); static void prompt(const arg *); static void echdir(const char *); @@ -183,28 +157,17 @@ static void entfree(struct pane *); static void cleanup(void); static void die(const char *, ...); -/* useful strings */ static const char *cmds[] = { [CMD_OPEN] = "xdg-open", - [CMD_MV] = "mv", -}; - -static const char *envs[] = { - [ENV_SHELL] = "SHELL", - [ENV_EDITOR] = "EDITOR", - [ENV_PAGER] = "PAGER", }; static const char *msgs[] = { - [MSG_OPENWITH] = "open with: ", - [MSG_RENAME] = "rename: ", [MSG_EXEC] = "%s (y/N)?", [MSG_SORT] = "'n'ame 's'ize 'd'ate 'r'everse", [MSG_PROMPT] = ":", [MSG_FAIL] = "action failed", }; -/* globals variables */ static char *argv0; /* program name */ static struct pane *pane; /* display */ static int selpane = 0; /* current pane */ @@ -224,7 +187,6 @@ static uchar f_running = 1; /* 0 when sfm should exit */ #include "config.h" -/* function implementations */ static inline int namecmp(const void *x, const void *y) { @@ -275,13 +237,14 @@ initcurses(void) curs_set(0); keypad(stdscr, 1); /*timeout(1000);*/ - /*set_escdelay(25);*/ + set_escdelay(0); if (has_colors() && can_change_color()) { start_color(); use_default_colors(); for (i = 1; i < ARRLEN(colors); i++) init_pair(i, colors[i], COLOR_BLACK); } + memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; @@ -320,10 +283,11 @@ entget(const char *path) stat(e->name, &e->stat); tm = localtime(&e->stat.st_ctime); strftime(e->date, sizeof(e->date), datefmt, tm); - (void)strcpy(e->sizestr, fmtsize(e->stat.st_size)); + (void)strncpy(e->sizestr, fmtsize(e->stat.st_size), + sizeof(e->sizestr)); e->flags = 0; - /* FIXME: resets on every redraw, keep track somehow */ + /* XXX: resets on every redraw, keep track somehow */ e->selected = 0; e->flags |= dent->d_type; @@ -356,8 +320,8 @@ entget(const char *path) break; } - /* XXX: seperate field for lsperms? */ - sprintf(e->statstr, "%c%c%c%c%c%c%c%c%c%c %s %s", + snprintf(e->statstr, sizeof(e->statstr), + "%c%c%c%c%c%c%c%c%c%c %s %s", type, e->stat.st_mode & S_IRUSR ? 'r' : '-', e->stat.st_mode & S_IWUSR ? 'w' : '-', @@ -368,7 +332,8 @@ entget(const char *path) e->stat.st_mode & S_IROTH ? 'r' : '-', e->stat.st_mode & S_IWOTH ? 'w' : '-', e->stat.st_mode & S_IXOTH ? 'x' : '-', - e->sizestr, e->date); + e->sizestr, + e->date); i++; @@ -485,7 +450,7 @@ entprint(struct pane *p) static int spawn(char *cmd) { - char *args[] = {getenv(envs[ENV_SHELL]), "-c", cmd, NULL}; + char *args[] = {getenv("SHELL"), "-c", cmd, NULL}; struct sigaction oldsighup; struct sigaction oldsigtstp; pid_t pid; @@ -509,25 +474,18 @@ spawn(char *cmd) return 0; } -/* TODO: get rid of the `switch`, use vfprintf */ static void -notify(int flag, const char *str) +notify(const char *fmt, ...) { + va_list args; + char buf[XMAX]; /* Line shouldn't get longer than `XMAX`. */ + move(YMAX - 1, 0); clrtoeol(); - - /* FIXME: let user continue */ - switch (flag) { - case MSG_EXEC: - printw(msgs[MSG_EXEC], str); - break; - case MSG_FAIL: /* FALLTHROUGH */ - case MSG_SORT: /* FALLTHROUGH */ - addstr(msgs[flag]); - break; - default: - addstr(str); - } + va_start(args, fmt); + vsnprintf(buf, XMAX, fmt, args); + va_end(args); + addstr(buf); } static int @@ -535,7 +493,7 @@ confirmact(const char *str) { if (f_noconfirm) return 1; - notify(MSG_EXEC, str); + notify(msgs[MSG_EXEC], str); return (getch() == 'y'); } @@ -543,12 +501,15 @@ static int appendbuf(char *buf, size_t *sz, const char *str) { char *esc; + size_t l; - /* TODO: error checks */ esc = escape(str); - *sz += strlen(esc) + 2; /* +2 for the spaces. */ - buf = erealloc(buf, *sz + 1); - sprintf(buf + strlen(buf), " %s ", esc); + l = strlen(esc) + 2; /* +2 for the spaces in snprintf. */ + *sz += l; + if (snprintf(buf + strlen(buf), l, " %s ", esc) != l) { + free(esc); + return 0; + } free(esc); return 1; @@ -587,19 +548,19 @@ fmtsize(size_t sz) for (; sz > 1024; i++) sz >>= 10; + /* XXX: s/sprintf/snprintf/ */ sprintf(buf, "%ld%c", sz, "BKMGTPEZY"[i]); return buf; } -/* TODO: fix backspace and change name */ static char * -promptstr(const char *msg) +promptread(const char *msg) { char buf[BUFSIZ], *str; int len = 0, c; - notify(-1, msg); + notify(msg); echo(); curs_set(1); @@ -611,18 +572,17 @@ promptstr(const char *msg) if (len > 0) len--; break; - /* FIXME: why is this slow? */ case ESC: - return NULL; + str = NULL; + goto exit; default: buf[len++] = c; } } buf[len] = '\0'; - str = emalloc(len + 1); - (void)strcpy(str, buf); - + str = estrdup(buf); +exit: curs_set(0); noecho(); @@ -639,11 +599,12 @@ selclump(struct pane *p) cur = p->sel; } +/* TODO: use nanosleep(2) */ static void -xdelay(useconds_t delay) +xdelay(ulong delay) { refresh(); - usleep(delay); + usleep(delay * 1000); } /* TODO: add sel "history" */ @@ -663,31 +624,37 @@ chlevel(const arg *arg) /* TODO: handle links to dirs */ if (p->ents[p->sel].flags & DT_REG && p->ents[p->sel].flags & ~DT_LNK) { + /* XXX: s/sprintf/snprintf/ */ sprintf(buf, "%s %s", cmds[CMD_OPEN], p->ents[p->sel].name); /* TODO: escape this buf! */ - if (!spawn(buf)) - notify(MSG_FAIL, NULL); + if (!spawn(buf)) { + notify(msgs[MSG_FAIL]); + xdelay(DELAY_MS << 2); + } } } f_redraw = 1; } -/* TODO: handle top, bottom, and half scrolls */ +/* TODO: handle bottom and half scrolls */ static void scrollpane(const arg *arg) { struct pane *p; + p = &pane[selpane]; if (arg->i < 0) p->sel--; else if (arg->i > 0) p->sel++; + else + p->sel = 0; selclump(p); - /* FIXME: hell. */ if (p->sel > YMAX - 4 - SCROLLOFF) { cur = YMAX - 4 - SCROLLOFF; - curscroll += arg->i; + if (cur + curscroll + arg->i < p->nents) + curscroll += arg->i; } else { cur = p->sel; curscroll = 0; @@ -734,7 +701,7 @@ select(const arg *arg) p = &pane[selpane]; if (arg->i == 0) - _select(p, p->sel++); + _select(p, p->sel); else for (i = 0; i < p->nents; i++) _select(p, i); @@ -747,67 +714,43 @@ cd(const arg *arg) f_redraw = 1; } -/* FIXME: broken. doesn't create args list */ +/* FIXME: only runs on sels */ static void run(const arg *arg) { struct pane *p; - char *buf; + char buf[BUFSIZ], *prog; size_t sz; int i; + /* + * Always check if the argument is an environmental variable so we + * don't have to implement seperate functions to handle just + * that. If the argument isn't an environmental variable, getenv(3) + * will return NULL and we'll use the actual argument as a program. + */ + if ((prog = getenv(arg->s)) == NULL) + prog = (char *)arg->s; /* Remove `const`ness. */ p = &pane[selpane]; - buf = estrdup(arg->s); - sz = strlen(buf); + sz = strlen(prog); + strncpy(buf, prog, ++sz); if (p->nsel > 0) { for (i = 0; i < p->nents; i++) if (p->ents[i].selected) - appendbuf(buf, &sz, p->ents[i].name); + if (!appendbuf(buf, &sz, p->ents[i].name)) + return; } else - appendbuf(buf, &sz, p->ents[p->sel].name); - buf[sz] = '\0'; + if (!appendbuf(buf, &sz, p->ents[p->sel].name)) + return; + buf[--sz] = '\0'; f_noconfirm = 0; if (confirmact(buf)) { - //spawn(buf); + spawn(buf); f_redraw = 1; p->nsel = 0; } - free(buf); -} - -/* XXX: argg? */ -static void -builtinrun(const arg *argg) -{ - arg prog; - - switch (argg->i) { - case RUN_EDITOR: - prog.s = getenv(envs[ENV_EDITOR]); - break; - case RUN_PAGER: - prog.s = getenv(envs[ENV_PAGER]); - break; - case RUN_OPENWITH: - if ((prog.s = promptstr(msgs[MSG_OPENWITH])) == NULL) - return; - break; - case RUN_RENAME: - /* FIXME */ - /*sprintf(prog.s, "%s %s %s", cmds[CMD_MV], tmp,*/ - /*promptstr(msgs[MSG_RENAME]));*/ - return; - break; - default: - return; - } - - f_noconfirm = 0; - run(&prog); - f_redraw = 1; - /* TODO: free prog.s! */ } static void @@ -816,7 +759,7 @@ sort(const arg *arg) struct pane *p; p = &pane[selpane]; - notify(MSG_SORT, NULL); + notify(msgs[MSG_SORT]); switch (getch()) { case 'n': @@ -857,8 +800,11 @@ static void prompt(const arg *arg) { char *buf; + char *str; - buf = estrdup(promptstr(msgs[MSG_PROMPT])); + if ((str = promptread(msgs[MSG_PROMPT])) == NULL) + return; + buf = estrdup(str); if (confirmact(buf)) { spawn(buf); f_redraw = 1; @@ -866,11 +812,12 @@ prompt(const arg *arg) free(buf); } +/* FIXME: always have to wait until xdelay returns */ static void echdir(const char *path) { if (chdir(path) == -1) { - notify(MSG_FAIL, NULL); + notify(msgs[MSG_FAIL]); xdelay(DELAY_MS << 2); } } @@ -912,6 +859,7 @@ entfree(struct pane *p) free(p->ents[i].name); free(p->ents); p->nents = 0; + p->nsel = 0; } static void @@ -930,8 +878,7 @@ die(const char *fmt, ...) { va_list args; - fprintf(stderr, "%s: ", argv0); - + (void)fprintf(stderr, "%s: ", argv0); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); @@ -954,27 +901,11 @@ main(int argc, char *argv[]) int ch, i; argv0 = *argv; - if (!setlocale(LC_ALL, "")) die("setlocale:"); if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) die("isatty:"); - if (npanes < 1) - npanes = 1; - else if (npanes > 9) - npanes = 9; - - f_redraw = 1; - f_namesort = 1; - pane = emalloc(npanes * sizeof(struct pane)); - for (i = 0; i < npanes; i++) { - pane[i].ents = NULL; - pane[i].curdir = NULL; - pane[i].sel = pane[i].nsel = pane[i].nents = 0; - pane[i].sortfn = namecmp; - } - while ((ch = getopt(argc, argv, "Hi")) != -1) { switch (ch) { case 'H': @@ -985,13 +916,27 @@ main(int argc, char *argv[]) break; case '?': /* FALLTHROUGH */ default: - fprintf(stderr, "usage: %s [-Hi]\n", argv0); + (void)fprintf(stderr, "usage: %s [-Hi]\n", argv0); exit(EXIT_FAILURE); } } argc -= optind; argv += optind; + if (npanes < 1) + npanes = 1; + else if (npanes > 9) + npanes = 9; + + f_redraw = 1; + f_namesort = 1; + pane = emalloc(npanes * sizeof(struct pane)); + for (i = 0; i < npanes; i++) { + pane[i].ents = NULL; + pane[i].curdir = NULL; + pane[i].sel = pane[i].nsel = pane[i].nents = 0; + pane[i].sortfn = namecmp; + } /* TODO: add [dir] */ initcurses(); @@ -999,11 +944,12 @@ main(int argc, char *argv[]) p = &pane[selpane]; erase(); if (f_redraw) { + /* FIXME: getcwd(3) is not suitable anymore... */ if ((p->curdir = getcwd(cwd, sizeof(cwd))) == NULL) die("getcwd:"); entfree(p); if ((p->ents = entget(p->curdir)) == NULL) - die("entget:"); + die("entget: %s: ", p->curdir); selclump(p); f_redraw = 0; refresh(); @@ -1020,6 +966,7 @@ main(int argc, char *argv[]) f_redraw = 1; } } + refresh(); } cleanup();