diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2019-11-11 12:29:40 -0800 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2019-11-11 12:29:40 -0800 |
| commit | b7ad0e93a243da19e39fc750054afcfa3c6bb9b0 (patch) | |
| tree | 33295fb95c09d02e9d3afed9e68ef28c3a415107 | |
| parent | af3f6421500dfeb58b8ac251edf639552e48b762 (diff) | |
Refactored `bb +...` to `bbcmd ...` within bb bindings. This makes
things a lot less ambiguous. Also removed the default marks created in
bbstartup.sh and ensured that `$XDG_DATA_HOME` and `$XDG_CONFIG_HOME`
always get set as environment variables.
| -rw-r--r-- | API.md | 25 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | bb.c | 15 | ||||
| -rw-r--r-- | bb.h | 6 | ||||
| -rwxr-xr-x | bbstartup.sh | 20 | ||||
| -rw-r--r-- | bindings.bb | 131 |
6 files changed, 100 insertions, 103 deletions
@@ -36,7 +36,7 @@ environment variables: ## BB Internal State Commands -In order to modify bb's internal state, you can call `bb +cmd`, where "cmd" +In order to modify bb's internal state, you can call `bbcmd <cmd>`, where "cmd" is one of the following commands (or a unique prefix of one): - `.:[01]` Whether to show "." in each directory @@ -57,10 +57,10 @@ is one of the following commands (or a unique prefix of one): - `spread:<num*>` Spread the selection state at the cursor - `toggle:<filename>` Toggle the selection status of <filename> -For any of these commands (e.g. `+select`), an empty parameter followed by -additional arguments (e.g. `bb +select: <file1> <file2> ...`) is equivalent to -repeating the command with each argument (e.g. `bb +select:<file1> -+select:<file2> ...`). +For any of these commands (e.g. `select`), an empty parameter followed by +additional arguments (e.g. `bbcmd select: <file1> <file2> ...`) is equivalent to +repeating the command with each argument (e.g. `bbcmd select:<file1> +select:<file2> ...`). Note: for numeric-based commands (like scroll), the number can be either an absolute value or a relative value (starting with `+` or `-`), and/or a percent @@ -70,12 +70,13 @@ height down, and `100%n` means the last file) ## Final Notes -Internally, `bb` writes the commands (NUL terminated) to a file whose path is -in`$BBCMD` and reads from that file when `bb` resumes. These commands can also -be passed to bb at startup, and will run immediately. E.g. `bb '+col:n' -'+sort:+r' .` will launch `bb` only showing the name column, randomly sorted. +Internally, `bbcmd` writes the commands (NUL 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 +showing the name column, randomly sorted. -`bb` also optimizes any scripts that only contain just a `bb` command and no -shell variables, other commands, etc. (e.g. `bb +move:+1`) These -`bb`-command-only scripts directly modify `bb`'s internal state without +`bb` also optimizes any scripts that only contain just a `bbcmd` command and no +shell variables, other commands, etc. (e.g. `bbcmd move:+1`) These +`bbcmd`-command-only scripts directly modify `bb`'s internal state without spawning a shell, so they're much faster and avoid flickering the screen. @@ -49,7 +49,7 @@ to the filesystem (passing selected files as arguments), rather than reinventing the wheel by hard-coding operations like `rm`, `mv`, `cp`, `touch`, and so on. Shell scripts can be bound to keypresses in `~/.config/bb/bindings.bb`. For example, `D` is bound to a script that prints a -confirmation message, then runs `rm -rf "$@" && bb +deselect +refresh`, +confirmation message, then runs `rm -rf "$@" && bbcmd deselect refresh`, which means selecting `file1` and `file2`, then pressing `D` will cause `bb` to run the shell command `rm -rf file1 file2` and then tell `bb` to deselect all (now deleted) files and refresh. @@ -74,8 +74,8 @@ up an easy way to repeat some custom workflow). ### API `bb` also exposes an API that allows shell scripts to modify `bb`'s internal -state. To do this, call `bb +<your command>` from within `bb`. For example, by -default, `j` is bound to `bb +move:+1`, which has the effect of moving `bb`'s +state. To do this, call `bbcmd <your command>` from within `bb`. For example, by +default, `j` is bound to `bbcmd move:+1`, which has the effect of moving `bb`'s cursor down one item. More details about the API can be found in [the API documentation](API.md). @@ -263,13 +263,13 @@ void init_term(void) /* * Return whether or not 's' is a simple bb command that doesn't need - * a full shell instance (e.g. "bb +cd:.." or "bb +move:+1"). + * a full shell instance (e.g. "bbcmd cd:.." or "bbcmd move:+1"). */ static int is_simple_bbcmd(const char *s) { if (!s) return 0; while (*s == ' ') ++s; - if (s[0] != '+' && strncmp(s, "bb +", 4) != 0) + if (strncmp(s, "bbcmd ", strlen("bbcmd ")) != 0) return 0; const char *special = ";$&<>|\n*?\\\"'"; for (const char *p = special; *p; ++p) { @@ -542,8 +542,7 @@ void print_bindings(int fd) */ void run_bbcmd(bb_t *bb, const char *cmd) { - if (cmd[0] == '+') ++cmd; - else if (strncmp(cmd, "bb +", 4) == 0) cmd = &cmd[4]; + if (strncmp(cmd, "bbcmd ", strlen("bbcmd ")) == 0) cmd = &cmd[strlen("bbcmd ")]; const char *value = strchr(cmd, ':'); if (value) ++value; #define set_bool(target) do { if (!value) { target = !target; } else { target = value[0] == '1'; } } while (0) @@ -1212,6 +1211,12 @@ int main(int argc, char *argv[]) // Set up environment variables // Default values + char xdg_config_home[PATH_MAX], xdg_data_home[PATH_MAX]; + sprintf(xdg_config_home, "%s/.config", getenv("HOME")); + setenv("XDG_CONFIG_HOME", xdg_config_home, 0); + sprintf(xdg_data_home, "%s/.local/share", getenv("HOME")); + setenv("XDG_DATA_HOME", xdg_data_home, 0); + setenv("sysconfdir", "/etc", 0); setenv("SHELL", "bash", 0); setenv("EDITOR", "nano", 0); char *curdepth = getenv("BB_DEPTH"); @@ -1254,7 +1259,7 @@ int main(int argc, char *argv[]) write(cmdfd, "\0", 1); for (int i = 0; i < argc; i++) { if (argv[i][0] == '+') { - char *cmd = argv[i]; + char *cmd = argv[i] + 1; char *colon = strchr(cmd, ':'); if (colon && !colon[1]) { for (++i; i < argc; i++) { @@ -25,7 +25,7 @@ #include "bterm.h" // Macros: -#define BB_VERSION "0.19.2" +#define BB_VERSION "0.20.0" #ifndef PATH_MAX #define PATH_MAX 4096 @@ -254,11 +254,11 @@ static const char *description_str = "bb - an itty bitty console TUI file browse static const char *usage_str = "Usage: bb (-h/--help | -v/--version | -s | -d | -0 | +command | path)*\n"; // Shell functions -static const char *bbcmdfn = "bb() {\n" +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 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" diff --git a/bbstartup.sh b/bbstartup.sh index 38bea99..ecc7e18 100755 --- a/bbstartup.sh +++ b/bbstartup.sh @@ -1,28 +1,14 @@ #!/bin/sh # This file contains the script that is run when bb launches -# See API.md for details on bb's API. - -# Default XDG values -[ ! "$XDG_CONFIG_HOME" ] && XDG_CONFIG_HOME=~/.config -[ ! "$sysconfdir" ] && sysconfdir=/etc - -# Create some default marks: -mkdir -p "$XDG_CONFIG_HOME/bb/marks" -mark() { - ln -sT "$2" "$XDG_CONFIG_HOME/bb/marks/$1" 2>/dev/null -} -mark home ~ -mark root / -mark config "$XDG_CONFIG_HOME" -mark marks "$XDG_CONFIG_HOME/bb/marks" +# See API.md for details on bb's command API. # Load key bindings # first check ~/.config/bb/bindings.bb, then /etc/xdg/bb/bindings.bb, then ./bindings.bb if [ ! -e "$XDG_CONFIG_HOME/bb/bindings.bb" ] && [ ! -e "$sysconfdir/xdg/bb/bindings.bb" ]; then - cat "./bindings.bb" 2>/dev/null | sed -e '/^#/d' -e "s/^[^ ]/$(printf '\034')+bind:\\0/" | tr '\034' '\0' >> "$BBCMD" + cat "./bindings.bb" 2>/dev/null | sed -e '/^#/d' -e "s/^[^ ]/$(printf '\034')bind:\\0/" | tr '\034' '\0' >> "$BBCMD" else for path in "$sysconfdir/xdg/bb" "$XDG_CONFIG_HOME/bb"; do cat "$path/bindings.bb" 2>/dev/null - done | sed -e '/^#/d' -e "s/^[^ ]/$(printf '\034')+bind:\\0/" | tr '\034' '\0' >> "$BBCMD" + done | sed -e '/^#/d' -e "s/^[^ ]/$(printf '\034')bind:\\0/" | tr '\034' '\0' >> "$BBCMD" fi printf '\0' >> "$BBCMD" diff --git a/bindings.bb b/bindings.bb index f63aa91..42b8e25 100644 --- a/bindings.bb +++ b/bindings.bb @@ -2,9 +2,9 @@ # The format is: <key>(,<key>)*:[ ]*#<description>(\n[ ]+script)+ Section: BB Commands ?,F1: # Show Help menu - bb +help + bbcmd help q,Q: # Quit - bb +quit + bbcmd quit Ctrl-c: # Send interrupt signal kill -INT $PPID Ctrl-z: # Suspend @@ -14,92 +14,97 @@ Ctrl-\: # Quit and generate core dump Section: File Navigation j,Down: # Next file - bb +move:+1 + bbcmd move:+1 k,Up: # Previous file - bb +move:-1 + bbcmd move:-1 h,Left: # Parent directory - bb +cd:.. + bbcmd cd:.. l,Right: # Enter directory - if [ -d "$BBCURSOR" ]; then bb +cd:"$BBCURSOR"; fi + if [ -d "$BBCURSOR" ]; then bbcmd cd:"$BBCURSOR"; fi Ctrl-f: # Search for file file="$( if [ $BBDOTFILES ]; then find -mindepth 1 -printf '%P\0'; else find -mindepth 1 ! -path '*/.*' -printf '%P\0'; fi | pick "Find: " - )" && bb +goto:"$file" + )" && bbcmd goto:"$file" /: # Pick a file file="$(find -mindepth 1 -maxdepth 1 -printf '%P\0' | pick "Pick: ")" || exit - expr "$file" : "\..*" >/dev/null && ! [ "$BBDOTFILES" ] && bb +dotfiles - bb +goto:"$file" + expr "$file" : "\..*" >/dev/null && ! [ "$BBDOTFILES" ] && bbcmd dotfiles + bbcmd goto:"$file" Ctrl-g: # Go to directory - ask goto "Go to directory: " && bb +cd:"$goto" + ask goto "Go to directory: " && bbcmd cd:"$goto" m: # Mark this directory - ask mark "Mark: " && ln -s "$PWD" ~/.config/bb/marks/"$mark" + 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" ': # Go to a marked directory - mark="$(find ~/.config/bb/marks/ -mindepth 1 -printf '%P\0' | pick "Jump to: ")" && - bb +cd:"$(readlink -f ~/.config/bb/marks/"$mark")" + [ -d "$XDG_CONFIG_HOME"/bb/marks ] || exit + mark="$(find "$XDG_CONFIG_HOME"/bb/marks/ -mindepth 1 -type l -printf '%P\0' | pick "Jump to: ")" && + bbcmd cd:"$(readlink -f "$XDG_CONFIG_HOME"/bb/marks/"$mark")" -,Backspace: # Go to previous directory - [ "$BBPREVPATH" ] && bb +cd:"$BBPREVPATH" + [ "$BBPREVPATH" ] && bbcmd cd:"$BBPREVPATH" ;: # Show selected files - bb +cd:'<selection>' + bbcmd cd:'<selection>' 0: # Go to intitial directory - bb +cd:"$BBINITIALPATH" + bbcmd cd:"$BBINITIALPATH" g,Home: # Go to first file - bb +move:0 + bbcmd move:0 G,End: # Go to last file - bb +move:100%n + bbcmd move:100%n PgDn: # Page down - bb +scroll:+100% + bbcmd scroll:+100% PgUp: # Page up - bb +scroll:-100% + bbcmd scroll:-100% Ctrl-d: # Half page down - bb +scroll:+50% + bbcmd scroll:+50% Ctrl-u: # Half page up - bb +scroll:-50% + bbcmd scroll:-50% Mouse wheel down: # Scroll down - bb +scroll:+3 + bbcmd scroll:+3 Mouse wheel up: # Scroll up - bb +scroll:-3 + bbcmd scroll:-3 Section: File Selection v,V,Space: # Toggle selection at cursor - bb +toggle + bbcmd toggle Escape: # Clear selection - bb +deselect + bbcmd deselect S: # Select pattern - ask patt "Select: " && eval bb +select: "$patt" + ask patt "Select: " && eval bbcmd select: "$patt" U: # Unselect pattern - ask patt "Unselect: " && eval bb +deselect: "$patt" + ask patt "Unselect: " && eval bbcmd deselect: "$patt" Ctrl-s: # Save the selection [ $# -gt 0 ] && ask savename "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' "$@" > ~/.config/bb/"$savename" + 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 ~/.config/bb/ -mindepth 1 -name '*.sel' -printf '%P\0' | pick "Load selection: ")" && - cat ~/.config/bb/"$loadpath" | bb +deselect +select: + loadpath="$(find "$XDG_DATA_HOME"/bb/ -mindepth 1 -name '*.sel' -printf '%P\0' | pick "Load selection: ")" && + cat "$XDG_DATA_HOME"/bb/"$loadpath" | bbcmd deselect select: J: # Spread selection down - bb +spread:+1 + bbcmd spread:+1 K: # Spread selection up - bb +spread:-1 + bbcmd spread:-1 Shift-Home: # Spread the selection to the top - bb +spread:0 + bbcmd spread:0 Shift-End: # Spread the selection to the bottom - bb +spread:100%n + bbcmd spread:100%n Ctrl-a: # Select all files here - bb +select + bbcmd select Section: File Actions Left click: # Move cursor to file if [ "$BBCLICKED" = "<column label>" ]; then - bb +sort:"~$BBMOUSECOL" + bbcmd sort:"~$BBMOUSECOL" elif [ "$BBCLICKED" -a "$BBMOUSECOL" = "*" ]; then - bb +toggle:"$BBCLICKED" + bbcmd toggle:"$BBCLICKED" elif [ "$BBCLICKED" ]; then - bb +goto:"$BBCLICKED" + bbcmd goto:"$BBCLICKED" fi Enter,Double left click: # Open file/directory - if [ -d "$BBCURSOR" ]; then bb +cd:"$BBCURSOR"; + if [ -d "$BBCURSOR" ]; then bbcmd cd:"$BBCURSOR"; elif [ "$(uname)" = "Darwin" ]; then if expr "$(file -bI "$BBCURSOR")" : '\(text/\|inode/empty\)' >/dev/null; then $EDITOR "$BBCURSOR"; else open "$BBCURSOR"; fi @@ -111,23 +116,23 @@ 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" && bb +deselect:"$BBCURSOR" +refresh + 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 "$@" && bb +deselect +refresh + confirm && 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." && - bb +deselect +refresh && for f; do bb +sel:"$(basename "$f")"; 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" && bb +refresh + 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..." && for f; do if [ "./$(basename "$f")" -ef "$f" ]; then cp -ri "$f" "$f.copy" || break; - else cp -ri "$f" . || break; fi; done; printf 'done.' && bb +refresh + 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 File) @@ -140,7 +145,7 @@ Ctrl-n: # New file/directory ;; *) exit ;; - esac && bb +goto:"$name" +refresh || pause + esac && bbcmd goto:"$name" refresh || pause p: # Page through a file with `less` less -XK "$BBCURSOR" r,F2: # Rename a file @@ -149,9 +154,9 @@ r,F2: # Rename a file [ "$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" && bb +refresh && - while [ $# -gt 0 ]; do "$1" = "$BBCURSOR" && bb +deselect:"$BBCURSOR" +select:"$r"; shift; done && - bb +goto:"$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 for f; do ask newname "Rename $(printf "\033[33m%s\033[39m" "$(basename "$f")"): " "$(basename "$f")" || break; @@ -160,10 +165,10 @@ R: # Rename all selected files [ -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; } - bb +deselect:"$f" +select:"$r"; - [ "$f" = "$BBCURSOR" ] && bb +goto:"$r"; + bbcmd deselect:"$f" select:"$r"; + [ "$f" = "$BBCURSOR" ] && bbcmd goto:"$r"; done; - bb +refresh + bbcmd refresh Ctrl-r: # Regex rename files 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; }; @@ -172,33 +177,33 @@ Ctrl-r: # Regex rename files confirm || exit 1 if [ $# -eq 0 ]; then set -- *; ! [ -e "$1" ] && exit; fi rename -i "$patt" "$rep" "$@" - bb +deselect +refresh + bbcmd deselect refresh Section: Shell Commands :: # Run a command - ask cmd ':' && sh -c "$BBSHELLFUNC$cmd" -- "$@"; bb +r; pause + ask cmd ':' && sh -c "$BBSHELLFUNC$cmd" -- "$@"; bbcmd refresh; pause |: # Pipe selected files to a command - ask cmd '|' && printf '%s\n' "$@" | sh -c "$BBSHELLFUNC$cmd"; bb +r; pause + ask cmd '|' && printf '%s\n' "$@" | sh -c "$BBSHELLFUNC$cmd"; bbcmd refresh; pause >: # Open a shell - tput rmcup; tput cvvis; $SHELL; bb +r + tput rmcup; tput cvvis; $SHELL; bbcmd refresh f: # Resume suspended process - bb +fg + bbcmd fg Section: Viewing Options s: # Sort by... ask1 sort "Sort (n)ame (s)ize (m)odification (c)reation (a)ccess (r)andom (p)ermissions: " && - bb +sort:"~$sort" + bbcmd sort:"~$sort" ---,#: # Set columns ask columns "Set columns (*)selected (a)ccessed (c)reated (m)odified (n)ame (p)ermissions (r)andom (s)ize: " && - bb +col:"$columns" + bbcmd col:"$columns" .: # Toggle dotfile visibility - bb +dotfiles + bbcmd dotfiles i: # Toggle interleaving files and directories - bb +interleave + bbcmd interleave F5: # Refresh view - bb +refresh + bbcmd refresh Ctrl-b: # Bind a key to a script ask1 key "Press key to bind..." && echo && ask script "Bind script: " && - bb +bind:"$key":"{ $script; } || pause" || pause + bbcmd bind:"$key":"{ $script; } || pause" || pause Section: User Bindings |
