Overhaul of sorting and columns, simplifying and unifying things a bit
This commit is contained in:
parent
ca45d7750c
commit
2d7d20f9a1
495
bb.c
495
bb.c
@ -33,7 +33,7 @@
|
|||||||
#define MAX(a,b) ((a) < (b) ? (b) : (a))
|
#define MAX(a,b) ((a) < (b) ? (b) : (a))
|
||||||
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
||||||
#define IS_SELECTED(p) (((p)->atme) != NULL)
|
#define IS_SELECTED(p) (((p)->atme) != NULL)
|
||||||
#define OPTNUM(o) ((int)((o) & ~'0'))
|
#define LOWERCASE(c) ('A' <= (c) && (c) <= 'Z' ? ((c) + 'a' - 'A') : (c))
|
||||||
|
|
||||||
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_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);
|
static const char *T_LEAVE_BBMODE = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR ";" T_ALT_SCREEN);
|
||||||
@ -52,6 +52,7 @@ static const char *T_LEAVE_BBMODE_PARTIAL = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";
|
|||||||
// Types
|
// Types
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SORT_NONE = 0,
|
SORT_NONE = 0,
|
||||||
|
SORT_DIR = '/',
|
||||||
SORT_NAME = 'n',
|
SORT_NAME = 'n',
|
||||||
SORT_SIZE = 's',
|
SORT_SIZE = 's',
|
||||||
SORT_PERM = 'p',
|
SORT_PERM = 'p',
|
||||||
@ -59,7 +60,7 @@ typedef enum {
|
|||||||
SORT_CTIME = 'c',
|
SORT_CTIME = 'c',
|
||||||
SORT_ATIME = 'a',
|
SORT_ATIME = 'a',
|
||||||
SORT_RANDOM = 'r',
|
SORT_RANDOM = 'r',
|
||||||
SORT_SELECTED = '+',
|
SORT_SELECTED = '*',
|
||||||
} sortmethod_t;
|
} sortmethod_t;
|
||||||
|
|
||||||
/* entry_t uses intrusive linked lists. This means entries can only belong to
|
/* entry_t uses intrusive linked lists. This means entries can only belong to
|
||||||
@ -73,23 +74,34 @@ typedef struct entry_s {
|
|||||||
char *name, *linkname;
|
char *name, *linkname;
|
||||||
struct stat info;
|
struct stat info;
|
||||||
mode_t linkedmode;
|
mode_t linkedmode;
|
||||||
int refcount : 2; // Should only be between 0-2
|
unsigned int refcount : 2; // Should only be between 0-2
|
||||||
int no_esc : 1;
|
int no_esc : 1;
|
||||||
int link_no_esc : 1;
|
int link_no_esc : 1;
|
||||||
int shufflepos;
|
int shufflepos;
|
||||||
char fullname[1]; // Must be last
|
char fullname[1]; // Must be last
|
||||||
} entry_t;
|
} entry_t;
|
||||||
|
|
||||||
|
#define MAX_COLS 12
|
||||||
|
#define MAX_SORT (2*MAX_COLS)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char sort[MAX_SORT+1];
|
||||||
|
char columns[MAX_COLS+1];
|
||||||
|
char aligns[MAX_COLS+1]; // l,r,c
|
||||||
|
int colwidths[MAX_COLS+1];
|
||||||
|
int show_dotdot : 1;
|
||||||
|
int show_dot : 1;
|
||||||
|
int show_dotfiles : 1;
|
||||||
|
} options_t;
|
||||||
|
|
||||||
typedef struct bb_s {
|
typedef struct bb_s {
|
||||||
entry_t **files;
|
entry_t **files;
|
||||||
entry_t *firstselected;
|
entry_t *firstselected;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
int nfiles;
|
int nfiles;
|
||||||
int scroll, cursor;
|
int scroll, cursor;
|
||||||
char options[128]; // General purpose options
|
options_t options, initialopts;
|
||||||
char initialopts[128]; // Initial values of the options (after startupcmds)
|
|
||||||
char *marks[128]; // Mapping from key to directory
|
char *marks[128]; // Mapping from key to directory
|
||||||
int colwidths[10];
|
|
||||||
} bb_t;
|
} bb_t;
|
||||||
|
|
||||||
typedef enum { BB_NOP = 0, BB_INVALID, BB_REFRESH, BB_QUIT } bb_result_t;
|
typedef enum { BB_NOP = 0, BB_INVALID, BB_REFRESH, BB_QUIT } bb_result_t;
|
||||||
@ -103,6 +115,7 @@ static void* memcheck(void *p);
|
|||||||
static int run_cmd_on_selection(bb_t *bb, const char *cmd);
|
static int run_cmd_on_selection(bb_t *bb, const char *cmd);
|
||||||
static int fputs_escaped(FILE *f, const char *str, const char *color);
|
static int fputs_escaped(FILE *f, const char *str, const char *color);
|
||||||
static const char* color_of(mode_t mode);
|
static const char* color_of(mode_t mode);
|
||||||
|
static void set_sort(bb_t *bb, const char *sort);
|
||||||
static void render(bb_t *bb, int lazy);
|
static void render(bb_t *bb, int lazy);
|
||||||
static int compare_files(void *r, const void *v1, const void *v2);
|
static int compare_files(void *r, const void *v1, const void *v2);
|
||||||
static int find_file(bb_t *bb, const char *name);
|
static int find_file(bb_t *bb, const char *name);
|
||||||
@ -128,7 +141,8 @@ static FILE *tty_out = NULL, *tty_in = NULL;
|
|||||||
static int termwidth, termheight;
|
static int termwidth, termheight;
|
||||||
static int mouse_x, mouse_y;
|
static int mouse_x, mouse_y;
|
||||||
static char *cmdfilename = NULL;
|
static char *cmdfilename = NULL;
|
||||||
static const int colsizew = 7, coldatew = 19, colpermw = 4, colnamew = 40, colselw = 1;
|
static const int colsizew = 7, coldatew = 19, colpermw = 4, colnamew = 40,
|
||||||
|
colselw = 1, coldirw = 1, colrandw = 2;
|
||||||
static struct timespec lastclick = {0, 0};
|
static struct timespec lastclick = {0, 0};
|
||||||
|
|
||||||
|
|
||||||
@ -298,6 +312,19 @@ const char* color_of(mode_t mode)
|
|||||||
return NORMAL_COLOR;
|
return NORMAL_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_sort(bb_t *bb, const char *sort)
|
||||||
|
{
|
||||||
|
for (const char *s = sort; s[0] && s[1]; s += 2) {
|
||||||
|
char *found;
|
||||||
|
if ((found = strchr(bb->options.sort, s[1]))) {
|
||||||
|
memmove(found-1, found+1, strlen(found+1)+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t len = MIN(MAX_SORT, strlen(sort));
|
||||||
|
memmove(bb->options.sort + len, bb->options.sort, MAX_SORT+1 - len);
|
||||||
|
memmove(bb->options.sort, sort, len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Draw everything to the screen.
|
* Draw everything to the screen.
|
||||||
* If lazy is true, then use terminal scrolling to move the file listing
|
* If lazy is true, then use terminal scrolling to move the file listing
|
||||||
@ -319,38 +346,6 @@ void render(bb_t *bb, int lazy)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize columns and column sizes
|
|
||||||
// TODO: make this lazy?
|
|
||||||
int cols = 0;
|
|
||||||
for (char *valid = &bb->options['0'], *p = &bb->options['0']; p <= &bb->options['9']; ++p) {
|
|
||||||
switch (*p) {
|
|
||||||
case 'n': case 's': case 'm': case 'c': case 'a': case 'p': case '+':
|
|
||||||
++cols;
|
|
||||||
*(valid++) = *p;
|
|
||||||
}
|
|
||||||
if (p >= valid) *p = 0;
|
|
||||||
}
|
|
||||||
int tot = 0, space = termwidth - 1 - 3*cols;
|
|
||||||
for (int i = 0; i < cols; i++) {
|
|
||||||
switch (bb->options['0' + i]) {
|
|
||||||
case 'a': case 'c': case 'm': bb->colwidths[i] = coldatew; break;
|
|
||||||
case 'n': bb->colwidths[i] = colnamew; break;
|
|
||||||
case 'p': bb->colwidths[i] = colpermw; break;
|
|
||||||
case 's': bb->colwidths[i] = colsizew; break;
|
|
||||||
case '+': bb->colwidths[i] = colselw; break;
|
|
||||||
default: bb->colwidths[i] = 0; break;
|
|
||||||
}
|
|
||||||
int ratio = OPTNUM(bb->options['A' + i]);
|
|
||||||
if (ratio == 0) space -= bb->colwidths[i];
|
|
||||||
else tot += ratio;
|
|
||||||
}
|
|
||||||
if (tot > 0) {
|
|
||||||
for (int i = 0; i < cols; i++) {
|
|
||||||
int ratio = OPTNUM(bb->options['A' + i]);
|
|
||||||
bb->colwidths[i] = MAX(bb->colwidths[i], (space * ratio) / tot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lazy) {
|
if (!lazy) {
|
||||||
// Path
|
// Path
|
||||||
move_cursor(tty_out, 0, 0);
|
move_cursor(tty_out, 0, 0);
|
||||||
@ -363,36 +358,38 @@ void render(bb_t *bb, int lazy)
|
|||||||
move_cursor(tty_out, 0, 1);
|
move_cursor(tty_out, 0, 1);
|
||||||
fputs("\033[0;44;30m\033[K", tty_out);
|
fputs("\033[0;44;30m\033[K", tty_out);
|
||||||
int x = 0;
|
int x = 0;
|
||||||
for (int col = 0; col < cols; col++) {
|
for (int col = 0; bb->options.columns[col]; col++) {
|
||||||
|
const char *title = NULL;
|
||||||
|
switch (bb->options.columns[col]) {
|
||||||
|
case '*': title = "*"; break;
|
||||||
|
case '/': title = "/"; break;
|
||||||
|
case 'r': title = "Random"; break;
|
||||||
|
case 'n': title = "Name"; break;
|
||||||
|
case 's': title = "Size"; break;
|
||||||
|
case 'p': title = "Permissions"; break;
|
||||||
|
case 'm': title = "Modified"; break;
|
||||||
|
case 'a': title = "Accessed"; break;
|
||||||
|
case 'c': title = "Created"; break;
|
||||||
|
default: title = ""; break;
|
||||||
|
}
|
||||||
move_cursor(tty_out, x, 1);
|
move_cursor(tty_out, x, 1);
|
||||||
if (col > 0) {
|
if (col > 0) {
|
||||||
fputs("│\033[K", tty_out);
|
fputs("│\033[K", tty_out);
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
|
int k = bb->options.aligns[col] == 'l' ? 0 : (bb->options.aligns[col] == 'r' ? 2 : 1);
|
||||||
const char *indicator = " ";
|
const char *indicator = " ";
|
||||||
if (bb->options['s'] == bb->options['0' + col])
|
char *found;
|
||||||
indicator = bb->options['r'] ? RSORT_INDICATOR : SORT_INDICATOR;
|
if ((found = strchr(bb->options.sort, bb->options.columns[col])))
|
||||||
switch (bb->options['0' + col]) {
|
indicator = found[-1] == '-' ? RSORT_INDICATOR : SORT_INDICATOR;
|
||||||
case '+': fprintf(tty_out, "%s+", indicator); break;
|
move_cursor(tty_out, x + MAX(0, ((bb->options.colwidths[col] - (int)strlen(title) - 1)*k)/2), 1);
|
||||||
case 'n': fprintf(tty_out, "%sName", indicator); break;
|
if (bb->options.columns[col] == bb->options.sort[1])
|
||||||
case 's': move_cursor(tty_out, x + MAX(0, bb->colwidths[col] - (int)strlen(" Size")), 1);
|
fputs("\033[1m", tty_out);
|
||||||
fprintf(tty_out, "%sSize", indicator);
|
fputs(indicator, tty_out);
|
||||||
break;
|
fputs(title, tty_out);
|
||||||
case 'p': move_cursor(tty_out, x + MAX(0, (bb->colwidths[col] - (int)strlen(" Permissions"))/2), 1);
|
if (bb->options.columns[col] == bb->options.sort[1])
|
||||||
fprintf(tty_out, "%sPermissions", indicator);
|
fputs("\033[22m", tty_out);
|
||||||
break;
|
x += bb->options.colwidths[col];
|
||||||
case 'm': move_cursor(tty_out, x + MAX(0, (bb->colwidths[col] - (int)strlen(" Modified"))/2), 1);
|
|
||||||
fprintf(tty_out, "%sModified", indicator);
|
|
||||||
break;
|
|
||||||
case 'a': move_cursor(tty_out, x + MAX(0, (bb->colwidths[col] - (int)strlen(" Accessed"))/2), 1);
|
|
||||||
fprintf(tty_out, "%sAccessed", indicator);
|
|
||||||
break;
|
|
||||||
case 'c': move_cursor(tty_out, x + MAX(0, (bb->colwidths[col] - (int)strlen(" Created"))/2), 1);
|
|
||||||
fprintf(tty_out, "%sCreated", indicator);
|
|
||||||
break;
|
|
||||||
default: continue;
|
|
||||||
}
|
|
||||||
x += bb->colwidths[col];
|
|
||||||
}
|
}
|
||||||
fputs(" \033[K\033[0m", tty_out);
|
fputs(" \033[K\033[0m", tty_out);
|
||||||
}
|
}
|
||||||
@ -411,6 +408,12 @@ void render(bb_t *bb, int lazy)
|
|||||||
do_render:
|
do_render:
|
||||||
y = i - bb->scroll + 2;
|
y = i - bb->scroll + 2;
|
||||||
move_cursor(tty_out, 0, y);
|
move_cursor(tty_out, 0, y);
|
||||||
|
|
||||||
|
if (i == bb->scroll && bb->nfiles == 0) {
|
||||||
|
const char *s = "...no files here...";
|
||||||
|
fprintf(tty_out, "\033[37;2m%s\033[0m", s);
|
||||||
|
}
|
||||||
|
|
||||||
if (i >= bb->nfiles) {
|
if (i >= bb->nfiles) {
|
||||||
fputs("\033[K", tty_out);
|
fputs("\033[K", tty_out);
|
||||||
continue;
|
continue;
|
||||||
@ -426,7 +429,7 @@ void render(bb_t *bb, int lazy)
|
|||||||
fputs(color, tty_out);
|
fputs(color, tty_out);
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
for (int col = 0; col < cols; col++) {
|
for (int col = 0; bb->options.columns[col]; col++) {
|
||||||
fprintf(tty_out, "\033[%d;%dH\033[K", y+1, x+1);
|
fprintf(tty_out, "\033[%d;%dH\033[K", y+1, x+1);
|
||||||
if (col > 0) {
|
if (col > 0) {
|
||||||
if (i == bb->cursor) fputs("│", tty_out);
|
if (i == bb->cursor) fputs("│", tty_out);
|
||||||
@ -434,11 +437,27 @@ void render(bb_t *bb, int lazy)
|
|||||||
fputs(color, tty_out);
|
fputs(color, tty_out);
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
switch (bb->options['0' + col]) {
|
int k = bb->options.aligns[col] == 'l' ? 0 : (bb->options.aligns[col] == 'r' ? 2 : 1);
|
||||||
case '+':
|
switch (bb->options.columns[col]) {
|
||||||
|
case '*':
|
||||||
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - colselw))/2), y);
|
||||||
fputs(IS_SELECTED(entry) ? SELECTED_INDICATOR : NOT_SELECTED_INDICATOR, tty_out);
|
fputs(IS_SELECTED(entry) ? SELECTED_INDICATOR : NOT_SELECTED_INDICATOR, tty_out);
|
||||||
fputs(color, tty_out);
|
fputs(color, tty_out);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - coldirw))/2), y);
|
||||||
|
if (S_ISDIR(S_ISLNK(entry->info.st_mode) ? entry->linkedmode : entry->info.st_mode))
|
||||||
|
fputs("/", tty_out);
|
||||||
|
else
|
||||||
|
fputs(" ", tty_out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - colrandw))/2), y);
|
||||||
|
fprintf(tty_out, "\033[48;5;%dm \033[0m%s", 232 + (entry->shufflepos / (RAND_MAX / (255-232))), color);
|
||||||
|
break;
|
||||||
|
|
||||||
case 's': {
|
case 's': {
|
||||||
int j = 0;
|
int j = 0;
|
||||||
const char* units = "BKMGTPEZY";
|
const char* units = "BKMGTPEZY";
|
||||||
@ -447,38 +466,37 @@ void render(bb_t *bb, int lazy)
|
|||||||
bytes /= 1024;
|
bytes /= 1024;
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
move_cursor(tty_out, x + bb->colwidths[col] - colsizew, y);
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - colsizew))/2), y);
|
||||||
fprintf(tty_out, "%6.*f%c", j > 0 ? 1 : 0, bytes, units[j]);
|
fprintf(tty_out, "%6.*f%c", j > 0 ? 1 : 0, bytes, units[j]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
move_cursor(tty_out, x + (bb->colwidths[col] - coldatew)/2, y);
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - coldatew))/2), y);
|
||||||
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(entry->info.st_mtime)));
|
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(entry->info.st_mtime)));
|
||||||
fputs(buf, tty_out);
|
fputs(buf, tty_out);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
move_cursor(tty_out, x + (bb->colwidths[col] - coldatew)/2, y);
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - coldatew))/2), y);
|
||||||
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(entry->info.st_ctime)));
|
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(entry->info.st_ctime)));
|
||||||
fputs(buf, tty_out);
|
fputs(buf, tty_out);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'a':
|
case 'a':
|
||||||
move_cursor(tty_out, x + (bb->colwidths[col] - coldatew)/2, y);
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - coldatew))/2), y);
|
||||||
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(entry->info.st_atime)));
|
strftime(buf, sizeof(buf), "%l:%M%p %b %e %Y", localtime(&(entry->info.st_atime)));
|
||||||
fputs(buf, tty_out);
|
fputs(buf, tty_out);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
move_cursor(tty_out, x + (bb->colwidths[col] - colpermw)/2, y);
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - colpermw))/2), y);
|
||||||
fprintf(tty_out, " %c%c%c",
|
fprintf(tty_out, " %03o", entry->info.st_mode & 0777);
|
||||||
'0' + ((entry->info.st_mode >> 6) & 7),
|
|
||||||
'0' + ((entry->info.st_mode >> 3) & 7),
|
|
||||||
'0' + ((entry->info.st_mode >> 0) & 7));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'n': {
|
case 'n': {
|
||||||
|
move_cursor(tty_out, x + MAX(0, (k*(bb->options.colwidths[col] - (int)strlen(entry->name)))/2), y);
|
||||||
|
|
||||||
if (entry->no_esc) fputs(entry->name, tty_out);
|
if (entry->no_esc) fputs(entry->name, tty_out);
|
||||||
else entry->no_esc |= !fputs_escaped(tty_out, entry->name, color);
|
else entry->no_esc |= !fputs_escaped(tty_out, entry->name, color);
|
||||||
|
|
||||||
@ -502,9 +520,9 @@ void render(bb_t *bb, int lazy)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: continue;
|
default: break;
|
||||||
}
|
}
|
||||||
x += bb->colwidths[col];
|
x += bb->options.colwidths[col];
|
||||||
}
|
}
|
||||||
fputs(" \033[K\033[0m", tty_out); // Reset color and attributes
|
fputs(" \033[K\033[0m", tty_out); // Reset color and attributes
|
||||||
}
|
}
|
||||||
@ -516,116 +534,96 @@ void render(bb_t *bb, int lazy)
|
|||||||
fputs(help, tty_out);
|
fputs(help, tty_out);
|
||||||
lastcursor = bb->cursor;
|
lastcursor = bb->cursor;
|
||||||
lastscroll = bb->scroll;
|
lastscroll = bb->scroll;
|
||||||
int x = termwidth;
|
|
||||||
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;
|
|
||||||
move_cursor(tty_out, x, 0);
|
|
||||||
fputs("\033[2m,\033[0m", tty_out);
|
|
||||||
}
|
|
||||||
if (bb->options[o] >= ' ') {
|
|
||||||
x -= 3;
|
|
||||||
move_cursor(tty_out, x, 0);
|
|
||||||
fprintf(tty_out, "\033[0;1m%c\033[0m=\033[33m%c\033[0m", (char)o, bb->options[o]);
|
|
||||||
} else if (bb->options[o] > 0) {
|
|
||||||
x -= 1;
|
|
||||||
move_cursor(tty_out, x, 0);
|
|
||||||
fprintf(tty_out, "\033[0;1m%c\033[0m", (char)o);
|
|
||||||
}
|
|
||||||
nopts++;
|
|
||||||
}
|
|
||||||
move_cursor(tty_out, --x, 0);
|
|
||||||
fputs("\033[0;2m[\033[0m", tty_out);
|
|
||||||
fflush(tty_out);
|
fflush(tty_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used for sorting, this function compares files according to the sorting-related options,
|
* Used for sorting, this function compares files according to the sorting-related options,
|
||||||
* like bb->options['s'], bb->options['i'], and bb->options['r']
|
* like bb->options.sort
|
||||||
*/
|
*/
|
||||||
int compare_files(void *v, const void *v1, const void *v2)
|
int compare_files(void *v, const void *v1, const void *v2)
|
||||||
{
|
{
|
||||||
|
#define compare(a, b) ((a) == (b) ? 0 : ((a) < (b) ? 1 : -1))
|
||||||
|
#define compare_time(t1, t2) ((t1).tv_sec == (t2).tv_sec ? compare((t1).tv_nsec, (t2).tv_nsec) : compare((t1).tv_sec, (t2).tv_sec))
|
||||||
bb_t *bb = (bb_t*)v;
|
bb_t *bb = (bb_t*)v;
|
||||||
char sort = bb->options['s'];
|
int diff = 0;
|
||||||
int sign = bb->options['r'] ? -1 : 1;
|
|
||||||
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
|
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
|
||||||
// *always* put ".." before everything else, then "."
|
int sign = 1;
|
||||||
int diff = (strcmp(f1->name, "..") == 0) - (strcmp(f2->name, "..") == 0);
|
for (char *sort = bb->options.sort; *sort && diff == 0; ++sort) {
|
||||||
if (diff) return -diff;
|
if (*sort == '-') {
|
||||||
diff = (strcmp(f1->name, ".") == 0) - (strcmp(f2->name, ".") == 0);
|
sign = -1;
|
||||||
if (diff) return -diff;
|
continue;
|
||||||
if (sort == '+') {
|
|
||||||
diff = -(IS_SELECTED(f1) - IS_SELECTED(f2)) * sign;
|
|
||||||
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));
|
|
||||||
int d2 = S_ISDIR(f2->info.st_mode) || (S_ISLNK(f2->info.st_mode) && S_ISDIR(f2->linkedmode));
|
|
||||||
if (d1 != d2) return d2 - d1;
|
|
||||||
}
|
|
||||||
switch (sort) {
|
|
||||||
case SORT_SELECTED:
|
|
||||||
goto sort_by_name;
|
|
||||||
case SORT_NAME: {
|
|
||||||
const char *n1, *n2;
|
|
||||||
sort_by_name:
|
|
||||||
n1 = f1->name;
|
|
||||||
n2 = f2->name;
|
|
||||||
while (*n1 && *n2) {
|
|
||||||
char c1 = *n1, c2 = *n2;
|
|
||||||
if ('A' <= c1 && 'Z' <= c1) c1 = c1 - 'A' + 'a';
|
|
||||||
if ('A' <= c2 && 'Z' <= c2) c2 = c2 - 'A' + 'a';
|
|
||||||
diff = (c1 - c2);
|
|
||||||
if ('0' <= c1 && c1 <= '9' && '0' <= c2 && c2 <= '9') {
|
|
||||||
long i1 = strtol(n1, (char**)&n1, 10);
|
|
||||||
long i2 = strtol(n2, (char**)&n2, 10);
|
|
||||||
diff = (int)(n1 - f1->name) - (int)(n2 - f2->name);
|
|
||||||
if (diff != 0)
|
|
||||||
return diff*sign;
|
|
||||||
if (i1 != i2)
|
|
||||||
return (i1 > i2 ? 1 : -1)*sign;
|
|
||||||
} else if (diff) {
|
|
||||||
return diff*sign;
|
|
||||||
} else {
|
|
||||||
++n1; ++n2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (*n1 - *n2)*sign;
|
|
||||||
}
|
}
|
||||||
case SORT_PERM:
|
switch (*sort) {
|
||||||
diff = -((f1->info.st_mode & 0x3FF) - (f2->info.st_mode & 0x3FF))*sign;
|
case SORT_DIR: {
|
||||||
break;
|
int d1 = S_ISDIR(f1->info.st_mode) || (S_ISLNK(f1->info.st_mode) && S_ISDIR(f1->linkedmode));
|
||||||
case SORT_SIZE:
|
int d2 = S_ISDIR(f2->info.st_mode) || (S_ISLNK(f2->info.st_mode) && S_ISDIR(f2->linkedmode));
|
||||||
diff = f1->info.st_size > f2->info.st_size ? -sign : sign;
|
diff = compare(d1, d2);
|
||||||
break;
|
break;
|
||||||
case SORT_MTIME:
|
}
|
||||||
if (f1->info.st_mtimespec.tv_sec == f2->info.st_mtimespec.tv_sec)
|
case SORT_SELECTED:
|
||||||
diff = f1->info.st_mtimespec.tv_nsec > f2->info.st_mtimespec.tv_nsec ? -sign : sign;
|
diff = compare(IS_SELECTED(f1), IS_SELECTED(f2));
|
||||||
else
|
break;
|
||||||
diff = f1->info.st_mtimespec.tv_sec > f2->info.st_mtimespec.tv_sec ? -sign : sign;
|
case SORT_NAME: {
|
||||||
break;
|
/* This sorting method is not identical to strverscmp(). Notably, bb's sort
|
||||||
case SORT_CTIME:
|
* will order: [0, 1, 9, 00, 01, 09, 10, 000, 010] instead of strverscmp()'s
|
||||||
if (f1->info.st_ctimespec.tv_sec == f2->info.st_ctimespec.tv_sec)
|
* order: [000, 00, 01, 010, 09, 0, 1, 9, 10]. I believe bb's sort is consistent
|
||||||
diff = f1->info.st_ctimespec.tv_nsec > f2->info.st_ctimespec.tv_nsec ? -sign : sign;
|
* with how people want their files grouped: all files padded to n digits
|
||||||
else
|
* will be grouped together, and files with the same padding will be sorted
|
||||||
diff = f1->info.st_ctimespec.tv_sec > f2->info.st_ctimespec.tv_sec ? -sign : sign;
|
* ordinally. This version also does case-insensitivity by lowercasing words,
|
||||||
break;
|
* so the following characters come before all letters: [\]^_`
|
||||||
case SORT_ATIME:
|
*/
|
||||||
if (f1->info.st_atimespec.tv_sec == f2->info.st_atimespec.tv_sec)
|
const char *n1 = f1->name, *n2 = f2->name;
|
||||||
diff = f1->info.st_atimespec.tv_nsec > f2->info.st_atimespec.tv_nsec ? -sign : sign;
|
while (*n1 && *n2) {
|
||||||
else
|
char c1 = LOWERCASE(*n1), c2 = LOWERCASE(*n2);
|
||||||
diff = f1->info.st_atimespec.tv_sec > f2->info.st_atimespec.tv_sec ? -sign : sign;
|
diff = -compare(c1, c2);
|
||||||
break;
|
if ('0' <= c1 && c1 <= '9' && '0' <= c2 && c2 <= '9') {
|
||||||
case SORT_RANDOM:
|
long i1 = strtol(n1, (char**)&n1, 10);
|
||||||
return f1->shufflepos - f2->shufflepos;
|
long i2 = strtol(n2, (char**)&n2, 10);
|
||||||
|
// Shorter numbers always go before longer. In practice, I assume
|
||||||
|
// filenames padded to the same number of digits should be grouped
|
||||||
|
// together, instead of
|
||||||
|
// [1.png, 0001.png, 2.png, 0002.png, 3.png], it makes more sense to have:
|
||||||
|
// [1.png, 2.png, 3.png, 0001.png, 0002.png]
|
||||||
|
diff = -compare((n1 - f1->name), (n2 - f2->name));
|
||||||
|
if (diff != 0) goto found_diff;
|
||||||
|
diff = -compare(i1, i2);
|
||||||
|
if (diff != 0) goto found_diff;
|
||||||
|
} else if (diff) {
|
||||||
|
goto found_diff;
|
||||||
|
} else {
|
||||||
|
++n1; ++n2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff = -compare(LOWERCASE(*n1), LOWERCASE(*n2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SORT_PERM:
|
||||||
|
diff = compare((f1->info.st_mode & 0x3FF), (f2->info.st_mode & 0x3FF));
|
||||||
|
break;
|
||||||
|
case SORT_SIZE:
|
||||||
|
diff = compare(f1->info.st_size, f2->info.st_size);
|
||||||
|
break;
|
||||||
|
case SORT_MTIME:
|
||||||
|
diff = compare_time(f1->info.st_mtimespec, f2->info.st_mtimespec);
|
||||||
|
break;
|
||||||
|
case SORT_CTIME:
|
||||||
|
diff = compare_time(f1->info.st_ctimespec, f2->info.st_ctimespec);
|
||||||
|
break;
|
||||||
|
case SORT_ATIME:
|
||||||
|
diff = compare_time(f1->info.st_atimespec, f2->info.st_atimespec);
|
||||||
|
break;
|
||||||
|
case SORT_RANDOM:
|
||||||
|
diff = f1->shufflepos - f2->shufflepos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
found_diff:
|
||||||
|
diff *= sign;
|
||||||
|
sign = 1;
|
||||||
}
|
}
|
||||||
if (diff == 0) goto sort_by_name;
|
|
||||||
return diff;
|
return diff;
|
||||||
|
#undef compare
|
||||||
|
#undef compare_time
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_file(bb_t *bb, const char *name)
|
int find_file(bb_t *bb, const char *name)
|
||||||
@ -706,10 +704,14 @@ void set_cursor(bb_t *bb, int newcur)
|
|||||||
bb->scroll = newcur - SCROLLOFF;
|
bb->scroll = newcur - SCROLLOFF;
|
||||||
else if (newcur > bb->scroll + (termheight-4) - SCROLLOFF)
|
else if (newcur > bb->scroll + (termheight-4) - SCROLLOFF)
|
||||||
bb->scroll = newcur - (termheight-4) + SCROLLOFF;
|
bb->scroll = newcur - (termheight-4) + SCROLLOFF;
|
||||||
int max_scroll = bb->nfiles - (termheight-4) - 1;
|
if (bb->nfiles <= termheight - 4) {
|
||||||
if (max_scroll < 0) max_scroll = 0;
|
bb->scroll = 0;
|
||||||
if (bb->scroll > max_scroll) bb->scroll = max_scroll;
|
} else {
|
||||||
if (bb->scroll < 0) bb->scroll = 0;
|
int max_scroll = bb->nfiles - (termheight-4) - 1;
|
||||||
|
if (max_scroll < 0) max_scroll = 0;
|
||||||
|
if (bb->scroll > max_scroll) bb->scroll = max_scroll;
|
||||||
|
if (bb->scroll < 0) bb->scroll = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -719,17 +721,18 @@ void set_scroll(bb_t *bb, int newscroll)
|
|||||||
{
|
{
|
||||||
newscroll = MIN(newscroll, bb->nfiles - (termheight-4) - 1);
|
newscroll = MIN(newscroll, bb->nfiles - (termheight-4) - 1);
|
||||||
newscroll = MAX(newscroll, 0);
|
newscroll = MAX(newscroll, 0);
|
||||||
bb->scroll = newscroll;
|
if (bb->nfiles <= termheight - 4)
|
||||||
|
newscroll = 0;
|
||||||
|
|
||||||
int delta = newscroll - bb->scroll;
|
int delta = newscroll - bb->scroll;
|
||||||
int oldcur = bb->cursor;
|
int oldcur = bb->cursor;
|
||||||
if (bb->nfiles < termheight - 4) {
|
if (bb->nfiles > termheight - 4) {
|
||||||
newscroll = 0;
|
|
||||||
} else {
|
|
||||||
if (bb->cursor > newscroll + (termheight-4) - SCROLLOFF && bb->scroll < bb->nfiles - (termheight-4) - 1)
|
if (bb->cursor > newscroll + (termheight-4) - SCROLLOFF && bb->scroll < bb->nfiles - (termheight-4) - 1)
|
||||||
bb->cursor = newscroll + (termheight-4) - SCROLLOFF;
|
bb->cursor = newscroll + (termheight-4) - SCROLLOFF;
|
||||||
else if (bb->cursor < newscroll + SCROLLOFF && bb->scroll > 0)
|
else if (bb->cursor < newscroll + SCROLLOFF && bb->scroll > 0)
|
||||||
bb->cursor = newscroll + SCROLLOFF;
|
bb->cursor = newscroll + SCROLLOFF;
|
||||||
|
} else {
|
||||||
|
newscroll = 0;
|
||||||
}
|
}
|
||||||
bb->scroll = newscroll;
|
bb->scroll = newscroll;
|
||||||
if (abs(bb->cursor - oldcur) < abs(delta))
|
if (abs(bb->cursor - oldcur) < abs(delta))
|
||||||
@ -776,10 +779,8 @@ entry_t* load_entry(const char *path)
|
|||||||
*/
|
*/
|
||||||
void populate_files(bb_t *bb, const char *path)
|
void populate_files(bb_t *bb, const char *path)
|
||||||
{
|
{
|
||||||
ino_t old_inode = 0;
|
|
||||||
// Clear old files (if any)
|
// Clear old files (if any)
|
||||||
if (bb->files) {
|
if (bb->files) {
|
||||||
old_inode = bb->files[bb->cursor]->info.st_ino;
|
|
||||||
for (int i = 0; i < bb->nfiles; i++) {
|
for (int i = 0; i < bb->nfiles; i++) {
|
||||||
if (--bb->files[i]->refcount <= 0)
|
if (--bb->files[i]->refcount <= 0)
|
||||||
free(bb->files[i]);
|
free(bb->files[i]);
|
||||||
@ -787,9 +788,11 @@ void populate_files(bb_t *bb, const char *path)
|
|||||||
free(bb->files);
|
free(bb->files);
|
||||||
bb->files = NULL;
|
bb->files = NULL;
|
||||||
}
|
}
|
||||||
int old_cursor = bb->cursor, old_scroll = bb->scroll;
|
|
||||||
|
int old_scroll = bb->scroll;
|
||||||
bb->nfiles = 0;
|
bb->nfiles = 0;
|
||||||
bb->cursor = 0;
|
bb->cursor = 0;
|
||||||
|
bb->scroll = 0;
|
||||||
|
|
||||||
if (path == NULL)
|
if (path == NULL)
|
||||||
return;
|
return;
|
||||||
@ -822,18 +825,12 @@ void populate_files(bb_t *bb, const char *path)
|
|||||||
strcpy(pathbuf, path);
|
strcpy(pathbuf, path);
|
||||||
pathbuf[pathlen] = '/';
|
pathbuf[pathlen] = '/';
|
||||||
for (struct dirent *dp; (dp = readdir(dir)) != NULL; ) {
|
for (struct dirent *dp; (dp = readdir(dir)) != NULL; ) {
|
||||||
/*
|
|
||||||
* 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] == '.') {
|
if (dp->d_name[0] == '.') {
|
||||||
int o = OPTNUM(bb->options['.']);
|
|
||||||
if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
|
if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
|
||||||
if (!(o & 1)) continue;
|
if (!bb->options.show_dotdot) continue;
|
||||||
} else if (dp->d_name[1] == '\0') {
|
} else if (dp->d_name[1] == '\0') {
|
||||||
if (!(o & 2)) continue;
|
if (!bb->options.show_dot) continue;
|
||||||
} else if (!(o & 4)) continue;
|
} else if (!bb->options.show_dotfiles) continue;
|
||||||
}
|
}
|
||||||
if ((size_t)bb->nfiles >= filecap) {
|
if ((size_t)bb->nfiles >= filecap) {
|
||||||
filecap += 100;
|
filecap += 100;
|
||||||
@ -861,27 +858,13 @@ void populate_files(bb_t *bb, const char *path)
|
|||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
free(selecthash);
|
free(selecthash);
|
||||||
if (bb->nfiles == 0) err("No files found (not even '..')");
|
|
||||||
// TODO: this may have some weird aliasing issues, but eh, it's simple and effective
|
// TODO: this may have some weird aliasing issues, but eh, it's simple and effective
|
||||||
for (int i = 0; i < bb->nfiles; i++)
|
for (int i = 0; i < bb->nfiles; i++)
|
||||||
bb->files[i]->shufflepos = rand();
|
bb->files[i]->shufflepos = rand();
|
||||||
|
|
||||||
qsort_r(bb->files, (size_t)bb->nfiles, sizeof(entry_t*), bb, compare_files);
|
qsort_r(bb->files, (size_t)bb->nfiles, sizeof(entry_t*), bb, compare_files);
|
||||||
if (samedir) {
|
set_scroll(bb, old_scroll);
|
||||||
if (old_inode) {
|
|
||||||
for (int i = 0; i < bb->nfiles; i++) {
|
|
||||||
if (bb->files[i]->info.st_ino == old_inode) {
|
|
||||||
set_scroll(bb, old_scroll);
|
|
||||||
set_cursor(bb, i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set_cursor(bb, old_cursor);
|
|
||||||
set_scroll(bb, old_scroll);
|
|
||||||
} else {
|
|
||||||
set_cursor(bb, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -971,35 +954,43 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
|
|||||||
}
|
}
|
||||||
case 'o': { // +options:
|
case 'o': { // +options:
|
||||||
if (!value) return BB_INVALID;
|
if (!value) return BB_INVALID;
|
||||||
|
char *cmdscratch = memcheck(strdup(value));
|
||||||
for (char *key = value; *key; ) {
|
char *v, *nextv = cmdscratch;
|
||||||
char *op = key;
|
while ((v = strsep(&nextv, " ")) != NULL) {
|
||||||
while (*op != '\0' && *op != '^' && *op != '=' && *op != '%')
|
if (!*v) continue;
|
||||||
++op;
|
char *k;
|
||||||
char *nextkey = op;
|
if ((k = strsep(&v, "=")) == NULL)
|
||||||
for ( ; key < op; key++)
|
v = "1";
|
||||||
switch (*op) {
|
#define matches(s) (strncmp(k, (s), strlen(k)) == 0)
|
||||||
case '\0': case ' ': case ',':
|
if (matches("..")) {
|
||||||
bb->options[(int)*key] = bb->initialopts[(int)*key];
|
bb->options.show_dotdot = v[0] == '1';
|
||||||
break;
|
} else if (matches(".*")) {
|
||||||
case '%': {
|
bb->options.show_dotfiles = v[0] == '1';
|
||||||
int n = (int)strtol(op+1, &nextkey, 10);
|
} else if (matches(".")) {
|
||||||
if (n < 0)
|
bb->options.show_dot = v[0] == '1';
|
||||||
bb->options[(int)*key] = (char)((OPTNUM(bb->options[(int)*key]) - n - 1) % (-n));
|
} else if (matches("columns") || matches("cols")) {
|
||||||
else
|
strncpy(bb->options.columns, v, MAX_COLS);
|
||||||
bb->options[(int)*key] = (char)((OPTNUM(bb->options[(int)*key]) + 1) % n);
|
for (int i = 0; i < v[i] && i < MAX_COLS; i++) {
|
||||||
bb->options[(int)*key] += '0';
|
int *colw = &bb->options.colwidths[i];
|
||||||
break;
|
switch (v[i]) {
|
||||||
|
case 'c': case 'm': case 'a': *colw = coldatew; break;
|
||||||
|
case 's': *colw = colsizew; break;
|
||||||
|
case 'p': *colw = colpermw; break;
|
||||||
|
case 'n': *colw = colnamew; break;
|
||||||
|
case '*': *colw = colselw; break;
|
||||||
|
case 'r': *colw = colrandw; break;
|
||||||
|
case '/': *colw = coldirw; break;
|
||||||
}
|
}
|
||||||
case '=':
|
}
|
||||||
bb->options[(int)*key] = op[1] == '0' ? 0 : op[1];
|
} else if (matches("sort")) {
|
||||||
nextkey = op + 2;
|
set_sort(bb, v);
|
||||||
break;
|
} else if (matches("aligns")) {
|
||||||
default: bb->options[(int)*key] = '1'; break;
|
strncpy(bb->options.aligns, v, MAX_COLS);
|
||||||
|
} else {
|
||||||
}
|
}
|
||||||
key = nextkey;
|
#undef matches
|
||||||
while (*key == ' ' || *key == ',') ++key;
|
|
||||||
}
|
}
|
||||||
|
free(cmdscratch);
|
||||||
populate_files(bb, bb->path);
|
populate_files(bb, bb->path);
|
||||||
return BB_REFRESH;
|
return BB_REFRESH;
|
||||||
}
|
}
|
||||||
@ -1097,9 +1088,6 @@ void bb_browse(bb_t *bb, const char *path)
|
|||||||
int lastwidth = termwidth, lastheight = termheight;
|
int lastwidth = termwidth, lastheight = termheight;
|
||||||
int lazy = 0, check_cmds = 1;
|
int lazy = 0, check_cmds = 1;
|
||||||
|
|
||||||
init_term();
|
|
||||||
fputs(T_ON(T_ALT_SCREEN), tty_out);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
char *real = realpath(path, NULL);
|
char *real = realpath(path, NULL);
|
||||||
if (!real || chdir(real))
|
if (!real || chdir(real))
|
||||||
@ -1117,7 +1105,12 @@ void bb_browse(bb_t *bb, const char *path)
|
|||||||
check_cmds = 1;
|
check_cmds = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memcpy(bb->initialopts, bb->options, sizeof(bb->initialopts));
|
memcpy(&bb->initialopts, &bb->options, sizeof(bb->initialopts));
|
||||||
|
|
||||||
|
init_term();
|
||||||
|
fputs(T_ON(T_ALT_SCREEN), tty_out);
|
||||||
|
bb->scroll = 0;
|
||||||
|
bb->cursor = 0;
|
||||||
|
|
||||||
refresh:
|
refresh:
|
||||||
lazy = 0;
|
lazy = 0;
|
||||||
@ -1183,26 +1176,26 @@ void bb_browse(bb_t *bb, const char *path)
|
|||||||
dt_ms += 1e-6*(double)(clicktime.tv_nsec - lastclick.tv_nsec);
|
dt_ms += 1e-6*(double)(clicktime.tv_nsec - lastclick.tv_nsec);
|
||||||
lastclick = clicktime;
|
lastclick = clicktime;
|
||||||
// Get column:
|
// Get column:
|
||||||
char column = '?';
|
char column[3] = "+?";
|
||||||
for (int col = 0, x = 0; col < 10; col++) {
|
for (int col = 0, x = 0; bb->options.columns[col]; col++) {
|
||||||
if (col > 0) x += 1;
|
if (col > 0) x += 1;
|
||||||
x += bb->colwidths[col];
|
x += bb->options.colwidths[col];
|
||||||
if (x >= mouse_x) {
|
if (x >= mouse_x) {
|
||||||
column = bb->options['0' + col];
|
column[1] = bb->options.columns[col];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mouse_y == 1) {
|
if (mouse_y == 1) {
|
||||||
if (bb->options['s'] == column)
|
char *pos;
|
||||||
bb->options['r'] = !bb->options['r'];
|
if ((pos = strstr(bb->options.sort, column)) && pos == bb->options.sort)
|
||||||
else bb->options['r'] = 0;
|
column[0] = '-';
|
||||||
bb->options['s'] = column;
|
set_sort(bb, column);
|
||||||
qsort_r(bb->files, (size_t)bb->nfiles, sizeof(entry_t*), bb, compare_files);
|
qsort_r(bb->files, (size_t)bb->nfiles, sizeof(entry_t*), bb, compare_files);
|
||||||
goto refresh;
|
goto refresh;
|
||||||
} else if (2 <= mouse_y && mouse_y <= termheight - 2) {
|
} else if (2 <= mouse_y && mouse_y <= termheight - 2) {
|
||||||
int clicked = bb->scroll + (mouse_y - 2);
|
int clicked = bb->scroll + (mouse_y - 2);
|
||||||
if (clicked > bb->nfiles - 1) goto next_input;
|
if (clicked > bb->nfiles - 1) goto next_input;
|
||||||
if (column == '+') {
|
if (column[1] == SORT_SELECTED) {
|
||||||
toggle_file(bb, bb->files[clicked]);
|
toggle_file(bb, bb->files[clicked]);
|
||||||
lazy = 0;
|
lazy = 0;
|
||||||
goto redraw;
|
goto redraw;
|
||||||
@ -1485,8 +1478,8 @@ int main(int argc, char *argv[])
|
|||||||
if (!real || chdir(real)) err("Not a valid path: %s\n", initial_path);
|
if (!real || chdir(real)) err("Not a valid path: %s\n", initial_path);
|
||||||
|
|
||||||
bb_t *bb = memcheck(calloc(1, sizeof(bb_t)));
|
bb_t *bb = memcheck(calloc(1, sizeof(bb_t)));
|
||||||
bb->options['0'] = 'n';
|
bb->options.columns[0] = 'n';
|
||||||
bb->options['s'] = 'n';
|
bb->options.sort[0] = 'n';
|
||||||
bb_browse(bb, real);
|
bb_browse(bb, real);
|
||||||
free(real);
|
free(real);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
jump:<key> Jump to the mark associated with <key>
|
jump:<key> Jump to the mark associated with <key>
|
||||||
mark:<key>[=<path>] Associate <key> with <path> (or current dir, if blank)
|
mark:<key>[=<path>] Associate <key> with <path> (or current dir, if blank)
|
||||||
move:<num*> Move the cursor a numeric amount
|
move:<num*> Move the cursor a numeric amount
|
||||||
option:(<o>[=<v>|%n])+ Set options (see below)
|
option:(<k>=<v>)+ Set space-separated options (see below)
|
||||||
quit Quit bb
|
quit Quit bb
|
||||||
refresh Refresh the file listing
|
refresh Refresh the file listing
|
||||||
scroll:<num*> Scroll the view a numeric amount
|
scroll:<num*> Scroll the view a numeric amount
|
||||||
@ -38,8 +38,7 @@
|
|||||||
toggle:<filename> Toggle the selection status of <filename>
|
toggle:<filename> Toggle the selection status of <filename>
|
||||||
|
|
||||||
Currently supported options:
|
Currently supported options:
|
||||||
's': sort, one of (n)ame (s)ize (c)reation (m)odification (a)ccess (p)ermission (r)andom
|
'sort': a list of things to sort by.
|
||||||
'r': reverse-sort (boolean)
|
|
||||||
'.': dotfiles visibility (bit 1: "..", bit 2: ".", bit 3: .whatever)
|
'.': dotfiles visibility (bit 1: "..", bit 2: ".", bit 3: .whatever)
|
||||||
'i': interleave files and directories (boolean), when false, directories are always at the top
|
'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])
|
'0'-'9': what to put in each of the (maximum of 10) columns (one of: [nscmap])
|
||||||
@ -109,7 +108,7 @@ const char *startupcmds[] = {
|
|||||||
"+mark:0", "+mark:~=~", "+mark:h=~", "+mark:/=/", "+mark:c=~/.config",
|
"+mark:0", "+mark:~=~", "+mark:h=~", "+mark:/=/", "+mark:c=~/.config",
|
||||||
"+mark:l=~/.local",
|
"+mark:l=~/.local",
|
||||||
// Default column and sorting options:
|
// Default column and sorting options:
|
||||||
"+opt:0=+,1=s,2=m,3=p,4=n,s=n,E=1,.=1",
|
"+opt:sort=+/+n col=*smpn aligns=ccccl",
|
||||||
NULL, // NULL-terminated array
|
NULL, // NULL-terminated array
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user