Mostly finalized working version of better signal handling, including
allowing for suspended/resuming processes (bb +fg) and cleaner linked list handling code.
This commit is contained in:
parent
dc6ecc4e7f
commit
55d3f0e160
3
API.md
3
API.md
@ -45,9 +45,9 @@ is one of the following commands (or a unique prefix of one):
|
||||
- `columns:<columns>` Change which columns are visible, and in what order
|
||||
- `deselect:<filename>` Deselect <filename>
|
||||
- `dotfiles:[01]` Whether dotfiles are visible
|
||||
- `fg:[num]` Send a background process to the foreground
|
||||
- `goto:<filename>` Move the cursor to <filename> (changing directory if needed)
|
||||
- `interleave:[01]` Whether or not directories should be interleaved with files in the display
|
||||
- `kill` Exit with failure
|
||||
- `move:<num*>` Move the cursor a numeric amount
|
||||
- `quit` Quit bb
|
||||
- `refresh` Refresh the file listing
|
||||
@ -55,7 +55,6 @@ is one of the following commands (or a unique prefix of one):
|
||||
- `select:<filename>` Select <filename>
|
||||
- `sort:([+-]method)+` Set sorting method (+: normal, -: reverse), additional methods act as tiebreaker
|
||||
- `spread:<num*>` Spread the selection state at the cursor
|
||||
- `suspend` Suspend `bb` (SIGSTP signal)
|
||||
- `toggle:<filename>` Toggle the selection status of <filename>
|
||||
|
||||
For any of these commands (e.g. `+select`), an empty parameter followed by
|
||||
|
232
bb.c
232
bb.c
@ -13,6 +13,8 @@ static struct termios orig_termios, bb_termios;
|
||||
static FILE *tty_out = NULL, *tty_in = NULL;
|
||||
static int termwidth, termheight;
|
||||
static char *cmdfilename = NULL;
|
||||
proc_t *running_procs = NULL;
|
||||
static int nprocs = 0;
|
||||
|
||||
/*
|
||||
* Use bb to browse the filesystem.
|
||||
@ -43,6 +45,7 @@ void bb_browse(bb_t *bb)
|
||||
force_check_cmds:
|
||||
cmdfile = fopen(cmdfilename, "r");
|
||||
if (!cmdfile) {
|
||||
cmdpos = 0;
|
||||
if (bb->dirty) goto redraw;
|
||||
goto get_keyboard_input;
|
||||
}
|
||||
@ -119,6 +122,7 @@ void bb_browse(bb_t *bb)
|
||||
run_bbcmd(bb, binding->script);
|
||||
} else {
|
||||
move_cursor(tty_out, 0, termheight-1);
|
||||
fputs("\033[K" T_ON(T_SHOW_CURSOR), tty_out);
|
||||
restore_term(&default_termios);
|
||||
run_script(bb, binding->script);
|
||||
init_term();
|
||||
@ -128,21 +132,23 @@ void bb_browse(bb_t *bb)
|
||||
}
|
||||
|
||||
/*
|
||||
* Close safely in a way that doesn't gunk up the terminal.
|
||||
* Clean up the terminal before going to the default signal handling behavior.
|
||||
*/
|
||||
void cleanup_and_exit(int sig)
|
||||
void cleanup_and_raise(int sig)
|
||||
{
|
||||
static volatile sig_atomic_t error_in_progress = 0;
|
||||
if (error_in_progress)
|
||||
raise(sig);
|
||||
error_in_progress = 1;
|
||||
cleanup();
|
||||
signal(sig, SIG_DFL);
|
||||
int childsig = (sig == SIGTSTP || sig == SIGSTOP) ? sig : SIGHUP;
|
||||
for (proc_t *p = running_procs; p; p = p->running.next)
|
||||
kill(p->pid, childsig);
|
||||
raise(sig);
|
||||
// This code will only ever be run if sig is SIGTSTP/SIGSTOP, otherwise, raise() won't return:
|
||||
init_term();
|
||||
struct sigaction sa = {.sa_handler = &cleanup_and_raise, .sa_flags = SA_NODEFER | SA_RESETHAND};
|
||||
sigaction(sig, &sa, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the terminal, reset the screen, and delete the cmdfile
|
||||
* Reset the screen and delete the cmdfile
|
||||
*/
|
||||
void cleanup(void)
|
||||
{
|
||||
@ -151,9 +157,9 @@ void cleanup(void)
|
||||
free(cmdfilename);
|
||||
cmdfilename = NULL;
|
||||
}
|
||||
if (tty_out)
|
||||
fputs(T_OFF(T_ALT_SCREEN) T_ON(T_SHOW_CURSOR), tty_out);
|
||||
restore_term(&orig_termios);
|
||||
fputs(T_OFF(T_ALT_SCREEN) T_ON(T_SHOW_CURSOR), tty_out);
|
||||
fflush(tty_out);
|
||||
tcsetattr(fileno(tty_out), TCSANOW, &orig_termios);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -264,23 +270,12 @@ int fputs_escaped(FILE *f, const char *str, const char *color)
|
||||
*/
|
||||
void init_term(void)
|
||||
{
|
||||
static int first_time = 1;
|
||||
if (!tty_in) tty_in = fopen("/dev/tty", "r");
|
||||
if (!tty_out) tty_out = fopen("/dev/tty", "w");
|
||||
if (first_time) {
|
||||
tcgetattr(fileno(tty_out), &orig_termios);
|
||||
first_time = 0;
|
||||
}
|
||||
memcpy(&bb_termios, &orig_termios, sizeof(bb_termios));
|
||||
cfmakeraw(&bb_termios);
|
||||
bb_termios.c_cc[VMIN] = 0;
|
||||
bb_termios.c_cc[VTIME] = 1;
|
||||
if (tcsetattr(fileno(tty_out), TCSAFLUSH, &bb_termios) == -1)
|
||||
if (tcsetattr(fileno(tty_out), TCSANOW, &bb_termios) == -1)
|
||||
err("Couldn't tcsetattr");
|
||||
update_term_size(0);
|
||||
signal(SIGWINCH, update_term_size);
|
||||
// Initiate mouse tracking and disable text wrapping:
|
||||
fputs(T_ENTER_BBMODE, tty_out);
|
||||
fflush(tty_out);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -350,10 +345,7 @@ entry_t* load_entry(bb_t *bb, const char *path, int clear_dots)
|
||||
if (S_ISLNK(filestat.st_mode))
|
||||
entry->linkedmode = linkedstat.st_mode;
|
||||
entry->info = filestat;
|
||||
if (bb->hash[(int)filestat.st_ino & HASH_MASK])
|
||||
bb->hash[(int)filestat.st_ino & HASH_MASK]->hash.atme = &entry->hash.next;
|
||||
entry->hash.next = bb->hash[(int)filestat.st_ino & HASH_MASK];
|
||||
entry->hash.atme = &bb->hash[(int)filestat.st_ino & HASH_MASK];
|
||||
LL_PREPEND(bb->hash[(int)filestat.st_ino & HASH_MASK], entry, hash);
|
||||
entry->index = -1;
|
||||
bb->hash[(int)filestat.st_ino & HASH_MASK] = entry;
|
||||
return entry;
|
||||
@ -647,10 +639,24 @@ void run_bbcmd(bb_t *bb, const char *cmd)
|
||||
populate_files(bb, bb->path);
|
||||
} else if (matches_cmd(cmd, "execute:")) { // +execute:
|
||||
move_cursor(tty_out, 0, termheight-1);
|
||||
fputs(T_ON(T_SHOW_CURSOR), tty_out);
|
||||
fputs("\033[K" T_ON(T_SHOW_CURSOR), tty_out);
|
||||
restore_term(&default_termios);
|
||||
run_script(bb, value);
|
||||
init_term();
|
||||
} else if (matches_cmd(cmd, "fg:") || matches_cmd(cmd, "fg")) { // +fg:
|
||||
int fg = value ? nprocs - (int)strtol(value, NULL, 10) : 0;
|
||||
proc_t *child = NULL;
|
||||
for (proc_t *p = running_procs; p && !child; p = p->running.next) {
|
||||
if (fg-- == 0) child = p;
|
||||
}
|
||||
if (!child) return;
|
||||
move_cursor(tty_out, 0, termheight-1);
|
||||
fputs("\033[K", tty_out);
|
||||
restore_term(&default_termios);
|
||||
kill(-(child->pid), SIGCONT);
|
||||
wait_for_process(&child);
|
||||
init_term();
|
||||
bb->dirty = 1;
|
||||
} else if (matches_cmd(cmd, "goto:")) { // +goto:
|
||||
entry_t *e = load_entry(bb, value, 1);
|
||||
if (!e) goto invalid_cmd;
|
||||
@ -671,27 +677,29 @@ void run_bbcmd(bb_t *bb, const char *cmd)
|
||||
restore_term(&default_termios);
|
||||
int fds[2];
|
||||
pipe(fds);
|
||||
pid_t child = fork();
|
||||
void (*old_handler)(int) = signal(SIGINT, SIG_IGN);
|
||||
if (child != 0) {
|
||||
close(fds[0]);
|
||||
print_bindings(fds[1]);
|
||||
close(fds[1]);
|
||||
waitpid(child, NULL, 0);
|
||||
} else {
|
||||
proc_t *proc = memcheck(calloc(1, sizeof(proc_t)));
|
||||
if ((proc->pid = fork()) == 0) {
|
||||
fclose(tty_out); tty_out = NULL;
|
||||
fclose(tty_in); tty_in = NULL;
|
||||
setpgid(0, 0);
|
||||
close(fds[1]);
|
||||
dup2(fds[0], STDIN_FILENO);
|
||||
char *args[] = {SH, "-c", "$PAGER -rX", NULL};
|
||||
char *args[] = {SH, "-c", "less -rKX", NULL};
|
||||
execvp(SH, args);
|
||||
}
|
||||
LL_PREPEND(running_procs, proc, running);
|
||||
++nprocs;
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
tcsetpgrp(fileno(tty_out), proc->pid);
|
||||
close(fds[0]);
|
||||
print_bindings(fds[1]);
|
||||
close(fds[1]);
|
||||
wait_for_process(&proc);
|
||||
init_term();
|
||||
signal(SIGINT, old_handler);
|
||||
bb->dirty = 1;
|
||||
} else if (matches_cmd(cmd, "interleave:") || matches_cmd(cmd, "interleave")) { // +interleave
|
||||
set_bool(bb->interleave_dirs);
|
||||
sort_files(bb);
|
||||
} else if (matches_cmd(cmd, "kill")) { // +kill
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (matches_cmd(cmd, "move:")) { // +move:
|
||||
int oldcur, isdelta, n;
|
||||
move:
|
||||
@ -732,12 +740,6 @@ void run_bbcmd(bb_t *bb, const char *cmd)
|
||||
sort_files(bb);
|
||||
} else if (matches_cmd(cmd, "spread:")) { // +spread:
|
||||
goto move;
|
||||
} else if (matches_cmd(cmd, "suspend")) { // +suspend
|
||||
fputs(T_LEAVE_BBMODE, tty_out);
|
||||
restore_term(&orig_termios);
|
||||
raise(SIGTSTP);
|
||||
init_term();
|
||||
bb->dirty = 1;
|
||||
} else if (matches_cmd(cmd, "toggle:") || matches_cmd(cmd, "toggle")) { // +toggle
|
||||
if (!value && !bb->nfiles) return;
|
||||
if (!value) value = bb->files[bb->cursor]->fullname;
|
||||
@ -746,11 +748,7 @@ void run_bbcmd(bb_t *bb, const char *cmd)
|
||||
set_selected(bb, e, !IS_SELECTED(e));
|
||||
} else {
|
||||
invalid_cmd:
|
||||
if (tty_out) fputs(T_LEAVE_BBMODE, tty_out);
|
||||
restore_term(&orig_termios);
|
||||
fprintf(stderr, "Invalid bb command: +%s", cmd);
|
||||
fprintf(stderr, "\n");
|
||||
init_term();
|
||||
warn("Invalid bb command: +%s.", cmd);
|
||||
bb->dirty = 1;
|
||||
}
|
||||
}
|
||||
@ -936,17 +934,24 @@ void render(bb_t *bb)
|
||||
fputs(" \033[K\033[0m", tty_out); // Reset color and attributes
|
||||
}
|
||||
|
||||
if (bb->firstselected) {
|
||||
move_cursor(tty_out, termwidth/2, termheight - 1);
|
||||
fputs("\033[0m\033[K", tty_out);
|
||||
int x = termwidth;
|
||||
if (bb->firstselected) { // Number of selected files
|
||||
int n = 0;
|
||||
for (entry_t *s = bb->firstselected; s; s = s->selected.next) ++n;
|
||||
int x = termwidth - 14;
|
||||
x -= 14;
|
||||
for (int k = n; k; k /= 10) x--;
|
||||
move_cursor(tty_out, MAX(0, x), termheight - 1);
|
||||
fprintf(tty_out, "\033[41;30m %d Selected \033[0m", n);
|
||||
} else {
|
||||
move_cursor(tty_out, termwidth/2, termheight - 1);
|
||||
fputs("\033[0m\033[K", tty_out);
|
||||
}
|
||||
if (nprocs > 0) { // Number of suspended processes
|
||||
x -= 13;
|
||||
for (int k = nprocs; k; k /= 10) x--;
|
||||
move_cursor(tty_out, MAX(0, x), termheight - 1);
|
||||
fprintf(tty_out, "\033[44;30m %d Suspended \033[0m", nprocs);
|
||||
}
|
||||
move_cursor(tty_out, termwidth/2, termheight - 1);
|
||||
|
||||
lastcursor = bb->cursor;
|
||||
lastscroll = bb->scroll;
|
||||
@ -958,16 +963,9 @@ void render(bb_t *bb)
|
||||
*/
|
||||
void restore_term(const struct termios *term)
|
||||
{
|
||||
if (tty_out) {
|
||||
tcsetattr(fileno(tty_out), TCSAFLUSH, term);
|
||||
fputs(T_LEAVE_BBMODE_PARTIAL, tty_out);
|
||||
fflush(tty_out);
|
||||
fclose(tty_out);
|
||||
tty_out = NULL;
|
||||
fclose(tty_in);
|
||||
tty_in = NULL;
|
||||
}
|
||||
signal(SIGWINCH, SIG_DFL);
|
||||
tcsetattr(fileno(tty_out), TCSANOW, term);
|
||||
fputs(T_LEAVE_BBMODE_PARTIAL, tty_out);
|
||||
fflush(tty_out);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -981,10 +979,11 @@ int run_script(bb_t *bb, const char *cmd)
|
||||
strcpy(fullcmd, bbcmdfn);
|
||||
strcat(fullcmd, cmd);
|
||||
|
||||
pid_t child;
|
||||
void (*old_handler)(int) = signal(SIGINT, SIG_IGN);
|
||||
if ((child = fork()) == 0) {
|
||||
signal(SIGINT, SIG_DFL);
|
||||
proc_t *proc = memcheck(calloc(1, sizeof(proc_t)));
|
||||
if ((proc->pid = fork()) == 0) {
|
||||
fclose(tty_out); tty_out = NULL;
|
||||
fclose(tty_in); tty_in = NULL;
|
||||
setpgid(0, 0);
|
||||
// TODO: is there a max number of args? Should this be batched?
|
||||
size_t space = 32;
|
||||
char **args = memcheck(calloc(space, sizeof(char*)));
|
||||
@ -1011,13 +1010,12 @@ int run_script(bb_t *bb, const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (child == -1)
|
||||
if (proc->pid == -1)
|
||||
err("Failed to fork");
|
||||
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
signal(SIGINT, old_handler);
|
||||
|
||||
LL_PREPEND(running_procs, proc, running);
|
||||
++nprocs;
|
||||
int status = wait_for_process(&proc);
|
||||
bb->dirty = 1;
|
||||
return status;
|
||||
}
|
||||
@ -1082,19 +1080,11 @@ void set_selected(bb_t *bb, entry_t *e, int selected)
|
||||
bb->dirty = 1;
|
||||
|
||||
if (selected) {
|
||||
if (bb->firstselected)
|
||||
bb->firstselected->selected.atme = &e->selected.next;
|
||||
e->selected.next = bb->firstselected;
|
||||
e->selected.atme = &bb->firstselected;
|
||||
bb->firstselected = e;
|
||||
LL_PREPEND(bb->firstselected, e, selected);
|
||||
} else {
|
||||
if (bb->nfiles > 0 && e != bb->files[bb->cursor])
|
||||
bb->dirty = 1;
|
||||
if (e->selected.next)
|
||||
e->selected.next->selected.atme = e->selected.atme;
|
||||
*(e->selected.atme) = e->selected.next;
|
||||
e->selected.next = NULL;
|
||||
e->selected.atme = NULL;
|
||||
LL_REMOVE(e, selected);
|
||||
try_free_entry(e);
|
||||
}
|
||||
}
|
||||
@ -1127,11 +1117,7 @@ void set_sort(bb_t *bb, const char *sort)
|
||||
int try_free_entry(entry_t *e)
|
||||
{
|
||||
if (IS_SELECTED(e) || IS_VIEWED(e)) return 0;
|
||||
if (e->hash.next)
|
||||
e->hash.next->hash.atme = e->hash.atme;
|
||||
*(e->hash.atme) = e->hash.next;
|
||||
e->hash.atme = NULL;
|
||||
e->hash.next = NULL;
|
||||
LL_REMOVE(e, hash);
|
||||
free(e);
|
||||
return 1;
|
||||
}
|
||||
@ -1171,10 +1157,33 @@ static char *trim(char *s)
|
||||
void update_term_size(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
struct winsize sz = {0};
|
||||
ioctl(fileno(tty_in), TIOCGWINSZ, &sz);
|
||||
termwidth = sz.ws_col;
|
||||
termheight = sz.ws_row;
|
||||
if (tty_in) {
|
||||
struct winsize sz = {0};
|
||||
ioctl(fileno(tty_in), TIOCGWINSZ, &sz);
|
||||
termwidth = sz.ws_col;
|
||||
termheight = sz.ws_row;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for a process to either suspend or exit and return the status.
|
||||
*/
|
||||
int wait_for_process(proc_t **proc)
|
||||
{
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
tcsetpgrp(fileno(tty_out), (*proc)->pid);
|
||||
int status;
|
||||
while (waitpid((*proc)->pid, &status, WUNTRACED) < 0 && errno == EINTR) // Can happen, e.g. if process sends SIGTSTP
|
||||
continue;
|
||||
tcsetpgrp(fileno(tty_out), getpgid(0));
|
||||
signal(SIGTTOU, SIG_DFL);
|
||||
if (!WIFSTOPPED(status)) {
|
||||
LL_REMOVE((*proc), running);
|
||||
free(*proc);
|
||||
*proc = NULL;
|
||||
--nprocs;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -1238,7 +1247,6 @@ int main(int argc, char *argv[])
|
||||
// Default values
|
||||
setenv("SHELL", "bash", 0);
|
||||
setenv("EDITOR", "nano", 0);
|
||||
setenv("PAGER", "less", 0);
|
||||
char *curdepth = getenv("BB_DEPTH");
|
||||
int depth = curdepth ? atoi(curdepth) : 0;
|
||||
sprintf(depthstr, "%d", depth + 1);
|
||||
@ -1251,13 +1259,21 @@ int main(int argc, char *argv[])
|
||||
if (strcmp(full_initial_path, "/") != 0) strcat(full_initial_path, "/");
|
||||
setenv("BBINITIALPATH", full_initial_path, 1);
|
||||
|
||||
// Terminal must be initialized before working with bb to ensure the
|
||||
// cursor/scroll can be set properly within the screen's bounds
|
||||
init_term();
|
||||
|
||||
tty_in = fopen("/dev/tty", "r");
|
||||
tty_out = fopen("/dev/tty", "w");
|
||||
tcgetattr(fileno(tty_out), &orig_termios);
|
||||
memcpy(&bb_termios, &orig_termios, sizeof(bb_termios));
|
||||
cfmakeraw(&bb_termios);
|
||||
bb_termios.c_cc[VMIN] = 0;
|
||||
bb_termios.c_cc[VTIME] = 1;
|
||||
atexit(cleanup);
|
||||
int signals[] = {SIGTERM, SIGINT, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGSEGV};
|
||||
struct sigaction sa_winch = {.sa_handler = &update_term_size};
|
||||
sigaction(SIGWINCH, &sa_winch, NULL);
|
||||
int signals[] = {SIGTERM, SIGINT, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGSEGV, SIGTSTP};
|
||||
struct sigaction sa = {.sa_handler = &cleanup_and_raise, .sa_flags = SA_NODEFER | SA_RESETHAND};
|
||||
for (int i = 0; i < sizeof(signals)/sizeof(signals[0]); i++)
|
||||
signal(signals[i], cleanup_and_exit);
|
||||
sigaction(signals[i], &sa, NULL);
|
||||
|
||||
bb_t *bb = memcheck(calloc(1, sizeof(bb_t)));
|
||||
strcpy(bb->columns, "*smpn");
|
||||
@ -1281,18 +1297,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
close(cmdfd); cmdfd = -1;
|
||||
|
||||
if (cmdfd != -1) {
|
||||
close(cmdfd);
|
||||
cmdfd = -1;
|
||||
}
|
||||
|
||||
init_term();
|
||||
bb_browse(bb);
|
||||
|
||||
if (tty_out) {
|
||||
fputs(T_LEAVE_BBMODE, tty_out);
|
||||
cleanup();
|
||||
}
|
||||
fputs(T_LEAVE_BBMODE, tty_out);
|
||||
cleanup();
|
||||
|
||||
if (bb->firstselected && print_selection) {
|
||||
for (entry_t *e = bb->firstselected; e; e = e->selected.next) {
|
||||
|
45
bb.h
45
bb.h
@ -62,11 +62,27 @@
|
||||
} while (0)
|
||||
|
||||
#define warn(...) do { \
|
||||
fputs(T_LEAVE_BBMODE, tty_out); \
|
||||
restore_term(&orig_termios); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
init_term(); \
|
||||
move_cursor(tty_out, 0, termheight-1); \
|
||||
fputs("\033[41;33;1m", tty_out); \
|
||||
fprintf(tty_out, __VA_ARGS__); \
|
||||
fputs(" Press any key to continue...\033[0m ", tty_out); \
|
||||
fflush(tty_out); \
|
||||
while (bgetkey(tty_in, NULL, NULL) == -1) usleep(100); \
|
||||
} while (0)
|
||||
|
||||
#define LL_PREPEND(head, node, name) do { \
|
||||
((node)->name).atme = &(head); \
|
||||
((node)->name).next = head; \
|
||||
if (head) ((head)->name).atme = &(((node)->name).next); \
|
||||
head = node; \
|
||||
} while (0)
|
||||
|
||||
#define LL_REMOVE(node, name) do { \
|
||||
if (((node)->name).next) \
|
||||
((__typeof__(node))(node)->name.next)->name.atme = ((node)->name).atme; \
|
||||
*(((node)->name).atme) = ((node)->name).next; \
|
||||
((node)->name).atme = NULL; \
|
||||
((node)->name).next = NULL; \
|
||||
} while (0)
|
||||
|
||||
// Types:
|
||||
@ -99,12 +115,10 @@ typedef enum {
|
||||
* the variable that points to the first list member. In other words,
|
||||
* item->next->atme == &item->next and firstitem->atme == &firstitem.
|
||||
*/
|
||||
typedef struct {
|
||||
struct entry_s *next, **atme;
|
||||
} llnode_t;
|
||||
|
||||
typedef struct entry_s {
|
||||
llnode_t selected, hash;
|
||||
struct {
|
||||
struct entry_s *next, **atme;
|
||||
} selected, hash;
|
||||
char *name, *linkname;
|
||||
struct stat info;
|
||||
mode_t linkedmode;
|
||||
@ -137,6 +151,14 @@ typedef struct bb_s {
|
||||
unsigned int should_quit : 1;
|
||||
} bb_t;
|
||||
|
||||
// For keeping track of child processes
|
||||
typedef struct proc_s {
|
||||
pid_t pid;
|
||||
struct {
|
||||
struct proc_s *next, **atme;
|
||||
} running;
|
||||
} proc_t;
|
||||
|
||||
// Configurable options:
|
||||
#define SCROLLOFF MIN(5, (termheight-4)/2)
|
||||
#define CMDFILE_FORMAT "/tmp/bb.XXXXXX"
|
||||
@ -171,7 +193,7 @@ const column_t columns[] = {
|
||||
// Functions
|
||||
void bb_browse(bb_t *bb);
|
||||
static void cleanup(void);
|
||||
static void cleanup_and_exit(int sig);
|
||||
static void cleanup_and_raise(int sig);
|
||||
static const char* color_of(mode_t mode);
|
||||
#ifdef __APPLE__
|
||||
static int compare_files(void *v, const void *v1, const void *v2);
|
||||
@ -199,6 +221,7 @@ static void sort_files(bb_t *bb);
|
||||
static char *trim(char *s);
|
||||
static int try_free_entry(entry_t *e);
|
||||
static void update_term_size(int sig);
|
||||
static int wait_for_process(proc_t **proc);
|
||||
|
||||
// Constants
|
||||
static const char *T_ENTER_BBMODE = T_OFF(T_SHOW_CURSOR ";" T_WRAP) T_ON(T_ALT_SCREEN ";" T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR);
|
||||
|
28
bindings.bb
28
bindings.bb
@ -5,10 +5,12 @@ Section: BB Commands
|
||||
bb +help
|
||||
q,Q: # Quit
|
||||
bb +quit
|
||||
Ctrl-c: # Exit with failure
|
||||
bb +kill
|
||||
Ctrl-c: # Send interrupt signal
|
||||
kill -INT $PPID
|
||||
Ctrl-z: # Suspend
|
||||
bb +suspend
|
||||
kill -TSTP $PPID
|
||||
Ctrl-\: # Quit and generate core dump
|
||||
kill -QUIT $PPID
|
||||
|
||||
Section: File Navigation
|
||||
j,Down: # Next file
|
||||
@ -139,14 +141,8 @@ Ctrl-n: # New file/directory
|
||||
*) exit
|
||||
;;
|
||||
esac && bb +goto:"$name" +refresh || pause
|
||||
p: # Page through a file with $PAGER
|
||||
$PAGER "$BBCURSOR"
|
||||
|: # Pipe selected files to a command
|
||||
ask cmd '|' && printf '%s\n' "$@" | sh -c "$BBSHELLFUNC$cmd"; bb +r; pause
|
||||
:: # Run a command
|
||||
ask cmd ':' && sh -c "$BBSHELLFUNC$cmd" -- "$@"; bb +r; pause
|
||||
>: # Open a shell
|
||||
tput rmcup; tput cvvis; $SHELL; bb +r
|
||||
p: # Page through a file with `less`
|
||||
less -XK "$BBCURSOR"
|
||||
r,F2: # Rename a file
|
||||
ask newname "Rename $(printf "\033[33m%s\033[39m" "$(basename "$BBCURSOR")"): " "$(basename "$BBCURSOR")" || exit
|
||||
r="$(dirname "$BBCURSOR")/$newname" || exit
|
||||
@ -177,6 +173,16 @@ Ctrl-r: # Regex rename files
|
||||
if [ $# -gt 0 ]; then rename -i "$patt" "$rep" "$@"; else rename -i "$patt" "$rep" *; fi;
|
||||
bb +deselect +refresh
|
||||
|
||||
Section: Shell Commands
|
||||
:: # Run a command
|
||||
ask cmd ':' && sh -c "$BBSHELLFUNC$cmd" -- "$@"; bb +r; pause
|
||||
|: # Pipe selected files to a command
|
||||
ask cmd '|' && printf '%s\n' "$@" | sh -c "$BBSHELLFUNC$cmd"; bb +r; pause
|
||||
>: # Open a shell
|
||||
tput rmcup; tput cvvis; $SHELL; bb +r
|
||||
f: # Resume suspended process
|
||||
bb +fg
|
||||
|
||||
Section: Viewing Options
|
||||
s: # Sort by...
|
||||
ask1 sort "Sort (n)ame (s)ize (m)odification (c)reation (a)ccess (r)andom (p)ermissions: " &&
|
||||
|
Loading…
Reference in New Issue
Block a user