Merge pull request #20 from piotr-marendowski/feature

Feature
This commit is contained in:
Piotr Marendowski 2024-04-02 18:58:36 +00:00 committed by GitHub
commit 7a8f8ff881
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 570 additions and 190 deletions

View file

@ -14,9 +14,9 @@ MANDIR = $(PREFIX)/share/man/man1
LDFLAGS = $(shell pkg-config --libs ncursesw) LDFLAGS = $(shell pkg-config --libs ncursesw)
CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 $(shell pkg-config --cflags ncursesw) CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 $(shell pkg-config --cflags ncursesw)
SRC = ccc.c util.c file.c SRC = ccc.c util.c file.c icons.c
$(TARGET): $(SRC) $(TARGET): $(SRC) $(CONF)
$(CC) $(SRC) -o $@ $(CFLAGS) $(LDFLAGS) $(CC) $(SRC) -o $@ $(CFLAGS) $(LDFLAGS)
dist: dist:

View file

@ -13,6 +13,7 @@ Consider this project incomplete and WIP!
| Standard movement | X | | | Standard movement | X | |
| Advanced movement (jumps) | X | | | Advanced movement (jumps) | X | |
| File details | X | | | File details | X | |
| File icons! | X | |
| Searching for files | | | | Searching for files | | |
| Sorting | | | | Sorting | | |
| Marking and marking operations | | | | Marking and marking operations | | |
@ -32,9 +33,10 @@ Consider this project incomplete and WIP!
### Dependencies ### Dependencies
- gcc - gcc
- ncurses - ncursesw
- make - make
- pkg-config - pkg-config
- Any [Nerd Font](https://www.nerdfonts.com/) for file icons (optional, but turned on by default)
### Building ### Building

325
ccc.c
View file

@ -10,25 +10,31 @@
#include <ftw.h> #include <ftw.h>
#include <time.h> #include <time.h>
#include <ncurses.h> #include <ncurses.h>
#include <locale.h>
#include <wchar.h>
#include "file.h"
#include "util.h"
#include "config.h" #include "config.h"
#include "file.h"
#include "icons.h"
#include "util.h"
/* functions' definitions */ /* functions' definitions */
void show_help(); void show_help();
void start_ccc(); void start_ccc();
char *check_trash_dir();
void change_dir(const char *buf, int selection, int ftype); void change_dir(const char *buf, int selection, int ftype);
int mkdir_p(const char *destdir); void mkdir_p(const char *destdir);
void populate_files(const char *path, int ftype); void populate_files(const char *path, int ftype);
int get_directory_size(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf); int get_directory_size(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf);
void add_file_stat(char *filepath, int ftype); void add_file_stat(char *filename, char *path, int ftype);
char *get_file_mode(mode_t mode); char *get_file_mode(mode_t mode);
void highlight_current_line(); void highlight_current_line();
void show_file_content(); void show_file_content();
void edit_file(); void edit_file();
void toggle_executable(); void toggle_executable();
int write_last_d(); int write_last_d();
void create_file();
void delete_files();
void wpprintw(const char *fmt, ...); void wpprintw(const char *fmt, ...);
void init_windows(); void init_windows();
void draw_border_title(WINDOW *window, bool active); void draw_border_title(WINDOW *window, bool active);
@ -36,9 +42,12 @@ void draw_border_title(WINDOW *window, bool active);
/* global variables */ /* global variables */
unsigned int focus = 0; unsigned int focus = 0;
long current_selection = 0; long current_selection = 0;
bool to_open_file = false;
bool dirs_size = DIRS_SIZE; bool dirs_size = DIRS_SIZE;
bool show_hidden = SHOW_HIDDEN; bool show_hidden = SHOW_HIDDEN;
bool file_details = SHOW_DETAILS; bool file_details = SHOW_DETAILS;
bool show_icons = SHOW_ICONS;
char *argv_cp;
char *cwd; char *cwd;
char *p_cwd; /* previous cwd */ char *p_cwd; /* previous cwd */
int half_width; int half_width;
@ -58,10 +67,24 @@ int main(int argc, char** argv)
die("Usage: ccc filename"); die("Usage: ccc filename");
if (argc == 2) { if (argc == 2) {
struct stat st; struct stat st;
if (lstat(argv[1], &st) != 0) { if (lstat(argv[1], &st)) {
perror("ccc"); perror("ccc");
die("Error from lstat"); die("Error from lstat");
} }
/* chdir to directory from argument */
if (S_ISDIR(st.st_mode) && chdir(argv[1])) {
perror("ccc");
die("Error from chdir");
} else if (S_ISREG(st.st_mode)) {
argv_cp = estrdup(argv[1]);
char *last_slash = strrchr(argv_cp, '/');
*last_slash = '\0';
if (chdir(argv[1])) {
perror("ccc");
die("Error from chdir");
}
to_open_file = true;
}
} }
/* check if it is interactive shell */ /* check if it is interactive shell */
@ -71,6 +94,7 @@ int main(int argc, char** argv)
/* initialize screen, don't print special chars, /* initialize screen, don't print special chars,
* make ctrl + c work, don't show cursor * make ctrl + c work, don't show cursor
* enable arrow keys */ * enable arrow keys */
setlocale(LC_ALL, "");
initscr(); initscr();
noecho(); noecho();
cbreak(); cbreak();
@ -99,16 +123,18 @@ int main(int argc, char** argv)
/* init files and marked arrays */ /* init files and marked arrays */
files = arraylist_init(100); files = arraylist_init(100);
marked = arraylist_init(100); marked = arraylist_init(100);
hashtable_init();
cwd = memalloc(PATH_MAX * sizeof(char)); cwd = memalloc(PATH_MAX * sizeof(char));
if (argc == 2) { getcwd(cwd, PATH_MAX);
strcpy(cwd, argv[1]);
} else {
getcwd(cwd, PATH_MAX);
}
p_cwd = memalloc(PATH_MAX * sizeof(char)); p_cwd = memalloc(PATH_MAX * sizeof(char));
start_ccc(); start_ccc();
populate_files(cwd, 0); populate_files(cwd, 0);
if (to_open_file) {
current_selection = arraylist_search(files, argv_cp, true);
highlight_current_line();
}
int ch, ch2; int ch, ch2;
while (1) { while (1) {
@ -117,7 +143,6 @@ int main(int argc, char** argv)
die("ccc: Terminal size needs to be at least 80x24\n"); die("ccc: Terminal size needs to be at least 80x24\n");
} }
ch = getch(); ch = getch();
/* printf("%d ",ch); */
switch (ch) { switch (ch) {
/* quit */ /* quit */
case 'q': case 'q':
@ -134,8 +159,8 @@ int main(int argc, char** argv)
break; break;
/* go back by backspace or h or left arrow */ /* go back by backspace or h or left arrow */
case BACKSPACE: case BACKSPACE: /* PASSTHROUGH */
case LEFT: case LEFT: /* PASSTHROUGH */
case 'h':; case 'h':;
/* get parent directory */ /* get parent directory */
strcpy(p_cwd, cwd); strcpy(p_cwd, cwd);
@ -151,9 +176,9 @@ int main(int argc, char** argv)
break; break;
/* enter directory/open a file using enter or l or right arrow */ /* enter directory/open a file using enter or l or right arrow */
case ENTER: case ENTER: /* PASSTHROUGH */
case RIGHT: case RIGHT: /* PASSTHROUGH */
case 'l':; case 'l':
strcpy(p_cwd, cwd); strcpy(p_cwd, cwd);
file c_file = files->items[current_selection]; file c_file = files->items[current_selection];
/* check if it is directory or a regular file */ /* check if it is directory or a regular file */
@ -176,7 +201,7 @@ int main(int argc, char** argv)
break; break;
/* go up by k or up arrow */ /* go up by k or up arrow */
case UP: case UP: /* PASSTHROUGH */
case 'k': case 'k':
if (current_selection > 0) if (current_selection > 0)
current_selection--; current_selection--;
@ -195,7 +220,7 @@ int main(int argc, char** argv)
break; break;
/* go down by j or down arrow */ /* go down by j or down arrow */
case DOWN: case DOWN: /* PASSTHROUGH */
case 'j': case 'j':
if (current_selection < (files->length - 1)) if (current_selection < (files->length - 1))
current_selection++; current_selection++;
@ -234,25 +259,9 @@ int main(int argc, char** argv)
/* go to the trash dir */ /* go to the trash dir */
case 't':; case 't':;
#ifdef TRASH_DIR char *trash_dir = check_trash_dir();
char *trash_dir = TRASH_DIR; if (trash_dir != NULL)
#else
char *trash_dir = getenv("CCC_TRASH");
#endif
if (trash_dir == NULL) {
wpprintw("$CCC_TRASH is not defined");
} else {
if (access(trash_dir, F_OK) != 0) {
/* create the directory with 755 perm if it doesn't exit */
if (mkdir_p(trash_dir) == -1) {
switch (errno) {
case EACCES:
wpprintw("Parent directory does not allow write permission or one of directories does not allow search access");
}
}
}
change_dir(trash_dir, 0, 0); change_dir(trash_dir, 0, 0);
}
break; break;
/* show directories' sizes */ /* show directories' sizes */
@ -283,13 +292,22 @@ int main(int argc, char** argv)
change_dir(cwd, 0, 0); change_dir(cwd, 0, 0);
break; break;
case 'w':
show_icons = !show_icons;
change_dir(cwd, 0, 0);
break;
case 'f':
create_file();
break;
case 'X': case 'X':
toggle_executable(); toggle_executable();
break; break;
/* mark one file */ /* mark one file */
case SPACE: case SPACE:
add_file_stat(files->items[current_selection].path, 1); add_file_stat(files->items[current_selection].name, files->items[current_selection].path, 1);
highlight_current_line(); highlight_current_line();
break; break;
@ -300,35 +318,33 @@ int main(int argc, char** argv)
/* mark actions: */ /* mark actions: */
/* delete */ /* delete */
case 'd':; case 'd':
if (marked->length) { delete_files();
;
}
break; break;
/* move */ /* move */
case 'm':; case 'm':
if (marked->length) { if (marked->length) {
; ;
} }
break; break;
/* copy */ /* copy */
case 'c':; case 'c':
if (marked->length) { if (marked->length) {
; ;
} }
break; break;
/* symbolic link */ /* symbolic link */
case 's':; case 's':
if (marked->length) { if (marked->length) {
; ;
} }
break; break;
/* bulk rename */ /* bulk rename */
case 'b':; case 'b':
if (marked->length) { if (marked->length) {
; ;
} }
@ -352,6 +368,7 @@ int main(int argc, char** argv)
break; break;
} }
} }
free(argv_cp);
arraylist_free(files); arraylist_free(files);
arraylist_free(marked); arraylist_free(marked);
endwin(); endwin();
@ -374,6 +391,47 @@ void start_ccc()
init_windows(); init_windows();
} }
/*
* Checks if the trash directory is set and returns it
*/
char *check_trash_dir()
{
char *path = memalloc(PATH_MAX * sizeof(char));
char *trash_dir;
#ifdef TRASH_DIR
trash_dir = TRASH_DIR;
#endif
/* check if there is trash_dir */
if (trash_dir == NULL) {
wpprintw("No trash directory defined");
return NULL;
} else {
/* if trash_dir has ~ then make it $HOME */
/* use path as trash_dir */
if (trash_dir[0] == '~') {
char *home = getenv("HOME");
if (home == NULL) {
wpprintw("$HOME is not defined, can't read the trash directory");
return NULL;
}
/* replace ~ with home */
snprintf(path, PATH_MAX, "%s%s", home, trash_dir + 1);
}
else {
strcpy(path, trash_dir);
}
/* if has access to trash_dir */
if (access(path, F_OK) != 0) {
/* create the directory with 755 permissions if it doesn't exist */
mkdir_p(path);
}
}
return path;
}
/* /*
* Change directory in window with selection * Change directory in window with selection
*/ */
@ -381,16 +439,15 @@ void change_dir(const char *buf, int selection, int ftype)
{ {
char *buf_dup; char *buf_dup;
if (buf == p_cwd) { if (buf == p_cwd) {
buf_dup = strdup(p_cwd); buf_dup = estrdup(p_cwd);
if (buf_dup == NULL) {
return;
}
} else { } else {
buf_dup = (char *) buf; buf_dup = (char *) buf;
} }
strcpy(cwd, buf_dup); strcpy(cwd, buf_dup);
arraylist_free(files); if (ftype != 2) {
files = arraylist_init(100); arraylist_free(files);
files = arraylist_init(100);
}
current_selection = selection; current_selection = selection;
populate_files(cwd, ftype); populate_files(cwd, ftype);
} }
@ -399,7 +456,7 @@ void change_dir(const char *buf, int selection, int ftype)
* Recursively create directory by creating each subdirectory * Recursively create directory by creating each subdirectory
* like mkdir -p * like mkdir -p
*/ */
int mkdir_p(const char *destdir) void mkdir_p(const char *destdir)
{ {
char *path = memalloc(PATH_MAX * sizeof(char)); char *path = memalloc(PATH_MAX * sizeof(char));
char dir_path[PATH_MAX] = ""; char dir_path[PATH_MAX] = "";
@ -408,7 +465,7 @@ int mkdir_p(const char *destdir)
char *home = getenv("HOME"); char *home = getenv("HOME");
if (home == NULL) { if (home == NULL) {
wpprintw("$HOME is not defined"); wpprintw("$HOME is not defined");
return -1; return;
} }
/* replace ~ with home */ /* replace ~ with home */
snprintf(path, PATH_MAX, "%s%s", home, destdir + 1); snprintf(path, PATH_MAX, "%s%s", home, destdir + 1);
@ -433,21 +490,20 @@ int mkdir_p(const char *destdir)
continue; continue;
} }
perror("ccc"); wpprintw("mkdir failed: %s\n", strerror(errno));
free(path); free(path);
return -1; return;
} }
token = strtok(NULL, "/"); token = strtok(NULL, "/");
} }
free(path); free(path);
return 0; return;
} }
/* /*
* Read the provided directory and add all files in directory to linked list * Read the provided directory and add all files in directory to linked list
* ftype: normal files = 0, marked = 1, marking ALL = 2 * ftype: normal files = 0, marked = 1, marking ALL = 2
* ep->d_name -> filename
*/ */
void populate_files(const char *path, int ftype) void populate_files(const char *path, int ftype)
{ {
@ -459,22 +515,22 @@ void populate_files(const char *path, int ftype)
wclear(directory_content); wclear(directory_content);
while ((ep = readdir(dp)) != NULL) { while ((ep = readdir(dp)) != NULL) {
char *path = memalloc(PATH_MAX * sizeof(char));
char *filename = memalloc(PATH_MAX * sizeof(char)); char *filename = memalloc(PATH_MAX * sizeof(char));
/* make filename be basename of selected item just to pass check */ /* copy filename */
filename[0] = '\0'; strcpy(filename, ep->d_name);
strcat(filename, ep->d_name);
/* use strncmp to filter out dotfiles */ /* use strncmp to filter out dotfiles */
if ((!show_hidden && strncmp(filename, ".", 1) && strncmp(filename, "..", 2)) || (show_hidden && strcmp(filename, ".") && strcmp(filename, ".."))) { if ((!show_hidden && strncmp(filename, ".", 1) && strncmp(filename, "..", 2)) || (show_hidden && strcmp(filename, ".") && strcmp(filename, ".."))) {
/* construct full file path */ /* construct full file path */
filename[0] = '\0'; strcpy(path, cwd);
strcat(filename, cwd); strcat(path, "/");
strcat(filename, "/"); strcat(path, filename); /* add filename */
strcat(filename, ep->d_name); /* add filename */
add_file_stat(filename, ftype); add_file_stat(filename, path, ftype);
} }
free(filename); free(filename);
free(path);
} }
closedir(dp); closedir(dp);
wrefresh(directory_content); wrefresh(directory_content);
@ -493,40 +549,62 @@ int get_directory_size(const char *fpath, const struct stat *sb, int typeflag, s
/* /*
* Get file's last modified time, size, type * Get file's last modified time, size, type
* Add that file into list * Add that file into list
* ftype: normal file = 0, normal marked = 1, marking ALL = 2 * ftype: normal file = 0, normal marked = 1, marked ALL = 2
*/ */
void add_file_stat(char *filepath, int ftype) void add_file_stat(char *filename, char *path, int ftype)
{ {
struct stat file_stat; struct stat file_stat;
if (stat(filepath, &file_stat) == -1) { if (stat(path, &file_stat) == -1) {
/* can't be triggered? */ /* can't be triggered? */
if (errno == EACCES) if (errno == EACCES)
arraylist_add(files, filepath, "", "", 8, false, false); arraylist_add(files, filename, path, NULL, NULL, NULL, 8, false, false);
} }
/* get file type and color, 4 chars for the type */ /* get file type and color, 4 chars for the type and icon */
char *type = memalloc(4 * sizeof(char)); size_t type_size = 4 * sizeof(char);
size_t icon_size = 2 * sizeof(wchar_t);
char *type = memalloc(type_size);
wchar_t *icon_str = memalloc(icon_size);
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) {
ext += 1;
}
/* add file extension */
icon *ext_icon = hashtable_search(ext != NULL ? ext : filename);
if (ext_icon == NULL)
wcsncpy(icon_str, L"", 2);
else
wcsncpy(icon_str, ext_icon->icon, 2);
int color; int color;
if (S_ISDIR(file_stat.st_mode)) { if (S_ISDIR(file_stat.st_mode)) {
strcpy(type, "DIR"); /* directory type */ strncpy(type, "DIR", 4); /* directory type */
color = 5; /* blue color */ color = 5; /* blue color */
wcsncpy(icon_str, L"󰉋", 2);
} else if (S_ISREG(file_stat.st_mode)) { } else if (S_ISREG(file_stat.st_mode)) {
strcpy(type, "REG"); /* regular file */ strncpy(type, "REG", 4); /* regular file */
color = 8; /* white color */ color = 8; /* white color */
} else if (S_ISLNK(file_stat.st_mode)) { } else if (S_ISLNK(file_stat.st_mode)) {
strcpy(type, "LNK"); /* symbolic link */ strncpy(type, "LNK", 4); /* symbolic link */
color = 3; /* green color */ color = 3; /* green color */
} else if (S_ISCHR(file_stat.st_mode)) { } else if (S_ISCHR(file_stat.st_mode)) {
strcpy(type, "CHR"); /* character device */ strncpy(type, "CHR", 4); /* character device */
color = 8; /* white color */ color = 8; /* white color */
} else if (S_ISSOCK(file_stat.st_mode)) { } else if (S_ISSOCK(file_stat.st_mode)) {
strcpy(type, "SOC"); /* socket */ strncpy(type, "SOC", 4); /* socket */
color = 8; /* white color */ color = 8; /* white color */
} else if (S_ISBLK(file_stat.st_mode)) { } else if (S_ISBLK(file_stat.st_mode)) {
strcpy(type, "BLK"); /* block device */ strncpy(type, "BLK", 4); /* block device */
color = 4; /* yellow color */ color = 4; /* yellow color */
} else if (S_ISFIFO(file_stat.st_mode)) { } else if (S_ISFIFO(file_stat.st_mode)) {
strcpy(type, "FIF"); /* FIFO */ strncpy(type, "FIF", 4); /* FIFO */
color = 8; /* white color */ color = 8; /* white color */
} else { } else {
color = 8; /* white color */ color = 8; /* white color */
@ -536,16 +614,17 @@ void add_file_stat(char *filepath, int ftype)
if (ftype == 1 || ftype == 2) { if (ftype == 1 || ftype == 2) {
/* force if user is marking all files */ /* force if user is marking all files */
bool force = ftype == 2 ? true : false; bool force = ftype == 2 ? true : false;
arraylist_add(marked, filepath, NULL, type, 8, true, force); arraylist_add(marked, filename, path, NULL, type, icon_str, 8, true, force);
/* free type and return without allocating more stuff */ /* free type and return without allocating more stuff */
free(type); free(type);
return; return;
} }
/* get last modified time */ /* get last modified time */
char *time = memalloc(20 * sizeof(char)); size_t time_size = 17 * sizeof(char);
char *time = memalloc(time_size);
/* format last modified time to a string */ /* format last modified time to a string */
strftime(time, 20, "%Y-%m-%d %H:%M", localtime(&file_stat.st_mtime)); strftime(time, time_size, "%Y-%m-%d %H:%M", localtime(&file_stat.st_mtime));
/* get file size */ /* get file size */
double bytes = file_stat.st_size; double bytes = file_stat.st_size;
@ -555,35 +634,44 @@ void add_file_stat(char *filepath, int ftype)
if (S_ISDIR(file_stat.st_mode)) { if (S_ISDIR(file_stat.st_mode)) {
/* at most 15 fd opened */ /* at most 15 fd opened */
total_dir_size = 0; total_dir_size = 0;
nftw(filepath, &get_directory_size, 15, FTW_PHYS); nftw(path, &get_directory_size, 15, FTW_PHYS);
bytes = total_dir_size; bytes = total_dir_size;
} }
} }
/* max 25 chars due to long, space, suffix and null */ /* 4 before decimal + 1 dot + DECIMAL_PLACES (after decimal) +
char *size = memalloc(25 * sizeof(char)); unit length (1 for K, 3 for KiB, taking units[1] as B never changes) + 1 space + 1 null */
int size_size = 4 + 1 + DECIMAL_PLACES + strlen(units[1]) + 1 + 1;
char *size = memalloc(size_size * sizeof(char));
int unit = 0; int unit = 0;
const char* units[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
while (bytes > 1024) { while (bytes > 1024) {
bytes /= 1024; bytes /= 1024;
unit++; unit++;
} }
/* display sizes */ /* display sizes and check if there are decimal places */
sprintf(size, "%.3g%s", bytes, units[unit]); if (bytes == (unsigned int) bytes) {
sprintf(size, "%d%s", (unsigned int) bytes, units[unit]);
} else {
sprintf(size, "%.*f%s", DECIMAL_PLACES, bytes, units[unit]);
}
/* get file mode string */ /* get file mode string */
char *mode_str = get_file_mode(file_stat.st_mode); char *mode_str = get_file_mode(file_stat.st_mode);
if (mode_str[0] == '-' && (mode_str[3] == 'x' || mode_str[6] == 'x' || mode_str[9] == 'x')) {
}
char *total_stat = memalloc(56 * sizeof(char)); /* mode_str(11) + time(17) + size_size + 2 spaces + 1 null */
snprintf(total_stat, 56, "%s %s %-8s", mode_str, time, size); size_t stat_size = 11 * sizeof(char) + time_size + size_size + 3 * sizeof(char);
total_stat[strlen(total_stat)] = '\0'; char *total_stat = memalloc(stat_size);
snprintf(total_stat, stat_size, "%s %s %-*s", mode_str, time, size_size, size);
arraylist_add(files, filepath, total_stat, type, color, false, false); arraylist_add(files, filename, path, total_stat, type, icon_str, color, false, false);
free(time); free(time);
free(size); free(size);
free(total_stat); free(total_stat);
free(type); free(type);
free(mode_str); free(mode_str);
free(icon_str);
} }
/* /*
@ -592,8 +680,8 @@ void add_file_stat(char *filepath, int ftype)
*/ */
char *get_file_mode(mode_t mode) char *get_file_mode(mode_t mode)
{ {
char *mode_str = memalloc(sizeof(char) * 11); char *mode_str = memalloc(11 * sizeof(char));
mode_str[0] = S_ISDIR(mode) ? 'd' : '-'; // Check if it's a directory mode_str[0] = S_ISDIR(mode) ? 'd' : '-'; /* Check if it's a directory */
mode_str[1] = (mode & S_IRUSR) ? 'r' : '-'; mode_str[1] = (mode & S_IRUSR) ? 'r' : '-';
mode_str[2] = (mode & S_IWUSR) ? 'w' : '-'; mode_str[2] = (mode & S_IWUSR) ? 'w' : '-';
mode_str[3] = (mode & S_IXUSR) ? 'x' : '-'; mode_str[3] = (mode & S_IXUSR) ? 'x' : '-';
@ -603,7 +691,7 @@ char *get_file_mode(mode_t mode)
mode_str[7] = (mode & S_IROTH) ? 'r' : '-'; mode_str[7] = (mode & S_IROTH) ? 'r' : '-';
mode_str[8] = (mode & S_IWOTH) ? 'w' : '-'; mode_str[8] = (mode & S_IWOTH) ? 'w' : '-';
mode_str[9] = (mode & S_IXOTH) ? 'x' : '-'; mode_str[9] = (mode & S_IXOTH) ? 'x' : '-';
mode_str[10] = '\0'; // Null terminator mode_str[10] = '\0';
return mode_str; return mode_str;
} }
@ -660,10 +748,10 @@ void highlight_current_line()
} }
} }
/* print the actual filename and stats */ /* print the actual filename and stats */
char *line = get_line(files, i, file_details); char *line = get_line(files, i, file_details, show_icons);
int color = files->items[i].color; int color = files->items[i].color;
/* check is file marked for action */ /* check is file marked for action */
bool is_marked = arraylist_includes(marked, files->items[i].path); bool is_marked = arraylist_search(marked, files->items[i].path, false) != -1;
if (is_marked) { if (is_marked) {
/* show file is selected */ /* show file is selected */
wattron(directory_content, COLOR_PAIR(7)); wattron(directory_content, COLOR_PAIR(7));
@ -694,10 +782,10 @@ void highlight_current_line()
#if DRAW_PREVIEW #if DRAW_PREVIEW
show_file_content(); show_file_content();
#endif #endif
wrefresh(preview_content);
#if DRAW_BORDERS #if DRAW_BORDERS
draw_border_title(preview_border, true); draw_border_title(preview_border, true);
#endif #endif
wrefresh(preview_content);
} }
/* /*
@ -705,15 +793,16 @@ void highlight_current_line()
*/ */
void show_file_content() void show_file_content()
{ {
wclear(preview_content);
file current_file = files->items[current_selection]; file current_file = files->items[current_selection];
if (strncmp(current_file.type, "DIR", 3) == 0) if (strncmp(current_file.type, "DIR", 3) == 0)
return; return;
wclear(preview_content);
FILE *file = fopen(current_file.path, "r"); FILE *file = fopen(current_file.path, "r");
if (file == NULL) { if (file == NULL) {
mvwprintw(preview_content, 0, 0, "Unable to read %s", current_file.path); mvwprintw(preview_content, 0, 0, "Unable to read %s", current_file.name);
return; return;
} }
@ -825,6 +914,42 @@ int write_last_d()
return 0; return 0;
} }
void create_file()
{
echo();
wpprintw("New file: ");
char input[PATH_MAX];
/* get string at y=0, x=10 */
mvwgetstr(panel, 0, 10, input);
FILE *f = fopen(input, "w+");
fclose(f);
wpprintw("Created %s", input);
change_dir(cwd, 0, 0);
noecho();
}
void delete_files()
{
if (marked->length) {
char *trash_dir = check_trash_dir();
if (trash_dir != NULL) {
for (int i = 0; i < marked->length; i++) {
char *new_path = memalloc(PATH_MAX * sizeof(char));
strcpy(new_path, trash_dir);
strcat(new_path, "/");
strcat(new_path, marked->items[i].name);
if (rename(marked->items[i].path, new_path)) {
wpprintw("delete failed: %s\n", strerror(errno));
} else {
change_dir(cwd, 0, 0);
}
}
} else {
wpprintw("TODO: implement hard delete");
}
}
}
/* /*
* Print line to the panel * Print line to the panel
*/ */

