aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--API.md26
-rw-r--r--Makefile49
-rw-r--r--bb.12
-rw-r--r--bb.c14
-rw-r--r--bb.h76
-rw-r--r--bbcmd.12
-rw-r--r--bindings.bb111
-rwxr-xr-xhelpers/bbask31
-rwxr-xr-xhelpers/bbcmd11
-rwxr-xr-xhelpers/bbconfirm9
-rwxr-xr-xhelpers/bbpause4
-rwxr-xr-xhelpers/bbpick39
-rwxr-xr-xhelpers/bbtargets25
-rwxr-xr-xhelpers/bbunscroll5
14 files changed, 206 insertions, 198 deletions
diff --git a/API.md b/API.md
index ec10f88..ce92b1c 100644
--- a/API.md
+++ b/API.md
@@ -9,18 +9,18 @@ internal state.
## Helper Functions
-- `bb`: used for modifying `bb`'s internal state (see BB Commands).
-- `ask`: get user input in a standardized and customizable way. The first
- argument is a variable where the value is stored. The second argument is
- a prompt. A third optional argument can provide a default value (may be
- ignored).
-- `ask1`: get a single character of user input. The first argument is a variable
- where the input will be stored and the second argument is a prompt.
-- `pause`: Display a "press any key to continue" message and wait for a keypress.
-- `confirm`: Display a "Is this okay? [y/N]" prompt and exit with failure if
- the user does not press 'y'.
-- `spin`: Display a spinning icon while a slow command executes in the background.
- (e.g. `spin sleep 5`).
+- `bbask [-1] [prompt [initial]]`: get user input in a standardized and
+ customizable way and output it to `STDOUT`.
+- `bbcmd <cmd>*`: used for modifying `bb`'s internal state (see BB Commands).
+- `bbconfirm [prompt]`: Display a "Is this okay? [y/N]" prompt and exit with
+ failure if the user does not press 'y'.
+- `bbpause`: Display a "press any key to continue" message and wait for a keypress.
+- `bbpick [prompt]`: Select one of `NULL`-delimited multiple inputs and print it.
+- `bbtargets "$BBCMD" "$@"`: If `$BBCURSOR` is not currently among `$@` (the
+ selected files), this script prompts the user to ask whether they want to
+ perform an action on the selected files, or on the cursor. The result is
+ printed as `cursor` or `selected`.
+- `bbunscroll`: Print text to the screen *above* the cursor instead of below it.
## Environment Variables
@@ -80,7 +80,7 @@ glob is available in `$BBGLOB`, which can be used in scripts if left unquoted.
## Final Notes
-Internally, `bbcmd` writes the commands (NUL terminated) to a file whose path is
+Internally, `bbcmd` writes the commands (`NULL` terminated) to a file whose path is
in`$BBCMD` and `bb` reads from that file when it resumes. These commands can also
be passed to `bb` at startup as command line arugments starting with `+`, and
will run immediately. E.g. `bbcmd +'col:n' +'sort:+r' .` will launch `bb` only
diff --git a/Makefile b/Makefile
index 606f8db..dae7e36 100644
--- a/Makefile
+++ b/Makefile
@@ -8,49 +8,6 @@ CWARN=-Wall -Wpedantic -Wextra -Wno-unknown-pragmas -Wno-missing-field-initializ
#CFLAGS += -fsanitize=address -fno-omit-frame-pointer
G=
-ifeq ($(shell uname),Darwin)
- CFLAGS += -D_DARWIN_C_SOURCE
-endif
-
-ifneq (, $(SH))
- CFLAGS += -D'SH="$(SH)"'
-endif
-
-PICKER_FLAG=
-ifeq (, $(PICKER))
- PICKER=$(shell sh -c "(which fzf >/dev/null 2>/dev/null && echo 'fzf') || (which fzy >/dev/null 2>/dev/null && echo 'fzy') || (which pick >/dev/null 2>/dev/null && echo 'pick') || (which ask >/dev/null 2>/dev/null && echo 'ask')")
-endif
-ifneq (, $(PICKER))
- PICKER_FLAG=-D"PICK=\"$(PICKER) --prompt=\\\"$$1\\\"\""
- ifeq ($(shell which $(PICKER)),$(shell which fzf 2>/dev/null || echo '<none>'))
- PICKER_FLAG=-D'PICK="printf \"\\033[3A\\033[?25h\" >/dev/tty; fzf --read0 --height=4 --prompt=\"$$1\""'
- endif
- ifeq ($(shell which $(PICKER)),$(shell which fzy 2>/dev/null || echo '<none>'))
- PICKER_FLAG=-D'PICK="printf \"\\033[3A\\033[?25h\" >/dev/tty; tr "\\0" "\\n" | fzy --lines=3 --prompt=\"\033[1m$$1\033[0m\""'
- endif
- ifeq ($(shell which $(PICKER)),$(shell which ask 2>/dev/null || echo '<none>'))
- PICKER_FLAG=-D'PICK="/usr/bin/env ask --read0 --prompt=\"$$1\033[?25h\""'
- endif
- ifeq ($(shell which $(PICKER)),$(shell which pick 2>/dev/null || echo '<none>'))
- PICKER_FLAG=-D'PICK="printf \"\\033[?25h\" >/dev/tty; tr "\\0" "\\n" | pick"'
- endif
- ifeq ($(shell which $(PICKER)),$(shell which dmenu 2>/dev/null || echo '<none>'))
- PICKER_FLAG=-D'PICK="tr "\\0" "\\n" | dmenu -i -l 10 -p \"$$1\""'
- endif
-endif
-CFLAGS += $(PICKER_FLAG)
-
-ifneq (, $(ASKER))
- PERCENT := %
- ifeq ($(shell which $(ASKER)),$(shell which ask 2>/dev/null || echo '<none>'))
- CFLAGS += -D'ASK="eval \"$$1=\\$$(/usr/bin/env ask --history=bb.hist --prompt=\\\"$$2\033[?25h\\\" --query=\\\"$$3\\\")\""'
- CFLAGS += -D'CONFIRM="/usr/bin/env ask -n \"$$1Is that okay?\033[?25h\""'
- endif
- ifeq ($(shell which $(ASKER)),$(shell which dmenu 2>/dev/null || echo '<none>'))
- CFLAGS += -D'ASK="eval \"$$1=\\$$(echo \"$$3\" | dmenu -p \"$$2\")\""'
- endif
-endif
-
all: $(NAME)
clean:
@@ -67,8 +24,8 @@ install: $(NAME)
fi; \
[ ! "$$prefix" ] && prefix="/usr/local"; \
[ ! "$$sysconfdir" ] && sysconfdir=/etc; \
- mkdir -pv -m 755 "$$prefix/share/man/man1" "$$prefix/bin" "$$sysconfdir/xdg/bb" \
- && cp -v bbstartup.sh bindings.bb "$$sysconfdir/xdg/bb/" \
+ mkdir -pv -m 755 "$$prefix/share/man/man1" "$$prefix/bin" "$$sysconfdir/xdg/bb" "$$sysconfdir/xdg/bb/helpers" \
+ && cp -rv bbstartup.sh bindings.bb helpers "$$sysconfdir/xdg/bb/" \
&& cp -v bb.1 bbcmd.1 "$$prefix/share/man/man1/" \
&& rm -f "$$prefix/bin/$(NAME)" \
&& cp -v $(NAME) "$$prefix/bin/"
@@ -83,5 +40,5 @@ uninstall:
[ ! "$$sysconfdir" ] && sysconfdir=/etc; \
echo "Deleting..."; \
rm -rvf "$$prefix/bin/$(NAME)" "$$prefix/share/man/man1/bb.1" "$$prefix/share/man/man1/bbcmd.1" "$$sysconfdir/xdg/bb"; \
- printf "\033[1mIf you created any config files in ~/.config/bb, you may want to delete them manually.\033[0m"
+ printf "\033[1mIf you created any config files in ~/.config/bb, you may want to delete them manually.\033[0m\n"
diff --git a/bb.1 b/bb.1
index 42e450c..c474fba 100644
--- a/bb.1
+++ b/bb.1
@@ -1,6 +1,6 @@
.\" Manpage for bb.
.\" Contact bruce@bruce-hill.com to correct errors or typos.
-.TH man 1 "23 Feb 2020" "0.24" "bb manual page"
+.TH man 1 "23 Feb 2020" "0.25" "bb manual page"
.SH NAME
bb \- A bitty browser for command line file management
.SH SYNOPSIS
diff --git a/bb.c b/bb.c
index 8fce4d2..9f81830 100644
--- a/bb.c
+++ b/bb.c
@@ -904,12 +904,9 @@ int run_script(bb_t *bb, const char *cmd)
err("Couldn't set pgrp");
char **args = memcheck(calloc(4 + (size_t)bb->nselected + 1, sizeof(char*)));
int i = 0;
- args[i++] = SH;
+ args[i++] = "sh";
args[i++] = "-c";
- char *fullcmd = calloc(strlen(cmd) + strlen(bbcmdfn) + 1, sizeof(char));
- strcpy(fullcmd, bbcmdfn);
- strcat(fullcmd, cmd);
- args[i++] = fullcmd;
+ args[i++] = (char*)cmd;
args[i++] = "--"; // ensure files like "-i" are not interpreted as flags for sh
// bb->selected is in most-recent order, so populate args in reverse to make sure
// that $1 is the first selected, etc.
@@ -924,7 +921,7 @@ int run_script(bb_t *bb, const char *cmd)
ttyin = open("/dev/tty", O_RDONLY);
dup2(ttyout, STDOUT_FILENO);
dup2(ttyin, STDIN_FILENO);
- execvp(SH, args);
+ execvp(args[0], args);
err("Failed to execute command: '%s'", cmd);
return -1;
}
@@ -1195,6 +1192,10 @@ int main(int argc, char *argv[])
sprintf(xdg_data_home, "%s/.local/share", getenv("HOME"));
setenv("XDG_DATA_HOME", xdg_data_home, 0);
setenv("sysconfdir", "/etc", 0);
+ char *newpath;
+ if (asprintf(&newpath, "%s/xdg/bb/helpers:%s/bb/helpers:%s", getenv("sysconfdir"), getenv("XDG_CONFIG_HOME"), getenv("PATH")) < 0)
+ err("Could not allocate memory");
+ setenv("PATH", newpath, 1);
setenv("SHELL", "bash", 0);
setenv("EDITOR", "nano", 0);
char *curdepth = getenv("BBDEPTH");
@@ -1202,7 +1203,6 @@ int main(int argc, char *argv[])
char depthstr[16];
sprintf(depthstr, "%d", depth + 1);
setenv("BBDEPTH", depthstr, 1);
- setenv("BBSHELLFUNC", bbcmdfn, 1);
char full_initial_path[PATH_MAX];
getcwd(full_initial_path, PATH_MAX);
normalize_path(full_initial_path, initial_path, full_initial_path);
diff --git a/bb.h b/bb.h
index c25ceae..21bbe89 100644
--- a/bb.h
+++ b/bb.h
@@ -26,7 +26,7 @@
#include "bterm.h"
// Macros:
-#define BB_VERSION "0.24.0"
+#define BB_VERSION "0.25.0"
#ifndef PATH_MAX
#define PATH_MAX 4096
@@ -243,80 +243,6 @@ static const struct termios default_termios = {
static const char *description_str = "bb - an itty bitty console TUI file browser\n";
static const char *usage_str = "Usage: bb (-h/--help | -v/--version | -s | -d | -0 | +command)* [[--] directory]\n";
-// Shell functions
-static const char *bbcmdfn = "bbcmd() {\n"
-" if test $# -eq 0; then cat >> $BBCMD; return; fi\n"
-" for arg; do\n"
-" shift;\n"
-" if expr \"$arg\" : \"^[^:]*:$\" >/dev/null; then\n"
-" if test $# -gt 0; then printf \"$arg%s\\0\" \"$@\" >> $BBCMD;\n"
-" else sed \"s/\\([^\\x00]\\+\\)/$arg\\1/g\" >> $BBCMD; fi;\n"
-" return;\n"
-" fi;\n"
-" printf \"%s\\0\" \"$arg\" >> $BBCMD;\n"
-" done;\n"
-"}\n"
-// This shell function overwrites the lines above the cursor instead of scrolling the screen
-"unscroll() {\n"
-" input=\"$(cat)\"\n"
-" printf \"\\033[$(echo \"$input\" | wc -l)A\\033[J\" >/dev/tty\n"
-" echo \"$input\"\n"
-"}\n"
-"ask() {\n"
-#ifdef ASK
-ASK ";\n"
-#else
-" [ $# -lt 2 ] && printf '\033[31;1mNot enough args to ask!\033[0m\n' && return 1;\n"
-" printf \"\033[1m%s\033[0m\" \"$2\" >/dev/tty;\n"
-" tput cvvis >/dev/tty;\n"
-#ifdef __APPLE__
-" read -e $1 </dev/tty >/dev/tty;\n"
-#else
-" read $1 </dev/tty >/dev/tty;\n"
-#endif
-#endif
-"}\n"
-"ask1() {\n"
-#ifdef ASK1
-ASK1 ";\n"
-#else
-" tput civis >/dev/tty;\n"
-" printf \"\033[1m%s\033[0m\" \"$2\" >/dev/tty;\n"
-" stty -icanon -echo >/dev/tty 2>/dev/tty;\n"
-#ifdef __APPLE__
-" read -n 1 $1 </dev/tty >/dev/tty;\n"
-#else
-" eval \"$1=\\$(dd bs=1 count=1 2>/dev/null </dev/tty)\";\n"
-#endif
-" stty icanon echo >/dev/tty 2>/dev/tty;\n"
-" tput cvvis >/dev/tty;\n"
-#endif
-"}\n"
-"confirm() {\n"
-#ifdef CONFIRM
-CONFIRM ";\n"
-#else
-" ask1 REPLY \"$1\033[0;1mIs that okay? [y/N] \" && [ \"$REPLY\" = 'y' ];\n"
-#endif
-"}\n"
-"pause() {\n"
-" ask1 REPLY \"\033[0;2m Press any key to continue...\033[0m\";"
-"}\n"
-"pick() {\n"
-#ifdef PICK
-PICK ";\n"
-#else
-" ask query \"$1\" && awk '{print length, $1}' | sort -n | cut -d' ' -f2- |\n"
-" grep -i -m1 \"$(echo \"$query\" | sed 's;.;[^/&]*[&];g')\";\n"
-#endif
-"}\n"
-#ifdef SH
-"alias sh="SH";\n"
-#else
-#define SH "sh"
-#endif
-;
-
static const char *runstartup =
"for path in \"$XDG_CONFIG_HOME/bb\" \"$sysconfdir/xdg/bb\" .; do\n"
" if [ -e \"$path/bbstartup.sh\" ]; then\n"
diff --git a/bbcmd.1 b/bbcmd.1
index bc9345c..06794de 100644
--- a/bbcmd.1
+++ b/bbcmd.1
@@ -1,6 +1,6 @@
.\" Manpage for bbcmd.
.\" Contact bruce@bruce-hill.com to correct errors or typos.
-.TH man 1 "23 Feb 2020" "0.24" "bbcmd manual page"
+.TH man 1 "23 Feb 2020" "0.25" "bbcmd manual page"
.de TPx
. TP 4n
diff --git a/bindings.bb b/bindings.bb
index 5e3a232..083183e 100644
--- a/bindings.bb
+++ b/bindings.bb
@@ -23,23 +23,22 @@ l,Right: # Enter directory
if [ -d "$BBCURSOR" ]; then bbcmd cd:"$BBCURSOR"; fi
Ctrl-f: # Search for file
file="$(
- find $BBGLOB -mindepth 1 -printf '%P\0' | pick "Find: "
+ find $BBGLOB -mindepth 1 -printf '%P\0' | bbpick "Find: "
)" && bbcmd goto:"$file"
/: # Pick a file
- file="$(printf "%s\0" $BBGLOB | pick "Pick: ")" || exit
+ file="$(printf "%s\0" $BBGLOB | bbpick "Pick: ")" || exit
bbcmd goto:"$file"
*: # Set the glob
- ask BBGLOB "Show files matching: "
- bbcmd glob:"$BBGLOB"
+ glob="$(bbask "Show files matching: ")" && bbcmd glob:"$glob"
Ctrl-g: # Go to directory
- ask goto "Go to directory: " && bbcmd cd:"$goto"
+ cd="$(bbask "Go to directory: ")" && bbcmd cd:"$cd"
m: # Mark this directory
mkdir -p "$XDG_CONFIG_HOME/bb/marks"
ln -sT "$2" "$XDG_CONFIG_HOME/bb/marks/$1" 2>/dev/null
- ask mark "Mark: " && ln -s "$PWD" "$XDG_CONFIG_HOME"/bb/marks/"$mark"
+ mark="$(bbask "Mark: ") && ln -s "$PWD" "$XDG_CONFIG_HOME"/bb/marks/"$mark"
': # Go to a marked directory
[ -d "$XDG_CONFIG_HOME"/bb/marks ] || exit
- mark="$(find "$XDG_CONFIG_HOME"/bb/marks/ -mindepth 1 -type l -printf '%P\0' | pick "Jump to: ")" &&
+ mark="$(find "$XDG_CONFIG_HOME"/bb/marks/ -mindepth 1 -type l -printf '%P\0' | bbpick "Jump to: ")" &&
bbcmd cd:"$(readlink -f "$XDG_CONFIG_HOME"/bb/marks/"$mark")"
-,Backspace: # Go to previous directory
[ "$BBPREVPATH" ] && bbcmd cd:"$BBPREVPATH"
@@ -70,18 +69,18 @@ v,V,Space: # Toggle selection at cursor
Escape: # Clear selection
bbcmd deselect
S: # Select pattern
- ask patt "Select: " && bbcmd select: $patt
+ patt="$(bbask "Select: ")" && bbcmd select: $patt
U: # Unselect pattern
- ask patt "Unselect: " && eval bbcmd deselect: "$patt"
+ patt="$(bbask "Unselect: ")" && bbcmd deselect: $patt
Ctrl-s: # Save the selection
- [ $# -gt 0 ] && ask savename "Save selection as: " || exit 1
+ [ $# -gt 0 ] && savename="$(bbask "Save selection as: ")" || exit 1
mkdir -p "$XDG_DATA_HOME"/bb
if ! expr "$savename" : ".*\.sel" >/dev/null; then savename="$savename.sel"; fi
printf '%s\0' "$@" > "$XDG_DATA_HOME"/bb/"$savename"
Ctrl-o: # Open a saved selection
[ -d "$XDG_DATA_HOME"/bb ] || exit
- [ $# -gt 0 ] && ! confirm "The current selection will be discarded. " && exit 1
- loadpath="$(find "$XDG_DATA_HOME"/bb/ -mindepth 1 -name '*.sel' -printf '%P\0' | pick "Load selection: ")" &&
+ [ $# -gt 0 ] && ! bbconfirm "The current selection will be discarded. " && exit 1
+ loadpath="$(find "$XDG_DATA_HOME"/bb/ -mindepth 1 -name '*.sel' -printf '%P\0' | bbpick "Load selection: ")" &&
cat "$XDG_DATA_HOME"/bb/"$loadpath" | bbcmd deselect select:
J: # Spread selection down
bbcmd spread:+1
@@ -113,77 +112,79 @@ Enter,Double left click: # Open file/directory
else xdg-open "$BBCURSOR"; fi
fi
e: # Edit file in $EDITOR
- $EDITOR "$BBCURSOR" || pause
-d,Delete: # Delete a file
- printf "\033[1mDeleting \033[33m$BBCURSOR\033[0;1m...\033[0m " && confirm &&
- rm -rf "$BBCURSOR" && bbcmd deselect:"$BBCURSOR" refresh
-D: # Delete all selected files
- [ $# -gt 0 ] && printf "\033[1mDeleting the following:\n\033[33m$(printf ' %s\n' "$@")\033[0m" | unscroll | more &&
- confirm && rm -rf "$@" && bbcmd deselect refresh
+ $EDITOR "$BBCURSOR" || bbpause
+d,Delete: # Delete
+ case "$(bbtargets "$BBCURSOR" "$@")" in
+ cursor) set -- "$BBCURSOR" ;;
+ both) set -- "$BBCURSOR" "$@" ;;
+ esac
+ printf "\033[1mDeleting the following:\n\033[33m$(printf ' %s\n' "$@")\033[0m" | bbunscroll | more &&
+ bbconfirm && rm -rf "$@" && bbcmd deselect refresh
Ctrl-v: # Move files here
- printf "\033[1mMoving the following to here:\n\033[33m$(printf ' %s\n' "$@")\033[0m" | unscroll | more &&
- confirm && printf "\033[1G\033[KMoving..." && mv -i "$@" . && printf "done." &&
+ printf "\033[1mMoving the following to here:\n\033[33m$(printf ' %s\n' "$@")\033[0m" | bbunscroll | more &&
+ bbconfirm && printf "\033[1G\033[KMoving..." && mv -i "$@" . && printf "done." &&
bbcmd deselect refresh && for f; do bbcmd sel:"$(basename "$f")"; done
c: # Copy a file
- printf "\033[1mCreating copy of \033[33m$BBCURSOR\033[0;1m...\033[0m " &&
- confirm && cp -ri "$BBCURSOR" "$BBCURSOR.copy" && bbcmd refresh
-C: # Copy all selected files here
- [ $# -gt 0 ] && printf "\033[1mCopying the following to here:\n\033[33m$(printf ' %s\n' "$@")\033[0m" | unscroll | more &&
- confirm && printf "\033[1G\033[KCopying..." &&
+ case "$(bbtargets "$BBCURSOR" "$@")" in
+ cursor) set -- "$BBCURSOR";;
+ both) set -- "$BBCURSOR" "$@";;
+ esac
+ [ $# -gt 0 ] || exit
+ printf "\033[1mCopying the following to here:\n\033[33m$(printf ' %s\n' "$@")\033[0m" | bbunscroll | more
+ bbconfirm && printf "\033[1G\033[KCopying..." &&
for f; do if [ "./$(basename "$f")" -ef "$f" ]; then
cp -ri "$f" "$f.copy" || break;
else cp -ri "$f" . || break; fi; done; printf 'done.' && bbcmd refresh
Ctrl-n: # New file/directory
- case "$(printf '%s\0' File Directory | pick "Create new: ")" in
+ case "$(printf '%s\0' File Directory | bbpick "Create new: ")" in
File)
- ask name "New File: " || exit
- touch -- "$name"
+ name="$(bbask "New File: ")" && touch -- "$name"
;;
Directory)
- ask name "New Directory: " || exit
- mkdir -- "$name"
+ name="$(bbask "New Directory: ")" && mkdir -- "$name"
;;
*) exit
;;
- esac && bbcmd goto:"$name" refresh || pause
+ esac && bbcmd goto:"$name" refresh || bbpause
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
- [ "$r" = "$BBCURSOR" ] && exit
- [ -e "$r" ] && printf "\033[31;1m$r already exists! It will be overwritten.\033[0m " &&
- confirm && { rm -rf "$r" || { pause; exit; }; }
- mv "$BBCURSOR" "$r" && bbcmd refresh &&
- while [ $# -gt 0 ]; do "$1" = "$BBCURSOR" && bbcmd deselect:"$BBCURSOR" select:"$r"; shift; done &&
- bbcmd goto:"$r" || { pause; exit; }
-R: # Rename all selected files
+r,F2: # Rename files
+ case "$(bbtargets "$BBCURSOR" "$@")" in
+ cursor) set -- "$BBCURSOR";;
+ both) set -- "$BBCURSOR" "$@";;
+ esac
for f; do
- ask newname "Rename $(printf "\033[33m%s\033[39m" "$(basename "$f")"): " "$(basename "$f")" || break;
+ newname="$(bbask "Rename $(printf "\033[33m%s\033[39m" "$(basename "$f")"): " "$(basename "$f")")" || break;
r="$(dirname "$f")/$newname";
[ "$r" = "$f" ] && continue;
[ -e "$r" ] && printf "\033[31;1m$r already exists! It will be overwritten.\033[0m "
- && confirm && { rm -rf "$r" || { pause; exit; }; }
- mv "$f" "$r" || { pause; exit; }
+ && bbconfirm && { rm -rf "$r" || { bbpause; exit; }; }
+ mv "$f" "$r" || { bbpause; exit; }
bbcmd deselect:"$f" select:"$r";
[ "$f" = "$BBCURSOR" ] && bbcmd goto:"$r";
done;
bbcmd refresh
Ctrl-r: # Regex rename files
+ case "$(bbtargets "$BBCURSOR" "$@")" in
+ cursor) set -- "$BBCURSOR";;
+ both) set -- "$BBCURSOR" "$@";;
+ esac
command -v rename >/dev/null ||
- { printf '\033[31;1mThe `rename` command is not installed. Please install it to use this key binding.\033[0m\n'; pause; exit; };
- ask patt "Replace pattern: " && ask rep "Replacement: " &&
- printf "\033[1mRenaming:\n\033[33m$(if [ $# -gt 0 ]; then rename -nv "$patt" "$rep" "$@"; else rename -nv "$patt" "$rep" *; fi)\033[0m" | unscroll | more &&
- confirm || exit 1
+ { printf '\033[31;1mThe `rename` command is not installed. Please install it to use this key binding.\033[0m\n'; bbpause; exit; };
+ patt="$(bbask "Replace pattern: ") && rep="$(bbask "Replacement: ")" &&
+ printf "\033[1mRenaming:\n\033[33m$(if [ $# -gt 0 ]; then rename -nv "$patt" "$rep" "$@"; else rename -nv "$patt" "$rep" *; fi)\033[0m" | bbunscroll | more &&
+ bbconfirm || exit 1
if [ $# -eq 0 ]; then set -- *; ! [ -e "$1" ] && exit; fi
rename -i "$patt" "$rep" "$@"
bbcmd deselect refresh
Section: Shell Commands
:: # Run a command
- ask cmd ':' && sh -c "$BBSHELLFUNC$cmd" -- "$@"; bbcmd refresh; pause
+ cmd="$(bbask ':')" && sh -c "$cmd" -- "$@"; bbcmd refresh; bbpause
|: # Pipe selected files to a command
- ask cmd '|' && printf '%s\n' "$@" | sh -c "$BBSHELLFUNC$cmd"; bbcmd refresh; pause
+ cmd="$(bbask '|')" && printf '%s\n' "$@" | sh -c "$cmd"; bbcmd refresh; bbpause
+@: # Pipe selected files to a command
+ cmd="$(bbask '@')" && sh -c "$cmd \"$$@\"" -- "$@"; bbcmd refresh; bbpause
>: # Open a shell
tput rmcup; tput cvvis; $SHELL; bbcmd refresh
f: # Resume suspended process
@@ -191,10 +192,10 @@ f: # Resume suspended process
Section: Viewing Options
s: # Sort by...
- ask1 sort "Sort (n)ame (s)ize (m)odification (c)reation (a)ccess (r)andom (p)ermissions: " &&
+ sort="$(bbask -1 "Sort (n)ame (s)ize (m)odification (c)reation (a)ccess (r)andom (p)ermissions: ")" &&
bbcmd sort:"~$sort"
---,#: # Set columns
- ask columns "Set columns (*)selected (a)ccessed (c)reated (m)odified (n)ame (p)ermissions (r)andom (s)ize: " &&
+ columns="$(bbask "Set columns (*)selected (a)ccessed (c)reated (m)odified (n)ame (p)ermissions (r)andom (s)ize: ")" &&
bbcmd col:"$columns"
.: # Toggle dotfile visibility
if [ "$BBGLOB" = ".* *" ]; then
@@ -207,7 +208,7 @@ i: # Toggle interleaving files and directories
F5,Ctrl-l: # Refresh view
bbcmd refresh
Ctrl-b: # Bind a key to a script
- ask1 key "Press key to bind..." && echo && ask script "Bind script: " &&
- bbcmd bind:"$key":"{ $script; } || pause" || pause
+ key="$(bbask -1 "Press key to bind...")" && echo && script="$(bbask "Bind script: ")" &&
+ bbcmd bind:"$key":"{ $script; } || bbpause" || bbpause
Section: User Bindings
diff --git a/helpers/bbask b/helpers/bbask
new file mode 100755
index 0000000..ad446ec
--- /dev/null
+++ b/helpers/bbask
@@ -0,0 +1,31 @@
+#!/bin/sh
+# A simple user-input asker. Result is output to stdout.
+# Usage: ask [-1] [prompt [initial query]]
+if [ "$1" = '-1' ]; then
+ # Get one character of input
+ tput civis >/dev/tty;
+ printf '\033[1m%s\033[0m' "$2" >/dev/tty;
+ stty -icanon -echo >/dev/tty 2>/dev/tty;
+ if [ "$(uname)" = "Darwin" ]; then
+ read -n 1 REPLY </dev/tty >/dev/tty;
+ echo $REPLY
+ else
+ dd bs=1 count=1 2>/dev/null </dev/tty
+ fi
+ stty icanon echo >/dev/tty 2>/dev/tty
+ tput cvvis >/dev/tty
+else
+ # Get a line of input
+ if command -v ask >/dev/null; then
+ ask --history=bb.hist --prompt="$(printf '%s\033[?25h' "$1")" --query="$2"
+ else
+ printf "\033[1m%s\033[0m" "$1" >/dev/tty
+ tput cvvis >/dev/tty
+ if [ "$(uname)" = "Darwin" ]; then
+ read -e REPLY </dev/tty >/dev/tty
+ else
+ read REPLY </dev/tty >/dev/tty
+ fi
+ echo $REPLY
+ fi
+fi
diff --git a/helpers/bbcmd b/helpers/bbcmd
new file mode 100755
index 0000000..fdc4e78
--- /dev/null
+++ b/helpers/bbcmd
@@ -0,0 +1,11 @@
+#!/bin/sh
+if [ $# -eq 0 ]; then cat >> $BBCMD; exit; fi
+for arg; do
+ shift
+ if expr "$arg" : '^[^:]*:$' >/dev/null; then
+ if [ $# -gt 0 ]; then printf "$arg%s\\0" "$@" >> $BBCMD
+ else sed "s/\([^\\x00]\+\)/$arg\1/g" >> $BBCMD; fi
+ exit
+ fi
+ printf "%s\\0" "$arg" >> $BBCMD
+done
diff --git a/helpers/bbconfirm b/helpers/bbconfirm
new file mode 100755
index 0000000..cae1153
--- /dev/null
+++ b/helpers/bbconfirm
@@ -0,0 +1,9 @@
+#!/bin/sh
+# Ask for user confirmation
+set -e
+if command -v ask >/dev/null; then
+ ask -n "$(printf "$1Is that okay?\033[?25h")"
+else
+ reply="$(bbask -1 "$(printf "$1\033[0;1mIs that okay? [y/N] ")")"
+ [ "$reply" != 'y' ]
+fi
diff --git a/helpers/bbpause b/helpers/bbpause
new file mode 100755
index 0000000..6604fbd
--- /dev/null
+++ b/helpers/bbpause
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Pause before continuing
+printf '\033[0;2m Press any key to continue...\033[0m' >/dev/tty
+bbask -1 >/dev/null
diff --git a/helpers/bbpick b/helpers/bbpick
new file mode 100755
index 0000000..3316484
--- /dev/null
+++ b/helpers/bbpick
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Pick from the provided input
+if [ -z "$PICKER" ]; then
+ if command -v fzf >/dev/null; then
+ PICKER=fzf
+ elif command -v fzy >/dev/null; then
+ PICKER=fzy
+ elif command -v ask >/dev/null; then
+ PICKER=ask
+ elif command -v dmenu >/dev/null; then
+ PICKER=dmenu
+ elif command -v pick >/dev/null; then
+ PICKER=pick
+ fi
+fi
+
+case "$PICKER" in
+ fzf)
+ printf '\033[3A\033[?25h' >/dev/tty
+ fzf --read0 --height=4 --prompt="$(printf "$1")"
+ ;;
+ fzy)
+ printf '\033[3A\033[?25h' >/dev/tty
+ tr '\0' '\n' | fzy --lines=3 --prompt="$(printf "\033[1m$1\033[0m")"
+ ;;
+ ask)
+ ask --read0 --prompt="$(printf "$1\033[?25h")"
+ ;;
+ dmenu)
+ tr '\0' '\n' | dmenu -i -l 10 -p "$(printf "$1")"
+ ;;
+ pick)
+ printf '\033[?25h' >/dev/tty
+ tr '\0' '\n' | pick
+ ;;
+ *)
+ query="$(bbask "$1")" && grep -i -m1 "$(echo "$query" | sed 's;.;[^/&]*[&];g')"
+ ;;
+esac
diff --git a/helpers/bbtargets b/helpers/bbtargets
new file mode 100755
index 0000000..1862ca0
--- /dev/null
+++ b/helpers/bbtargets
@@ -0,0 +1,25 @@
+#!/bin/sh
+# If the user is doing something ambiguous, like selecting a bunch of files,
+# moving the cursor off of those files, then doing an action, this will ask
+# what they mean to target, then output either 'cursor' or 'selected'.
+# Usage: targets "$BBCURSOR" "$@"
+cursor="$1"
+shift
+if [ $# -gt 0 ]; then
+ for f in "$@"; do
+ if [ "$f" = "$cursor" ]; then
+ intended='Selected files'
+ break
+ fi
+ done
+else
+ intended='Cursor file'
+fi
+
+[ -z "$intended" ] && intended="$(printf '%s\0' 'Cursor file' 'Selected files' 'Both' | bbpick 'Which do you want to delete? ')"
+
+case "$intended" in
+ Cursor*) echo cursor ;;
+ Selected*) echo selected ;;
+ Both) echo both ;;
+esac
diff --git a/helpers/bbunscroll b/helpers/bbunscroll
new file mode 100755
index 0000000..15102c4
--- /dev/null
+++ b/helpers/bbunscroll
@@ -0,0 +1,5 @@
+#!/bin/sh
+# Display text from the current line upward (instead of downward)
+input="$(cat)"
+printf "\\033[$(echo "$input" | wc -l)A\\033[J" >/dev/tty
+echo "$input"