Added a bit more functionality including a user input prompt

This commit is contained in:
Bruce Hill 2019-05-21 16:02:25 -07:00
parent 6957d67051
commit 2d90cc8fd6

232
bb.c
View File

@ -408,6 +408,68 @@ static int term_get_event()
return -1;
}
static char *input(const char *prompt, const char *starter)
{
size_t len = 0, capacity = MAX(100, starter ? strlen(starter)+1 : 0);
char *reply = calloc(capacity, 1);
if (!reply) err("allocation failure");
if (starter)
len = strcpy(reply, starter) - reply;
// Show cursor:
writez(termfd, "\e[?25h");
while (1) {
redraw:
term_move(0, height-1);
writez(termfd, "\e[K\e[33m");
writez(termfd, prompt);
writez(termfd, "\e[0m");
write(termfd, reply, len);
skip_redraw:;
int c = term_get_event();
switch (c) {
case KEY_BACKSPACE: case KEY_BACKSPACE2:
if (len > 0) {
reply[--len] = '\0';
goto redraw;
}
goto skip_redraw;
case KEY_CTRL_U:
if (len > 0) {
len = 0;
reply[0] = '\0';
goto redraw;
}
goto skip_redraw;
case KEY_CTRL_C: case KEY_ESC:
free(reply);
reply = NULL;
goto done;
case '\r':
goto done;
default:
if (' ' <= c && c <= '~') {
if (len + 1 >= capacity) {
capacity += 100;
reply = realloc(reply, capacity);
if (!reply)
err("allocation failure");
}
reply[len++] = c;
reply[len] = '\0';
goto redraw;
}
goto skip_redraw;
}
}
done:
// Hide cursor:
writez(termfd, "\e[?25l");
return reply;
}
static void explore(char *path, int print_dir, int print_selection)
{
char *tmp = path;
@ -420,27 +482,6 @@ static void explore(char *path, int print_dir, int print_selection)
tail_call:
state.path = path;
DIR *dir = opendir(state.path);
if (!dir)
err("Couldn't open dir: %s", state.path);
if (chdir(state.path) != 0)
err("Couldn't chdir into %s", state.path);
struct dirent *dp;
// Hash inode -> entry_t with linear probing
size_t hashsize = 2 * state.nselected;
entry_t **selecthash = calloc(hashsize, sizeof(entry_t*));
if (!selecthash)
err("Failed to allocate %d spaces for selecthash", hashsize);
for (entry_t *p = state.firstselected; p; p = p->next) {
int probe = ((int)p->d_ino) % hashsize;
while (selecthash[probe])
probe = (probe + 1) % hashsize;
selecthash[probe] = p;
}
if (state.files) {
for (int i = 0; i < state.nfiles; i++) {
entry_t *e = state.files[i];
@ -451,51 +492,74 @@ static void explore(char *path, int print_dir, int print_selection)
free(state.files);
state.files = NULL;
}
size_t pathlen = strlen(state.path);
size_t filecap = 0;
state.nfiles = 0;
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.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;
state.files[state.nfiles++] = selecthash[probe];
goto next_file;
state.path = path;
{ // Populate the file list:
// Hash inode -> entry_t with linear probing
size_t hashsize = 2 * state.nselected;
entry_t **selecthash = calloc(hashsize, sizeof(entry_t*));
if (!selecthash)
err("Failed to allocate %d spaces for selecthash", hashsize);
for (entry_t *p = state.firstselected; p; p = p->next) {
int probe = ((int)p->d_ino) % hashsize;
while (selecthash[probe])
probe = (probe + 1) % hashsize;
selecthash[probe] = p;
}
DIR *dir = opendir(state.path);
if (!dir)
err("Couldn't open dir: %s", state.path);
if (chdir(state.path) != 0)
err("Couldn't chdir into %s", state.path);
struct dirent *dp;
size_t pathlen = strlen(state.path);
size_t filecap = 0;
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.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;
state.files[state.nfiles++] = selecthash[probe];
goto next_file;
}
}
}
}
entry_t *entry = malloc(sizeof(entry_t) + pathlen + dp->d_namlen + 2);
strncpy(entry->d_fullname, state.path, pathlen);
entry->d_fullname[pathlen] = '/';
entry->d_name = &entry->d_fullname[pathlen + 1];
strncpy(entry->d_name, dp->d_name, dp->d_namlen + 1);
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->atme = NULL;
entry_t *entry = malloc(sizeof(entry_t) + pathlen + dp->d_namlen + 2);
strncpy(entry->d_fullname, state.path, pathlen);
entry->d_fullname[pathlen] = '/';
entry->d_name = &entry->d_fullname[pathlen + 1];
strncpy(entry->d_name, dp->d_name, dp->d_namlen + 1);
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->atme = NULL;
if (state.nfiles >= filecap) {
filecap += 100;
state.files = realloc(state.files, filecap*sizeof(entry_t*));
if (state.nfiles >= filecap) {
filecap += 100;
state.files = realloc(state.files, filecap*sizeof(entry_t*));
if (!state.files) err("allocation failure");
}
state.files[state.nfiles++] = entry;
next_file:;
}
state.files[state.nfiles++] = entry;
next_file:;
closedir(dir);
free(selecthash);
if (state.nfiles == 0) err("No files found (not even '..')");
}
closedir(dir);
free(selecthash);
if (state.nfiles == 0) err("No files found (not even '..')");
state.cursor = 0;
state.scroll = 0;
@ -649,7 +713,6 @@ static void explore(char *path, int print_dir, int print_selection)
e->atme = NULL;
if (!e->visible) free(e);
}
state.firstselected = NULL;
state.nselected = 0;
goto redraw;
@ -796,14 +859,20 @@ static void explore(char *path, int print_dir, int print_selection)
case 'D': {
int fd;
run_cmd(NULL, &fd, "xargs rm -rf");
if (state.nselected)
if (state.nselected > 0) {
write_selection(fd, state.firstselected);
else {
for (entry_t *e = state.firstselected; e; e = e->next) {
e->next = NULL;
e->atme = NULL;
if (!e->visible) free(e);
}
state.nselected = 0;
} else {
writez(fd, state.files[state.cursor]->d_fullname);
write(fd, "\n", 1);
}
close(fd);
break;
goto tail_call;
}
case 'p':
@ -812,34 +881,39 @@ static void explore(char *path, int print_dir, int print_selection)
run_cmd(NULL, &fd, "xargs -I {} cp {} .");
write_selection(fd, state.firstselected);
close(fd);
} else if (strcmp(state.files[state.cursor]->d_name, "..") != 0) {
run_cmd(NULL, NULL, "cp %s %s.copy", state.files[state.cursor]->d_name);
}
break;
goto tail_call;
case 'n': {
char *name = input("new file: ", "foo");
if (!name) goto redraw;
run_cmd(NULL, NULL, "touch %s", name);
goto tail_call;
}
case '|': {
char *cmd = input("> ", NULL);
if (!cmd)
goto redraw;
// TODO: avoid having this spam the terminal history
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);
pid_t child = run_cmd(NULL, &fd, cmd);
free(cmd);
if (state.nselected > 0) {
write_selection(fd, state.firstselected);
} 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);
}
write(fd, state.files[state.cursor]->d_name, state.files[state.cursor]->d_namlen);
write(fd, "\n", 1);
}
close(fd);
waitpid(child, NULL, 0);
printf("press enter to continue...");
printf("press any key to continue...");
fflush(stdout);
getline(&buf, &bufsize, stdin);
free(buf);
getchar();
init_term();
goto tail_call;