sfm

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

commit ec495021bfce39149831e61a4075da363d23edfd
parent 7b799ec3119c863e045e68e830f99155546f3ecd
Author: Christos Margiolis <christos@margiolis.net>
Date:   Tue, 20 Apr 2021 18:44:33 +0300

fixed some bugs, created others

Diffstat:
MLICENSE | 43++++++++++++++++---------------------------
MMakefile | 5++---
MREADME | 5++---
Mconfig.h | 89+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mconfig.mk | 8++++----
Dpatches/filepreview.c | 24------------------------
Msfm.c | 1464++++++++++++++++++++++++++++++++++++++++---------------------------------------
7 files changed, 807 insertions(+), 831 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,31 +1,20 @@ -BSD 3-Clause License +MIT License -Copyright (c) 2020-present, Christos Margiolis. -All rights reserved. +(c) 2020-Present Christos Margiolis <christos@christosmarg.xyz> -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY -THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT -NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -8,9 +8,8 @@ BIN = sfm DIST = ${BIN}-${VERSION} MAN1 = ${BIN}.1 -EXT = c SRC = sfm.c -OBJ = ${SRC:.${EXT}=.o} +OBJ = ${SRC:.c=.o} all: options ${BIN} @@ -25,7 +24,7 @@ ${OBJ}: config.h config.mk ${BIN}: ${OBJ} ${CC} ${LDFLAGS} ${OBJ} -o $@ -.${EXT}.o: +.c.o: ${CC} -c ${CFLAGS} $< dist: clean diff --git a/README b/README @@ -6,12 +6,11 @@ that follows the suckless style of configuration. Usage ----- - cd sfm - sudo make install clean + # make install clean sfm will be installed in /usr/local/bin by default. Configuration is done by editing config.h and recompiling the source code. Note ---- -This project is still early in development so you can't use sfm yet. +This project is still early in development so don't use it. diff --git a/config.h b/config.h @@ -1,53 +1,60 @@ #ifndef CONFIG_H #define CONFIG_H +#define DELAY_MS 350000 +#define SCROLLOFF 4 + +static char *datefmt = "%F"; + /* c1 e2 27 2e 00 60 33 f7 c6 d6 ab c4 */ /* https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg */ +/* TODO: Handle any number of colors */ static int colors[] = { - [C_BLK] = 0x00, /* TODO: Block device */ - [C_CHR] = 0xe2, /* Character device */ - [C_DIR] = 0x27, /* Directory */ - [C_EXE] = 0xd6, /* Executable file */ - [C_FIL] = 0xff, /* Regular file */ - [C_HRD] = 0x00, /* TODO: Hard link */ - [C_LNK] = 0x33, /* Symbolic link */ - [C_MIS] = 0x00, /* TODO: Missing file OR file details */ - [C_ORP] = 0x00, /* TODO: Orphaned symlink */ - [C_PIP] = 0x00, /* TODO: Named pipe (FIFO) */ - [C_SOC] = 0x2e, /* Socket */ - [C_UND] = 0x00, /* TODO: Unknown OR 0B regular/exe file */ - [C_INF] = 0xf7, /* Information */ + [C_BLK] = 0x00, /* TODO: Block device */ + [C_CHR] = 0xe2, /* Character device */ + [C_DIR] = 0x27, /* Directory */ + [C_EXE] = 0xd6, /* Executable file */ + [C_FIL] = 0xff, /* Regular file */ + [C_HRD] = 0x00, /* TODO: Hard link */ + [C_LNK] = 0x33, /* Symbolic link */ + [C_MIS] = 0x00, /* TODO: Missing file OR file details */ + [C_ORP] = 0x00, /* TODO: Orphaned symlink */ + [C_PIP] = 0x00, /* TODO: Named pipe (FIFO) */ + [C_SOC] = 0x2e, /* Socket */ + [C_UND] = 0x00, /* TODO: Unknown OR 0B regular/exe file */ + [C_INF] = 0xf7, /* Information */ }; -static Key keys[] = { - /* 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, {.s = "/home/christos"} }, - { CTRL('b'), cd, {.s = "/storage"} }, - /* TODO: get rid of builtinrun */ - { 'p', builtinrun, {.n = RUN_PAGER} }, - { 'e', builtinrun, {.n = RUN_EDITOR} }, - { 'o', builtinrun, {.n = RUN_OPENWITH} }, - { 'r', builtinrun, {.n = RUN_RENAME} }, - { 'x', run, {.s = "rm -rf"} }, - { 's', sort, {.v = NULL} }, - { ':', prompt, {.v = NULL} }, +static struct key keys[] = { + /* key func arg */ + { KEY_LEFT, chlevel, {.i = -1} }, + { 'h', chlevel, {.i = -1} }, + { KEY_RIGHT, chlevel, {.i = +1} }, + { 'l', chlevel, {.i = +1} }, + { '\n', chlevel, {.i = +1} }, + { KEY_UP, scrollpane, {.i = -1} }, + { 'k', scrollpane, {.i = -1} }, + { KEY_DOWN, scrollpane, {.i = +1} }, + { 'j', scrollpane, {.i = +1} }, + //{ 'g', scrollpane, {.i = TODO} }, + //{ 'G', scrollpane, {.i = TODO} }, + { ' ', 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} }, + { '~', 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} }, + { 'x', run, {.s = "rm -rf"} }, + { 's', sort, {.v = NULL} }, + { ':', prompt, {.v = NULL} }, }; #endif /* CONFIG_H */ diff --git a/config.mk b/config.mk @@ -4,16 +4,16 @@ VERSION = 0 # paths PREFIX = /usr/local -MAN_DIR = ${PREFIX}/man/man1 +MAN_DIR = ${PREFIX}/share/man/man1 BIN_DIR = ${PREFIX}/bin # includes and libs INCS = -Iinclude -LIBS = -Llib -lncursesw +LIBS = -Llib -lcursesw # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \ - -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" + -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} @@ -28,4 +28,4 @@ TAR = tar -cf GZIP = gzip # compiler -CC = gcc +CC = cc diff --git a/patches/filepreview.c b/patches/filepreview.c @@ -1,24 +0,0 @@ -static void -filepreview(void) -{ - WINDOW *fw; - Entry *ent = &win->ents[win->sel]; - FILE *fp; - char buf[BUFSIZ]; - size_t maxlen = XMAX >> 1; - int ln = 0; - - if (ent->flags & DT_REG) { - if ((fp = fopen(ent->name, "r")) == NULL) - return; - fw = newwin(YMAX - 2, maxlen, 2, maxlen); - while (fgets(buf, BUFSIZ, fp) && ln < YMAX) { - if (strlen(buf) > maxlen) - buf[maxlen] = '\0'; - mvwaddstr(fw, ln++, 0, buf); - } - fclose(fp); - wrefresh(fw); - f_redraw = 1; - } -} diff --git a/sfm.c b/sfm.c @@ -4,6 +4,7 @@ #include <sys/wait.h> #include <dirent.h> +#include <fcntl.h> #include <limits.h> #include <locale.h> #include <signal.h> @@ -13,7 +14,7 @@ #include <time.h> #include <unistd.h> -#include <ncurses.h> +#include <curses.h> #ifndef PATH_MAX #define PATH_MAX 1024 @@ -42,17 +43,18 @@ #define DEL 127 #endif /* DEL */ -#define DELAY_MS 350000 -#define SCROLLOFF 4 +#ifndef SIGWINCH +#define SIGWINCH 28 +#endif /* SIGWINCH */ -#define CTRL(x) ((x) & 0x1f) -#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 ENTSORT(e, n) (qsort((e), (n), sizeof(*(e)), sortfn)) +#define CTRL(x) ((x) & 0x1f) +#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 ENTSORT(e, n, f) (qsort((e), (n), sizeof(*(e)), f)) /* type definitions */ typedef unsigned char uchar; @@ -63,166 +65,157 @@ typedef long long ll; typedef unsigned long long ull; /* structs, unions and enums */ -typedef struct { - struct stat stat; - char statstr[36]; /* XXX no? */ - char date[12]; - char sizestr[12]; - char *name; - ushort nlen; - uchar flags; - uchar selected; -} Entry; - -typedef struct { - Entry *ents; - ulong nents; - long sel; - long nsel; -} Win; - -typedef union { - int n; - const char *s; - const void *v; -} Arg; - -typedef struct { - int key; - void (*func)(const Arg *arg); - const Arg arg; -} Key; +struct entry { + struct stat stat; + char statstr[36]; /* XXX no? */ + char date[12]; + char sizestr[12]; /* XXX: too hardcoded? */ + char *name; + ushort nlen; + uchar flags; + uchar selected; +}; +struct pane { + struct entry *ents; + ulong nents; + long sel; + long nsel; + int (*sortfn)(const void *, const void *); +}; -enum { - DIR_OR_DIRLNK = 1 << 0, - HARD_LNK = 1 << 1, +typedef union { + int i; + const char *s; + const void *v; +} arg; + +struct key { + int key; + void (*func)(const arg *); + const arg arg; }; + enum { - NAV_LEFT, - NAV_RIGHT, - NAV_UP, - NAV_DOWN, - NAV_TOP, - NAV_BOTTOM, - NAV_SELECT, - NAV_SHOWALL, - NAV_INFO, - NAV_REDRAW, - NAV_EXIT, + DIR_OR_DIRLNK = 1 << 0, + HARD_LNK = 1 << 1, }; enum { - RUN_EDITOR, - RUN_PAGER, - RUN_OPENWITH, - RUN_RENAME, + RUN_EDITOR, + RUN_PAGER, + RUN_OPENWITH, + RUN_RENAME, }; enum { - CMD_OPEN, - CMD_MV, + CMD_OPEN, + CMD_MV, }; enum { - ENV_SHELL, - ENV_EDITOR, - ENV_PAGER, + ENV_SHELL, + ENV_EDITOR, + ENV_PAGER, }; enum { - MSG_OPENWITH, - MSG_RENAME, - MSG_EXEC, - MSG_SORT, - MSG_PROMPT, - MSG_FAIL, + MSG_OPENWITH, + MSG_RENAME, + MSG_EXEC, + MSG_SORT, + MSG_PROMPT, + MSG_FAIL, }; /* Colors */ enum { - C_BLK = 1, /* Block device */ - C_CHR, /* Character device */ - C_DIR, /* Directory */ - C_EXE, /* Executable file */ - C_FIL, /* Regular file */ - C_HRD, /* Hard link */ - C_LNK, /* Symbolic link */ - C_MIS, /* Missing file OR file details */ - C_ORP, /* Orphaned symlink */ - C_PIP, /* Named pipe (FIFO) */ - C_SOC, /* Socket */ - C_UND, /* Unknown OR 0B regular/exe file */ - C_INF, /* Information */ + C_BLK = 1, /* Block device */ + C_CHR, /* Character device */ + C_DIR, /* Directory */ + C_EXE, /* Executable file */ + C_FIL, /* Regular file */ + C_HRD, /* Hard link */ + C_LNK, /* Symbolic link */ + C_MIS, /* Missing file OR file details */ + C_ORP, /* Orphaned symlink */ + C_PIP, /* Named pipe (FIFO) */ + C_SOC, /* Socket */ + C_UND, /* Unknown OR 0B regular/exe file */ + C_INF, /* Information */ }; /* function declarations */ -static void cursesinit(void); -static ulong entcount(char *); -static Entry *entget(char *, ulong); -static void entprint(void); -static char *fmtsize(size_t); -static void notify(int, const char *); -static char *promptstr(const char *); -static int confirmact(const char *); -static int spawn(char *); -static void nav(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 selcorrect(void); -static void entcleanup(void); -static void escape(char *, const char *); -static void xdelay(useconds_t); -static void echdir(const char *); -static void *emalloc(size_t); -static void cleanup(void); -static void usage(void); -static void die(const char *, ...); +static void initcurses(void); +static struct entry *entget(const char *); +static void entprint(struct pane *); +static char *fmtsize(size_t); +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 void chlevel(const arg *); +static void setflag(const arg *); +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 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 cleanup(void); +static void die(const char *, ...); /* useful strings */ static const char *cmds[] = { - [CMD_OPEN] = "xdg-open", - [CMD_MV] = "mv", + [CMD_OPEN] = "xdg-open", + [CMD_MV] = "mv", }; static const char *envs[] = { - [ENV_SHELL] = "SHELL", - [ENV_EDITOR] = "EDITOR", - [ENV_PAGER] = "PAGER", + [ENV_SHELL] = "SHELL", + [ENV_EDITOR] = "EDITOR", + [ENV_PAGER] = "PAGER", }; static const char *msgs[] = { - [MSG_OPENWITH] = "open with: ", - [MSG_RENAME] = "rename: ", - [MSG_EXEC] = "execute '%s' (y/N)?", - [MSG_SORT] = "'n'ame 's'ize 'd'ate 'r'everse", - [MSG_PROMPT] = ":", - [MSG_FAIL] = "action failed" + [MSG_OPENWITH] = "open with: ", + [MSG_RENAME] = "rename: ", + [MSG_EXEC] = "execute '%s' (y/N)?", + [MSG_SORT] = "'n'ame 's'ize 'd'ate 'r'everse", + [MSG_PROMPT] = ":", + [MSG_FAIL] = "action failed", }; /* globals variables */ -static Win *win = NULL; /* main display */ -static char *curdir = NULL; /* current directory */ -static int cur = 0; /* cursor position */ -static int curscroll = 0; /* cursor scroll */ -static int scrolldir; /* scroll direction */ +static char *argv0; /* program name */ +static struct pane *pane; /* display */ +static char *curdir; /* current directory */ +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 */ -static uchar f_redraw = 0; /* redraw screen */ -static uchar f_info = 0; /* show info about entries */ -static uchar f_noconfirm = 0; /* exec without confirmation */ -static uchar f_namesort = 0; /* sort entries by name */ -static uchar f_sizesort = 0; /* sort entries by size */ -static uchar f_datesort = 0; /* sort entries by date */ -static uchar f_revsort = 0; /* reverse current sort function */ -static uchar f_running = 1; /* 0 when sfm should exit */ - -static int (*sortfn)(const void *x, const void *y); +static uchar f_showall = 0; /* show hidden files */ +static uchar f_redraw = 0; /* redraw screen */ +static uchar f_info = 0; /* show info about entries */ +static uchar f_noconfirm = 0; /* exec without confirmation */ +static uchar f_namesort = 0; /* sort entries by name */ +static uchar f_sizesort = 0; /* sort entries by size */ +static uchar f_datesort = 0; /* sort entries by date */ +static uchar f_revsort = 0; /* reverse current sort function */ +static uchar f_running = 1; /* 0 when sfm should exit */ #include "config.h" @@ -230,726 +223,739 @@ static int (*sortfn)(const void *x, const void *y); static inline int namecmp(const void *x, const void *y) { - return (strcmp(((Entry *)x)->name, ((Entry *)y)->name)); + return (strcmp(((struct entry *)x)->name, ((struct entry *)y)->name)); } static inline int revnamecmp(const void *x, const void *y) { - return -namecmp(x, y); + return -namecmp(x, y); } static inline int datecmp(const void *x, const void *y) { - return (strcmp(((Entry *)x)->date, ((Entry *)y)->date)); + return (strcmp(((struct entry *)x)->date, ((struct entry *)y)->date)); } static inline int revdatecmp(const void *x, const void *y) { - return -datecmp(x, y); + return -datecmp(x, y); } static inline int sizecmp(const void *x, const void *y) { - return -(((Entry *)x)->stat.st_size - ((Entry *)y)->stat.st_size); + return -(((struct entry *)x)->stat.st_size - + ((struct entry *)y)->stat.st_size); } static inline int revsizecmp(const void *x, const void *y) { - return -sizecmp(x, y); + return -sizecmp(x, y); } static void -cursesinit(void) -{ - int i = 1; - - if (!initscr()) - die("initscr:"); - - noecho(); - cbreak(); - curs_set(0); - keypad(stdscr, 1); - /*timeout(1000);*/ - /*set_escdelay(25);*/ - - start_color(); - - for (; i < ARRLEN(colors); i++) - init_pair(i, colors[i], COLOR_BLACK); -} - -static ulong -entcount(char *path) -{ - DIR *dir; - struct dirent *dent; - int n = 0; - - /* FIXME: repating code, do something! */ - if ((dir = opendir(path)) == NULL) - die("opendir:"); - - while ((dent = readdir(dir)) != NULL) { - if (!strcmp(dent->d_name, "..") || !strcmp(dent->d_name, ".")) - continue; - if (!f_showall && dent->d_name[0] == '.') - continue; - n++; - - } - (void)closedir(dir); - - return n; -} - -static Entry * -entget(char *path, ulong n) -{ - DIR *dir; - struct dirent *dent; - struct tm *tm; - Entry *ents; - int i = 0; - char type; - - ents = emalloc(n * sizeof(Entry)); - if ((dir = opendir(path)) == NULL) - die("opendir:"); - - while ((dent = readdir(dir)) != NULL) { - if (!strcmp(dent->d_name, "..") || !strcmp(dent->d_name, ".")) - continue; - if (!f_showall && dent->d_name[0] == '.') - continue; - - ents[i].nlen = strlen(dent->d_name); - ents[i].name = emalloc(ents[i].nlen + 1); - strcpy(ents[i].name, dent->d_name); - - stat(ents[i].name, &ents[i].stat); - tm = localtime(&ents[i].stat.st_ctime); - strftime(ents[i].date, 12, "%F", tm); - strcpy(ents[i].sizestr, fmtsize(ents[i].stat.st_size)); - - ents[i].flags = 0; - /* FIXME: resets on every redraw, keep track somehow */ - ents[i].selected = 0; - ents[i].flags |= dent->d_type; - - /* TODO: use fstatat(3) */ - - /* FIXME: links don't work */ - switch (ents[i].stat.st_mode & S_IFMT) { - case S_IFREG: - type = '-'; - break; - case S_IFDIR: - type = 'd'; - break; - case S_IFLNK: - type = 'l'; - break; - case S_IFSOCK: - type = 's'; - break; - case S_IFIFO: - type = 'p'; - break; - case S_IFBLK: - type = 'b'; - break; - case S_IFCHR: - type = 'c'; - break; - default: - type = '?'; - break; - } - - /* seperate field for lsperms? */ - sprintf(ents[i].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); - - i++; - - } - (void)closedir(dir); - ENTSORT(ents, n); - - return ents; +initcurses(void) +{ + struct sigaction sa; + int i; + + if (!initscr()) + die("initscr:"); + noecho(); + cbreak(); + curs_set(0); + keypad(stdscr, 1); + /*timeout(1000);*/ + /*set_escdelay(25);*/ + 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; + /*sa.sa_handler = sighandler;*/ + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); +} + +static struct entry * +entget(const char *path) +{ + struct entry *ents; + struct dirent *dent; + struct tm *tm; + DIR *dir; + int i = 0; + char type; + + if ((dir = opendir(path)) == NULL) + die("opendir: %s", path); + + ents = emalloc(sizeof(struct entry)); + + while ((dent = readdir(dir)) != NULL) { + if (!strcmp(dent->d_name, "..") || !strcmp(dent->d_name, ".")) + continue; + if (!f_showall && dent->d_name[0] == '.') + continue; + /* 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); + + 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)); + + ents[i].flags = 0; + /* FIXME: resets on every redraw, keep track somehow */ + ents[i].selected = 0; + ents[i].flags |= dent->d_type; + + /* TODO: use fstatat(3) */ + /* FIXME: links don't work */ + switch (ents[i].stat.st_mode & S_IFMT) { + case S_IFREG: + type = '-'; + break; + case S_IFDIR: + type = 'd'; + break; + case S_IFLNK: + type = 'l'; + break; + case S_IFSOCK: + type = 's'; + break; + case S_IFIFO: + type = 'p'; + break; + case S_IFBLK: + type = 'b'; + break; + case S_IFCHR: + type = 'c'; + break; + default: + type = '?'; + break; + } + + /* XXX: seperate field for lsperms? */ + sprintf(ents[i].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); + + i++; + + } + (void)closedir(dir); + pane->nents = i; + ENTSORT(ents, pane->nents, pane->sortfn); + + return ents; } static void -entprint(void) -{ - Entry *ent; - int i = 0; - uint attrs; - uchar color; - char ind; - - attron(A_BOLD | COLOR_PAIR(C_DIR)); - addstr(curdir); - attroff(A_BOLD | COLOR_PAIR(C_DIR)); - - /* TODO: change 4 to line ignore constant */ - for (; i < win->nents && i <= YMAX - 4; i++) { - ent = &win->ents[i + curscroll]; - ind = ' '; - attrs = 0; - color = 0; - - move(i + 2, 0); - - if (i == cur) - attrs |= A_REVERSE; - - 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); - attroff(COLOR_PAIR(C_INF)); - } - - addch(ent->selected ? '+' : ' '); - - switch (ent->stat.st_mode & S_IFMT) { - case S_IFDIR: - ind = '/'; - color = C_DIR; - attrs |= A_BOLD; - break; - case S_IFREG: - color = C_FIL; - if (ent->stat.st_mode & 0100) { - ind = '*'; - color = C_EXE; - } - break; - case S_IFLNK: - ind = (ent->flags & DT_DIR) ? '/' : '@'; - color = C_LNK; - if (S_ISDIR(ent->stat.st_mode)) - attrs |= A_BOLD; - break; - case S_IFSOCK: - ind = '='; - color = C_SOC; - break; - case S_IFIFO: - ind = '|'; - color = C_PIP; - break; - case S_IFBLK: - color = C_BLK; - break; - case S_IFCHR: - color = C_CHR; - break; - default: - ind = '?'; - color = C_UND; - break; - } - - attrs |= COLOR_PAIR(color); - attron(attrs); - addstr(ent->name); - attroff(attrs); - - addch(ind); - } - - mvprintw(YMAX - 1, 0, "%ld/%ld %s", win->sel + 1, win->nents, - win->ents[win->sel].statstr); +entprint(struct pane *pane) +{ + struct entry *ent; + int n, i; + uint attrs; + uchar color; + char ind; + + attron(A_BOLD | COLOR_PAIR(C_DIR)); + addstr(curdir); + attroff(A_BOLD | COLOR_PAIR(C_DIR)); + + if ((n = MIN(YMAX - 4, pane->nents)) == 0) + return; + + for (i = 0; i < n; i++) { + ent = &pane->ents[i + curscroll]; + ind = ' '; + attrs = 0; + color = 0; + + move(i + 2, 0); + + if (i == cur) + attrs |= A_REVERSE; + + 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); + attroff(COLOR_PAIR(C_INF)); + } + + addch(ent->selected ? '+' : ' '); + + switch (ent->stat.st_mode & S_IFMT) { + case S_IFDIR: + ind = '/'; + color = C_DIR; + attrs |= A_BOLD; + break; + case S_IFREG: + color = C_FIL; + if (ent->stat.st_mode & 0100) { + ind = '*'; + color = C_EXE; + } + break; + case S_IFLNK: + ind = (ent->flags & DT_DIR) ? '/' : '@'; + color = C_LNK; + if (S_ISDIR(ent->stat.st_mode)) + attrs |= A_BOLD; + break; + case S_IFSOCK: + ind = '='; + color = C_SOC; + break; + case S_IFIFO: + ind = '|'; + color = C_PIP; + break; + case S_IFBLK: + color = C_BLK; + break; + case S_IFCHR: + color = C_CHR; + break; + default: + ind = '?'; + color = C_UND; + break; + } + + attrs |= COLOR_PAIR(color); + attron(attrs); + addstr(ent->name); + attroff(attrs); + + addch(ind); + } + + mvprintw(YMAX - 1, 0, "%ld/%ld %s", pane->sel + 1, pane->nents, + pane->ents[pane->sel].statstr); } static char * fmtsize(size_t sz) { - static char buf[12]; - int i = 0; + static char buf[12]; + int i = 0; - for (; sz > 1024; i++) - sz >>= 10; - /* TODO: handle floating point parts */ - sprintf(buf, "%ld%c", sz, "BKMGTPEZY"[i]); + for (; sz > 1024; i++) + sz >>= 10; + sprintf(buf, "%ld%c", sz, "BKMGTPEZY"[i]); - return buf; + return buf; } /* TODO: get rid of the `switch`, use vfprintf */ static void notify(int flag, const char *str) { - move(YMAX - 1, 0); - clrtoeol(); - - 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); - } + 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); + } } /* TODO: fix backspace and change name */ static char * promptstr(const char *msg) { - char buf[BUFSIZ], *str; - int len = 0, c; - - notify(-1, msg); - echo(); - curs_set(1); - - while ((c = getch()) != '\n') { - switch (c) { - case KEY_BACKSPACE: /* FALLTHROUGH */ - case KEY_DC: /* FALLTHROUGH */ - case '\b': /* FALLTHROUGH */ - if (len > 0) - len--; - break; - /* FIXME: why is this slow? */ - case ESC: - return NULL; - default: - buf[len++] = c; - } - } - - buf[len] = '\0'; - str = emalloc(len + 1); - strcpy(str, buf); - - curs_set(0); - noecho(); - - return str; + char buf[BUFSIZ], *str; + int len = 0, c; + + notify(-1, msg); + echo(); + curs_set(1); + + while ((c = getch()) != '\n') { + switch (c) { + case KEY_BACKSPACE: /* FALLTHROUGH */ + case KEY_DC: /* FALLTHROUGH */ + case '\b': /* FALLTHROUGH */ + if (len > 0) + len--; + break; + /* FIXME: why is this slow? */ + case ESC: + return NULL; + default: + buf[len++] = c; + } + } + + buf[len] = '\0'; + str = emalloc(len + 1); + (void)strcpy(str, buf); + + curs_set(0); + noecho(); + + return str; } static int confirmact(const char *str) { - if (f_noconfirm) - return 1; - notify(MSG_EXEC, str); - return (getch() == 'y'); + if (f_noconfirm) + return 1; + notify(MSG_EXEC, str); + return (getch() == 'y'); } static int spawn(char *cmd) { - 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; + 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; +} + +/* TODO: handle top, bottom, and half scrolls */ +static void +scrollpane(const arg *arg) +{ + pane->sel += arg->i; + scrolldir += arg->i; } static void -nav(const Arg *arg) -{ - Entry *ent = &win->ents[win->sel]; - char buf[BUFSIZ]; - - switch (arg->n) { - case NAV_LEFT: - echdir(".."); - f_redraw = 1; - break; - case NAV_RIGHT: - if (ent->flags & DT_DIR) - echdir(ent->name); - - /* TODO: handle links to dirs */ - if (ent->flags & DT_REG && ent->flags & ~DT_LNK) { - sprintf(buf, "%s %s", cmds[CMD_OPEN], - win->ents[win->sel].name); - /* TODO: escape this buf! */ - if (!spawn(buf)) - notify(MSG_FAIL, NULL); - } - f_redraw = 1; - break; - case NAV_UP: - win->sel--; - /* - * Sign indicates direction, number indicates how many - * new directories we should show on every scroll. - */ - scrolldir = -1; - break; - case NAV_DOWN: - win->sel++; - scrolldir = 1; - break; - case NAV_TOP: - win->sel = 0; - break; - case NAV_BOTTOM: - win->sel = win->nents - 1; - break; - case NAV_SELECT: - win->ents[win->sel].selected ^= 1; - if (win->ents[win->sel++].selected) - win->nsel++; - else - win->nsel--; - break; - case NAV_SHOWALL: - f_showall ^= 1; - f_redraw = 1; - break; - case NAV_INFO: - f_info ^= 1; - f_redraw = 1; - break; - case NAV_REDRAW: - f_redraw = 1; - break; - case NAV_EXIT: - f_running = 0; - break; - } +chlevel(const arg *arg) +{ + char buf[BUFSIZ]; + + if (arg->i < 0) { + echdir(".."); + } else if (arg->i > 0) { + if (pane->ents[pane->sel].flags & DT_DIR) + echdir(pane->ents[pane->sel].name); + + /* TODO: handle links to dirs */ + if (pane->ents[pane->sel].flags & DT_REG + && pane->ents[pane->sel].flags & ~DT_LNK) { + sprintf(buf, "%s %s", cmds[CMD_OPEN], + pane->ents[pane->sel].name); + /* TODO: escape this buf! */ + if (!spawn(buf)) + notify(MSG_FAIL, NULL); + } + } + f_redraw = 1; } static void -cd(const Arg *arg) +setflag(const arg *arg) { - echdir(arg->s); - f_redraw = 1; + if (arg->i & (1 << 1)) + f_showall ^= 1; + else if (arg->i & (1 << 2)) + f_info ^= 1; + else if (arg->i & (1 << 3)) + f_redraw = 1; /* XXX: redundant */ + else if (arg->i & (1 << 4)) + f_running = 0; + f_redraw = 1; } static void -run(const Arg *arg) -{ - char buf[BUFSIZ]; - char tmp[BUFSIZ]; - int i = 0; - - sprintf(buf, "%s", (const char *)arg->v); - - /*TODO: make prettier */ - if (win->nsel > 0) { - for (; i < win->nents; i++) { - if (win->ents[i].selected) { - escape(tmp, win->ents[i].name); - sprintf(buf + strlen(buf), " %s ", tmp); - } - } - } else { - escape(tmp, win->ents[win->sel].name); - sprintf(buf + strlen(buf), " %s ", tmp); - } - - f_noconfirm = 0; - if (confirmact(buf)) { - spawn(buf); - f_redraw = 1; - } +_select(struct pane *pane, int i) +{ + pane->ents[i].selected ^= 1; + if (pane->ents[i].selected) + pane->nsel++; + else + pane->nsel--; +} + +static void +select(const arg *arg) +{ + int i; + + if (arg->i == 0) + _select(pane, pane->sel++); + else + for (i = 0; i < pane->nents; i++) + _select(pane, i); } static void -builtinrun(const Arg *arg) -{ - Arg prog; - - switch (arg->n) { - 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; +cd(const arg *arg) +{ + echdir(arg->s); + f_redraw = 1; } +/* FIXME: broken. doesn't create args list */ static void -sort(const Arg *arg) -{ - notify(MSG_SORT, NULL); - - switch (getch()) { - case 'n': - f_namesort = 1; - f_sizesort = f_datesort = 0; - break; - case 's': - f_sizesort = 1; - f_namesort = f_datesort = 0; - break; - case 'd': - f_datesort = 1; - f_namesort = f_sizesort = 0; - case 'r': - f_revsort ^= 1; - break; - } - - if (!f_revsort) { - if (f_namesort) - sortfn = namecmp; - else if (f_sizesort) - sortfn = sizecmp; - else if (f_datesort) - sortfn = datecmp; - } else { - if (f_namesort) - sortfn = revnamecmp; - else if (f_sizesort) - sortfn = revsizecmp; - else if (f_datesort) - sortfn = revdatecmp; - } - - ENTSORT(win->ents, win->nents); +run(const arg *arg) +{ + char *buf; + size_t sz; + int i; + + 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); + } else + appendbuf(buf, &sz, pane->ents[pane->sel].name); + buf[sz] = '\0'; + + f_noconfirm = 0; + if (confirmact(buf)) { + //spawn(buf); + f_redraw = 1; + pane->nsel = 0; + } + free(buf); } static void -prompt(const Arg *arg) +builtinrun(const arg *argg) { - char buf[BUFSIZ]; + 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! */ +} - sprintf(buf, "%s", promptstr(msgs[MSG_PROMPT])); - if (confirmact(buf)) { - spawn(buf); - f_redraw = 1; - } +static void +sort(const arg *arg) +{ + notify(MSG_SORT, NULL); + + switch (getch()) { + case 'n': + f_namesort = 1; + f_sizesort = f_datesort = 0; + break; + case 's': + f_sizesort = 1; + f_namesort = f_datesort = 0; + break; + case 'd': + f_datesort = 1; + f_namesort = f_sizesort = 0; + case 'r': + f_revsort ^= 1; + break; + } + + if (!f_revsort) { + if (f_namesort) + pane->sortfn = namecmp; + else if (f_sizesort) + pane->sortfn = sizecmp; + else if (f_datesort) + pane->sortfn = datecmp; + } else { + if (f_namesort) + pane->sortfn = revnamecmp; + else if (f_sizesort) + pane->sortfn = revsizecmp; + else if (f_datesort) + pane->sortfn = revdatecmp; + } + + ENTSORT(pane->ents, pane->nents, pane->sortfn); } static void -selcorrect(void) -{ - if (win->sel < 0) - win->sel = 0; - else if (win->sel > win->nents - 1) - win->sel = win->nents - 1; - - /* TODO: count `onscreen` */ - /* FIXME: BUGS! NAV_BOTTOM doesn't work. Resets after using exec */ - if (win->sel > YMAX - 4 - SCROLLOFF) { - cur = YMAX - 4 - SCROLLOFF; - /*if (win->sel == win->nents - 1)*/ - /*return;*/ - /*else*/ - curscroll += scrolldir; - } else { - cur = win->sel; - curscroll = 0; - } +prompt(const arg *arg) +{ + char buf[BUFSIZ]; + + sprintf(buf, "%s", promptstr(msgs[MSG_PROMPT])); + if (confirmact(buf)) { + spawn(buf); + f_redraw = 1; + } } +/* FIXME: change name and actually implement scrolling */ static void -entcleanup(void) +selcorrect(struct pane *pane) { - int i = 0; + if (pane->sel < 0) + pane->sel = 0; + else if (pane->sel > pane->nents - 1) + pane->sel = pane->nents - 1; - if (win->ents != NULL) { - for (; i < win->nents; i++) - if (win->ents[i].name != NULL) - free(win->ents[i].name); - free(win->ents); - } + cur = pane->sel; + curscroll = 0; } static void -escape(char *buf, const char *str) +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) { - for (; *str; str++) { - switch (*str) { - case ' ': /* FALLTHROUGH */ - case '\'': /* FALLTHROUGH */ - case '(': /* FALLTHROUGH */ - case ')': /* FALLTHROUGH */ - *buf++ = '\\'; - } - *buf++ = *str; - } - *buf = '\0'; + 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); + refresh(); + usleep(delay); } static void echdir(const char *path) { - if (chdir(path) != 0) { - notify(MSG_FAIL, NULL); - xdelay(DELAY_MS << 2); - } + if (chdir(path) == -1) { + notify(MSG_FAIL, NULL); + xdelay(DELAY_MS << 2); + } } static void * emalloc(size_t nb) { - void *p; + void *p; - if ((p = malloc(nb)) == NULL) - die("emalloc:"); - return p; + if ((p = malloc(nb)) == NULL) + die("emalloc:"); + return p; } -static void -cleanup(void) +static void * +erealloc(void *p, size_t nb) +{ + if ((p = realloc(p, nb)) == NULL) + die("realloc:"); + return p; +} + +static char * +estrdup(const char *s) { - entcleanup(); - free(win); - endwin(); + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup:"); + return p; } static void -usage(void) +cleanup(void) { - die("usage: %s [-Hi]"); + entfree(); + free(pane); + endwin(); } static void die(const char *fmt, ...) { - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - - if (fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else - fputc('\n', stderr); - - exit(EXIT_FAILURE); + va_list args; + + fprintf(stderr, "%s: ", argv0); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else + fputc('\n', stderr); + + cleanup(); + exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { - char cwd[PATH_MAX] = {0}; - int ch, i; - - win = emalloc(sizeof(Win)); - win->ents = NULL; - win->sel = win->nsel = win->nents = 0; - - f_redraw = 1; - f_namesort = 1; - sortfn = namecmp; - - if (!setlocale(LC_ALL, "")) - die("setlocale:"); - - while ((ch = getopt(argc, argv, "Hi")) != -1) { - switch (ch) { - case 'H': - f_showall = 1; - break; - case 'i': - f_info = 1; - break; - case '?': /* FALLTHROUGH */ - default: - usage(); - } - } - argc -= optind; - argv += optind; - - cursesinit(); - - while (f_running) { - erase(); - - if (f_redraw) { - if ((curdir = getcwd(cwd, sizeof(cwd))) == NULL) - die("getcwd:"); - - entcleanup(); - win->nents = entcount(curdir); - if ((win->ents = entget(curdir, win->nents)) == NULL) - die("entget:"); - - f_redraw = 0; - refresh(); - } - - /* TODO: change name */ - selcorrect(); - entprint(); - - /*TODO: signal/timeout */ - if ((ch = getch()) != ERR) - for (i = 0; i < ARRLEN(keys); i++) - if (ch == keys[i].key) - keys[i].func(&(keys[i].arg)); - } - - cleanup(); - - return 0; + char cwd[PATH_MAX] = {0}; + int ch, i; + + argv0 = *argv; + + if (!setlocale(LC_ALL, "")) + die("setlocale:"); + if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) + die("isatty:"); + + f_redraw = 1; + f_namesort = 1; + pane = emalloc(sizeof(struct pane)); + pane->ents = NULL; + pane->sel = pane->nsel = pane->nents = 0; + pane->sortfn = namecmp; + + while ((ch = getopt(argc, argv, "Hi")) != -1) { + switch (ch) { + case 'H': + f_showall = 1; + break; + case 'i': + f_info = 1; + break; + case '?': /* FALLTHROUGH */ + default: + fprintf(stderr, "usage: %s [-Hi]\n", argv0); + exit(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + + /* TODO: add [dir] */ + initcurses(); + + while (f_running) { + erase(); + if (f_redraw) { + if ((curdir = getcwd(cwd, sizeof(cwd))) == NULL) + die("getcwd:"); + entfree(); + if ((pane->ents = entget(curdir)) == NULL) + die("entget:"); + f_redraw = 0; + refresh(); + } + selcorrect(pane); + entprint(pane); + /*TODO: signal/timeout, handle SIGTERM to clean up or warn first */ + if ((ch = getch()) != ERR) + for (i = 0; i < ARRLEN(keys); i++) + if (ch == keys[i].key) + keys[i].func(&(keys[i].arg)); + } + cleanup(); + + return 0; }