aboutsummaryrefslogtreecommitdiffstats
path: root/noice.c
diff options
context:
space:
mode:
authorlostd <lostd@2f30.org>2014-10-07 06:05:30 +0000
committerlostd <>2014-10-07 06:05:30 +0000
commit561caf46dbd76221d98b8c3e55b96148794fad3c (patch)
tree0f949e7553e2a7cd9434a9e622d20422e1f844b7 /noice.c
downloadnoice-561caf46dbd76221d98b8c3e55b96148794fad3c.tar.gz
Add the noice file browser
Diffstat (limited to 'noice.c')
-rw-r--r--noice.c354
1 files changed, 354 insertions, 0 deletions
diff --git a/noice.c b/noice.c
new file mode 100644
index 0000000..a399d24
--- /dev/null
+++ 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;
+}
Un proyecto texto-plano.xyz