noice

small file browser
git clone git://git.2f30.org/noice.git
Log | Files | Refs | README | LICENSE

commit 561caf46dbd76221d98b8c3e55b96148794fad3c
Author: lostd <lostd@2f30.org>
Date:   Tue Oct  7 06:05:30 +0000

Add the noice file browser

Diffstat:
Makefile | 9+++++++++
noice.c | 354+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 363 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile @@ -0,0 +1,9 @@ +#CPPFLAGS += -DDEBUG +#CFLAGS += -g +LDLIBS = -lncursesw +BIN = noice + +all: $(BIN) + +clean: + rm -f $(BIN) diff --git a/noice.c b/noice.c @@ -0,0 +1,354 @@ +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <curses.h> +#include <libgen.h> +#include <locale.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> + +#ifdef DEBUG +#define DPRINTF_D(x) printw(#x "=%d\n", x) +#define DPRINTF_S(x) printw(#x "=%s\n", x) +#define DPRINTF_P(x) printw(#x "=0x%p\n", x) +#else +#define DPRINTF_D(x) +#define DPRINTF_S(x) +#define DPRINTF_P(x) +#endif /* DEBUG */ + +#define LEN(x) (sizeof(x) / sizeof(*(x))) + +/* + * Layout: + * .--------- + * | cwd: /mnt/path + * | + * | > file0 + * | file1 + * ... + * | filen + * | + * | msg: invalid extension + * '------ + */ + +int die = 0; + +struct assoc { + char *ext; + char *bin; +} assocs[] = { + { "avi", "mplayer" }, + { "mp4", "mplayer" }, + { "mkv", "mplayer" }, + { "mp3", "mplayer" }, + { "ogg", "mplayer" }, + { "srt", "less" }, + { "txt", "less" }, +}; + +char * +extension(char *file) +{ + char *dot; + + dot = strrchr(file, '.'); + if (dot == NULL || dot == file) + return NULL; + else + return dot + 1; +} + +char * +openwith(char *ext) +{ + int i; + + for (i = 0; i < LEN(assocs); i++) + if (strncmp(assocs[i].ext, ext, strlen(ext)) == 0) + return assocs[i].bin; + return NULL; +} + +int +dentcmp(const void *va, const void *vb) +{ + const struct dirent *a, *b; + + a = *(struct dirent **)va; + b = *(struct dirent **)vb; + + return strcmp(a->d_name, b->d_name); +} + +/* Warning shows up at the bottom */ +void +printwarn(char *prefix) +{ + move(LINES - 1, 0); + printw("%s: %s\n", prefix, strerror(errno)); +} + +/* Kill curses and display error before exiting */ +void +printerr(int ret, char *prefix) +{ + endwin(); + printf("%s: %s\n", prefix, strerror(errno)); + exit(ret); +} + +/* + * Returns 0 normally + * On movement it updates *cur + * Returns 1 on quit + * Returns 2 on go in + * Returns 3 on go up + */ +int +nextsel(int *cur, int max) +{ + int c; + + c = getch(); + switch (c) { + case 'q': + return 1; + /* go up */ + case KEY_BACKSPACE: + case KEY_LEFT: + case 'h': + return 2; + /* go in */ + case KEY_ENTER: + case '\r': + case KEY_RIGHT: + case 'l': + return 3; + /* next */ + case 'j': + case KEY_DOWN: + if (*cur < max - 1) + (*cur)++; + break; + /* prev */ + case 'k': + case KEY_UP: + if (*cur > 0) + (*cur)--; + break; + } + + return 0; +} + +int +testopen(char *path) +{ + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) { + return 0; + } else { + close(fd); + return 1; + } +} + +int +testopendir(char *path) +{ + DIR *dirp; + + dirp = opendir(path); + if (dirp == NULL) { + return 0; + } else { + closedir(dirp); + return 1; + } +} + +void +browse(const char *ipath) +{ + DIR *dirp; + struct dirent *dp; + struct dirent **dents; + int i, n, cur; + int r, ret; + char *path = strdup(ipath); + +begin: + /* Path is a malloc(3)-ed string */ + n = 0; + cur = 0; + dents = NULL; + + dirp = opendir(path); + if (dirp == NULL) { + printwarn("opendir"); + goto nochange; + } + + while ((dp = readdir(dirp)) != NULL) { + /* Skip self and parent */ + if (strncmp(dp->d_name, ".", 2) == 0 + || strncmp(dp->d_name, "..", 3) == 0) + continue; + dents = realloc(dents, (n + 1) * sizeof(*dents)); + if (dents == NULL) + printerr(1, "realloc"); + dents[n] = dp; + n++; + } + + qsort(dents, n, sizeof(*dents), dentcmp); + + for (;;) { +redraw: + /* Clean screen */ + erase(); + + /* Strip slashes */ + for (i = strlen(path) - 1; i > -1; i--) + if (path[i] == '/') + path[i] = '\0'; + else + break; + + DPRINTF_D(cur); + DPRINTF_S(path); + + /* Print cwd */ + printw("cwd: %s%s\n\n", + strncmp(path, "", 1) == 0 ? "/" : "", + path); + + /* Print listing */ + for (i = 0; i < n; i++) + printw(" %s %s\n", + i == cur ? ">" : " ", + dents[i]->d_name); + +nochange: + ret = nextsel(&cur, n); + if (ret == 1) { + free(path); + return; + } + if (ret == 2) { + /* Handle root case */ + if (strncmp(path, "", 1) == 0) { + goto nochange; + } else { + path = dirname(path); + goto out; + } + } + if (ret == 3) { + char *name, *file = NULL; + char *newpath; + char *ext, *bin; + pid_t pid; + + name = dents[cur]->d_name; + + switch (dents[cur]->d_type) { + case DT_DIR: + newpath = malloc(strlen(path) + 1 + + strlen(name) + 1); + sprintf(newpath, "%s/%s", path, name); + if (testopen(newpath)) { + free(path); + path = newpath; + goto out; + } else { + printwarn(newpath); + free(newpath); + goto nochange; + } + case DT_REG: + file = malloc(strlen(path) + 1 + + strlen(name) + 1); + sprintf(file, "%s/%s", path, name); + DPRINTF_S(file); + + /* Open with */ + ext = extension(name); + if (ext == NULL) { + printwarn("invalid extension\n"); + goto nochange; + } + bin = openwith(ext); + if (bin == NULL) { + printwarn("no association\n"); + goto nochange; + } + DPRINTF_S(ext); + DPRINTF_S(bin); + + /* Run program */ + pid = fork(); + if (pid == 0) + execlp(bin, bin, file, NULL); + else + waitpid(pid, NULL, 0); + + free(file); + + /* Screen may be messed up */ + clear(); + /* Some programs reset this */ + keypad(stdscr, TRUE); + goto redraw; + default: + DPRINTF_D(dents[cur]->d_type); + } + } + } + +out: + free(dents); + + r = closedir(dirp); + if (r == -1) + printerr(1, "closedir"); + + goto begin; +} + +int +main(int argc, char *argv[]) +{ + char *ipath = argv[1] != NULL ? argv[1] : "/"; + + /* Test initial path */ + if (!testopendir(ipath)) + printerr(1, ipath); + + /* Set locale before curses setup */ + setlocale(LC_ALL, ""); + + /* Init curses */ + initscr(); + cbreak(); + noecho(); + nonl(); + intrflush(stdscr, FALSE); + keypad(stdscr, TRUE); + curs_set(FALSE); /* Hide cursor */ + + browse(ipath); + + endwin(); /* Restore terminal */ + + return 0; +}