Merge pull request #19 from piotr-marendowski/feature

Feature
This commit is contained in:
Piotr Marendowski 2024-03-20 20:53:29 +00:00 committed by GitHub
commit 1b202d5df1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 233 additions and 311 deletions

View file

@ -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

View file

@ -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

176
ccc.c
View file

@ -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);

View file

@ -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:
+-------------%-------------+

316
file.c
View file

@ -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;
}

29
file.h
View file

@ -1,27 +1,26 @@
#ifndef FILE_H_
#define FILE_H_
#include <stdio.h>
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