#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 static magic_t g_magic_ctx = NULL; /* 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); 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; info->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); } file_list *fm_getdir(file_list *list, const char *path) { log_write(LOG_INFO, "path = %s", path); DIR *dir = opendir(path); if (!dir) { die("Cannot open dir"); } file_list *new_list = list; LOG_WRITE_INFO("list->cap = %zu", list->cap); 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; 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; } 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_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); 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) { wclear(w->win); if (!w || !path) return false; char *file_type = detect_file_type(path); if (strstr(file_type, "text") == NULL) { mvwprintw(w->win, 0, 0, "%s", file_type); return false; } FILE *fp = fopen(path, "r"); if (!fp) { mvwprintw(w->win, 0, 0, ""); 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; } info->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; LOG_WRITE_INFO("11: %s", info->cwd); } info->cursor = 0; info->top = 0; info->bottom = info->windows[FM_PANE_LEFT]->height; info->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; info->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); }