diff --git a/Makefile b/Makefile index 7beca36..7d94498 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,21 @@ PREFIX= CC=gcc -CFLAGS=-O0 -std=gnu99 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L \ - -Wall -Wpedantic -Wno-unknown-pragmas -fsanitize=address -fno-omit-frame-pointer +CFLAGS=-O0 -std=gnu99 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L +CWARN= -Wall -Wpedantic -Wno-unknown-pragmas -fsanitize=address -fno-omit-frame-pointer UNAME := $(shell uname) ifeq ($(UNAME),Darwin) -CFLAGS += -D_DARWIN_C_SOURCE -Weverything -Wno-missing-field-initializers -Wno-padded\ +CFLAGS += -D_DARWIN_C_SOURCE +CWARN += -Weverything -Wno-missing-field-initializers -Wno-padded\ -Wno-missing-noreturn -Wno-cast-qual endif LIBS= NAME=bb G=-g +ifneq (, $(shell which ask)) +CFLAGS += -D'ASKECHO(prompt)="ask \"" prompt "\""' -D'FUZZY(prompt)="ask \"" prompt "\""' +endif + all: $(NAME) clean: @@ -20,7 +25,7 @@ config.h: cp config.def.h config.h $(NAME): $(NAME).c bterm.h config.h - $(CC) $(NAME).c $(LIBS) $(CFLAGS) $(G) -o $(NAME) + $(CC) $(NAME).c $(LIBS) $(CFLAGS) $(CWARN) $(G) -o $(NAME) install: $(NAME) @prefix="$(PREFIX)"; \ diff --git a/bb.c b/bb.c index 34baa34..c6c53f4 100644 --- a/bb.c +++ b/bb.c @@ -1411,18 +1411,6 @@ int main(int argc, char *argv[]) int i; for (i = 1; i < argc; i++) { - if (argv[i][0] == '?') { - if (cmdfd != -1) - close(cmdfd); - init_term(); - char *line = breadline(tty_in, tty_out, argv[i] + 1, argv[i+1]); - close_term(); - if (!line) return 1; - fputs(line, stdout); - free(line); - fflush(stdout); - return 0; - } if (argv[i][0] == '+') { write(cmdfd, argv[i]+1, strlen(argv[i]+1)+1); continue; diff --git a/bterm.h b/bterm.h index b6441fa..dbade7a 100644 --- a/bterm.h +++ b/bterm.h @@ -109,7 +109,6 @@ int bgetkey(FILE *in, int *mouse_x, int *mouse_y, int timeout); const char *bkeyname(int key); -char *breadline(FILE *in, FILE *out, const char *prompt, const char *initial); static inline int nextchar(int fd, int timeout) { @@ -302,129 +301,5 @@ const char *bkeyname(int key) return NULL; } -/** - * A basic readline implementation. No history, but all the normal editing - * key bindings like Ctrl-U to clear to the beginning of the line, backspace, - * arrow keys, etc. - * - * Takes an input file and output file, and an optional prompt and returns - * a malloc'd string containing the user's input or NULL if an error occurred - * or if the user hit Escape or Ctrl-c. - */ -char *breadline(FILE *in, FILE *out, const char *prompt, const char *initial) -{ - size_t cap = initial ? strlen(initial) + 100 : 100; - char *buf = calloc(cap, 1); - if (!buf) return NULL; - int i = 0, len = 0; - if (prompt) { - fputs("\033[1G\033[K\033[37;1m", out); - fputs(prompt, out); - fputs("\033[0m", out); - } - if (initial) { - strcpy(buf, initial); - len = (int)strlen(initial); - i = len; - fputs(initial, out); - } - // Show cursor and set to blinking line: - fputs("\033[?25h\033[5 q", out); - while (1) { - fflush(out); - int key = bgetkey(in, NULL, NULL, -1); - switch (key) { - case -1: case -2: case -3: continue; - case '\r': - // TODO: support backslash-enter - goto finished; - case KEY_CTRL_C: case KEY_ESC: - free(buf); - buf = NULL; - goto finished; - case KEY_CTRL_A: - if (i > 0) { - fprintf(out, "\033[%dD", i); - i = 0; - } - break; - case KEY_CTRL_E: - if (i < len) { - fprintf(out, "\033[%dC", len-i); - i = len; - } - break; - case KEY_CTRL_U: { - int to_clear = i; - if (to_clear) { - memmove(buf, buf+i, (size_t)(len-i)); - buf[len -= i] = 0; - i = 0; - fprintf(out, "\033[%dD\033[K", to_clear); - if (len > 0) - fprintf(out, "%s\033[%dD", buf, len); - } - break; - } - case KEY_CTRL_K: - if (i < len) { - buf[len = i] = 0; - fputs("\033[K", out); - } - break; - case KEY_BACKSPACE: case KEY_BACKSPACE2: - if (i > 0) { - --i; - memmove(buf+i, buf+i+1, (size_t)(len-i)); - buf[--len] = 0; - if (i == len) fputs("\033[D \033[D", out); - else fprintf(out, "\033[D%s\033[K\033[%dD", buf+i, len-i); - } - break; - case KEY_DELETE: case KEY_CTRL_D: - if (i < len) { - memmove(buf+i, buf+i+1, (size_t)(len-i)); - buf[--len] = 0; - if (i == len) fputs(" \033[D", out); - else fprintf(out, "%s\033[K\033[%dD", buf+i, len-i); - } - break; - case KEY_ARROW_LEFT: case KEY_CTRL_B: - if (i > 0) { - --i; - fputs("\033[D", out); - } - break; - case KEY_ARROW_RIGHT: case KEY_CTRL_F: - if (i < len) { - ++i; - fputs("\033[C", out); - } - break; - default: - if (' ' <= key && key <= '~') { - if (len + 1 >= (int)cap) { - cap += 100; - buf = realloc(buf, cap); - if (!buf) goto finished; - } - if (i < len) - memmove(buf+i+1, buf+i, (size_t)(len-i+1)); - buf[i++] = (char)key; - buf[++len] = 0; - if (i == len) - fputc(key, out); - else - fprintf(out, "%c%s\033[%dD", (char)key, buf+i, len-i); - } - break; - } - } - finished: - // Reset cursor to block - fputs("\033[1G\033[1 q\033[2K", out); - return buf; -} - #endif // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 diff --git a/config.def.h b/config.def.h index 83d83c3..b9f3f18 100644 --- a/config.def.h +++ b/config.def.h @@ -76,6 +76,22 @@ #define PAUSE " read -n1 -p '\033[2mPress any key to continue...\033[0m\033[?25l'" +#ifndef FUZZY +#define FUZZY(prompt) "fzy --prompt=\"" prompt "\"" +#endif + +#ifndef ASK +#ifdef ASKECHO +#define ASK(var, prompt) var "=\"$(" ASKECHO(prompt) ")\"" +#else +#define ASK(var, prompt) "read -p \"" prompt "\" " var +#endif +#endif + +#ifndef ASKECHO +#define ASKECHO(prompt) ASK("REPLY", prompt) " && echo \"$REPLY\"" +#endif + #define NORMAL_TERM (1<<0) #define AT_CURSOR (1<<1) @@ -163,19 +179,20 @@ binding_t bindings[] = { {{' ','v','V'}, "+toggle", B("Toggle")" selection"}, {{KEY_ESC}, "+deselect:*", B("Clear")" selection"}, {{'e'}, "$EDITOR \"$@\"", B("Edit")" file in $EDITOR", NORMAL_TERM}, - {{KEY_CTRL_F}, "bb \"+g:`fzf`\"", B("Fuzzy search")" for file", NORMAL_TERM}, - {{'/'}, "bb \"+g:`ls -a|fzf`\"", B("Fuzzy select")" file", NORMAL_TERM}, + {{KEY_CTRL_F}, "bb \"+g:`find | "FUZZY("Find: ")"`\" +r", B("Fuzzy search")" for file"}, + {{'/'}, "bb \"+g:`ls -a | " FUZZY("Pick: ") "`\" +r", B("Fuzzy pick")" file"}, {{'d', KEY_DELETE}, "rm -rfi \"$@\"; bb '+de:*' +r", B("Delete")" files", AT_CURSOR}, {{'D'}, "rm -rf \"$@\"; bb '+de:*' +r", B("Delete")" files (without confirmation)"}, {{'M'}, "mv -i \"$@\" .; bb '+de:*' +r; for f; do bb \"+sel:`pwd`/`basename \"$f\"`\"; done", B("Move")" files to current directory"}, {{'c'}, "cp -i \"$@\" .; bb +r", B("Copy")" files to current directory"}, {{'C'}, "bb '+de:*'; for f; do cp \"$f\" \"$f.copy\" && bb \"+sel:$f.copy\"; done; bb +r", B("Clone")" files"}, - {{'n'}, "name=`bb '?New file: '` && touch \"$name\"; bb +r \"+goto:$name\"", B("New file")}, - {{'N'}, "name=`bb '?New dir: '` && mkdir \"$name\"; bb +r \"+goto:$name\"", B("New directory")}, - {{'|'}, "cmd=`bb '?|'` && printf '%s\\n' \"$@\" | sh -c \"$cmd\"; " PAUSE "; bb +r", + {{'n'}, ASK("name", "New file: ")" && touch \"$name\"; bb +r \"+goto:$name\"", B("New file")}, + {{'N'}, ASK("name", "New dir: ")" && mkdir \"$name\"; bb +r \"+goto:$name\"", B("New directory")}, + {{KEY_CTRL_G}, "bb \"+cd:`" ASKECHO("Go to directory: ") "`\" +r", B("Go to")" directory"}, + {{'|'}, ASK("cmd", "|") " && printf '%s\\n' \"$@\" | sh -c \"$cmd\"; " PAUSE "; bb +r", B("Pipe")" selected files to a command"}, - {{':'}, "sh -c \"`bb '?:'`\" -- \"$@\"; " PAUSE "; bb +refresh", + {{':'}, "sh -c \"`" ASKECHO(":") "`\" -- \"$@\"; " PAUSE "; bb +refresh", B("Run")" a command"}, {{'>'}, "$SHELL", "Open a "B("shell"), NORMAL_TERM}, {{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set "B("mark")}, @@ -185,7 +202,7 @@ binding_t bindings[] = { QUOTE( bb '+deselect:*' +refresh; for f; do - if renamed="$(dirname "$f")/$(bb '?Rename: ' "$(basename "$f")")" && + if renamed="$(dirname "$f")/$(ask --initial="$(basename "$f")" 'Rename: ')" && test "$f" != "$renamed" && mv -i "$f" "$renamed"; then test $BBSELECTED && bb "+select:$renamed"; elif test $BBSELECTED; then bb "+select:$f"; fi @@ -194,7 +211,7 @@ binding_t bindings[] = { {{'R'}, QUOTE( - if patt="`bb '?Rename pattern: ' 's/'`"; then true; else bb +r; exit; fi; + if patt="`ask --initial='s/' 'Rename pattern: '`"; then true; else bb +r; exit; fi; if sed -E "$patt" /dev/null 2>/dev/null && bb "+sel:$f"; done + patt=`ask 'Select pattern: '` && + for f in *; do echo "$f" | grep "$patt" >/dev/null 2>/dev/null && bb "+sel:$f"; done )/*ENDQUOTE*/, B("Regex select")" files"}, {{'J'}, "+spread:+1", B("Spread")" selection down"}, {{'K'}, "+spread:-1", B("Spread")" selection up"}, - {{'b'}, "bb \"+`bb '?bb +'`\"", "Run a "B("bb command")}, + {{'b'}, "bb \"+`"ASKECHO("bb +")"`\"", "Run a "B("bb command")}, {{'s'}, - ("read -n1 -p 'Sort "B("(a)")"lphabetic "B("(s)")"ize "B("(m)")"odification " - B("(c)")"creation "B("(a)")"access "B("(r)")"andom "B("(p)")"ermissions:\033[0m ' sort " + ("sort=\"$(ask -q 'Sort (n)ame (s)ize (m)odification (c)reation (a)ccess (r)andom " + "(p)ermissions: ' n s m c a r p)\"" "&& bb \"+sort:+$sort\""), B("Sort")" by..."}, - {{'#'}, "bb \"+col:`bb '?Set columns: '`\"", "Set "B("columns")}, + {{'#'}, "bb \"+col:`"ASKECHO("Set columns: ")"`\"", "Set "B("columns")}, {{'.'}, "bb +dotfiles", "Toggle "B("dotfiles")}, {{'g', KEY_HOME}, "+move:0", "Go to "B("first")" file"}, {{'G', KEY_END}, "+move:100%n", "Go to "B("last")" file"},