aboutsummaryrefslogtreecommitdiff
path: root/bb.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2019-05-21 03:59:30 -0700
committerBruce Hill <bruce@bruce-hill.com>2019-05-21 03:59:30 -0700
commitab461858437fb57d1ec529a1218d31de709bef24 (patch)
tree4c029ab38704d28b6b6ba41c7a659cbfcdd77b72 /bb.c
parent9786f9a525c7fffea49379aae05c14af0db1107b (diff)
Lotsa improvements
Diffstat (limited to 'bb.c')
-rw-r--r--bb.c235
1 files changed, 128 insertions, 107 deletions
diff --git a/bb.c b/bb.c
index 14079f1..237795d 100644
--- a/bb.c
+++ b/bb.c
@@ -24,7 +24,7 @@
#define MIN(a,b) ((a) > (b) ? (b) : (a))
#define MAX_PATH 4096
#define writez(fd, str) write(fd, str, strlen(str))
-#define IS_SELECTED(p) ((p)->next || (p)->prev)
+#define IS_SELECTED(p) ((p)->atme)
static const int SCROLLOFF = 5;
@@ -34,8 +34,9 @@ static int width, height;
static int mouse_x, mouse_y;
typedef struct entry_s {
- struct entry_s *next, *prev;
+ struct entry_s *next, **atme;
int visible : 1;
+ int d_isdir : 1;
ino_t d_ino;
__uint16_t d_reclen;
__uint8_t d_type;
@@ -50,6 +51,7 @@ typedef struct {
size_t nselected, nfiles;
int scroll, cursor;
struct timespec lastclick;
+ int showhidden : 1;
} bb_state_t;
static void err(const char *msg, ...);
@@ -163,6 +165,9 @@ static void render(bb_state_t *state)
writez(termfd, "\e[2J\e[0;1m"); // Clear, reset color + bold
term_move(0,0);
writez(termfd, state->path);
+
+ term_move(0,1);
+ writez(termfd, "\e[32m Size Date Bits Name");
writez(termfd, "\e[0m"); // Reset color
char fullpath[MAX_PATH];
@@ -173,21 +178,23 @@ static void render(bb_state_t *state)
entry_t *entry = files[i];
int x = 0;
- int y = i - state->scroll + 1;
+ int y = i - state->scroll + 2;
term_move(x, y);
// Selection box:
if (IS_SELECTED(entry))
- writez(termfd, "\e[43m \e[0m"); // Yellow BG
+ writez(termfd, "\e[43m \e[0m");
else
writez(termfd, " ");
- if (i != state->cursor && entry->d_type & DT_DIR) {
- writez(termfd, "\e[34m"); // Blue FG
- }
- if (i == state->cursor) {
- writez(termfd, "\e[7m"); // Reverse color
- }
+ if (i == state->cursor)
+ writez(termfd, "\e[30;47m");
+ else if (entry->d_isdir && entry->d_type == DT_LNK)
+ writez(termfd, "\e[36m");
+ else if (entry->d_isdir)
+ writez(termfd, "\e[34m");
+ else if (entry->d_type == DT_LNK)
+ writez(termfd, "\e[33m");
struct stat info = {0};
fullpath[pathlen] = '/';
@@ -205,7 +212,7 @@ static void render(bb_state_t *state)
j++;
}
char buf[16] = {0};
- sprintf(buf, "%6.*f%c ", j > 0 ? 1 : 0, bytes, units[j]);
+ sprintf(buf, "%6.*f%c │", j > 0 ? 1 : 0, bytes, units[j]);
writez(termfd, buf);
}
@@ -214,24 +221,38 @@ static void render(bb_state_t *state)
char buf[64];
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(info.st_mtime)));
writez(termfd, buf);
- writez(termfd, " ");
+ //writez(termfd, " ");
+ writez(termfd, " │ ");
+ }
+
+ {
+ // Permissions:
+ char buf[] = {
+ '0' + ((info.st_mode >> 6) & 7),
+ '0' + ((info.st_mode >> 3) & 7),
+ '0' + ((info.st_mode >> 0) & 7),
+ };
+ write(termfd, buf, 5);
+ writez(termfd, " │ ");
}
// Name:
write(termfd, entry->d_name, entry->d_namlen);
-
- if (entry->d_type & DT_DIR) {
+ if (entry->d_isdir)
writez(termfd, "/");
- }
+
if (entry->d_type == DT_LNK) {
char linkpath[MAX_PATH] = {0};
ssize_t pathlen;
if ((pathlen = readlink(entry->d_name, linkpath, sizeof(linkpath))) < 0)
err("readlink() failed");
- writez(termfd, "\e[36m -> "); // Cyan FG
+ //writez(termfd, "\e[36m -> "); // Cyan FG
+ writez(termfd, " -> ");
write(termfd, linkpath, pathlen);
+ if (entry->d_isdir)
+ writez(termfd, "/");
}
- writez(termfd, "\e[0m"); // Reset color and attributes
+ writez(termfd, " \e[0m"); // Reset color and attributes
}
term_move(0, height - 1);
@@ -244,7 +265,7 @@ static int compare_alpha(const void *v1, const void *v2)
{
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
int diff;
- diff = (f1->d_type & DT_DIR) - (f2->d_type & DT_DIR);
+ diff = -(f1->d_isdir - f2->d_isdir);
if (diff) return -diff;
const char *p1 = f1->d_name, *p2 = f2->d_name;
while (*p1 && *p2) {
@@ -385,8 +406,10 @@ static void explore(char *path)
while ((dp = readdir(dir)) != NULL) {
if (dp->d_name[0] == '.' && dp->d_name[1] == '\0')
continue;
+ if (!state.showhidden && dp->d_name[0] == '.' && !(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
+ continue;
// Hashed lookup from selected:
- if (state.firstselected) {
+ if (state.nselected > 0) {
for (int probe = ((int)dp->d_ino) % hashsize; selecthash[probe]; probe = (probe + 1) % hashsize) {
if (selecthash[probe]->d_ino == dp->d_ino) {
selecthash[probe]->visible = 1;
@@ -403,8 +426,14 @@ static void explore(char *path)
entry->d_ino = dp->d_ino;
entry->d_reclen = dp->d_reclen;
entry->d_type = dp->d_type;
+ entry->d_isdir = dp->d_type == DT_DIR;
+ if (!entry->d_isdir && entry->d_type == DT_LNK) {
+ struct stat statbuf;
+ if (stat(entry->d_fullname, &statbuf) == 0)
+ entry->d_isdir = S_ISDIR(statbuf.st_mode);
+ }
entry->d_namlen = dp->d_namlen;
- entry->next = NULL, entry->prev = NULL;
+ entry->next = NULL, entry->atme = NULL;
if (state.nfiles >= filecap) {
filecap += 100;
@@ -437,9 +466,7 @@ static void explore(char *path)
redraw:
render(&state);
skip_redraw:
- scrolloff = MIN(SCROLLOFF, height/2);
- //sleep(2);
- //if (1) goto done;
+ scrolloff = MIN(SCROLLOFF, (height-4)/2);
int key = term_get_event();
switch (key) {
case KEY_MOUSE_LEFT: {
@@ -448,8 +475,8 @@ static void explore(char *path)
double dt_ms = 1e3*(double)(clicktime.tv_sec - state.lastclick.tv_sec);
dt_ms += 1e-6*(double)(clicktime.tv_nsec - state.lastclick.tv_nsec);
state.lastclick = clicktime;
- if (mouse_y > 0 && state.scroll + (mouse_y - 1) < state.nfiles) {
- int clicked = state.scroll + (mouse_y - 1);
+ if (mouse_y > 0 && state.scroll + (mouse_y - 2) < state.nfiles) {
+ int clicked = state.scroll + (mouse_y - 2);
if (dt_ms > 200) {
// Single click
if (mouse_x == 0) {
@@ -473,43 +500,52 @@ static void explore(char *path)
goto done;
case KEY_CTRL_D:
- state.cursor = MIN(state.nfiles - 1, state.cursor + (height - 3) / 2);
- if (state.nfiles <= height - 3)
+ state.cursor = MIN(state.nfiles - 1, state.cursor + (height - 4) / 2);
+ if (state.nfiles <= height - 4)
goto redraw;
- state.scroll += (height - 3)/2;
- if (state.scroll > state.nfiles - (height - 3))
- state.scroll = state.nfiles - (height - 3);
+ state.scroll += (height - 4)/2;
+ if (state.scroll > state.nfiles - (height - 4))
+ state.scroll = state.nfiles - (height - 4);
goto redraw;
case KEY_CTRL_U:
- state.cursor = MAX(0, state.cursor - (height - 3) / 2);
- if (state.nfiles <= height - 3)
+ state.cursor = MAX(0, state.cursor - (height - 4) / 2);
+ if (state.nfiles <= height - 4)
goto redraw;
- state.scroll -= (height - 3)/2;
+ state.scroll -= (height - 4)/2;
if (state.scroll < 0)
state.scroll = 0;
goto redraw;
+ case 'g':
+ state.cursor = 0;
+ state.scroll = 0;
+ goto redraw;
+
+ case 'G':
+ state.cursor = state.nfiles - 1;
+ if (state.nfiles > height - 4)
+ state.scroll = state.nfiles - (height - 4);
+ goto redraw;
+
case ' ':
picked = state.cursor;
toggle:
+ if (picked == 0) goto skip_redraw;
if (IS_SELECTED(state.files[picked])) {
toggle_off:;
entry_t *e = state.files[picked];
- if (state.firstselected == e)
- state.firstselected = e->next;
- if (e->prev) e->prev->next = e->next;
- if (e->next) e->next->prev = e->prev;
- e->next = NULL;
- e->prev = NULL;
+ if (e->next) e->next->atme = e->atme;
+ *(e->atme) = e->next;
+ e->next = NULL, e->atme = NULL;
--state.nselected;
} else {
toggle_on:;
entry_t *e = state.files[picked];
- if (state.firstselected) {
- e->next = state.firstselected;
- state.firstselected->prev = e;
- }
+ if (state.firstselected)
+ state.firstselected->atme = &e->next;
+ e->next = state.firstselected;
+ e->atme = &state.firstselected;
state.firstselected = e;
++state.nselected;
}
@@ -518,7 +554,7 @@ static void explore(char *path)
case KEY_ESC:
for (entry_t *e = state.firstselected; e; e = e->next) {
e->next = NULL;
- e->prev = NULL;
+ e->atme = NULL;
if (!e->visible) free(e);
}
state.firstselected = NULL;
@@ -529,7 +565,7 @@ static void explore(char *path)
if (state.cursor >= state.nfiles - 1)
goto skip_redraw;
++state.cursor;
- if (state.cursor > state.scroll + height - 4 - scrolloff && state.scroll < state.nfiles - (height - 3)) {
+ if (state.cursor > state.scroll + height - 4 - 1 - scrolloff && state.scroll < state.nfiles - (height - 4)) {
++state.scroll;
}
goto redraw;
@@ -547,11 +583,14 @@ static void explore(char *path)
if (state.cursor < state.nfiles - 1) {
if (IS_SELECTED(state.files[state.cursor])) {
picked = ++state.cursor;
- goto toggle_on;
+ if (!IS_SELECTED(state.files[picked]))
+ goto toggle_on;
} else {
picked = ++state.cursor;
- goto toggle_off;
+ if (IS_SELECTED(state.files[picked]))
+ goto toggle_off;
}
+ goto redraw;
}
goto skip_redraw;
@@ -559,64 +598,42 @@ static void explore(char *path)
if (state.cursor > 0) {
if (IS_SELECTED(state.files[state.cursor])) {
picked = --state.cursor;
- goto toggle_on;
+ if (!IS_SELECTED(state.files[picked]))
+ goto toggle_on;
} else {
picked = --state.cursor;
- goto toggle_off;
+ if (IS_SELECTED(state.files[picked]))
+ goto toggle_off;
}
+ goto redraw;
}
goto skip_redraw;
case 'h':
- if (strcmp(state.path, "/") != 0) {
- char *p = strrchr(state.path, '/');
- if (p) strcpy(to_select, p+1);
- else to_select[0] = '\0';
- char tmp[MAX_PATH], tmp2[MAX_PATH];
- strcpy(tmp, state.path);
- strcat(tmp, "/");
- strcat(tmp, "..");
- if (!realpath(tmp, tmp2))
- err("realpath failed");
- free(path);
- path = malloc(strlen(tmp2) + 1);
- strcpy(path, tmp2);
- goto tail_call;
- }
- break;
+ picked = 0;
+ goto open_file;
+
+ case '.':
+ state.showhidden ^= 1;
+ goto tail_call;
case 'l': case '\r':
picked = state.cursor;
open_file:
{
- int is_dir = state.files[picked]->d_type & DT_DIR;
- if (state.files[picked]->d_type == DT_LNK) {
- char linkpath[MAX_PATH];
- if (readlink(state.files[picked]->d_name, linkpath, sizeof(linkpath)) < 0)
- err("readlink() failed");
- DIR *dir = opendir(linkpath);
- if (dir) {
- is_dir = 1;
- if (closedir(dir) < 0)
- err("Failed to close directory: %s", linkpath);
- }
- }
- if (is_dir) {
+ if (state.files[picked]->d_isdir) {
if (strcmp(state.files[picked]->d_name, "..") == 0) {
char *p = strrchr(state.path, '/');
if (p) strcpy(to_select, p+1);
else to_select[0] = '\0';
} else to_select[0] = '\0';
- char tmp[MAX_PATH], tmp2[MAX_PATH];
- strcpy(tmp, state.path);
- strcat(tmp, "/");
- strcat(tmp, state.files[picked]->d_name);
- if (!realpath(tmp, tmp2))
+ char tmp[MAX_PATH];
+ if (!realpath(state.files[picked]->d_fullname, tmp))
err("realpath failed");
free(path);
- path = malloc(strlen(tmp2) + 1);
- strcpy(path, tmp2);
+ path = calloc(strlen(tmp) + 1, sizeof(char));
+ strcpy(path, tmp);
goto tail_call;
} else {
char *name = state.files[picked]->d_name;
@@ -662,31 +679,35 @@ static void explore(char *path)
}
break;
- case 'x':
- if (state.nselected) {
- close_term();
-
- char *buf = NULL;
- size_t bufsize = 0;
- printf("> ");
- fflush(stdout);
- getline(&buf, &bufsize, stdin);
-
- int fd;
- pid_t child = run_cmd(NULL, &fd, "xargs %s", buf);
+ case '|': {
+ close_term();
+ char *buf = NULL;
+ size_t bufsize = 0;
+ printf("> ");
+ fflush(stdout);
+ getline(&buf, &bufsize, stdin);
+
+ int fd;
+ pid_t child = run_cmd(NULL, &fd, buf);
+ if (state.nselected > 0) {
write_selection(fd, state.firstselected);
- close(fd);
- waitpid(child, NULL, 0);
+ } else {
+ for (int i = 0; i < state.nfiles; i++) {
+ write(fd, state.files[i]->d_name, state.files[i]->d_namlen);
+ write(fd, "\n", 1);
+ }
+ }
+ close(fd);
+ waitpid(child, NULL, 0);
- printf("press enter to continue...");
- fflush(stdout);
- getline(&buf, &bufsize, stdin);
- free(buf);
+ printf("press enter to continue...");
+ fflush(stdout);
+ getline(&buf, &bufsize, stdin);
+ free(buf);
- init_term();
- goto redraw;
- }
- break;
+ init_term();
+ goto tail_call;
+ }
default:
goto skip_redraw;