From 9bffcafa9ae0c38103a8c64b3482bb099e792869 Mon Sep 17 00:00:00 2001 From: verdant Date: Tue, 28 Jul 2026 18:30:16 +0800 Subject: Move source files to src/ --- src/fm.c | 463 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 src/fm.c (limited to 'src/fm.c') diff --git a/src/fm.c b/src/fm.c new file mode 100644 index 0000000..92526c3 --- /dev/null +++ b/src/fm.c @@ -0,0 +1,463 @@ +#define _GNU_SOURCE +#include "fm.h" +#include "display.h" +#include "err.h" +#include "log.h" +#include "wm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static magic_t g_magic_ctx = NULL; +static char *const utils[] = {"vi", "xdg-open"}; +static char *const envs[] = {"SHELL", "EDITOR", "SF"}; +bool need_redraw = true; + +/* Function forward declarations */ +static fm_info *fm_w_init(terminal *term); +static void fm_file_list_init(file_list *l, int cap); +static void fm_file_items_realloc(file_list *l); +static char *detect_file_type(const char *filepath); +static void fm_getenv(void); +static char *xgetenv(const char *const name, char *fallback); +int open_with_editor(char **argv); +bool fm_preview_file(window *w, const char *path); + +/* Functions */ +static fm_info * +fm_w_init(terminal *term) + +{ + int maxx = term->maxx; + int maxy = term->maxy; + + fm_info *info = xmalloc(sizeof(fm_info)); + info->windows[PANE_LEFT] = xmalloc(sizeof(window)); + info->windows[PANE_RIGHT] = xmalloc(sizeof(window)); + + int pane_width = maxx / 2; + int pane_height = maxy - 1; + int pane_starty = 0; + + int pane_left_startx = 0; + int pane_right_startx = maxx / 2; + + info->windows[PANE_LEFT] = + wm_create_new_w(term, "pane_left", pane_height, pane_width, + pane_starty, pane_left_startx); + info->windows[PANE_RIGHT] = + wm_create_new_w(term, "pane_right", pane_height, pane_width, + pane_starty, pane_right_startx); + + info->top = 0; + info->bottom = pane_height; + + need_redraw = true; + return info; +} + +void +fm_getcwd(fm_info *info) +{ + getcwd(info->cwd, sizeof(info->cwd)); +} + +static void +fm_file_items_realloc(file_list *l) +{ + l->cap *= 2; + l->items = realloc(l->items, sizeof(file_item) * l->cap); + l->items = xrealloc(l->items, sizeof(file_item) * l->cap); +} + +void +get_permission(mode_t mode, char *str) +{ + if (S_ISDIR(mode)) + str[0] = 'd'; /* Directory */ + else if (S_ISLNK(mode)) + str[0] = 'l'; /* Link */ + else if (S_ISCHR(mode)) + str[0] = 'c'; /* Charcter devices */ + else if (S_ISBLK(mode)) + str[0] = 'b'; /* Block devices */ + else if (S_ISFIFO(mode)) + str[0] = 'p'; /* FIFO */ + else if (S_ISSOCK(mode)) + str[0] = 's'; /* Socket */ + else + str[0] = '-'; /* Files */ + + str[1] = (mode & S_IRUSR) ? 'r' : '-'; + str[2] = (mode & S_IWUSR) ? 'w' : '-'; + str[3] = (mode & S_IXUSR) ? 'x' : '-'; + str[4] = (mode & S_IRGRP) ? 'r' : '-'; + str[5] = (mode & S_IWGRP) ? 'w' : '-'; + str[6] = (mode & S_IXGRP) ? 'x' : '-'; + str[7] = (mode & S_IROTH) ? 'r' : '-'; + str[8] = (mode & S_IWOTH) ? 'w' : '-'; + str[9] = (mode & S_IXOTH) ? 'x' : '-'; + str[10] = '\0'; +} + +file_list * +fm_getdir(file_list *list, const char *path) +{ + DIR *dir = opendir(path); + if (!dir) { + log_write(LOG_ERR, "Cannot open directory"); + return NULL; + } + + file_list *new_list = list; + + if (new_list->items && new_list->cap > 0) { + LOG_WRITE_INFO("Initialize list"); + memset(new_list->items, 0, new_list->cap * sizeof(file_item)); + new_list->count = 0; + } + + struct dirent *d; + struct stat st; + char filepath[PATH_MAX]; + while ((d = readdir(dir)) != NULL) { + if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) + continue; + + if (new_list->count >= new_list->cap) + fm_file_items_realloc(new_list); + + snprintf(new_list->items[new_list->count].name, + sizeof(new_list->items[new_list->count].name), "%s", + d->d_name); + + if (d->d_type == DT_DIR) { + new_list->items[new_list->count].is_dir = true; + } else { + new_list->items[new_list->count].is_dir = false; + } + + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, sizeof(filepath), "%s/%s", path, d->d_name); + + if (lstat(filepath, &st) == 0) { + get_permission( + st.st_mode, + new_list->items[new_list->count].permission); + } + + new_list->count++; + } + + closedir(dir); + return new_list; +} + +static void +fm_file_list_init(file_list *l, int cap) +{ + if (!l) + die("File list initialization failed"); + + l->cap = cap; + l->count = 0; + l->items = xmalloc(sizeof(file_item) * cap); + l->items->is_dir = 0; + memset(l->items, 0, l->cap * sizeof(file_item)); + fm_getenv(); +} + +fm_info * +fm_init(terminal *t) +{ + fm_info *info = fm_w_init(t); + info->cursor = 0; + info->cur = xmalloc(sizeof(file_list)); + info->next = xmalloc(sizeof(file_list)); + fm_file_list_init(info->cur, 30); + fm_file_list_init(info->next, 30); + create_status_bar_win(t); + return info; +} + +static char * +detect_file_type(const char *filepath) +{ + if (!g_magic_ctx) + magic_init(); + + const char *desc = magic_file(g_magic_ctx, filepath); + return desc ? strdup(desc) : strdup("unknown"); +} + +void +fm_draw_file_stream(window *w, FILE *fp) +{ + char line_buf[512]; + int line_num = 0; + + while (line_num < w->height && + fgets(line_buf, sizeof(line_buf), fp) != NULL) { + mvwprintw(w->win, line_num, 0, "%.*s", w->width, line_buf); + line_num++; + } +} + +bool +fm_preview_file(window *w, const char *path) +{ + if (!w || !path) + return false; + + wclear(w->win); + + char *file_type = detect_file_type(path); + + if (strstr(file_type, "text") == NULL) { + mvwprintw(w->win, 0, 0, "%s", file_type); + free(file_type); + return false; + } + + FILE *fp = fopen(path, "r"); + if (!fp) { + mvwprintw(w->win, 0, 0, ""); + free(file_type); + return false; + } + + fm_draw_file_stream(w, fp); + + fclose(fp); + free(file_type); + return true; +} + +void +fm_draw_entries(window *window, file_list *list, size_t cursor, int top, + int bottom) +{ + if (!window || !list) + return; + + wclear(window->win); + + for (int i = top; i < bottom && (size_t)i < list->count; i++) { + int y = i - top; + + int color = 0; + if ((size_t)i == cursor) + color = COLOR_PAIR_FM_HL; + else if (list->items[i].is_dir) + color = COLOR_PAIR_FM_DIR; + + if (color != 0) { + wattron(window->win, COLOR_PAIR(color)); + } + + mvwprintw(window->win, y, 0, "%s", list->items[i].name); + + if (color != 0) { + wattroff(window->win, COLOR_PAIR(color)); + } + } +} + +void +fm_entries_scroll(fm_info *info, int n) +{ + if (!info) + return; + + info->cursor += n; + + if (info->cursor < 0) + info->cursor = 0; + if ((size_t)info->cursor >= info->cur->count) + info->cursor = info->cur->count - 1; + + int win_height = info->bottom - info->top; + + if (info->cursor >= info->bottom) { + info->bottom = info->cursor + 1; + info->top = info->bottom - win_height; + } + if (info->cursor < info->top) { + info->top = info->cursor; + info->bottom = info->top + win_height; + } + + need_redraw = true; +} + +static void +fm_reset_windows_data(fm_info *i) +{ + if (!i) + return; + + i->top = 0; + i->bottom = i->windows[FM_PANE_LEFT]->height - 1; +} +void +fm_cd_up(fm_info *info) +{ + if (strcmp(info->cwd, "/") == 0) + return; + + fm_reset_windows_data(info); + + char *cp_cwd = strdup(info->cwd); + if (!cp_cwd) + die("strdup failed"); + + char *parent_dir = dirname(cp_cwd); + + if (chdir(parent_dir) != 0) { + free(cp_cwd); + return; + } + + snprintf(info->cwd, sizeof(info->cwd), "%s", parent_dir); + free(cp_cwd); + + info->cur->cap = 30; + info->cur->count = 0; + + file_list *new_list = fm_getdir(info->cur, info->cwd); + if (new_list) { + info->cur = new_list; + } + + info->cursor = 0; + info->top = 0; + info->bottom = info->windows[FM_PANE_LEFT]->height; + need_redraw = true; +} + +void +fm_cd_down(fm_info *info) +{ + if (!info->cur || info->cur->count == 0) + return; + + if (!info->cur->items[info->cursor].is_dir) + return; + + fm_reset_windows_data(info); + + size_t buf_size = strlen(info->cwd) + 1 + + strlen(info->cur->items[info->cursor].name) + 1; + char *next_dir = xmalloc(buf_size); + + if (strcmp(info->cwd, "/") == 0) { + snprintf(next_dir, buf_size, "/%s", + info->cur->items[info->cursor].name); + } else { + snprintf(next_dir, buf_size, "%s/%s", info->cwd, + info->cur->items[info->cursor].name); + } + + if (chdir(next_dir) == 0) { + snprintf(info->cwd, sizeof(info->cwd), "%s", next_dir); + + info->cur->cap = 30; + info->cur->count = 0; + file_list *new_list = fm_getdir(info->cur, info->cwd); + if (new_list) { + info->cur = new_list; + } + + info->cursor = 0; + info->top = 0; + need_redraw = true; + } + + free(next_dir); +} + +void +magic_init(void) +{ + if (!g_magic_ctx) { + g_magic_ctx = magic_open(MAGIC_MIME_TYPE | MAGIC_SYMLINK); + if (!g_magic_ctx) + die("Cannot initialize magic"); + if (magic_load(g_magic_ctx, NULL) != 0) + die("Magic load failed: %s", magic_error(g_magic_ctx)); + } +} + +void +magic_cleanup(void) +{ + if (g_magic_ctx) + magic_close(g_magic_ctx); +} +static char * +xgetenv(const char *const name, char *fallback) +{ + char *value = getenv(name); + + return value && value[0] ? value : fallback; +} + +static void +fm_getenv(void) +{ + enveditor = xgetenv(envs[ENV_EDITOR], utils[UTIL_VI]); +} + +int +open_with_editor(char **argv) +{ + pid_t pid; + def_prog_mode(); + endwin(); + + if (!argv[0] || *argv[0] == '\0') { + argv[0] = utils[UTIL_VI]; + } + + if ((pid = fork()) < 0) { + log_write(LOG_ERR, "fork error"); + return -1; + } else if (pid == 0) { + if (execvp(argv[0], argv) < 0) { + log_write(LOG_ERR, "execvp error"); + return -1; + } + } else { + wait(NULL); + need_redraw = true; + } + + return 0; +} + +void +create_status_bar_win(terminal *t) +{ + wm_create_new_w(t, "status_bar", 1, t->maxx, t->maxy - 1, 0); +} + +void +draw_status_bar(window *bar_win, const char *cwd, char permission[11]) +{ + werase(bar_win->win); + wattron(bar_win->win, COLOR_PAIR(COLOR_PAIR_FM_STATUS_BAR)); + mvwprintw(bar_win->win, bar_win->height - 1, 0, "%s %s", cwd, + permission); + wattroff(bar_win->win, COLOR_PAIR(COLOR_PAIR_FM_STATUS_BAR)); +} -- cgit v1.2.3