Compare commits

...

5 commits

4 changed files with 99 additions and 48 deletions

View file

@ -44,7 +44,7 @@ right: go to child dir
enter: go to child dir/open file enter: go to child dir/open file
backspace: go to parent dir backspace: go to parent dir
gg: go to top g: go to top
G: go to bottom G: go to bottom
ctrl+u: jump up ctrl+u: jump up
@ -60,6 +60,7 @@ u: sort files
.: toggle hidden files .: toggle hidden files
i: toggle file details i: toggle file details
X: toggle executable X: toggle executable
!: open shell in current dir
A: show directory disk usage/block size A: show directory disk usage/block size
@ -80,7 +81,6 @@ o: open file with
O: open file with a GUI program detached from file manager O: open file with a GUI program detached from file manager
/: search /: search
!: open shell in current dir
x: view file/dir attributes x: view file/dir attributes
e: show history e: show history

12
ccc.1
View file

@ -1,11 +1,11 @@
. .
.TH CCC "1" "March 2024" "ccc" "User Commands" .TH CCC "1" "March 2024" "ccc" "User Commands"
.SH NAME .SH NAME
ccc \- Fast TUI file manager written in C, using ncurses. ccc \- Fast, small, hackable TUI file manager with no dependency
.SH SYNOPSIS .SH SYNOPSIS
.B ccc .B ccc
.SH DESCRIPTION .SH DESCRIPTION
ccc is a rewrite of fff file manager in C aiming for usefulness and speed. The fact that it is written in C makes it more versatile and rapid, enabling us to add features that were previously ruled out due to time complexity. You may call it a soft fork. Soft fork of fff in C aiming for size and speed with no dependency, hackable with patches and configurable
.PP .PP
.SH "Usage" .SH "Usage"
. .
@ -24,7 +24,7 @@ right: go to child dir
enter: go to child dir/open file enter: go to child dir/open file
backspace: go to parent dir backspace: go to parent dir
gg: go to top g: go to top
G: go to bottom G: go to bottom
ctrl+u: jump up ctrl+u: jump up
@ -34,13 +34,19 @@ t: go to trash dir
~: go to home dir ~: go to home dir
-: go to previous dir -: go to previous dir
z: refresh current dir z: refresh current dir
:: go to a directory by typing
u: sort files
.: toggle hidden files
i: toggle file details i: toggle file details
X: toggle executable X: toggle executable
!: open shell in current dir
A: show directory disk usage/block size A: show directory disk usage/block size
f: new file f: new file
n: new dir
r: rename
space: mark file space: mark file
a: mark all files in directory a: mark all files in directory

120
ccc.c
View file

