diff --git a/Makefile b/Makefile index 696bbfa..510ebf7 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ MANDIR = $(PREFIX)/share/man/man1 # Flags LDFLAGS = $(shell pkg-config --libs ncurses) -CFLAGS = -O3 -pipe -s -std=c99 -pedantic -Wall $(shell pkg-config --cflags ncurses) +CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall $(shell pkg-config --cflags ncurses) SRC = ccc.c util.c file.c diff --git a/README.md b/README.md index e0daa2a..7ecd57b 100644 --- a/README.md +++ b/README.md @@ -49,30 +49,37 @@ $ sudo make install ## Usage ``` +h: go to parent dir j: scroll down k: scroll up -h: go to parent dir l: go to child dir +left: go to parent dir down: scroll down up: scroll up -left: go to parent dir right: go to child dir enter: go to child dir/open file backspace: go to parent dir -g: go to top +gg: go to top G: go to bottom -t: go to trash -~: go to home +ctrl+u: jump up +ctrl+d: jump down + +t: go to trash dir +~: go to home dir +-: go to previous dir z: refresh current dir +A: show directory disk usage/block size space: mark file a: mark all files in directory -q: exit +?: show help +q: exit with last dir written to file +ctrl+c exit without writing last dir ``` ## License diff --git a/ccc.c b/ccc.c index 0a4ad66..8981b9e 100644 --- a/ccc.c +++ b/ccc.c @@ -16,11 +16,13 @@ #include "config.h" /* functions' definitions */ +void show_help(); +void start_ccc(); void change_dir(const char *buf, int selection); int mkdir_p(const char *destdir); void populate_files(const char *path, int ftype); int get_directory_size(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf); -long add_file_stat(char *filepath, int ftype); +void add_file_stat(char *filepath, int ftype); void highlight_current_line(); void show_file_content(); void edit_file(); @@ -33,8 +35,13 @@ void draw_border_title(WINDOW *window, bool active); unsigned int focus = 0; long current_selection = 0; bool dirs_size = DIRS_SIZE; +bool show_hidden = SHOW_HIDDEN; +bool file_details = SHOW_DETAILS; char *cwd; +char *p_cwd; /* previous cwd */ int half_width; +ArrayList *files; +ArrayList *marked; WINDOW *directory_border; WINDOW *directory_content; WINDOW *preview_border; @@ -80,15 +87,14 @@ int main(int argc, char** argv) init_pair(7, COLOR_CYAN, -1); /* MARKED FILES */ init_pair(8, COLOR_WHITE, -1); /* REG */ - half_width = COLS / 2; - init_windows(); - refresh(); + /* init files and marked arrays */ + files = arraylist_init(100); + marked = arraylist_init(100); cwd = memalloc(PATH_MAX * sizeof(char)); + p_cwd = memalloc(PATH_MAX * sizeof(char)); getcwd(cwd, PATH_MAX); - - populate_files(cwd, 0); - highlight_current_line(); + start_ccc(); int ch, ch2; while (1) { @@ -118,6 +124,7 @@ int main(int argc, char** argv) case LEFT: case 'h':; /* get parent directory */ + strcpy(p_cwd, cwd); char *last_slash = strrchr(cwd, '/'); if (last_slash != NULL) { *last_slash = '\0'; @@ -129,15 +136,14 @@ int main(int argc, char** argv) case ENTER: case RIGHT: case 'l':; - file *file = get_file(current_selection); - if (file != NULL) { - /* check if it is directory or a regular file */ - if (strncmp(file->type, "DIR", 3) == 0) { - /* change cwd to directory */ - change_dir(file->path, 0); - } else if (strncmp(file->type, "REG", 3) == 0) { - edit_file(); - } + strcpy(p_cwd, cwd); + file c_file = files->items[current_selection]; + /* check if it is directory or a regular file */ + if (strncmp(c_file.type, "DIR", 3) == 0) { + /* change cwd to directory */ + change_dir(c_file.path, 0); + } else if (strncmp(c_file.type, "REG", 3) == 0) { + edit_file(); } break; @@ -162,10 +168,10 @@ int main(int argc, char** argv) /* jump down (ctrl d) */ case CTRLD: - if ((current_selection + JUMP_NUM) < (files_len() - 1)) + if ((current_selection + JUMP_NUM) < (files->length - 1)) current_selection += JUMP_NUM; else - current_selection = (files_len() - 1); + current_selection = (files->length - 1); highlight_current_line(); break; @@ -173,7 +179,7 @@ int main(int argc, char** argv) /* go down by j or down arrow */ case DOWN: case 'j': - if (current_selection < (files_len() - 1)) + if (current_selection < (files->length - 1)) current_selection++; highlight_current_line(); @@ -181,7 +187,7 @@ int main(int argc, char** argv) /* jump to the bottom */ case 'G': - current_selection = (files_len() - 1); + current_selection = (files->length - 1); highlight_current_line(); break; @@ -234,14 +240,33 @@ int main(int argc, char** argv) /* show directories' sizes */ case 'A': dirs_size = !dirs_size; - clear_files(); - populate_files(cwd, 0); - highlight_current_line(); + change_dir(cwd, 0); break; + /* go to previous dir */ + case '-': + change_dir(p_cwd, 0); + break; + + /* show help */ + case '?': + show_help(); + break; + + /* toggle hidden files */ + case '.': + show_hidden = !show_hidden; + change_dir(cwd, 0); + break; + + /* toggle file details */ + case 'i': + file_details = !file_details; + change_dir(cwd, 0); + /* mark one file */ case SPACE: - add_file_stat(get_filepath(current_selection), 1); + add_file_stat(files->items[current_selection].path, 1); highlight_current_line(); break; @@ -254,35 +279,35 @@ int main(int argc, char** argv) /* mark actions: */ /* delete */ case 'd':; - if (marked_len()) { + if (marked->length) { ; } break; /* move */ case 'm':; - if (marked_len()) { + if (marked->length) { ; } break; /* copy */ case 'c':; - if (marked_len()) { + if (marked->length) { ; } break; /* symbolic link */ case 's':; - if (marked_len()) { + if (marked->length) { ; } break; /* bulk rename */ case 'b':; - if (marked_len()) { + if (marked->length) { ; } break; @@ -298,29 +323,54 @@ int main(int argc, char** argv) delwin(preview_content); delwin(panel); endwin(); - half_width = COLS / 2; - init_windows(); - refresh(); - populate_files(cwd, 0); - highlight_current_line(); + start_ccc(); break; default: break; } } - clear_files(); - clear_marked(); + arraylist_free(files); + arraylist_free(marked); endwin(); return 0; } +void show_help() +{ + wclear(directory_content); + wclear(preview_content); + wprintw(directory_content,"h: go to parent dir\nj: scroll down\nk: scroll up\nl: go to child dir\n\nleft: go to parent dir\ndown: scroll down\nup: scroll up\nright: go to child dir\n\nenter: go to child dir/open file\nbackspace: go to parent dir\n\ngg: go to top\nG: go to bottom\n\nctrl+u: jump up\nctrl+d: jump down\n\nt: go to trash dir\n~: go to home dir\n-: go to previous dir\nz: refresh current dir\n\nA: show directory disk usage/block size\nspace: mark file\na: mark all files in directory\n\n?: show help\nq: exit with last dir written to file\nctrl+c exit without writing last dir"); + wpprintw("Visit https://github.com/piotr-marendowski/ccc or use 'man ccc' for help"); + wrefresh(directory_content); + wrefresh(preview_content); +} + +void start_ccc() +{ + half_width = COLS / 2; + init_windows(); + refresh(); + populate_files(cwd, 0); + highlight_current_line(); +} + /* * Change directory in window with selection */ void change_dir(const char *buf, int selection) { - strcpy(cwd, buf); - clear_files(); + char *buf_dup; + if (buf == p_cwd) { + buf_dup = strdup(p_cwd); + if (buf_dup == NULL) { + return; + } + } else { + buf_dup = (char *) buf; + } + strcpy(cwd, buf_dup); + arraylist_free(files); + files = arraylist_init(100); populate_files(cwd, 0); current_selection = selection; highlight_current_line(); @@ -398,8 +448,8 @@ void populate_files(const char *path, int ftype) filename[0] = '\0'; strcat(filename, ep->d_name); - /* can't be strncmp as that would filter out the dotfiles */ - if (strcmp(filename, ".") && strcmp(filename, "..")) { + /* use strncmp to filter out dotfiles */ + if ((show_hidden && strncmp(filename, ".", 1) && strncmp(filename, "..", 2)) || (!show_hidden && strcmp(filename, ".") && strcmp(filename, ".."))) { /* construct full file path */ filename[0] = '\0'; strcat(filename, cwd); @@ -428,13 +478,13 @@ int get_directory_size(const char *fpath, const struct stat *sb, int typeflag, s * Add that file into list * ftype: normal file = 0, normal marked = 1, marking ALL = 2 */ -long add_file_stat(char *filepath, int ftype) +void add_file_stat(char *filepath, int ftype) { struct stat file_stat; if (stat(filepath, &file_stat) == -1) { /* can't be triggered? */ if (errno == EACCES) - return add_file(filepath, "", "", 8); + arraylist_add(files, filepath, "", "", 8, false, false); } /* get file type and color, 4 chars for the type */ @@ -469,14 +519,10 @@ long add_file_stat(char *filepath, int ftype) if (ftype == 1 || ftype == 2) { /* force if user is marking all files */ bool force = ftype == 2 ? true : false; - long index = add_marked(filepath, type, force); + arraylist_add(marked, filepath, NULL, type, 8, true, force); /* free type and return without allocating more stuff */ free(type); - if (index != -1) { - return index; /* just marked */ - } else { - return -1; /* already marked */ - } + return; } /* get last modified time */ @@ -511,13 +557,12 @@ long add_file_stat(char *filepath, int ftype) snprintf(total_stat, 45, "%-18s %-8s", time, size); total_stat[strlen(total_stat)] = '\0'; - long index = add_file(filepath, total_stat, type, color); + arraylist_add(files, filepath, total_stat, type, color, false, false); free(time); free(size); free(total_stat); free(type); - return index; } @@ -533,7 +578,7 @@ void highlight_current_line() } /* calculate range of files to show */ - long range = files_len(); + long range = files->length; /* not highlight if no files in directory */ if (range == 0) { #if DRAW_PREVIEW @@ -560,24 +605,24 @@ void highlight_current_line() wclear(panel); /* check for marked files */ - long num_marked = marked_len(); + long num_marked = marked->length; 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) * sizeof(char)); snprintf(selected, m_len + 1, "[%ld] selected", num_marked); - wprintw(panel, "(%ld/%ld) %s %s", current_selection + 1, files_len(), selected, cwd); + wprintw(panel, "(%ld/%ld) %s %s", current_selection + 1, files->length, selected, cwd); } else { - wprintw(panel, "(%ld/%ld) %s", current_selection + 1, files_len(), cwd); + wprintw(panel, "(%ld/%ld) %s", current_selection + 1, files->length, cwd); } } /* print the actual filename and stats */ - char *line = get_line(i); - int color = get_color(i); + char *line = get_line(files, i, file_details); + int color = files->items[i].color; /* check is file marked for action */ - bool marked = in_marked(get_filepath(i)); - if (marked) { + bool is_marked = arraylist_includes(marked, files->items[i].path); + if (is_marked) { /* show file is selected */ wattron(directory_content, COLOR_PAIR(7)); } else { @@ -590,7 +635,7 @@ void highlight_current_line() else mvwprintw(directory_content, i, 0, "%s", line); - if (marked) { + if (is_marked) { wattroff(directory_content, COLOR_PAIR(7)); } else { wattroff(directory_content, COLOR_PAIR(color)); @@ -616,14 +661,14 @@ void highlight_current_line() void show_file_content() { wclear(preview_content); - file *current_file = get_file((long) 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; - FILE *file = fopen(current_file->path, "r"); + FILE *file = fopen(current_file.path, "r"); 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.path); return; } #if DRAW_BORDERS @@ -673,7 +718,7 @@ void edit_file() def_prog_mode(); /* save the tty modes */ endwin(); /* end curses mode temporarily */ - char *filename = get_filepath(current_selection); + char *filename = files->items[current_selection].path; int length = strlen(editor) + strlen(filename) + 2; /* one for space one for null */ char command[length]; @@ -685,7 +730,8 @@ void edit_file() } } -int write_last_d() { +int write_last_d() +{ #ifdef LAST_D char *last_d = memalloc(PATH_MAX * sizeof(char)); strcpy(last_d, LAST_D); diff --git a/config.h b/config.h index 60f4caa..f8abf36 100644 --- a/config.h +++ b/config.h @@ -8,6 +8,8 @@ #define DRAW_BORDERS true /* Draw borders around 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: +-------------%-------------+ diff --git a/file.c b/file.c index 92179b2..e954dd3 100644 --- a/file.c +++ b/file.c @@ -5,274 +5,142 @@ #include "util.h" -/* files in a link list data structure */ typedef struct file { char *path; char *stats; char *type; int color; - struct file *next; } file; -file *files = NULL; -file *marked = NULL; +typedef struct ArrayList { + size_t length; + size_t capacity; + file *items; +} ArrayList; -/* - * Get length of files linked list - */ -long files_len() +ArrayList *arraylist_init(size_t capacity) { - file *current = files; - int count = 0; - while (current != NULL) { - count++; - current = current->next; - } - return count; + ArrayList *list = memalloc(sizeof(ArrayList)); + list->length = 0; + list->capacity = capacity; + list->items = memalloc(capacity * sizeof(file)); + return list; } -/* - * Get length of marked files - */ -long marked_len() +void arraylist_free(ArrayList *list) { - file *current = marked; - int count = 0; - while (current != NULL) { - count++; - current = current->next; + for (size_t i = 0; i < list->length; i++) { + if (list->items[i].type != NULL) + free(list->items[i].type); + if (list->items[i].path != NULL) + free(list->items[i].path); + if (list->items[i].stats != NULL) + free(list->items[i].stats); } - return count; - + free(list->items); + list->length = 0; } -void free_file(file *toremove) +bool arraylist_includes(ArrayList *list, char *path) { - if (toremove->type != NULL) - free(toremove->type); - if (toremove->path != NULL) - free(toremove->path); - if (toremove->stats != NULL) - free(toremove->stats); - free(toremove); + for (int i = 0; i < list->length; i++) { + if (strcmp(list->items[i].path, path) == 0) { + return true; + } + } + return false; } -void clear_files() +void arraylist_remove(ArrayList *list, long index) { - file *tmp; - while (files != NULL) { - tmp = files; - files = files->next; - free_file(tmp); - } -} - -void clear_marked() -{ - file *tmp; - while (marked != NULL) { - tmp = marked; - files = marked->next; - free_file(tmp); - } -} - -long add_file(char *filepath, char *stats, char *type, int color) -{ - file *current = files; - file *new_file = memalloc(sizeof(file)); - char *buf = strdup(filepath); - char *buf2 = strdup(stats); - char *buf3 = strdup(type); - int buf4 = color; - - if (buf == NULL || buf2 == NULL || buf3 == NULL) - perror("ccc"); - - new_file->path = buf; - new_file->stats = buf2; - new_file->type = buf3; - new_file->color = buf4; - new_file->next = NULL; - - if (current == NULL) { - files = new_file; - return 0; - } - long index = 1; - while (current->next != NULL) { - current = current->next; - index++; - } - current->next = new_file; - return index; -} - -void remove_marked(file *marked_file) -{ - /* If the head node itself is marked for removal */ - if (marked == marked_file) { - marked = marked->next; - free_file(marked_file); + if (index >= list->length) { return; } - - /* Search for the marked file node in the list */ - file* temp = marked; - while (temp != NULL && temp->next != marked_file) { - temp = temp->next; + free(list->items[index].path); + free(list->items[index].stats); + free(list->items[index].type); + + for (long i = index; i < list->length - 1; i++) { + list->items[i] = list->items[i + 1]; } - /* If the marked file node is found, remove it from the list */ - if (temp != NULL) { - temp->next = marked_file->next; - free_file(marked_file); - } + list->length--; } /* * force will not remove duplicate marked files, instead it just skip adding */ -long add_marked(char *filepath, char *type, bool force) +void arraylist_add(ArrayList *list, char *filepath, char *stats, char *type, int color, bool marked, bool force) { - file *current = marked; - file *new_file = memalloc(sizeof(file)); - char *buf = strdup(filepath); - char *buf2 = strdup(type); - if (buf == NULL || buf2 == NULL) { - perror("ccc"); + char *filepath_cp = NULL; + char *stats_cp = NULL; + char *type_cp = NULL; + if (filepath != NULL) { + filepath_cp = strdup(filepath); + if (filepath_cp == NULL) { + perror("ccc"); + } } - new_file->path = buf; - new_file->type = buf2; - new_file->stats = NULL; - new_file->color = 0; - new_file->next = NULL; - if (current == NULL) { - marked = new_file; - return 0; + if (stats != NULL) { + stats_cp = strdup(stats); + if (stats_cp == NULL) { + perror("ccc"); + } } - - long index = 1; - while (current->next != NULL) { - if (strcmp(current->path, new_file->path) == 0) { - if (force) { - return index; - } else { - remove_marked(current); - free_file(new_file); - return -1; + if (type != NULL) { + type_cp = strdup(type); + if (type_cp == NULL) { + perror("ccc"); + } + } + + /* path, stats, type, color */ + file new_file = { filepath_cp, stats_cp, type_cp, color }; + + if (list->capacity != list->length) { + if (marked) { + for (int i = 0; i < list->length; i++) { + if (strcmp(list->items[i].path, new_file.path) == 0) { + if (!force) { + arraylist_remove(list, i); + } + return; + } } } - current = current->next; - index++; - } - if (strcmp(current->path, new_file->path) == 0){ - if (force) { - return 0; - } else { - remove_marked(current); - free_file(new_file); - return -1; - } - } - current->next = new_file; - return index; -} - -file *get_marked(long index) -{ - file *current = marked; - if (index == 0) { - return current; - } - if (index > files_len()) { - return NULL; - } - for (long i = 0; i < index; i++) { - current = current->next; - } - return current; -} - -bool in_marked(char *path) -{ - file *tmp = marked; - if (tmp == NULL) - return false; - - while (tmp != NULL) { - if (strcmp(path, tmp->path) == 0) { - return true; - } - tmp = tmp->next; - } - return false; -} - -file *get_file(long index) -{ - file *current = files; - if (index == 0) { - return current; - } - if (index > files_len()) { - return NULL; - } - for (long i = 0; i < index; i++) { - current = current->next; - } - return current; -} - -char *get_filepath(long index) -{ - file *file = get_file(index); - if (file != NULL) { - char *name = strdup(file->path); - if (!name) { - perror("ccc"); - } - return name; + list->items[list->length] = new_file; } else { - return NULL; - } -} - -int get_color(long index) -{ - file *file = get_file(index); - if (file != NULL) { - int color = file->color; - if (!color) { - perror("ccc"); + int new_cap = list->capacity * 2; + file *new_items = memalloc(new_cap * sizeof(file)); + file *old_items = list->items; + list->capacity = new_cap; + list->items = new_items; + for (int i = 0; i < list->length; i++) { + new_items[i] = old_items[i]; } - return color; - } else { - return 8; /* white */ + free(old_items); + list->items[list->length] = new_file; } + list->length++; } /* * Construct a formatted line for display */ -char *get_line(long index) +char *get_line(ArrayList *list, long index, bool detail) { - file *file = get_file(index); - if (file != NULL) { - char *name = strdup(file->path); - char *stats = strdup(file->stats); - size_t length = strlen(name) + strlen(stats) + 2; /* one for space and one for null */ - char *line = memalloc(length * sizeof(char)); + file file = list->items[index]; + char *name = strdup(file.path); + char *stats = strdup(file.stats); + size_t length = strlen(name) + strlen(stats) + 2; /* one for space and one for null */ + char *line = memalloc(length * sizeof(char)); - name = basename(name); - if (name == NULL || stats == NULL) - perror("ccc"); + name = basename(name); + if (name == NULL || stats == NULL) + perror("ccc"); - snprintf(line, length, "%s %s", stats, name); + snprintf(line, length, "%s %s", stats, name); - return line; - } else { - return NULL; - } + return line; } diff --git a/file.h b/file.h index 57070aa..e7a0434 100644 --- a/file.h +++ b/file.h @@ -1,27 +1,26 @@ #ifndef FILE_H_ #define FILE_H_ +#include + typedef struct file { char *path; char *stats; char *type; int color; - struct file *next; } file; -long files_len(); -long marked_len(); -void free_file(file *toremove); -void clear_files(); -void clear_marked(); -long add_file(char *filepath, char *stats, char *type, int color); -void remove_marked(file *marked_file); -long add_marked(char *filepath, char *type, bool force); -file *get_marked(long index); -bool in_marked(char *path); -file *get_file(long index); -char *get_filepath(long index); -int get_color(long index); -char *get_line(long index); +typedef struct ArrayList { + size_t length; + size_t capacity; + file *items; +} ArrayList; + +ArrayList *arraylist_init(size_t capacity); +void arraylist_free(ArrayList *list); +bool arraylist_includes(ArrayList *list, char *path); +void arraylist_remove(ArrayList *list, long index); +void arraylist_add(ArrayList *list, char *filepath, char *stats, char *type, int color, bool marked, bool force); +char *get_line(ArrayList *list, long index, bool detail); #endif