Added dynamically generated help, fixed some ctrl-c stuff.
This commit is contained in:
parent
b7b6b6cc74
commit
0755256039
190
bb.c
190
bb.c
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -71,6 +72,11 @@ static void update_term_size(void)
|
|||||||
height = sz.ws_row;
|
height = sz.ws_row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_nothing(int _)
|
||||||
|
{
|
||||||
|
// Used as SIGINT handler
|
||||||
|
}
|
||||||
|
|
||||||
static void init_term()
|
static void init_term()
|
||||||
{
|
{
|
||||||
termfd = open("/dev/tty", O_RDWR);
|
termfd = open("/dev/tty", O_RDWR);
|
||||||
@ -175,7 +181,7 @@ static void render(bb_state_t *state)
|
|||||||
|
|
||||||
term_move(0,1);
|
term_move(0,1);
|
||||||
{ // Column labels
|
{ // Column labels
|
||||||
char buf[] = "\e[32m Size Date Bits Name\e[0m";
|
char buf[] = "\e[32m Size Date Perm Name\e[0m";
|
||||||
buf[8] = state->sortmethod == SORT_SIZE ? (state->sort_reverse ? '-' : '+') : ' ';
|
buf[8] = state->sortmethod == SORT_SIZE ? (state->sort_reverse ? '-' : '+') : ' ';
|
||||||
buf[21] = state->sortmethod == SORT_DATE ? (state->sort_reverse ? '-' : '+') : ' ';
|
buf[21] = state->sortmethod == SORT_DATE ? (state->sort_reverse ? '-' : '+') : ' ';
|
||||||
buf[36] = state->sortmethod == SORT_BITS ? (state->sort_reverse ? '-' : '+') : ' ';
|
buf[36] = state->sortmethod == SORT_BITS ? (state->sort_reverse ? '-' : '+') : ' ';
|
||||||
@ -260,10 +266,15 @@ static void render(bb_state_t *state)
|
|||||||
writez(termfd, " \e[0m"); // Reset color and attributes
|
writez(termfd, " \e[0m"); // Reset color and attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
term_move(0, height - 1);
|
static const char *help = "Press '?' to see key bindings ";
|
||||||
char buf[32] = {0};
|
char buf[32] = {0};
|
||||||
int len = snprintf(buf, sizeof(buf), "%lu selected", state->nselected);
|
int len = snprintf(buf, sizeof(buf), " %lu selected", state->nselected);
|
||||||
write(termfd, buf, len);
|
if (strlen(help) + len < width + 1) {
|
||||||
|
term_move(0, height - 1);
|
||||||
|
write(termfd, buf, len);
|
||||||
|
}
|
||||||
|
term_move(MAX(0, width - strlen(help)), height - 1);
|
||||||
|
writez(termfd, help);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compare_alpha(void *r, const void *v1, const void *v2)
|
static int compare_alpha(void *r, const void *v1, const void *v2)
|
||||||
@ -292,7 +303,7 @@ static int compare_alpha(void *r, const void *v1, const void *v2)
|
|||||||
return (*p1 - *p2)*sign;
|
return (*p1 - *p2)*sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compare_bits(void *r, const void *v1, const void *v2)
|
static int compare_perm(void *r, const void *v1, const void *v2)
|
||||||
{
|
{
|
||||||
int sign = *((int *)r) ? -1 : 1;
|
int sign = *((int *)r) ? -1 : 1;
|
||||||
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
|
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
|
||||||
@ -468,9 +479,13 @@ static void explore(char *path, int print_dir, int print_selection)
|
|||||||
cmp = compare_alpha;
|
cmp = compare_alpha;
|
||||||
if (state.sortmethod == SORT_SIZE) cmp = compare_size;
|
if (state.sortmethod == SORT_SIZE) cmp = compare_size;
|
||||||
if (state.sortmethod == SORT_DATE) cmp = compare_date;
|
if (state.sortmethod == SORT_DATE) cmp = compare_date;
|
||||||
if (state.sortmethod == SORT_BITS) cmp = compare_bits;
|
if (state.sortmethod == SORT_BITS) cmp = compare_perm;
|
||||||
qsort_r(&state.files[1], state.nfiles-1, sizeof(entry_t*), &state.sort_reverse, cmp);
|
qsort_r(&state.files[1], state.nfiles-1, sizeof(entry_t*), &state.sort_reverse, cmp);
|
||||||
|
|
||||||
|
// Put the cursor on the first *real* file if one exists
|
||||||
|
if (state.nfiles > 1)
|
||||||
|
++state.cursor;
|
||||||
|
|
||||||
if (to_select[0]) {
|
if (to_select[0]) {
|
||||||
for (int i = 0; i < state.nfiles; i++) {
|
for (int i = 0; i < state.nfiles; i++) {
|
||||||
if (strcmp(to_select, state.files[i]->d_name) == 0) {
|
if (strcmp(to_select, state.files[i]->d_name) == 0) {
|
||||||
@ -498,13 +513,13 @@ static void explore(char *path, int print_dir, int print_selection)
|
|||||||
dt_ms += 1e-6*(double)(clicktime.tv_nsec - state.lastclick.tv_nsec);
|
dt_ms += 1e-6*(double)(clicktime.tv_nsec - state.lastclick.tv_nsec);
|
||||||
state.lastclick = clicktime;
|
state.lastclick = clicktime;
|
||||||
if (mouse_y == 1) {
|
if (mouse_y == 1) {
|
||||||
// Size Date Bits Name
|
// Size Date Perm Name
|
||||||
if (mouse_x <= 8)
|
if (mouse_x <= 8)
|
||||||
goto sort_size;
|
goto sort_size;
|
||||||
else if (mouse_x <= 30)
|
else if (mouse_x <= 30)
|
||||||
goto sort_date;
|
goto sort_date;
|
||||||
else if (mouse_x <= 35)
|
else if (mouse_x <= 35)
|
||||||
goto sort_bits;
|
goto sort_perm;
|
||||||
else
|
else
|
||||||
goto sort_alpha;
|
goto sort_alpha;
|
||||||
} else if (mouse_y >= 2 && state.scroll + (mouse_y - 2) < state.nfiles) {
|
} else if (mouse_y >= 2 && state.scroll + (mouse_y - 2) < state.nfiles) {
|
||||||
@ -694,44 +709,61 @@ static void explore(char *path, int print_dir, int print_selection)
|
|||||||
picked = 0;
|
picked = 0;
|
||||||
goto open_file;
|
goto open_file;
|
||||||
|
|
||||||
case 'a':
|
|
||||||
sort_alpha:
|
|
||||||
if (state.sortmethod == SORT_ALPHA)
|
|
||||||
state.sort_reverse ^= 1;
|
|
||||||
else {
|
|
||||||
state.sortmethod = SORT_ALPHA;
|
|
||||||
state.sort_reverse = 0;
|
|
||||||
}
|
|
||||||
goto sort_files;
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
sort_bits:
|
|
||||||
if (state.sortmethod == SORT_BITS)
|
|
||||||
state.sort_reverse ^= 1;
|
|
||||||
else {
|
|
||||||
state.sortmethod = SORT_BITS;
|
|
||||||
state.sort_reverse = 0;
|
|
||||||
}
|
|
||||||
goto sort_files;
|
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
sort_size:
|
// Change sorting method:
|
||||||
if (state.sortmethod == SORT_SIZE)
|
term_move(0, height-1);
|
||||||
state.sort_reverse ^= 1;
|
writez(termfd, "\e[K\e[1mSort by (a)lphabetic (s)ize (t)ime (p)ermissions:\e[0m \e[?25h");
|
||||||
else {
|
try_sort_again:
|
||||||
state.sortmethod = SORT_SIZE;
|
switch (term_getkey(termfd, &mouse_x, &mouse_y)) {
|
||||||
state.sort_reverse = 0;
|
case 'a': case 'A':
|
||||||
}
|
sort_alpha:
|
||||||
goto sort_files;
|
if (state.sortmethod == SORT_ALPHA)
|
||||||
|
state.sort_reverse ^= 1;
|
||||||
|
else {
|
||||||
|
state.sortmethod = SORT_ALPHA;
|
||||||
|
state.sort_reverse = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 't':
|
case 'p': case 'P':
|
||||||
sort_date:
|
sort_perm:
|
||||||
if (state.sortmethod == SORT_DATE)
|
if (state.sortmethod == SORT_BITS)
|
||||||
state.sort_reverse ^= 1;
|
state.sort_reverse ^= 1;
|
||||||
else {
|
else {
|
||||||
state.sortmethod = SORT_DATE;
|
state.sortmethod = SORT_BITS;
|
||||||
state.sort_reverse = 0;
|
state.sort_reverse = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's': case 'S':
|
||||||
|
sort_size:
|
||||||
|
if (state.sortmethod == SORT_SIZE)
|
||||||
|
state.sort_reverse ^= 1;
|
||||||
|
else {
|
||||||
|
state.sortmethod = SORT_SIZE;
|
||||||
|
state.sort_reverse = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't': case 'T':
|
||||||
|
sort_date:
|
||||||
|
if (state.sortmethod == SORT_DATE)
|
||||||
|
state.sort_reverse ^= 1;
|
||||||
|
else {
|
||||||
|
state.sortmethod = SORT_DATE;
|
||||||
|
state.sort_reverse = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
goto try_sort_again;
|
||||||
|
|
||||||
|
default:
|
||||||
|
writez(termfd, "\e[?25l");
|
||||||
|
goto redraw;
|
||||||
}
|
}
|
||||||
|
// Hide cursor again
|
||||||
|
writez(termfd, "\e[?25l");
|
||||||
goto sort_files;
|
goto sort_files;
|
||||||
|
|
||||||
case '.':
|
case '.':
|
||||||
@ -810,21 +842,64 @@ static void explore(char *path, int print_dir, int print_selection)
|
|||||||
goto tail_call;
|
goto tail_call;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
case '?': {
|
||||||
|
close_term();
|
||||||
|
int fd;
|
||||||
|
pid_t child = run_cmd(NULL, &fd, "less -r");
|
||||||
|
|
||||||
|
writez(fd, "\n \e[33;1mKey Bindings:\e[0m\n");
|
||||||
for (int i = 0; bindings[i].key; i++) {
|
for (int i = 0; bindings[i].key; i++) {
|
||||||
|
if (bindings[i].key > 0) continue;
|
||||||
|
writez(fd, "\e[1m");
|
||||||
|
char *tab = strchr(bindings[i].command, '\t');
|
||||||
|
const char *spaces = " ";
|
||||||
|
write(fd, spaces, 16 - (tab - bindings[i].command));
|
||||||
|
write(fd, bindings[i].command, tab - bindings[i].command);
|
||||||
|
write(fd, " ", 1);
|
||||||
|
writez(fd, tab + 1);
|
||||||
|
writez(fd, "\e[0m\n");
|
||||||
|
}
|
||||||
|
writez(fd, "\n \e[33;1mScript Key Bindings:\e[0m\n");
|
||||||
|
for (int i = 0; bindings[i].key; i++) {
|
||||||
|
if (bindings[i].key <= 0) continue;
|
||||||
|
writez(fd, "\e[1m");
|
||||||
|
char buf[] = " X \e[0m";
|
||||||
|
*strchr(buf, 'X') = bindings[i].key;
|
||||||
|
writez(fd, buf);
|
||||||
|
writez(fd, bindings[i].command);
|
||||||
|
writez(fd, "\e[0m\n");
|
||||||
|
}
|
||||||
|
writez(fd, "\n");
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
waitpid(child, NULL, 0);
|
||||||
|
init_term();
|
||||||
|
goto redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
goto skip_redraw;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Search user-defined key bindings from config.h:
|
||||||
|
for (int i = 0; bindings[i].key > 0; i++) {
|
||||||
if (key == bindings[i].key) {
|
if (key == bindings[i].key) {
|
||||||
term_move(0, height-1);
|
term_move(0, height-1);
|
||||||
if (!(bindings[i].flags & ONSCREEN))
|
struct termios cur_tios;
|
||||||
|
if (!(bindings[i].flags & ONSCREEN)) {
|
||||||
close_term();
|
close_term();
|
||||||
else {
|
} else {
|
||||||
// Show cursor:
|
tcgetattr(termfd, &cur_tios);
|
||||||
|
struct termios tios;
|
||||||
|
memcpy(&tios, &orig_termios, sizeof(tios));
|
||||||
|
tcsetattr(termfd, TCSAFLUSH, &tios);
|
||||||
|
// Show cursor
|
||||||
writez(termfd, "\e[?25h");
|
writez(termfd, "\e[?25h");
|
||||||
tcsetattr(termfd, TCSAFLUSH, &orig_termios);
|
|
||||||
close(termfd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int scriptinfd;
|
int scriptinfd;
|
||||||
pid_t child;
|
pid_t child;
|
||||||
|
sig_t old_handler = signal(SIGINT, do_nothing);
|
||||||
child = run_cmd(NULL, &scriptinfd, bindings[i].command);
|
child = run_cmd(NULL, &scriptinfd, bindings[i].command);
|
||||||
if (!(bindings[i].flags & NO_FILES)) {
|
if (!(bindings[i].flags & NO_FILES)) {
|
||||||
if (state.nselected > 0) {
|
if (state.nselected > 0) {
|
||||||
@ -835,7 +910,15 @@ static void explore(char *path, int print_dir, int print_selection)
|
|||||||
}
|
}
|
||||||
close(scriptinfd);
|
close(scriptinfd);
|
||||||
waitpid(child, NULL, 0);
|
waitpid(child, NULL, 0);
|
||||||
init_term();
|
signal(SIGINT, old_handler);
|
||||||
|
|
||||||
|
if (!(bindings[i].flags & ONSCREEN)) {
|
||||||
|
init_term();
|
||||||
|
} else {
|
||||||
|
tcsetattr(termfd, TCSAFLUSH, &cur_tios);
|
||||||
|
// hide cursor
|
||||||
|
writez(termfd, "\e[?25l");
|
||||||
|
}
|
||||||
|
|
||||||
if (bindings[i].flags & CLEAR_SELECTION)
|
if (bindings[i].flags & CLEAR_SELECTION)
|
||||||
clear_selection(&state);
|
clear_selection(&state);
|
||||||
@ -848,13 +931,6 @@ static void explore(char *path, int print_dir, int print_selection)
|
|||||||
goto redraw;
|
goto redraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (key != -1) {
|
|
||||||
term_move(0,height-1);
|
|
||||||
writez(termfd, "\e[K");
|
|
||||||
char buf[64] = {0};
|
|
||||||
sprintf(buf, "unknown: %x", key);
|
|
||||||
writez(termfd, buf);
|
|
||||||
}
|
|
||||||
goto skip_redraw;
|
goto skip_redraw;
|
||||||
}
|
}
|
||||||
goto skip_redraw;
|
goto skip_redraw;
|
||||||
|
34
config.h
34
config.h
@ -1,8 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* User-defined key bindings.
|
* User-defined key bindings.
|
||||||
*/
|
*/
|
||||||
|
#include "keys.h"
|
||||||
|
|
||||||
#define PROG_FUZZY "fzf"
|
#define PROG_FUZZY "fzf"
|
||||||
#define PROG_DELETE "rm -rf"
|
|
||||||
#define SCROLLOFF 5
|
#define SCROLLOFF 5
|
||||||
|
|
||||||
#define NO_FILES (1<<0)
|
#define NO_FILES (1<<0)
|
||||||
@ -18,18 +19,39 @@ struct {
|
|||||||
const char *command;
|
const char *command;
|
||||||
int flags;
|
int flags;
|
||||||
} bindings[] = {
|
} bindings[] = {
|
||||||
{'?', "less"},
|
// User-defined custom scripts go here:
|
||||||
|
{'L', "less"},
|
||||||
{'D', "xargs rm -rf" DEVNULL, CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'D', "xargs rm -rf" DEVNULL, CLEAR_SELECTION | REFRESH | ONSCREEN},
|
||||||
{'d', "xargs -I @ sh -c 'rm -rfi @ </dev/tty'", CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'d', "xargs -I @ sh -c 'rm -rfi @ </dev/tty'", CLEAR_SELECTION | REFRESH | ONSCREEN},
|
||||||
{'+', "xargs -n1 -I @ cp @ @.copy" DEVNULL, REFRESH | ONSCREEN},
|
{'c', "xargs -n1 -I @ cp @ @.copy" DEVNULL, REFRESH | ONSCREEN},
|
||||||
{'m', "xargs -I @ mv -i @ . </dev/tty" DEVNULL, CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'m', "xargs -I @ mv -i @ . </dev/tty" DEVNULL, CLEAR_SELECTION | REFRESH | ONSCREEN},
|
||||||
{'p', "xargs -I @ cp -i @ . </dev/tty" DEVNULL, CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'p', "xargs -I @ cp -i @ . </dev/tty" DEVNULL, CLEAR_SELECTION | REFRESH | ONSCREEN},
|
||||||
{'n', "touch \"`printf '\\033[33;1mNew file:\\033[0m ' >/dev/tty && head -n1 /dev/tty`\"", ONSCREEN | REFRESH | NO_FILES},
|
{'n', "touch \"`printf '\\033[33;1mNew file:\\033[0m ' >/dev/tty && head -n1 /dev/tty`\"", ONSCREEN | REFRESH | NO_FILES},
|
||||||
{'|', "sh -c \"`printf '> ' >/dev/tty && head -n1 /dev/tty`\"", REFRESH},
|
{'|', "sh -c \"`printf '> ' >/dev/tty && head -n1 /dev/tty`\"", REFRESH},
|
||||||
{'>', "sh -c \"`printf '> ' >/dev/tty && head -n1 /dev/tty`\"", NO_FILES | REFRESH},
|
{'>', "sh -c \"`printf '> ' >/dev/tty && head -n1 /dev/tty`\"", NO_FILES | REFRESH},
|
||||||
{'r', "xargs -I @ -n1 sh -c 'mv \"@\" \"`printf \"\e[1mRename \e[0;33m%%s\e[0m: \" \"@\" >&2 && head -n1 </dev/tty`\"'",
|
{'r', "xargs -I @ -n1 sh -c 'mv \"@\" \"`printf \"\e[1mRename \e[1;33m%%s\e[0m: \" \"@\" >&2 && head -n1 </dev/tty`\"'",
|
||||||
REFRESH | CLEAR_SELECTION | ONSCREEN},
|
|
||||||
{'r', "xargs -I @ -n1 sh -c 'mv \"@\" \"`printf \"\\033[1mRename \\033[0;33m%%s\\033[0m: \" \"@\" >&2 && head -n1 </dev/tty`\"'",
|
|
||||||
REFRESH | CLEAR_SELECTION | ONSCREEN},
|
REFRESH | CLEAR_SELECTION | ONSCREEN},
|
||||||
|
|
||||||
|
// Hard-coded behaviors (these are just placeholders for the help):
|
||||||
|
{-1, "?\t\e[0;34mOpen help menu\e[0m"},
|
||||||
|
{-1, "h,Left\t\e[0;34mNavigate up a directory\e[0m"},
|
||||||
|
{-1, "j,Down\t\e[0;34mMove cursor down\e[0m"},
|
||||||
|
{-1, "k,Up\t\e[0;34mMove cursor up\e[0m"},
|
||||||
|
{-1, "l,Right,Enter\t\e[0;34mOpen file/dir\e[0m"},
|
||||||
|
{-1, "Space\t\e[0;34mToggle selection\e[0m"},
|
||||||
|
{-1, "J\t\e[0;34mMove selection state down\e[0m"},
|
||||||
|
{-1, "K\t\e[0;34mMove selection state up\e[0m"},
|
||||||
|
{-1, "q,Q\t\e[0;34mQuit\e[0m"},
|
||||||
|
{-1, "g,Home\t\e[0;34mGo to first item\e[0m"},
|
||||||
|
{-1, "G,End\t\e[0;34mGo to last item\e[0m"},
|
||||||
|
{-1, "s\t\e[0;34mChange sorting\e[0m"},
|
||||||
|
{-1, "f,/\t\e[0;34mFuzzy find\e[0m"},
|
||||||
|
{-1, "Escape\t\e[0;34mClear selection\e[0m"},
|
||||||
|
{-1, "F5,Ctrl-R\t\e[0;34mRefresh\e[0m"},
|
||||||
|
{-1, "Ctrl-A\t\e[0;34mSelect all\e[0m"},
|
||||||
|
{-1, "Ctrl-C\t\e[0;34mAbort and exit\e[0m"},
|
||||||
|
{-1, "PgDn,Ctrl-D\t\e[0;34mPage down\e[0m"},
|
||||||
|
{-1, "PgUp,Ctrl-U\t\e[0;34mPage up\e[0m"},
|
||||||
|
{-1, "Ctrl-Z\t\e[0;34mSuspend\e[0m"},
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
|
9
keys.h
9
keys.h
@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Definitions of some key character constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEYS_DEFINED
|
||||||
|
#define KEYS_DEFINED
|
||||||
|
|
||||||
#define KEY_F1 (0xFFFF-0)
|
#define KEY_F1 (0xFFFF-0)
|
||||||
#define KEY_F2 (0xFFFF-1)
|
#define KEY_F2 (0xFFFF-1)
|
||||||
#define KEY_F3 (0xFFFF-2)
|
#define KEY_F3 (0xFFFF-2)
|
||||||
@ -189,3 +196,5 @@ int term_getkey(int fd, int *mouse_x, int *mouse_y)
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user