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/ --- Makefile | 2 +- dir.c | 57 -------- dir.h | 30 ---- display.c | 58 -------- display.h | 59 -------- err.c | 18 --- err.h | 8 - fm.c | 463 ---------------------------------------------------------- fm.h | 69 --------- keys.c | 67 --------- keys.h | 9 -- log.c | 61 -------- log.h | 21 --- main.c | 121 --------------- mem.c | 43 ------ mem.h | 10 -- src/dir.c | 57 ++++++++ src/dir.h | 30 ++++ src/display.c | 58 ++++++++ src/display.h | 59 ++++++++ src/err.c | 18 +++ src/err.h | 8 + src/fm.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/fm.h | 69 +++++++++ src/keys.c | 67 +++++++++ src/keys.h | 9 ++ src/log.c | 61 ++++++++ src/log.h | 21 +++ src/main.c | 121 +++++++++++++++ src/mem.c | 43 ++++++ src/mem.h | 10 ++ src/version.c | 16 ++ src/version.h | 13 ++ src/wm.c | 78 ++++++++++ src/wm.h | 30 ++++ version.c | 16 -- version.h | 13 -- wm.c | 78 ---------- wm.h | 30 ---- 39 files changed, 1232 insertions(+), 1232 deletions(-) delete mode 100644 dir.c delete mode 100644 dir.h delete mode 100644 display.c delete mode 100644 display.h delete mode 100644 err.c delete mode 100644 err.h delete mode 100644 fm.c delete mode 100644 fm.h delete mode 100644 keys.c delete mode 100644 keys.h delete mode 100644 log.c delete mode 100644 log.h delete mode 100644 main.c delete mode 100644 mem.c delete mode 100644 mem.h create mode 100644 src/dir.c create mode 100644 src/dir.h create mode 100644 src/display.c create mode 100644 src/display.h create mode 100644 src/err.c create mode 100644 src/err.h create mode 100644 src/fm.c create mode 100644 src/fm.h create mode 100644 src/keys.c create mode 100644 src/keys.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/main.c create mode 100644 src/mem.c create mode 100644 src/mem.h create mode 100644 src/version.c create mode 100644 src/version.h create mode 100644 src/wm.c create mode 100644 src/wm.h delete mode 100644 version.c delete mode 100644 version.h delete mode 100644 wm.c delete mode 100644 wm.h diff --git a/Makefile b/Makefile index 1ef292e..9708e42 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CFLAGS=-Wall -Wextra -Wpedantic -Wstrict-prototypes -Wmissing-prototypes -g LDFLAGS=-lncurses -lmagic TARGET=sf -SRCS=$(wildcard *.c) +SRCS=$(wildcard src/*.c) OBJS=$(SRCS:.c=.o) TEST_SRC=test/fm_draw_entries.c diff --git a/dir.c b/dir.c deleted file mode 100644 index d79d804..0000000 --- a/dir.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Ported/adapted from nnn (https://github.com/jarun/nnn): xdirname(), - * xbasename() - * - * Copyright (C) 2014-2016, Lazaros Koromilas - * Copyright (C) 2014-2016, Dimitris Papastamos - * Copyright (C) 2016-2026, Arun Prakash Jana - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define _GNU_SOURCE -#include "dir.h" -#include "log.h" -#include -#include -#include - -char *xdirname(char *path) -{ - char *base = memrchr((unsigned char *)path, '/', strlen(path)); - - if (base == path) - path[1] = '\0'; - else - *base = '\0'; - - return path; -} - -char *xbasename(char *path) -{ - char *base = memrchr((unsigned char *)path, '/', strlen(path)); - - return base ? base + 1 : path; -} diff --git a/dir.h b/dir.h deleted file mode 100644 index 7c576b4..0000000 --- a/dir.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef SF_DIR_H -#define SF_DIR_H - -#include -#include -#include - -#include "wm.h" - -/* typedef struct { */ -/* char name[256]; */ -/* bool is_dir; */ -/* } file_entry; */ - -/* typedef struct { */ -/* char path[PATH_MAX]; */ -/* directory dir; */ -/* size_t cursor; */ -/* file_entry *entries; */ -/* size_t count; */ -/* size_t capacity; */ -/* } dir_stat; */ - -char *xdirname(char *path); -char *xbasename(char *path); -/* directory *dir_read(directory *dir, const char *path); */ - -/* void dir_free(directory *dir); */ - -#endif /* SF_DIR_H */ diff --git a/display.c b/display.c deleted file mode 100644 index e7dd031..0000000 --- a/display.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "display.h" - -#include - -#include "err.h" -#include "log.h" -#include "version.h" -#include "wm.h" - -void -color_pair_init(void) -{ - init_pair(COLOR_PAIR_FM_HL, COLOR_BLACK, COLOR_WHITE); - init_pair(COLOR_PAIR_FM_DIR, COLOR_CYAN, COLOR_BLACK); - init_pair(COLOR_PAIR_FM_STATUS_BAR, COLOR_BLUE, COLOR_BLACK); -} - -terminal * -ncurses_init(void) -{ - initscr(); - raw(); - curs_set(0); - noecho(); - if (!has_colors()) { - log_write(LOG_WARN, "Terminal does not support colors"); - } - - start_color(); - color_pair_init(); - terminal *t = wm_init(); - keypad(ROOT_WIN, TRUE); - return t; -} - -void -wprintw_colorful(WINDOW *w, int attr, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - wattron(w, COLOR_PAIR(attr)); - vw_printw(w, format, ap); - wattroff(w, COLOR_PAIR(attr)); - va_end(ap); -} - -void -handle_resize(void) -{ - endwin(); - refresh(); -} - -void -redraw(void) -{ - erase(); -} diff --git a/display.h b/display.h deleted file mode 100644 index ac405e7..0000000 --- a/display.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef SF_DISPLAY_H -#define SF_DISPLAY_H - -#include -#include - -#include "dir.h" -#include "log.h" -#include "mem.h" - -#define COLOR_PAIR_FM_HL 1 -#define COLOR_PAIR_FM_DIR 2 -#define COLOR_PAIR_FM_STATUS_BAR 3 - -#define STAT_INFO_WIDTH 15 -#define BAR_LABEL_WIDTH 7 -#define STAT_BAR_HEIGHT 1 - -#define FM_PANE_LEFT 0 -#define FM_PANE_RIGHT 1 - -#include "wm.h" - -typedef struct { - size_t top; - size_t bottom; - size_t height; - size_t widght; - size_t margin; - bool need_redraw; - window *left; - window *right; -} view_stat; - -void color_pair_init(void); -terminal *ncurses_init(void); - -static inline WINDOW * -get_root_win(const terminal *t) -{ - if (!t) { - log_write(LOG_ERR, "get_root_win: terminal is NULL"); - return NULL; - } - if (t->w_count <= 0 || !t->windows[0]) { - log_write(LOG_ERR, "get_root_win: windows[0] is not allocated"); - return NULL; - } - return t->windows[0]->win; -} - -#define ROOT_WIN get_root_win(t) - -void draw_stat_bar(terminal *t, const char *sys_version); -void wprintw_colorful(WINDOW *w, int attr, const char *format, ...); -void handle_resize(void); -void redraw(void); - -#endif /* SF_DISPLAY_H */ diff --git a/err.c b/err.c deleted file mode 100644 index 2a5859f..0000000 --- a/err.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "err.h" - -#include -#include -#include -#include - -#include "log.h" - -void die(const char *err, ...) -{ - va_list p; - va_start(p, err); - log_write(LOG_ERR, err, p); - va_end(p); - endwin(); - exit(128); -} diff --git a/err.h b/err.h deleted file mode 100644 index 6bb4697..0000000 --- a/err.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef SS_ERR_H -#define SS_ERR_H - -#include - -void die(const char* err, ...); - -#endif /* SS_ERR_H */ diff --git a/fm.c b/fm.c deleted file mode 100644 index 92526c3..0000000 --- a/fm.c +++ /dev/null @@ -1,463 +0,0 @@ -#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)); -} diff --git a/fm.h b/fm.h deleted file mode 100644 index 493d1f9..0000000 --- a/fm.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef SF_FM_H -#define SF_FM_H - -#include -#include -#include -#include -#include - -#include "wm.h" - -#define PANE_LEFT 0 -#define PANE_RIGHT 1 - -#define MARGIN 3 - -typedef struct { - char name[256]; - int is_dir; - char permission[11]; -} file_item; - -typedef struct { - file_item *items; - size_t count; - size_t cap; -} file_list; - -typedef struct { - file_list *cur; - file_list *next; - int cursor; - window *windows[2]; - /* int got_next_dir; */ - char got_dir[PATH_MAX]; - char cwd[PATH_MAX]; - int top, bottom; -} fm_info; - -extern bool need_redraw; - -#define UTIL_VI 0 -#define UTIL_XDG_OPEN 1 - -#define ENV_SHELL 0 -#define ENV_EDITOR 1 -#define ENV_SF 2 - -static char *enveditor; - -file_list *fm_getdir(file_list *list, const char *path); -fm_info *fm_init(terminal *t); -void fm_getcwd(fm_info *info); -void fm_draw_entries(window *window, file_list *list, size_t cursor, int top, - int bottom); -void fm_entries_scroll(fm_info *info, int n); -void fm_draw_file_stream(window *w, FILE *fp); -/* void fm_draw_right_window(fm_info *i); */ - -void fm_cd_up(fm_info *i); -void fm_cd_down(fm_info *i); -bool fm_preview_file(window *w, const char *path); -int open_with_editor(char **argv); -void magic_init(void); -void magic_cleanup(void); -void create_status_bar_win(terminal *t); -void draw_status_bar(window *bar_win, const char *cwd, char permission[11]); - -#endif /* SF_FM_H */ diff --git a/keys.c b/keys.c deleted file mode 100644 index 8fa11c6..0000000 --- a/keys.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "keys.h" - -#include -#include -#include -#include - -#include "display.h" -#include "err.h" -#include "fm.h" -#include "log.h" -#include "mem.h" -#include "wm.h" - -void handling_keys(int ch, fm_info *i) -{ - switch (ch) { -#ifdef KEY_RESIZE - case KEY_RESIZE: - handle_resize(); - /* TODO: redraw ui */ - break; -#endif - /* Quit */ - case 'q': - endwin(); - exit(0); - /* Down */ - case 'j': - fm_entries_scroll(i, 1); - break; - case 'k': - fm_entries_scroll(i, -1); - break; - case 'h': - fm_cd_up(i); - break; - case 'l': - fm_cd_down(i); - break; - case 'o': - case '\n': - case '\r': - case KEY_ENTER: - log_write(LOG_INFO, "Open file"); - if (!(i->cur->items[i->cursor].is_dir)) { - char filepath[PATH_MAX]; - size_t len = - snprintf(filepath, PATH_MAX, "%s/%s", i->cwd, - i->cur->items[i->cursor].name); - if (len >= PATH_MAX) { - die("snprintf failed"); - } - - char *argv[] = {enveditor, filepath, NULL}; - open_with_editor(argv); - break; - } else { - return; - } - break; - default: - return; - } - - need_redraw = true; -} diff --git a/keys.h b/keys.h deleted file mode 100644 index 4ca2502..0000000 --- a/keys.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef SF_KEYS_H -#define SF_KEYS_H - -#include "fm.h" -#include "wm.h" - -void handling_keys(int ch, fm_info *i); - -#endif /* SF_KEYS_H */ diff --git a/log.c b/log.c deleted file mode 100644 index 436307b..0000000 --- a/log.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "log.h" - -#include -#include -#include -#include -#include - -#include "err.h" - -FILE *log_file = NULL; - -void -log_init(void) -{ - log_file = fopen(LOG_FILEPATH, "a"); - - if (!log_file) { - die("Faild to open log file"); - } -} - -void -log_write(log_level level, const char *fmt, ...) -{ - char *level_str; - - if (level == LOG_INFO) - level_str = "INFO"; - if (level == LOG_WARN) - level_str = "WARN"; - if (level == LOG_ERR) - level_str = "ERR"; - - time_t rawtime; - struct tm *timeinfo; - char time_str[32]; - - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo); - - fprintf(log_file, "[%s] [%s] ", time_str, level_str); - - va_list ap; - va_start(ap, fmt); - vfprintf(log_file, fmt, ap); - va_end(ap); - - fprintf(log_file, "\n"); - fflush(log_file); -} - -void -log_end(FILE *log_file) -{ - if (log_file) { - fclose(log_file); - log_file = NULL; - } -} diff --git a/log.h b/log.h deleted file mode 100644 index 0b5a6ab..0000000 --- a/log.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SS_LOG_H -#define SS_LOG_H - -#include - -#define LOG_FILEPATH "./log" - -typedef enum { - LOG_INFO, - LOG_WARN, - LOG_ERR, -} log_level; - -void log_init(void); - -void log_write(log_level level, const char *fmt, ...); - -void log_end(FILE *log_file); -#define LOG_WRITE_INFO(fmt, ...) log_write(LOG_INFO, fmt, ##__VA_ARGS__) - -#endif /* SS_LOG_H */ diff --git a/main.c b/main.c deleted file mode 100644 index ba19c05..0000000 --- a/main.c +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dir.h" -#include "display.h" -#include "err.h" -#include "fm.h" -#include "keys.h" -#include "log.h" -#include "mem.h" -#include "version.h" -#include "wm.h" - -static void -path_normalize(char *path) -{ - if (!path) - return; - - char new_path[PATH_MAX]; - if (realpath(path, new_path)) { - path = new_path; - } else { - return; - } - - return; -} - -int -main(int argc, char **argv) -{ - if (argc > 1 && - (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0)) - version(); - - int ch; - log_init(); - - terminal *term = ncurses_init(); - - fm_info *info = fm_init(term); - int stat_bar_win_index = wm_fd_w_by_name(term, "status_bar"); - if (stat_bar_win_index == -1) { - log_write(LOG_WARN, "Cannot find window: status_bar"); - } - need_redraw = true; - if (argc == 2) { - snprintf(info->cwd, sizeof(info->cwd), "%s", argv[1]); - path_normalize(info->cwd); - } else { - fm_getcwd(info); - } - - info->cur = fm_getdir(info->cur, info->cwd); - magic_init(); - char next_path[PATH_MAX]; - timeout(100); - while (1) { - if (!need_redraw) - continue; - - fm_draw_entries(info->windows[FM_PANE_LEFT], info->cur, - info->cursor, info->top, info->bottom); - - if (!info->cur->items[info->cursor].is_dir) { - size_t len = snprintf( - next_path, sizeof(next_path), "%s/%s", info->cwd, - info->cur->items[info->cursor].name); - if (len >= sizeof(next_path)) { - die("Length of items error"); - } - fm_preview_file(info->windows[FM_PANE_RIGHT], - next_path); - } else { - size_t len = snprintf( - next_path, sizeof(next_path), "%s/%s", info->cwd, - info->cur->items[info->cursor].name); - - if (len >= sizeof(next_path)) { - die("Length of items error"); - } - file_list *new_list = info->next; - new_list = fm_getdir(new_list, next_path); - if (new_list) { - info->next = new_list; - int right_height = - info->windows[FM_PANE_RIGHT]->height; - fm_draw_entries(info->windows[FM_PANE_RIGHT], - info->next, -1, 0, - right_height); - } - } - - draw_status_bar(term->windows[stat_bar_win_index], info->cwd, - info->cur->items[info->cursor].permission); - - wrefresh(info->windows[FM_PANE_RIGHT]->win); - wrefresh(info->windows[FM_PANE_LEFT]->win); - wrefresh(term->windows[stat_bar_win_index]->win); - - ch = getch(); - handling_keys(ch, info); - } - - magic_cleanup(); - endwin(); - - return 0; -} diff --git a/mem.c b/mem.c deleted file mode 100644 index c3d5db3..0000000 --- a/mem.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "mem.h" - -#include -#include -#include - -#include "err.h" -#include "log.h" - -void *xmalloc(size_t size) -{ - void *rtn = malloc(size); - if (!rtn) { - die("Out of memory"); - } - return rtn; -} - -void *xrealloc(void *p, size_t size) -{ - void *pmem = realloc(p, size); - - if (!pmem) - free(p); - - return pmem; -} - -void free_pointer_array(void **array, size_t len) -{ - if (!array) { - log_write(LOG_WARN, "free_poiner_array: array is NULL"); - return; - } - - for (size_t i = 0; i < len; i++) { - if (array[i]) { - free(array[i]); - } - } - - free(array); -} diff --git a/mem.h b/mem.h deleted file mode 100644 index bcc112f..0000000 --- a/mem.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SS_MEM_H -#define SS_MEM_H - -#include - -void *xmalloc(size_t size); -void *xrealloc(void *p, size_t size); -void free_pointer_array(void **array, size_t len); - -#endif /* SS_MEM_H */ diff --git a/src/dir.c b/src/dir.c new file mode 100644 index 0000000..d79d804 --- /dev/null +++ b/src/dir.c @@ -0,0 +1,57 @@ +/* + * Ported/adapted from nnn (https://github.com/jarun/nnn): xdirname(), + * xbasename() + * + * Copyright (C) 2014-2016, Lazaros Koromilas + * Copyright (C) 2014-2016, Dimitris Papastamos + * Copyright (C) 2016-2026, Arun Prakash Jana + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define _GNU_SOURCE +#include "dir.h" +#include "log.h" +#include +#include +#include + +char *xdirname(char *path) +{ + char *base = memrchr((unsigned char *)path, '/', strlen(path)); + + if (base == path) + path[1] = '\0'; + else + *base = '\0'; + + return path; +} + +char *xbasename(char *path) +{ + char *base = memrchr((unsigned char *)path, '/', strlen(path)); + + return base ? base + 1 : path; +} diff --git a/src/dir.h b/src/dir.h new file mode 100644 index 0000000..7c576b4 --- /dev/null +++ b/src/dir.h @@ -0,0 +1,30 @@ +#ifndef SF_DIR_H +#define SF_DIR_H + +#include +#include +#include + +#include "wm.h" + +/* typedef struct { */ +/* char name[256]; */ +/* bool is_dir; */ +/* } file_entry; */ + +/* typedef struct { */ +/* char path[PATH_MAX]; */ +/* directory dir; */ +/* size_t cursor; */ +/* file_entry *entries; */ +/* size_t count; */ +/* size_t capacity; */ +/* } dir_stat; */ + +char *xdirname(char *path); +char *xbasename(char *path); +/* directory *dir_read(directory *dir, const char *path); */ + +/* void dir_free(directory *dir); */ + +#endif /* SF_DIR_H */ diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..e7dd031 --- /dev/null +++ b/src/display.c @@ -0,0 +1,58 @@ +#include "display.h" + +#include + +#include "err.h" +#include "log.h" +#include "version.h" +#include "wm.h" + +void +color_pair_init(void) +{ + init_pair(COLOR_PAIR_FM_HL, COLOR_BLACK, COLOR_WHITE); + init_pair(COLOR_PAIR_FM_DIR, COLOR_CYAN, COLOR_BLACK); + init_pair(COLOR_PAIR_FM_STATUS_BAR, COLOR_BLUE, COLOR_BLACK); +} + +terminal * +ncurses_init(void) +{ + initscr(); + raw(); + curs_set(0); + noecho(); + if (!has_colors()) { + log_write(LOG_WARN, "Terminal does not support colors"); + } + + start_color(); + color_pair_init(); + terminal *t = wm_init(); + keypad(ROOT_WIN, TRUE); + return t; +} + +void +wprintw_colorful(WINDOW *w, int attr, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + wattron(w, COLOR_PAIR(attr)); + vw_printw(w, format, ap); + wattroff(w, COLOR_PAIR(attr)); + va_end(ap); +} + +void +handle_resize(void) +{ + endwin(); + refresh(); +} + +void +redraw(void) +{ + erase(); +} diff --git a/src/display.h b/src/display.h new file mode 100644 index 0000000..ac405e7 --- /dev/null +++ b/src/display.h @@ -0,0 +1,59 @@ +#ifndef SF_DISPLAY_H +#define SF_DISPLAY_H + +#include +#include + +#include "dir.h" +#include "log.h" +#include "mem.h" + +#define COLOR_PAIR_FM_HL 1 +#define COLOR_PAIR_FM_DIR 2 +#define COLOR_PAIR_FM_STATUS_BAR 3 + +#define STAT_INFO_WIDTH 15 +#define BAR_LABEL_WIDTH 7 +#define STAT_BAR_HEIGHT 1 + +#define FM_PANE_LEFT 0 +#define FM_PANE_RIGHT 1 + +#include "wm.h" + +typedef struct { + size_t top; + size_t bottom; + size_t height; + size_t widght; + size_t margin; + bool need_redraw; + window *left; + window *right; +} view_stat; + +void color_pair_init(void); +terminal *ncurses_init(void); + +static inline WINDOW * +get_root_win(const terminal *t) +{ + if (!t) { + log_write(LOG_ERR, "get_root_win: terminal is NULL"); + return NULL; + } + if (t->w_count <= 0 || !t->windows[0]) { + log_write(LOG_ERR, "get_root_win: windows[0] is not allocated"); + return NULL; + } + return t->windows[0]->win; +} + +#define ROOT_WIN get_root_win(t) + +void draw_stat_bar(terminal *t, const char *sys_version); +void wprintw_colorful(WINDOW *w, int attr, const char *format, ...); +void handle_resize(void); +void redraw(void); + +#endif /* SF_DISPLAY_H */ diff --git a/src/err.c b/src/err.c new file mode 100644 index 0000000..2a5859f --- /dev/null +++ b/src/err.c @@ -0,0 +1,18 @@ +#include "err.h" + +#include +#include +#include +#include + +#include "log.h" + +void die(const char *err, ...) +{ + va_list p; + va_start(p, err); + log_write(LOG_ERR, err, p); + va_end(p); + endwin(); + exit(128); +} diff --git a/src/err.h b/src/err.h new file mode 100644 index 0000000..6bb4697 --- /dev/null +++ b/src/err.h @@ -0,0 +1,8 @@ +#ifndef SS_ERR_H +#define SS_ERR_H + +#include + +void die(const char* err, ...); + +#endif /* SS_ERR_H */ 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)); +} diff --git a/src/fm.h b/src/fm.h new file mode 100644 index 0000000..493d1f9 --- /dev/null +++ b/src/fm.h @@ -0,0 +1,69 @@ +#ifndef SF_FM_H +#define SF_FM_H + +#include +#include +#include +#include +#include + +#include "wm.h" + +#define PANE_LEFT 0 +#define PANE_RIGHT 1 + +#define MARGIN 3 + +typedef struct { + char name[256]; + int is_dir; + char permission[11]; +} file_item; + +typedef struct { + file_item *items; + size_t count; + size_t cap; +} file_list; + +typedef struct { + file_list *cur; + file_list *next; + int cursor; + window *windows[2]; + /* int got_next_dir; */ + char got_dir[PATH_MAX]; + char cwd[PATH_MAX]; + int top, bottom; +} fm_info; + +extern bool need_redraw; + +#define UTIL_VI 0 +#define UTIL_XDG_OPEN 1 + +#define ENV_SHELL 0 +#define ENV_EDITOR 1 +#define ENV_SF 2 + +static char *enveditor; + +file_list *fm_getdir(file_list *list, const char *path); +fm_info *fm_init(terminal *t); +void fm_getcwd(fm_info *info); +void fm_draw_entries(window *window, file_list *list, size_t cursor, int top, + int bottom); +void fm_entries_scroll(fm_info *info, int n); +void fm_draw_file_stream(window *w, FILE *fp); +/* void fm_draw_right_window(fm_info *i); */ + +void fm_cd_up(fm_info *i); +void fm_cd_down(fm_info *i); +bool fm_preview_file(window *w, const char *path); +int open_with_editor(char **argv); +void magic_init(void); +void magic_cleanup(void); +void create_status_bar_win(terminal *t); +void draw_status_bar(window *bar_win, const char *cwd, char permission[11]); + +#endif /* SF_FM_H */ diff --git a/src/keys.c b/src/keys.c new file mode 100644 index 0000000..8fa11c6 --- /dev/null +++ b/src/keys.c @@ -0,0 +1,67 @@ +#include "keys.h" + +#include +#include +#include +#include + +#include "display.h" +#include "err.h" +#include "fm.h" +#include "log.h" +#include "mem.h" +#include "wm.h" + +void handling_keys(int ch, fm_info *i) +{ + switch (ch) { +#ifdef KEY_RESIZE + case KEY_RESIZE: + handle_resize(); + /* TODO: redraw ui */ + break; +#endif + /* Quit */ + case 'q': + endwin(); + exit(0); + /* Down */ + case 'j': + fm_entries_scroll(i, 1); + break; + case 'k': + fm_entries_scroll(i, -1); + break; + case 'h': + fm_cd_up(i); + break; + case 'l': + fm_cd_down(i); + break; + case 'o': + case '\n': + case '\r': + case KEY_ENTER: + log_write(LOG_INFO, "Open file"); + if (!(i->cur->items[i->cursor].is_dir)) { + char filepath[PATH_MAX]; + size_t len = + snprintf(filepath, PATH_MAX, "%s/%s", i->cwd, + i->cur->items[i->cursor].name); + if (len >= PATH_MAX) { + die("snprintf failed"); + } + + char *argv[] = {enveditor, filepath, NULL}; + open_with_editor(argv); + break; + } else { + return; + } + break; + default: + return; + } + + need_redraw = true; +} diff --git a/src/keys.h b/src/keys.h new file mode 100644 index 0000000..4ca2502 --- /dev/null +++ b/src/keys.h @@ -0,0 +1,9 @@ +#ifndef SF_KEYS_H +#define SF_KEYS_H + +#include "fm.h" +#include "wm.h" + +void handling_keys(int ch, fm_info *i); + +#endif /* SF_KEYS_H */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..436307b --- /dev/null +++ b/src/log.c @@ -0,0 +1,61 @@ +#include "log.h" + +#include +#include +#include +#include +#include + +#include "err.h" + +FILE *log_file = NULL; + +void +log_init(void) +{ + log_file = fopen(LOG_FILEPATH, "a"); + + if (!log_file) { + die("Faild to open log file"); + } +} + +void +log_write(log_level level, const char *fmt, ...) +{ + char *level_str; + + if (level == LOG_INFO) + level_str = "INFO"; + if (level == LOG_WARN) + level_str = "WARN"; + if (level == LOG_ERR) + level_str = "ERR"; + + time_t rawtime; + struct tm *timeinfo; + char time_str[32]; + + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo); + + fprintf(log_file, "[%s] [%s] ", time_str, level_str); + + va_list ap; + va_start(ap, fmt); + vfprintf(log_file, fmt, ap); + va_end(ap); + + fprintf(log_file, "\n"); + fflush(log_file); +} + +void +log_end(FILE *log_file) +{ + if (log_file) { + fclose(log_file); + log_file = NULL; + } +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..0b5a6ab --- /dev/null +++ b/src/log.h @@ -0,0 +1,21 @@ +#ifndef SS_LOG_H +#define SS_LOG_H + +#include + +#define LOG_FILEPATH "./log" + +typedef enum { + LOG_INFO, + LOG_WARN, + LOG_ERR, +} log_level; + +void log_init(void); + +void log_write(log_level level, const char *fmt, ...); + +void log_end(FILE *log_file); +#define LOG_WRITE_INFO(fmt, ...) log_write(LOG_INFO, fmt, ##__VA_ARGS__) + +#endif /* SS_LOG_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ba19c05 --- /dev/null +++ b/src/main.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dir.h" +#include "display.h" +#include "err.h" +#include "fm.h" +#include "keys.h" +#include "log.h" +#include "mem.h" +#include "version.h" +#include "wm.h" + +static void +path_normalize(char *path) +{ + if (!path) + return; + + char new_path[PATH_MAX]; + if (realpath(path, new_path)) { + path = new_path; + } else { + return; + } + + return; +} + +int +main(int argc, char **argv) +{ + if (argc > 1 && + (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0)) + version(); + + int ch; + log_init(); + + terminal *term = ncurses_init(); + + fm_info *info = fm_init(term); + int stat_bar_win_index = wm_fd_w_by_name(term, "status_bar"); + if (stat_bar_win_index == -1) { + log_write(LOG_WARN, "Cannot find window: status_bar"); + } + need_redraw = true; + if (argc == 2) { + snprintf(info->cwd, sizeof(info->cwd), "%s", argv[1]); + path_normalize(info->cwd); + } else { + fm_getcwd(info); + } + + info->cur = fm_getdir(info->cur, info->cwd); + magic_init(); + char next_path[PATH_MAX]; + timeout(100); + while (1) { + if (!need_redraw) + continue; + + fm_draw_entries(info->windows[FM_PANE_LEFT], info->cur, + info->cursor, info->top, info->bottom); + + if (!info->cur->items[info->cursor].is_dir) { + size_t len = snprintf( + next_path, sizeof(next_path), "%s/%s", info->cwd, + info->cur->items[info->cursor].name); + if (len >= sizeof(next_path)) { + die("Length of items error"); + } + fm_preview_file(info->windows[FM_PANE_RIGHT], + next_path); + } else { + size_t len = snprintf( + next_path, sizeof(next_path), "%s/%s", info->cwd, + info->cur->items[info->cursor].name); + + if (len >= sizeof(next_path)) { + die("Length of items error"); + } + file_list *new_list = info->next; + new_list = fm_getdir(new_list, next_path); + if (new_list) { + info->next = new_list; + int right_height = + info->windows[FM_PANE_RIGHT]->height; + fm_draw_entries(info->windows[FM_PANE_RIGHT], + info->next, -1, 0, + right_height); + } + } + + draw_status_bar(term->windows[stat_bar_win_index], info->cwd, + info->cur->items[info->cursor].permission); + + wrefresh(info->windows[FM_PANE_RIGHT]->win); + wrefresh(info->windows[FM_PANE_LEFT]->win); + wrefresh(term->windows[stat_bar_win_index]->win); + + ch = getch(); + handling_keys(ch, info); + } + + magic_cleanup(); + endwin(); + + return 0; +} diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..c3d5db3 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,43 @@ +#include "mem.h" + +#include +#include +#include + +#include "err.h" +#include "log.h" + +void *xmalloc(size_t size) +{ + void *rtn = malloc(size); + if (!rtn) { + die("Out of memory"); + } + return rtn; +} + +void *xrealloc(void *p, size_t size) +{ + void *pmem = realloc(p, size); + + if (!pmem) + free(p); + + return pmem; +} + +void free_pointer_array(void **array, size_t len) +{ + if (!array) { + log_write(LOG_WARN, "free_poiner_array: array is NULL"); + return; + } + + for (size_t i = 0; i < len; i++) { + if (array[i]) { + free(array[i]); + } + } + + free(array); +} diff --git a/src/mem.h b/src/mem.h new file mode 100644 index 0000000..bcc112f --- /dev/null +++ b/src/mem.h @@ -0,0 +1,10 @@ +#ifndef SS_MEM_H +#define SS_MEM_H + +#include + +void *xmalloc(size_t size); +void *xrealloc(void *p, size_t size); +void free_pointer_array(void **array, size_t len); + +#endif /* SS_MEM_H */ diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..01feb76 --- /dev/null +++ b/src/version.c @@ -0,0 +1,16 @@ +#include "./version.h" +#include +#include +void version(void) +{ + printf(PROGRAM_NAME + " " PROGRAM_VERSION " (built " BUILD_DATE + " with gcc " GCC_VERSION ")\n" + "Copyright (C) 2026 " PROGRAM_AUTHOR ".\n" + "License GPLv3+: GNU GPL version 3 or later " + ".\n" + "This is free software: you are free to change and redistribute " + "it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"); + exit(EXIT_SUCCESS); +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..23d5a09 --- /dev/null +++ b/src/version.h @@ -0,0 +1,13 @@ +#ifndef SF_VERSION_H +#define SF_VERSION_H + +#define PROGRAM_NAME "sf" +#define PROGRAM_LONG_NAME "Status & Files" +#define PROGRAM_VERSION "0.0.1" +#define PROGRAM_AUTHOR "Verdant " +#define BUILD_DATE __DATE__ +#define GCC_VERSION __VERSION__ + +void version(void); + +#endif /* SF_VERSION_H */ diff --git a/src/wm.c b/src/wm.c new file mode 100644 index 0000000..f25c6ba --- /dev/null +++ b/src/wm.c @@ -0,0 +1,78 @@ +#include "wm.h" + +#include +#include + +#include "err.h" +#include "log.h" +#include "mem.h" + +window *wm_create_new_w(terminal *t, const char *name, int height, int width, + int start_y, int start_x) +{ + if (t->w_count >= WINDOWS_MAX) { + die("Windows count has arrived max"); + } + + WINDOW *win; + win = derwin(t->windows[0]->win, height, width, start_y, start_x); + + int current_idx = t->w_count; + + strncpy(t->windows[current_idx]->name, name, 31); + t->windows[current_idx]->win = win; + t->windows[current_idx]->start_y = start_y; + t->windows[current_idx]->start_x = start_x; + t->windows[current_idx]->width = width; + t->windows[current_idx]->height = height; + + t->w_count++; + + return t->windows[current_idx]; +} + +terminal *wm_init(void) +{ + terminal *t = xmalloc(sizeof(terminal)); + t->windows = xmalloc(sizeof(window *) * WINDOWS_MAX); + for (int i = 0; i < WINDOWS_MAX; i++) { + t->windows[i] = xmalloc(sizeof(window)); + } + t->w_count = 0; + getmaxyx(stdscr, t->maxy, t->maxx); + t->windows[0]->win = newwin(t->maxy, t->maxx, 0, 0); + t->windows[t->w_count]->start_y = 0; + t->windows[t->w_count]->start_x = 0; + t->windows[t->w_count]->height = t->maxy; + t->windows[t->w_count]->width = t->maxx; + t->w_count++; + + return t; +} + +int wm_fd_w_by_name(terminal *t, const char *name) +{ + log_write(LOG_INFO, "w_count = %d", t->w_count); + for (int i = 0; i < t->w_count; i++) { + if (strcmp(t->windows[i]->name, name) != 0) + continue; + return i; + } + + return -1; +} + +void wm_refresh_all(terminal *t) +{ + if (t->windows[0] && t->windows[0]->win) { + touchwin(t->windows[0]->win); + } + + for (int i = 1; i < t->w_count; i++) { + if (t->windows[i] && t->windows[i]->win) { + wnoutrefresh(t->windows[i]->win); + } + } + + doupdate(); +} diff --git a/src/wm.h b/src/wm.h new file mode 100644 index 0000000..713f2cb --- /dev/null +++ b/src/wm.h @@ -0,0 +1,30 @@ +#ifndef SF_WM_H +#define SF_WM_H + +#include + +#define WINDOWS_MAX 15 + +typedef struct { + WINDOW *win; + char name[32]; + int height; + int width; + int start_x; + int start_y; +} window; + +typedef struct { + window **windows; /* windows[0] is root_win */ + int w_count; /* index of windows */ + int maxy; + int maxx; +} terminal; + +terminal *wm_init(void); +window *wm_create_new_w(terminal *t, const char *name, int height, int width, + int start_y, int start_x); +int wm_fd_w_by_name(terminal *t, const char *name); +void wm_refresh_all(terminal *t); + +#endif /* SF_WM_H */ diff --git a/version.c b/version.c deleted file mode 100644 index 01feb76..0000000 --- a/version.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "./version.h" -#include -#include -void version(void) -{ - printf(PROGRAM_NAME - " " PROGRAM_VERSION " (built " BUILD_DATE - " with gcc " GCC_VERSION ")\n" - "Copyright (C) 2026 " PROGRAM_AUTHOR ".\n" - "License GPLv3+: GNU GPL version 3 or later " - ".\n" - "This is free software: you are free to change and redistribute " - "it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n"); - exit(EXIT_SUCCESS); -} diff --git a/version.h b/version.h deleted file mode 100644 index 23d5a09..0000000 --- a/version.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SF_VERSION_H -#define SF_VERSION_H - -#define PROGRAM_NAME "sf" -#define PROGRAM_LONG_NAME "Status & Files" -#define PROGRAM_VERSION "0.0.1" -#define PROGRAM_AUTHOR "Verdant " -#define BUILD_DATE __DATE__ -#define GCC_VERSION __VERSION__ - -void version(void); - -#endif /* SF_VERSION_H */ diff --git a/wm.c b/wm.c deleted file mode 100644 index f25c6ba..0000000 --- a/wm.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "wm.h" - -#include -#include - -#include "err.h" -#include "log.h" -#include "mem.h" - -window *wm_create_new_w(terminal *t, const char *name, int height, int width, - int start_y, int start_x) -{ - if (t->w_count >= WINDOWS_MAX) { - die("Windows count has arrived max"); - } - - WINDOW *win; - win = derwin(t->windows[0]->win, height, width, start_y, start_x); - - int current_idx = t->w_count; - - strncpy(t->windows[current_idx]->name, name, 31); - t->windows[current_idx]->win = win; - t->windows[current_idx]->start_y = start_y; - t->windows[current_idx]->start_x = start_x; - t->windows[current_idx]->width = width; - t->windows[current_idx]->height = height; - - t->w_count++; - - return t->windows[current_idx]; -} - -terminal *wm_init(void) -{ - terminal *t = xmalloc(sizeof(terminal)); - t->windows = xmalloc(sizeof(window *) * WINDOWS_MAX); - for (int i = 0; i < WINDOWS_MAX; i++) { - t->windows[i] = xmalloc(sizeof(window)); - } - t->w_count = 0; - getmaxyx(stdscr, t->maxy, t->maxx); - t->windows[0]->win = newwin(t->maxy, t->maxx, 0, 0); - t->windows[t->w_count]->start_y = 0; - t->windows[t->w_count]->start_x = 0; - t->windows[t->w_count]->height = t->maxy; - t->windows[t->w_count]->width = t->maxx; - t->w_count++; - - return t; -} - -int wm_fd_w_by_name(terminal *t, const char *name) -{ - log_write(LOG_INFO, "w_count = %d", t->w_count); - for (int i = 0; i < t->w_count; i++) { - if (strcmp(t->windows[i]->name, name) != 0) - continue; - return i; - } - - return -1; -} - -void wm_refresh_all(terminal *t) -{ - if (t->windows[0] && t->windows[0]->win) { - touchwin(t->windows[0]->win); - } - - for (int i = 1; i < t->w_count; i++) { - if (t->windows[i] && t->windows[i]->win) { - wnoutrefresh(t->windows[i]->win); - } - } - - doupdate(); -} diff --git a/wm.h b/wm.h deleted file mode 100644 index 713f2cb..0000000 --- a/wm.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef SF_WM_H -#define SF_WM_H - -#include - -#define WINDOWS_MAX 15 - -typedef struct { - WINDOW *win; - char name[32]; - int height; - int width; - int start_x; - int start_y; -} window; - -typedef struct { - window **windows; /* windows[0] is root_win */ - int w_count; /* index of windows */ - int maxy; - int maxx; -} terminal; - -terminal *wm_init(void); -window *wm_create_new_w(terminal *t, const char *name, int height, int width, - int start_y, int start_x); -int wm_fd_w_by_name(terminal *t, const char *name); -void wm_refresh_all(terminal *t); - -#endif /* SF_WM_H */ -- cgit v1.2.3