@ -35,9 +35,11 @@ enum keys {
PAGE_UP, PAGE_UP,
PAGE_DOWN, PAGE_DOWN,
}; };
typedef struct { typedef struct {
int key; int key;
} key; } key;
#define PATH_MAX 4096 /* Max length of path */ #define PATH_MAX 4096 /* Max length of path */
#include "config.h" #include "config.h"
@ -66,9 +68,11 @@ void goto_dir(void);
void create_dir(void); void create_dir(void);
void create_file(void); void create_file(void);
void delete_files(void); void delete_files(void);
void start_shell(void);
void yank_clipboard(void);
void wpprintw(const char *fmt, ...); void wpprintw(const char *fmt, ...);
void move_cursor(int row, int col); void move_cursor(int row, int col);
int read_key(void); int readch(void);
int get_window_size(int *row, int *col); int get_window_size(int *row, int *col);
void bprintf(const char *fmt, ...); void bprintf(const char *fmt, ...);
@ -165,19 +169,18 @@ int main(int argc, char **argv)
if (to_open_file) { if (to_open_file) {
sel_file = arraylist_search(files, argv_cp, 1); sel_file = arraylist_search(files, argv_cp, 1);
list_files();
} }
int ch, ch2; int ch, run = 1;
int run = 1;
while (run) { while (run) {
ch = read_key(); list_files();
ch = readch();
switch (ch) { switch (ch) {
/* quit */ /* quit */
case 'q': case 'q':
if (write_last_d() == -1) { if (write_last_d() == -1) {
/* prompt user so error message can be shown to user */ /* prompt user so error message can be shown to user */
read_key(); readch();
} }
cleanup(); cleanup();
run = 0; run = 0;
@ -239,8 +242,6 @@ int main(int argc, char **argv)
sel_file -= jump_num; sel_file -= jump_num;
else else
sel_file = 0; sel_file = 0;
list_files();
break; break;
/* go up */ /* go up */
@ -248,8 +249,6 @@ int main(int argc, char **argv)
case 'k': case 'k':
if (sel_file > 0) if (sel_file > 0)
sel_file--; sel_file--;
list_files();
break; break;
/* jump down */ /* jump down */
@ -258,8 +257,6 @@ int main(int argc, char **argv)
sel_file += jump_num; sel_file += jump_num;
else else
sel_file = (files->length - 1); sel_file = (files->length - 1);
list_files();
break; break;
/* go down */ /* go down */
@ -267,27 +264,16 @@ int main(int argc, char **argv)
case 'j': case 'j':
if (sel_file < (files->length - 1)) if (sel_file < (files->length - 1))
sel_file++; sel_file++;
list_files();
break; break;
/* jump to the bottom */ /* jump to the bottom */
case 'G': case 'G':
sel_file = (files->length - 1); sel_file = (files->length - 1);
list_files();
break; break;
/* jump to the top */ /* jump to the top */
case 'g':
ch2 = read_key();
switch (ch2) {
case 'g': case 'g':
sel_file = 0; sel_file = 0;
list_files();
break;
default:
break;
}
break; break;
/* '~' go to $HOME */ /* '~' go to $HOME */
@ -296,6 +282,7 @@ int main(int argc, char **argv)
if (home == NULL) { if (home == NULL) {
wpprintw("$HOME not defined"); wpprintw("$HOME not defined");
} else { } else {
strcpy(p_cwd, cwd);
change_dir(home, 0, 0); change_dir(home, 0, 0);
} }
break; break;
@ -366,12 +353,20 @@ int main(int argc, char **argv)
case 'X': case 'X':
toggle_executable(); toggle_executable();
change_dir(cwd, 0, 0);
break;
case '!':
start_shell();
break;
case 'y':
yank_clipboard();
break; break;
/* mark one file */ /* mark one file */
case SPACE: case SPACE:
add_file_stat(files->items[sel_file].name, files->items[sel_file].path, 1); add_file_stat(files->items[sel_file].name, files->items[sel_file].path, 1);
list_files();
break; break;
/* mark all files in directory */ /* mark all files in directory */
@ -463,8 +458,10 @@ void show_help(void)
"r: rename\n\nspace: mark file\na: mark all files in directory\nd: trash" "r: rename\n\nspace: mark file\na: mark all files in directory\nd: trash"
"\n\n?: show help\nq: exit with last dir written to file\n" "\n\n?: show help\nq: exit with last dir written to file\n"
"ctrl+c exit without writing last dir" "ctrl+c exit without writing last dir"
"\nPress any key to continue"
); );
wpprintw("Visit https://github.com/night0721/ccc or use 'man ccc' for help"); wpprintw("Visit https://github.com/night0721/ccc or use 'man ccc' for help");
readch();
} }
/* /*
@ -599,8 +596,6 @@ void populate_files(const char *path, int ftype, ArrayList **list)
free(tmp2); free(tmp2);
} }
closedir(dp); closedir(dp);
if (list == &files)
list_files();
} else { } else {
wpprintw("stat failed: %s", strerror(errno)); wpprintw("stat failed: %s", strerror(errno));
} }
@ -1004,7 +999,6 @@ void edit_file(void)
} else if (pid > 0) { } else if (pid > 0) {
/* Parent process */ /* Parent process */
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
list_files();
} else { } else {
/* Fork failed */ /* Fork failed */
wpprintw("fork failed: %s", strerror(errno)); wpprintw("fork failed: %s", strerror(errno));
@ -1047,7 +1041,7 @@ int write_last_d(void)
if (!strcmp(last_d, "")) { if (!strcmp(last_d, "")) {
strcpy(last_d, getenv("CCC_LAST_D")); strcpy(last_d, getenv("CCC_LAST_D"));
if (!strcmp(last_d, "")) { if (!strcmp(last_d, "")) {
wpprintw("$CCC_LAST_D not defined (Press enter to continue)"); wpprintw("$CCC_LAST_D not defined (Press any key to continue)");
return -1; return -1;
} }
} else { } else {
@ -1064,7 +1058,7 @@ int write_last_d(void)
mkdir_p(last_ddup); mkdir_p(last_ddup);
FILE *last_d_file = fopen(last_d, "w"); FILE *last_d_file = fopen(last_d, "w");
if (last_d_file == NULL) { if (last_d_file == NULL) {
wpprintw("Cannot open last directory file (Press enter to continue)"); wpprintw("Cannot open last directory file (Press any key to continue)");
return -1; return -1;
} }
fwrite(cwd, strlen(cwd), sizeof(char), last_d_file); fwrite(cwd, strlen(cwd), sizeof(char), last_d_file);
@ -1081,7 +1075,6 @@ int sort_compare(const void *a, const void *b)
void sort_files(void) void sort_files(void)
{ {
qsort(files->items, files->length, sizeof(file), sort_compare); qsort(files->items, files->length, sizeof(file), sort_compare);
list_files();
} }
char *get_panel_string(char *prompt) char *get_panel_string(char *prompt)
@ -1090,9 +1083,10 @@ char *get_panel_string(char *prompt)
char *buf = memalloc(bufsize); char *buf = memalloc(bufsize);
size_t buflen = 0; size_t buflen = 0;
buf[0] = '\0'; buf[0] = '\0';
bprintf("\033[?25h");
while (1) { while (1) {
wpprintw(prompt); wpprintw("%s%s", prompt, buf);
int c = read_key(); int c = readch();
if (c == BACKSPACE) { if (c == BACKSPACE) {
if (buflen != 0) { if (buflen != 0) {
buf[--buflen] = '\0'; buf[--buflen] = '\0';
@ -1100,10 +1094,12 @@ char *get_panel_string(char *prompt)
} else if (c == '\033') { } else if (c == '\033') {
wpprintw(""); wpprintw("");
free(buf); free(buf);
bprintf("\033[?25l");
return NULL; return NULL;
} else if (c == '\r') { } else if (c == '\r') {
wpprintw(""); wpprintw("");
if (buflen != 0) { if (buflen != 0) {
bprintf("\033[?25l");
return buf; return buf;
} }
} else if (!iscntrl(c) && c < 128) { } else if (!iscntrl(c) && c < 128) {
@ -1119,6 +1115,7 @@ char *get_panel_string(char *prompt)
if (buf[0] == '~') if (buf[0] == '~')
replace_home(buf); replace_home(buf);
bprintf("\033[?25l");
return buf; return buf;
} }
@ -1126,6 +1123,9 @@ void rename_file(void)
{ {
char *filename = files->items[sel_file].path; char *filename = files->items[sel_file].path;
char *input = get_panel_string("Rename file: "); char *input = get_panel_string("Rename file: ");
if (!input) {
return;
}
char *newfilename = estrdup(filename); char *newfilename = estrdup(filename);
/* remove basename of newfilename */ /* remove basename of newfilename */
char *last_slash = strrchr(newfilename, '/'); char *last_slash = strrchr(newfilename, '/');
@ -1134,8 +1134,8 @@ void rename_file(void)
strcat(newfilename, "/"); strcat(newfilename, "/");
strcat(newfilename, input); strcat(newfilename, input);
if (rename(filename, newfilename)) { if (rename(filename, newfilename)) {
wpprintw("rename failed: %s (Press enter to continue)", strerror(errno)); wpprintw("rename failed: %s (Press any key to continue)", strerror(errno));
read_key(); readch();
} else { } else {
change_dir(cwd, 0, 0); change_dir(cwd, 0, 0);
wpprintw("Renamed %s to %s", filename, newfilename); wpprintw("Renamed %s to %s", filename, newfilename);
@ -1149,13 +1149,13 @@ void goto_dir(void)
char *input = get_panel_string("Goto dir: "); char *input = get_panel_string("Goto dir: ");
struct stat st; struct stat st;
if (lstat(input, &st)) { if (lstat(input, &st)) {
wpprintw("lstat failed: %s (Press enter to continue)", strerror(errno)); wpprintw("lstat failed: %s (Press any key to continue)", strerror(errno));
read_key(); readch();
} }
/* chdir to directory from argument */ /* chdir to directory from argument */
if (S_ISDIR(st.st_mode) && chdir(input)) { if (S_ISDIR(st.st_mode) && chdir(input)) {
wpprintw("chdir failed: %s (Press enter to continue)", strerror(errno)); wpprintw("chdir failed: %s (Press any key to continue)", strerror(errno));
read_key(); readch();
} }
getcwd(cwd, PATH_MAX); getcwd(cwd, PATH_MAX);
change_dir(cwd, 0, 0); change_dir(cwd, 0, 0);
@ -1211,6 +1211,48 @@ void delete_files(void)
} }
} }
void start_shell(void)
{
bprintf("\033[2J\033[?25h");
move_cursor(1, 1);
char shell[PATH_MAX];
strcpy(shell, getenv("SHELL"));
if (strlen(shell) == 0) {
strcpy(shell, "sh");
} else {
pid_t pid = fork();
if (pid == 0) {
/* Child process */
execlp(shell, shell, NULL);
_exit(1); /* Exit if exec fails */
} else if (pid > 0) {
/* Parent process */
waitpid(pid, NULL, 0);
bprintf("\033[?25l");
} else {
/* Fork failed */
wpprintw("fork failed: %s", strerror(errno));
}
}
}
void yank_clipboard(void)
{
pid_t pid = fork();
if (pid == 0) {
/* Child process */
execlp(clipboard, clipboard, files->items[sel_file].name, NULL);
_exit(1); /* Exit if exec fails */
} else if (pid > 0) {
/* Parent process */
waitpid(pid, NULL, 0);
bprintf("\033[?25l");
} else {
/* Fork failed */
wpprintw("fork failed: %s", strerror(errno));
}
}
/* /*
* Print line to the panel * Print line to the panel
*/ */
@ -1233,7 +1275,7 @@ void move_cursor(int row, int col)
bprintf("\033[%d;%dH", row, col); bprintf("\033[%d;%dH", row, col);
} }
int read_key(void) int readch(void)
{ {
int nread; int nread;
char c; char c;

View file

@ -42,6 +42,9 @@ static int dirs_size = 0;
/* Default text editor */ /* Default text editor */
static const char *editor = "nvim"; static const char *editor = "nvim";
/* Default clipboard program */
static const char *clipboard = "wl-copy";
/* File location to write last directory */ /* File location to write last directory */
static char last_d[PATH_MAX] = "~/.cache/ccc/.ccc_d"; static char last_d[PATH_MAX] = "~/.cache/ccc/.ccc_d";