Working version of running (nearly) everything on bb IPC commands
This commit is contained in:
parent
566ee2f272
commit
e080490d51
5
bb.1
5
bb.1
@ -8,6 +8,7 @@ bb \- A bitty browser for command line file management
|
|||||||
[\fI-d\fR]
|
[\fI-d\fR]
|
||||||
[\fI-s\fR]
|
[\fI-s\fR]
|
||||||
[\fI-0\fR]
|
[\fI-0\fR]
|
||||||
|
[\fI-c commands... \fR]
|
||||||
[\fIdirectory\fR]
|
[\fIdirectory\fR]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
\fBbb\fR is a tiny TUI console application for browsing and managing files.
|
\fBbb\fR is a tiny TUI console application for browsing and managing files.
|
||||||
@ -22,6 +23,10 @@ Print the selected files on exit.
|
|||||||
If printing the selected files on exit, use NULL-terminated strings instead of
|
If printing the selected files on exit, use NULL-terminated strings instead of
|
||||||
newline-separated.
|
newline-separated.
|
||||||
|
|
||||||
|
.B \-c commands...
|
||||||
|
From within \fBbb\fR, running \fBbb -c ...\fR will execute an internal bb
|
||||||
|
command for modifying bb's state.
|
||||||
|
|
||||||
.B directory
|
.B directory
|
||||||
Open to this directory.
|
Open to this directory.
|
||||||
|
|
||||||
|
577
bb.c
577
bb.c
@ -25,10 +25,11 @@
|
|||||||
#define MAX(a,b) ((a) < (b) ? (b) : (a))
|
#define MAX(a,b) ((a) < (b) ? (b) : (a))
|
||||||
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
||||||
#define MAX_PATH 4096
|
#define MAX_PATH 4096
|
||||||
|
#define startswith(str, start) (strncmp(str, start, strlen(start)) == 0)
|
||||||
#define writez(fd, str) write(fd, str, strlen(str))
|
#define writez(fd, str) write(fd, str, strlen(str))
|
||||||
#define IS_SELECTED(p) ((p)->atme)
|
#define IS_SELECTED(p) ((p)->atme)
|
||||||
|
|
||||||
#define KEY_DELAY -1
|
#define KEY_DELAY 50
|
||||||
|
|
||||||
static struct termios orig_termios;
|
static struct termios orig_termios;
|
||||||
static int termfd;
|
static int termfd;
|
||||||
@ -39,7 +40,7 @@ typedef enum {
|
|||||||
SORT_ALPHA = 0,
|
SORT_ALPHA = 0,
|
||||||
SORT_SIZE,
|
SORT_SIZE,
|
||||||
SORT_BITS,
|
SORT_BITS,
|
||||||
SORT_DATE
|
SORT_TIME
|
||||||
} sortmethod_t;
|
} sortmethod_t;
|
||||||
|
|
||||||
typedef struct entry_s {
|
typedef struct entry_s {
|
||||||
@ -63,7 +64,12 @@ typedef struct {
|
|||||||
sortmethod_t sortmethod;
|
sortmethod_t sortmethod;
|
||||||
} bb_state_t;
|
} bb_state_t;
|
||||||
|
|
||||||
static void err(const char *msg, ...);
|
#define err(...) do { \
|
||||||
|
close_term(); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
_err(); \
|
||||||
|
} while (0)
|
||||||
|
static void _err();
|
||||||
|
|
||||||
static void update_term_size(void)
|
static void update_term_size(void)
|
||||||
{
|
{
|
||||||
@ -73,6 +79,13 @@ static void update_term_size(void)
|
|||||||
height = sz.ws_row;
|
height = sz.ws_row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int clamped(int x, int low, int high)
|
||||||
|
{
|
||||||
|
if (x < low) return low;
|
||||||
|
if (x > high) return high;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
static void do_nothing(int _)
|
static void do_nothing(int _)
|
||||||
{
|
{
|
||||||
// Used as SIGINT handler
|
// Used as SIGINT handler
|
||||||
@ -112,28 +125,34 @@ static void close_term()
|
|||||||
writez(termfd, "\e[?1049l");
|
writez(termfd, "\e[?1049l");
|
||||||
// Show cursor:
|
// Show cursor:
|
||||||
writez(termfd, "\e[?25h");
|
writez(termfd, "\e[?25h");
|
||||||
|
// Restore scrollable region
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "\e[1;%dr", height);
|
||||||
|
writez(termfd, buf);
|
||||||
|
|
||||||
tcsetattr(termfd, TCSAFLUSH, &orig_termios);
|
tcsetattr(termfd, TCSAFLUSH, &orig_termios);
|
||||||
close(termfd);
|
close(termfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void err(const char *msg, ...)
|
static void _err()
|
||||||
{
|
{
|
||||||
close_term();
|
|
||||||
va_list args;
|
|
||||||
va_start(args, msg);
|
|
||||||
int len = fprintf(stderr, msg, args);
|
|
||||||
va_end(args);
|
|
||||||
if (errno)
|
if (errno)
|
||||||
fprintf(stderr, "\n%s", strerror(errno));
|
fprintf(stderr, "\n%s", strerror(errno));
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char bb_tmpfile[] = "/tmp/bb.XXXXXX";
|
||||||
|
|
||||||
static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
|
static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
|
||||||
{
|
{
|
||||||
pid_t child;
|
pid_t child;
|
||||||
sig_t old_handler = signal(SIGINT, do_nothing);
|
sig_t old_handler = signal(SIGINT, do_nothing);
|
||||||
|
|
||||||
|
strcpy(bb_tmpfile, "/tmp/bb.XXXXXX");
|
||||||
|
if (!mktemp(bb_tmpfile))
|
||||||
|
err("Could not create temp file");
|
||||||
|
|
||||||
if ((child = fork()) == 0) {
|
if ((child = fork()) == 0) {
|
||||||
// TODO: is there a max number of args? Should this be batched?
|
// TODO: is there a max number of args? Should this be batched?
|
||||||
char **const args = calloc(MAX(1, state->nselected) + 5, sizeof(char*));
|
char **const args = calloc(MAX(1, state->nselected) + 5, sizeof(char*));
|
||||||
@ -148,12 +167,15 @@ static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
|
|||||||
}
|
}
|
||||||
args[i] = NULL;
|
args[i] = NULL;
|
||||||
|
|
||||||
|
char bb_depth_str[64] = {0}, bb_ipc_str[64] = {0};
|
||||||
{ // Set environment variable to track shell nesting
|
{ // Set environment variable to track shell nesting
|
||||||
char *depthstr = getenv("BB_DEPTH");
|
char *depthstr = getenv("BB_DEPTH");
|
||||||
int depth = depthstr ? atoi(depthstr) : 0;
|
int depth = depthstr ? atoi(depthstr) : 0;
|
||||||
char buf[64] = {0};
|
snprintf(bb_depth_str, sizeof(bb_depth_str), "%d", depth + 1);
|
||||||
snprintf(buf, sizeof(buf), "BB_DEPTH=%d", depth + 1);
|
setenv("BB_DEPTH", bb_depth_str, 1);
|
||||||
putenv(buf);
|
setenv("BBCMD", bb_tmpfile, 1);
|
||||||
|
setenv("BBCURSOR", state->files[state->cursor]->d_name, 1);
|
||||||
|
setenv("BBFULLCURSOR", state->files[state->cursor]->d_fullname, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
execvp("sh", args);
|
execvp("sh", args);
|
||||||
@ -163,53 +185,13 @@ static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
|
|||||||
|
|
||||||
if (child == -1)
|
if (child == -1)
|
||||||
err("Failed to fork");
|
err("Failed to fork");
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
waitpid(child, &status, 0);
|
waitpid(child, &status, 0);
|
||||||
signal(SIGINT, old_handler);
|
signal(SIGINT, old_handler);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static pid_t run_cmd(int *child_out, int *child_in, const char *cmd, ...)
|
|
||||||
{
|
|
||||||
int child_outfds[2], child_infds[2];
|
|
||||||
pid_t child;
|
|
||||||
if (child_out)
|
|
||||||
pipe(child_outfds);
|
|
||||||
if (child_in)
|
|
||||||
pipe(child_infds);
|
|
||||||
if ((child = fork())) {
|
|
||||||
if (child == -1)
|
|
||||||
err("Failed to fork");
|
|
||||||
if (child_out) {
|
|
||||||
*child_out = child_outfds[0];
|
|
||||||
close(child_outfds[1]);
|
|
||||||
}
|
|
||||||
if (child_in) {
|
|
||||||
*child_in = child_infds[1];
|
|
||||||
close(child_infds[0]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (child_out) {
|
|
||||||
dup2(child_outfds[1], STDOUT_FILENO);
|
|
||||||
close(child_outfds[0]);
|
|
||||||
}
|
|
||||||
if (child_in) {
|
|
||||||
dup2(child_infds[0], STDIN_FILENO);
|
|
||||||
close(child_infds[1]);
|
|
||||||
}
|
|
||||||
char *formatted_cmd;
|
|
||||||
va_list args;
|
|
||||||
va_start(args, cmd);
|
|
||||||
int len = vasprintf(&formatted_cmd, cmd, args);
|
|
||||||
va_end(args);
|
|
||||||
if (formatted_cmd)
|
|
||||||
execlp("sh", "sh", "-c", formatted_cmd);
|
|
||||||
err("Failed to execute command %d: '%s'", len, formatted_cmd);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void term_move(int x, int y)
|
static void term_move(int x, int y)
|
||||||
{
|
{
|
||||||
static char buf[32] = {0};
|
static char buf[32] = {0};
|
||||||
@ -263,7 +245,7 @@ static void render(bb_state_t *state, int lazy)
|
|||||||
{ // Column labels
|
{ // Column labels
|
||||||
char buf[] = "\e[32m Size Date Perm 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_TIME ? (state->sort_reverse ? '-' : '+') : ' ';
|
||||||
buf[36] = state->sortmethod == SORT_BITS ? (state->sort_reverse ? '-' : '+') : ' ';
|
buf[36] = state->sortmethod == SORT_BITS ? (state->sort_reverse ? '-' : '+') : ' ';
|
||||||
buf[42] = state->sortmethod == SORT_ALPHA ? (state->sort_reverse ? '-' : '+') : ' ';
|
buf[42] = state->sortmethod == SORT_ALPHA ? (state->sort_reverse ? '-' : '+') : ' ';
|
||||||
writez(termfd, buf);
|
writez(termfd, buf);
|
||||||
@ -460,6 +442,16 @@ static int compare_date(void *r, const void *v1, const void *v2)
|
|||||||
return -(info1.st_mtimespec.tv_sec - info2.st_mtimespec.tv_sec)*sign;
|
return -(info1.st_mtimespec.tv_sec - info2.st_mtimespec.tv_sec)*sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static entry_t *find_file(bb_state_t *state, const char *name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < state->nfiles; i++) {
|
||||||
|
entry_t *e = state->files[i];
|
||||||
|
if (strcmp(name[0] == '/' ? e->d_fullname : e->d_name, name) == 0)
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void write_selection(int fd, entry_t *firstselected, char sep)
|
static void write_selection(int fd, entry_t *firstselected, char sep)
|
||||||
{
|
{
|
||||||
while (firstselected) {
|
while (firstselected) {
|
||||||
@ -491,21 +483,46 @@ static void clear_selection(bb_state_t *state)
|
|||||||
state->nselected = 0;
|
state->nselected = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int select_file(bb_state_t *state, entry_t *e)
|
||||||
|
{
|
||||||
|
if (IS_SELECTED(e)) return 0;
|
||||||
|
if (strcmp(e->d_name, "..") == 0) return 0;
|
||||||
|
if (state->firstselected)
|
||||||
|
state->firstselected->atme = &e->next;
|
||||||
|
e->next = state->firstselected;
|
||||||
|
e->atme = &state->firstselected;
|
||||||
|
state->firstselected = e;
|
||||||
|
++state->nselected;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int deselect_file(bb_state_t *state, entry_t *e)
|
||||||
|
{
|
||||||
|
if (!IS_SELECTED(e)) return 0;
|
||||||
|
if (e->next) e->next->atme = e->atme;
|
||||||
|
*(e->atme) = e->next;
|
||||||
|
e->next = NULL, e->atme = NULL;
|
||||||
|
--state->nselected;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void explore(char *path, int print_dir, int print_selection, char sep)
|
static void explore(char *path, int print_dir, int print_selection, char sep)
|
||||||
{
|
{
|
||||||
char *tmp = path;
|
char realpath_buf[MAX_PATH+1];
|
||||||
char *original_path = calloc(strlen(tmp) + 1, 1);
|
path = strdup(path);
|
||||||
if (!original_path) err("allocation failure");
|
|
||||||
strcpy(original_path, path);
|
|
||||||
path = calloc(strlen(tmp) + 1, 1);
|
|
||||||
if (!path) err("allocation failure");
|
if (!path) err("allocation failure");
|
||||||
strcpy(path, tmp);
|
char *original_path = strdup(path);
|
||||||
tmp = NULL;
|
if (!original_path) err("allocation failure");
|
||||||
|
|
||||||
char to_select[MAX_PATH+1] = {0};
|
char to_select[MAX_PATH+1] = {0};
|
||||||
bb_state_t state = {0};
|
bb_state_t state = {0};
|
||||||
memset(&state, 0, sizeof(bb_state_t));
|
memset(&state, 0, sizeof(bb_state_t));
|
||||||
|
|
||||||
tail_call:
|
tail_call:
|
||||||
|
if (!realpath(path, realpath_buf))
|
||||||
|
err("realpath failed on %s", path);
|
||||||
|
path = realloc(path, strlen(realpath_buf));
|
||||||
|
strcpy(path, realpath_buf);
|
||||||
|
|
||||||
if (state.files) {
|
if (state.files) {
|
||||||
for (int i = 0; i < state.nfiles; i++) {
|
for (int i = 0; i < state.nfiles; i++) {
|
||||||
@ -525,7 +542,7 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
size_t hashsize = 2 * state.nselected;
|
size_t hashsize = 2 * state.nselected;
|
||||||
entry_t **selecthash = calloc(hashsize, sizeof(entry_t*));
|
entry_t **selecthash = calloc(hashsize, sizeof(entry_t*));
|
||||||
if (!selecthash)
|
if (!selecthash)
|
||||||
err("Failed to allocate %d spaces for selecthash", hashsize);
|
err("Failed to allocate %ld spaces for selecthash", hashsize);
|
||||||
for (entry_t *p = state.firstselected; p; p = p->next) {
|
for (entry_t *p = state.firstselected; p; p = p->next) {
|
||||||
int probe = ((int)p->d_ino) % hashsize;
|
int probe = ((int)p->d_ino) % hashsize;
|
||||||
while (selecthash[probe])
|
while (selecthash[probe])
|
||||||
@ -596,7 +613,7 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
sort_files:
|
sort_files:
|
||||||
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_TIME) cmp = compare_date;
|
||||||
if (state.sortmethod == SORT_BITS) cmp = compare_perm;
|
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);
|
||||||
|
|
||||||
@ -644,20 +661,24 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
} else if (mouse_y >= 2 && state.scroll + (mouse_y - 2) < state.nfiles) {
|
} else if (mouse_y >= 2 && state.scroll + (mouse_y - 2) < state.nfiles) {
|
||||||
int clicked = state.scroll + (mouse_y - 2);
|
int clicked = state.scroll + (mouse_y - 2);
|
||||||
if (dt_ms > 200) {
|
if (dt_ms > 200) {
|
||||||
|
lazy = 1;
|
||||||
// Single click
|
// Single click
|
||||||
if (mouse_x == 0) {
|
if (mouse_x == 0) {
|
||||||
// Toggle
|
if (IS_SELECTED(state.files[clicked]))
|
||||||
picked = clicked;
|
deselect_file(&state, state.files[clicked]);
|
||||||
goto toggle;
|
else
|
||||||
|
select_file(&state, state.files[clicked]);
|
||||||
|
goto redraw;
|
||||||
} else {
|
} else {
|
||||||
state.cursor = clicked;
|
state.cursor = clicked;
|
||||||
lazy = 1;
|
|
||||||
goto redraw;
|
goto redraw;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Double click
|
// Double click
|
||||||
picked = clicked;
|
// TODO: hacky
|
||||||
goto open_file;
|
state.cursor = clicked;
|
||||||
|
key = '\n';
|
||||||
|
goto user_bindings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -676,9 +697,6 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
init_term();
|
init_term();
|
||||||
goto redraw;
|
goto redraw;
|
||||||
|
|
||||||
case 'q': case 'Q':
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
case KEY_MOUSE_WHEEL_DOWN:
|
case KEY_MOUSE_WHEEL_DOWN:
|
||||||
if (state.cursor >= state.nfiles - 1)
|
if (state.cursor >= state.nfiles - 1)
|
||||||
goto skip_redraw;
|
goto skip_redraw;
|
||||||
@ -697,146 +715,28 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
--state.scroll;
|
--state.scroll;
|
||||||
goto redraw;
|
goto redraw;
|
||||||
|
|
||||||
case KEY_CTRL_D: case KEY_PGDN:
|
|
||||||
if (state.cursor == state.nfiles - 1)
|
|
||||||
goto skip_redraw;
|
|
||||||
lazy = 1;
|
|
||||||
state.cursor = MIN(state.nfiles - 1, state.cursor + (height - 4) / 2);
|
|
||||||
if (state.nfiles <= height - 4)
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
state.scroll += (height - 4)/2;
|
|
||||||
if (state.scroll > state.nfiles - (height - 4) - 1)
|
|
||||||
state.scroll = state.nfiles - (height - 4) - 1;
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case KEY_CTRL_U: case KEY_PGUP:
|
|
||||||
state.cursor = MAX(0, state.cursor - (height - 4) / 2);
|
|
||||||
if (state.nfiles <= height - 4)
|
|
||||||
goto redraw;
|
|
||||||
lazy = 1;
|
|
||||||
state.scroll -= (height - 4)/2;
|
|
||||||
if (state.scroll < 0)
|
|
||||||
state.scroll = 0;
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case 'g': case KEY_HOME:
|
|
||||||
lazy = 1;
|
|
||||||
state.cursor = 0;
|
|
||||||
state.scroll = 0;
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case 'G': case KEY_END:
|
|
||||||
lazy = 1;
|
|
||||||
state.cursor = state.nfiles - 1;
|
|
||||||
if (state.nfiles > height - 4)
|
|
||||||
state.scroll = state.nfiles - (height - 4) - 1;
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case ' ':
|
|
||||||
picked = state.cursor;
|
|
||||||
toggle:
|
|
||||||
lazy = 1;
|
|
||||||
if (strcmp(state.files[picked]->d_name, "..") == 0)
|
|
||||||
goto skip_redraw;
|
|
||||||
if (IS_SELECTED(state.files[picked])) {
|
|
||||||
toggle_off:;
|
|
||||||
entry_t *e = state.files[picked];
|
|
||||||
if (e->next) e->next->atme = e->atme;
|
|
||||||
*(e->atme) = e->next;
|
|
||||||
e->next = NULL, e->atme = NULL;
|
|
||||||
--state.nselected;
|
|
||||||
} else {
|
|
||||||
toggle_on:;
|
|
||||||
entry_t *e = state.files[picked];
|
|
||||||
if (state.firstselected)
|
|
||||||
state.firstselected->atme = &e->next;
|
|
||||||
e->next = state.firstselected;
|
|
||||||
e->atme = &state.firstselected;
|
|
||||||
state.firstselected = e;
|
|
||||||
++state.nselected;
|
|
||||||
}
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case KEY_CTRL_A:
|
|
||||||
for (int i = 0; i < state.nfiles; i++) {
|
|
||||||
entry_t *e = state.files[i];
|
|
||||||
if (e->atme) continue;
|
|
||||||
if (strcmp(e->d_name, "..") == 0)
|
|
||||||
continue;
|
|
||||||
if (state.firstselected)
|
|
||||||
state.firstselected->atme = &e->next;
|
|
||||||
e->next = state.firstselected;
|
|
||||||
e->atme = &state.firstselected;
|
|
||||||
state.firstselected = e;
|
|
||||||
++state.nselected;
|
|
||||||
}
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case KEY_ESC:
|
|
||||||
clear_selection(&state);
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case KEY_F5: case KEY_CTRL_R:
|
|
||||||
strcpy(to_select, state.files[state.cursor]->d_name);
|
|
||||||
goto tail_call;
|
|
||||||
|
|
||||||
case 'j': case KEY_ARROW_DOWN:
|
|
||||||
if (state.cursor >= state.nfiles - 1)
|
|
||||||
goto skip_redraw;
|
|
||||||
lazy = 1;
|
|
||||||
++state.cursor;
|
|
||||||
if (state.cursor > state.scroll + (height - 4) - 1 - scrolloff
|
|
||||||
&& state.scroll < state.nfiles - (height - 4) - 1)
|
|
||||||
++state.scroll;
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case 'k': case KEY_ARROW_UP:
|
|
||||||
if (state.cursor <= 0)
|
|
||||||
goto skip_redraw;
|
|
||||||
lazy = 1;
|
|
||||||
--state.cursor;
|
|
||||||
if (state.cursor < state.scroll + scrolloff && state.scroll > 0)
|
|
||||||
--state.scroll;
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
case 'J':
|
case 'J':
|
||||||
if (state.cursor < state.nfiles - 1) {
|
if (state.cursor < state.nfiles - 1) {
|
||||||
if (IS_SELECTED(state.files[state.cursor])) {
|
|
||||||
picked = ++state.cursor;
|
|
||||||
if (!IS_SELECTED(state.files[picked]))
|
|
||||||
goto toggle_on;
|
|
||||||
} else {
|
|
||||||
picked = ++state.cursor;
|
|
||||||
if (IS_SELECTED(state.files[picked]))
|
|
||||||
goto toggle_off;
|
|
||||||
}
|
|
||||||
lazy = 1;
|
lazy = 1;
|
||||||
|
if (IS_SELECTED(state.files[state.cursor]))
|
||||||
|
select_file(&state, state.files[++state.cursor]);
|
||||||
|
else
|
||||||
|
deselect_file(&state, state.files[++state.cursor]);
|
||||||
goto redraw;
|
goto redraw;
|
||||||
}
|
}
|
||||||
goto skip_redraw;
|
goto skip_redraw;
|
||||||
|
|
||||||
case 'K':
|
case 'K':
|
||||||
if (state.cursor > 0) {
|
if (state.cursor > 0) {
|
||||||
if (IS_SELECTED(state.files[state.cursor])) {
|
|
||||||
picked = --state.cursor;
|
|
||||||
if (!IS_SELECTED(state.files[picked]))
|
|
||||||
goto toggle_on;
|
|
||||||
} else {
|
|
||||||
picked = --state.cursor;
|
|
||||||
if (IS_SELECTED(state.files[picked]))
|
|
||||||
goto toggle_off;
|
|
||||||
}
|
|
||||||
lazy = 1;
|
lazy = 1;
|
||||||
|
if (IS_SELECTED(state.files[state.cursor]))
|
||||||
|
select_file(&state, state.files[--state.cursor]);
|
||||||
|
else
|
||||||
|
deselect_file(&state, state.files[--state.cursor]);
|
||||||
goto redraw;
|
goto redraw;
|
||||||
}
|
}
|
||||||
goto skip_redraw;
|
goto skip_redraw;
|
||||||
|
|
||||||
case 'h': case KEY_ARROW_LEFT:
|
|
||||||
// TODO: slightly hacky, depends on ".." being at 0 (currently always true)
|
|
||||||
picked = 0;
|
|
||||||
goto open_file;
|
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
// Change sorting method:
|
// Change sorting method:
|
||||||
term_move(0, height-1);
|
term_move(0, height-1);
|
||||||
@ -875,10 +775,10 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
|
|
||||||
case 't': case 'T':
|
case 't': case 'T':
|
||||||
sort_date:
|
sort_date:
|
||||||
if (state.sortmethod == SORT_DATE)
|
if (state.sortmethod == SORT_TIME)
|
||||||
state.sort_reverse ^= 1;
|
state.sort_reverse ^= 1;
|
||||||
else {
|
else {
|
||||||
state.sortmethod = SORT_DATE;
|
state.sortmethod = SORT_TIME;
|
||||||
state.sort_reverse = 0;
|
state.sort_reverse = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -896,85 +796,12 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
|
|
||||||
case '.':
|
case '.':
|
||||||
state.showhidden ^= 1;
|
state.showhidden ^= 1;
|
||||||
|
strcpy(to_select, state.files[state.cursor]->d_name);
|
||||||
goto tail_call;
|
goto tail_call;
|
||||||
|
|
||||||
case 'l': case '\r': case KEY_ARROW_RIGHT:
|
|
||||||
picked = state.cursor;
|
|
||||||
open_file: {
|
|
||||||
if (state.files[picked]->d_isdir) {
|
|
||||||
if (strcmp(state.files[picked]->d_name, "..") == 0) {
|
|
||||||
char *p = strrchr(state.path, '/');
|
|
||||||
if (p) strcpy(to_select, p+1);
|
|
||||||
else to_select[0] = '\0';
|
|
||||||
} else to_select[0] = '\0';
|
|
||||||
|
|
||||||
char tmp[MAX_PATH+1];
|
|
||||||
if (!realpath(state.files[picked]->d_fullname, tmp))
|
|
||||||
err("realpath failed");
|
|
||||||
free(path);
|
|
||||||
path = calloc(strlen(tmp) + 1, sizeof(char));
|
|
||||||
strcpy(path, tmp);
|
|
||||||
goto tail_call;
|
|
||||||
} else {
|
|
||||||
char *name = state.files[picked]->d_name;
|
|
||||||
close_term();
|
|
||||||
pid_t child = run_cmd(NULL, NULL,
|
|
||||||
#ifdef __APPLE__
|
|
||||||
"if file -bI %s | grep '^text/' >/dev/null; then $EDITOR %s; else open %s; fi",
|
|
||||||
#else
|
|
||||||
"if file -bi %s | grep '^text/' >/dev/null; then $EDITOR %s; else xdg-open %s; fi",
|
|
||||||
#endif
|
|
||||||
name, name, name);
|
|
||||||
waitpid(child, NULL, 0);
|
|
||||||
init_term();
|
|
||||||
goto redraw;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'f': case '/': {
|
|
||||||
close_term();
|
|
||||||
int fd;
|
|
||||||
if (state.showhidden)
|
|
||||||
run_cmd(&fd, NULL, "find | cut -d/ -f2- | " PROG_FUZZY);
|
|
||||||
else
|
|
||||||
run_cmd(&fd, NULL, "find -not -name '\\.*' -not -path '*/\\.*' | cut -d/ -f2- | " PROG_FUZZY);
|
|
||||||
int len = 0, space = MAX_PATH, consumed;
|
|
||||||
while ((consumed = read(fd, &to_select[len], space)) > 0) {
|
|
||||||
if (consumed < 0) err("Error reading selection");
|
|
||||||
to_select[len + consumed] = '\0';
|
|
||||||
char *nl = strchr(&to_select[len], '\n');
|
|
||||||
if (nl) {
|
|
||||||
*nl = '\0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
len += consumed;
|
|
||||||
space -= consumed;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
init_term();
|
|
||||||
|
|
||||||
if (to_select[0] == '\0')
|
|
||||||
goto redraw;
|
|
||||||
|
|
||||||
char *pathend = strrchr(to_select, '/');
|
|
||||||
if (pathend) {
|
|
||||||
char *newpath = calloc(strlen(path) + 1 + (pathend - to_select), 1);
|
|
||||||
strcpy(newpath, path);
|
|
||||||
strcat(newpath, "/");
|
|
||||||
strncat(newpath, to_select, pathend - to_select);
|
|
||||||
free(path);
|
|
||||||
path = newpath;
|
|
||||||
strcpy(to_select, pathend + 1);
|
|
||||||
}
|
|
||||||
goto tail_call;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '?': {
|
case '?': {
|
||||||
close_term();
|
char tmpname[] = "/tmp/bb_help.XXXXXX";
|
||||||
int fd;
|
int fd = mkstemp(tmpname);
|
||||||
pid_t child = run_cmd(NULL, &fd, "less -r");
|
|
||||||
|
|
||||||
writez(fd, "\n \e[33;1mKey Bindings:\e[0m\n");
|
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;
|
if (bindings[i].key > 0) continue;
|
||||||
@ -990,17 +817,27 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
writez(fd, "\n \e[33;1mScript Key Bindings:\e[0m\n");
|
writez(fd, "\n \e[33;1mScript Key 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;
|
if (bindings[i].key <= 0) continue;
|
||||||
writez(fd, "\e[1m");
|
writez(fd, "\e[1m ");
|
||||||
char buf[] = " X \e[0m";
|
char buf[16];
|
||||||
*strchr(buf, 'X') = bindings[i].key;
|
buf[0] = bindings[i].key;
|
||||||
writez(fd, buf);
|
if (' ' <= buf[0] && buf[0] <= '~')
|
||||||
writez(fd, bindings[i].command);
|
write(fd, buf, 1);
|
||||||
|
else {
|
||||||
|
sprintf(buf, "\e[31m%04X", bindings[i].key);
|
||||||
|
writez(fd, buf);
|
||||||
|
}
|
||||||
|
writez(fd, " \e[0m\t");
|
||||||
|
write_escaped(fd, bindings[i].command, strlen(bindings[i].command), "\e[0m");
|
||||||
writez(fd, "\e[0m\n");
|
writez(fd, "\e[0m\n");
|
||||||
}
|
}
|
||||||
writez(fd, "\n");
|
writez(fd, "\n");
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
waitpid(child, NULL, 0);
|
|
||||||
|
close_term();
|
||||||
|
char cmd[256];
|
||||||
|
sprintf(cmd, "less -r %s", tmpname);
|
||||||
|
system(cmd);
|
||||||
|
unlink(tmpname);
|
||||||
init_term();
|
init_term();
|
||||||
goto redraw;
|
goto redraw;
|
||||||
}
|
}
|
||||||
@ -1010,36 +847,182 @@ static void explore(char *path, int print_dir, int print_selection, char sep)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// Search user-defined key bindings from config.h:
|
// Search user-defined key bindings from config.h:
|
||||||
|
user_bindings:
|
||||||
for (int i = 0; bindings[i].key > 0; i++) {
|
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);
|
||||||
writez(termfd, "\e[K");
|
//writez(termfd, "\e[K");
|
||||||
|
|
||||||
struct termios cur_tios;
|
struct termios cur_tios;
|
||||||
if (!(bindings[i].flags & ONSCREEN)) {
|
if (bindings[i].flags & NORMAL_TERM) {
|
||||||
close_term();
|
close_term();
|
||||||
} else {
|
} else {
|
||||||
|
{ // Restore scrolling region
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "\e[1;%dr", height);
|
||||||
|
writez(termfd, buf);
|
||||||
|
}
|
||||||
|
/*
|
||||||
tcgetattr(termfd, &cur_tios);
|
tcgetattr(termfd, &cur_tios);
|
||||||
tcsetattr(termfd, TCSAFLUSH, &orig_termios);
|
tcsetattr(termfd, TCSAFLUSH, &orig_termios);
|
||||||
writez(termfd, "\e[?25h"); // Show cursor
|
writez(termfd, "\e[?25h"); // Show cursor
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
run_cmd_on_selection(&state, bindings[i].command);
|
run_cmd_on_selection(&state, bindings[i].command);
|
||||||
|
|
||||||
if (!(bindings[i].flags & ONSCREEN)) {
|
if (bindings[i].flags & NORMAL_TERM) {
|
||||||
|
lazy = 0;
|
||||||
init_term();
|
init_term();
|
||||||
} else {
|
} else {
|
||||||
|
lazy = 1;
|
||||||
|
/*
|
||||||
tcsetattr(termfd, TCSAFLUSH, &cur_tios);
|
tcsetattr(termfd, TCSAFLUSH, &cur_tios);
|
||||||
writez(termfd, "\e[?25l"); // Hide cursor
|
writez(termfd, "\e[?25l"); // Hide cursor
|
||||||
|
*/
|
||||||
|
{ // Restore scrolling region
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "\e[3;%dr", height-1);
|
||||||
|
writez(termfd, buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bindings[i].flags & CLEAR_SELECTION)
|
// Scan for IPC requests
|
||||||
clear_selection(&state);
|
int needs_full_refresh = 0;
|
||||||
|
char newsort[3] = "";
|
||||||
|
FILE *tmpfile;
|
||||||
|
if ((tmpfile = fopen(bb_tmpfile, "r"))) {
|
||||||
|
char *line = NULL;
|
||||||
|
size_t capacity = 0;
|
||||||
|
ssize_t len;
|
||||||
|
while ((len = getline(&line, &capacity, tmpfile)) >= 0) {
|
||||||
|
if (len > 0 && line[len-1] == '\n') line[--len] = '\0';
|
||||||
|
if (strcmp(line, "refresh") == 0) {
|
||||||
|
needs_full_refresh = 1;
|
||||||
|
} else if (strcmp(line, "quit") == 0) {
|
||||||
|
goto done;
|
||||||
|
} else if (startswith(line, "sort:") && len >= 7) {
|
||||||
|
newsort[0] = line[strlen("sort:")];
|
||||||
|
newsort[1] = line[strlen("sort:") + 1];
|
||||||
|
} else if (startswith(line, "cd:")) {
|
||||||
|
free(path);
|
||||||
|
path = calloc(strlen(line+strlen("cd:")) + 1, sizeof(char));
|
||||||
|
strcpy(path, line+strlen("cd:"));
|
||||||
|
goto tail_call;
|
||||||
|
} else if (startswith(line, "toggle:")) {
|
||||||
|
lazy = 0;
|
||||||
|
entry_t *e = find_file(&state, line + strlen("select:"));
|
||||||
|
if (e) {
|
||||||
|
if (IS_SELECTED(e)) deselect_file(&state, e);
|
||||||
|
else select_file(&state, e);
|
||||||
|
}
|
||||||
|
} else if (startswith(line, "select:")) {
|
||||||
|
char *name = line + strlen("select:");
|
||||||
|
lazy = 0;
|
||||||
|
if (strcmp(name, "*") == 0) {
|
||||||
|
for (int i = 0; i < state.nfiles; i++)
|
||||||
|
select_file(&state, state.files[i]);
|
||||||
|
} else {
|
||||||
|
entry_t *e = find_file(&state, name);
|
||||||
|
if (e) select_file(&state, e);
|
||||||
|
}
|
||||||
|
} else if (startswith(line, "deselect:")) {
|
||||||
|
char *name = line + strlen("deselect:");
|
||||||
|
lazy = 0;
|
||||||
|
if (strcmp(name, "*") == 0) {
|
||||||
|
clear_selection(&state);
|
||||||
|
} else {
|
||||||
|
entry_t *e = find_file(&state, name);
|
||||||
|
if (e) select_file(&state, e);
|
||||||
|
}
|
||||||
|
} else if (startswith(line, "cursor:")) {
|
||||||
|
char *name = line + strlen("cursor:");
|
||||||
|
for (int i = 0; i < state.nfiles; i++) {
|
||||||
|
if (strcmp(name[0] == '/' ?
|
||||||
|
state.files[i]->d_fullname : state.files[i]->d_name,
|
||||||
|
name) == 0) {
|
||||||
|
state.cursor = i;
|
||||||
|
goto found_it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
char *lastslash = strrchr(name, '/');
|
||||||
|
if (!lastslash) goto found_it;
|
||||||
|
size_t len = lastslash - name;
|
||||||
|
path = calloc(len + 1, sizeof(char));
|
||||||
|
memcpy(path, name, len);
|
||||||
|
strcpy(to_select, lastslash+1);
|
||||||
|
goto tail_call;
|
||||||
|
found_it:;
|
||||||
|
} else if (startswith(line, "move:")) {
|
||||||
|
char *value = line + strlen("move:");
|
||||||
|
int oldcur = state.cursor;
|
||||||
|
int isabs = value[0] != '-' && value[0] != '+';
|
||||||
|
long delta = strtol(value, &value, 10);
|
||||||
|
if (*value == '%') delta = (delta * height)/100;
|
||||||
|
if (isabs) state.cursor = delta;
|
||||||
|
else state.cursor += delta;
|
||||||
|
|
||||||
if (bindings[i].flags & REFRESH) {
|
state.cursor = clamped(state.cursor, 0, state.nfiles-1);
|
||||||
|
delta = state.cursor - oldcur;
|
||||||
|
|
||||||
|
if (state.nfiles > height-4) {
|
||||||
|
if (delta > 0) {
|
||||||
|
if (state.cursor >= state.scroll + (height-4) - scrolloff)
|
||||||
|
state.scroll += delta;
|
||||||
|
} else if (delta < 0) {
|
||||||
|
if (state.cursor <= state.scroll + scrolloff)
|
||||||
|
state.scroll += delta;
|
||||||
|
}
|
||||||
|
//int target = clamped(state.scroll, state.cursor - (height-4) + scrolloff, state.cursor - scrolloff);
|
||||||
|
//state.scroll += (delta > 0 ? 1 : -1)*MIN(abs(target-state.scroll), abs((int)delta));
|
||||||
|
//state.scroll = target;
|
||||||
|
state.scroll = clamped(state.scroll, state.cursor - (height-4) + 1, state.cursor);
|
||||||
|
state.scroll = clamped(state.scroll, 0, state.nfiles-1 - (height-4));
|
||||||
|
}
|
||||||
|
} else if (startswith(line, "scroll:")) {
|
||||||
|
char *value = line + strlen("scroll:");
|
||||||
|
int oldscroll = state.scroll;
|
||||||
|
int isabs = value[0] != '-' && value[0] != '+';
|
||||||
|
long delta = strtol(value, &value, 10);
|
||||||
|
if (*value == '%') delta = (delta * height)/100;
|
||||||
|
|
||||||
|
if (state.nfiles > height-4) {
|
||||||
|
if (isabs) state.scroll = delta;
|
||||||
|
else state.scroll += delta;
|
||||||
|
state.scroll = clamped(state.scroll, 0, state.nfiles-1 - (height-4));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.cursor = clamped(state.cursor + delta, 0, state.nfiles-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line) free(line);
|
||||||
|
fclose(tmpfile);
|
||||||
|
unlink(bb_tmpfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortmethod_t oldmethod = state.sortmethod;
|
||||||
|
int oldreverse = state.sort_reverse;
|
||||||
|
if (newsort[0] == '+')
|
||||||
|
state.sort_reverse = 0;
|
||||||
|
else if (newsort[0] == '+')
|
||||||
|
state.sort_reverse = 1;
|
||||||
|
switch (newsort[1]) {
|
||||||
|
case 'a': state.sortmethod = SORT_ALPHA; break;
|
||||||
|
case 's': state.sortmethod = SORT_SIZE; break;
|
||||||
|
case 't': state.sortmethod = SORT_TIME; break;
|
||||||
|
case 'p': state.sortmethod = SORT_BITS; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needs_full_refresh) {
|
||||||
strcpy(to_select, state.files[state.cursor]->d_name);
|
strcpy(to_select, state.files[state.cursor]->d_name);
|
||||||
goto tail_call;
|
goto tail_call;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.sortmethod != oldmethod || oldreverse != state.sort_reverse)
|
||||||
|
goto sort_files;
|
||||||
|
|
||||||
goto redraw;
|
goto redraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1058,7 +1041,6 @@ done:
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char _realpath[MAX_PATH+1];
|
|
||||||
char *path = ".";
|
char *path = ".";
|
||||||
char sep = '\n';
|
char sep = '\n';
|
||||||
int print_dir = 0, print_selection = 0;
|
int print_dir = 0, print_selection = 0;
|
||||||
@ -1069,6 +1051,17 @@ int main(int argc, char *argv[])
|
|||||||
printf("Usage: bb [-h/--help] [-s] [-b] [-0] [path]\n");
|
printf("Usage: bb [-h/--help] [-s] [-b] [-0] [path]\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (strcmp(argv[i], "-c") == 0) {
|
||||||
|
char *bb_cmdfile = getenv("BBCMD");
|
||||||
|
if (!bb_cmdfile)
|
||||||
|
err("Can only execute bb commands from inside bb");
|
||||||
|
FILE *f = fopen(bb_cmdfile, "w");
|
||||||
|
for (i = i+1; i < argc; i++) {
|
||||||
|
fprintf(f, "%s\n", argv[i]);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argv[i][0] == '-' && argv[i][1] == '-')
|
if (argv[i][0] == '-' && argv[i][1] == '-')
|
||||||
continue;
|
continue;
|
||||||
if (argv[i][0] == '-') {
|
if (argv[i][0] == '-') {
|
||||||
@ -1094,8 +1087,6 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
init_term();
|
init_term();
|
||||||
if (!realpath(path, _realpath))
|
explore(path, print_dir, print_selection, sep);
|
||||||
err("realpath failed");
|
|
||||||
explore(_realpath, print_dir, print_selection, sep);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
76
config.def.h
76
config.def.h
@ -8,9 +8,7 @@
|
|||||||
#define AND_PAUSE " && read -n1 -p '\n\e[2m...press any key to continue...\e[0m\e[?25l'"
|
#define AND_PAUSE " && read -n1 -p '\n\e[2m...press any key to continue...\e[0m\e[?25l'"
|
||||||
#define SCROLLOFF 5
|
#define SCROLLOFF 5
|
||||||
|
|
||||||
#define REFRESH (1<<0)
|
#define NORMAL_TERM (1<<0)
|
||||||
#define CLEAR_SELECTION (1<<1)
|
|
||||||
#define ONSCREEN (1<<2)
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int key;
|
int key;
|
||||||
@ -22,19 +20,65 @@ struct {
|
|||||||
// Please note that these are sh scripts, not bash scripts, so bash-isms
|
// Please note that these are sh scripts, not bash scripts, so bash-isms
|
||||||
// won't work unless you make your script use `bash -c "<your script>"`
|
// won't work unless you make your script use `bash -c "<your script>"`
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
{'e', "$EDITOR \"$@\""},
|
{'e', "$EDITOR \"$@\"", NORMAL_TERM},
|
||||||
{'L', PIPE_SELECTION_TO "less"},
|
{'L', PIPE_SELECTION_TO "less", NORMAL_TERM},
|
||||||
{'D', "rm -rf \"$@\"", CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'D', "rm -rf \"$@\"; bb -c 'deselect:*' refresh"},
|
||||||
{'d', "rm -rfi \"$@\"", CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'d', "rm -rfi \"$@\"; bb -c 'deselect:*' refresh"},
|
||||||
{'m', "mv -i \"$@\" .", CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'m', "mv -i \"$@\" .; bb -c 'deselect:*' refresh"},
|
||||||
{'c', "cp -i \"$@\" .", CLEAR_SELECTION | REFRESH | ONSCREEN},
|
{'c', "cp -i \"$@\" .; bb -c refresh"},
|
||||||
{'C', "for f; do cp \"$f\" \"$f.copy\"; done", REFRESH | ONSCREEN},
|
{'C', "for f; do cp \"$f\" \"$f.copy\"; done; bb -c refresh"},
|
||||||
{'n', "read -p '\e[33;1mNew file:\e[0m ' name && touch \"$name\"", ONSCREEN | REFRESH},
|
{'n', "read -p '\e[33;1mNew file:\e[0m \e[K' name && touch \"$name\"; bb -c refresh"},
|
||||||
{'N', "read -p '\e[33;1mNew dir:\e[0m ' name && mkdir \"$name\"", ONSCREEN | REFRESH},
|
{'N', "read -p '\e[33;1mNew dir:\e[0m \e[K' name && mkdir \"$name\"; bb -c refresh"},
|
||||||
{'|', "read -p \"\e[33;1m>\e[0m \" cmd && " PIPE_SELECTION_TO "$SHELL -c \"$cmd\"" AND_PAUSE, REFRESH},
|
{'|', "read -p '\e[33;1m|>\e[0m \e[K' cmd && " PIPE_SELECTION_TO "$SHELL -c \"$cmd\"" AND_PAUSE},
|
||||||
{'>', "$SHELL", REFRESH},
|
{':', "read -p '\e[33;1m:>\e[0m \e[K' cmd && $SHELL -c \"$cmd\" -- \"$@\"" AND_PAUSE},
|
||||||
{'r', "for f; do read -p \"Rename $f: \" renamed && mv \"$f\" \"$renamed\"; done",
|
{'>', "$SHELL", NORMAL_TERM},
|
||||||
REFRESH | CLEAR_SELECTION | ONSCREEN},
|
{'r', "for f; do read -p \"Rename $f: \e[K\" renamed && mv \"$f\" \"$renamed\"; done;"
|
||||||
|
" bb -c 'deselect:*' refresh"},
|
||||||
|
|
||||||
|
|
||||||
|
{'h', "bb -c \"cd:..\""},
|
||||||
|
{KEY_ARROW_LEFT, "bb -c 'cd:..'"},
|
||||||
|
{'j', "bb -c 'move:+1'"},
|
||||||
|
{KEY_ARROW_DOWN, "bb -c 'move:+1'"},
|
||||||
|
{'k', "bb -c 'move:-1'"},
|
||||||
|
{KEY_ARROW_UP, "bb -c 'move:-1'"},
|
||||||
|
|
||||||
|
{'l', "bb -c \"cd:$BBFULLCURSOR\""},
|
||||||
|
{KEY_ARROW_RIGHT, "bb -c \"cd:$BBFULLCURSOR\""},
|
||||||
|
#ifdef __APPLE__
|
||||||
|
{'\r', "if test -x \"$BBCURSOR\"; then \"$BBCURSOR\"; "
|
||||||
|
"elif test -d \"$BBCURSOR\"; then bb -c \"cd:$BBFULLCURSOR\"; "
|
||||||
|
"elif file -bI \"$BBCURSOR\" | grep '^text/' >/dev/null; then $EDITOR \"$BBCURSOR\"; "
|
||||||
|
"else open \"$BBCURSOR\"; fi"},
|
||||||
|
#else
|
||||||
|
{'\r', "if test -x \"$BBCURSOR\"; then \"$BBCURSOR\"; "
|
||||||
|
"elif test -d \"$BBCURSOR\"; then bb -c \"cd:$BBFULLCURSOR\"; "
|
||||||
|
"elif file -bi \"$BBCURSOR\" | grep '^text/' >/dev/null; then $EDITOR \"$BBCURSOR\"; "
|
||||||
|
"else xdg-open \"$BBCURSOR\"; fi"},
|
||||||
|
#endif
|
||||||
|
{' ', "bb -c \"toggle:$BBCURSOR\""},
|
||||||
|
//{-1, "J\t\e[0;34mMove selection state down\e[0m"},
|
||||||
|
//{-1, "K\t\e[0;34mMove selection state up\e[0m"},
|
||||||
|
{'q', "bb -c quit"},
|
||||||
|
{'Q', "bb -c quit"},
|
||||||
|
{'g', "bb -c move:0"},
|
||||||
|
{KEY_HOME, "bb -c move:0"},
|
||||||
|
{'G', "bb -c move:9999999999"},
|
||||||
|
{KEY_END, "bb -c move:999999999"},
|
||||||
|
{'f', "bb -c \"cursor:`fzf`\"", NORMAL_TERM},
|
||||||
|
{'/', "bb -c \"cursor:`ls -a|fzf`\"", NORMAL_TERM},
|
||||||
|
{KEY_ESC, "bb -c 'deselect:*'"},
|
||||||
|
{KEY_F5, "bb -c refresh"},
|
||||||
|
{KEY_CTRL_R, "bb -c refresh"},
|
||||||
|
{KEY_CTRL_A, "bb -c 'select:*'"},
|
||||||
|
//{-1, "Ctrl-C\t\e[0;34mAbort and exit\e[0m"},
|
||||||
|
{KEY_PGDN, "bb -c 'scroll:+100%'"},
|
||||||
|
{KEY_CTRL_D, "bb -c 'scroll:+50%'"},
|
||||||
|
{KEY_PGUP, "bb -c 'scroll:-100%'"},
|
||||||
|
{KEY_CTRL_U, "bb -c 'scroll:-50%'"},
|
||||||
|
//{-1, "Ctrl-Z\t\e[0;34mSuspend\e[0m"},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Hard-coded behaviors (these are just placeholders for the help):
|
// Hard-coded behaviors (these are just placeholders for the help):
|
||||||
{-1, "?\t\e[0;34mOpen help menu\e[0m"},
|
{-1, "?\t\e[0;34mOpen help menu\e[0m"},
|
||||||
|
Loading…
Reference in New Issue
Block a user