View file

@ -1,17 +1,13 @@
/* Settings */ /* Settings */
#define PH 1 /* panel height */ #define PH 1 /* panel height */
#define JUMP_NUM 14 /* how long ctrl + u/d jump are */ #define JUMP_NUM 14 /* how long ctrl + u/d jump are */
#define PATH_MAX 4096 #define PATH_MAX 4096 /* max length of the path */
#define DECIMAL_PLACES 1 /* how many decimal places show size with */
/* Calculate directories' sizes RECURSIVELY upon entering? */ /* Size units */
#define DIRS_SIZE false static const char* units[] = {"B", "K", "M", "G", "T", "P"};
#define DRAW_BORDERS true /* Draw borders around windows? */ /* Set width offset for windows:
#define DRAW_PREVIEW true /* Draw file preview? */
#define SHOW_HIDDEN true /* show hidden files/dotfiles in preview */
#define SHOW_DETAILS true /* show file details */
/* set width offset for windows:
+-------------%-------------+ +-------------%-------------+
| % | | % |
| files % preview | | files % preview |
@ -25,12 +21,28 @@ In COLS:
-15 will make preview bigger */ -15 will make preview bigger */
#define WINDOW_OFFSET 0 #define WINDOW_OFFSET 0
/* Options */
#define DRAW_BORDERS true /* draw borders around windows */
#define DRAW_PREVIEW true /* draw file preview */
#define SHOW_HIDDEN true /* show hidden files/dotfiles at startup */
#define SHOW_DETAILS true /* show file details at startup */
#define SHOW_ICONS true /* show file icons at startup */
/* Calculate directories' sizes RECURSIVELY upon entering
`A` keybind at the startup
**VERY EXPENSIVE**, **CAN TAKE UP TO A MINUTE IN ROOT** */
#define DIRS_SIZE false
/* Default text editor */ /* Default text editor */
#define EDITOR "nvim" #define EDITOR "nvim"
/* File location to write last directory */ /* File location to write last directory */
#define LAST_D "~/.cache/ccc/.ccc_d" #define LAST_D "~/.cache/ccc/.ccc_d"
/* Will create this directory if doesn't exist! */
#define TRASH_DIR "~/.cache/ccc/trash/"
/* Keybindings */ /* Keybindings */
#define CTRLD 0x04 #define CTRLD 0x04
#define ENTER 0xA #define ENTER 0xA

