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
backspace: go to parent dir
gg: go to top
g: go to top
G: go to bottom
ctrl+u: jump up
@ -60,6 +60,7 @@ u: sort files
.: toggle hidden files
i: toggle file details
X: toggle executable
!: open shell in current dir
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
/: search
!: open shell in current dir
x: view file/dir attributes
e: show history

18
ccc.1
View file

@ -1,11 +1,11 @@
.
.TH CCC "1" "March 2024" "ccc" "User Commands"
.SH NAME
ccc \- Fast TUI file manager written in C, using ncurses.
ccc \- Fast, small, hackable TUI file manager with no dependency
.SH SYNOPSIS
.B ccc
.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
.SH "Usage"
.
@ -16,15 +16,15 @@ j: scroll down
k: scroll up
l: go to child dir
left: go to parent dir
down: scroll down
up: scroll up
left: go to parent dir
down: scroll down
up: scroll up
right: go to child dir
enter: go to child dir/open file
backspace: go to parent dir
gg: go to top
g: go to top
G: go to bottom
ctrl+u: jump up
@ -34,13 +34,19 @@ t: go to trash dir
~: go to home dir
-: go to previous dir
z: refresh current dir
:: go to a directory by typing
u: sort files
.: toggle hidden files
i: toggle file details
X: toggle executable
!: open shell in current dir
A: show directory disk usage/block size
f: new file
n: new dir
r: rename
space: mark file
a: mark all files in directory

122
ccc.c
View file

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

View file

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