summaryrefslogtreecommitdiff
path: root/sfm-0.4/sfm.c
diff options
context:
space:
mode:
authorstkhan <personal@slickd.xyz>2022-05-07 12:42:36 +0000
committerstkhan <personal@slickd.xyz>2022-05-07 12:42:36 +0000
commit0f734f0e317996d246fed2b0bdd1550c49d46e5b (patch)
treedb0f535dcb02dc1959508a1e94570db74ea3bbb2 /sfm-0.4/sfm.c
parent42966e3e531b8e46e4ab31480d45aa7a141f19ce (diff)
Fixed things, added sfm
Diffstat (limited to 'sfm-0.4/sfm.c')
-rw-r--r--sfm-0.4/sfm.c2033
1 files changed, 2033 insertions, 0 deletions
diff --git a/sfm-0.4/sfm.c b/sfm-0.4/sfm.c
new file mode 100644
index 0000000..3a69586
--- /dev/null
+++ b/sfm-0.4/sfm.c
@@ -0,0 +1,2033 @@
+/* See LICENSE file for copyright and license details. */
+
+#if defined(__linux__)
+#define _GNU_SOURCE
+#elif defined(__APPLE__)
+#define _DARWIN_C_SOURCE
+#elif defined(__FreeBSD__)
+#define __BSD_VISIBLE 1
+#endif
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#if defined(__linux__)
+#include <sys/inotify.h>
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
+ defined(__APPLE__)
+#include <sys/event.h>
+#endif
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "termbox.h"
+#include "util.h"
+
+/* macros */
+#define MAX_P 4096
+#define MAX_N 255
+#define MAX_USRI 32
+#define MAX_EXT 4
+#define MAX_STATUS 255
+#define MAX_LINE 4096
+#define MAX_USRN 32
+#define MAX_GRPN 32
+#define MAX_DTF 32
+#define CURSOR(x) (x)->direntr[(x)->hdir - 1]
+
+/* typedef */
+typedef struct {
+ char name[MAX_N];
+ gid_t group;
+ mode_t mode;
+ off_t size;
+ time_t dt;
+ uid_t user;
+} Entry;
+
+typedef struct {
+ uint16_t fg;
+ uint16_t bg;
+} Cpair;
+
+typedef struct {
+ int pane_id;
+ char dirn[MAX_P]; // dir name cwd
+ char *filter;
+ Entry *direntr; // dir entries
+ int dirc; // dir entries sum
+ int hdir; // highlighted dir
+ int x_srt;
+ int x_end;
+ int firstrow;
+ int parent_firstrow;
+ int parent_row; // FIX
+ Cpair dircol;
+ int inotify_wd;
+ int event_fd;
+} Pane;
+
+typedef struct {
+ const char **ext;
+ size_t exlen;
+ const void *v;
+ size_t vlen;
+} Rule;
+
+typedef union {
+ uint16_t key; /* one of the TB_KEY_* constants */
+ uint32_t ch; /* unicode character */
+} Evkey;
+
+typedef union {
+ int i;
+ const void *v;
+} Arg;
+
+typedef struct {
+ const Evkey evkey;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+/* function declarations */
+static void print_tb(const char *, int, int, uint16_t, uint16_t);
+static void printf_tb(int, int, Cpair, const char *, ...);
+static void print_status(Cpair, const char *, ...);
+static void print_xstatus(char, int);
+static void print_error(char *);
+static void print_prompt(char *);
+static void print_info(Pane *, char *);
+static void print_row(Pane *, size_t, Cpair);
+static void clear(int, int, int, uint16_t);
+static void clear_status(void);
+static void clear_pane(Pane *);
+static void add_hi(Pane *, size_t);
+static void rm_hi(Pane *, size_t);
+static int check_dir(char *);
+static int sort_name(const void *const, const void *const);
+static void get_dirp(char *);
+static char *get_ext(char *);
+static int get_fdt(char *, time_t);
+static char *get_fgrp(gid_t);
+static char *get_fperm(mode_t);
+static char *get_fsize(off_t);
+static char *get_fullpath(char *, char *);
+static char *get_fusr(uid_t);
+static void get_dirsize(char *, off_t *);
+static void get_hicol(Cpair *, mode_t);
+static void delent(const Arg *arg);
+static void calcdir(const Arg *arg);
+static void crnd(const Arg *arg);
+static void crnf(const Arg *arg);
+static void mv_ver(const Arg *arg);
+static void mvbk(const Arg *arg);
+static void mvbtm(const Arg *arg);
+static void mvfwd(const Arg *arg);
+static void mvtop(const Arg *arg);
+static void bkmrk(const Arg *arg);
+static int get_usrinput(char *, size_t, const char *, ...);
+static int frules(char *);
+static int spawn(const void *, size_t, const void *, size_t, char *, int);
+static int opnf(char *);
+static int fsev_init(void);
+static int addwatch(Pane *);
+static int read_events(void);
+static void rmwatch(Pane *);
+static void fsev_shdn(void);
+static void toggle_df(const Arg *arg);
+static void start_filter(const Arg *arg);
+static void start_vmode(const Arg *arg);
+static void exit_vmode(const Arg *arg);
+static void start_change(const Arg *arg);
+static void exit_change(const Arg *arg);
+static void selup(const Arg *arg);
+static void seldwn(const Arg *arg);
+static void selall(const Arg *arg);
+static void selref(void);
+static void selynk(const Arg *arg);
+static void selcalc(void);
+static void paste(const Arg *arg);
+static void selmv(const Arg *arg);
+static void seldel(const Arg *arg);
+static void init_files(void);
+static void free_files(void);
+static void yank(const Arg *arg);
+static void rname(const Arg *arg);
+static void chngo(const Arg *arg);
+static void chngm(const Arg *arg);
+static void chngf(const Arg *arg);
+static void dupl(const Arg *arg);
+static void switch_pane(const Arg *arg);
+static void quit(const Arg *arg);
+static void grabkeys(struct tb_event *, Key *, size_t);
+static void *read_th(void *arg);
+static void start_ev(void);
+static void refresh_pane(Pane *);
+static void set_direntr(Pane *, struct dirent *, DIR *, char *);
+static int listdir(Pane *);
+static void t_resize(void);
+static void get_shell(void);
+static void opnsh(const Arg *arg);
+static void set_panes(void);
+static void draw_frame(void);
+static void refresh(const Arg *arg);
+static void start(void);
+
+/* global variables */
+static pthread_t fsev_thread;
+static Pane panes[2];
+static Pane *cpane;
+static int pane_idx;
+static char *editor[2];
+static char fed[] = "vi";
+static char *shell[2];
+static char sh[] = "/bin/sh";
+static int theight, twidth, hwidth, scrheight;
+static int *sel_indexes;
+static size_t sel_len = 0;
+static char **sel_files;
+static int cont_vmode = 0;
+static int cont_change = 0;
+static pid_t fork_pid = 0, main_pid;
+#if defined(_SYS_INOTIFY_H)
+#define READEVSZ 16
+static int inotify_fd;
+#elif defined(_SYS_EVENT_H_)
+#define READEVSZ 0
+static int kq;
+struct kevent evlist[2]; /* events we want to monitor */
+struct kevent chlist[2]; /* events that were triggered */
+static struct timespec gtimeout;
+#endif
+#if defined(__linux__) || defined(__FreeBSD__)
+#define OFF_T "%ld"
+#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+#define OFF_T "%lld"
+#endif
+enum { Left, Right }; /* panes */
+enum { Wait, DontWait }; /* spawn forks */
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* function implementations */
+static void
+print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
+{
+ while (*str != '\0') {
+ uint32_t uni = 0;
+ str += tb_utf8_char_to_unicode(&uni, str);
+ tb_change_cell(x, y, uni, fg, bg);
+ x++;
+ }
+}
+
+static void
+printf_tb(int x, int y, Cpair col, const char *fmt, ...)
+{
+ char buf[MAX_LINE];
+ va_list vl;
+ va_start(vl, fmt);
+ (void)vsnprintf(buf, MAX_LINE, fmt, vl);
+ va_end(vl);
+ print_tb(buf, x, y, col.fg, col.bg);
+}
+
+static void
+print_status(Cpair col, const char *fmt, ...)
+{
+ char buf[MAX_STATUS];
+ va_list vl;
+ va_start(vl, fmt);
+ (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
+ va_end(vl);
+ clear_status();
+ print_tb(buf, 1, theight - 1, col.fg, col.bg);
+}
+
+static void
+print_xstatus(char c, int x)
+{
+ uint32_t uni = 0;
+ (void)tb_utf8_char_to_unicode(&uni, &c);
+ tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
+}
+
+static void
+print_error(char *errmsg)
+{
+ print_status(cerr, errmsg);
+}
+
+static void
+print_prompt(char *prompt)
+{
+ print_status(cprompt, prompt);
+}
+
+static void
+print_info(Pane *pane, char *dirsize)
+{
+ char *sz, *ur, *gr, *dt, *prm;
+
+ dt = ecalloc(MAX_DTF, sizeof(char));
+
+ prm = get_fperm(CURSOR(pane).mode);
+ ur = get_fusr(CURSOR(pane).user);
+ gr = get_fgrp(CURSOR(pane).group);
+
+ if (get_fdt(dt, CURSOR(pane).dt) < 0)
+ *dt = '\0';
+
+ if (S_ISREG(CURSOR(pane).mode)) {
+ sz = get_fsize(CURSOR(pane).size);
+ } else {
+ if (dirsize == NULL) {
+ sz = ecalloc(1, sizeof(char));
+ *sz = '\0';
+ } else {
+ sz = dirsize;
+ }
+ }
+
+ print_status(cstatus, "%02d/%02d %s %s:%s %s %s", pane->hdir,
+ pane->dirc, prm, ur, gr, dt, sz);
+
+ free(prm);
+ free(ur);
+ free(gr);
+ free(dt);
+ free(sz);
+}
+
+static void
+print_row(Pane *pane, size_t entpos, Cpair col)
+{
+ int x, y;
+ char *full_str, *rez_pth;
+ char lnk_full[MAX_N];
+
+ full_str = basename(pane->direntr[entpos].name);
+ x = pane->x_srt;
+ y = entpos - pane->firstrow + 1;
+
+ if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
+ rez_pth = ecalloc(MAX_P, sizeof(char));
+ if (realpath(pane->direntr[entpos].name, rez_pth) != NULL) {
+ snprintf(
+ lnk_full, MAX_N, "%s -> %s", full_str, rez_pth);
+ full_str = lnk_full;
+ }
+ free(rez_pth);
+ }
+
+ printf_tb(x, y, col, "%*.*s", ~hwidth, hwidth, full_str);
+}
+
+static void
+clear(int sx, int ex, int y, uint16_t bg)
+{
+ /* clear line from to */
+ /* x = line number vertical */
+ /* y = column number horizontal */
+ int i;
+ for (i = sx; i < ex; i++) {
+ tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
+ }
+}
+
+static void
+clear_status(void)
+{
+ clear(1, twidth - 1, theight - 1, cstatus.bg);
+}
+
+static void
+clear_pane(Pane *pane)
+{
+ int i, y;
+ y = 0, i = 0;
+
+ while (i < scrheight) {
+ clear(pane->x_srt, pane->x_end, y, TB_DEFAULT);
+ i++;
+ y++;
+ }
+
+ /* draw top line */
+ for (y = pane->x_srt; y < pane->x_end; ++y) {
+ tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
+ }
+}
+
+static void
+add_hi(Pane *pane, size_t entpos)
+{
+ Cpair col;
+ get_hicol(&col, pane->direntr[entpos].mode);
+ col.fg |= TB_REVERSE | TB_BOLD;
+ col.bg |= TB_REVERSE;
+ print_row(pane, entpos, col);
+}
+
+static void
+rm_hi(Pane *pane, size_t entpos)
+{
+ Cpair col;
+ get_hicol(&col, pane->direntr[entpos].mode);
+ print_row(pane, entpos, col);
+}
+
+static int
+check_dir(char *path)
+{
+ DIR *dir;
+ dir = opendir(path);
+
+ if (dir == NULL) {
+ if (errno == ENOTDIR) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ if (closedir(dir) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+sort_name(const void *const A, const void *const B)
+{
+ int result;
+ mode_t data1 = (*(Entry *)A).mode;
+ mode_t data2 = (*(Entry *)B).mode;
+
+ if (data1 < data2) {
+ return -1;
+ } else if (data1 == data2) {
+ result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
+ return result;
+ } else {
+ return 1;
+ }
+}
+
+static void
+get_dirp(char *cdir)
+{
+ int counter, len, i;
+
+ counter = 0;
+ len = strnlen(cdir, MAX_P);
+ if (len == 1)
+ return;
+
+ for (i = len - 1; i > 1; i--) {
+ if (cdir[i] == '/')
+ break;
+ else
+ counter++;
+ }
+
+ cdir[len - counter - 1] = '\0';
+}
+
+static char *
+get_ext(char *str)
+{
+ char *ext;
+ char dot;
+ size_t counter, len, i;
+
+ dot = '.';
+ counter = 0;
+ len = strnlen(str, MAX_N);
+
+ for (i = len - 1; i > 0; i--) {
+ if (str[i] == dot) {
+ break;
+ } else {
+ counter++;
+ }
+ }
+
+ ext = ecalloc(MAX_EXT + 1, sizeof(char));
+ strncpy(ext, &str[len - counter], MAX_EXT);
+ ext[MAX_EXT] = '\0';
+ return ext;
+}
+
+static int
+get_fdt(char *result, time_t status)
+{
+ struct tm lt;
+ localtime_r(&status, &lt);
+ return strftime(result, MAX_DTF, dtfmt, &lt);
+}
+
+static char *
+get_fgrp(gid_t status)
+{
+ char *result;
+ struct group *gr;
+
+ result = ecalloc(MAX_GRPN, sizeof(char));
+ gr = getgrgid(status);
+ if (gr == NULL)
+ (void)snprintf(result, MAX_GRPN, "%u", status);
+ else
+ strncpy(result, gr->gr_name, MAX_GRPN);
+
+ result[MAX_GRPN - 1] = '\0';
+ return result;
+}
+
+static char *
+get_fperm(mode_t mode)
+{
+ char *buf;
+ size_t i;
+
+ const char chars[] = "rwxrwxrwx";
+ buf = ecalloc(11, sizeof(char));
+
+ if (S_ISDIR(mode))
+ buf[0] = 'd';
+ else if (S_ISREG(mode))
+ buf[0] = '-';
+ else if (S_ISLNK(mode))
+ buf[0] = 'l';
+ else if (S_ISBLK(mode))
+ buf[0] = 'b';
+ else if (S_ISCHR(mode))
+ buf[0] = 'c';
+ else if (S_ISFIFO(mode))
+ buf[0] = 'p';
+ else if (S_ISSOCK(mode))
+ buf[0] = 's';
+ else
+ buf[0] = '?';
+
+ for (i = 1; i < 10; i++) {
+ buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
+ }
+ buf[10] = '\0';
+
+ return buf;
+}
+
+static char *
+get_fsize(off_t size)
+{
+ char *result; /* need to be freed */
+ char unit;
+ int result_len;
+ int counter;
+
+ counter = 0;
+ result_len = 6; /* 9999X/0 */
+ result = ecalloc(result_len, sizeof(char));
+
+ while (size >= 1000) {
+ size /= 1024;
+ ++counter;
+ }
+
+ switch (counter) {
+ case 0:
+ unit = 'B';
+ break;
+ case 1:
+ unit = 'K';
+ break;
+ case 2:
+ unit = 'M';
+ break;
+ case 3:
+ unit = 'G';
+ break;
+ case 4:
+ unit = 'T';
+ break;
+ default:
+ unit = '?';
+ }
+
+ if (snprintf(result, result_len, OFF_T "%c", size, unit) < 0)
+ strncat(result, "???", result_len);
+
+ return result;
+}
+
+static char *
+get_fullpath(char *first, char *second)
+{
+ char *full_path;
+
+ full_path = ecalloc(MAX_P, sizeof(char));
+
+ if (strncmp(first, "/", MAX_P) == 0)
+ (void)snprintf(full_path, MAX_P, "/%s", second);
+ else
+ (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
+
+ return full_path;
+}
+
+static char *
+get_fusr(uid_t status)
+{
+ char *result;
+ struct passwd *pw;
+
+ result = ecalloc(MAX_USRN, sizeof(char));
+ pw = getpwuid(status);
+ if (pw == NULL)
+ (void)snprintf(result, MAX_USRN, "%u", status);
+ else
+ strncpy(result, pw->pw_name, MAX_USRN);
+
+ result[MAX_USRN - 1] = '\0';
+ return result;
+}
+
+static void
+get_dirsize(char *fullpath, off_t *fullsize)
+{
+ DIR *dir;
+ char *ent_full;
+ mode_t mode;
+ struct dirent *entry;
+ struct stat status;
+
+ dir = opendir(fullpath);
+ if (dir == NULL) {
+ return;
+ }
+
+ while ((entry = readdir(dir)) != 0) {
+ if ((strncmp(entry->d_name, ".", 2) == 0 ||
+ strncmp(entry->d_name, "..", 3) == 0))
+ continue;
+
+ ent_full = get_fullpath(fullpath, entry->d_name);
+ if (lstat(ent_full, &status) == 0) {
+ mode = status.st_mode;
+ if (S_ISDIR(mode)) {
+ get_dirsize(ent_full, fullsize);
+ free(ent_full);
+ } else {
+ *fullsize += status.st_size;
+ free(ent_full);
+ }
+ }
+ }
+
+ closedir(dir);
+ clear_status();
+}
+
+static void
+get_hicol(Cpair *col, mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ *col = cfile;
+ if ((S_IXUSR | S_IXGRP | S_IXOTH) & mode)
+ *col = cexec;
+ break;
+ case S_IFDIR:
+ *col = cdir;
+ break;
+ case S_IFLNK:
+ *col = clnk;
+ break;
+ case S_IFBLK:
+ *col = cblk;
+ break;
+ case S_IFCHR:
+ *col = cchr;
+ break;
+ case S_IFIFO:
+ *col = cifo;
+ break;
+ case S_IFSOCK:
+ *col = csock;
+ break;
+ default:
+ *col = cother;
+ break;
+ }
+}
+
+static void
+delent(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char *inp_conf;
+
+ inp_conf = ecalloc(delconf_len, sizeof(char));
+ if ((get_usrinput(inp_conf, delconf_len, "delete files(s) (%s) ?",
+ delconf) < 0) ||
+ (strncmp(inp_conf, delconf, delconf_len) != 0)) {
+ free(inp_conf);
+ return; /* canceled by user or wrong inp_conf */
+ }
+ free(inp_conf);
+
+ char *tmp[1];
+ tmp[0] = CURSOR(cpane).name;
+ if (spawn(rm_cmd, rm_cmd_len, tmp, 1, NULL, DontWait) < 0) {
+ print_error(strerror(errno));
+ return;
+ }
+}
+
+static void
+calcdir(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ if (!S_ISDIR(CURSOR(cpane).mode))
+ return;
+
+ off_t *fullsize;
+ char *csize;
+
+ fullsize = ecalloc(1, sizeof(off_t));
+ get_dirsize(CURSOR(cpane).name, fullsize);
+ csize = get_fsize(*fullsize);
+
+ CURSOR(cpane).size = *fullsize;
+ print_info(cpane, csize);
+ free(fullsize);
+}
+
+static void
+crnd(const Arg *arg)
+{
+ char *user_input, *path;
+
+ user_input = ecalloc(MAX_USRI, sizeof(char));
+ if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
+ free(user_input);
+ return;
+ }
+
+ path = ecalloc(MAX_P, sizeof(char));
+ if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
+ free(user_input);
+ free(path);
+ return;
+ }
+
+ PERROR(mkdir(path, ndir_perm) < 0);
+
+ free(user_input);
+ free(path);
+}
+
+static void
+crnf(const Arg *arg)
+{
+ char *user_input, *path;
+ int rf;
+
+ user_input = ecalloc(MAX_USRI, sizeof(char));
+ if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
+ free(user_input);
+ return;
+ }
+
+ path = ecalloc(MAX_P, sizeof(char));
+ if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
+ free(user_input);
+ free(path);
+ return;
+ }
+
+ rf = open(path, O_CREAT | O_EXCL, nf_perm);
+
+ if (rf < 0)
+ print_error(strerror(errno));
+ else if (close(rf) < 0)
+ print_error(strerror(errno));
+
+ free(user_input);
+ free(path);
+}
+static void
+mv_ver(const Arg *arg)
+{
+
+ if (cpane->dirc < 1)
+ return;
+ if (cpane->hdir - arg->i < 1) /* first line */
+ return;
+
+ if (cpane->hdir - arg->i > cpane->dirc) /* last line */
+ return;
+
+ if (cpane->firstrow > 0 && arg->i > 0 &&
+ cpane->hdir <= (cpane->firstrow + arg->i)) { /* scroll up */
+ cpane->firstrow = cpane->firstrow - arg->i;
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = cpane->hdir - arg->i;
+ refresh_pane(cpane);
+ add_hi(cpane, cpane->hdir - 1);
+ return;
+ }
+
+ if (cpane->hdir - cpane->firstrow >= scrheight + arg->i &&
+ arg->i < 0) { /* scroll down */
+ cpane->firstrow = cpane->firstrow - arg->i;
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = cpane->hdir - arg->i;
+ refresh_pane(cpane);
+ add_hi(cpane, cpane->hdir - 1);
+ return;
+ }
+
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = cpane->hdir - arg->i;
+ add_hi(cpane, cpane->hdir - 1);
+ print_info(cpane, NULL);
+}
+
+static void
+mvbk(const Arg *arg)
+{
+ if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
+ return;
+ }
+
+ get_dirp(cpane->dirn);
+ if (check_dir(cpane->dirn) < 0) {
+ print_error(strerror(errno));
+ return;
+ }
+
+ cpane->firstrow = cpane->parent_firstrow;
+ cpane->hdir = cpane->parent_row;
+ PERROR(listdir(cpane) < 0);
+ cpane->parent_firstrow = 0;
+ cpane->parent_row = 1;
+}
+
+static void
+mvbtm(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ if (cpane->dirc > scrheight) {
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = cpane->dirc;
+ cpane->firstrow = cpane->dirc - scrheight + 1;
+ refresh_pane(cpane);
+ add_hi(cpane, cpane->hdir - 1);
+ } else {
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = cpane->dirc;
+ add_hi(cpane, cpane->hdir - 1);
+ }
+ print_info(cpane, NULL);
+}
+
+static void
+mvfwd(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ int s;
+
+ switch (check_dir(CURSOR(cpane).name)) {
+ case 0:
+ strncpy(cpane->dirn, CURSOR(cpane).name, MAX_P);
+ cpane->parent_row = cpane->hdir;
+ cpane->parent_firstrow = cpane->firstrow;
+ cpane->hdir = 1;
+ cpane->firstrow = 0;
+ PERROR(listdir(cpane) < 0);
+ break;
+ case 1: /* not a directory open file */
+ tb_shutdown();
+ s = opnf(CURSOR(cpane).name);
+ if (tb_init() != 0)
+ die("tb_init");
+ t_resize();
+ if (s < 0)
+ print_error("process failed non-zero exit");
+ break;
+ case -1: /* failed to open directory */
+ print_error(strerror(errno));
+ }
+}
+
+static void
+mvtop(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ if (cpane->dirc > scrheight) {
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = 1;
+ cpane->firstrow = 0;
+ refresh_pane(cpane);
+ add_hi(cpane, cpane->hdir - 1);
+ } else {
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane->hdir = 1;
+ add_hi(cpane, cpane->hdir - 1);
+ print_info(cpane, NULL);
+ }
+}
+
+static void
+bkmrk(const Arg *arg)
+{
+ if (check_dir((char *)arg->v) != 0) {
+ print_error(strerror(errno));
+ return;
+ }
+
+ strncpy(cpane->dirn, (char *)arg->v, MAX_P);
+ cpane->firstrow = 0;
+ cpane->parent_row = 1;
+ cpane->hdir = 1;
+ PERROR(listdir(cpane) < 0);
+}
+
+static int
+get_usrinput(char *result, size_t max_chars, const char *fmt, ...)
+{
+ char msg[MAX_N];
+ size_t i, cpos, startat;
+ struct tb_event fev;
+ va_list vl;
+
+ i = 0;
+ cpos = 1;
+
+ va_start(vl, fmt);
+ startat = vsnprintf(msg, MAX_N, fmt, vl) + 1;
+ va_end(vl);
+
+ clear_status();
+ print_tb(msg, 1, theight - 1, cprompt.fg, cprompt.bg);
+ tb_set_cursor(startat + 1, theight - 1);
+ tb_present();
+
+ while (tb_poll_event(&fev) != 0) {
+ switch (fev.type) {
+ case TB_EVENT_KEY:
+ if (fev.key == TB_KEY_ESC) {
+ tb_set_cursor(-1, -1);
+ clear_status();
+ return -1;
+ }
+
+ if (fev.key == TB_KEY_BACKSPACE ||
+ fev.key == TB_KEY_BACKSPACE2) {
+ if (BETWEEN(cpos, 2, max_chars)) {
+ result[i - 1] = '\0';
+ cpos--;
+ i--;
+ print_xstatus(' ', startat + cpos);
+ tb_set_cursor(
+ startat + cpos, theight - 1);
+ }
+
+ } else if (fev.key == TB_KEY_ENTER) {
+ tb_set_cursor(-1, -1);
+ result[cpos - 1] = '\0';
+ return 0;
+
+ } else if (fev.key) { /* disable other TB_KEY_* */
+ break;
+
+ } else {
+ if (cpos < max_chars) {
+ print_xstatus(
+ (char)fev.ch, (startat + cpos));
+ result[i] = (char)fev.ch;
+ tb_set_cursor((startat + cpos + 1),
+ theight - 1);
+ cpos++;
+ i++;
+ }
+ }
+
+ tb_present();
+ break;
+
+ case TB_EVENT_RESIZE:
+ t_resize();
+ clear_status();
+ print_tb(msg, 1, theight - 1, cprompt.fg, cprompt.bg);
+ print_tb(result, startat + 1, theight - 1, cstatus.fg,
+ cstatus.bg);
+ tb_present();
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+static int
+frules(char *ex)
+{
+ size_t c, d;
+
+ for (c = 0; c < LEN(rules); c++)
+ for (d = 0; d < rules[c].exlen; d++)
+ if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
+ return c;
+ return -1;
+}
+
+static int
+spawn(const void *com_argv, size_t com_argc, const void *f_argv, size_t f_argc,
+ char *fn, int waiting)
+{
+ int ws;
+ size_t argc;
+ pid_t r;
+
+ argc = com_argc + f_argc + 2;
+ char *argv[argc];
+
+ memcpy(argv, com_argv, com_argc * sizeof(char *)); /* command */
+ memcpy(&argv[com_argc], f_argv, f_argc * sizeof(char *)); /* files */
+
+ argv[argc - 2] = fn;
+ argv[argc - 1] = NULL;
+
+ fork_pid = fork();
+ switch (fork_pid) {
+ case -1:
+ return -1;
+ case 0:
+ execvp(argv[0], argv);
+ exit(EXIT_SUCCESS);
+ default:
+ if (waiting == Wait) {
+ while ((r = waitpid(fork_pid, &ws, 0)) == -1 &&
+ errno == EINTR)
+ continue;
+ if (r == -1)
+ return -1;
+ if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
+ return -1;
+ }
+ }
+ fork_pid = 0; /* enable th_handler() */
+ return 0;
+}
+
+static int
+opnf(char *fn)
+{
+ char *ex;
+ int c;
+
+ ex = get_ext(fn);
+ c = frules(ex);
+ free(ex);
+
+ if (c < 0) /* extension not found open in editor */
+ return spawn(editor, 1, NULL, 0, fn, Wait);
+ else
+ return spawn(
+ (char **)rules[c].v, rules[c].vlen, NULL, 0, fn, Wait);
+}
+
+static void
+opnsh(const Arg *arg)
+{
+ int s;
+
+ tb_shutdown();
+ chdir(cpane->dirn);
+ s = spawn(shell, 1, NULL, 0, NULL, Wait);
+ if (tb_init() != 0)
+ die("tb_init");
+ t_resize();
+ if (s < 0)
+ print_error("process failed non-zero exit");
+}
+
+static int
+fsev_init(void)
+{
+#if defined(_SYS_INOTIFY_H)
+ inotify_fd = inotify_init();
+ if (inotify_fd < 0)
+ return -1;
+#elif defined(_SYS_EVENT_H_)
+ gtimeout.tv_sec = 1;
+ kq = kqueue();
+ if (kq < 0)
+ return -1;
+#endif
+ return 0;
+}
+
+static int
+addwatch(Pane *pane)
+{
+#if defined(_SYS_INOTIFY_H)
+ return pane->inotify_wd = inotify_add_watch(inotify_fd, pane->dirn,
+ IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
+ IN_ATTRIB | IN_DELETE | IN_DELETE_SELF |
+ IN_MOVE_SELF);
+#elif defined(_SYS_EVENT_H_)
+ pane->event_fd = open(pane->dirn, O_RDONLY);
+ if (pane->event_fd < 0)
+ return pane->event_fd;
+ EV_SET(&evlist[pane->pane_id], pane->event_fd, EVFILT_VNODE,
+ EV_ADD | EV_CLEAR,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
+ NOTE_ATTRIB | NOTE_REVOKE | NOTE_WRITE,
+ 0, NULL);
+ return 0;
+#endif
+}
+
+static int
+read_events(void)
+{
+#if defined(_SYS_INOTIFY_H)
+ char *p;
+ ssize_t r;
+ struct inotify_event *event;
+ const size_t events = 32;
+ const size_t evbuflen =
+ events * (sizeof(struct inotify_event) + MAX_N + 1);
+ char buf[evbuflen];
+
+ if (cpane->inotify_wd < 0)
+ return -1;
+ r = read(inotify_fd, buf, evbuflen);
+ if (r <= 0)
+ return r;
+
+ for (p = buf; p < buf + r;) {
+ event = (struct inotify_event *)p;
+ if (!event->wd)
+ break;
+ if (event->mask) {
+ return r;
+ }
+
+ p += sizeof(struct inotify_event) + event->len;
+ }
+#elif defined(_SYS_EVENT_H_)
+ return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
+#endif
+ return -1;
+}
+
+static void
+rmwatch(Pane *pane)
+{
+#if defined(_SYS_INOTIFY_H)
+ if (pane->inotify_wd >= 0)
+ inotify_rm_watch(inotify_fd, pane->inotify_wd);
+#elif defined(_SYS_EVENT_H_)
+ close(pane->event_fd);
+#endif
+}
+
+static void
+fsev_shdn(void)
+{
+ pthread_cancel(fsev_thread);
+#if defined(__linux__)
+ pthread_join(fsev_thread, NULL);
+#endif
+ rmwatch(&panes[Left]);
+ rmwatch(&panes[Right]);
+#if defined(_SYS_INOTIFY_H)
+ close(inotify_fd);
+#elif defined(_SYS_EVENT_H_)
+ close(kq);
+#endif
+}
+
+static void
+toggle_df(const Arg *arg)
+{
+ show_dotfiles = !show_dotfiles;
+ PERROR(listdir(&panes[Left]));
+ PERROR(listdir(&panes[Right]));
+ tb_present();
+}
+
+static void
+start_filter(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char *user_input;
+ user_input = ecalloc(MAX_USRI, sizeof(char));
+ if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
+ free(user_input);
+ return;
+ }
+ cpane->filter = user_input;
+ if (listdir(cpane) < 0)
+ print_error("no match");
+ cpane->filter = NULL;
+ free(user_input);
+}
+
+static void
+start_vmode(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ struct tb_event fev;
+ if (sel_indexes != NULL) {
+ free(sel_indexes);
+ sel_indexes = NULL;
+ }
+
+ sel_indexes = ecalloc(cpane->dirc, sizeof(size_t));
+ sel_indexes[0] = cpane->hdir;
+ cont_vmode = 0;
+ print_prompt("-- VISUAL --");
+ tb_present();
+ while (tb_poll_event(&fev) != 0) {
+ switch (fev.type) {
+ case TB_EVENT_KEY:
+ grabkeys(&fev, vkeys, vkeyslen);
+ if (cont_vmode == -1)
+ return;
+ tb_present();
+ break;
+ }
+ }
+}
+
+static void
+exit_vmode(const Arg *arg)
+{
+ refresh_pane(cpane);
+ add_hi(cpane, cpane->hdir - 1);
+ cont_vmode = -1;
+}
+
+static void
+start_change(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ struct tb_event fev;
+
+ cont_change = 0;
+ print_prompt("c [womf]");
+ tb_present();
+ while (tb_poll_event(&fev) != 0) {
+ switch (fev.type) {
+ case TB_EVENT_KEY:
+ grabkeys(&fev, ckeys, ckeyslen);
+ if (cont_change == -1)
+ return;
+ tb_present();
+ break;
+ }
+ }
+}
+
+static void
+exit_change(const Arg *arg)
+{
+ cont_change = -1;
+ print_info(cpane, NULL);
+}
+
+static void
+selup(const Arg *arg)
+{
+ mv_ver(arg);
+ print_prompt("-- VISUAL --");
+ int index = abs(cpane->hdir - sel_indexes[0]);
+
+ if (cpane->hdir < sel_indexes[0]) {
+ sel_indexes[index] = cpane->hdir;
+ add_hi(cpane, sel_indexes[index]);
+ } else if (index < cpane->dirc) {
+ sel_indexes[index + 1] = 0;
+ }
+ if (cpane->dirc >= scrheight ||
+ cpane->hdir <= 1) { /* rehighlight all if scrolling */
+ selref();
+ }
+}
+
+static void
+seldwn(const Arg *arg)
+{
+ mv_ver(arg);
+ print_prompt("-- VISUAL --");
+ int index = abs(cpane->hdir - sel_indexes[0]);
+
+ if (cpane->hdir > sel_indexes[0]) {
+ sel_indexes[index] = cpane->hdir;
+ add_hi(cpane, sel_indexes[index] - 2);
+ } else {
+ sel_indexes[index + 1] = 0;
+ }
+ if (cpane->dirc >= scrheight ||
+ cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
+ selref();
+ }
+}
+
+static void
+selall(const Arg *arg)
+{
+ int i;
+ for (i = 0; i < cpane->dirc; i++) {
+ sel_indexes[i] = i + 1;
+ }
+ selref();
+}
+
+static void
+selref(void)
+{
+ int i;
+ for (i = 0; i < cpane->dirc; i++) {
+ if (sel_indexes[i] < (scrheight + cpane->firstrow) &&
+ sel_indexes[i] >
+ cpane->firstrow) { /* checks if in the frame of the directories */
+ add_hi(cpane, sel_indexes[i] - 1);
+ }
+ }
+}
+
+static void
+selcalc(void)
+{
+ int j;
+ sel_len = 0;
+
+ for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
+ if (sel_indexes[j] != 0)
+ sel_len++;
+ else
+ break;
+ }
+}
+
+static void
+free_files(void)
+{
+ size_t i;
+
+ if (sel_files != NULL) {
+ for (i = 0; i < sel_len; i++) {
+ free(sel_files[i]);
+ sel_files[i] = NULL;
+ }
+ free(sel_files);
+ sel_files = NULL;
+ }
+}
+
+static void
+init_files(void)
+{
+ size_t i;
+ free_files();
+
+ selcalc();
+ sel_files = ecalloc(sel_len, sizeof(char *));
+
+ for (i = 0; i < sel_len; i++) {
+ sel_files[i] = ecalloc(MAX_P, sizeof(char));
+ strncpy(sel_files[i], cpane->direntr[sel_indexes[i] - 1].name,
+ MAX_P);
+ }
+}
+
+static void
+selynk(const Arg *arg)
+{
+ init_files();
+ refresh_pane(cpane);
+ add_hi(cpane, cpane->hdir - 1);
+ print_status(cprompt, "%zu files are yanked", sel_len);
+ cont_vmode = -1;
+}
+
+static void
+seldel(const Arg *arg)
+{
+ char *inp_conf;
+
+ inp_conf = ecalloc(delconf_len, sizeof(char));
+ if ((get_usrinput(inp_conf, delconf_len, "delete files(s) (%s) ?",
+ delconf) < 0) ||
+ (strncmp(inp_conf, delconf, delconf_len) != 0)) {
+ free(inp_conf);
+ return; /* canceled by user or wrong inp_conf */
+ }
+ free(inp_conf);
+
+ init_files();
+
+ if (spawn(rm_cmd, rm_cmd_len, sel_files, sel_len, NULL, DontWait) < 0)
+ print_error(strerror(errno));
+ else
+ print_status(cprompt, "%zu files are deleted", sel_len);
+
+ free_files();
+ cont_vmode = -1;
+}
+
+static void
+paste(const Arg *arg)
+{
+ if (sel_files == NULL) {
+ print_error("nothing to paste");
+ return;
+ }
+
+ if (spawn(cp_cmd, cp_cmd_len, sel_files, sel_len, cpane->dirn,
+ DontWait) < 0)
+ print_error(strerror(errno));
+ else
+ print_status(cprompt, "%zu files are copied", sel_len);
+
+ free_files();
+}
+
+static void
+selmv(const Arg *arg)
+{
+ if (sel_files == NULL) {
+ print_error("nothing to move");
+ return;
+ }
+
+ if (spawn(mv_cmd, mv_cmd_len, sel_files, sel_len, cpane->dirn,
+ DontWait) < 0)
+ print_error(strerror(errno));
+ else
+ print_status(cprompt, "%zu files are moved", sel_len);
+
+ free_files();
+}
+
+static void
+rname(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char new_name[MAX_P];
+ char *input_name;
+
+ input_name = ecalloc(MAX_N, sizeof(char));
+
+ if (get_usrinput(input_name, MAX_N, "rename: %s",
+ basename(CURSOR(cpane).name)) < 0) {
+ exit_change(0);
+ free(input_name);
+ return;
+ }
+
+ if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
+ free(input_name);
+ print_error(strerror(errno));
+ return;
+ }
+
+ char *rename_cmd[] = { "mv", CURSOR(cpane).name, new_name };
+ PERROR(spawn(rename_cmd, 3, NULL, 0, NULL, DontWait) < 0);
+
+ free(input_name);
+ exit_change(0);
+}
+
+static void
+chngo(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char *input_og;
+ char *tmp[1];
+
+ input_og = ecalloc(MAX_N, sizeof(char));
+
+ if (get_usrinput(input_og, MAX_N, "OWNER:GROUP %s",
+ basename(CURSOR(cpane).name)) < 0) {
+ exit_change(0);
+ free(input_og);
+ return;
+ }
+
+ tmp[0] = input_og;
+ if (spawn(chown_cmd, chown_cmd_len, tmp, 1, CURSOR(cpane).name,
+ DontWait) < 0) {
+ print_error(strerror(errno));
+ return;
+ }
+
+ free(input_og);
+ exit_change(0);
+}
+
+static void
+chngm(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char *input_og;
+ char *tmp[1];
+
+ input_og = ecalloc(MAX_N, sizeof(char));
+
+ if (get_usrinput(input_og, MAX_N, "chmod %s",
+ basename(CURSOR(cpane).name)) < 0) {
+ exit_change(0);
+ free(input_og);
+ return;
+ }
+
+ tmp[0] = input_og;
+ if (spawn(chmod_cmd, chmod_cmd_len, tmp, 1, CURSOR(cpane).name,
+ DontWait) < 0) {
+ print_error(strerror(errno));
+ return;
+ }
+
+ free(input_og);
+ exit_change(0);
+}
+
+static void
+chngf(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char *input_og;
+ char *tmp[1];
+
+ input_og = ecalloc(MAX_N, sizeof(char));
+
+ if (get_usrinput(input_og, MAX_N, CHFLAG " %s",
+ basename(CURSOR(cpane).name)) < 0) {
+ exit_change(0);
+ free(input_og);
+ return;
+ }
+
+ tmp[0] = input_og;
+ if (spawn(chflags_cmd, chflags_cmd_len, tmp, 1, CURSOR(cpane).name,
+ DontWait) < 0) {
+ print_error(strerror(errno));
+ return;
+ }
+
+ free(input_og);
+ exit_change(0);
+}
+
+static void
+dupl(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+ char new_name[MAX_P];
+ char *input_name;
+
+ input_name = ecalloc(MAX_N, sizeof(char));
+
+ if (get_usrinput(input_name, MAX_N, "new name: %s",
+ basename(CURSOR(cpane).name)) < 0) {
+ free(input_name);
+ return;
+ }
+
+ if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
+ free(input_name);
+ print_error(strerror(errno));
+ return;
+ }
+
+ char *tmp[1];
+ tmp[0] = CURSOR(cpane).name;
+ if (spawn(cp_cmd, cp_cmd_len, tmp, 1, new_name, DontWait) < 0) {
+ print_error(strerror(errno));
+ return;
+ }
+
+ free(input_name);
+}
+
+static void
+yank(const Arg *arg)
+{
+ if (cpane->dirc < 1)
+ return;
+
+ free_files();
+ sel_len = 1;
+ sel_files = ecalloc(sel_len, sizeof(char *));
+ sel_files[0] = ecalloc(MAX_P, sizeof(char));
+ strncpy(sel_files[0], CURSOR(cpane).name, MAX_P);
+ print_status(cprompt, "1 file is yanked", sel_len);
+}
+
+static void
+switch_pane(const Arg *arg)
+{
+ if (cpane->dirc > 0)
+ rm_hi(cpane, cpane->hdir - 1);
+ cpane = &panes[pane_idx ^= 1];
+ if (cpane->dirc > 0) {
+ add_hi(cpane, cpane->hdir - 1);
+ print_info(cpane, NULL);
+ } else {
+ clear_status();
+ }
+}
+
+static void
+quit(const Arg *arg)
+{
+ if (cont_vmode == -1) { /* check if selection was allocated */
+ free(sel_indexes);
+ if (sel_files != NULL)
+ free_files();
+ }
+ free(panes[Left].direntr);
+ free(panes[Right].direntr);
+ fsev_shdn();
+ tb_shutdown();
+ exit(EXIT_SUCCESS);
+}
+
+static void
+grabkeys(struct tb_event *event, Key *key, size_t max_keys)
+{
+ size_t i;
+
+ for (i = 0; i < max_keys; i++) {
+ if (event->ch != 0) {
+ if (event->ch == key[i].evkey.ch) {
+ key[i].func(&key[i].arg);
+ return;
+ }
+ } else if (event->key != 0) {
+ if (event->key == key[i].evkey.key) {
+ key[i].func(&key[i].arg);
+ return;
+ }
+ }
+ }
+}
+
+void *
+read_th(void *arg)
+{
+ struct timespec tim;
+ tim.tv_sec = 0;
+ tim.tv_nsec = 5000000L; /* 0.005 sec */
+
+ while (1)
+ if (read_events() > READEVSZ) {
+ kill(main_pid, SIGUSR1);
+ nanosleep(&tim, NULL);
+ }
+ return arg;
+}
+
+static void
+start_ev(void)
+{
+ struct tb_event ev;
+
+ while (tb_poll_event(&ev) != 0) {
+ switch (ev.type) {
+ case TB_EVENT_KEY:
+ grabkeys(&ev, nkeys, nkeyslen);
+ tb_present();
+ break;
+ case TB_EVENT_RESIZE:
+ t_resize();
+ break;
+ default:
+ break;
+ }
+ }
+ tb_shutdown();
+}
+
+static void
+refresh_pane(Pane *pane)
+{
+ size_t y, dyn_max, start_from;
+ hwidth = (twidth / 2) - 4;
+ Cpair col;
+
+ y = 1;
+ start_from = pane->firstrow;
+ dyn_max = MIN(pane->dirc, (scrheight - 1) + pane->firstrow);
+
+ /* print each entry in directory */
+ while (start_from < dyn_max) {
+ get_hicol(&col, pane->direntr[start_from].mode);
+ print_row(pane, start_from, col);
+ start_from++;
+ y++;
+ }
+
+ if (pane->dirc > 0)
+ print_info(pane, NULL);
+ else
+ clear_status();
+
+ /* print current directory title */
+ pane->dircol.fg |= TB_BOLD;
+ printf_tb(pane->x_srt, 0, pane->dircol, " %.*s", hwidth, pane->dirn);
+}
+
+static void
+set_direntr(Pane *pane, struct dirent *entry, DIR *dir, char *filter)
+{
+ int i;
+ char *tmpfull;
+ struct stat status;
+
+#define ADD_ENTRY \
+ tmpfull = get_fullpath(pane->dirn, entry->d_name); \
+ strncpy(pane->direntr[i].name, tmpfull, MAX_N); \
+ if (lstat(tmpfull, &status) == 0) { \
+ pane->direntr[i].size = status.st_size; \
+ pane->direntr[i].mode = status.st_mode; \
+ pane->direntr[i].group = status.st_gid; \
+ pane->direntr[i].user = status.st_uid; \
+ pane->direntr[i].dt = status.st_mtime; \
+ } \
+ i++; \
+ free(tmpfull);
+
+ i = 0;
+ pane->direntr =
+ erealloc(pane->direntr, (10 + pane->dirc) * sizeof(Entry));
+ while ((entry = readdir(dir)) != 0) {
+ if (show_dotfiles == 1) {
+ if (entry->d_name[0] == '.' &&
+ (entry->d_name[1] == '\0' ||
+ entry->d_name[1] == '.'))
+ continue;
+ } else {
+ if (entry->d_name[0] == '.')
+ continue;
+ }
+
+ if (filter == NULL) {
+ ADD_ENTRY
+ } else if (filter != NULL) {
+ if (strcasestr(entry->d_name, filter) != NULL) {
+ ADD_ENTRY
+ }
+ }
+ }
+
+ pane->dirc = i;
+}
+
+static int
+listdir(Pane *pane)
+{
+ DIR *dir;
+ struct dirent *entry;
+ int filtercount = 0;
+ size_t oldc = pane->dirc;
+
+ pane->dirc = 0;
+
+ dir = opendir(pane->dirn);
+ if (dir == NULL)
+ return -1;
+
+ /* get content and filter sum */
+ while ((entry = readdir(dir)) != 0) {
+ if (pane->filter != NULL) {
+ if (strcasestr(entry->d_name, pane->filter) != NULL)
+ filtercount++;
+ } else { /* no filter */
+ pane->dirc++;
+ }
+ }
+
+ if (pane->filter == NULL) {
+ clear_pane(pane);
+ pane->dirc -= 2;
+ }
+
+ if (pane->filter != NULL) {
+ if (filtercount > 0) {
+ pane->dirc = filtercount;
+ clear_pane(pane);
+ pane->hdir = 1;
+ } else if (filtercount == 0) {
+ if (closedir(dir) < 0)
+ return -1;
+ pane->dirc = oldc;
+ return -1;
+ }
+ }
+
+ /* print current directory title */
+ pane->dircol.fg |= TB_BOLD;
+ printf_tb(pane->x_srt, 0, pane->dircol, " %.*s", hwidth, pane->dirn);
+
+ if (pane->filter == NULL) /* dont't watch when filtering */
+ if (addwatch(pane) < 0)
+ print_error("can't add watch");
+
+ /* empty directory */
+ if (pane->dirc == 0) {
+ clear_status();
+ if (closedir(dir) < 0)
+ return -1;
+ return 0;
+ }
+
+ rewinddir(dir); /* reset position */
+ set_direntr(
+ pane, entry, dir, pane->filter); /* create array of entries */
+ qsort(pane->direntr, pane->dirc, sizeof(Entry), sort_name);
+ refresh_pane(pane);
+
+ if (pane->hdir > pane->dirc)
+ pane->hdir = pane->dirc;
+
+ if (pane == cpane && pane->dirc > 0)
+ add_hi(pane, pane->hdir - 1);
+
+ if (closedir(dir) < 0)
+ return -1;
+ return 0;
+}
+
+static void
+t_resize(void)
+{
+ tb_clear();
+ draw_frame();
+ panes[Left].x_end = (twidth / 2) - 1;
+ panes[Right].x_end = twidth - 1;
+ panes[Right].x_srt = (twidth / 2) + 2;
+ refresh_pane(&panes[Left]);
+ refresh_pane(&panes[Right]);
+ if (cpane->dirc > 0)
+ add_hi(cpane, cpane->hdir - 1);
+ tb_present();
+}
+
+static void
+get_editor(void)
+{
+ editor[0] = getenv("EDITOR");
+ editor[1] = NULL;
+
+ if (editor[0] == NULL)
+ editor[0] = fed;
+}
+
+static void
+get_shell(void)
+{
+ shell[0] = getenv("SHELL");
+ shell[1] = NULL;
+
+ if (shell[0] == NULL)
+ shell[0] = sh;
+}
+
+static void
+set_panes(void)
+{
+ char *home;
+ char cwd[MAX_P];
+
+ home = getenv("HOME");
+ if (home == NULL)
+ home = "/";
+ if ((getcwd(cwd, sizeof(cwd)) == NULL))
+ strncpy(cwd, home, MAX_P);
+
+ pane_idx = Left; /* cursor pane */
+ cpane = &panes[pane_idx];
+
+ panes[Left].pane_id = 0;
+ panes[Left].x_srt = 2;
+ panes[Left].x_end = (twidth / 2) - 1;
+ panes[Left].dircol = cpanell;
+ panes[Left].firstrow = 0;
+ panes[Left].direntr = ecalloc(0, sizeof(Entry));
+ strncpy(panes[Left].dirn, cwd, MAX_P);
+ panes[Left].hdir = 1;
+ panes[Left].inotify_wd = -1;
+ panes[Left].parent_row = 1;
+
+ panes[Right].pane_id = 1;
+ panes[Right].x_srt = (twidth / 2) + 2;
+ panes[Right].x_end = twidth - 1;
+ panes[Right].dircol = cpanelr;
+ panes[Right].firstrow = 0;
+ panes[Right].direntr = ecalloc(0, sizeof(Entry));
+ strncpy(panes[Right].dirn, home, MAX_P);
+ panes[Right].hdir = 1;
+ panes[Right].inotify_wd = -1;
+ panes[Right].parent_row = 1;
+}
+
+static void
+draw_frame(void)
+{
+ int i;
+ theight = tb_height();
+ twidth = tb_width();
+ hwidth = (twidth / 2) - 4;
+ scrheight = theight - 2;
+
+ /* 2 horizontal lines */
+ for (i = 1; i < twidth - 1; ++i) {
+ tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
+ tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
+ }
+
+ /* 4 vertical lines */
+ for (i = 1; i < theight - 1; ++i) {
+ tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
+ tb_change_cell(
+ (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
+ tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
+ cframe.bg);
+ tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
+ }
+
+ /* 4 corners */
+ tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
+ tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
+ tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
+ tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
+
+ /* 2 middel top and bottom */
+ tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
+ tb_change_cell(
+ (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
+}
+
+void
+th_handler(int num)
+{
+ if (fork_pid > 0) /* while forking don't listdir() */
+ return;
+ (void)num;
+ PERROR(listdir(&panes[Left]));
+ PERROR(listdir(&panes[Right]));
+ tb_present();
+}
+
+static int
+start_signal(void)
+{
+ struct sigaction sa;
+
+ main_pid = getpid();
+ sa.sa_handler = th_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ return sigaction(SIGUSR1, &sa, NULL);
+}
+
+static void
+refresh(const Arg *arg)
+{
+ kill(main_pid, SIGWINCH);
+}
+
+static void
+start(void)
+{
+ switch (tb_init()) {
+ case TB_EFAILED_TO_OPEN_TTY:
+ die("TB_EFAILED_TO_OPEN_TTY");
+ break;
+ case TB_EUNSUPPORTED_TERMINAL:
+ die("TB_EUNSUPPORTED_TERMINAL");
+ break;
+ case TB_EPIPE_TRAP_ERROR:
+ die("TB_EUNSUPPORTED_TERMINAL");
+ break;
+ case 0:
+ break;
+ default:
+ die("UNKNOWN FAILURE");
+ }
+
+ if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
+ if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
+ die("output error");
+ draw_frame();
+ set_panes();
+ get_editor();
+ get_shell();
+ PERROR(start_signal() < 0);
+ PERROR(fsev_init() < 0);
+ PERROR(listdir(&panes[Left]) < 0);
+ PERROR(listdir(&panes[Right]) < 0);
+ tb_present();
+
+ pthread_create(&fsev_thread, NULL, read_th, NULL);
+ start_ev();
+}
+
+int
+main(int argc, char *argv[])
+{
+#if defined(__OpenBSD__)
+ if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
+ NULL) == -1)
+ die("pledge");
+#endif /* __OpenBSD__ */
+ if (argc == 1)
+ start();
+ else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
+ die("sfm-" VERSION);
+ else
+ die("usage: sfm [-v]");
+ return 0;
+}