2014-11-07 20:26:47 (UTC-02:00)
Marcel Rodrigues <marcelgmr@gmail.com>
Add screenshot.
diff --git a/Makefile b/Makefile deleted file mode 100644 index 48a221a..0000000 --- a/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -CLINK = -lcurses -CFLAGS = -Wall -Wextra -Werror -std=c89 -DEBUG ?= 0 -ifeq ($(DEBUG), 1) - CFLAGS += -O0 -g -else - CFLAGS += -O2 -endif - -all : rover - -rover : rover.c config.h - $(CC) $(CFLAGS) -o $@ $< $(CLINK) diff --git a/README.md b/README.md deleted file mode 100644 index c0d26e2..0000000 --- a/README.md +++ /dev/null @@ -1,55 +0,0 @@ -Introduction -============ - - Rover is an interactive application for file system navigation. The -main goal is to provide a faster way to explore a file system from the -terminal, compared to what's possible by using `cd`, `ls`, etc. Rover -is designed to be simple and portable. It was originally written to be -used on a headless Raspberry Pi accessed via ssh. The [Ranger file manager](http://ranger.nongnu.org/) -was a major inspiration for the user interface design, but Rover has -significantly less features and dependencies. - - -Quick Start -=========== - - Building: - ``` - $ make - ``` - - Running: - ``` - $ ./rover - ``` - - Using: - ``` - q - quit Rover - j/k - move cursor up/down - J/K - move cursor up/down 10 times - l - enter selected directory - h - go to parent directory - H - go to $HOME directory - RETURN - open $SHELL on the current directory - SPACE - open $PAGER on the selected file - e - open $EDITOR on the selected file - / - start incremental search (RETURN to finish) - f - toggle file listing - d - toggle directory listing - s - toggle hidden file/directory listing - ``` - - -Configuration -============= - - Rover configuration (mostly key bindings and colors) can only be changed -by editing config.h and rebuilding the binary. - - -Copyright -========= - - All of the code and documentation in Rover has been dedicated to the - public domain. diff --git a/TODO b/TODO deleted file mode 100644 index e676d79..0000000 --- a/TODO +++ /dev/null @@ -1,6 +0,0 @@ -file operations - copy, move, delete... -tabs - simply store paths in one array? -browsing history - use keys < & > for navigation diff --git a/config.h b/config.h deleted file mode 100644 index 6bfbaa5..0000000 --- a/config.h +++ /dev/null @@ -1,28 +0,0 @@ -/* CTRL+X: "^X" - * ALT+X: "M-X" - */ -#define RVK_QUIT "q" -#define RVK_DOWN "j" -#define RVK_UP "k" -#define RVK_JUMP_DOWN "J" -#define RVK_JUMP_UP "K" -#define RVK_CD_DOWN "l" -#define RVK_CD_UP "h" -#define RVK_HOME "H" -#define RVK_SHELL "^M" -#define RVK_VIEW " " -#define RVK_EDIT "e" -#define RVK_SEARCH "/" -#define RVK_TG_FILES "f" -#define RVK_TG_DIRS "d" -#define RVK_TG_HIDDEN "s" - -/* Colors available: DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, MAGENTA, WHITE. */ -#define RVC_CWD GREEN -#define RVC_STATUS BLUE -#define RVC_BORDER CYAN -#define RVC_FILE DEFAULT -#define RVC_DIR DEFAULT -#define RVC_HIDDEN MAGENTA - -#define RV_JUMP 10 diff --git a/rover.c b/rover.c deleted file mode 100644 index 1d6e7e3..0000000 --- a/rover.c +++ /dev/null @@ -1,385 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <sys/types.h> /* ? */ -#include <stdio.h> /* FILENAME_MAX */ -#include <locale.h> /* setlocale(), LC_ALL */ -#include <unistd.h> /* chdir(), getcwd() */ -#include <dirent.h> /* DIR, struct dirent, opendir(), ... */ -#include <sys/stat.h> -#include <sys/wait.h> /* waitpid() */ -#include <curses.h> - -#include "config.h" - -#define STATUSSZ 256 -char STATUS[STATUSSZ]; -#define SEARCHSZ 256 -char SEARCH[SEARCHSZ]; -#define MAXARGS 256 -char *args[MAXARGS]; - -typedef enum {DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, MAGENTA, WHITE} color_t; - -#define HEIGHT (LINES-4) - -#define SHOW_FILES 0x01u -#define SHOW_DIRS 0x02u -#define SHOW_HIDDEN 0x04u - -struct rover_t { - int nfiles; - int scroll; - int fsel; - uint8_t flags; - char **fnames; - WINDOW *window; - char cwd[FILENAME_MAX]; -} rover; - -static int -spcmp(const void *a, const void *b) -{ - int isdir1, isdir2, cmpdir; - const char *s1 = *(const char **) a; - const char *s2 = *(const char **) b; - isdir1 = strchr(s1, '/') != NULL; - isdir2 = strchr(s2, '/') != NULL; - cmpdir = isdir2 - isdir1; - /* FIXME: why doesn't `return cmpdir || strcoll(s1, s2)` work here? */ - return cmpdir ? cmpdir : strcoll(s1, s2); -} - -int -ls(char *path, char ***namesp, uint8_t flags) -{ - DIR *dp; - struct dirent *ep; - struct stat statbuf; - char **names; - int i, n; - - if((dp = opendir(path)) == NULL) - return -1; - n = -2; /* We don't want the entries "." and "..". */ - while (readdir(dp)) n++; - rewinddir(dp); - names = (char **) malloc(n * sizeof(char *)); - i = 0; - while ((ep = readdir(dp))) { - if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, "..")) - continue; - if (!(flags & SHOW_HIDDEN) && ep->d_name[0] == '.') - continue; - /* FIXME: ANSI C doesn't have lstat(). How do we handle symlinks? */ - (void) stat(ep->d_name, &statbuf); - if (S_ISDIR(statbuf.st_mode)) { - if (flags & SHOW_DIRS) { - names[i] = (char *) malloc(strlen(ep->d_name) + 2); - strcpy(names[i], ep->d_name); - strcat(names[i], "/"); - i++; - } - } - else if (flags & SHOW_FILES) { - names[i] = (char *) malloc(strlen(ep->d_name) + 1); - strcpy(names[i], ep->d_name); - i++; - } - } - n = i; /* Ignore unused space in array caused by filters. */ - qsort(names, n, sizeof(char *), spcmp); - (void) closedir(dp); - *namesp = names; - return n; -} - -static void -clean_term() -{ - endwin(); -} - -static void -init_term() -{ - setlocale(LC_ALL, ""); - initscr(); - cbreak(); /* Get one character at a time. */ - noecho(); - nonl(); /* No NL->CR/NL on output. */ - intrflush(stdscr, FALSE); - keypad(stdscr, TRUE); - curs_set(FALSE); /* Hide blinking cursor. */ - if (has_colors()) { - start_color(); - init_pair(RED, COLOR_RED, COLOR_BLACK); - init_pair(GREEN, COLOR_GREEN, COLOR_BLACK); - init_pair(YELLOW, COLOR_YELLOW,COLOR_BLACK); - init_pair(BLUE, COLOR_BLUE, COLOR_BLACK); - init_pair(CYAN, COLOR_CYAN, COLOR_BLACK); - init_pair(MAGENTA, COLOR_MAGENTA, COLOR_BLACK); - init_pair(WHITE, COLOR_WHITE, COLOR_BLACK); - } - atexit(clean_term); -} - -static void -update_browser() -{ - int i, j, n; - char fmt[32]; - - for (i = 0, j = rover.scroll; i < HEIGHT && j < rover.nfiles; i++, j++) { - if (j == rover.fsel) - wattr_on(rover.window, A_REVERSE, NULL); - if (rover.fnames[j][0] == '.') - wcolor_set(rover.window, RVC_HIDDEN, NULL); - else if (strchr(rover.fnames[j], '/') != NULL) - wcolor_set(rover.window, RVC_DIR, NULL); - else - wcolor_set(rover.window, RVC_FILE, NULL); - (void) mvwhline(rover.window, i + 1, 1, ' ', COLS - 2); - (void) mvwaddnstr(rover.window, i + 1, 1, rover.fnames[j], COLS - 2); - wcolor_set(rover.window, DEFAULT, NULL); - if (j == rover.fsel) - wattr_off(rover.window, A_REVERSE, NULL); - } - wrefresh(rover.window); - sprintf(STATUS, "%d/%d%n", rover.fsel + 1, rover.nfiles, &n); - sprintf(fmt, "%% %dd/%%d", 10-n); - STATUS[0] = rover.flags & SHOW_FILES ? 'F' : ' '; - STATUS[1] = rover.flags & SHOW_DIRS ? 'D' : ' '; - STATUS[2] = rover.flags & SHOW_HIDDEN ? 'H' : ' '; - sprintf(STATUS+3, fmt, rover.fsel + 1, rover.nfiles); - color_set(RVC_STATUS, NULL); - mvaddstr(LINES - 1, COLS - strlen(STATUS), STATUS); - color_set(DEFAULT, NULL); - refresh(); -} - -/* NOTE: The caller needs to write the new path to rover.cwd - * *before* calling this function. */ -static void -cd() -{ - int i; - - rover.fsel = 0; - rover.scroll = 0; - (void) chdir(rover.cwd); - (void) mvhline(0, 0, ' ', COLS); - color_set(RVC_CWD, NULL); - (void) mvaddnstr(0, 0, rover.cwd, COLS); - color_set(DEFAULT, NULL); - for (i = 0; i < rover.nfiles; i++) - free(rover.fnames[i]); - if (rover.nfiles) - free(rover.fnames); - rover.nfiles = ls(rover.cwd, &rover.fnames, rover.flags); - (void) wclear(rover.window); - wcolor_set(rover.window, RVC_BORDER, NULL); - wborder(rover.window, 0, 0, 0, 0, 0, 0, 0, 0); - wcolor_set(rover.window, DEFAULT, NULL); - update_browser(); - refresh(); -} - -static void -spawn() -{ - pid_t pid; - int status; - - pid = fork(); - if (pid > 0) { - /* fork() succeeded. */ - clean_term(); - (void) waitpid(pid, &status, 0); - init_term(); - doupdate(); - } - else if (pid == 0) { - /* Child process. */ - execvp(args[0], args); - } -} - -int -main() -{ - char *program; - char *key; - - init_term(); - /* Avoid invalid free() calls in cd() by zeroing the tally. */ - rover.nfiles = 0; - rover.flags = SHOW_FILES | SHOW_DIRS; - (void) getcwd(rover.cwd, FILENAME_MAX); - strcat(rover.cwd, "/"); - rover.window = subwin(stdscr, LINES - 2, COLS, 1, 0); - cd(); - while (1) { - key = keyname(getch()); - if (!strcmp(key, RVK_QUIT)) - break; - else if (!strcmp(key, RVK_DOWN)) { - if (rover.fsel == rover.nfiles - 1) - rover.scroll = rover.fsel = 0; - else { - rover.fsel++; - if ((rover.fsel - rover.scroll) == HEIGHT) - rover.scroll++; - } - update_browser(); - } - else if (!strcmp(key, RVK_UP)) { - if (rover.fsel == 0) { - rover.fsel = rover.nfiles - 1; - rover.scroll = rover.nfiles - HEIGHT; - if (rover.scroll < 0) - rover.scroll = 0; - } - else { - rover.fsel--; - if (rover.fsel < rover.scroll) - rover.scroll--; - } - update_browser(); - } - else if (!strcmp(key, RVK_JUMP_DOWN)) { - rover.fsel += RV_JUMP; - if (rover.fsel >= rover.nfiles) - rover.fsel = rover.nfiles - 1; - if (rover.nfiles > HEIGHT) { - rover.scroll += RV_JUMP; - if (rover.scroll > rover.nfiles - HEIGHT) - rover.scroll = rover.nfiles - HEIGHT; - } - update_browser(); - } - else if (!strcmp(key, RVK_JUMP_UP)) { - rover.fsel -= RV_JUMP; - if (rover.fsel < 0) - rover.fsel = 0; - rover.scroll -= RV_JUMP; - if (rover.scroll < 0) - rover.scroll = 0; - update_browser(); - } - else if (!strcmp(key, RVK_CD_DOWN)) { - if (strchr(rover.fnames[rover.fsel], '/') == NULL) - continue; - strcat(rover.cwd, rover.fnames[rover.fsel]); - cd(); - } - else if (!strcmp(key, RVK_CD_UP)) { - if (strlen(rover.cwd) == 1) - continue; - rover.cwd[strlen(rover.cwd) - 1] = '\0'; - *(strrchr(rover.cwd, '/') + 1) = '\0'; - cd(); - } - else if (!strcmp(key, RVK_HOME)) { - strcpy(rover.cwd, getenv("HOME")); - if (rover.cwd[strlen(rover.cwd) - 1] != '/') - strcat(rover.cwd, "/"); - cd(); - } - else if (!strcmp(key, RVK_SHELL)) { - program = getenv("SHELL"); - if (program) { - args[0] = program; - args[1] = NULL; - spawn(); - } - } - else if (!strcmp(key, RVK_VIEW)) { - if (strchr(rover.fnames[rover.fsel], '/') != NULL) - continue; - program = getenv("PAGER"); - if (program) { - args[0] = program; - args[1] = rover.fnames[rover.fsel]; - args[2] = NULL; - spawn(); - } - } - else if (!strcmp(key, RVK_EDIT)) { - if (strchr(rover.fnames[rover.fsel], '/') != NULL) - continue; - program = getenv("EDITOR"); - if (program) { - args[0] = program; - args[1] = rover.fnames[rover.fsel]; - args[2] = NULL; - spawn(); - } - } - else if (!strcmp(key, RVK_SEARCH)) { - int ch, length, sel, oldsel, oldscroll; - color_t color; - oldsel = rover.fsel; - oldscroll = rover.scroll; - *SEARCH = '\0'; - length = 0; - while ((ch = getch()) != '\r') { - switch (ch) { - case 8: - case 127: - if (length) - SEARCH[--length] = '\0'; - if (!length) { - rover.fsel = oldsel; - rover.scroll = oldscroll; - } - break; - default: - if (length < SEARCHSZ - 2) - SEARCH[length++] = ch; - } - if (length) { - for (sel = 0; sel < rover.nfiles; sel++) - if (!strncmp(rover.fnames[sel], SEARCH, length)) - break; - if (sel < rover.nfiles) { - color = GREEN; - rover.fsel = sel; - if (rover.nfiles > HEIGHT) { - if (sel > rover.nfiles - HEIGHT) - rover.scroll = rover.nfiles - HEIGHT; - else - rover.scroll = sel; - } - } - else - color = RED; - } - update_browser(); - SEARCH[length] = ' '; - color_set(color, NULL); - mvaddstr(LINES - 1, 0, SEARCH); - color_set(DEFAULT, NULL); - } - move(LINES - 1, 0); - clrtoeol(); - update_browser(); - } - else if (!strcmp(key, RVK_TG_FILES)) { - rover.flags ^= SHOW_FILES; - cd(); - } - else if (!strcmp(key, RVK_TG_DIRS)) { - rover.flags ^= SHOW_DIRS; - cd(); - } - else if (!strcmp(key, RVK_TG_HIDDEN)) { - rover.flags ^= SHOW_HIDDEN; - cd(); - } - } - while (rover.nfiles--) free(rover.fnames[rover.nfiles]); - free(rover.fnames); - delwin(rover.window); - return 0; -} diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..3c25a14 Binary files /dev/null and b/screenshot.png differ