]> Sergey Matveev's repositories - nnn.git/commitdiff
Add the noice file browser
authorlostd <lostd@2f30.org>
Tue, 7 Oct 2014 06:05:30 +0000 (06:05 +0000)
committerlostd <>
Tue, 7 Oct 2014 06:05:30 +0000 (06:05 +0000)
Makefile [new file with mode: 0644]
noice.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..922bf96
--- /dev/null
+++ 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
new file mode 100644 (file)
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;
+}