aboutsummaryrefslogtreecommitdiff
path: root/bb.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2019-05-25 04:30:51 -0700
committerBruce Hill <bruce@bruce-hill.com>2019-05-25 04:30:51 -0700
commitd111493a8d9cce3c0e9af27d26b7347712cbbf0b (patch)
tree57a2599f34cf722564f603c48863445fd9a10022 /bb.c
parente5ac52b1d5002a496d1d791e206da6b3a56e8267 (diff)
Lots of refactoring, including adding function stubs
Diffstat (limited to 'bb.c')
-rw-r--r--bb.c870
1 files changed, 520 insertions, 350 deletions
diff --git a/bb.c b/bb.c
index a87db6d..275ac88 100644
--- a/bb.c
+++ b/bb.c
@@ -24,25 +24,33 @@
#define MAX_PATH 4096
#define KEY_DELAY 50
+#define SCROLLOFF MIN(5, (termheight-4)/2)
#define CMDFILE_FORMAT "/tmp/bb.XXXXXX"
#define MAX(a,b) ((a) < (b) ? (b) : (a))
#define MIN(a,b) ((a) > (b) ? (b) : (a))
#define writez(fd, str) write(fd, str, strlen(str))
#define IS_SELECTED(p) (((p)->atme) != NULL)
+// This uses the intrusive linked list offsets
+//#define PREV_STATE(s) ((s) == firststate ? NULL : (bb_state_t*)((s)->atme - offsetof(bb_state_t, next)))
+#define PREV_STATE(s) ((s) == firststate ? NULL : (bb_state_t*)((long)(s)->atme + (long)(s) - (long)(&(s)->atme)))
+#define LLREMOVE(e) do { \
+ (e)->next->atme = (e)->atme; \
+ *((e)->atme) = (e)->next; \
+ (e)->next = NULL; \
+ (e)->atme = NULL; \
+} while (0)
#define alt_screen() writez(termfd, "\033[?1049h")
#define default_screen() writez(termfd, "\033[?1049l")
#define hide_cursor() writez(termfd, "\033[?25l");
#define show_cursor() writez(termfd, "\033[?25h");
-#define queue_select(state, name) do {\
- char *__name = (name); \
- (state)->to_select = realloc((state)->to_select, strlen(__name)+1); \
- strcpy((state)->to_select, __name); \
-} while (0)
#define err(...) do { \
- if (termfd) close_term(); \
+ if (termfd) { \
+ default_screen(); \
+ close_term(); \
+ } \
fprintf(stderr, __VA_ARGS__); \
if (errno) \
fprintf(stderr, "\n%s", strerror(errno)); \
@@ -50,13 +58,12 @@
cleanup_and_exit(1); \
} while (0)
-extern binding_t bindings[];
-static struct termios orig_termios;
-static int termfd = 0;
-static int width, height;
-static int mouse_x, mouse_y;
-static char *cmdfilename = NULL;
+// This bit toggles 'A' (0) vs 'a' (1)
+#define SORT_DESCENDING 32
+#define IS_REVERSED(method) (!((method) & SORT_DESCENDING))
+#define DESCENDING(method) ((method) | SORT_DESCENDING)
+// Types
typedef enum {
SORT_NONE = 0,
SORT_NAME = 'n',
@@ -76,14 +83,17 @@ typedef enum {
RSORT_RANDOM = 'R',
} sortmethod_t;
-// This bit toggles 'A' (0) vs 'a' (1)
-#define SORT_DESCENDING 32
-#define IS_REVERSED(method) (!((method) & SORT_DESCENDING))
-#define DESCENDING(method) ((method) | SORT_DESCENDING)
-
+/* Both entry_t and bb_state_t use intrusive linked lists. This means they can
+ * only belong to one list at a time, in this case the list of selected entries
+ * and the list of states, respectively. 'atme' is an indirect pointer to
+ * either the 'next' field of the previous list member, or the variable that
+ * points to the first list member. In other words, item->next->atme == &item->next
+ * and firstitem->atme == &firstitem.
+ */
typedef struct entry_s {
struct entry_s *next, **atme;
- int visible : 1, d_isdir : 1;
+ int visible;
+ int d_isdir : 1;
ino_t d_ino;
__uint16_t d_reclen;
__uint8_t d_type;
@@ -92,46 +102,117 @@ typedef struct entry_s {
char d_fullname[1];
} entry_t;
-typedef struct {
+typedef struct bb_state_s {
+ struct bb_state_s *next, **atme;
+ entry_t **files;
char path[MAX_PATH];
char *to_select;
- entry_t *firstselected, **files;
- size_t nselected, nfiles;
+ int nfiles;
int scroll, cursor;
int showhidden;
- struct timespec lastclick;
char columns[16];
char sort;
} bb_state_t;
-static void update_term_size(int sig)
+// Functions
+static bb_state_t *new_state(bb_state_t *template);
+static void delete_state(bb_state_t *s);
+static void update_term_size(int sig);
+static void init_term(void);
+static void cleanup_and_exit(int sig);
+static void close_term(void);
+static void* memcheck(void *p);
+static int run_cmd_on_selection(bb_state_t *s, const char *cmd);
+static void term_move(int x, int y);
+static int write_escaped(int fd, const char *str, size_t n, const char *reset_color);
+static void render(bb_state_t *s, int lazy);
+static int compare_files(void *r, const void *v1, const void *v2);
+static int find_file(bb_state_t *s, const char *name);
+static void write_selection(int fd, char sep);
+static void clear_selection(void);
+static int select_file(entry_t *e);
+static int deselect_file(entry_t *e);
+static void set_cursor(bb_state_t *state, int i);
+static void set_scroll(bb_state_t *state, int i);
+static void populate_files(bb_state_t *s, const char *path);
+static void sort_files(bb_state_t *state);
+static entry_t *explore(const char *path);
+static void print_bindings(int verbose);
+
+// Global variables
+extern binding_t bindings[];
+static struct termios orig_termios;
+static int termfd = 0;
+static int termwidth, termheight;
+static int mouse_x, mouse_y;
+static char *cmdfilename = NULL;
+static const int colsizew = 7, coldatew = 19, colpermw = 4;
+static int colnamew;
+static struct timespec lastclick = {0, 0};
+static entry_t *firstselected = NULL;
+static bb_state_t *firststate = NULL;
+
+
+bb_state_t *new_state(bb_state_t *template)
{
- (void)sig;
- struct winsize sz = {0};
- ioctl(termfd, TIOCGWINSZ, &sz);
- width = sz.ws_col;
- height = sz.ws_row;
+ bb_state_t *s = memcheck(calloc(1, sizeof(bb_state_t)));
+ if (template) {
+ populate_files(s, template->path);
+ s->cursor = template->cursor;
+ s->scroll = template->scroll;
+ s->showhidden = template->showhidden;
+ strcpy(s->columns, template->columns);
+ s->sort = template->sort;
+ sort_files(s);
+ } else {
+ s->sort = 'n';
+ strncpy(s->columns, "smpn", sizeof(s->columns));
+ }
+ return s;
+}
+
+void delete_state(bb_state_t *s)
+{
+ if (s->files) {
+ for (int i = 0; i < s->nfiles; i++) {
+ entry_t *e = s->files[i];
+ --e->visible;
+ if (!IS_SELECTED(e))
+ free(e);
+ }
+ free(s->files);
+ }
+ if (s->to_select)
+ free(s->to_select);
+
+ LLREMOVE(s);
+ memset(s, 'X', sizeof(bb_state_t)); // Just to be safe
+ free(s);
+ if (!firststate)
+ cleanup_and_exit(0);
}
-static inline int clamped(int x, int low, int high)
+void update_term_size(int sig)
{
- if (x < low) return low;
- if (x > high) return high;
- return x;
+ (void)sig;
+ struct winsize sz = {0};
+ ioctl(termfd, TIOCGWINSZ, &sz);
+ termwidth = sz.ws_col;
+ termheight = sz.ws_row;
}
-static void init_term()
+void init_term(void)
{
termfd = open("/dev/tty", O_RDWR);
tcgetattr(termfd, &orig_termios);
struct termios tios;
memcpy(&tios, &orig_termios, sizeof(tios));
- tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+ tios.c_iflag &= (unsigned long)~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
- tios.c_oflag &= ~OPOST;
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- tios.c_cflag &= ~(CSIZE | PARENB);
- tios.c_cflag |= CS8;
+ tios.c_oflag &= (unsigned long)~OPOST;
+ tios.c_lflag &= (unsigned long)~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ tios.c_cflag &= (unsigned long)~(CSIZE | PARENB);
+ tios.c_cflag |= (unsigned long)CS8;
tios.c_cc[VMIN] = 0;
tios.c_cc[VTIME] = 0;
tcsetattr(termfd, TCSAFLUSH, &tios);
@@ -141,7 +222,7 @@ static void init_term()
writez(termfd, "\033[?1000h\033[?1002h\033[?1015h\033[?1006h");
}
-static void cleanup_and_exit(int sig)
+void cleanup_and_exit(int sig)
{
(void)sig;
if (termfd) {
@@ -155,7 +236,7 @@ static void cleanup_and_exit(int sig)
exit(1);
}
-static void close_term()
+void close_term(void)
{
signal(SIGWINCH, SIG_IGN);
@@ -167,13 +248,13 @@ static void close_term()
termfd = 0;
}
-static void* memcheck(void *p)
+void* memcheck(void *p)
{
if (!p) err("Allocation failure");
return p;
}
-static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
+int run_cmd_on_selection(bb_state_t *s, const char *cmd)
{
pid_t child;
sig_t old_handler = signal(SIGINT, SIG_IGN);
@@ -181,28 +262,25 @@ static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
if ((child = fork()) == 0) {
signal(SIGINT, SIG_DFL);
// TODO: is there a max number of args? Should this be batched?
- char **args = memcheck(calloc(MAX(1, state->nselected) + 5, sizeof(char*)));
- int i = 0;
+ size_t space = 32;
+ char **args = memcheck(calloc(space, sizeof(char*)));
+ size_t i = 0;
args[i++] = "sh";
args[i++] = "-c";
args[i++] = (char*)cmd;
args[i++] = "--";
- entry_t *first = state->firstselected ? state->firstselected : state->files[state->cursor];
+ entry_t *first = firstselected ? firstselected : s->files[s->cursor];
for (entry_t *e = first; e; e = e->next) {
+ if (i >= space) {
+ space += 100;
+ args = memcheck(realloc(args, space*sizeof(char*)));
+ }
args[i++] = e->d_name;
}
args[i] = NULL;
- char bb_depth_str[64] = {0};
- { // Set environment variable to track shell nesting
- char *depthstr = getenv("BB_DEPTH");
- int depth = depthstr ? atoi(depthstr) : 0;
- snprintf(bb_depth_str, sizeof(bb_depth_str), "%d", depth + 1);
- setenv("BB_DEPTH", bb_depth_str, 1);
- setenv("BBCMD", cmdfilename, 1);
- setenv("BBCURSOR", state->files[state->cursor]->d_name, 1);
- setenv("BBFULLCURSOR", state->files[state->cursor]->d_fullname, 1);
- }
+ setenv("BBCURSOR", s->files[s->cursor]->d_name, 1);
+ setenv("BBFULLCURSOR", s->files[s->cursor]->d_fullname, 1);
execvp("sh", args);
err("Failed to execute command: '%s'", cmd);
@@ -219,22 +297,22 @@ static int run_cmd_on_selection(bb_state_t *state, const char *cmd)
return status;
}
-static void term_move(int x, int y)
+void term_move(int x, int y)
{
static char buf[32] = {0};
int len = snprintf(buf, sizeof(buf), "\033[%d;%dH", y+1, x+1);
if (len > 0)
- write(termfd, buf, len);
+ write(termfd, buf, (size_t)len);
}
-static int write_escaped(int fd, const char *str, size_t n, const char *reset_color)
+int write_escaped(int fd, const char *str, size_t n, const char *reset_color)
{
// Returns number of *visible* characters written, not including coloring
// escapes['\n'] == 'n', etc.
static const char *escapes = " abtnvfr e";
char buf[5];
int ret = 0;
- int backlog = 0;
+ size_t backlog = 0;
for (size_t i = 0; i < n; i++) {
int escapelen = 0;
if (str[i] <= '\x1b' && escapes[(int)str[i]] != ' ')
@@ -253,7 +331,7 @@ static int write_escaped(int fd, const char *str, size_t n, const char *reset_co
backlog = 0;
}
writez(fd, "\033[31m");
- write(fd, buf, escapelen);
+ write(fd, buf, (size_t)escapelen);
writez(fd, reset_color);
}
if (backlog > 0)
@@ -261,9 +339,7 @@ static int write_escaped(int fd, const char *str, size_t n, const char *reset_co
return ret;
}
-static const int sizewidth = 7, datewidth = 19, permwidth = 4;
-static int namewidth;
-static void render(bb_state_t *state, int lazy)
+void render(bb_state_t *s, int lazy)
{
static int lastcursor = -1, lastscroll = -1;
char buf[64];
@@ -272,25 +348,25 @@ static void render(bb_state_t *state, int lazy)
if (lazy) {
// Use terminal scrolling:
- if (lastscroll > state->scroll) {
- int n = sprintf(buf, "\033[3;%dr\033[%dT\033[1;%dr", height-1, lastscroll - state->scroll, height);
- write(termfd, buf, n);
- } else if (lastscroll < state->scroll) {
- int n = sprintf(buf, "\033[3;%dr\033[%dS\033[1;%dr", height-1, state->scroll - lastscroll, height);
- write(termfd, buf, n);
+ if (lastscroll > s->scroll) {
+ int n = sprintf(buf, "\033[3;%dr\033[%dT\033[1;%dr", termheight-1, lastscroll - s->scroll, termheight);
+ write(termfd, buf, (size_t)n);
+ } else if (lastscroll < s->scroll) {
+ int n = sprintf(buf, "\033[3;%dr\033[%dS\033[1;%dr", termheight-1, s->scroll - lastscroll, termheight);
+ write(termfd, buf, (size_t)n);
}
}
- namewidth = width - 1;
- for (char *col = state->columns; *col; ++col) {
+ colnamew = termwidth - 1;
+ for (char *col = s->columns; *col; ++col) {
switch (*col) {
case 's':
- namewidth -= sizewidth + 3;
+ colnamew -= colsizew + 3;
break;
case 'm': case 'c': case 'a':
- namewidth -= datewidth + 3;
+ colnamew -= coldatew + 3;
break;
case 'p':
- namewidth -= permwidth + 3;
+ colnamew -= colpermw + 3;
break;
}
}
@@ -298,75 +374,74 @@ static void render(bb_state_t *state, int lazy)
if (!lazy) {
// Path
term_move(0,0);
- writez(termfd, "\033[0;2;37m ");
- write_escaped(termfd, state->path, strlen(state->path), "\033[0;2;37m");
+ char tabnum[] = "1)";
+ for (bb_state_t *si = firststate; si; si = si->next) {
+ const char *color = si == s ? "\033[0;1;37m" : "\033[0;2;37m";
+ writez(termfd, color);
+ write(termfd, tabnum, 2);
+ ++tabnum[0];
+ // TODO error check
+ char *title = si == s ? si->path : strrchr(si->path, '/') + 1;
+ write_escaped(termfd, title, strlen(title), color);
+ writez(termfd, " ");
+ }
writez(termfd, "\033[K\033[0m");
// Columns
term_move(0,1);
writez(termfd, " \033[0;44;30m");
- for (char *col = state->columns; *col; ++col) {
+ for (char *col = s->columns; *col; ++col) {
const char *colname;
int colwidth = 0;
switch (*col) {
case 's':
- colname = " Size"; colwidth = sizewidth;
+ colname = " Size"; colwidth = colsizew;
break;
case 'p':
- colname = "Per"; colwidth = permwidth;
+ colname = "Per"; colwidth = colpermw;
break;
case 'm':
- colname = " Modified"; colwidth = datewidth;
+ colname = " Modified"; colwidth = coldatew;
break;
case 'a':
- colname = " Accessed"; colwidth = datewidth;
+ colname = " Accessed"; colwidth = coldatew;
break;
case 'c':
- colname = " Created"; colwidth = datewidth;
+ colname = " Created"; colwidth = coldatew;
break;
case 'n':
- colname = "Name"; colwidth = namewidth;
+ colname = "Name"; colwidth = colnamew;
break;
default:
continue;
}
- if (col != state->columns) writez(termfd, " │ ");
- writez(termfd, DESCENDING(state->sort) == *col ? (IS_REVERSED(state->sort) ? "▲" : "▼") : " ");
+ if (col != s->columns) writez(termfd, " │ ");
+ writez(termfd, DESCENDING(s->sort) == *col ? (IS_REVERSED(s->sort) ? "▲" : "▼") : " ");
for (ssize_t i = writez(termfd, colname); i < colwidth-1; i++)
write(termfd, " ", 1);
}
writez(termfd, "\033[0m");
}
- if (state->nselected > 0) {
- int len = snprintf(buf, sizeof(buf), "%lu selected ", state->nselected);
- if (strlen(state->path) + 1 + len < width) {
- term_move(width-len, 0);
- writez(termfd, "\033[0;1;30;47m");
- write(termfd, buf, len);
- writez(termfd, "\033[0m");
- }
- }
-
- entry_t **files = state->files;
+ entry_t **files = s->files;
static const char *NORMAL_COLOR = "\033[0m";
static const char *CURSOR_COLOR = "\033[0;30;43m";
static const char *LINKDIR_COLOR = "\033[0;36m";
static const char *DIR_COLOR = "\033[0;34m";
static const char *LINK_COLOR = "\033[0;33m";
- for (int i = state->scroll; i < state->scroll + height - 3; i++) {
+ for (int i = s->scroll; i < s->scroll + termheight - 3; i++) {
if (lazy) {
- if (i == state->cursor || i == lastcursor)
+ if (i == s->cursor || i == lastcursor)
goto do_render;
- if (i < lastscroll || i >= lastscroll + height - 3)
+ if (i < lastscroll || i >= lastscroll + termheight - 3)
goto do_render;
continue;
}
do_render:;
- int y = i - state->scroll + 2;
+ int y = i - s->scroll + 2;
term_move(0, y);
- if (i >= state->nfiles) {
+ if (i >= s->nfiles) {
writez(termfd, "\033[K");
continue;
}
@@ -383,7 +458,7 @@ static void render(bb_state_t *state, int lazy)
}
const char *color;
- if (i == state->cursor)
+ if (i == s->cursor)
color = CURSOR_COLOR;
else if (entry->d_isdir && entry->d_type == DT_LNK)
color = LINKDIR_COLOR;
@@ -395,8 +470,8 @@ static void render(bb_state_t *state, int lazy)
color = NORMAL_COLOR;
writez(termfd, color);
- for (char *col = state->columns; *col; ++col) {
- if (col != state->columns) writez(termfd, " │ ");
+ for (char *col = s->columns; *col; ++col) {
+ if (col != s->columns) writez(termfd, " │ ");
switch (*col) {
case 's': {
int j = 0;
@@ -449,13 +524,13 @@ static void render(bb_state_t *state, int lazy)
err("readlink() failed");
writez(termfd, "\033[2m -> ");
wrote += 4;
- wrote += write_escaped(termfd, linkpath, pathlen, color);
+ wrote += write_escaped(termfd, linkpath, (size_t)pathlen, color);
if (entry->d_isdir) {
writez(termfd, "/");
++wrote;
}
}
- while (wrote++ < namewidth - 1)
+ while (wrote++ < colnamew - 1)
write(termfd, " ", 1);
break;
}
@@ -465,15 +540,16 @@ static void render(bb_state_t *state, int lazy)
}
static const char *help = "Press '?' to see key bindings ";
- term_move(0, height - 1);
+ term_move(0, termheight - 1);
writez(termfd, "\033[K");
- term_move(MAX(0, width - (int)strlen(help)), height - 1);
+ term_move(MAX(0, termwidth - (int)strlen(help)), termheight - 1);
writez(termfd, help);
- lastcursor = state->cursor;
- lastscroll = state->scroll;
+ lastcursor = s->cursor;
+ lastscroll = s->scroll;
+ // TODO: show selection and dotfile setting and anything else?
}
-static int compare_files(void *r, const void *v1, const void *v2)
+int compare_files(void *r, const void *v1, const void *v2)
{
char sort = *((char *)r);
int sign = IS_REVERSED(sort) ? -1 : 1;
@@ -529,118 +605,169 @@ static int compare_files(void *r, const void *v1, const void *v2)
return 0;
}
-static entry_t *find_file(bb_state_t *state, const char *name)
+int find_file(bb_state_t *s, const char *name)
{
- for (int i = 0; i < state->nfiles; i++) {
- entry_t *e = state->files[i];
+ for (int i = 0; i < s->nfiles; i++) {
+ entry_t *e = s->files[i];
if (strcmp(name[0] == '/' ? e->d_fullname : e->d_name, name) == 0)
- return e;
+ return i;
}
- return NULL;
+ return -1;
}
-static void write_selection(int fd, entry_t *firstselected, char sep)
+void write_selection(int fd, char sep)
{
- while (firstselected) {
- const char *p = firstselected->d_fullname;
+ for (entry_t *e = firstselected; e; e = e->next) {
+ const char *p = e->d_fullname;
while (*p) {
const char *p2 = strchr(p, '\n');
if (!p2) p2 = p + strlen(p);
- write(fd, p, p2 - p);
+ write(fd, p, (size_t)(p2 - p));
if (*p2 == '\n' && sep == '\n')
write(fd, "\\", 1);
p = p2;
}
write(fd, &sep, 1);
- firstselected = firstselected->next;
}
}
-static void clear_selection(bb_state_t *state)
+void clear_selection(void)
{
- entry_t **tofree = memcheck(calloc(state->nselected, sizeof(entry_t*)));
- int i = 0;
- for (entry_t *e = state->firstselected; e; e = e->next) {
- if (!e->visible) tofree[i++] = e;
- *e->atme = NULL;
- e->atme = NULL;
+ for (entry_t *next, *e = firstselected; e; e = next) {
+ next = e->next;
+ if (!e->visible)
+ free(e);
}
- while (i) free(tofree[--i]);
- free(tofree);
- state->nselected = 0;
+ firstselected = NULL;
}
-static int select_file(bb_state_t *state, entry_t *e)
+int select_file(entry_t *e)
{
if (IS_SELECTED(e)) return 0;
if (strcmp(e->d_name, "..") == 0) return 0;
- if (state->firstselected)
- state->firstselected->atme = &e->next;
- e->next = state->firstselected;
- e->atme = &state->firstselected;
- state->firstselected = e;
- ++state->nselected;
+ if (firstselected)
+ firstselected->atme = &e->next;
+ e->next = firstselected;
+ e->atme = &firstselected;
+ firstselected = e;
return 1;
}
-static int deselect_file(bb_state_t *state, entry_t *e)
+int deselect_file(entry_t *e)
{
if (!IS_SELECTED(e)) return 0;
- if (e->next) e->next->atme = e->atme;
- *(e->atme) = e->next;
- e->next = NULL; e->atme = NULL;
- --state->nselected;
+ LLREMOVE(e);
return 1;
}
-static void populate_files(bb_state_t *state)
+void set_cursor(bb_state_t *state, int newcur)
{
+ if (newcur > state->nfiles - 1) newcur = state->nfiles - 1;
+ if (newcur < 0) newcur = 0;
+ state->cursor = newcur;
+ if (state->nfiles <= termheight - 4)
+ return;
+
+ if (newcur < state->scroll + SCROLLOFF)
+ state->scroll = newcur - SCROLLOFF;
+ else if (newcur > state->scroll + (termheight-4) - SCROLLOFF)
+ state->scroll = newcur - (termheight-4) + SCROLLOFF;
+ int max_scroll = state->nfiles - (termheight-4) - 1;
+ if (max_scroll < 0) max_scroll = 0;
+ if (state->scroll > max_scroll) state->scroll = max_scroll;
+ if (state->scroll < 0) state->scroll = 0;
+}
+
+void set_scroll(bb_state_t *state, int newscroll)
+{
+ newscroll = MIN(newscroll, state->nfiles - (termheight-4) - 1);
+ newscroll = MAX(newscroll, 0);
+ state->scroll = newscroll;
+
+ int delta = newscroll - state->scroll;
+ int oldcur = state->cursor;
+ if (state->nfiles < termheight - 4) {
+ newscroll = 0;
+ } else {
+ if (state->cursor > newscroll + (termheight-4) - SCROLLOFF && state->scroll < state->nfiles - (termheight-4) - 1)
+ state->cursor = newscroll + (termheight-4) - SCROLLOFF;
+ else if (state->cursor < newscroll + SCROLLOFF && state->scroll > 0)
+ state->cursor = newscroll + SCROLLOFF;
+ }
+ state->scroll = newscroll;
+ if (abs(state->cursor - oldcur) < abs(delta))
+ state->cursor += delta - (state->cursor - oldcur);
+ if (state->cursor > state->nfiles - 1) state->cursor = state->nfiles - 1;
+ if (state->cursor < 0) state->cursor = 0;
+}
+
+void populate_files(bb_state_t *s, const char *path)
+{
+ if (!path) err("No path given");
+ ino_t old_inode = 0;
// Clear old files (if any)
- if (state->files) {
- for (int i = 0; i < state->nfiles; i++) {
- entry_t *e = state->files[i];
- e->visible = 0;
+ if (s->files) {
+ old_inode = s->files[s->cursor]->d_ino;
+ for (int i = 0; i < s->nfiles; i++) {
+ entry_t *e = s->files[i];
+ --e->visible;
if (!IS_SELECTED(e))
free(e);
}
- free(state->files);
+ free(s->files);
+ s->files = NULL;
+ }
+ s->nfiles = 0;
+ s->cursor = 0;
+ if (strcmp(path, s->path) != 0) {
+ s->scroll = 0;
+ strcpy(s->path, path);
}
- state->files = NULL;
- state->nfiles = 0;
// Hash inode -> entry_t with linear probing
- size_t hashsize = 2 * state->nselected;
- entry_t **selecthash = memcheck(calloc(hashsize, sizeof(entry_t*)));
- 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;
+ int nselected = 0;
+ for (entry_t *p = firstselected; p; p = p->next) ++nselected;
+ int hashsize = 2 * nselected;
+ entry_t **selecthash = NULL;
+ if (nselected > 0) {
+ selecthash = memcheck(calloc((size_t)hashsize, sizeof(entry_t*)));
+ for (entry_t *p = 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);
+ DIR *dir = opendir(s->path);
if (!dir)
- err("Couldn't open dir: %s", state->path);
- struct dirent *dp;
- size_t pathlen = strlen(state->path);
+ err("Couldn't open dir: %s", s->path);
+ size_t pathlen = strlen(s->path);
size_t filecap = 0;
- while ((dp = readdir(dir)) != NULL) {
+ for (struct dirent *dp; (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'))
+ if (!s->showhidden && dp->d_name[0] == '.' && !(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
continue;
+ if ((size_t)s->nfiles >= filecap) {
+ filecap += 100;
+ s->files = memcheck(realloc(s->files, filecap*sizeof(entry_t*)));
+ }
+
// Hashed lookup from selected:
- if (state->nselected > 0) {
+ if (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];
+ ++selecthash[probe]->visible;
+ s->files[s->nfiles++] = selecthash[probe];
goto next_file;
}
}
}
+
entry_t *entry = memcheck(malloc(sizeof(entry_t) + pathlen + dp->d_namlen + 2));
- strncpy(entry->d_fullname, state->path, pathlen);
+ if (pathlen > MAX_PATH) err("Path is too big");
+ strncpy(entry->d_fullname, s->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);
@@ -648,7 +775,7 @@ static void populate_files(bb_state_t *state)
entry->d_reclen = dp->d_reclen;
entry->d_type = dp->d_type;
entry->d_isdir = dp->d_type == DT_DIR;
- entry->visible = 1;
+ ++entry->visible;
if (!entry->d_isdir && entry->d_type == DT_LNK) {
struct stat statbuf;
if (stat(entry->d_fullname, &statbuf) == 0)
@@ -656,78 +783,80 @@ static void populate_files(bb_state_t *state)
}
entry->d_namlen = dp->d_namlen;
entry->next = NULL; entry->atme = NULL;
-
- if (state->nfiles >= filecap) {
- filecap += 100;
- state->files = memcheck(realloc(state->files, filecap*sizeof(entry_t*)));
- }
- state->files[state->nfiles++] = entry;
+ s->files[s->nfiles++] = entry;
next_file:;
}
closedir(dir);
free(selecthash);
- if (state->nfiles == 0) err("No files found (not even '..')");
+ if (s->nfiles == 0) err("No files found (not even '..')");
+
+ if (old_inode) {
+ for (int i = 0; i < s->nfiles; i++) {
+ if (s->files[i]->d_ino == old_inode) {
+ set_cursor(s, i);
+ break;
+ }
+ }
+ }
}
-static int explore(bb_state_t *state)
+void sort_files(bb_state_t *state)
{
- long cmdpos = 0;
- if (chdir(state->path) != 0)
- err("Couldn't chdir into '%s'", state->path);
- refresh:;
- if (!getwd(state->path))
- err("Couldn't get working directory");
-
- populate_files(state);
-
- state->cursor = 0;
- state->scroll = 0;
-
- sort_files:
- qsort_r(&state->files[1], state->nfiles-1, sizeof(entry_t*), &state->sort, compare_files);
+ ino_t cursor_inode = state->files[state->cursor]->d_ino;
+ qsort_r(&state->files[1], (size_t)(state->nfiles-1), sizeof(entry_t*), &state->sort, compare_files);
if (DESCENDING(state->sort) == SORT_RANDOM) {
entry_t **files = &state->files[1];
- size_t ndirs = 0, nents = state->nfiles - 1;
+ int ndirs = 0, nents = state->nfiles - 1;
for (int i = 0; i < nents; i++) {
if (state->files[i]->d_isdir) ++ndirs;
else break;
}
- for (size_t i = 0; i < ndirs - 1; i++) {
- //size_t j = i + rand() / (RAND_MAX / (ndirs - i) + 1);
- size_t j = i + rand() / (RAND_MAX / (ndirs - 1 - i));
+ for (int i = 0; i < ndirs - 1; i++) {
+ int j = i + rand() / (RAND_MAX / (ndirs - 1 - i));
entry_t *tmp = files[j];
files[j] = files[i];
files[i] = tmp;
}
- for (size_t i = ndirs; i < nents - 1; i++) {
- //size_t j = i + rand() / (RAND_MAX / (nents - i) + 1);
- size_t j = i + rand() / (RAND_MAX / (nents - 1 - i));
+ for (int i = ndirs; i < nents - 1; i++) {
+ int j = i + rand() / (RAND_MAX / (nents - 1 - i));
entry_t *tmp = files[j];
files[j] = files[i];
files[i] = tmp;
}
}
-
- // Put the cursor on the first *real* file if one exists
- if (state->cursor == 0 && state->nfiles > 1)
- ++state->cursor;
-
- if (state->to_select) {
- char *sel = state->to_select;
- for (int i = 0; i < state->nfiles; i++) {
- if (strcmp(sel, sel[0] == '/' ? state->files[i]->d_fullname : state->files[i]->d_name) == 0) {
- state->cursor = i;
- if (state->nfiles > height - 4)
- state->scroll = MAX(0, i - MIN(SCROLLOFF, (height-4)/2));
- break;
- }
+ for (int i = 0; i < state->nfiles; i++) {
+ if (state->files[i]->d_ino == cursor_inode) {
+ set_cursor(state, i);
+ break;
}
- free(state->to_select);
- state->to_select = NULL;
}
+}
+
+entry_t *explore(const char *path)
+{
+ static long cmdpos = 0;
+
+ init_term();
+ alt_screen();
+ hide_cursor();
+
+ bb_state_t *state = new_state(NULL);
+ if (!firststate) {
+ firststate = state;
+ state->atme = &firststate;
+ }
+ {
+ char *real = realpath(path, NULL);
+ if (!real || chdir(real))
+ err("Not a valid path: %s\n", path);
+ populate_files(state, real);
+ free(real); // estate
+ }
+ sort_files(state);
+
+ refresh:;
- int lastwidth = width, lastheight = height;
- int scrolloff = MIN(SCROLLOFF, (height-4)/2);
+ int lastwidth = termwidth, lastheight = termheight;
int lazy = 0;
redraw:
@@ -735,9 +864,8 @@ static int explore(bb_state_t *state)
lazy = 1;
next_input:
- if (width != lastwidth || height != lastheight) {
- scrolloff = MIN(SCROLLOFF, (height-4)/2);
- lastwidth = width; lastheight = height;
+ if (termwidth != lastwidth || termheight != lastheight) {
+ lastwidth = termwidth; lastheight = termheight;
lazy = 0;
goto redraw;
}
@@ -765,8 +893,8 @@ static int explore(bb_state_t *state)
if (value) ++value;
switch (cmd[0]) {
case 'r': // refresh
- queue_select(state, state->files[state->cursor]->d_name);
- cleanup_cmd();
+ populate_files(state, state->path);
+ sort_files(state);
goto refresh;
case 'q': // quit
cleanup_cmd();
@@ -780,44 +908,38 @@ static int explore(bb_state_t *state)
case 'c': case 'C': case 'a': case 'A':
case 'r': case 'R':
state->sort = *value;
- queue_select(state, state->files[state->cursor]->d_name);
+ sort_files(state);
cleanup_cmd();
- goto sort_files;
+ lazy = 0;
+ goto refresh;
}
goto next_cmd;
case 'c': { // scroll:
+ // TODO: figure out the best version of this
int isdelta = value[0] == '+' || value[0] == '-';
- long n = strtol(value, &value, 10);
+ int n = (int)strtol(value, &value, 10);
if (*value == '%')
- n = (n * (value[1] == 'n' ? state->nfiles : height)) / 100;
- if (isdelta) {
- state->cursor += n;
- if (state->nfiles > height-4)
- state->scroll += n;
- } else {
- state->cursor = (int)n;
- if (state->nfiles > height-4)
- state->scroll = (int)n;
- }
- if (state->nfiles > height-4)
- state->scroll = clamped(state->scroll, 0, state->nfiles-1 - (height-4));
- state->cursor = clamped(state->cursor, 0, state->nfiles-1);
- break;
+ n = (n * (value[1] == 'n' ? state->nfiles : termheight)) / 100;
+ if (isdelta)
+ set_scroll(state, state->scroll + n);
+ else
+ set_scroll(state, n);
+ goto next_cmd;
}
- case 'p':
+ case 'p': // spread:
goto move;
case '\0': case 'e': // select:
lazy = 0;
if (strcmp(value, "*") == 0) {
for (int i = 0; i < state->nfiles; i++)
- select_file(state, state->files[i]);
+ select_file(state->files[i]);
} else {
- entry_t *e = find_file(state, value);
- if (e) select_file(state, e);
+ int f = find_file(state, value);
+ if (f >= 0) select_file(state->files[f]);
}
- break;
+ goto next_cmd;
}
case 'c':
switch (cmd[1]) {
@@ -826,18 +948,23 @@ static int explore(bb_state_t *state)
if (!rpbuf) continue;
if (strcmp(rpbuf, state->path) == 0) {
free(rpbuf);
- continue;
+ goto next_cmd;
}
- if (chdir(rpbuf) == 0) {
- if (strcmp(value, "..") == 0)
- queue_select(state, state->path);
- strcpy(state->path, rpbuf);
- free(rpbuf);
- cleanup_cmd();
- goto refresh;
- } else {
+ if (chdir(rpbuf)) {
free(rpbuf);
+ goto next_cmd;
+ }
+ char *oldpath = memcheck(strdup(state->path));
+ populate_files(state, rpbuf);
+ sort_files(state);
+ free(rpbuf);
+ if (strcmp(value, "..") == 0) {
+ int f = find_file(state, oldpath);
+ if (f >= 0) set_cursor(state, f);
}
+ free(oldpath);
+ cleanup_cmd();
+ goto refresh;
}
case 'o': // cols:
for (char *col = value, *dst = state->columns;
@@ -845,41 +972,110 @@ static int explore(bb_state_t *state)
*(dst++) = DESCENDING(*col);
*dst = '\0';
}
+ break;
+ case 'l': { // closetab:
+ if (!value) value = "+0";
+ bb_state_t *from = (value[0] == '+' || value[0] == '-') ? state : firststate;
+ int n = (int)strtol(value, &value, 10);
+ bb_state_t *s;
+ if (n < 0) {
+ for (s = from; n++; s = PREV_STATE(s))
+ if (!s) goto next_cmd;
+ } else {
+ for (s = from; n--; s = s->next)
+ if (!s) goto next_cmd;
+ }
+ if (state == s) {
+ if (s->next)
+ state = s->next;
+ else if (PREV_STATE(s))
+ state = PREV_STATE(s);
+ if (chdir(state->path))
+ err("Could not cd to '%s'", state->path);
+ }
+ delete_state(s);
+ goto next_cmd;
+ }
}
- case 't': { // toggle:
- lazy = 0;
- entry_t *e = find_file(state, value);
- if (e) {
- if (IS_SELECTED(e)) deselect_file(state, e);
- else select_file(state, e);
- }
- break;
+ case 'n': { // newtab
+ bb_state_t *ns = new_state(state);
+ if (state->next)
+ state->next->atme = &ns->next;
+ ns->next = state->next;
+ ns->atme = &state->next;
+ state->next = ns;
+ state = ns;
+ if (chdir(ns->path))
+ err("Could not cd to '%s'", ns->path);
+ goto refresh;
}
- case 'd': { // deselect:
- lazy = 0;
- if (strcmp(value, "*") == 0) {
- clear_selection(state);
- } else {
- entry_t *e = find_file(state, value);
- if (e) select_file(state, e);
+ case 't': { // toggle:, tab:
+ if (cmd[1] == 'o') { // toggle:
+ lazy = 0;
+ int f = find_file(state, value);
+ if (f >= 0) {
+ entry_t *e = state->files[f];
+ if (IS_SELECTED(e)) deselect_file(e);
+ else select_file(e);
+ }
+ goto next_cmd;
+ } else if (cmd[1] == 'a') { // tab:
+ bb_state_t *from = (value[0] == '+' || value[0] == '-') ? state : firststate;
+ int n = (int)strtol(value, &value, 10);
+ bb_state_t *s = from;
+ if (n < 0) {
+ while (s && n++) s = PREV_STATE(s);
+ } else {
+ while (s && n--) s = s->next;
+ }
+ if (!s || s == state)
+ goto next_cmd;
+
+ state = s;
+ if (chdir(s->path))
+ err("Could not cd to '%s'", s->path);
+ goto refresh;
}
}
- case 'g': { // goto:
- for (int i = 0; i < state->nfiles; i++) {
- if (strcmp(value[0] == '/' ?
- state->files[i]->d_fullname : state->files[i]->d_name,
- value) == 0) {
- state->cursor = i;
+ case 'd': { // deselect:, dots:
+ if (cmd[1] == 'o') { // dots:
+ int requested = value ? (value[0] == 'y') : state->showhidden ^ 1;
+ if (requested == state->showhidden)
goto next_cmd;
+ state->showhidden = requested;
+ populate_files(state, state->path);
+ sort_files(state);
+ goto refresh;
+ } else if (value) { // deselect:
+ lazy = 0;
+ if (strcmp(value, "*") == 0) {
+ clear_selection();
+ } else {
+ int f = find_file(state, value);
+ if (f >= 0)
+ select_file(state->files[f]);
}
+ goto next_cmd;
+ }
+ }
+ case 'g': { // goto:
+ int f = find_file(state, value);
+ if (f >= 0) {
+ set_cursor(state, f);
+ goto next_cmd;
}
char *lastslash = strrchr(value, '/');
if (!lastslash) goto next_cmd;
*lastslash = '\0'; // Split in two
- if (chdir(value) != 0) goto next_cmd;
- strcpy(state->path, value);
- if (lastslash[1])
- queue_select(state, lastslash+1);
+ char *real = realpath(path, NULL);
+ if (!real || chdir(real))
+ err("Not a valid path: %s\n", path);
+ populate_files(state, real);
+ free(real); // estate
+ if (lastslash[1]) {
+ f = find_file(state, value);
+ if (f >= 0) set_cursor(state, f);
+ }
cleanup_cmd();
goto refresh;
}
@@ -887,39 +1083,22 @@ static int explore(bb_state_t *state)
move:;
int oldcur = state->cursor;
int isdelta = value[0] == '-' || value[0] == '+';
- long n = strtol(value, &value, 10);
+ int n = (int)strtol(value, &value, 10);
if (*value == '%')
- n = (n * (value[1] == 'n' ? state->nfiles : height)) / 100;
- if (isdelta) state->cursor += n;
- else state->cursor = n;
-
- state->cursor = clamped(state->cursor, 0, state->nfiles-1);
- int delta = state->cursor - oldcur;
-
- if (state->nfiles > height-4) {
- if (delta > 0) {
- if (state->cursor >= state->scroll + (height-4) - scrolloff)
- state->scroll += delta;
- } else if (delta < 0) {
- if (state->cursor <= state->scroll + scrolloff)
- state->scroll += delta;
- }
- //int target = clamped(state->scroll, state->cursor - (height-4) + scrolloff, state->cursor - scrolloff);
- //state->scroll += (delta > 0 ? 1 : -1)*MIN(abs(target-state->scroll), abs((int)delta));
- //state->scroll = target;
- state->scroll = clamped(state->scroll, state->cursor - (height-4) + 1, state->cursor);
- state->scroll = clamped(state->scroll, 0, state->nfiles-1 - (height-4));
- }
+ n = (n * (value[1] == 'n' ? state->nfiles : termheight)) / 100;
+ if (isdelta) set_cursor(state, state->cursor + n);
+ else set_cursor(state, n);
if (cmd[0] == 's') { // spread:
int sel = IS_SELECTED(state->files[oldcur]);
for (int i = state->cursor; i != oldcur; i += (oldcur > i ? 1 : -1)) {
if (sel && !IS_SELECTED(state->files[i]))
- select_file(state, state->files[i]);
+ select_file(state->files[i]);
else if (!sel && IS_SELECTED(state->files[i]))
- deselect_file(state, state->files[i]);
+ deselect_file(state->files[i]);
}
lazy &= abs(oldcur - state->cursor) <= 1;
}
+ goto next_cmd;
}
default: break;
}
@@ -940,27 +1119,28 @@ static int explore(bb_state_t *state)
case KEY_MOUSE_LEFT: {
struct timespec clicktime;
clock_gettime(CLOCK_MONOTONIC, &clicktime);
- 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;
+ double dt_ms = 1e3*(double)(clicktime.tv_sec - lastclick.tv_sec);
+ dt_ms += 1e-6*(double)(clicktime.tv_nsec - lastclick.tv_nsec);
+ lastclick = clicktime;
if (mouse_y == 1) {
// Sort column:
int x = 1;
for (char *col = state->columns; *col; ++col) {
if (col != state->columns) x += 3;
switch (*col) {
- case 's': x += sizewidth; break;
- case 'p': x += permwidth; break;
+ case 's': x += colsizew; break;
+ case 'p': x += colpermw; break;
case 'm': case 'a': case 'c':
- x += datewidth; break;
- case 'n': x += namewidth; break;
+ x += coldatew; break;
+ case 'n': x += colnamew; break;
}
if (x >= mouse_x) {
if (DESCENDING(state->sort) == *col)
state->sort ^= SORT_DESCENDING;
else
state->sort = *col;
- goto sort_files;
+ sort_files(state);
+ goto refresh;
}
}
goto next_input;
@@ -968,12 +1148,12 @@ static int explore(bb_state_t *state)
int clicked = state->scroll + (mouse_y - 2);
if (mouse_x == 0) {
if (IS_SELECTED(state->files[clicked]))
- deselect_file(state, state->files[clicked]);
+ deselect_file(state->files[clicked]);
else
- select_file(state, state->files[clicked]);
+ select_file(state->files[clicked]);
goto redraw;
}
- state->cursor = clicked;
+ set_cursor(state, clicked);
if (dt_ms <= 200)
key = KEY_MOUSE_DOUBLE_LEFT;
goto user_bindings;
@@ -983,7 +1163,7 @@ static int explore(bb_state_t *state)
case KEY_CTRL_C:
cleanup_and_exit(SIGINT);
- return 1;
+ goto quit; // Unreachable
case KEY_CTRL_Z:
default_screen();
@@ -996,17 +1176,12 @@ static int explore(bb_state_t *state)
lazy = 0;
goto redraw;
- case '.':
- state->showhidden ^= 1;
- queue_select(state, state->files[state->cursor]->d_name);
- goto refresh;
-
case KEY_CTRL_H: {
- term_move(0,height-1);
+ term_move(0,termheight-1);
writez(termfd, "\033[K\033[33;1mPress any key...\033[0m");
while ((key = term_getkey(termfd, &mouse_x, &mouse_y, 1000)) == -1)
;
- term_move(0,height-1);
+ term_move(0,termheight-1);
writez(termfd, "\033[K\033[1m<\033[33m");
const char *name = keyname(key);
char buf[32] = {(char)key};
@@ -1052,7 +1227,7 @@ static int explore(bb_state_t *state)
run_binding:
if (cmdpos != 0)
err("Command file still open");
- term_move(0, height-1);
+ term_move(0, termheight-1);
//writez(termfd, "\033[K");
if (binding->flags & NORMAL_TERM) {
default_screen();
@@ -1071,10 +1246,14 @@ static int explore(bb_state_t *state)
goto next_input;
quit:
- return 0;
+
+ default_screen();
+ show_cursor();
+ close_term();
+ return firstselected;
}
-static void print_bindings(int verbose)
+void print_bindings(int verbose)
{
struct winsize sz = {0};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &sz);
@@ -1112,15 +1291,10 @@ static void print_bindings(int verbose)
int main(int argc, char *argv[])
{
- bb_state_t state;
- memset(&state, 0, sizeof(bb_state_t));
- clock_gettime(CLOCK_MONOTONIC, &state.lastclick);
- state.sort = 'n';
- strncpy(state.columns, "smpn", sizeof(state.columns));
-
- char *initial_path = NULL;
+ char *initial_path = NULL, *depthstr;
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] == '+') {
@@ -1142,6 +1316,13 @@ int main(int argc, char *argv[])
if (!mktemp(cmdfilename)) err("Couldn't create tmpfile\n");
cmdfile = fopen(cmdfilename, "a");
if (!cmdfile) err("Couldn't create cmdfile: '%s'\n", cmdfilename);
+ // Set up environment variables
+ depthstr = getenv("BB_DEPTH");
+ int depth = depthstr ? atoi(depthstr) : 0;
+ if (asprintf(&depthstr, "%d", depth + 1) < 0)
+ err("Allocation failure");
+ setenv("BB_DEPTH", depthstr, 1);
+ setenv("BBCMD", cmdfilename, 1);
} else if (cmd_args > 0) {
char *parent_bbcmd = getenv("BBCMD");
if (!parent_bbcmd || parent_bbcmd[0] == '\0') {
@@ -1200,10 +1381,6 @@ int main(int argc, char *argv[])
return 0;
}
- char *real = realpath(initial_path, NULL);
- if (!real) err("Not a valid file: %s\n", initial_path);
- strcpy(state.path, initial_path);
-
signal(SIGTERM, cleanup_and_exit);
signal(SIGINT, cleanup_and_exit);
signal(SIGXCPU, cleanup_and_exit);
@@ -1211,26 +1388,19 @@ int main(int argc, char *argv[])
signal(SIGVTALRM, cleanup_and_exit);
signal(SIGPROF, cleanup_and_exit);
- init_term();
- alt_screen();
- hide_cursor();
- int ret = explore(&state);
- default_screen();
- show_cursor();
- close_term();
-
- unlink(cmdfilename);
+ char *real = realpath(initial_path, NULL);
+ if (!real || chdir(real)) err("Not a valid path: %s\n", initial_path);
+ explore(real);
+ free(real);
- if (ret == 0) {
- if (print_dir)
- printf("%s\n", state.path);
- if (print_selection)
- write_selection(STDOUT_FILENO, state.firstselected, sep);
- } else if (print_dir) {
+ if (firstselected && print_selection) {
+ write_selection(STDOUT_FILENO, sep);
+ }
+ if (print_dir) {
printf("%s\n", initial_path);
}
- return ret;
+ return 0;
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1