Compare commits

..

13 commits

6 changed files with 181 additions and 134 deletions

View file

@ -55,18 +55,20 @@ t: go to trash 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
i: toggle file details
u: sort files
x: view file/dir attributes
e: show history
y: copy filename to clipboard
!: open shell in current dir
f: new file
n: new dir
r: rename
X: toggle executable
space: mark file
a: mark all files in directory
@ -82,10 +84,6 @@ O: open file with a GUI program detached from file manager
/: search
x: view file/dir attributes
e: show history
y: copy filename to clipboard
c: copy
m: move
s: symbolic link

12
ccc.1
View file

@ -35,18 +35,20 @@ t: go to trash 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
i: toggle file details
u: sort files
x: view file/dir attributes
e: show history
y: copy filename to clipboard
!: open shell in current dir
f: new file
n: new dir
r: rename
X: toggle executable
space: mark file
a: mark all files in directory

249
ccc.c
View file

@ -53,7 +53,6 @@ void mkdir_p(const char *destdir);
void populate_files(const char *path, int ftype, ArrayList **list);
int get_directory_size(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf);
void add_file_stat(char *filename, char *path, int ftype);
char *get_file_mode(mode_t mode);
void list_files(void);
void show_file_content(void);
void edit_file(void);
@ -70,6 +69,8 @@ void create_file(void);
void delete_files(void);
void start_shell(void);
void yank_clipboard(void);
void view_file_attr(void);
void show_history(void);
void wpprintw(const char *fmt, ...);
void move_cursor(int row, int col);
int readch(void);
@ -81,9 +82,9 @@ unsigned int focus = 0;
long sel_file = 0;
int file_picker = 1;
int to_open_file = 0;
char *argv_cp;
char *cwd;
char *p_cwd; /* previous cwd */
char argv_cp[PATH_MAX];
char cwd[PATH_MAX];
char p_cwd[PATH_MAX]; /* previous cwd */
int half_width;
ArrayList *files;
ArrayList *marked;
@ -114,7 +115,7 @@ int main(int argc, char **argv)
perror("ccc");
die("Error from chdir");
} else if (S_ISREG(st.st_mode)) {
argv_cp = estrdup(argv[1]);
strcpy(argv_cp, argv[1]);
char *last_slash = strrchr(argv_cp, '/');
if (last_slash) {
*last_slash = '\0';
@ -160,10 +161,7 @@ int main(int argc, char **argv)
marked = arraylist_init(100);
hashtable_init();
cwd = memalloc(PATH_MAX);
getcwd(cwd, PATH_MAX);
p_cwd = memalloc(PATH_MAX);
p_cwd[0] = '\0';
populate_files(cwd, 0, &files);
handle_sigwinch(-1);
@ -192,27 +190,27 @@ int main(int argc, char **argv)
break;
/* go back */
case BACKSPACE:
case ARROW_LEFT:
case 'h':
case BACKSPACE:;
case ARROW_LEFT:;
case 'h':;
char dir[PATH_MAX];
strcpy(dir, cwd);
/* get parent directory */
strcpy(p_cwd, cwd);
char *last_slash = strrchr(cwd, '/');
if (last_slash != NULL) {
if (last_slash == cwd) {
char *last_slash = strrchr(dir, '/');
if (last_slash) {
if (!strcmp(last_slash, dir)) {
change_dir("/", 0, 0);
break;
}
*last_slash = '\0';
change_dir(cwd, 0, 0);
change_dir(dir, 0, 0);
}
break;
/* enter directory/open a file */
case ENTER:
case ARROW_RIGHT:
case 'l':
strcpy(p_cwd, cwd);
case ENTER:;
case ARROW_RIGHT:;
case 'l':;
file c_file = files->items[sel_file];
/* Check if it is directory or a regular file */
@ -279,10 +277,10 @@ int main(int argc, char **argv)
/* '~' go to $HOME */
case TILDE:;
char *home = getenv("HOME");
if (home == NULL) {
wpprintw("$HOME not defined");
if (!home) {
wpprintw("$HOME not defined (Press any key to continue)");
readch();
} else {
strcpy(p_cwd, cwd);
change_dir(home, 0, 0);
}
break;
@ -290,9 +288,9 @@ int main(int argc, char **argv)
/* go to the trash dir */
case 't':;
char *trash_dir = check_trash_dir();
if (trash_dir != NULL) {
strcpy(p_cwd, cwd);
if (trash_dir) {
change_dir(trash_dir, 0, 0);
free(trash_dir);
}
break;
@ -364,6 +362,16 @@ int main(int argc, char **argv)
yank_clipboard();
break;
case 'o':
case 'O':
case 'x':
view_file_attr();
break;
case 'e':
show_history();
break;
/* mark one file */
case SPACE:
add_file_stat(files->items[sel_file].name, files->items[sel_file].path, 1);
@ -429,8 +437,6 @@ void handle_sigwinch(int ignore)
void cleanup(void)
{
if (argv_cp != NULL)
free(argv_cp);
if (files->length != 0) {
arraylist_free(files);
}
@ -496,8 +502,18 @@ char *check_trash_dir(void)
*/
void change_dir(const char *buf, int selection, int ftype)
{
if (cwd != buf)
strcpy(cwd, buf);
if (strcmp(cwd, buf) != 0) {
char tmp[PATH_MAX];
strcpy(tmp, buf);
strcpy(p_cwd, cwd);
strcpy(cwd, tmp);
char history_path[PATH_MAX];
strcpy(history_path, "~/.cache/ccc/history");
replace_home(history_path);
FILE *history_file = fopen(history_path, "a");
fprintf(history_file, "%s\n", cwd);
fclose(history_file);
}
if (ftype == 0)
arraylist_free(files);
chdir(cwd);
@ -511,12 +527,11 @@ void change_dir(const char *buf, int selection, int ftype)
*/
void mkdir_p(const char *destdir)
{
char *path = memalloc(PATH_MAX);
char dir_path[PATH_MAX] = "";
char path[PATH_MAX], dir_path[PATH_MAX];
if (destdir[0] == '~') {
char *home = getenv("HOME");
if (home == NULL) {
if (!home) {
wpprintw("$HOME not defined");
return;
}
@ -531,7 +546,7 @@ void mkdir_p(const char *destdir)
dir_path[0] = '/';
char *token = strtok(path, "/");
while (token != NULL) {
while (token) {
strcat(dir_path, token);
strcat(dir_path, "/");
@ -544,13 +559,10 @@ void mkdir_p(const char *destdir)
}
wpprintw("mkdir failed: %s", strerror(errno));
free(path);
return;
}
token = strtok(NULL, "/");
}
free(path);
return;
}
@ -563,13 +575,13 @@ void populate_files(const char *path, int ftype, ArrayList **list)
DIR *dp;
struct dirent *ep;
if ((dp = opendir(path)) != NULL) {
if ((dp = opendir(path))) {
if (ftype == 0) {
tmp1 = arraylist_init(10);
tmp2 = arraylist_init(10);
}
while ((ep = readdir(dp)) != NULL) {
while ((ep = readdir(dp))) {
char *filename = estrdup(ep->d_name);
/* Filter out dotfiles */
@ -586,6 +598,7 @@ void populate_files(const char *path, int ftype, ArrayList **list)
if (ftype == 0) {
*list = arraylist_init(tmp1->length + tmp2->length);
(*list)->length = tmp1->length + tmp2->length;
/* Need to see how to sort by date */
qsort(tmp1->items, tmp1->length, sizeof(file), sort_compare);
qsort(tmp2->items, tmp2->length, sizeof(file), sort_compare);
memcpy((*list)->items, tmp1->items, tmp1->length * sizeof(file));
@ -622,29 +635,29 @@ void add_file_stat(char *filename, char *path, int ftype)
}
int type;
char *icon_str = memalloc(8);
char icon_str[5];
filename[strlen(filename)] = '\0';
/* handle file without extension
* ext is the extension if . exist in filename
* otherwise is nothing and handled through tenery operator */
char *ext = strrchr(filename, '.');
if (ext != NULL) {
if (ext) {
ext += 1;
}
/* add file extension */
icon *ext_icon = hashtable_search(ext != NULL ? ext : filename);
if (ext_icon == NULL)
icon *ext_icon = hashtable_search(ext ? ext : filename);
if (!ext_icon)
memcpy(icon_str, "", 4);
else
memcpy(icon_str, ext_icon->icon, 5);
memcpy(icon_str, ext_icon->icon, 4);
int color = DEF_COLOR;
if (S_ISDIR(file_stat.st_mode)) {
type = DRY; /* dir */
color = DIR_COLOR;
memcpy(icon_str, "󰉋", 5);
memcpy(icon_str, "󰉋", 4);
} else if (S_ISREG(file_stat.st_mode)) {
type = REG; /* regular file */
color = REG_COLOR;
@ -677,7 +690,7 @@ void add_file_stat(char *filename, char *path, int ftype)
/* get last modified time */
size_t time_size = 17;
char *time = memalloc(time_size);
char time[time_size];
/* Format last modified time to a string */
strftime(time, time_size, "%Y-%m-%d %H:%M", localtime(&file_stat.st_mtime));
@ -694,10 +707,10 @@ void add_file_stat(char *filename, char *path, int ftype)
}
}
/* 4 before decimal + 1 dot + decimal_place (after decimal) +
unit length (1 for K, 3 for KiB, taking units[1] as B never changes) + 1 space + 1 null */
* unit length (1 for K, 3 for KiB, taking units[1] as B never changes) + 1 space + 1 null */
static const char* units[] = {"B", "K", "M", "G", "T", "P"};
int size_size = 4 + 1 + decimal_place + strlen(units[1]) + 1 + 1;
char *size = memalloc(size_size);
char size[size_size];
int unit = 0;
while (bytes > 1024) {
bytes /= 1024;
@ -710,46 +723,33 @@ void add_file_stat(char *filename, char *path, int ftype)
sprintf(size, "%.*f%s", decimal_place, bytes, units[unit]);
}
/* get file mode string */
char *mode_str = get_file_mode(file_stat.st_mode);
char mode_str[11];
mode_str[0] = S_ISDIR(file_stat.st_mode) ? 'd' : '-';
mode_str[1] = (file_stat.st_mode & S_IRUSR) ? 'r' : '-';
mode_str[2] = (file_stat.st_mode & S_IWUSR) ? 'w' : '-';
mode_str[3] = (file_stat.st_mode & S_IXUSR) ? 'x' : '-';
mode_str[4] = (file_stat.st_mode & S_IRGRP) ? 'r' : '-';
mode_str[5] = (file_stat.st_mode & S_IWGRP) ? 'w' : '-';
mode_str[6] = (file_stat.st_mode & S_IXGRP) ? 'x' : '-';
mode_str[7] = (file_stat.st_mode & S_IROTH) ? 'r' : '-';
mode_str[8] = (file_stat.st_mode & S_IWOTH) ? 'w' : '-';
mode_str[9] = (file_stat.st_mode & S_IXOTH) ? 'x' : '-';
mode_str[10] = 0;
if (mode_str[0] == '-' && (mode_str[3] == 'x' || mode_str[6] == 'x' || mode_str[9] == 'x')) {
color = EXE_COLOR;
}
/* mode_str(11) + time(17) + size_size + 2 spaces + 1 null */
size_t stat_size = 11 + time_size + size_size + 3;
/* mode_str + time(17) + size_size + 2 spaces + 1 null */
size_t stat_size = 11 + 17 + size_size + 3;
char *total_stat = memalloc(stat_size);
snprintf(total_stat, stat_size, "%s %s %-*s", mode_str, time, size_size, size);
sprintf(total_stat, "%s %s %-*s", mode_str, time, size_size, size);
/* DIR if color is blue */
if (color == 34)
arraylist_add(tmp1, filename, path, total_stat, type, icon_str, color, 0, 0);
else
arraylist_add(tmp2, filename, path, total_stat, type, icon_str, color, 0, 0);
free(time);
free(size);
free(mode_str);
}
/*
* get file mode string from stat mode
* eg: drwxr-sr-x
*/
char *get_file_mode(mode_t mode)
{
char *mode_str = memalloc(11);
mode_str[0] = S_ISDIR(mode) ? 'd' : '-';
mode_str[1] = (mode & S_IRUSR) ? 'r' : '-';
mode_str[2] = (mode & S_IWUSR) ? 'w' : '-';
mode_str[3] = (mode & S_IXUSR) ? 'x' : '-';
mode_str[4] = (mode & S_IRGRP) ? 'r' : '-';
mode_str[5] = (mode & S_IWGRP) ? 'w' : '-';
mode_str[6] = (mode & S_IXGRP) ? 'x' : '-';
mode_str[7] = (mode & S_IROTH) ? 'r' : '-';
mode_str[8] = (mode & S_IWOTH) ? 'w' : '-';
mode_str[9] = (mode & S_IXOTH) ? 'x' : '-';
mode_str[10] = '\0';
return mode_str;
}
/*
@ -796,7 +796,7 @@ void list_files(void)
if (num_marked > 0) {
/* Determine length of formatted string */
int m_len = snprintf(NULL, 0, "[%ld] selected", num_marked);
char *selected = memalloc(m_len + 1);
char selected[m_len + 1];
snprintf(selected, m_len + 1, "[%ld] selected", num_marked);
wpprintw("(%ld/%ld) %s %s", sel_file + 1, files->length, selected, cwd);
@ -810,8 +810,8 @@ void list_files(void)
/* check is file marked for action */
int is_marked = arraylist_search(marked, files->items[i].path, 0) != -1;
move_cursor(i + 1, 1);
if (is_marked) color = 32;
bprintf("\033[30m\033[%dm%s\033[0m\n",
if (is_marked) color = MAR_COLOR;
bprintf("\033[30m\033[%dm%s\033[m\n",
is_selected ? color + 10 : color, line);
free(line);
@ -885,7 +885,7 @@ void show_file_content(void)
return;
}
FILE *file = fopen(current_file.path, "r");
if (file == NULL) {
if (!file) {
bprintf("Unable to read %s", current_file.name);
return;
}
@ -919,7 +919,7 @@ void show_file_content(void)
char buffer[4096];
int row = 1;
FILE *stream = fdopen(pipe_fd[0], "r");
while (fgets(buffer, sizeof(buffer), stream) != NULL && row <= rows - 1) {
while (fgets(buffer, sizeof(buffer), stream) && row <= rows - 1) {
buffer[strcspn(buffer, "\n")] = 0;
if (buffer[0] == '\0' || strspn(buffer, " \t") == strlen(buffer)) {
@ -982,9 +982,9 @@ void show_file_content(void)
*/
void edit_file(void)
{
if (editor == NULL) {
if (!strcmp(editor, "")) {
editor = getenv("EDITOR");
if (editor == NULL) {
if (!editor) {
wpprintw("$EDITOR not defined");
return;
}
@ -1011,21 +1011,23 @@ void toggle_executable(void)
file f = files->items[sel_file];
struct stat st;
if (stat(f.path, &st) == -1) {
wpprintw("stat failed: %s", strerror(errno));
wpprintw("stat failed: %s (Press any key to continue)", strerror(errno));
readch();
return;
}
if (f.type == DRY)
return;
/* chmod by xor executable bits */
if (chmod(f.path, st.st_mode ^ (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) {
wpprintw("Error toggling executable: %s", strerror(errno));
wpprintw("Error toggling executable: %s (Press any key to continue)", strerror(errno));
readch();
}
}
void replace_home(char *str)
{
char *home = getenv("HOME");
if (home == NULL) {
if (!home) {
wpprintw("$HOME not defined");
return;
}
@ -1052,12 +1054,12 @@ int write_last_d(void)
strcpy(last_ddup, last_d);
char *last_d_dir = strrchr(last_ddup, '/');
if (last_d_dir != NULL) {
if (last_d_dir) {
*last_d_dir = '\0'; /* truncate string */
}
mkdir_p(last_ddup);
FILE *last_d_file = fopen(last_d, "w");
if (last_d_file == NULL) {
if (!last_d_file) {
wpprintw("Cannot open last directory file (Press any key to continue)");
return -1;
}
@ -1126,7 +1128,8 @@ void rename_file(void)
if (!input) {
return;
}
char *newfilename = estrdup(filename);
char newfilename[PATH_MAX];
strcpy(newfilename, filename);
/* remove basename of newfilename */
char *last_slash = strrchr(newfilename, '/');
*last_slash = '\0';
@ -1141,12 +1144,14 @@ void rename_file(void)
wpprintw("Renamed %s to %s", filename, newfilename);
}
free(input);
free(newfilename);
}
void goto_dir(void)
{
char *input = get_panel_string("Goto dir: ");
if (!input) {
return;
}
struct stat st;
if (lstat(input, &st)) {
wpprintw("lstat failed: %s (Press any key to continue)", strerror(errno));
@ -1157,16 +1162,21 @@ void goto_dir(void)
wpprintw("chdir failed: %s (Press any key to continue)", strerror(errno));
readch();
}
getcwd(cwd, PATH_MAX);
change_dir(cwd, 0, 0);
char new_cwd[PATH_MAX];
getcwd(new_cwd, PATH_MAX);
change_dir(new_cwd, 0, 0);
free(input);
}
void create_dir(void)
{
char *input = get_panel_string("New dir: ");
char *newfilename = memalloc(PATH_MAX);
if (!input) {
return;
}
char newfilename[PATH_MAX];
snprintf(newfilename, PATH_MAX, "%s/%s", cwd, input);
if (access(newfilename, F_OK) != 0) {
mkdir_p(newfilename);
change_dir(cwd, 0, 0);
@ -1175,12 +1185,14 @@ void create_dir(void)
wpprintw("Directory already exist");
}
free(input);
free(newfilename);
}
void create_file(void)
{
char *input = get_panel_string("New file: ");
if (!input) {
return;
}
FILE *f = fopen(input, "w+");
fclose(f);
change_dir(cwd, 0, 0);
@ -1192,16 +1204,15 @@ void delete_files(void)
{
if (marked->length) {
char *trash_dir = check_trash_dir();
if (trash_dir != NULL) {
if (trash_dir) {
for (int i = 0; i < marked->length; i++) {
char *new_path = memalloc(PATH_MAX);
char new_path[PATH_MAX];
snprintf(new_path, PATH_MAX, "%s/%s", trash_dir, marked->items[i].name);
if (rename(marked->items[i].path, new_path)) {
wpprintw("delete failed: %s", strerror(errno));
} else {
}
}
change_dir(cwd, 0, 0);
}
}
for (int i = 0; i < marked->length; i++) {
arraylist_remove(marked, 0);
}
@ -1253,6 +1264,44 @@ void yank_clipboard(void)
}
}
void view_file_attr(void)
{
bprintf("\033[2J");
move_cursor(1, 1);
pid_t pid = fork();
if (pid == 0) {
/* Child process */
execlp("stat", "stat", files->items[sel_file].name, NULL);
_exit(1); /* Exit if exec fails */
} else if (pid > 0) {
/* Parent process */
waitpid(pid, NULL, 0);
} else {
/* Fork failed */
wpprintw("fork failed: %s", strerror(errno));
}
readch();
}
void show_history(void)
{
bprintf("\033[2J");
move_cursor(1, 1);
char history_path[PATH_MAX];
strcpy(history_path, "~/.cache/ccc/history");
replace_home(history_path);
FILE *history_file = fopen(history_path, "r");
char buffer[PATH_MAX];
int row = 1;
while (fgets(buffer, sizeof(buffer), history_file) && row <= rows - 1) {
move_cursor(row++, 1);
bprintf(buffer);
}
fclose(history_file);
readch();
}
/*
* Print line to the panel
*/

View file

@ -13,6 +13,7 @@ enum files_colors {
FIF_COLOR = 35, /* FIFO */
DEF_COLOR = 37, /* Default */
EXE_COLOR = 32, /* Executable file */
MAR_COLOR = 36, /* Marked files */
};
/* Set width offset for windows:

29
file.c
View file

@ -23,8 +23,6 @@ void arraylist_free(ArrayList *list)
free(list->items[i].path);
if (list->items[i].stats != NULL)
free(list->items[i].stats);
if (list->items[i].icon != NULL)
free(list->items[i].icon);
}
free(list->items);
@ -72,7 +70,8 @@ void arraylist_remove(ArrayList *list, long index)
*/
void arraylist_add(ArrayList *list, char *name, char *path, char *stats, int type, char *icon, int color, int marked, int force)
{
file new_file = { name, path, type, stats, icon, color };
file new_file = { name, path, type, stats, color };
strcpy(new_file.icon, icon);
if (list->capacity != list->length) {
if (marked) {
@ -106,29 +105,27 @@ void arraylist_add(ArrayList *list, char *name, char *path, char *stats, int typ
*/
char *get_line(ArrayList *list, long index, int detail, int icons)
{
file file = list->items[index];
file f = list->items[index];
size_t name_len = strlen(file.name);
size_t length;
size_t length = strlen(f.name) + 1;
if (detail) {
length = name_len + strlen(file.stats) + 7; /* 4 for icon, 2 for space and 1 for null */
} else {
length = name_len + 6; /* 4 for icon, 1 for space and 1 for null */
length += strlen(f.stats) + 1; /* 1 for space */
}
if (icons) {
length += 5; /* 4 for icon, 1 for space */
}
char *line = memalloc(length);
line[0] = '\0';
if (detail) {
strcat(line, file.stats);
strcat(line, f.stats);
strcat(line, " ");
}
if (icons) {
char *tmp = memalloc(10);
snprintf(tmp, 10, "%s ", file.icon);
strcat(line, tmp);
free(tmp);
strcat(line, f.icon);
strcat(line, " ");
}
strcat(line, file.name);
strcat(line, f.name);
line[length] = '\0';
return line;
}

6
file.h
View file

@ -13,16 +13,16 @@ enum ftypes {
FIF
};
typedef struct file {
typedef struct {
char *name; /* basename */
char *path; /* absolute path */
int type;
char *stats;
char *icon;
int color;
char icon[5];
} file;
typedef struct ArrayList {
typedef struct {
size_t length;
size_t capacity;
file *items;