diff options
| author | verdant <im@verdant.ee> | 2026-07-28 18:30:16 +0800 |
|---|---|---|
| committer | verdant <im@verdant.ee> | 2026-07-28 18:30:16 +0800 |
| commit | 9bffcafa9ae0c38103a8c64b3482bb099e792869 (patch) | |
| tree | 3cc18cabd99f9e300ca2539a9a48fb9c2d30e337 /src | |
| parent | cbdc8462b6e982d3137b74d61bed6f8977405b8a (diff) | |
| download | sf-9bffcafa9ae0c38103a8c64b3482bb099e792869.tar.gz sf-9bffcafa9ae0c38103a8c64b3482bb099e792869.zip | |
Move source files to src/
Diffstat (limited to 'src')
| -rw-r--r-- | src/dir.c | 57 | ||||
| -rw-r--r-- | src/dir.h | 30 | ||||
| -rw-r--r-- | src/display.c | 58 | ||||
| -rw-r--r-- | src/display.h | 59 | ||||
| -rw-r--r-- | src/err.c | 18 | ||||
| -rw-r--r-- | src/err.h | 8 | ||||
| -rw-r--r-- | src/fm.c | 463 | ||||
| -rw-r--r-- | src/fm.h | 69 | ||||
| -rw-r--r-- | src/keys.c | 67 | ||||
| -rw-r--r-- | src/keys.h | 9 | ||||
| -rw-r--r-- | src/log.c | 61 | ||||
| -rw-r--r-- | src/log.h | 21 | ||||
| -rw-r--r-- | src/main.c | 121 | ||||
| -rw-r--r-- | src/mem.c | 43 | ||||
| -rw-r--r-- | src/mem.h | 10 | ||||
| -rw-r--r-- | src/version.c | 16 | ||||
| -rw-r--r-- | src/version.h | 13 | ||||
| -rw-r--r-- | src/wm.c | 78 | ||||
| -rw-r--r-- | src/wm.h | 30 |
19 files changed, 1231 insertions, 0 deletions
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 <lostd@2f30.org> + * Copyright (C) 2014-2016, Dimitris Papastamos <sin@2f30.org> + * Copyright (C) 2016-2026, Arun Prakash Jana <engineerarun@gmail.com> + * 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 <dirent.h> +#include <stdio.h> +#include <string.h> + +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 <limits.h> +#include <stdbool.h> +#include <stddef.h> + +#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 <curses.h> + +#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 <ncurses.h> +#include <stdlib.h> + +#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 <curses.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#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 <stdarg.h> + +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 <curses.h> +#include <dirent.h> +#include <errno.h> +#include <libgen.h> +#include <linux/limits.h> +#include <magic.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +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, "<Cannot open file>"); + 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 <limits.h> +#include <linux/limits.h> +#include <ncurses.h> +#include <stdbool.h> +#include <sys/stat.h> + +#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 <linux/limits.h> +#include <ncurses.h> +#include <stdio.h> +#include <stdlib.h> + +#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 <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#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 <stdio.h> + +#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 <ctype.h> +#include <dirent.h> +#include <limits.h> +#include <linux/limits.h> +#include <magic.h> +#include <ncurses.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/statvfs.h> +#include <sys/types.h> +#include <unistd.h> + +#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 <curses.h> +#include <stddef.h> +#include <stdlib.h> + +#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 <stddef.h> + +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 <stdio.h> +#include <stdlib.h> +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 " + "<https://gnu.org/licenses/gpl.html>.\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 <im@verdant.ee>" +#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 <ncurses.h> +#include <string.h> + +#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 <ncurses.h> + +#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 */ |
