[fix #16] visual commands cp, mv, rm
[sfm.git] / sfm.c
blobfc32b7989501ac27d3911194c4b1614757deb8ac
1 /* See LICENSE file for copyright and license details. */
3 #if defined(__linux__)
4 #define _GNU_SOURCE
5 #elif defined(__APPLE__)
6 #define _DARWIN_C_SOURCE
7 #elif defined(__FreeBSD__)
8 #define __BSD_VISIBLE 1
9 #endif
10 #include <sys/types.h>
11 #include <sys/resource.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sys/wait.h>
15 #if defined(__linux__)
16 #include <sys/inotify.h>
17 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
18 defined(__APPLE__)
19 #include <sys/event.h>
20 #endif
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <grp.h>
26 #include <libgen.h>
27 #include <pthread.h>
28 #include <pwd.h>
29 #include <stdarg.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <unistd.h>
37 #include "termbox.h"
38 #include "util.h"
40 /* macros */
41 #define MAX_P 4096
42 #define MAX_N 255
43 #define MAX_USRI 32
44 #define MAX_EXT 4
45 #define MAX_STATUS 255
46 #define MAX_LINE 4096
47 #define MAX_USRN 32
48 #define MAX_GRPN 32
49 #define MAX_DTF 32
50 #define CURSOR cpane->direntr[cpane->hdir - 1]
52 /* enums */
53 enum { AddHi, NoHi }; /* add highlight in listdir */
55 /* typedef */
56 typedef struct {
57 char name[MAX_N];
58 gid_t group;
59 mode_t mode;
60 off_t size;
61 time_t dt;
62 uid_t user;
63 } Entry;
65 typedef struct {
66 uint16_t fg;
67 uint16_t bg;
68 } Cpair;
70 typedef struct {
71 int pane_id;
72 char dirn[MAX_P]; // dir name cwd
73 char *filter;
74 Entry *direntr; // dir entries
75 int dirx; // pane cwd x pos
76 int dirc; // dir entries sum
77 int hdir; // highlighted dir
78 int firstrow;
79 int parent_firstrow;
80 int parent_row; // FIX
81 Cpair dircol;
82 int inotify_wd;
83 int event_fd;
84 } Pane;
86 typedef struct {
87 uint32_t ch;
88 char path[MAX_P];
89 } Bookmark;
91 typedef struct {
92 const char **ext;
93 size_t exlen;
94 const void *v;
95 } Rule;
97 typedef union {
98 uint16_t key; /* one of the TB_KEY_* constants */
99 uint32_t ch; /* unicode character */
100 } Evkey;
102 typedef struct {
103 const Evkey evkey;
104 void (*func)(void);
105 } Key;
107 /* function declarations */
108 static void print_tb(const char *, int, int, uint16_t, uint16_t);
109 static void printf_tb(int, int, Cpair, const char *, ...);
110 static void print_status(Cpair, const char *, ...);
111 static void print_xstatus(char, int);
112 static void print_error(char *);
113 static void print_prompt(char *);
114 static void print_info(char *);
115 static void print_row(Pane *, size_t, Cpair);
116 static void clear(int, int, int, uint16_t);
117 static void clear_status(void);
118 static void clear_pane(void);
119 static void add_hi(Pane *, size_t);
120 static void rm_hi(Pane *, size_t);
121 static int check_dir(char *);
122 static mode_t chech_execf(mode_t);
123 static int sort_name(const void *const, const void *const);
124 static void get_dirp(char *);
125 static char *get_ext(char *);
126 static int get_fdt(char *, time_t);
127 static char *get_fgrp(gid_t);
128 static char *get_fperm(mode_t);
129 static char *get_fsize(off_t);
130 static char *get_fullpath(char *, char *);
131 static char *get_fusr(uid_t);
132 static void get_dirsize(char *, off_t *);
133 static void get_hicol(Cpair *, mode_t);
134 static int delent(char *);
135 static void calcdir(void);
136 static void crnd(void);
137 static void crnf(void);
138 static void delfd(void);
139 static void mvbk(void);
140 static void mvbtm(void);
141 static void mvdwn(void);
142 static void mvdwns(void);
143 static void mvfwd(void);
144 static void mvmid(void);
145 static void mvtop(void);
146 static void mvup(void);
147 static void mvups(void);
148 static void scrdwn(void);
149 static void scrdwns(void);
150 static void scrup(void);
151 static void scrups(void);
152 static int get_usrinput(char *, size_t, const char *, ...);
153 static int frules(char *);
154 static int spawn(const void *, char *);
155 static int opnf(char *);
156 static int fsev_init(void);
157 static int addwatch(void);
158 static int read_events(void);
159 static void rmwatch(Pane *);
160 static void fsev_shdn(void);
161 static ssize_t findbm(uint32_t);
162 static void start_filter(void);
163 static void start_vmode(void);
164 static void exit_vmode(void);
165 static void selup(void);
166 static void seldwn(void);
167 static void selall(void);
168 static void selref(void);
169 static void selynk(void);
170 static void selcalc(void);
171 static void paste(void);
172 static void selmv(void);
173 static void seldel(void);
174 static void init_files(void);
175 static void free_files(void);
176 static void yank(void);
177 static void rname(void);
178 static void switch_pane(void);
179 static void quit(void);
180 static void grabkeys(struct tb_event *, Key *, size_t);
181 static void *read_th(void *arg);
182 static void start_ev(void);
183 static void refresh_pane(void);
184 static void set_direntr(struct dirent *, DIR *, char *);
185 static int listdir(int);
186 static void t_resize(void);
187 static void set_panes(void);
188 static void draw_frame(void);
189 static void start(void);
191 /* global variables */
192 static pthread_t fsev_thread;
193 static Pane pane_r, pane_l, *cpane;
194 static char *editor[2];
195 static char fed[] = "vi";
196 static int theight, twidth, scrheight;
197 static size_t selection_size = 0;
198 static char yank_file[MAX_P];
199 static int *selection;
200 static int cont_vmode = 0;
201 static char **selected_files;
202 #if defined _SYS_INOTIFY_H
203 #define READEVSZ 16
204 static int inotify_fd;
205 #elif defined _SYS_EVENT_H_
206 #define READEVSZ 0
207 static int kq;
208 struct kevent evlist[2]; /* events we want to monitor */
209 struct kevent chlist[2]; /* events that were triggered */
210 static struct timespec gtimeout;
211 #endif
212 #if defined(__linux__) || defined(__FreeBSD__)
213 #define OFF_T "%ld"
214 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
215 #define OFF_T "%lld"
216 #endif
218 /* configuration, allows nested code to access above variables */
219 #include "config.h"
221 /* function implementations */
222 static void
223 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
225 while (*str != '\0') {
226 uint32_t uni = 0;
227 str += tb_utf8_char_to_unicode(&uni, str);
228 tb_change_cell(x, y, uni, fg, bg);
229 x++;
233 static void
234 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
236 char buf[MAX_LINE];
237 va_list vl;
238 va_start(vl, fmt);
239 (void)vsnprintf(buf, MAX_LINE, fmt, vl);
240 va_end(vl);
241 print_tb(buf, x, y, col.fg, col.bg);
244 static void
245 print_status(Cpair col, const char *fmt, ...)
247 char buf[MAX_STATUS];
248 va_list vl;
249 va_start(vl, fmt);
250 (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
251 va_end(vl);
252 clear_status();
253 print_tb(buf, 1, theight - 1, col.fg, col.bg);
256 static void
257 print_xstatus(char c, int x)
259 uint32_t uni = 0;
260 (void)tb_utf8_char_to_unicode(&uni, &c);
261 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
264 static void
265 print_error(char *errmsg)
267 print_status(cerr, errmsg);
270 static void
271 print_prompt(char *prompt)
273 print_status(cprompt, prompt);
276 static void
277 print_info(char *dirsize)
279 char *sz, *ur, *gr, *dt, *prm;
281 dt = ecalloc(MAX_DTF, sizeof(char));
283 prm = get_fperm(CURSOR.mode);
284 ur = get_fusr(CURSOR.user);
285 gr = get_fgrp(CURSOR.group);
287 if (get_fdt(dt, CURSOR.dt) < 0)
288 *dt = '\0';
290 if (S_ISREG(CURSOR.mode)) {
291 sz = get_fsize(CURSOR.size);
292 } else {
293 if (dirsize == NULL) {
294 sz = ecalloc(1, sizeof(char));
295 *sz = '\0';
296 } else {
297 sz = dirsize;
301 print_status(cstatus, "%02d/%02d %s %s:%s %s %s", cpane->hdir,
302 cpane->dirc, prm, ur, gr, dt, sz);
304 free(prm);
305 free(ur);
306 free(gr);
307 free(dt);
308 free(sz);
311 static void
312 print_row(Pane *pane, size_t entpos, Cpair col)
314 int x, y;
315 char *result;
316 char buf[MAX_P];
317 char lnk_full[MAX_P];
318 int width;
320 width = (twidth / 2) - 4;
321 result = basename(pane->direntr[entpos].name);
322 x = pane->dirx;
323 y = entpos - cpane->firstrow + 1;
325 if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
326 if (realpath(pane->direntr[entpos].name, buf) != NULL) {
327 (void)snprintf(
328 lnk_full, MAX_N, "%s -> %s", result, buf);
329 result = lnk_full;
333 printf_tb(x, y, col, "%*.*s", ~width, width, result);
336 static void
337 clear(int sx, int ex, int y, uint16_t bg)
339 /* clear line from to */
340 /* x = line number vertical */
341 /* y = column number horizontal */
342 int i;
343 for (i = sx; i < ex; i++) {
344 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
348 static void
349 clear_status(void)
351 clear(1, twidth - 1, theight - 1, cstatus.bg);
354 static void
355 clear_pane(void)
357 int i, ex, y;
358 y = 0, i = 0, ex = 0;
360 if (cpane->pane_id == pane_l.pane_id)
361 ex = (twidth / 2) - 1;
362 else if (cpane->pane_id == pane_r.pane_id)
363 ex = twidth - 1;
365 while (i < scrheight) {
366 clear(cpane->dirx, ex, y, TB_DEFAULT);
367 i++;
368 y++;
371 /* draw top line */
372 for (y = cpane->dirx; y < ex; ++y) {
373 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
377 static void
378 add_hi(Pane *pane, size_t entpos)
380 Cpair col;
381 get_hicol(&col, pane->direntr[entpos].mode);
382 col.fg |= TB_REVERSE | TB_BOLD;
383 col.bg |= TB_REVERSE;
384 print_row(pane, entpos, col);
387 static void
388 rm_hi(Pane *pane, size_t entpos)
390 Cpair col;
391 get_hicol(&col, pane->direntr[entpos].mode);
392 print_row(pane, entpos, col);
395 static int
396 check_dir(char *path)
398 DIR *dir;
399 dir = opendir(path);
401 if (dir == NULL) {
402 if (errno == ENOTDIR) {
403 return 1;
404 } else {
405 return -1;
409 if (closedir(dir) < 0)
410 return -1;
412 return 0;
415 static mode_t
416 chech_execf(mode_t mode)
418 if (S_ISREG(mode))
419 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
420 return 0;
423 static int
424 sort_name(const void *const A, const void *const B)
426 int result;
427 mode_t data1 = (*(Entry *)A).mode;
428 mode_t data2 = (*(Entry *)B).mode;
430 if (data1 < data2) {
431 return -1;
432 } else if (data1 == data2) {
433 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
434 return result;
435 } else {
436 return 1;
440 static void
441 get_dirp(char *cdir)
443 int counter, len, i;
445 counter = 0;
446 len = strnlen(cdir, MAX_P);
447 if (len == 1)
448 return;
450 for (i = len - 1; i > 1; i--) {
451 if (cdir[i] == '/')
452 break;
453 else
454 counter++;
457 cdir[len - counter - 1] = '\0';
460 static char *
461 get_ext(char *str)
463 char *ext;
464 char dot;
465 size_t counter, len, i;
467 dot = '.';
468 counter = 0;
469 len = strnlen(str, MAX_N);
471 for (i = len - 1; i > 0; i--) {
472 if (str[i] == dot) {
473 break;
474 } else {
475 counter++;
479 ext = ecalloc(MAX_EXT + 1, sizeof(char));
480 strncpy(ext, &str[len - counter], MAX_EXT);
481 ext[MAX_EXT] = '\0';
482 return ext;
485 static int
486 get_fdt(char *result, time_t status)
488 struct tm lt;
489 localtime_r(&status, &lt);
490 return strftime(result, MAX_DTF, dtfmt, &lt);
493 static char *
494 get_fgrp(gid_t status)
496 char *result;
497 struct group *gr;
499 result = ecalloc(MAX_GRPN, sizeof(char));
500 gr = getgrgid(status);
501 if (gr == NULL)
502 (void)snprintf(result, MAX_GRPN, "%u", status);
503 else
504 strncpy(result, gr->gr_name, MAX_GRPN);
506 result[MAX_GRPN - 1] = '\0';
507 return result;
510 static char *
511 get_fperm(mode_t mode)
513 char *buf;
514 size_t i;
516 const char chars[] = "rwxrwxrwx";
517 buf = ecalloc(11, sizeof(char));
519 if (S_ISDIR(mode))
520 buf[0] = 'd';
521 else if (S_ISREG(mode))
522 buf[0] = '-';
523 else if (S_ISLNK(mode))
524 buf[0] = 'l';
525 else if (S_ISBLK(mode))
526 buf[0] = 'b';
527 else if (S_ISCHR(mode))
528 buf[0] = 'c';
529 else if (S_ISFIFO(mode))
530 buf[0] = 'p';
531 else if (S_ISSOCK(mode))
532 buf[0] = 's';
533 else
534 buf[0] = '?';
536 for (i = 1; i < 10; i++) {
537 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
539 buf[10] = '\0';
541 return buf;
544 static char *
545 get_fsize(off_t size)
547 char *result; /* need to be freed */
548 char unit;
549 int result_len;
550 int counter;
552 counter = 0;
553 result_len = 6; /* 9999X/0 */
554 result = ecalloc(result_len, sizeof(char));
556 while (size >= 1000) {
557 size /= 1024;
558 ++counter;
561 switch (counter) {
562 case 0:
563 unit = 'B';
564 break;
565 case 1:
566 unit = 'K';
567 break;
568 case 2:
569 unit = 'M';
570 break;
571 case 3:
572 unit = 'G';
573 break;
574 case 4:
575 unit = 'T';
576 break;
577 default:
578 unit = '?';
581 if (snprintf(result, result_len, OFF_T "%c", size, unit) < 0)
582 strncat(result, "???", result_len);
584 return result;
587 static char *
588 get_fullpath(char *first, char *second)
590 char *full_path;
592 full_path = ecalloc(MAX_P, sizeof(char));
594 if (strncmp(first, "/", MAX_P) == 0)
595 (void)snprintf(full_path, MAX_P, "/%s", second);
596 else
597 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
599 return full_path;
602 static char *
603 get_fusr(uid_t status)
605 char *result;
606 struct passwd *pw;
608 result = ecalloc(MAX_USRN, sizeof(char));
609 pw = getpwuid(status);
610 if (pw == NULL)
611 (void)snprintf(result, MAX_USRN, "%u", status);
612 else
613 strncpy(result, pw->pw_name, MAX_USRN);
615 result[MAX_USRN - 1] = '\0';
616 return result;
619 static void
620 get_dirsize(char *fullpath, off_t *fullsize)
622 DIR *dir;
623 char *ent_full;
624 mode_t mode;
625 struct dirent *entry;
626 struct stat status;
628 dir = opendir(fullpath);
629 if (dir == NULL) {
630 return;
633 while ((entry = readdir(dir)) != 0) {
634 if ((strncmp(entry->d_name, ".", 2) == 0 ||
635 strncmp(entry->d_name, "..", 3) == 0))
636 continue;
638 ent_full = get_fullpath(fullpath, entry->d_name);
639 if (lstat(ent_full, &status) == 0) {
640 mode = status.st_mode;
641 if (S_ISDIR(mode)) {
642 get_dirsize(ent_full, fullsize);
643 free(ent_full);
644 } else {
645 *fullsize += status.st_size;
646 free(ent_full);
651 closedir(dir);
652 clear_status();
655 static void
656 get_hicol(Cpair *col, mode_t mode)
658 *col = cfile;
659 if (S_ISDIR(mode))
660 *col = cdir;
661 else if (S_ISLNK(mode))
662 *col = cother;
663 else if (chech_execf(mode) > 0)
664 *col = cexec;
667 static int
668 delent(char *fullpath)
670 char *inp_conf;
671 int conf_len = 4;
672 char conf[] = "yes";
674 inp_conf = ecalloc(conf_len, sizeof(char));
675 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
676 (strncmp(inp_conf, conf, conf_len) != 0)) {
677 free(inp_conf);
678 return 1; /* canceled by user or wrong inp_conf */
680 free(inp_conf);
682 return spawn(rm_cmd, fullpath);
685 static void
686 calcdir(void)
688 if (!S_ISDIR(CURSOR.mode))
689 return;
691 off_t *fullsize;
692 char *csize;
694 fullsize = ecalloc(1, sizeof(off_t));
695 get_dirsize(CURSOR.name, fullsize);
696 csize = get_fsize(*fullsize);
698 CURSOR.size = *fullsize;
699 print_info(csize);
700 free(fullsize);
703 static void
704 crnd(void)
706 char *user_input, *path;
708 user_input = ecalloc(MAX_USRI, sizeof(char));
709 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
710 free(user_input);
711 return;
714 path = ecalloc(MAX_P, sizeof(char));
715 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
716 free(user_input);
717 free(path);
718 return;
721 if (mkdir(path, ndir_perm) < 0)
722 print_error(strerror(errno));
724 free(user_input);
725 free(path);
728 static void
729 crnf(void)
731 char *user_input, *path;
732 int rf;
734 user_input = ecalloc(MAX_USRI, sizeof(char));
735 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
736 free(user_input);
737 return;
740 path = ecalloc(MAX_P, sizeof(char));
741 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
742 free(user_input);
743 free(path);
744 return;
747 rf = open(path, O_CREAT | O_EXCL, nf_perm);
749 if (rf < 0)
750 print_error(strerror(errno));
751 else if (close(rf) < 0)
752 print_error(strerror(errno));
754 free(user_input);
755 free(path);
758 static void
759 delfd(void)
761 switch (delent(CURSOR.name)) {
762 case -1:
763 print_error(strerror(errno));
764 break;
765 case 0:
766 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
767 cpane->hdir--;
768 break;
772 static void
773 mvbk(void)
775 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
776 return;
779 get_dirp(cpane->dirn);
780 if (check_dir(cpane->dirn) < 0) {
781 print_error(strerror(errno));
782 return;
785 rmwatch(cpane);
786 cpane->firstrow = cpane->parent_firstrow;
787 cpane->hdir = cpane->parent_row;
788 if (listdir(AddHi) < 0)
789 print_error(strerror(errno));
790 cpane->parent_firstrow = 0;
791 cpane->parent_row = 1;
794 static void
795 mvbtm(void)
797 if (cpane->dirc < 1)
798 return;
799 if (cpane->dirc > scrheight) {
800 rm_hi(cpane, cpane->hdir - 1);
801 cpane->hdir = cpane->dirc;
802 cpane->firstrow = cpane->dirc - scrheight + 1;
803 refresh_pane();
804 add_hi(cpane, cpane->hdir - 1);
805 } else {
806 rm_hi(cpane, cpane->hdir - 1);
807 cpane->hdir = cpane->dirc;
808 add_hi(cpane, cpane->hdir - 1);
810 print_info(NULL);
813 static void
814 mvdwn(void)
816 if (cpane->dirc < 1)
817 return;
818 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
819 rm_hi(cpane, cpane->hdir - 1);
820 cpane->hdir++;
821 add_hi(cpane, cpane->hdir - 1);
822 } else {
823 mvdwns(); /* scroll */
825 print_info(NULL);
828 static void
829 mvdwns(void)
831 int real;
832 real = cpane->hdir - 1 - cpane->firstrow;
834 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
835 cpane->firstrow++;
836 rm_hi(cpane, cpane->hdir - 1);
837 cpane->hdir++;
838 refresh_pane();
839 add_hi(cpane, cpane->hdir - 1);
840 } else if (cpane->hdir < cpane->dirc) {
841 rm_hi(cpane, cpane->hdir - 1);
842 cpane->hdir++;
843 add_hi(cpane, cpane->hdir - 1);
847 static void
848 mvfwd(void)
850 rmwatch(cpane);
851 if (cpane->dirc < 1)
852 return;
853 int s;
855 switch (check_dir(CURSOR.name)) {
856 case 0:
857 strncpy(cpane->dirn, CURSOR.name, MAX_P);
858 cpane->parent_row = cpane->hdir;
859 cpane->parent_firstrow = cpane->firstrow;
860 cpane->hdir = 1;
861 cpane->firstrow = 0;
862 if (listdir(AddHi) < 0)
863 print_error(strerror(errno));
864 break;
865 case 1: /* not a directory open file */
866 tb_shutdown();
867 s = opnf(CURSOR.name);
868 if (tb_init() != 0)
869 die("tb_init");
870 t_resize();
871 if (s < 0)
872 print_error("process failed non-zero exit");
873 break;
874 case -1: /* failed to open directory */
875 print_error(strerror(errno));
879 static void
880 mvmid(void)
882 if (cpane->dirc < 1)
883 return;
884 rm_hi(cpane, cpane->hdir - 1);
885 if (cpane->dirc < scrheight / 2)
886 cpane->hdir = (cpane->dirc + 1) / 2;
887 else
888 cpane->hdir = (scrheight / 2) + cpane->firstrow;
889 add_hi(cpane, cpane->hdir - 1);
890 print_info(NULL);
893 static void
894 mvtop(void)
896 if (cpane->dirc < 1)
897 return;
898 if (cpane->dirc > scrheight) {
899 rm_hi(cpane, cpane->hdir - 1);
900 cpane->hdir = 1;
901 cpane->firstrow = 0;
902 refresh_pane();
903 add_hi(cpane, cpane->hdir - 1);
904 } else {
905 rm_hi(cpane, cpane->hdir - 1);
906 cpane->hdir = 1;
907 add_hi(cpane, cpane->hdir - 1);
908 print_info(NULL);
912 static void
913 mvup(void)
915 if (cpane->dirc < 1)
916 return;
917 if (cpane->dirc < scrheight && cpane->hdir > 1) {
918 rm_hi(cpane, cpane->hdir - 1);
919 cpane->hdir--;
920 add_hi(cpane, cpane->hdir - 1);
921 } else {
922 mvups(); /* scroll */
924 print_info(NULL);
927 static void
928 mvups(void)
930 size_t real;
931 real = cpane->hdir - 1 - cpane->firstrow;
933 if (cpane->firstrow > 0 && real < 1 + scrsp) {
934 cpane->firstrow--;
935 rm_hi(cpane, cpane->hdir - 1);
936 cpane->hdir--;
937 refresh_pane();
938 add_hi(cpane, cpane->hdir - 1);
939 } else if (cpane->hdir > 1) {
940 rm_hi(cpane, cpane->hdir - 1);
941 cpane->hdir--;
942 add_hi(cpane, cpane->hdir - 1);
946 static void
947 scrdwn(void)
949 if (cpane->dirc < 1)
950 return;
951 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
952 if (cpane->hdir < cpane->dirc - scrmv) {
953 rm_hi(cpane, cpane->hdir - 1);
954 cpane->hdir += scrmv;
955 add_hi(cpane, cpane->hdir - 1);
956 } else {
957 mvbtm();
959 } else {
960 scrdwns();
962 print_info(NULL);
965 static void
966 scrdwns(void)
968 int real, dynmv;
970 real = cpane->hdir - cpane->firstrow;
971 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
973 if (real + scrmv + 1 > scrheight &&
974 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
975 cpane->firstrow += dynmv;
976 rm_hi(cpane, cpane->hdir - 1);
977 cpane->hdir += scrmv;
978 refresh_pane();
979 add_hi(cpane, cpane->hdir - 1);
980 } else {
981 if (cpane->hdir < cpane->dirc - scrmv - 1) {
982 rm_hi(cpane, cpane->hdir - 1);
983 cpane->hdir += scrmv;
984 add_hi(cpane, cpane->hdir - 1);
985 } else {
986 mvbtm();
991 static void
992 scrup(void)
994 if (cpane->dirc < 1)
995 return;
996 if (cpane->dirc < scrheight && cpane->hdir > 1) {
997 if (cpane->hdir > scrmv) {
998 rm_hi(cpane, cpane->hdir - 1);
999 cpane->hdir = cpane->hdir - scrmv;
1000 add_hi(cpane, cpane->hdir - 1);
1001 print_info(NULL);
1002 } else {
1003 mvtop();
1005 } else {
1006 scrups();
1010 static void
1011 scrups(void)
1013 int real, dynmv;
1014 real = cpane->hdir - cpane->firstrow;
1015 dynmv = MIN(cpane->firstrow, scrmv);
1017 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1018 cpane->firstrow -= dynmv;
1019 rm_hi(cpane, cpane->hdir - 1);
1020 cpane->hdir -= scrmv;
1021 refresh_pane();
1022 add_hi(cpane, cpane->hdir - 1);
1023 } else {
1024 if (cpane->hdir > scrmv + 1) {
1025 rm_hi(cpane, cpane->hdir - 1);
1026 cpane->hdir -= scrmv;
1027 add_hi(cpane, cpane->hdir - 1);
1028 } else {
1029 mvtop();
1034 static int
1035 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1037 int height = tb_height();
1038 size_t startat;
1039 struct tb_event fev;
1040 size_t counter = (size_t)1;
1041 char empty = ' ';
1042 int x = 0;
1043 int name_size = 0;
1044 char buf[256];
1046 clear_status();
1048 va_list vl;
1049 Cpair col;
1050 col = cprompt;
1051 va_start(vl, fmt);
1052 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1053 va_end(vl);
1054 print_tb(buf, 1, height - 1, col.fg, col.bg);
1055 startat = name_size + 1;
1056 tb_set_cursor((int)(startat + 1), height - 1);
1057 tb_present();
1059 while (tb_poll_event(&fev) != 0) {
1060 switch (fev.type) {
1061 case TB_EVENT_KEY:
1062 if (fev.key == TB_KEY_ESC) {
1063 tb_set_cursor(-1, -1);
1064 clear_status();
1065 return -1;
1068 if (fev.key == TB_KEY_BACKSPACE ||
1069 fev.key == TB_KEY_BACKSPACE2) {
1070 if (BETWEEN(counter, 2, sout)) {
1071 out[x - 1] = '\0';
1072 counter--;
1073 x--;
1074 print_xstatus(empty, startat + counter);
1075 tb_set_cursor(
1076 startat + counter, theight - 1);
1079 } else if (fev.key == TB_KEY_ENTER) {
1080 tb_set_cursor(-1, -1);
1081 out[counter - 1] = '\0';
1082 return 0;
1084 } else {
1085 if (counter < sout) {
1086 print_xstatus((char)fev.ch,
1087 (startat + counter));
1088 out[x] = (char)fev.ch;
1089 tb_set_cursor((startat + counter + 1),
1090 theight - 1);
1091 counter++;
1092 x++;
1096 tb_present();
1097 break;
1099 default:
1100 return -1;
1104 return -1;
1107 static int
1108 frules(char *ex)
1110 size_t c, d;
1112 for (c = 0; c < LEN(rules); c++)
1113 for (d = 0; d < rules[c].exlen; d++)
1114 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1115 return c;
1116 return -1;
1119 static int
1120 spawn(const void *v, char *fn)
1122 int ws, x, argc;
1123 pid_t pid, r;
1125 x = 0;
1126 argc = 0;
1128 /* count args */
1129 while (((char **)v)[x++] != NULL)
1130 argc++;
1132 char *argv[argc + 2];
1133 for (x = 0; x < argc; x++)
1134 argv[x] = ((char **)v)[x];
1136 argv[argc] = fn;
1137 argv[argc + 1] = NULL;
1139 pid = fork();
1140 switch (pid) {
1141 case -1:
1142 return -1;
1143 case 0:
1144 execvp(argv[0], argv);
1145 exit(EXIT_SUCCESS);
1146 default:
1147 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1148 continue;
1149 if (r == -1)
1150 return -1;
1151 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1152 return -1;
1154 return 0;
1157 static int
1158 opnf(char *fn)
1160 char *ex;
1161 int c;
1163 ex = get_ext(fn);
1164 c = frules(ex);
1165 free(ex);
1167 if (c < 0) /* extension not found open in editor */
1168 return spawn(editor, fn);
1169 else
1170 return spawn((char **)rules[c].v, fn);
1173 static int
1174 fsev_init(void)
1176 #if defined _SYS_INOTIFY_H
1177 inotify_fd = inotify_init();
1178 if (inotify_fd < 0)
1179 return -1;
1180 #elif defined _SYS_EVENT_H_
1181 gtimeout.tv_sec = 1;
1182 kq = kqueue();
1183 if (kq < 0)
1184 return -1;
1185 #endif
1186 return 0;
1189 static int
1190 addwatch(void)
1192 #if defined _SYS_INOTIFY_H
1193 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1194 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1195 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1196 #elif defined _SYS_EVENT_H_
1197 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1198 if (cpane->event_fd < 0)
1199 return cpane->event_fd;
1200 EV_SET(&evlist[cpane->pane_id], cpane->event_fd, EVFILT_VNODE,
1201 EV_ADD | EV_CLEAR,
1202 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
1203 NOTE_REVOKE | NOTE_WRITE,
1204 0, NULL);
1205 return 0;
1206 #endif
1209 static int
1210 read_events(void)
1212 #if defined _SYS_INOTIFY_H
1213 char *p;
1214 ssize_t r;
1215 struct inotify_event *event;
1216 const size_t events = 32;
1217 const size_t evbuflen =
1218 events * (sizeof(struct inotify_event) + MAX_N + 1);
1219 char buf[evbuflen];
1221 if (cpane->inotify_wd < 0)
1222 return -1;
1223 r = read(inotify_fd, buf, evbuflen);
1224 if (r <= 0)
1225 return r;
1227 for (p = buf; p < buf + r;) {
1228 event = (struct inotify_event *)p;
1229 if (!event->wd)
1230 break;
1231 if (event->mask) {
1232 return r;
1235 p += sizeof(struct inotify_event) + event->len;
1237 #elif defined _SYS_EVENT_H_
1238 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1239 #endif
1240 return -1;
1243 static void
1244 rmwatch(Pane *pane)
1246 // if (pane->inotify_wd >= 0)
1247 // inotify_rm_watch(inotify_fd, pane->inotify_wd);
1248 #if defined _SYS_EVENT_H_
1249 close(pane->event_fd);
1250 #endif
1253 static void
1254 fsev_shdn(void)
1256 pthread_cancel(fsev_thread);
1257 #ifndef __APPLE__
1258 pthread_join(fsev_thread, NULL);
1259 #endif
1260 rmwatch(&pane_l);
1261 rmwatch(&pane_r);
1262 #if defined _SYS_INOTIFY_H
1263 close(inotify_fd);
1264 #elif defined _SYS_EVENT_H_
1265 close(kq);
1266 #endif
1269 static ssize_t
1270 findbm(uint32_t event)
1272 ssize_t i;
1274 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1275 if (event == bmarks[i].ch) {
1276 if (check_dir(bmarks[i].path) != 0) {
1277 print_error(strerror(errno));
1278 return -1;
1280 return i;
1283 return -1;
1286 static void
1287 start_filter(void)
1289 if (cpane->dirc < 1)
1290 return;
1291 char *user_input;
1292 user_input = ecalloc(MAX_USRI, sizeof(char));
1293 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1294 free(user_input);
1295 return;
1297 cpane->filter = user_input;
1298 if (listdir(AddHi) < 0)
1299 print_error("no match");
1300 cpane->filter = NULL;
1301 free(user_input);
1304 static void
1305 start_vmode(void)
1307 struct tb_event fev;
1308 if (selection != NULL) {
1309 free(selection);
1310 selection = NULL;
1313 selection = ecalloc(cpane->dirc, sizeof(size_t));
1314 selection[0] = cpane->hdir;
1315 cont_vmode = 0;
1316 print_prompt("-- VISUAL --");
1317 tb_present();
1318 while (tb_poll_event(&fev) != 0) {
1319 switch (fev.type) {
1320 case TB_EVENT_KEY:
1321 grabkeys(&fev, vkeys, vkeyslen);
1322 if (cont_vmode == -1)
1323 return;
1324 tb_present();
1325 break;
1330 static void
1331 exit_vmode(void)
1333 refresh_pane();
1334 add_hi(cpane, cpane->hdir - 1);
1335 cont_vmode = -1;
1338 static void
1339 selup(void)
1341 mvup();
1342 print_prompt("-- VISUAL --");
1343 int index = abs(cpane->hdir - selection[0]);
1345 if (cpane->hdir < selection[0]) {
1346 selection[index] = cpane->hdir;
1347 add_hi(cpane, selection[index]);
1348 } else if (index < cpane->dirc) {
1349 selection[index + 1] = 0;
1351 if (cpane->dirc >= scrheight ||
1352 cpane->hdir <= 1) { /* rehighlight all if scrolling */
1353 selref();
1357 static void
1358 seldwn(void)
1360 mvdwn();
1361 print_prompt("-- VISUAL --");
1362 int index = abs(cpane->hdir - selection[0]);
1364 if (cpane->hdir > selection[0]) {
1365 selection[index] = cpane->hdir;
1366 add_hi(cpane, selection[index] - 2);
1367 } else {
1368 selection[index + 1] = 0;
1370 if (cpane->dirc >= scrheight ||
1371 cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1372 selref();
1376 static void
1377 selall(void)
1379 int i;
1380 for (i = 0; i < cpane->dirc; i++) {
1381 selection[i] = i + 1;
1383 selref();
1386 static void
1387 selref(void)
1389 int i;
1390 for (i = 0; i < cpane->dirc; i++) {
1391 if (selection[i] < (scrheight + cpane->firstrow) &&
1392 selection[i] >
1393 cpane->firstrow) { /* checks if in the frame of the directories */
1394 add_hi(cpane, selection[i] - 1);
1399 static void
1400 selcalc(void)
1402 int j;
1403 selection_size = 0;
1405 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1406 if (selection[j] != 0)
1407 selection_size++;
1408 else
1409 break;
1413 static void
1414 free_files(void)
1416 if (selected_files == NULL)
1417 return;
1418 free(selected_files);
1419 selected_files = NULL;
1422 static void
1423 init_files(void)
1425 size_t i;
1426 free_files();
1428 selcalc();
1429 selected_files = ecalloc(selection_size, sizeof(char *));
1431 for (i = 0; i < selection_size; i++) {
1432 selected_files[i] = cpane->direntr[selection[i] - 1].name;
1436 static void
1437 selynk(void)
1439 init_files();
1440 refresh_pane();
1441 add_hi(cpane, cpane->hdir - 1);
1442 print_status(cprompt, "%zu files are yanked", selection_size);
1443 cont_vmode = -1;
1446 static void
1447 seldel(void)
1449 char *inp_conf;
1450 int conf_len = 4;
1451 char conf[] = "yes";
1452 size_t i;
1454 inp_conf = ecalloc(conf_len, sizeof(char));
1455 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1456 (strncmp(inp_conf, conf, conf_len) != 0)) {
1457 free(inp_conf);
1458 return; /* canceled by user or wrong inp_conf */
1460 free(inp_conf);
1462 init_files();
1464 /* selected_files */
1465 char *seldel_cmd[selection_size + 3];
1466 seldel_cmd[0] = "rm";
1467 seldel_cmd[1] = "-rf";
1468 seldel_cmd[selection_size + 2] = NULL;
1470 /* add files to array */
1471 for (i = 2; i < selection_size + 2; i++) {
1472 seldel_cmd[i] = selected_files[i - 2];
1475 if (spawn(seldel_cmd, NULL) < 0)
1476 print_error(strerror(errno));
1477 else
1478 print_status(cprompt, "%zu files are deleted", selection_size);
1480 if (cpane->dirc > 0)
1481 cpane->hdir = cpane->dirc - selection_size;
1483 free_files();
1484 cont_vmode = -1;
1487 static void
1488 paste(void)
1490 size_t i;
1491 if (strnlen(yank_file, MAX_P) != 0) {
1492 print_status(cprompt, "coping");
1493 if (spawn(cp_cmd, cpane->dirn) != 0)
1494 print_error(strerror(errno));
1495 else
1496 print_status(cprompt, "file copied");
1497 yank_file[0] = '\0'; /* set yank_file len 0 */
1498 return;
1501 print_error("nothing to paste");
1503 if (selected_files == NULL)
1504 return;
1506 /* selected_files */
1507 char *selcp_cmd[selection_size + 3];
1508 selcp_cmd[0] = "cp";
1509 selcp_cmd[1] = "-r";
1510 selcp_cmd[selection_size + 2] = NULL;
1512 /* add files to array */
1513 for (i = 2; i < selection_size + 2; i++) {
1514 selcp_cmd[i] = selected_files[i - 2];
1517 if (spawn(selcp_cmd, cpane->dirn) < 0)
1518 print_error(strerror(errno));
1519 else
1520 print_status(cprompt, "%zu files are copied", selection_size);
1522 free_files();
1525 static void
1526 selmv(void)
1528 size_t i;
1530 if (strnlen(yank_file, MAX_P) != 0) {
1531 print_status(cprompt, "moving");
1532 if (spawn(mv_cmd, cpane->dirn) != 0)
1533 print_error(strerror(errno));
1534 else
1535 print_status(cprompt, "file moved");
1536 yank_file[0] = '\0'; /* set yank_file len 0 */
1537 return;
1540 print_error("nothing to move");
1542 if (selected_files == NULL)
1543 return;
1545 /* selected_files */
1546 char *selmv_cmd[selection_size + 3];
1547 selmv_cmd[0] = "mv";
1548 selmv_cmd[1] = "-r";
1549 selmv_cmd[selection_size + 2] = NULL;
1551 /* add files to array */
1552 for (i = 2; i < selection_size + 2; i++) {
1553 selmv_cmd[i] = selected_files[i - 2];
1556 if (spawn(selmv_cmd, cpane->dirn) < 0)
1557 print_error(strerror(errno));
1558 else
1559 print_status(cprompt, "%zu files are moved", selection_size);
1561 free_files();
1564 static void
1565 rname(void)
1567 char new_name[MAX_P];
1568 char *input_name;
1570 input_name = ecalloc(MAX_N, sizeof(char));
1572 if (get_usrinput(input_name, MAX_N, "rename: %s",
1573 basename(CURSOR.name)) < 0) {
1574 free(input_name);
1575 return;
1578 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1579 free(input_name);
1580 print_error(strerror(errno));
1581 return;
1584 char *rename_cmd[] = { "mv", CURSOR.name, new_name, NULL };
1585 if (spawn(rename_cmd, NULL) < 0)
1586 print_error(strerror(errno));
1588 free(input_name);
1591 static void
1592 yank(void)
1594 strncpy(yank_file, CURSOR.name, MAX_P);
1595 print_status(cprompt, "1 file is yanked", selection_size);
1598 static void
1599 switch_pane(void)
1601 if (cpane->dirc > 0)
1602 rm_hi(cpane, cpane->hdir - 1);
1603 if (cpane == &pane_l)
1604 cpane = &pane_r;
1605 else if (cpane == &pane_r)
1606 cpane = &pane_l;
1607 if (cpane->dirc > 0) {
1608 add_hi(cpane, cpane->hdir - 1);
1609 print_info(NULL);
1610 } else {
1611 clear_status();
1615 static void
1616 quit(void)
1618 if (cont_vmode == -1) { /* check if selection was allocated */
1619 free(selection);
1620 if (selected_files != NULL)
1621 free_files();
1623 free(pane_l.direntr);
1624 free(pane_r.direntr);
1625 fsev_shdn();
1626 tb_shutdown();
1627 exit(EXIT_SUCCESS);
1630 static void
1631 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1633 size_t i;
1634 ssize_t b;
1636 for (i = 0; i < max_keys; i++) {
1637 if (event->ch != 0) {
1638 if (event->ch == key[i].evkey.ch) {
1639 key[i].func();
1640 return;
1642 } else if (event->key != 0) {
1643 if (event->key == key[i].evkey.key) {
1644 key[i].func();
1645 return;
1650 /* bookmarks */
1651 b = findbm(event->ch);
1652 if (b < 0)
1653 return;
1654 rmwatch(cpane);
1655 strncpy(cpane->dirn, bmarks[b].path, MAX_P);
1656 cpane->firstrow = 0;
1657 cpane->parent_row = 1;
1658 cpane->hdir = 1;
1659 if (listdir(AddHi) < 0)
1660 print_error(strerror(errno));
1663 void *
1664 read_th(void *arg)
1666 int i;
1667 while (1) {
1669 i = read_events();
1671 if (i > READEVSZ) {
1672 listdir(AddHi);
1673 if (cpane == &pane_l) {
1674 cpane = &pane_r;
1675 if (listdir(NoHi) < 0)
1676 print_error(strerror(errno));
1677 cpane = &pane_l;
1678 if (listdir(AddHi) < 0)
1679 print_error(strerror(errno));
1680 } else if (cpane == &pane_r) {
1681 cpane = &pane_l;
1682 if (listdir(NoHi) < 0)
1683 print_error(strerror(errno));
1684 cpane = &pane_r;
1685 if (listdir(AddHi) < 0)
1686 print_error(strerror(errno));
1689 tb_present();
1691 return arg;
1694 static void
1695 start_ev(void)
1697 struct tb_event ev;
1699 while (tb_poll_event(&ev) != 0) {
1700 switch (ev.type) {
1701 case TB_EVENT_KEY:
1702 grabkeys(&ev, nkeys, nkeyslen);
1703 tb_present();
1704 break;
1705 case TB_EVENT_RESIZE:
1706 t_resize();
1707 break;
1708 default:
1709 break;
1712 tb_shutdown();
1715 static void
1716 refresh_pane(void)
1718 size_t y, dyn_max, start_from;
1719 int width;
1720 width = (twidth / 2) - 4;
1721 Cpair col;
1723 y = 1;
1724 start_from = cpane->firstrow;
1725 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1727 /* print each entry in directory */
1728 while (start_from < dyn_max) {
1729 get_hicol(&col, cpane->direntr[start_from].mode);
1730 print_row(cpane, start_from, col);
1731 start_from++;
1732 y++;
1735 if (cpane->dirc > 0)
1736 print_info(NULL);
1737 else
1738 clear_status();
1740 /* print current directory title */
1741 cpane->dircol.fg |= TB_BOLD;
1742 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1745 static void
1746 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1748 int i;
1749 char *tmpfull;
1750 struct stat status;
1752 #define ADD_ENTRY \
1753 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1754 strncpy(cpane->direntr[i].name, tmpfull, MAX_N); \
1755 if (lstat(tmpfull, &status) == 0) { \
1756 cpane->direntr[i].size = status.st_size; \
1757 cpane->direntr[i].mode = status.st_mode; \
1758 cpane->direntr[i].group = status.st_gid; \
1759 cpane->direntr[i].user = status.st_uid; \
1760 cpane->direntr[i].dt = status.st_mtime; \
1762 i++; \
1763 free(tmpfull);
1765 i = 0;
1766 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1767 while ((entry = readdir(dir)) != 0) {
1768 if ((strncmp(entry->d_name, ".", 2) == 0 ||
1769 strncmp(entry->d_name, "..", 3) == 0))
1770 continue;
1772 if (filter == NULL) {
1773 ADD_ENTRY
1774 } else if (filter != NULL) {
1775 if (strcasestr(entry->d_name, filter) != NULL) {
1776 ADD_ENTRY
1781 cpane->dirc = i;
1784 static int
1785 listdir(int hi)
1787 DIR *dir;
1788 struct dirent *entry;
1789 int width;
1790 int filtercount = 0;
1791 size_t oldc = cpane->dirc;
1793 width = (twidth / 2) - 4;
1794 cpane->dirc = 0;
1796 dir = opendir(cpane->dirn);
1797 if (dir == NULL)
1798 return -1;
1800 /* get content and filter sum */
1801 while ((entry = readdir(dir)) != 0) {
1802 if (cpane->filter != NULL) {
1803 if (strcasestr(entry->d_name, cpane->filter) != NULL)
1804 filtercount++;
1805 } else { /* no filter */
1806 cpane->dirc++;
1810 if (cpane->filter == NULL) {
1811 clear_pane();
1812 cpane->dirc -= 2;
1815 if (cpane->filter != NULL) {
1816 if (filtercount > 0) {
1817 cpane->dirc = filtercount;
1818 clear_pane();
1819 cpane->hdir = 1;
1820 } else if (filtercount == 0) {
1821 if (closedir(dir) < 0)
1822 return -1;
1823 cpane->dirc = oldc;
1824 return -1;
1828 /* print current directory title */
1829 cpane->dircol.fg |= TB_BOLD;
1830 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1832 if (cpane->filter == NULL) /* dont't watch when filtering */
1833 if (addwatch() < 0)
1834 print_error("can't add watch");
1836 /* empty directory */
1837 if (cpane->dirc == 0) {
1838 clear_status();
1839 if (closedir(dir) < 0)
1840 return -1;
1841 return 0;
1844 rewinddir(dir); /* reset position */
1845 set_direntr(entry, dir, cpane->filter); /* create array of entries */
1846 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1847 refresh_pane();
1849 if (hi == AddHi && cpane->dirc > 0)
1850 add_hi(cpane, cpane->hdir - 1);
1852 if (closedir(dir) < 0)
1853 return -1;
1854 return 0;
1857 static void
1858 t_resize(void)
1860 /* TODO need refactoring */
1861 tb_clear();
1862 draw_frame();
1863 pane_r.dirx = (twidth / 2) + 2;
1865 if (cpane == &pane_l) {
1866 cpane = &pane_r;
1867 refresh_pane();
1868 cpane = &pane_l;
1869 refresh_pane();
1870 if (cpane->dirc > 0)
1871 add_hi(&pane_l, pane_l.hdir - 1);
1872 } else if (cpane == &pane_r) {
1873 cpane = &pane_l;
1874 refresh_pane();
1875 cpane = &pane_r;
1876 refresh_pane();
1877 if (cpane->dirc > 0)
1878 add_hi(&pane_r, pane_r.hdir - 1);
1881 tb_present();
1884 static void
1885 get_editor(void)
1887 editor[0] = getenv("EDITOR");
1888 editor[1] = NULL;
1890 if (editor[0] == NULL)
1891 editor[0] = fed;
1894 static void
1895 set_panes(void)
1897 char *home;
1898 char cwd[MAX_P];
1900 home = getenv("HOME");
1901 if (home == NULL)
1902 home = "/";
1903 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1904 strncpy(cwd, home, MAX_P);
1906 pane_l.pane_id = 0;
1907 pane_l.dirx = 2;
1908 pane_l.dircol = cpanell;
1909 pane_l.firstrow = 0;
1910 pane_l.direntr = ecalloc(0, sizeof(Entry));
1911 strncpy(pane_l.dirn, cwd, MAX_P);
1912 pane_l.hdir = 1;
1913 pane_l.inotify_wd = -1;
1914 pane_l.parent_row = 1;
1916 pane_r.pane_id = 1;
1917 pane_r.dirx = (twidth / 2) + 2;
1918 pane_r.dircol = cpanelr;
1919 pane_r.firstrow = 0;
1920 pane_r.direntr = ecalloc(0, sizeof(Entry));
1921 strncpy(pane_r.dirn, home, MAX_P);
1922 pane_r.hdir = 1;
1923 pane_r.inotify_wd = -1;
1924 pane_r.parent_row = 1;
1927 static void
1928 draw_frame(void)
1930 int i;
1931 theight = tb_height();
1932 twidth = tb_width();
1933 scrheight = theight - 2;
1935 /* 2 horizontal lines */
1936 for (i = 1; i < twidth - 1; ++i) {
1937 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1938 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1941 /* 4 vertical lines */
1942 for (i = 1; i < theight - 1; ++i) {
1943 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1944 tb_change_cell(
1945 (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
1946 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1947 cframe.bg);
1948 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1951 /* 4 corners */
1952 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1953 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1954 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1955 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1957 /* 2 middel top and bottom */
1958 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1959 tb_change_cell(
1960 (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1963 static void
1964 start(void)
1966 if (tb_init() != 0)
1967 die("tb_init");
1968 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1969 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1970 die("output error");
1972 draw_frame();
1973 set_panes();
1974 get_editor();
1975 if (fsev_init() < 0)
1976 print_error(strerror(errno));
1977 cpane = &pane_r;
1978 if (listdir(NoHi) < 0)
1979 print_error(strerror(errno));
1980 cpane = &pane_l;
1981 if (listdir(AddHi) < 0)
1982 print_error(strerror(errno));
1983 tb_present();
1985 pthread_create(&fsev_thread, NULL, read_th, NULL);
1986 start_ev();
1990 main(int argc, char *argv[])
1992 #ifdef __OpenBSD__
1993 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1994 NULL) == -1)
1995 die("pledge");
1996 #endif /* __OpenBSD__ */
1997 if (argc == 1)
1998 start();
1999 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
2000 die("sfm-" VERSION);
2001 else
2002 die("usage: sfm [-v]");
2003 return 0;