aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2019-05-28 23:51:20 -0700
committerBruce Hill <bruce@bruce-hill.com>2019-05-28 23:51:20 -0700
commitd38e1661517da0dbaea85b9c81d4607b78cb730b (patch)
tree77921bd865c26149e92b1c1f44d46164a0108032
parentf070b5da0340235fc58b011fe43b8d44c6197463 (diff)
Simplified the opts API a bit, cleaned up the code a little, added a
toggle_file() function
-rw-r--r--bb.c114
-rw-r--r--config.def.h20
2 files changed, 83 insertions, 51 deletions
diff --git a/bb.c b/bb.c
index f716a14..c6df90c 100644
--- a/bb.c
+++ b/bb.c
@@ -33,6 +33,7 @@
#define MAX(a,b) ((a) < (b) ? (b) : (a))
#define MIN(a,b) ((a) > (b) ? (b) : (a))
#define IS_SELECTED(p) (((p)->atme) != NULL)
+#define OPTNUM(o) ((int)((o) & ~'0'))
static const char *T_ENTER_BBMODE = T_OFF(T_SHOW_CURSOR) T_ON(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR);
static const char *T_LEAVE_BBMODE = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR ";" T_ALT_SCREEN);
@@ -92,6 +93,7 @@ typedef struct bb_s {
int nfiles;
int scroll, cursor;
char options[128]; // General purpose options
+ char initialopts[128]; // Initial values of the options (after startupcmds)
char *marks[128]; // Mapping from key to directory
int colwidths[10];
} bb_t;
@@ -113,6 +115,7 @@ static int find_file(bb_t *bb, const char *name);
static void clear_selection(bb_t *bb);
static void select_file(bb_t *bb, entry_t *e);
static void deselect_file(bb_t *bb, entry_t *e);
+static void toggle_file(bb_t *bb, entry_t *e);
static void set_cursor(bb_t *bb, int i);
static void set_scroll(bb_t *bb, int i);
static entry_t* load_entry(const char *path);
@@ -227,7 +230,6 @@ int run_cmd_on_selection(bb_t *bb, const char *cmd)
{
pid_t child;
sig_t old_handler = signal(SIGINT, SIG_IGN);
-
if ((child = fork()) == 0) {
signal(SIGINT, SIG_DFL);
// TODO: is there a max number of args? Should this be batched?
@@ -238,14 +240,12 @@ int run_cmd_on_selection(bb_t *bb, const char *cmd)
args[i++] = "-c";
args[i++] = (char*)cmd;
#ifdef __APPLE__
- args[i++] = "--";
+ args[i++] = "--"; // ensure files like "-i" are not interpreted as flags for sh
#endif
entry_t *first = bb->firstselected ? bb->firstselected : bb->files[bb->cursor];
for (entry_t *e = first; e; e = e->next) {
- if (i >= space) {
- space += 100;
- args = memcheck(realloc(args, space*sizeof(char*)));
- }
+ if (i >= space)
+ args = memcheck(realloc(args, (space += 100)*sizeof(char*)));
args[i++] = e->fullname;
}
args[i] = NULL;
@@ -263,7 +263,6 @@ int run_cmd_on_selection(bb_t *bb, const char *cmd)
int status;
waitpid(child, &status, 0);
-
signal(SIGINT, old_handler);
return status;
}
@@ -530,6 +529,7 @@ void render(bb_t *bb, int lazy)
move_cursor(tty_out, --x, 0);
fputs("\033[0;2m]\033[1m", tty_out);
for (int o = 127, nopts = 0; o > 0; --o) {
+ if (bb->options[o] == bb->initialopts[o]) continue;
if (bb->options[o] <= 0) continue;
if (nopts > 0) {
x -= 1;
@@ -562,9 +562,11 @@ int compare_files(void *v, const void *v1, const void *v2)
char sort = bb->options['s'];
int sign = bb->options['r'] ? -1 : 1;
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
- // *always* put ".." before everything else
+ // *always* put ".." before everything else, then "."
int diff = (strcmp(f1->name, "..") == 0) - (strcmp(f2->name, "..") == 0);
if (diff) return -diff;
+ diff = (strcmp(f1->name, ".") == 0) - (strcmp(f2->name, ".") == 0);
+ if (diff) return -diff;
if (!bb->options['i']) {
// Unless interleave mode is on, sort dirs before files
int d1 = S_ISDIR(f1->info.st_mode) || (S_ISLNK(f1->info.st_mode) && S_ISDIR(f1->linkedmode));
@@ -650,6 +652,7 @@ void select_file(bb_t *bb, entry_t *e)
{
if (IS_SELECTED(e)) return;
if (strcmp(e->name, "..") == 0) return;
+ if (strcmp(e->name, ".") == 0) return;
if (bb->firstselected)
bb->firstselected->atme = &e->next;
e->next = bb->firstselected;
@@ -659,7 +662,7 @@ void select_file(bb_t *bb, entry_t *e)
}
/*
- * Deselect a file, return 1 if this changed anything, 0 otherwise
+ * Deselect a file
*/
void deselect_file(bb_t *bb, entry_t *e)
{
@@ -674,6 +677,15 @@ void deselect_file(bb_t *bb, entry_t *e)
}
/*
+ * Toggle a file's selection state
+ */
+void toggle_file(bb_t *bb, entry_t *e)
+{
+ if (IS_SELECTED(e)) deselect_file(bb, e);
+ else select_file(bb, e);
+}
+
+/*
* Set bb's file cursor to the given index (and adjust the scroll as necessary)
*/
void set_cursor(bb_t *bb, int newcur)
@@ -804,10 +816,19 @@ void populate_files(bb_t *bb, const char *path)
strcpy(pathbuf, path);
pathbuf[pathlen] = '/';
for (struct dirent *dp; (dp = readdir(dir)) != NULL; ) {
- if (dp->d_name[0] == '.' && dp->d_name[1] == '\0')
- continue;
- if (!bb->options['.'] && dp->d_name[0] == '.' && !(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
- continue;
+ /*
+ * Bit 1: 0 = hide ".." 1 = show ".."
+ * Bit 1: 0 = hide "." 1 = show "."
+ * Bit 2: 0 = hide .anything, 1 = show .anything
+ */
+ if (dp->d_name[0] == '.') {
+ int o = OPTNUM(bb->options['.']);
+ if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
+ if (!(o & 1)) continue;
+ } else if (dp->d_name[1] == '\0') {
+ if (!(o & 2)) continue;
+ } else if (!(o & 4)) continue;
+ }
if ((size_t)bb->nfiles >= filecap) {
filecap += 100;
bb->files = memcheck(realloc(bb->files, filecap*sizeof(entry_t*)));
@@ -939,26 +960,39 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
if (!value) value = bb->files[bb->cursor]->name;
int f = find_file(bb, value);
if (f < 0) return BB_INVALID;
- entry_t *e = bb->files[f];
- if (IS_SELECTED(e)) deselect_file(bb, e);
- else select_file(bb, e);
+ toggle_file(bb, bb->files[f]);
return f == bb->cursor ? BB_NOP : BB_REFRESH;
}
case 'o': { // options:
if (!value) return BB_INVALID;
- for (char *o = value; *o > 0; ) {
- switch (o[1]) {
- case '!': bb->options[(int)*(o++)] = 0; break;
- case '~': bb->options[(int)*o] = bb->options[(int)*o] ? 0 : '1';
- o++;
- break;
- case '=': bb->options[(int)*o] = o[2];
- o += 2;
- break;
- default: bb->options[(int)*o] = '1'; break;
+
+ for (char *key = value; *key; ) {
+ char *op = key;
+ while (*op != '\0' && *op != '^' && *op != '=' && *op != '%')
+ ++op;
+ char *nextkey = op;
+ for ( ; key < op; key++)
+ switch (*op) {
+ case '\0': case ' ': case ',':
+ bb->options[(int)*key] = bb->initialopts[(int)*key];
+ break;
+ case '%': {
+ int n = (int)strtol(op+1, &nextkey, 10);
+ if (n < 0)
+ bb->options[(int)*key] = (char)((OPTNUM(bb->options[(int)*key]) - n - 1) % (-n));
+ else
+ bb->options[(int)*key] = (char)((OPTNUM(bb->options[(int)*key]) + 1) % n);
+ bb->options[(int)*key] += '0';
+ break;
+ }
+ case '=':
+ bb->options[(int)*key] = op[1] == '0' ? 0 : op[1];
+ nextkey = op + 2;
+ break;
+ default: bb->options[(int)*key] = '1'; break;
}
- o++;
- while (*o == ' ' || *o == ',') ++o;
+ key = nextkey;
+ while (*key == ' ' || *key == ',') ++key;
}
populate_files(bb, bb->path);
return BB_REFRESH;
@@ -970,8 +1004,8 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
return BB_REFRESH;
} else {
int f = find_file(bb, value);
- if (f >= 0)
- select_file(bb, bb->files[f]);
+ if (f < 0) return BB_INVALID;
+ select_file(bb, bb->files[f]);
return f == bb->cursor ? BB_NOP : BB_REFRESH;
}
@@ -987,8 +1021,7 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
if (!lastslash) return BB_INVALID;
*lastslash = '\0'; // Split in two
char *real = realpath(path, NULL);
- if (!real || chdir(real))
- err("Not a valid path: %s\n", path);
+ if (!real || chdir(real)) return BB_INVALID;
populate_files(bb, real);
free(real); // estate
if (lastslash[1]) {
@@ -1025,10 +1058,8 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
if (cmd[0] == 's') { // spread:
int sel = IS_SELECTED(bb->files[oldcur]);
for (int i = bb->cursor; i != oldcur; i += (oldcur > i ? 1 : -1)) {
- if (sel && !IS_SELECTED(bb->files[i]))
- select_file(bb, bb->files[i]);
- else if (!sel && IS_SELECTED(bb->files[i]))
- deselect_file(bb, bb->files[i]);
+ if (sel != IS_SELECTED(bb->files[i]))
+ toggle_file(bb, bb->files[i]);
}
if (abs(oldcur - bb->cursor) > 1)
return BB_REFRESH;
@@ -1080,6 +1111,7 @@ void bb_browse(bb_t *bb, const char *path)
check_cmds = 1;
}
}
+ memcpy(bb->initialopts, bb->options, sizeof(bb->initialopts));
refresh:
lazy = 0;
@@ -1132,7 +1164,6 @@ void bb_browse(bb_t *bb, const char *path)
unlink(cmdfilename);
cmdpos = 0;
check_cmds = 0;
- //if (!lazy) goto redraw;
}
int key;
@@ -1154,8 +1185,10 @@ void bb_browse(bb_t *bb, const char *path)
if (x < mouse_x) continue;
if (bb->options['s'] == bb->options['0' + col])
bb->options['r'] = !bb->options['r'];
- else
+ else {
bb->options['s'] = bb->options['0' + col];
+ bb->options['r'] = 0;
+ }
qsort_r(bb->files, (size_t)bb->nfiles, sizeof(entry_t*), bb, compare_files);
goto refresh;
}
@@ -1163,10 +1196,7 @@ void bb_browse(bb_t *bb, const char *path)
} else if (mouse_y >= 2 && bb->scroll + (mouse_y - 2) < bb->nfiles) {
int clicked = bb->scroll + (mouse_y - 2);
if (mouse_x == 0) {
- if (IS_SELECTED(bb->files[clicked]))
- deselect_file(bb, bb->files[clicked]);
- else
- select_file(bb, bb->files[clicked]);
+ toggle_file(bb, bb->files[clicked]);
goto redraw;
}
set_cursor(bb, clicked);
diff --git a/config.def.h b/config.def.h
index 60b445e..6bcfea3 100644
--- a/config.def.h
+++ b/config.def.h
@@ -29,7 +29,7 @@
jump:<key> Jump to the mark associated with <key>
mark:<key>[=<path>] Associate <key> with <path> (or current dir, if blank)
move:<num*> Move the cursor a numeric amount
- option:(<o>[=<v>|~|!])+ Set options (see below)
+ option:(<o>[=<v>|%n])+ Set options (see below)
quit Quit bb
refresh Refresh the file listing
scroll:<num*> Scroll the view a numeric amount
@@ -40,14 +40,15 @@
Currently supported options:
's': sort, one of (n)ame (s)ize (c)reation (m)odification (a)ccess (p)ermission (r)andom
'r': reverse-sort (boolean)
- '.': show dotfiles (boolean)
+ '.': dotfiles visibility (bit 1: "..", bit 2: ".", bit 3: .whatever)
'i': interleave files and directories (boolean), when false, directories are always at the top
'0'-'9': what to put in each of the (maximum of 10) columns (one of: [nscmap])
'A'-'J': how wide to make each column (A -> column 0, etc.). 0 means
minimum width, 1+ means divide the free space proportionally among all
nonzero columns
- The postfix operator '!' sets an option to 0, '~' toggles an option, '=' assigns an option
- to the following character value, and no postfix operator sets an option to 1.
+ The postfix operator '=' assigns an option to the following character
+ value, and '%'(+/-)n increments or decrements the value and takes the value
+ modulo n, and no postfix operator restores the initial value.
Internally, bb will write the commands (NUL terminated) to $BBCMD, if
$BBCMD is set, and read the file when file browsing resumes. These commands
@@ -73,7 +74,7 @@
#define SORT_INDICATOR "↓"
#define RSORT_INDICATOR "↑"
-#define SELECTED_INDICATOR "\033[33;7m \033[0m"
+#define SELECTED_INDICATOR "\033[31;7m \033[0m"
#define NOT_SELECTED_INDICATOR " "
#define TITLE_COLOR "\033[32;1m"
@@ -108,7 +109,7 @@ const char *startupcmds[] = {
"+mark:0", "+mark:~=~", "+mark:h=~", "+mark:/=/", "+mark:c=~/.config",
"+mark:l=~/.local",
// Default column and sorting options:
- "+opt:0=s,1=m,2=p,3=n,s=n,D=1",
+ "+opt:0=s,1=m,2=p,3=n,s=n,D=1,.=1",
NULL, // NULL-terminated array
};
@@ -190,14 +191,15 @@ done)/*ENDQUOTE*/, "Regex rename files", AT_CURSOR},
"Regex select files"},
{{'J'}, "+spread:+1", "Spread selection down"},
{{'K'}, "+spread:-1", "Spread selection up"},
+ {{'o'}, "bb \"+opts:`bb '?Options: '`\"", "Set bb options"},
{{'s'}, "read -n1 -p 'Sort \033[1m(a)\033[22mlphabetic "
"\033[1m(s)\033[22mize \033[1m(m)\033[22modification \033[1m(c)\033[22mcreation "
"\033[1m(a)\033[22maccess \033[1m(r)\033[22mandom \033[1m(p)\033[22mermissions:\033[0m ' sort "
"&& bb \"+opt:s=$sort\"", "Sort by..."},
- {{'#'}, "cols=`bb '?Set columns: '` && "
- "bb \"+opt:`echo $cols 0 | fold -w1 | sed 10q | nl -v0 -s= -w1 | paste -sd '\\0' -`\"",
+ {{'#'}, "cols=`bb '?Set columns: '` && bb '+opt:ABCDEFGHIJ=0' && "
+ "bb \"+opt:`echo \"$cols \" | fold -w1 | sed 10q | nl -v0 -s= -w1 | paste -sd '\\0' -`\"",
"Set columns"},
- {{'.'}, "+opt:.~", "Toggle dotfiles"},
+ {{'.'}, "bb '+opt:.%8' +refresh", "Toggle dotfiles"},
{{'g', KEY_HOME}, "+move:0", "Go to first file"},
{{'G', KEY_END}, "+move:100%n", "Go to last file"},
{{KEY_ESC}, "+deselect:*", "Clear selection"},