sfm

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

commit 742c94a216c17e59b45f7ff7b247f59cfd4298b5
parent ec495021bfce39149831e61a4075da363d23edfd
Author: Christos Margiolis <christos@margiolis.net>
Date:   Wed, 21 Apr 2021 01:38:58 +0300

added  panes, scrolling needs fix (again...)

Diffstat:
Mconfig.h | 18++++++++++--------
Msfm.c | 506+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
2 files changed, 296 insertions(+), 228 deletions(-)

diff --git a/config.h b/config.h @@ -4,6 +4,7 @@ #define DELAY_MS 350000 #define SCROLLOFF 4 +static uchar npanes = 4; /* Clumps to 1 if < 1 and to 9 if > 9 */ static char *datefmt = "%F"; /* c1 e2 27 2e 00 60 33 f7 c6 d6 ab c4 */ @@ -37,14 +38,15 @@ static struct key keys[] = { { 'k', scrollpane, {.i = -1} }, { KEY_DOWN, scrollpane, {.i = +1} }, { 'j', scrollpane, {.i = +1} }, - //{ 'g', scrollpane, {.i = TODO} }, - //{ 'G', scrollpane, {.i = TODO} }, + //{ 'g', scrollpane, {.i = 0} }, + //{ 'G', scrollpane, {.i = INT_MAX} }, + { '\t', cyclepane, {0} }, { ' ', select, {.i = 0} }, { 'a', select, {.i = 1} }, - { '.', setflag, {.i = 1 << 1} }, - { 'i', setflag, {.i = 1 << 2} }, - { CTRL('r'), setflag, {.i = 1 << 3} }, - { 'q', setflag, {.i = 1 << 4} }, + { '.', 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 */ @@ -53,8 +55,8 @@ static struct key keys[] = { { 'o', builtinrun, {.i = RUN_OPENWITH} }, { 'r', builtinrun, {.i = RUN_RENAME} }, { 'x', run, {.s = "rm -rf"} }, - { 's', sort, {.v = NULL} }, - { ':', prompt, {.v = NULL} }, + { 's', sort, {0} }, + { ':', prompt, {0} }, }; #endif /* CONFIG_H */ diff --git a/sfm.c b/sfm.c @@ -78,9 +78,10 @@ struct entry { struct pane { struct entry *ents; + char *curdir; + long nsel; /* XXX: shouldn't be long? */ ulong nents; - long sel; - long nsel; + int sel; int (*sortfn)(const void *, const void *); }; @@ -96,12 +97,14 @@ struct key { const arg arg; }; - enum { - DIR_OR_DIRLNK = 1 << 0, - HARD_LNK = 1 << 1, + F_SHOWALL = 1 << 1, + F_INFO = 1 << 2, + F_REDRAW = 1 << 3, + F_QUIT = 1 << 4, }; +/* XXX: do I need these? */ enum { RUN_EDITOR, RUN_PAGER, @@ -109,11 +112,13 @@ enum { RUN_RENAME, }; +/* XXX: do I need these? */ enum { CMD_OPEN, CMD_MV, }; +/* XXX: do I need these? */ enum { ENV_SHELL, ENV_EDITOR, @@ -150,13 +155,18 @@ enum { static void initcurses(void); static struct entry *entget(const char *); static void entprint(struct pane *); -static char *fmtsize(size_t); +static int spawn(char *); static void notify(int, const char *); -static char *promptstr(const char *); static int confirmact(const char *); -static int spawn(char *); -static void scrollpane(const arg *); +static int appendbuf(char *, size_t *, const char *); +static char *escape(const char *); +static char *fmtsize(size_t); +static char *promptstr(const char *); +static void selclump(struct pane *); +static void xdelay(useconds_t); static void chlevel(const arg *); +static void scrollpane(const arg *); +static void cyclepane(const arg *); static void setflag(const arg *); static void _select(struct pane *, int); static void select(const arg *); @@ -165,15 +175,11 @@ static void run(const arg *); static void builtinrun(const arg *); static void sort(const arg *); static void prompt(const arg *); -static void selcorrect(struct pane *); -static void entfree(void); -static int appendbuf(char *, size_t *, const char *); -static char *escape(const char *); -static void xdelay(useconds_t); static void echdir(const char *); static void *emalloc(size_t); static void *erealloc(void *, size_t); static char *estrdup(const char *); +static void entfree(struct pane *); static void cleanup(void); static void die(const char *, ...); @@ -192,7 +198,7 @@ static const char *envs[] = { static const char *msgs[] = { [MSG_OPENWITH] = "open with: ", [MSG_RENAME] = "rename: ", - [MSG_EXEC] = "execute '%s' (y/N)?", + [MSG_EXEC] = "%s (y/N)?", [MSG_SORT] = "'n'ame 's'ize 'd'ate 'r'everse", [MSG_PROMPT] = ":", [MSG_FAIL] = "action failed", @@ -201,10 +207,9 @@ static const char *msgs[] = { /* globals variables */ static char *argv0; /* program name */ static struct pane *pane; /* display */ -static char *curdir; /* current directory */ +static int selpane = 0; /* current pane */ static int cur = 0; /* cursor position */ static int curscroll = 0; /* cursor scroll */ -static int scrolldir; /* scroll direction */ /* flags */ static uchar f_showall = 0; /* show hidden files */ @@ -288,7 +293,7 @@ initcurses(void) static struct entry * entget(const char *path) { - struct entry *ents; + struct entry *ents, *e; struct dirent *dent; struct tm *tm; DIR *dir; @@ -308,22 +313,23 @@ entget(const char *path) /* TODO: handle filtering */ ents = erealloc(ents, (i + 1) * sizeof(struct entry)); - ents[i].nlen = strlen(dent->d_name); - ents[i].name = estrdup(dent->d_name); + e = &ents[i]; - stat(ents[i].name, &ents[i].stat); - tm = localtime(&ents[i].stat.st_ctime); - strftime(ents[i].date, sizeof(ents[i].date), datefmt, tm); - (void)strcpy(ents[i].sizestr, fmtsize(ents[i].stat.st_size)); + e->nlen = strlen(dent->d_name); + e->name = estrdup(dent->d_name); + 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)); - ents[i].flags = 0; + e->flags = 0; /* FIXME: resets on every redraw, keep track somehow */ - ents[i].selected = 0; - ents[i].flags |= dent->d_type; + e->selected = 0; + e->flags |= dent->d_type; /* TODO: use fstatat(3) */ /* FIXME: links don't work */ - switch (ents[i].stat.st_mode & S_IFMT) { + switch (e->stat.st_mode & S_IFMT) { case S_IFREG: type = '-'; break; @@ -351,47 +357,58 @@ entget(const char *path) } /* XXX: seperate field for lsperms? */ - sprintf(ents[i].statstr, "%c%c%c%c%c%c%c%c%c%c %s %s", + sprintf(e->statstr, "%c%c%c%c%c%c%c%c%c%c %s %s", type, - ents[i].stat.st_mode & S_IRUSR ? 'r' : '-', - ents[i].stat.st_mode & S_IWUSR ? 'w' : '-', - ents[i].stat.st_mode & S_IXUSR ? 'x' : '-', - ents[i].stat.st_mode & S_IRGRP ? 'r' : '-', - ents[i].stat.st_mode & S_IWGRP ? 'w' : '-', - ents[i].stat.st_mode & S_IXGRP ? 'x' : '-', - ents[i].stat.st_mode & S_IROTH ? 'r' : '-', - ents[i].stat.st_mode & S_IWOTH ? 'w' : '-', - ents[i].stat.st_mode & S_IXOTH ? 'x' : '-', - ents[i].sizestr, ents[i].date); + e->stat.st_mode & S_IRUSR ? 'r' : '-', + e->stat.st_mode & S_IWUSR ? 'w' : '-', + e->stat.st_mode & S_IXUSR ? 'x' : '-', + e->stat.st_mode & S_IRGRP ? 'r' : '-', + e->stat.st_mode & S_IWGRP ? 'w' : '-', + e->stat.st_mode & S_IXGRP ? 'x' : '-', + 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); i++; } (void)closedir(dir); - pane->nents = i; - ENTSORT(ents, pane->nents, pane->sortfn); + pane[selpane].nents = i; + ENTSORT(ents, pane[selpane].nents, pane[selpane].sortfn); return ents; } static void -entprint(struct pane *pane) +entprint(struct pane *p) { - struct entry *ent; + struct entry *e; int n, i; uint attrs; uchar color; char ind; + addch('['); + for (i = 0; i < npanes; i++) { + if (i == selpane) + attron(A_BOLD | A_REVERSE); + addch(i + '0' + 1); + attroff(A_BOLD | A_REVERSE); + if (i != npanes - 1) + addch(' '); + } + addstr("] "); + attron(A_BOLD | COLOR_PAIR(C_DIR)); - addstr(curdir); + addstr(p->curdir); attroff(A_BOLD | COLOR_PAIR(C_DIR)); - if ((n = MIN(YMAX - 4, pane->nents)) == 0) + if ((n = MIN(YMAX - 4, p->nents - curscroll)) <= 0) return; for (i = 0; i < n; i++) { - ent = &pane->ents[i + curscroll]; + e = &p->ents[i + curscroll]; ind = ' '; attrs = 0; color = 0; @@ -404,17 +421,17 @@ entprint(struct pane *pane) if (f_info) { attron(COLOR_PAIR(C_INF)); printw("%s %c%c%c %7s ", - ent->date, - '0' + ((ent->stat.st_mode >> 6) & 7), - '0' + ((ent->stat.st_mode >> 3) & 7), - '0' + (ent->stat.st_mode & 7), - ent->sizestr); + e->date, + '0' + ((e->stat.st_mode >> 6) & 7), + '0' + ((e->stat.st_mode >> 3) & 7), + '0' + (e->stat.st_mode & 7), + e->sizestr); attroff(COLOR_PAIR(C_INF)); } - addch(ent->selected ? '+' : ' '); + addch(e->selected ? '+' : ' '); - switch (ent->stat.st_mode & S_IFMT) { + switch (e->stat.st_mode & S_IFMT) { case S_IFDIR: ind = '/'; color = C_DIR; @@ -422,15 +439,15 @@ entprint(struct pane *pane) break; case S_IFREG: color = C_FIL; - if (ent->stat.st_mode & 0100) { + if (e->stat.st_mode & 0100) { ind = '*'; color = C_EXE; } break; case S_IFLNK: - ind = (ent->flags & DT_DIR) ? '/' : '@'; + ind = (e->flags & DT_DIR) ? '/' : '@'; color = C_LNK; - if (S_ISDIR(ent->stat.st_mode)) + if (S_ISDIR(e->stat.st_mode)) attrs |= A_BOLD; break; case S_IFSOCK: @@ -455,27 +472,41 @@ entprint(struct pane *pane) attrs |= COLOR_PAIR(color); attron(attrs); - addstr(ent->name); + addstr(e->name); attroff(attrs); addch(ind); } - mvprintw(YMAX - 1, 0, "%ld/%ld %s", pane->sel + 1, pane->nents, - pane->ents[pane->sel].statstr); + mvprintw(YMAX - 1, 0, "%ld/%ld %s", p->sel + 1, p->nents, + p->ents[p->sel].statstr); } -static char * -fmtsize(size_t sz) +static int +spawn(char *cmd) { - static char buf[12]; - int i = 0; - - for (; sz > 1024; i++) - sz >>= 10; - sprintf(buf, "%ld%c", sz, "BKMGTPEZY"[i]); + char *args[] = {getenv(envs[ENV_SHELL]), "-c", cmd, NULL}; + struct sigaction oldsighup; + struct sigaction oldsigtstp; + pid_t pid; + int status; - return buf; + switch (pid = fork()) { + case -1: + return 1; + case 0: + execvp(*args, args); + _exit(EXIT_SUCCESS); + break; + default: + endwin(); + while (wait(&status) != pid) + ; + sigaction(SIGHUP, &oldsighup, NULL); + sigaction(SIGTSTP, &oldsigtstp, NULL); + break; + } + return 0; } /* TODO: get rid of the `switch`, use vfprintf */ @@ -499,6 +530,68 @@ notify(int flag, const char *str) } } +static int +confirmact(const char *str) +{ + if (f_noconfirm) + return 1; + notify(MSG_EXEC, str); + return (getch() == 'y'); +} + +static int +appendbuf(char *buf, size_t *sz, const char *str) +{ + char *esc; + + /* 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); + free(esc); + + return 1; +} + +static char * +escape(const char *str) +{ + char *buf; + size_t sz; + int i = 0; + + sz = strlen(str); + buf = emalloc(sz + 1); + + for (; *str; str++) { + switch (*str) { + case ' ': /* FALLTHROUGH */ + case '\'': /* FALLTHROUGH */ + case '(': /* FALLTHROUGH */ + case ')': /* FALLTHROUGH */ + buf = erealloc(buf, ++sz + 1); + buf[i++] = '\\'; + } + buf[i++] = *str; + } + buf[i] = '\0'; + return buf; +} + +static char * +fmtsize(size_t sz) +{ + static char buf[12]; + int i = 0; + + for (; sz > 1024; i++) + sz >>= 10; + sprintf(buf, "%ld%c", sz, "BKMGTPEZY"[i]); + + return buf; +} + /* TODO: fix backspace and change name */ static char * promptstr(const char *msg) @@ -536,66 +629,42 @@ promptstr(const char *msg) return str; } -static int -confirmact(const char *str) -{ - if (f_noconfirm) - return 1; - notify(MSG_EXEC, str); - return (getch() == 'y'); -} - -static int -spawn(char *cmd) +static void +selclump(struct pane *p) { - char *args[] = {getenv(envs[ENV_SHELL]), "-c", cmd, NULL}; - struct sigaction oldsighup; - struct sigaction oldsigtstp; - pid_t pid; - int status; - - switch (pid = fork()) { - case -1: - return 1; - case 0: - execvp(*args, args); - _exit(EXIT_SUCCESS); - break; - default: - endwin(); - while (wait(&status) != pid) - ; - sigaction(SIGHUP, &oldsighup, NULL); - sigaction(SIGTSTP, &oldsigtstp, NULL); - break; - } - return 0; + if (p->sel < 0) + p->sel = 0; + else if (p->sel > p->nents - 1) + p->sel = p->nents - 1; + cur = p->sel; } -/* TODO: handle top, bottom, and half scrolls */ static void -scrollpane(const arg *arg) +xdelay(useconds_t delay) { - pane->sel += arg->i; - scrolldir += arg->i; + refresh(); + usleep(delay); } +/* TODO: add sel "history" */ static void chlevel(const arg *arg) { + struct pane *p; char buf[BUFSIZ]; + p = &pane[selpane]; if (arg->i < 0) { echdir(".."); } else if (arg->i > 0) { - if (pane->ents[pane->sel].flags & DT_DIR) - echdir(pane->ents[pane->sel].name); + if (p->ents[p->sel].flags & DT_DIR) + echdir(p->ents[p->sel].name); /* TODO: handle links to dirs */ - if (pane->ents[pane->sel].flags & DT_REG - && pane->ents[pane->sel].flags & ~DT_LNK) { + if (p->ents[p->sel].flags & DT_REG + && p->ents[p->sel].flags & ~DT_LNK) { sprintf(buf, "%s %s", cmds[CMD_OPEN], - pane->ents[pane->sel].name); + p->ents[p->sel].name); /* TODO: escape this buf! */ if (!spawn(buf)) notify(MSG_FAIL, NULL); @@ -604,40 +673,71 @@ chlevel(const arg *arg) f_redraw = 1; } +/* TODO: handle top, 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++; + selclump(p); + /* FIXME: hell. */ + if (p->sel > YMAX - 4 - SCROLLOFF) { + cur = YMAX - 4 - SCROLLOFF; + curscroll += arg->i; + } else { + cur = p->sel; + curscroll = 0; + } +} + +static void +cyclepane(const arg *arg) +{ + selpane++; + selpane %= npanes; + f_redraw = 1; +} + static void setflag(const arg *arg) { - if (arg->i & (1 << 1)) + if (arg->i & F_SHOWALL) f_showall ^= 1; - else if (arg->i & (1 << 2)) + if (arg->i & F_INFO) f_info ^= 1; - else if (arg->i & (1 << 3)) - f_redraw = 1; /* XXX: redundant */ - else if (arg->i & (1 << 4)) + if (arg->i & F_REDRAW) + ; + if (arg->i & F_QUIT) f_running = 0; f_redraw = 1; } static void -_select(struct pane *pane, int i) +_select(struct pane *p, int i) { - pane->ents[i].selected ^= 1; - if (pane->ents[i].selected) - pane->nsel++; + p->ents[i].selected ^= 1; + if (p->ents[i].selected) + p->nsel++; else - pane->nsel--; + p->nsel--; } static void select(const arg *arg) { + struct pane *p; int i; + p = &pane[selpane]; if (arg->i == 0) - _select(pane, pane->sel++); + _select(p, p->sel++); else - for (i = 0; i < pane->nents; i++) - _select(pane, i); + for (i = 0; i < p->nents; i++) + _select(p, i); } static void @@ -651,30 +751,33 @@ cd(const arg *arg) static void run(const arg *arg) { + struct pane *p; char *buf; size_t sz; int i; + p = &pane[selpane]; buf = estrdup(arg->s); sz = strlen(buf); - if (pane->nsel > 0) { - for (i = 0; i < pane->nents; i++) - if (pane->ents[i].selected) - appendbuf(buf, &sz, pane->ents[i].name); + if (p->nsel > 0) { + for (i = 0; i < p->nents; i++) + if (p->ents[i].selected) + appendbuf(buf, &sz, p->ents[i].name); } else - appendbuf(buf, &sz, pane->ents[pane->sel].name); + appendbuf(buf, &sz, p->ents[p->sel].name); buf[sz] = '\0'; f_noconfirm = 0; if (confirmact(buf)) { //spawn(buf); f_redraw = 1; - pane->nsel = 0; + p->nsel = 0; } free(buf); } +/* XXX: argg? */ static void builtinrun(const arg *argg) { @@ -710,6 +813,9 @@ builtinrun(const arg *argg) static void sort(const arg *arg) { + struct pane *p; + + p = &pane[selpane]; notify(MSG_SORT, NULL); switch (getch()) { @@ -731,103 +837,33 @@ sort(const arg *arg) if (!f_revsort) { if (f_namesort) - pane->sortfn = namecmp; + p->sortfn = namecmp; else if (f_sizesort) - pane->sortfn = sizecmp; + p->sortfn = sizecmp; else if (f_datesort) - pane->sortfn = datecmp; + p->sortfn = datecmp; } else { if (f_namesort) - pane->sortfn = revnamecmp; + p->sortfn = revnamecmp; else if (f_sizesort) - pane->sortfn = revsizecmp; + p->sortfn = revsizecmp; else if (f_datesort) - pane->sortfn = revdatecmp; + p->sortfn = revdatecmp; } - - ENTSORT(pane->ents, pane->nents, pane->sortfn); + ENTSORT(p->ents, p->nents, p->sortfn); } static void prompt(const arg *arg) { - char buf[BUFSIZ]; + char *buf; - sprintf(buf, "%s", promptstr(msgs[MSG_PROMPT])); + buf = estrdup(promptstr(msgs[MSG_PROMPT])); if (confirmact(buf)) { spawn(buf); f_redraw = 1; } -} - -/* FIXME: change name and actually implement scrolling */ -static void -selcorrect(struct pane *pane) -{ - if (pane->sel < 0) - pane->sel = 0; - else if (pane->sel > pane->nents - 1) - pane->sel = pane->nents - 1; - - cur = pane->sel; - curscroll = 0; -} - -static void -entfree(void) -{ - int i= 0; - - for (; i < pane->nents; i++) - free(pane->ents[i].name); - free(pane->ents); - pane->nents = 0; -} - -static int -appendbuf(char *buf, size_t *sz, const char *str) -{ - char *esc; - - esc = escape(str); - *sz += strlen(esc) + 2; /* +2 for the spaces. */ - buf = erealloc(buf, *sz + 1); - sprintf(buf + strlen(buf), " %s ", esc); - free(esc); - - return 1; -} - -static char * -escape(const char *str) -{ - char *buf; - size_t sz; - int i = 0; - - sz = strlen(str); - buf = emalloc(sz + 1); - - for (; *str; str++) { - switch (*str) { - case ' ': /* FALLTHROUGH */ - case '\'': /* FALLTHROUGH */ - case '(': /* FALLTHROUGH */ - case ')': /* FALLTHROUGH */ - buf = erealloc(buf, ++sz + 1); - buf[i++] = '\\'; - } - buf[i++] = *str; - } - buf[i] = '\0'; - return buf; -} - -static void -xdelay(useconds_t delay) -{ - refresh(); - usleep(delay); + free(buf); } static void @@ -868,9 +904,23 @@ estrdup(const char *s) } static void +entfree(struct pane *p) +{ + int i= 0; + + for (; i < p->nents; i++) + free(p->ents[i].name); + free(p->ents); + p->nents = 0; +} + +static void cleanup(void) { - entfree(); + int i = 0; + + for (i = 0; i < npanes; i++) + entfree(&pane[i]); free(pane); endwin(); } @@ -899,6 +949,7 @@ die(const char *fmt, ...) int main(int argc, char *argv[]) { + struct pane *p; char cwd[PATH_MAX] = {0}; int ch, i; @@ -909,12 +960,20 @@ main(int argc, char *argv[]) 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(sizeof(struct pane)); - pane->ents = NULL; - pane->sel = pane->nsel = pane->nents = 0; - pane->sortfn = namecmp; + 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) { @@ -937,23 +996,30 @@ main(int argc, char *argv[]) initcurses(); while (f_running) { + p = &pane[selpane]; erase(); if (f_redraw) { - if ((curdir = getcwd(cwd, sizeof(cwd))) == NULL) + if ((p->curdir = getcwd(cwd, sizeof(cwd))) == NULL) die("getcwd:"); - entfree(); - if ((pane->ents = entget(curdir)) == NULL) + entfree(p); + if ((p->ents = entget(p->curdir)) == NULL) die("entget:"); + selclump(p); f_redraw = 0; refresh(); } - selcorrect(pane); - entprint(pane); + entprint(p); /*TODO: signal/timeout, handle SIGTERM to clean up or warn first */ - if ((ch = getch()) != ERR) + if ((ch = getch()) != ERR) { for (i = 0; i < ARRLEN(keys); i++) if (ch == keys[i].key) keys[i].func(&(keys[i].arg)); + ch -= '0'; + if (ch > 0 && ch <= npanes) { + selpane = ch - 1; + f_redraw = 1; + } + } } cleanup();