mirror of
https://gittea.dev/nova/th.git
synced 2025-10-21 18:30:15 -04:00
401 lines
12 KiB
C
401 lines
12 KiB
C
#include <curses.h>
|
|
#include <pthread.h>
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "sorting.h"
|
|
#include "defines.h"
|
|
#include "config.h"
|
|
|
|
|
|
extern unsigned int settings;
|
|
extern unsigned int file_modifiers;
|
|
extern unsigned int color_count;
|
|
extern unsigned int terminal_height;
|
|
extern volatile unsigned long selected_file_current;
|
|
extern volatile unsigned long selected_file_last;
|
|
extern color *colors;
|
|
int (*order_func)() = sort_natural;
|
|
linked_dir *visited_dirs;
|
|
linked_dir *current_dir;
|
|
|
|
unsigned long get_dir_size(char *path);
|
|
void get_dir_content(char *path, unsigned long *dir_file_count, file *dir_content);
|
|
void print_dir(WINDOW *win, char print_info, unsigned long *dir_file_count, file *dir_content);
|
|
char recursive_delete(file current_file);
|
|
|
|
|
|
|
|
unsigned long get_dir_size(char *path){
|
|
DIR *dir = opendir(path);
|
|
unsigned long entry_count = 0;
|
|
if (dir) {
|
|
struct dirent *entry;
|
|
while ((entry=readdir(dir))) {
|
|
if (entry->d_name[0] != '.' || (file_modifiers & FILE_MODIFIERS_HIDDEN_FILES)) {
|
|
entry_count++;
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
if (file_modifiers & FILE_MODIFIERS_HIDDEN_FILES) {
|
|
/* removes files "." and ".." */
|
|
entry_count -= 2;
|
|
}
|
|
return entry_count;
|
|
|
|
}
|
|
|
|
void get_dir_content(char *path, unsigned long *dir_file_count, file *dir_content){
|
|
struct dirent **entry;
|
|
if (file_modifiers & FILE_MODIFIERS_HIDDEN_FILES) { /* print hidden files */
|
|
scandir(path, &entry, skip_dot, NULL);
|
|
} else {
|
|
scandir(path, &entry, skip_hidden_files, NULL);
|
|
}
|
|
|
|
unsigned long i = 0;
|
|
for (i = 0; i < *dir_file_count; i++ ) {
|
|
if (entry[i]->d_name[0] == '.' && !(file_modifiers & FILE_MODIFIERS_HIDDEN_FILES)) {
|
|
} else {
|
|
dir_content[i].file_name = malloc(strlen(entry[i]->d_name)+1);
|
|
strcpy(dir_content[i].file_name, entry[i]->d_name);
|
|
dir_content[i].file_name[strlen(entry[i]->d_name)] = '\0';
|
|
|
|
|
|
struct stat *file;
|
|
file = malloc(sizeof(struct stat));
|
|
memset(file, ' ', sizeof(struct stat));
|
|
|
|
/* using the full path allows using the same function for all windows */
|
|
unsigned long path_len = strlen(path);
|
|
char *full_path = malloc(strlen(path) + strlen(entry[i]->d_name) + 1 + sizeof("/"));
|
|
memcpy(full_path, path, strlen(path));
|
|
memcpy(full_path + path_len, "/", sizeof("/"));
|
|
memcpy(full_path + path_len + sizeof("/") - 1, entry[i]->d_name, strlen(entry[i]->d_name) + 1);
|
|
|
|
lstat(full_path, file);
|
|
|
|
dir_content[i].file_size = file->st_size;
|
|
dir_content[i].permissions = 1;
|
|
dir_content[i].permissions = file->st_mode;
|
|
|
|
if (S_ISDIR(file->st_mode)) {
|
|
dir_content[i].file_type = FILE_TYPE_DIR;
|
|
dir_content[i].color_pair = COLOR_DIR;
|
|
dir_content[i].file_size = get_dir_size(full_path);
|
|
} else if (S_ISLNK(file->st_mode)) {
|
|
dir_content[i].file_type = FILE_TYPE_SYMLINK;
|
|
dir_content[i].color_pair = COLOR_SYMLINK;
|
|
dir_content[i].file_size = get_dir_size(full_path);
|
|
} else if (file->st_mode & S_IXUSR) {
|
|
dir_content[i].file_type = FILE_TYPE_EXEC;
|
|
dir_content[i].color_pair = COLOR_EXEC;
|
|
} else if (S_ISBLK(file->st_mode)) {
|
|
dir_content[i].file_type = FILE_TYPE_BLOCK;
|
|
dir_content[i].color_pair = COLOR_BLOCK;
|
|
} else if (S_ISCHR(file->st_mode)) {
|
|
dir_content[i].file_type = COLOR_CHARDEV;
|
|
} else if (S_ISFIFO(file->st_mode)) {
|
|
dir_content[i].file_type = FILE_TYPE_FIFO;
|
|
dir_content[i].color_pair = COLOR_FIFO;
|
|
} else if (S_ISSOCK(file->st_mode)) {
|
|
dir_content[i].file_type = FILE_TYPE_SOCK;
|
|
dir_content[i].color_pair = COLOR_SOCK;
|
|
} else if (S_ISREG(file->st_mode)) {
|
|
dir_content[i].file_type = FILE_TYPE_REGULAR;
|
|
dir_content[i].color_pair = COLOR_REGULAR;
|
|
unsigned long j = 0;
|
|
char *extension = strrchr(entry[i]->d_name, '.');
|
|
if (extension) {
|
|
for (j = 0; j < color_count; j++) {
|
|
if (!strcmp(colors[j].file_extension, extension)) {
|
|
dir_content[i].color_pair = colors[j].color_pair;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else {
|
|
dir_content[i].file_type = COLOR_REGULAR;
|
|
dir_content[i].color_pair = COLOR_REGULAR;
|
|
unsigned long j = 0;
|
|
char *extension = strrchr(entry[i]->d_name, '.');
|
|
if (extension) {
|
|
for (j = 0; j < color_count; j++) {
|
|
if (!strcmp(colors[j].file_extension, extension)) {
|
|
dir_content[i].color_pair = colors[j].color_pair;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
free(full_path);
|
|
free(file);
|
|
}
|
|
}
|
|
|
|
qsort(dir_content, *dir_file_count, sizeof(file), order_func);
|
|
|
|
for (i = 0; i < *dir_file_count; i++) {
|
|
free(entry[i]);
|
|
}
|
|
free(entry);
|
|
|
|
}
|
|
|
|
void print_dir(WINDOW *win, char print_info, unsigned long *dir_file_count, file *dir_content){
|
|
/* i am not proud of this function */
|
|
unsigned long line_width = getmaxx(win);
|
|
|
|
char *bg = malloc(line_width+1);
|
|
memset(bg, ' ', line_width);
|
|
bg[line_width] = '\0';
|
|
|
|
unsigned long i = 0;
|
|
float file_size;
|
|
float printed_size = 0;
|
|
char size_char = ' ';
|
|
char is_selected = 0;
|
|
static const char sizes[6] = { 'B', 'K', 'M', 'G', 'T', 'P' };
|
|
|
|
unsigned long offset_vertical = 0;
|
|
unsigned long offset_back = 0;
|
|
#if SETTINGS_LINE_NUMBERS == 0
|
|
unsigned long offset_front = 0;
|
|
#else
|
|
unsigned long offset_front = 2;
|
|
#endif
|
|
|
|
unsigned long offset_index = 2; /* only used for the index of the file itself */
|
|
if (print_info) {
|
|
if (*dir_file_count > 9) {
|
|
|
|
#if SETTINGS_LINE_NUMBERS != 0
|
|
unsigned long dir_file_count_ = *dir_file_count;
|
|
while(dir_file_count_ > 9) {
|
|
offset_front++;
|
|
dir_file_count_ /= 10;
|
|
}
|
|
#endif
|
|
}
|
|
if (selected_file_current > (terminal_height/3)*2 && *dir_file_count > terminal_height - 2) {
|
|
if (selected_file_current + (terminal_height/3) >= *dir_file_count) {
|
|
offset_vertical = *dir_file_count - terminal_height+2;
|
|
} else {
|
|
offset_vertical = selected_file_current - (terminal_height/3)*2;
|
|
}
|
|
}
|
|
}
|
|
for (i = offset_vertical; i < *dir_file_count && i < (terminal_height + offset_vertical); i++) {
|
|
|
|
/* shortens the printed file name if it is too long
|
|
* example input: aaaaaaaa.txt
|
|
* example output: aaa~.txt
|
|
* if no extension is found, the name will truncate */
|
|
char *file_name;
|
|
unsigned long file_name_width = strlen(dir_content[i].file_name);
|
|
if ((file_name_width + offset_front + is_selected) > offset_back - 1) {
|
|
char *extension = strrchr(dir_content[i].file_name, '.');
|
|
if (extension) {
|
|
int char_offset = (file_name_width + offset_front + is_selected) - (offset_back - 1) ;
|
|
if ((file_name_width - char_offset - strlen(extension) - 1) > 1) {
|
|
file_name = malloc(file_name_width - char_offset + 1);
|
|
memcpy(file_name, dir_content[i].file_name, file_name_width - char_offset);
|
|
memcpy(file_name + (file_name_width - char_offset - strlen(extension)), extension, strlen(extension));
|
|
file_name[file_name_width - char_offset - strlen(extension) - 1] = '~';
|
|
file_name[file_name_width - char_offset] = '\0';
|
|
} else {
|
|
file_name = malloc(strlen(extension)+1);
|
|
file_name[0] = '~';
|
|
memcpy(file_name+1, extension, strlen(extension));
|
|
file_name[strlen(extension)] = '\0';
|
|
}
|
|
} else {
|
|
file_name = malloc(file_name_width+1);
|
|
memcpy(file_name, dir_content[i].file_name, file_name_width);
|
|
file_name[file_name_width] = '\0';
|
|
}
|
|
|
|
} else {
|
|
file_name = malloc(file_name_width+1);
|
|
memcpy(file_name, dir_content[i].file_name, file_name_width);
|
|
file_name[file_name_width] = '\0';
|
|
}
|
|
|
|
|
|
if (print_info) {
|
|
file_size = dir_content[i].file_size;
|
|
char size_index = 0;
|
|
while (file_size > 1) {
|
|
printed_size=file_size;
|
|
file_size /= 1024;
|
|
size_index++;
|
|
if (size_index >= 6) {
|
|
break;
|
|
}
|
|
}
|
|
size_char = sizes[size_index-1];
|
|
if (dir_content[i].file_type == FILE_TYPE_DIR || dir_content[i].file_type == FILE_TYPE_SYMLINK) {
|
|
offset_back = line_width - (snprintf(NULL,0,"%ld", dir_content[i].file_size) + 1);
|
|
} else if (size_char =='B') {
|
|
offset_back = line_width - (snprintf(NULL,0,"%0.0lf %c", printed_size, size_char) + 1);
|
|
} else {
|
|
offset_back = line_width - (snprintf(NULL,0,"%0.2lf %c", printed_size, size_char) + 1);
|
|
}
|
|
|
|
}
|
|
|
|
if (dir_content[i].status & FILE_STATUS_SELECTED) {
|
|
is_selected = 1;
|
|
} else {
|
|
is_selected = 0;
|
|
}
|
|
if (dir_content[i].status & FILE_STATUS_SELECTED) {
|
|
wattron(win, COLOR_PAIR(8));
|
|
} else {
|
|
wattron(win, COLOR_PAIR(dir_content[i].color_pair));
|
|
}
|
|
if (dir_content[i].status & FILE_STATUS_HOVER) {
|
|
wattron(win, A_REVERSE);
|
|
} else {
|
|
wattroff(win, A_REVERSE);
|
|
}
|
|
|
|
mvwaddstr(win, i-offset_vertical, 0, bg);
|
|
if(print_info) {
|
|
#if SETTINGS_LINE_NUMBERS == 2
|
|
long i_ = (selected_file_current) - i;
|
|
offset_index = !((selected_file_current) - i);
|
|
while(i_) {
|
|
offset_index++;
|
|
i_ /= 10;
|
|
}
|
|
long relative_index = selected_file_current - i;
|
|
if (relative_index < 0) {
|
|
relative_index = relative_index * -1;
|
|
}
|
|
mvwprintw(win, i-offset_vertical, offset_front-offset_index-1, "%ld", relative_index);
|
|
|
|
#elif SETTINGS_LINE_NUMBERS == 1
|
|
unsigned long i_ = i;
|
|
offset_index = 2;
|
|
while(i_ > 9) {
|
|
offset_index++;
|
|
i_ /= 10;
|
|
}
|
|
mvwprintw(win, i-offset_vertical, offset_front-offset_index, "%ld", i);
|
|
#endif
|
|
|
|
mvwaddnstr(win, i-offset_vertical, offset_front+is_selected, file_name, line_width-offset_front-is_selected-2);
|
|
free(file_name);
|
|
|
|
if (dir_content[i].file_type == FILE_TYPE_DIR || dir_content[i].file_type == FILE_TYPE_SYMLINK) {
|
|
mvwprintw(win, i-offset_vertical, offset_back, "%ld", dir_content[i].file_size);
|
|
}else if (size_char =='B') {
|
|
mvwprintw(win, i-offset_vertical, offset_back, "%0.0lf %c", printed_size, size_char);
|
|
} else {
|
|
mvwprintw(win, i-offset_vertical, offset_back, "%0.2lf %c", printed_size, size_char);
|
|
}
|
|
} else {
|
|
mvwaddnstr(win, i-offset_vertical, 0, file_name, line_width);
|
|
free(file_name);
|
|
}
|
|
|
|
if (dir_content[i].status & FILE_STATUS_SELECTED) {
|
|
wattroff(win, COLOR_PAIR(8));
|
|
} else {
|
|
wattroff(win, COLOR_PAIR(dir_content[i].color_pair));
|
|
}
|
|
|
|
}
|
|
free(bg);
|
|
}
|
|
|
|
char update_selected_file(){
|
|
char ret = -1; /* -1 on empty or inaccessible file, 0 on unchanged file, 1 on changed file */
|
|
if (mid_content->file_name[0] == '\0') { /* only happens if the current path is either empty or inaccessible */
|
|
return ret;
|
|
}
|
|
if (selected_file_current >= mid_file_count) {
|
|
selected_file_current = mid_file_count-1;
|
|
}
|
|
if (selected_file_current != selected_file_last) {
|
|
mid_content[selected_file_last].status &= ~FILE_STATUS_HOVER;
|
|
ret = 1;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
mid_content[selected_file_current].status |= FILE_STATUS_HOVER;
|
|
selected_file_last = selected_file_current;
|
|
return ret;
|
|
}
|
|
void dir_set_selected_file_current(unsigned long selected_file_current){
|
|
if (mid_content->file_name[0] != '\0') {
|
|
current_dir->selected_file_current = selected_file_current;
|
|
}
|
|
}
|
|
|
|
unsigned long dir_get_selected_file_current(){
|
|
|
|
current_dir = visited_dirs;
|
|
if (mid_content->file_name[0] == '\0') {
|
|
return 0;
|
|
}
|
|
char hit = 0;
|
|
char *path = getcwd(NULL, 0);
|
|
while(current_dir->next != NULL) {
|
|
if (strcmp(path, current_dir->path) == 0) {
|
|
hit = 1;
|
|
break;
|
|
}
|
|
current_dir = current_dir->next;
|
|
}
|
|
if (hit == 0) {
|
|
current_dir->next = malloc(sizeof(linked_dir));
|
|
current_dir->next->next = NULL;
|
|
current_dir->next->path = path;
|
|
return 0;
|
|
} else {
|
|
free(path);
|
|
return current_dir->selected_file_current;
|
|
}
|
|
}
|
|
void dir_init(){
|
|
visited_dirs = malloc(sizeof(linked_dir));
|
|
visited_dirs->path = getcwd(NULL, 0);
|
|
visited_dirs->selected_file_current = 0;
|
|
visited_dirs->next = NULL;
|
|
current_dir = visited_dirs;
|
|
|
|
}
|
|
|
|
char recursive_delete(file current_file){
|
|
if (current_file.file_type & (FILE_TYPE_SYMLINK | FILE_TYPE_DIR)) {
|
|
unsigned int file_modifiers_tmp = file_modifiers;
|
|
file_modifiers |= FILE_MODIFIERS_HIDDEN_FILES;
|
|
unsigned long current_file_count = get_dir_size(current_file.file_name);
|
|
if (current_file_count != 0) {
|
|
file *current_dir = malloc(current_file_count * sizeof(file));
|
|
memset(current_dir, '\0', current_file_count * sizeof(file));
|
|
get_dir_content(current_file.file_name, ¤t_file_count, current_dir);
|
|
chdir(current_file.file_name);
|
|
unsigned long i;
|
|
for (i = 0; i < current_file_count; i++) {
|
|
recursive_delete(current_dir[i]);
|
|
free(current_dir[i].file_name);
|
|
}
|
|
free(current_dir);
|
|
chdir("..");
|
|
}
|
|
remove(current_file.file_name);
|
|
file_modifiers = file_modifiers_tmp;
|
|
|
|
} else {
|
|
remove(current_file.file_name);
|
|
}
|
|
}
|