aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2019-07-12 16:19:31 -0700
committerBruce Hill <bruce@bruce-hill.com>2019-07-12 16:19:31 -0700
commitda6bb9176341c322aef8d184761c77a488a4f302 (patch)
tree86dde99264ce169863ec90bbb6fc102950aabe37
parentf5c07a5a70a2610935d5846cfea18f8315efe404 (diff)
Overhaul to use a shell function for internal commands instead of `bb`
itself, which lets bb work when not yet installed.
-rw-r--r--bb.14
-rw-r--r--bb.c103
-rw-r--r--config.def.h21
3 files changed, 71 insertions, 57 deletions
diff --git a/bb.1 b/bb.1
index d1719b7..1ec42ac 100644
--- a/bb.1
+++ b/bb.1
@@ -8,7 +8,6 @@ bb \- A bitty browser for command line file management
[\fI-d\fR]
[\fI-s\fR]
[\fI-0\fR]
-[\fI-b\rR]
[\fI+command... \fR]
[\fIdirectory\fR]
.SH DESCRIPTION
@@ -27,9 +26,6 @@ newline-separated.
.B \-d
Print the current directory on exit.
-.B \-b
-Print the bb key bindings and exit.
-
.B \+command
From within \fBbb\fR, running \fBbb +command\fR will execute an internal bb
command for modifying bb's state.
diff --git a/bb.c b/bb.c
index 5d6f3cd..982be2e 100644
--- a/bb.c
+++ b/bb.c
@@ -135,7 +135,7 @@ static entry_t* load_entry(bb_t *bb, const char *path, int clear_dots);
static void* memcheck(void *p);
static void normalize_path(const char *root, const char *path, char *pbuf, int clear_dots);
static void populate_files(bb_t *bb, int samedir);
-static void print_bindings(void);
+static void print_bindings(int fd);
static bb_result_t process_cmd(bb_t *bb, const char *cmd);
static void render(bb_t *bb);
static int run_script(bb_t *bb, const char *cmd);
@@ -157,6 +157,19 @@ static const char *T_ENTER_BBMODE = T_OFF(T_SHOW_CURSOR ";" T_WRAP) T_ON(T_ALT_S
static const char *T_LEAVE_BBMODE = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR ";" T_ALT_SCREEN) T_ON(T_SHOW_CURSOR ";" T_WRAP);
static const char *T_LEAVE_BBMODE_PARTIAL = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR) T_ON(T_WRAP);
+static const char *bbcmdfn = "bb() {\n"
+" if $# -eq 0; then cat >> $BBCMD; return; fi\n"
+" for arg; do\n"
+" shift;\n"
+" if echo \"$arg\" | grep \"^+[^:]*:$\" >/dev/null 2>/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 \"$arg\\0\" >> $BBCMD\n"
+" done\n"
+"}\n";
+
// Global variables
static struct termios orig_termios, bb_termios;
static FILE *tty_out = NULL, *tty_in = NULL;
@@ -276,7 +289,10 @@ int run_script(bb_t *bb, const char *cmd)
size_t i = 0;
args[i++] = "sh";
args[i++] = "-c";
- args[i++] = (char*)cmd;
+ char *fullcmd = calloc(strlen(cmd) + strlen(bbcmdfn) + 1, sizeof(char));
+ strcpy(fullcmd, bbcmdfn);
+ strcat(fullcmd, cmd);
+ args[i++] = fullcmd;
args[i++] = "--"; // ensure files like "-i" are not interpreted as flags for sh
entry_t *first = bb->firstselected ? bb->firstselected : (bb->nfiles ? bb->files[bb->cursor] : NULL);
for (entry_t *e = first; e; e = e->selected.next) {
@@ -1043,6 +1059,25 @@ bb_result_t process_cmd(bb_t *bb, const char *cmd)
} else try_free_entry(e);
return BB_OK;
}
+ case 'h': { // +help
+ close_term();
+ int fds[2];
+ pipe(fds);
+ pid_t child = fork();
+ if (child != 0) {
+ close(fds[0]);
+ print_bindings(fds[1]);
+ waitpid(child, NULL, 0);
+ } else {
+ close(fds[1]);
+ dup2(fds[0], STDIN_FILENO);
+ char *args[] = {"sh", "-c", "$PAGER -rX", NULL};
+ execvp("sh", args);
+ }
+ init_term();
+ bb->dirty = 1;
+ return BB_OK;
+ }
case 'i': { // +interleave
set_bool(bb->interleave_dirs);
sort_files(bb);
@@ -1325,16 +1360,12 @@ void bb_browse(bb_t *bb, const char *path)
/*
* Print the current key bindings
*/
-void print_bindings(void)
+void print_bindings(int fd)
{
- struct winsize sz = {0};
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &sz);
- int width = sz.ws_col;
- if (width == 0) width = 80;
-
- char buf[1024];
+ char buf[1024], buf2[1024];
char *kb = "Key Bindings";
- printf("\n\033[33;1;4m\033[%dG%s\033[0m\n\n", (width-(int)strlen(kb))/2, kb);
+ sprintf(buf, "\n\033[33;1;4m\033[%dG%s\033[0m\n\n", (termwidth-(int)strlen(kb))/2, kb);
+ write(fd, buf, strlen(buf));
for (int i = 0; bindings[i].keys[0] > 0; i++) {
char *p = buf;
for (int j = 0; bindings[i].keys[j]; j++) {
@@ -1349,11 +1380,13 @@ void print_bindings(void)
p += sprintf(p, "\033[31m\\x%02X", key);
}
*p = '\0';
- printf("\033[1m\033[%dG%s\033[0m", width/2 - 1 - (int)strlen(buf), buf);
- printf("\033[0m\033[%dG\033[34m%s\033[0m", width/2 + 1, bindings[i].description);
- printf("\033[0m\n");
+ sprintf(buf2, "\033[1m\033[%dG%s\033[0m", termwidth/2 - 1 - (int)strlen(buf), buf);
+ write(fd, buf2, strlen(buf2));
+ sprintf(buf2, "\033[0m\033[%dG\033[34m%s\033[0m", termwidth/2 + 1, bindings[i].description);
+ write(fd, buf2, strlen(buf2));
+ write(fd, "\033[0m\n", strlen("\033[0m\n"));
}
- printf("\n");
+ write(fd, "\n", 1);
}
int main(int argc, char *argv[])
@@ -1362,10 +1395,8 @@ int main(int argc, char *argv[])
char sep = '\n';
int print_dir = 0, print_selection = 0;
- int cmd_args = 0;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '+') {
- ++cmd_args;
char *colon = strchr(argv[i], ':');
if (colon && !colon[1]) break;
continue;
@@ -1377,29 +1408,19 @@ int main(int argc, char *argv[])
if (argv[i][0] != '-' && !initial_path)
initial_path = argv[i];
}
- if (!initial_path && cmd_args == 0) initial_path = ".";
-
- int cmdfd = -1;
- if (initial_path) {
- has_initial_path:
- cmdfilename = memcheck(strdup(CMDFILE_FORMAT));
- if ((cmdfd = mkostemp(cmdfilename, O_APPEND)) == -1)
- err("Couldn't create tmpfile: '%s'", CMDFILE_FORMAT);
- // Set up environment variables
- char *curdepth = getenv("BB_DEPTH");
- int depth = curdepth ? atoi(curdepth) : 0;
- sprintf(depthstr, "%d", depth + 1);
- setenv("BB_DEPTH", depthstr, 1);
- setenv("BBCMD", cmdfilename, 1);
- } else {
- char *parent_bbcmd = getenv("BBCMD");
- if (!parent_bbcmd || parent_bbcmd[0] == '\0') {
- initial_path = ".";
- goto has_initial_path;
- }
- cmdfd = open(parent_bbcmd, O_WRONLY | O_APPEND | O_CREAT, 0644);
- if (cmdfd == -1) err("Couldn't open cmdfile: '%s'\n", parent_bbcmd);
- }
+ if (!initial_path)
+ initial_path = ".";
+
+ cmdfilename = memcheck(strdup(CMDFILE_FORMAT));
+ int cmdfd;
+ if ((cmdfd = mkostemp(cmdfilename, O_APPEND)) == -1)
+ err("Couldn't create tmpfile: '%s'", CMDFILE_FORMAT);
+ // Set up environment variables
+ char *curdepth = getenv("BB_DEPTH");
+ int depth = curdepth ? atoi(curdepth) : 0;
+ sprintf(depthstr, "%d", depth + 1);
+ setenv("BB_DEPTH", depthstr, 1);
+ setenv("BBCMD", cmdfilename, 1);
int i;
for (i = 1; i < argc; i++) {
@@ -1419,7 +1440,7 @@ int main(int argc, char *argv[])
if (strcmp(argv[i], "--help") == 0) {
usage:
printf("bb - an itty bitty console TUI file browser\n");
- printf("Usage: bb [-h/--help] [-s] [-b] [-d] [-0] (+command)* [path]\n");
+ printf("Usage: bb [-h/--help] [-s] [-d] [-0] (+command)* [path]\n");
return 0;
}
if (strcmp(argv[i], "--version") == 0) {
@@ -1442,8 +1463,6 @@ int main(int argc, char *argv[])
break;
case 's': print_selection = 1;
break;
- case 'b': print_bindings();
- return 0;
}
}
continue;
diff --git a/config.def.h b/config.def.h
index f8a8525..26edb97 100644
--- a/config.def.h
+++ b/config.def.h
@@ -105,14 +105,14 @@ typedef struct {
#define ASKECHO(prompt, initial) "ask --prompt=\"" prompt "\" --query=\"" initial "\""
#define ASK(var, prompt, initial) var "=\"$(" ASKECHO(prompt, initial) ")\""
#else
-#define ASK(var, prompt, initial) "read -ep \"" prompt "\" " var " </dev/tty >/dev/tty"
-#define ASKECHO(prompt, initial) "read -ep \"" prompt "\" </dev/tty >/dev/tty && echo \"$REPLY\""
+#define ASK(var, prompt, initial) "read -p \"" prompt "\" " var " </dev/tty >/dev/tty"
+#define ASKECHO(prompt, initial) "read -p \"" prompt "\" REPLY </dev/tty >/dev/tty && echo \"$REPLY\""
#endif
// Macros for picking from a list of options:
#ifndef PICK
#define PICK(prompt, initial) " { awk '{print length, $1}' | sort -n | cut -d' ' -f2- | "\
- "grep -i -m1 \"^$(" ASKECHO(prompt, initial) " | sed 's/./[^&]*[&]/g')\"; } "
+ "grep -i -m1 \"$(" ASKECHO(prompt, initial) " | sed 's;.;[^/&]*[&];g')\"; } "
#endif
// Display a spinning indicator if command takes longer than 10ms:
@@ -168,7 +168,7 @@ const char *startupcmds[] = {
* after editing.
*****************************************************************************/
binding_t bindings[] = {
- {{'?', KEY_F1}, "bb -b | $PAGER -rX", B("Help")" menu"},
+ {{'?', KEY_F1}, "+help", B("Help")" menu"},
{{'q', 'Q'}, "+quit", B("Quit")},
{{'j', KEY_ARROW_DOWN}, "+move:+1", B("Next")" file"},
{{'k', KEY_ARROW_UP}, "+move:-1", B("Previous")" file"},
@@ -191,9 +191,9 @@ binding_t bindings[] = {
"| "PICK("Find: ", "")")\"", B("Search")" for file"},
{{'/'}, "bb \"+goto:$(if test $BBDOTFILES; then find -mindepth 1 -maxdepth 1; else find -mindepth 1 -maxdepth 1 ! -path '*/.*'; fi "
"| "PICK("Pick: ", "")")\"", B("Pick")" file"},
- {{'d', KEY_DELETE}, "rm -rfi \"$@\" && bb +refresh +deselect: \"$@\" ||" PAUSE, B("Delete")" files"},
- {{'D'}, SPIN("rm -rf \"$@\"")" && bb +refresh +deselect: \"$@\" ||" PAUSE, B("Delete")" files (without confirmation)"},
- {{'M'}, SPIN("mv -i \"$@\" . && bb +refresh +deselect: \"$@\" && for f; do bb \"+sel:$(basename \"$f\")\"; done")" || "PAUSE,
+ {{'d', KEY_DELETE}, "rm -rfi \"$@\" && bb +refresh && bb +deselect: \"$@\" ||" PAUSE, B("Delete")" files"},
+ {{'D'}, SPIN("rm -rf \"$@\"")" && bb +refresh && bb +deselect: \"$@\" ||" PAUSE, B("Delete")" files (without confirmation)"},
+ {{'M'}, SPIN("mv -i \"$@\" . && bb +refresh && bb +deselect: \"$@\" && for f; do bb \"+sel:$(basename \"$f\")\"; done")" || "PAUSE,
B("Move")" files to current directory"},
{{'c'}, SPIN("cp -ri \"$@\" .")" && bb +r || "PAUSE, B("Copy")" files to current directory"},
{{'C'}, "for f; do "SPIN("cp -ri \"$f\" \"$f.copy\"")"; done && bb +r || "PAUSE,
@@ -205,7 +205,7 @@ binding_t bindings[] = {
B("Pipe")" selected files to a command"},
{{':'}, "sh -c \"$(" ASKECHO(":", "") ")\" -- \"$@\"; " PAUSE "; bb +refresh",
B("Run")" a command"},
- {{'>'}, "tput rmcup; $SHELL; bb +r", "Open a "B("shell")},
+ {{'>'}, "tput rmcup >/dev/tty; $SHELL; bb +r", "Open a "B("shell")},
{{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set "B("mark")},
{{'\''}, "read -n1 -p 'Jump: ' j && bb \"+jump:$j\"", B("Jump")" to mark"},
{{'r'},
@@ -229,7 +229,7 @@ binding_t bindings[] = {
" fi;"
"done", B("Regex rename")" files"},
{{'S'},
- ASK("patt", "Select pattern: ", "")" && bb +sel: $patt",
+ ASK("patt", "Select pattern: ", "")" && bb +sel: \"$patt\"",
B("Select")" file(s) by pattern"},
{{'J'}, "+spread:+1", B("Spread")" selection down"},
{{'K'}, "+spread:-1", B("Spread")" selection up"},
@@ -244,8 +244,7 @@ binding_t bindings[] = {
{{'g', KEY_HOME}, "+move:0", "Go to "B("first")" file"},
{{'G', KEY_END}, "+move:100%n", "Go to "B("last")" file"},
{{KEY_F5, KEY_CTRL_R}, "+refresh", B("Refresh")},
- {{KEY_CTRL_A}, "if test $BBDOTFILES; then find -mindepth 1 -maxdepth 1 -print0; else find -mindepth 1 -maxdepth 1 ! -path '*/.*' -print0; fi "
- "| xargs -0 bb +sel:",
+ {{KEY_CTRL_A}, "if test $BBDOTFILES; then find -mindepth 1 -maxdepth 1 -print0; else find -mindepth 1 -maxdepth 1 ! -path '*/.*' -print0; fi | bb +sel:",
B("Select all")" files in current directory"},
{{KEY_PGDN}, "+scroll:+100%", B("Page down")},
{{KEY_PGUP}, "+scroll:-100%", B("Page up")},