142
file.c
View file

@ -2,21 +2,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <libgen.h> #include <libgen.h>
#include <wchar.h>
#include "util.h" #include "util.h"
#include "file.h"
typedef struct file {
char *path;
char *stats;
char *type;
int color;
} file;
typedef struct ArrayList {
size_t length;
size_t capacity;
file *items;
} ArrayList;
ArrayList *arraylist_init(size_t capacity) ArrayList *arraylist_init(size_t capacity)
{ {
@ -24,87 +13,96 @@ ArrayList *arraylist_init(size_t capacity)
list->length = 0; list->length = 0;
list->capacity = capacity; list->capacity = capacity;
list->items = memalloc(capacity * sizeof(file)); list->items = memalloc(capacity * sizeof(file));
return list; return list;
} }
void arraylist_free(ArrayList *list) void arraylist_free(ArrayList *list)
{ {
for (size_t i = 0; i < list->length; i++) { for (size_t i = 0; i < list->length; i++) {
if (list->items[i].type != NULL) if (list->items[i].name != NULL)
free(list->items[i].type); free(list->items[i].name);
if (list->items[i].path != NULL) if (list->items[i].path != NULL)
free(list->items[i].path); free(list->items[i].path);
if (list->items[i].type != NULL)
free(list->items[i].type);
if (list->items[i].stats != NULL) if (list->items[i].stats != NULL)
free(list->items[i].stats); free(list->items[i].stats);
if (list->items[i].icon != NULL)
free(list->items[i].icon);
} }
free(list->items); free(list->items);
list->length = 0; list->length = 0;
} }
bool arraylist_includes(ArrayList *list, char *path) /*
* Check if the file is in the arraylist
* Treat filepath as base name if bname is true
*/
long arraylist_search(ArrayList *list, char *filepath, bool bname)
{ {
for (int i = 0; i < list->length; i++) { for (long i = 0; i < list->length; i++) {
if (strcmp(list->items[i].path, path) == 0) { if (!bname && strcmp(list->items[i].path, filepath) == 0) {
return true; return i;
}
if (bname) {
if (strcmp(list->items[i].name, filepath) == 0) {
return i;
}
} }
} }
return false; return -1;
} }
void arraylist_remove(ArrayList *list, long index) void arraylist_remove(ArrayList *list, long index)
{ {
if (index >= list->length) { if (index >= list->length)
return; return;
}
free(list->items[index].name);
free(list->items[index].path); free(list->items[index].path);
free(list->items[index].stats);
free(list->items[index].type); free(list->items[index].type);
free(list->items[index].stats);
free(list->items[index].icon);
for (long i = index; i < list->length - 1; i++) { for (long i = index; i < list->length - 1; i++)
list->items[i] = list->items[i + 1]; list->items[i] = list->items[i + 1];
}
list->length--; list->length--;
} }
/* /*
* force will not remove duplicate marked files, instead it just skip adding * Force will not remove duplicate marked files, instead it just skip adding
*/ */
void arraylist_add(ArrayList *list, char *filepath, char *stats, char *type, int color, bool marked, bool force) void arraylist_add(ArrayList *list, char *name, char *path, char *stats, char *type, wchar_t *icon, int color, bool marked, bool force)
{ {
char *filepath_cp = NULL; char *name_cp = NULL;
char *stats_cp = NULL; char *path_cp = NULL;
char *type_cp = NULL; char *type_cp = NULL;
if (filepath != NULL) { char *stats_cp = NULL;
filepath_cp = strdup(filepath); wchar_t *icon_cp = NULL;
if (filepath_cp == NULL) {
perror("ccc");
}
}
if (stats != NULL) {
stats_cp = strdup(stats);
if (stats_cp == NULL) {
perror("ccc");
}
}
if (type != NULL) {
type_cp = strdup(type);
if (type_cp == NULL) {
perror("ccc");
}
}
/* path, stats, type, color */ if (name != NULL)
file new_file = { filepath_cp, stats_cp, type_cp, color }; name_cp = estrdup(name);
if (path != NULL)
path_cp = estrdup(path);
if (type != NULL)
type_cp = estrdup(type);
if (stats != NULL)
stats_cp = estrdup(stats);
if (icon != NULL)
icon_cp = ewcsdup(icon);
/* name, path, stats, type, icon, color */
file new_file = { name_cp, path_cp, type_cp, stats_cp, icon_cp, color };
if (list->capacity != list->length) { if (list->capacity != list->length) {
if (marked) { if (marked) {
for (int i = 0; i < list->length; i++) { for (int i = 0; i < list->length; i++) {
if (strcmp(list->items[i].path, new_file.path) == 0) { if (strcmp(list->items[i].path, new_file.path) == 0) {
if (!force) { if (!force)
arraylist_remove(list, i); arraylist_remove(list, i);
}
return; return;
} }
} }
@ -116,9 +114,10 @@ void arraylist_add(ArrayList *list, char *filepath, char *stats, char *type, int
file *old_items = list->items; file *old_items = list->items;
list->capacity = new_cap; list->capacity = new_cap;
list->items = new_items; list->items = new_items;
for (int i = 0; i < list->length; i++) {
for (int i = 0; i < list->length; i++)
new_items[i] = old_items[i]; new_items[i] = old_items[i];
}
free(old_items); free(old_items);
list->items[list->length] = new_file; list->items[list->length] = new_file;
} }
@ -128,31 +127,36 @@ void arraylist_add(ArrayList *list, char *filepath, char *stats, char *type, int
/* /*
* Construct a formatted line for display * Construct a formatted line for display
*/ */
char *get_line(ArrayList *list, long index, bool detail) char *get_line(ArrayList *list, long index, bool detail, bool icons)
{ {
file file = list->items[index]; file file = list->items[index];
char *name = strdup(file.path); char *name = estrdup(file.name);
wchar_t *icon = ewcsdup(file.icon);
size_t name_len = strlen(name);
char *stats = NULL; char *stats = NULL;
size_t length; size_t length;
if (detail) { if (detail) {
stats = strdup(file.stats); stats = estrdup(file.stats);
length = strlen(name) + strlen(stats) + 2; /* one for space and one for null */ length = name_len + strlen(stats) + 7; /* 4 for icon, 2 for space and 1 for null */
if (stats == NULL) {
perror("ccc");
}
} else { } else {
length = strlen(name) + 2; /* one for space and one for null */ length = name_len + 6; /* 4 for icon, 1 for space and 1 for null */
} }
char *line = memalloc(length * sizeof(char)); char *line = memalloc(length * sizeof(char));
line[0] = '\0';
name = basename(name);
if (name == NULL)
perror("ccc");
if (detail) { if (detail) {
snprintf(line, length, "%s %s", stats, name); strcat(line, stats);
} else { strcat(line, " ");
snprintf(line, length, "%s", name);
} }
if (icons) {
char *tmp = memalloc(8 * sizeof(char));
snprintf(tmp, 8, "%ls", icon);
strcat(line, tmp);
strcat(line, " ");
free(tmp);
}
strcat(line, name);
return line; return line;
} }

12
file.h
View file

@ -4,9 +4,11 @@
#include <stdio.h> #include <stdio.h>
typedef struct file { typedef struct file {
char *path; char *name; /* basename */
char *stats; char *path; /* absolute path */
char *type; char *type;
char *stats;
wchar_t *icon;
int color; int color;
} file; } file;
@ -18,9 +20,9 @@ typedef struct ArrayList {
ArrayList *arraylist_init(size_t capacity); ArrayList *arraylist_init(size_t capacity);
void arraylist_free(ArrayList *list); void arraylist_free(ArrayList *list);
bool arraylist_includes(ArrayList *list, char *path); long arraylist_search(ArrayList *list, char *filepath, bool bname);
void arraylist_remove(ArrayList *list, long index); void arraylist_remove(ArrayList *list, long index);
void arraylist_add(ArrayList *list, char *filepath, char *stats, char *type, int color, bool marked, bool force); void arraylist_add(ArrayList *list, char *filename, char *path, char *stats, char *type, wchar_t *icon, int color, bool marked, bool force);
char *get_line(ArrayList *list, long index, bool detail); char *get_line(ArrayList *list, long index, bool detail, bool icons);
#endif #endif

191
icons.c Normal file
View file

@ -0,0 +1,191 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <wchar.h>
#include "icons.h"
#include "util.h"
icon *hash_table[TABLE_SIZE];
/* Hashes every name with: name and TABLE_SIZE */
unsigned int hash(char *name)
{
int length = strnlen(name, MAX_NAME), i = 0;
unsigned int hash_value = 0;
for (; i < length; i++) {
hash_value += name[i];
hash_value = (hash_value * name[i]) % TABLE_SIZE;
}
return hash_value;
}
void hashtable_init()
{
for (int i = 0; i < TABLE_SIZE; i++)
hash_table[i] = NULL;
icon *c = memalloc(sizeof(icon));
strcpy(c->name, "c");
c->icon = L"";
icon *h = memalloc(sizeof(icon));
strcpy(h->name, "h");
h->icon = L"";
icon *cpp = memalloc(sizeof(icon));
strcpy(cpp->name, "cpp");
cpp->icon = L"";
icon *hpp = memalloc(sizeof(icon));
strcpy(hpp->name, "hpp");
hpp->icon = L"󰰀";
icon *md = memalloc(sizeof(icon));
strcpy(md->name, "md");
md->icon = L"";
icon *py = memalloc(sizeof(icon));
strcpy(py->name, "py");
py->icon = L"";
icon *java = memalloc(sizeof(icon));
strcpy(java->name, "java");
java->icon = L"";
icon *json = memalloc(sizeof(icon));
strcpy(json->name, "json");
json->icon = L"";
icon *js = memalloc(sizeof(icon));
strcpy(js->name, "js");
js->icon = L"";
icon *html = memalloc(sizeof(icon));
strcpy(html->name, "html");
html->icon = L"";
icon *rs = memalloc(sizeof(icon));
strcpy(rs->name, "rs");
rs->icon = L"";
icon *sh = memalloc(sizeof(icon));
strcpy(sh->name, "sh");
sh->icon = L"";
icon *go = memalloc(sizeof(icon));
strcpy(go->name, "go");
go->icon = L"";
icon *r = memalloc(sizeof(icon));
strcpy(r->name, "r");
r->icon = L"";
icon *diff = memalloc(sizeof(icon));
strcpy(diff->name, "diff");
diff->icon = L"";
icon *hs = memalloc(sizeof(icon));
strcpy(hs->name, "hs");
hs->icon = L"";
icon *log = memalloc(sizeof(icon));
strcpy(log->name, "log");
log->icon = L"󱀂";
icon *rb = memalloc(sizeof(icon));
strcpy(rb->name, "rb");
rb->icon = L"";
icon *iso = memalloc(sizeof(icon));
strcpy(iso->name, "iso");
iso->icon = L"󰻂";
icon *lua = memalloc(sizeof(icon));
strcpy(lua->name, "lua");
lua->icon = L"";
icon *license = memalloc(sizeof(icon));
strcpy(license->name, "LICENSE");
license->icon = L"";
icon *gitignore = memalloc(sizeof(icon));
strcpy(gitignore->name, "gitignore");
gitignore->icon = L"";
hashtable_add(c);
hashtable_add(h);
hashtable_add(cpp);
hashtable_add(hpp);
hashtable_add(md);
hashtable_add(py);
hashtable_add(java);
hashtable_add(json);
hashtable_add(js);
hashtable_add(html);
hashtable_add(rs);
hashtable_add(sh);
hashtable_add(go);
hashtable_add(r);
hashtable_add(diff);
hashtable_add(hs);
hashtable_add(log);
hashtable_add(rb);
hashtable_add(iso);
hashtable_add(lua);
hashtable_add(license);
hashtable_add(gitignore);
}
void hashtable_print()
{
int i = 0;
for (; i < TABLE_SIZE; i++) {
if (hash_table[i] == NULL) {
printf("%i. ---\n", i);
} else {
printf("%i. | Name %s | Icon %ls\n", i, hash_table[i]->name, hash_table[i]->icon);
}
}
}
/* Gets hashed name and tries to store the icon struct in that place */
bool hashtable_add(icon *p)
{
if (p == NULL) return false;
int index = hash(p->name);
int initial_index = index;
/* linear probing until an empty slot is found */
while (hash_table[index] != NULL) {
index = (index + 1) % TABLE_SIZE; /* move to next item */
/* the hash table is full as no available index back to initial index, cannot fit new item */
if (index == initial_index) return false;
}
hash_table[index] = p;
return true;
}
/* Rehashes the name and then looks in this spot, if found returns icon */
icon *hashtable_search(char *name)
{
int index = hash(name);
int initial_index = index;
/* Linear probing until an empty slot or the desired item is found */
while (hash_table[index] != NULL) {
if (strncmp(hash_table[index]->name, name, MAX_NAME) == 0)
return hash_table[index];
index = (index + 1) % TABLE_SIZE; /* Move to the next slot */
/* back to same item */
if (index == initial_index) break;
}
return NULL;
}

21
icons.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef ICONS_H_
#define ICONS_H_
#include <stdbool.h>
#include <wchar.h>
#define MAX_NAME 30
#define TABLE_SIZE 100
typedef struct {
char name[MAX_NAME];
wchar_t *icon;
} icon;
unsigned int hash(char *name);
void hashtable_init();
void hashtable_print();
bool hashtable_add(icon *p);
icon *hashtable_search(char *name);
#endif

25
util.c
View file

@ -1,5 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <wchar.h>
void die(char *reason) void die(char *reason)
{ {
@ -11,18 +13,37 @@ void *memalloc(size_t size)
{ {
void *ptr = malloc(size); void *ptr = malloc(size);
if (!ptr) { if (!ptr) {
fprintf(stderr, "ccc: Error allocating memory\n"); perror("ccc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return ptr; return ptr;
} }
void *estrdup(void *ptr)
{
void *duped = strdup(ptr);
if (!duped) {
perror("ccc");
exit(EXIT_FAILURE);
}
return duped;
}
void *ewcsdup(void *ptr)
{
void *duped = wcsdup(ptr);
if (!duped) {
perror("ccc");
exit(EXIT_FAILURE);
}
return duped;
}
void *rememalloc(void *ptr, size_t size) void *rememalloc(void *ptr, size_t size)
{ {
ptr = realloc(ptr, size); ptr = realloc(ptr, size);
if (!ptr) { if (!ptr) {
perror("ccc"); perror("ccc");
fprintf(stderr, "ccc: Error allocating memory\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return ptr; return ptr;

2
util.h
View file

@ -5,6 +5,8 @@
void die(char *reason); void die(char *reason);
void *memalloc(size_t size); void *memalloc(size_t size);
void *estrdup(void *ptr);
void *ewcsdup(void *ptr);
void *rememalloc(void *ptr, size_t size); void *rememalloc(void *ptr, size_t size);
#endif #endif