commit
7a8f8ff881
10 changed files with 570 additions and 190 deletions
4
Makefile
4
Makefile
|
@ -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:
|
||||||
|
|
|
@ -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
325
ccc.c
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
34
config.h
34
config.h
|
@ -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
142
file.c
|
@ -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
12
file.h
|
@ -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
191
icons.c
Normal 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
21
icons.h
Normal 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
25
util.c
|
@ -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
2
util.h
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue