[style] use #if defined()
[sfm.git] / sfm.c
blob7c82d5c718bbdc49cccc0a76fc11a7f6fd206ecb
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 <signal.h>
30 #include <stdarg.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
38 #include "termbox.h"
39 #include "util.h"
41 /* macros */
42 #define MAX_P 4096
43 #define MAX_N 255
44 #define MAX_USRI 32
45 #define MAX_EXT 4
46 #define MAX_STATUS 255
47 #define MAX_LINE 4096
48 #define MAX_USRN 32
49 #define MAX_GRPN 32
50 #define MAX_DTF 32
51 #define CURSOR(x) (x)->direntr[(x)->hdir - 1]
53 /* typedef */
54 typedef struct {
55 char name[MAX_N];
56 gid_t group;
57 mode_t mode;
58 off_t size;
59 time_t dt;
60 uid_t user;
61 } Entry;
63 typedef struct {
64 uint16_t fg;
65 uint16_t bg;
66 } Cpair;
68 typedef struct {
69 int pane_id;
70 char dirn[MAX_P]; // dir name cwd
71 char *filter;
72 Entry *direntr; // dir entries
73 int dirx; // pane cwd x pos
74 int dirc; // dir entries sum
75 int hdir; // highlighted dir
76 int firstrow;
77 int parent_firstrow;
78 int parent_row; // FIX
79 Cpair dircol;
80 int inotify_wd;
81 int event_fd;
82 } Pane;
84 typedef struct {
85 uint32_t ch;
86 char path[MAX_P];
87 } Bookmark;
89 typedef struct {
90 const char **ext;
91 size_t exlen;
92 const void *v;
93 size_t vlen;
94 } Rule;
96 typedef union {
97 uint16_t key; /* one of the TB_KEY_* constants */
98 uint32_t ch; /* unicode character */
99 } Evkey;
101 typedef struct {
102 const Evkey evkey;
103 void (*func)(void);
104 } Key;
106 /* function declarations */
107 static void print_tb(const char *, int, int, uint16_t, uint16_t);
108 static void printf_tb(int, int, Cpair, const char *, ...);
109 static void print_status(Cpair, const char *, ...);
110 static void print_xstatus(char, int);
111 static void print_error(char *);
112 static void print_prompt(char *);
113 static void print_info(Pane *, char *);
114 static void print_row(Pane *, size_t, Cpair);
115 static void clear(int, int, int, uint16_t);
116 static void clear_status(void);
117 static void clear_pane(Pane *);
118 static void add_hi(Pane *, size_t);
119 static void rm_hi(Pane *, size_t);
120 static int check_dir(char *);
121 static mode_t chech_execf(mode_t);
122 static int sort_name(const void *const, const void *const);
123 static void get_dirp(char *);
124 static char *get_ext(char *);
125 static int get_fdt(char *, time_t);
126 static char *get_fgrp(gid_t);
127 static char *get_fperm(mode_t);
128 static char *get_fsize(off_t);
129 static char *get_fullpath(char *, char *);
130 static char *get_fusr(uid_t);
131 static void get_dirsize(char *, off_t *);
132 static void get_hicol(Cpair *, mode_t);
133 static void delent(void);
134 static void calcdir(void);
135 static void crnd(void);
136 static void crnf(void);
137 static void mvbk(void);
138 static void mvbtm(void);
139 static void mvdwn(void);
140 static void mvdwns(void);
141 static void mvfwd(void);
142 static void mvmid(void);
143 static void mvtop(void);
144 static void mvup(void);
145 static void mvups(void);
146 static void scrdwn(void);
147 static void scrdwns(void);
148 static void scrup(void);
149 static void scrups(void);
150 static int get_usrinput(char *, size_t, const char *, ...);
151 static int frules(char *);
152 static int spawn(const void *, size_t, const void *, size_t, char *);
153 static int opnf(char *);
154 static int fsev_init(void);
155 static int addwatch(Pane *);
156 static int read_events(void);
157 static void rmwatch(Pane *);
158 static void fsev_shdn(void);
159 static ssize_t findbm(uint32_t);
160 static void start_filter(void);
161 static void start_vmode(void);
162 static void exit_vmode(void);
163 static void selup(void);
164 static void seldwn(void);
165 static void selall(void);
166 static void selref(void);
167 static void selynk(void);
168 static void selcalc(void);
169 static void paste(void);
170 static void selmv(void);
171 static void seldel(void);
172 static void init_files(void);
173 static void free_files(void);
174 static void yank(void);
175 static void rname(void);
176 static void switch_pane(void);
177 static void quit(void);
178 static void grabkeys(struct tb_event *, Key *, size_t);
179 static void *read_th(void *arg);
180 static void start_ev(void);
181 static void refresh_pane(Pane *);
182 static void set_direntr(Pane *, struct dirent *, DIR *, char *);
183 static int listdir(Pane *);
184 static void t_resize(void);
185 static void set_panes(void);
186 static void draw_frame(void);
187 static void start(void);
189 /* global variables */
190 static pthread_t fsev_thread;
191 static Pane pane_r, pane_l, *cpane;
192 static char *editor[2];
193 static char fed[] = "vi";
194 static int theight, twidth, scrheight;
195 static size_t selection_size = 0;
196 static int *selection;
197 static int cont_vmode = 0;
198 static char **selected_files;
199 pid_t main_pid;
200 #if defined(_SYS_INOTIFY_H)
201 #define READEVSZ 16
202 static int inotify_fd;
203 #elif defined(_SYS_EVENT_H_)
204 #define READEVSZ 0
205 static int kq;
206 struct kevent evlist[2]; /* events we want to monitor */
207 struct kevent chlist[2]; /* events that were triggered */
208 static struct timespec gtimeout;
209 #endif
210 #if defined(__linux__) || defined(__FreeBSD__)
211 #define OFF_T "%ld"
212 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
213 #define OFF_T "%lld"
214 #endif
216 /* configuration, allows nested code to access above variables */
217 #include "config.h"
219 /* function implementations */
220 static void
221 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
223 while (*str != '\0') {
224 uint32_t uni = 0;
225 str += tb_utf8_char_to_unicode(&uni, str);
226 tb_change_cell(x, y, uni, fg, bg);
227 x++;
231 static void
232 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
234 char buf[MAX_LINE];
235 va_list vl;
236 va_start(vl, fmt);
237 (void)vsnprintf(buf, MAX_LINE, fmt, vl);
238 va_end(vl);
239 print_tb(buf, x, y, col.fg, col.bg);
242 static void
243 print_status(Cpair col, const char *fmt, ...)
245 char buf[MAX_STATUS];
246 va_list vl;
247 va_start(vl, fmt);
248 (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
249 va_end(vl);
250 clear_status();
251 print_tb(buf, 1, theight - 1, col.fg, col.bg);
254 static void
255 print_xstatus(char c, int x)
257 uint32_t uni = 0;
258 (void)tb_utf8_char_to_unicode(&uni, &c);
259 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
262 static void
263 print_error(char *errmsg)
265 print_status(cerr, errmsg);
268 static void
269 print_prompt(char *prompt)
271 print_status(cprompt, prompt);
274 static void
275 print_info(Pane *pane, char *dirsize)
277 char *sz, *ur, *gr, *dt, *prm;
279 dt = ecalloc(MAX_DTF, sizeof(char));
281 prm = get_fperm(CURSOR(pane).mode);
282 ur = get_fusr(CURSOR(pane).user);
283 gr = get_fgrp(CURSOR(pane).group);
285 if (get_fdt(dt, CURSOR(pane).dt) < 0)
286 *dt = '\0';
288 if (S_ISREG(CURSOR(pane).mode)) {
289 sz = get_fsize(CURSOR(pane).size);
290 } else {
291 if (dirsize == NULL) {
292 sz = ecalloc(1, sizeof(char));
293 *sz = '\0';
294 } else {
295 sz = dirsize;
299 print_status(cstatus, "%02d/%02d %s %s:%s %s %s", pane->hdir,
300 pane->dirc, prm, ur, gr, dt, sz);
302 free(prm);
303 free(ur);
304 free(gr);
305 free(dt);
306 free(sz);
309 static void
310 print_row(Pane *pane, size_t entpos, Cpair col)
312 int x, y;
313 char *result;
314 char buf[MAX_P];
315 char lnk_full[MAX_P];
316 int width;
318 width = (twidth / 2) - 4;
319 result = basename(pane->direntr[entpos].name);
320 x = pane->dirx;
321 y = entpos - pane->firstrow + 1;
323 if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
324 if (realpath(pane->direntr[entpos].name, buf) != NULL) {
325 (void)snprintf(
326 lnk_full, MAX_N, "%s -> %s", result, buf);
327 result = lnk_full;
331 printf_tb(x, y, col, "%*.*s", ~width, width, result);
334 static void
335 clear(int sx, int ex, int y, uint16_t bg)
337 /* clear line from to */
338 /* x = line number vertical */
339 /* y = column number horizontal */
340 int i;
341 for (i = sx; i < ex; i++) {
342 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
346 static void
347 clear_status(void)
349 clear(1, twidth - 1, theight - 1, cstatus.bg);
352 static void
353 clear_pane(Pane *pane)
355 int i, ex, y;
356 y = 0, i = 0, ex = 0;
358 if (pane == &pane_l)
359 ex = (twidth / 2) - 1;
360 else if (pane == &pane_r)
361 ex = twidth - 1;
363 while (i < scrheight) {
364 clear(pane->dirx, ex, y, TB_DEFAULT);
365 i++;
366 y++;
369 /* draw top line */
370 for (y = pane->dirx; y < ex; ++y) {
371 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
375 static void
376 add_hi(Pane *pane, size_t entpos)
378 Cpair col;
379 get_hicol(&col, pane->direntr[entpos].mode);
380 col.fg |= TB_REVERSE | TB_BOLD;
381 col.bg |= TB_REVERSE;
382 print_row(pane, entpos, col);
385 static void
386 rm_hi(Pane *pane, size_t entpos)
388 Cpair col;
389 get_hicol(&col, pane->direntr[entpos].mode);
390 print_row(pane, entpos, col);
393 static int
394 check_dir(char *path)
396 DIR *dir;
397 dir = opendir(path);
399 if (dir == NULL) {
400 if (errno == ENOTDIR) {
401 return 1;
402 } else {
403 return -1;
407 if (closedir(dir) < 0)
408 return -1;
410 return 0;
413 static mode_t
414 chech_execf(mode_t mode)
416 if (S_ISREG(mode))
417 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
418 return 0;
421 static int
422 sort_name(const void *const A, const void *const B)
424 int result;
425 mode_t data1 = (*(Entry *)A).mode;
426 mode_t data2 = (*(Entry *)B).mode;
428 if (data1 < data2) {
429 return -1;
430 } else if (data1 == data2) {
431 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
432 return result;
433 } else {
434 return 1;
438 static void
439 get_dirp(char *cdir)
441 int counter, len, i;
443 counter = 0;
444 len = strnlen(cdir, MAX_P);
445 if (len == 1)
446 return;
448 for (i = len - 1; i > 1; i--) {
449 if (cdir[i] == '/')
450 break;
451 else
452 counter++;
455 cdir[len - counter - 1] = '\0';
458 static char *
459 get_ext(char *str)
461 char *ext;
462 char dot;
463 size_t counter, len, i;
465 dot = '.';
466 counter = 0;
467 len = strnlen(str, MAX_N);
469 for (i = len - 1; i > 0; i--) {
470 if (str[i] == dot) {
471 break;
472 } else {
473 counter++;
477 ext = ecalloc(MAX_EXT + 1, sizeof(char));
478 strncpy(ext, &str[len - counter], MAX_EXT);
479 ext[MAX_EXT] = '\0';
480 return ext;
483 static int
484 get_fdt(char *result, time_t status)
486 struct tm lt;
487 localtime_r(&status, &lt);
488 return strftime(result, MAX_DTF, dtfmt, &lt);
491 static char *
492 get_fgrp(gid_t status)
494 char *result;
495 struct group *gr;
497 result = ecalloc(MAX_GRPN, sizeof(char));
498 gr = getgrgid(status);
499 if (gr == NULL)
500 (void)snprintf(result, MAX_GRPN, "%u", status);
501 else
502 strncpy(result, gr->gr_name, MAX_GRPN);
504 result[MAX_GRPN - 1] = '\0';
505 return result;
508 static char *
509 get_fperm(mode_t mode)
511 char *buf;
512 size_t i;
514 const char chars[] = "rwxrwxrwx";
515 buf = ecalloc(11, sizeof(char));
517 if (S_ISDIR(mode))
518 buf[0] = 'd';
519 else if (S_ISREG(mode))
520 buf[0] = '-';
521 else if (S_ISLNK(mode))
522 buf[0] = 'l';
523 else if (S_ISBLK(mode))
524 buf[0] = 'b';
525 else if (S_ISCHR(mode))
526 buf[0] = 'c';
527 else if (S_ISFIFO(mode))
528 buf[0] = 'p';
529 else if (S_ISSOCK(mode))
530 buf[0] = 's';
531 else
532 buf[0] = '?';
534 for (i = 1; i < 10; i++) {
535 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
537 buf[10] = '\0';
539 return buf;
542 static char *
543 get_fsize(off_t size)
545 char *result; /* need to be freed */
546 char unit;
547 int result_len;
548 int counter;
550 counter = 0;
551 result_len = 6; /* 9999X/0 */
552 result = ecalloc(result_len, sizeof(char));
554 while (size >= 1000) {
555 size /= 1024;
556 ++counter;
559 switch (counter) {
560 case 0:
561 unit = 'B';
562 break;
563 case 1:
564 unit = 'K';
565 break;
566 case 2:
567 unit = 'M';
568 break;
569 case 3:
570 unit = 'G';
571 break;
572 case 4:
573 unit = 'T';
574 break;
575 default:
576 unit = '?';
579 if (snprintf(result, result_len, OFF_T "%c", size, unit) < 0)
580 strncat(result, "???", result_len);
582 return result;
585 static char *
586 get_fullpath(char *first, char *second)
588 char *full_path;
590 full_path = ecalloc(MAX_P, sizeof(char));
592 if (strncmp(first, "/", MAX_P) == 0)
593 (void)snprintf(full_path, MAX_P, "/%s", second);
594 else
595 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
597 return full_path;
600 static char *
601 get_fusr(uid_t status)
603 char *result;
604 struct passwd *pw;
606 result = ecalloc(MAX_USRN, sizeof(char));
607 pw = getpwuid(status);
608 if (pw == NULL)
609 (void)snprintf(result, MAX_USRN, "%u", status);
610 else
611 strncpy(result, pw->pw_name, MAX_USRN);
613 result[MAX_USRN - 1] = '\0';
614 return result;
617 static void
618 get_dirsize(char *fullpath, off_t *fullsize)
620 DIR *dir;
621 char *ent_full;
622 mode_t mode;
623 struct dirent *entry;
624 struct stat status;
626 dir = opendir(fullpath);
627 if (dir == NULL) {
628 return;
631 while ((entry = readdir(dir)) != 0) {
632 if ((strncmp(entry->d_name, ".", 2) == 0 ||
633 strncmp(entry->d_name, "..", 3) == 0))
634 continue;
636 ent_full = get_fullpath(fullpath, entry->d_name);
637 if (lstat(ent_full, &status) == 0) {
638 mode = status.st_mode;
639 if (S_ISDIR(mode)) {
640 get_dirsize(ent_full, fullsize);
641 free(ent_full);
642 } else {
643 *fullsize += status.st_size;
644 free(ent_full);
649 closedir(dir);
650 clear_status();
653 static void
654 get_hicol(Cpair *col, mode_t mode)
656 *col = cfile;
657 if (S_ISDIR(mode))
658 *col = cdir;
659 else if (S_ISLNK(mode))
660 *col = cother;
661 else if (chech_execf(mode) > 0)
662 *col = cexec;
665 static void
666 delent(void)
668 if (cpane->dirc < 1)
669 return;
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; /* canceled by user or wrong inp_conf */
680 free(inp_conf);
682 char *tmp[1];
683 tmp[0] = CURSOR(cpane).name;
684 if (spawn(rm_cmd, rm_cmd_len, tmp, 1, NULL) < 0) {
685 print_error(strerror(errno));
686 return;
689 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
690 cpane->hdir--;
693 static void
694 calcdir(void)
696 if (cpane->dirc < 1)
697 return;
698 if (!S_ISDIR(CURSOR(cpane).mode))
699 return;
701 off_t *fullsize;
702 char *csize;
704 fullsize = ecalloc(1, sizeof(off_t));
705 get_dirsize(CURSOR(cpane).name, fullsize);
706 csize = get_fsize(*fullsize);
708 CURSOR(cpane).size = *fullsize;
709 print_info(cpane, csize);
710 free(fullsize);
713 static void
714 crnd(void)
716 char *user_input, *path;
718 user_input = ecalloc(MAX_USRI, sizeof(char));
719 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
720 free(user_input);
721 return;
724 path = ecalloc(MAX_P, sizeof(char));
725 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
726 free(user_input);
727 free(path);
728 return;
731 if (mkdir(path, ndir_perm) < 0)
732 print_error(strerror(errno));
734 free(user_input);
735 free(path);
738 static void
739 crnf(void)
741 char *user_input, *path;
742 int rf;
744 user_input = ecalloc(MAX_USRI, sizeof(char));
745 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
746 free(user_input);
747 return;
750 path = ecalloc(MAX_P, sizeof(char));
751 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
752 free(user_input);
753 free(path);
754 return;
757 rf = open(path, O_CREAT | O_EXCL, nf_perm);
759 if (rf < 0)
760 print_error(strerror(errno));
761 else if (close(rf) < 0)
762 print_error(strerror(errno));
764 free(user_input);
765 free(path);
768 static void
769 mvbk(void)
771 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
772 return;
775 get_dirp(cpane->dirn);
776 if (check_dir(cpane->dirn) < 0) {
777 print_error(strerror(errno));
778 return;
781 rmwatch(cpane);
782 cpane->firstrow = cpane->parent_firstrow;
783 cpane->hdir = cpane->parent_row;
784 if (listdir(cpane) < 0)
785 print_error(strerror(errno));
786 cpane->parent_firstrow = 0;
787 cpane->parent_row = 1;
790 static void
791 mvbtm(void)
793 if (cpane->dirc < 1)
794 return;
795 if (cpane->dirc > scrheight) {
796 rm_hi(cpane, cpane->hdir - 1);
797 cpane->hdir = cpane->dirc;
798 cpane->firstrow = cpane->dirc - scrheight + 1;
799 refresh_pane(cpane);
800 add_hi(cpane, cpane->hdir - 1);
801 } else {
802 rm_hi(cpane, cpane->hdir - 1);
803 cpane->hdir = cpane->dirc;
804 add_hi(cpane, cpane->hdir - 1);
806 print_info(cpane, NULL);
809 static void
810 mvdwn(void)
812 if (cpane->dirc < 1)
813 return;
814 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
815 rm_hi(cpane, cpane->hdir - 1);
816 cpane->hdir++;
817 add_hi(cpane, cpane->hdir - 1);
818 } else {
819 mvdwns(); /* scroll */
821 print_info(cpane, NULL);
824 static void
825 mvdwns(void)
827 int real;
828 real = cpane->hdir - 1 - cpane->firstrow;
830 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
831 cpane->firstrow++;
832 rm_hi(cpane, cpane->hdir - 1);
833 cpane->hdir++;
834 refresh_pane(cpane);
835 add_hi(cpane, cpane->hdir - 1);
836 } else if (cpane->hdir < cpane->dirc) {
837 rm_hi(cpane, cpane->hdir - 1);
838 cpane->hdir++;
839 add_hi(cpane, cpane->hdir - 1);
843 static void
844 mvfwd(void)
846 rmwatch(cpane);
847 if (cpane->dirc < 1)
848 return;
849 int s;
851 switch (check_dir(CURSOR(cpane).name)) {
852 case 0:
853 strncpy(cpane->dirn, CURSOR(cpane).name, MAX_P);
854 cpane->parent_row = cpane->hdir;
855 cpane->parent_firstrow = cpane->firstrow;
856 cpane->hdir = 1;
857 cpane->firstrow = 0;
858 if (listdir(cpane) < 0)
859 print_error(strerror(errno));
860 break;
861 case 1: /* not a directory open file */
862 tb_shutdown();
863 s = opnf(CURSOR(cpane).name);
864 if (tb_init() != 0)
865 die("tb_init");
866 t_resize();
867 if (s < 0)
868 print_error("process failed non-zero exit");
869 break;
870 case -1: /* failed to open directory */
871 print_error(strerror(errno));
875 static void
876 mvmid(void)
878 if (cpane->dirc < 1)
879 return;
880 rm_hi(cpane, cpane->hdir - 1);
881 if (cpane->dirc < scrheight / 2)
882 cpane->hdir = (cpane->dirc + 1) / 2;
883 else
884 cpane->hdir = (scrheight / 2) + cpane->firstrow;
885 add_hi(cpane, cpane->hdir - 1);
886 print_info(cpane, NULL);
889 static void
890 mvtop(void)
892 if (cpane->dirc < 1)
893 return;
894 if (cpane->dirc > scrheight) {
895 rm_hi(cpane, cpane->hdir - 1);
896 cpane->hdir = 1;
897 cpane->firstrow = 0;
898 refresh_pane(cpane);
899 add_hi(cpane, cpane->hdir - 1);
900 } else {
901 rm_hi(cpane, cpane->hdir - 1);
902 cpane->hdir = 1;
903 add_hi(cpane, cpane->hdir - 1);
904 print_info(cpane, NULL);
908 static void
909 mvup(void)
911 if (cpane->dirc < 1)
912 return;
913 if (cpane->dirc < scrheight && cpane->hdir > 1) {
914 rm_hi(cpane, cpane->hdir - 1);
915 cpane->hdir--;
916 add_hi(cpane, cpane->hdir - 1);
917 } else {
918 mvups(); /* scroll */
920 print_info(cpane, NULL);
923 static void
924 mvups(void)
926 size_t real;
927 real = cpane->hdir - 1 - cpane->firstrow;
929 if (cpane->firstrow > 0 && real < 1 + scrsp) {
930 cpane->firstrow--;
931 rm_hi(cpane, cpane->hdir - 1);
932 cpane->hdir--;
933 refresh_pane(cpane);
934 add_hi(cpane, cpane->hdir - 1);
935 } else if (cpane->hdir > 1) {
936 rm_hi(cpane, cpane->hdir - 1);
937 cpane->hdir--;
938 add_hi(cpane, cpane->hdir - 1);
942 static void
943 scrdwn(void)
945 if (cpane->dirc < 1)
946 return;
947 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
948 if (cpane->hdir < cpane->dirc - scrmv) {
949 rm_hi(cpane, cpane->hdir - 1);
950 cpane->hdir += scrmv;
951 add_hi(cpane, cpane->hdir - 1);
952 } else {
953 mvbtm();
955 } else {
956 scrdwns();
958 print_info(cpane, NULL);
961 static void
962 scrdwns(void)
964 int real, dynmv;
966 real = cpane->hdir - cpane->firstrow;
967 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
969 if (real + scrmv + 1 > scrheight &&
970 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
971 cpane->firstrow += dynmv;
972 rm_hi(cpane, cpane->hdir - 1);
973 cpane->hdir += scrmv;
974 refresh_pane(cpane);
975 add_hi(cpane, cpane->hdir - 1);
976 } else {
977 if (cpane->hdir < cpane->dirc - scrmv - 1) {
978 rm_hi(cpane, cpane->hdir - 1);
979 cpane->hdir += scrmv;
980 add_hi(cpane, cpane->hdir - 1);
981 } else {
982 mvbtm();
987 static void
988 scrup(void)
990 if (cpane->dirc < 1)
991 return;
992 if (cpane->dirc < scrheight && cpane->hdir > 1) {
993 if (cpane->hdir > scrmv) {
994 rm_hi(cpane, cpane->hdir - 1);
995 cpane->hdir = cpane->hdir - scrmv;
996 add_hi(cpane, cpane->hdir - 1);
997 print_info(cpane, NULL);
998 } else {
999 mvtop();
1001 } else {
1002 scrups();
1006 static void
1007 scrups(void)
1009 int real, dynmv;
1010 real = cpane->hdir - cpane->firstrow;
1011 dynmv = MIN(cpane->firstrow, scrmv);
1013 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1014 cpane->firstrow -= dynmv;
1015 rm_hi(cpane, cpane->hdir - 1);
1016 cpane->hdir -= scrmv;
1017 refresh_pane(cpane);
1018 add_hi(cpane, cpane->hdir - 1);
1019 } else {
1020 if (cpane->hdir > scrmv + 1) {
1021 rm_hi(cpane, cpane->hdir - 1);
1022 cpane->hdir -= scrmv;
1023 add_hi(cpane, cpane->hdir - 1);
1024 } else {
1025 mvtop();
1030 static int
1031 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1033 int height = tb_height();
1034 size_t startat;
1035 struct tb_event fev;
1036 size_t counter = (size_t)1;
1037 char empty = ' ';
1038 int x = 0;
1039 int name_size = 0;
1040 char buf[256];
1042 clear_status();
1044 va_list vl;
1045 Cpair col;
1046 col = cprompt;
1047 va_start(vl, fmt);
1048 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1049 va_end(vl);
1050 print_tb(buf, 1, height - 1, col.fg, col.bg);
1051 startat = name_size + 1;
1052 tb_set_cursor((int)(startat + 1), height - 1);
1053 tb_present();
1055 while (tb_poll_event(&fev) != 0) {
1056 switch (fev.type) {
1057 case TB_EVENT_KEY:
1058 if (fev.key == TB_KEY_ESC) {
1059 tb_set_cursor(-1, -1);
1060 clear_status();
1061 return -1;
1064 if (fev.key == TB_KEY_BACKSPACE ||
1065 fev.key == TB_KEY_BACKSPACE2) {
1066 if (BETWEEN(counter, 2, sout)) {
1067 out[x - 1] = '\0';
1068 counter--;
1069 x--;
1070 print_xstatus(empty, startat + counter);
1071 tb_set_cursor(
1072 startat + counter, theight - 1);
1075 } else if (fev.key == TB_KEY_ENTER) {
1076 tb_set_cursor(-1, -1);
1077 out[counter - 1] = '\0';
1078 return 0;
1080 } else {
1081 if (counter < sout) {
1082 print_xstatus((char)fev.ch,
1083 (startat + counter));
1084 out[x] = (char)fev.ch;
1085 tb_set_cursor((startat + counter + 1),
1086 theight - 1);
1087 counter++;
1088 x++;
1092 tb_present();
1093 break;
1095 default:
1096 return -1;
1100 return -1;
1103 static int
1104 frules(char *ex)
1106 size_t c, d;
1108 for (c = 0; c < LEN(rules); c++)
1109 for (d = 0; d < rules[c].exlen; d++)
1110 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1111 return c;
1112 return -1;
1115 static int
1116 spawn(const void *com_argv, size_t com_argc, const void *f_argv, size_t f_argc,
1117 char *fn)
1119 int ws;
1120 size_t argc;
1121 pid_t pid, r;
1123 argc = com_argc + f_argc + 2;
1124 char *argv[argc];
1126 memcpy(argv, com_argv, com_argc * sizeof(char *)); /* command */
1127 memcpy(&argv[com_argc], f_argv, f_argc * sizeof(char *)); /* files */
1129 argv[argc - 2] = fn;
1130 argv[argc - 1] = NULL;
1132 pid = fork();
1133 switch (pid) {
1134 case -1:
1135 return -1;
1136 case 0:
1137 execvp(argv[0], argv);
1138 exit(EXIT_SUCCESS);
1139 default:
1140 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1141 continue;
1142 if (r == -1)
1143 return -1;
1144 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1145 return -1;
1147 return 0;
1150 static int
1151 opnf(char *fn)
1153 char *ex;
1154 int c;
1156 ex = get_ext(fn);
1157 c = frules(ex);
1158 free(ex);
1160 if (c < 0) /* extension not found open in editor */
1161 return spawn(editor, 1, NULL, 0, fn);
1162 else
1163 return spawn((char **)rules[c].v, rules[c].vlen, NULL, 0, fn);
1166 static int
1167 fsev_init(void)
1169 #if defined(_SYS_INOTIFY_H)
1170 inotify_fd = inotify_init();
1171 if (inotify_fd < 0)
1172 return -1;
1173 #elif defined(_SYS_EVENT_H_)
1174 gtimeout.tv_sec = 1;
1175 kq = kqueue();
1176 if (kq < 0)
1177 return -1;
1178 #endif
1179 return 0;
1182 static int
1183 addwatch(Pane *pane)
1185 #if defined(_SYS_INOTIFY_H)
1186 return pane->inotify_wd = inotify_add_watch(inotify_fd, pane->dirn,
1187 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1188 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1189 #elif defined(_SYS_EVENT_H_)
1190 pane->event_fd = open(pane->dirn, O_RDONLY);
1191 if (pane->event_fd < 0)
1192 return pane->event_fd;
1193 EV_SET(&evlist[pane->pane_id], pane->event_fd, EVFILT_VNODE,
1194 EV_ADD | EV_CLEAR,
1195 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
1196 NOTE_REVOKE | NOTE_WRITE,
1197 0, NULL);
1198 return 0;
1199 #endif
1202 static int
1203 read_events(void)
1205 #if defined(_SYS_INOTIFY_H)
1206 char *p;
1207 ssize_t r;
1208 struct inotify_event *event;
1209 const size_t events = 32;
1210 const size_t evbuflen =
1211 events * (sizeof(struct inotify_event) + MAX_N + 1);
1212 char buf[evbuflen];
1214 if (cpane->inotify_wd < 0)
1215 return -1;
1216 r = read(inotify_fd, buf, evbuflen);
1217 if (r <= 0)
1218 return r;
1220 for (p = buf; p < buf + r;) {
1221 event = (struct inotify_event *)p;
1222 if (!event->wd)
1223 break;
1224 if (event->mask) {
1225 return r;
1228 p += sizeof(struct inotify_event) + event->len;
1230 #elif defined(_SYS_EVENT_H_)
1231 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1232 #endif
1233 return -1;
1236 static void
1237 rmwatch(Pane *pane)
1239 #if defined(_SYS_INOTIFY_H)
1240 if (pane->inotify_wd >= 0)
1241 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1242 #elif defined(_SYS_EVENT_H_)
1243 close(pane->event_fd);
1244 #endif
1247 static void
1248 fsev_shdn(void)
1250 pthread_cancel(fsev_thread);
1251 #if defined(__linux__)
1252 pthread_join(fsev_thread, NULL);
1253 #endif
1254 rmwatch(&pane_l);
1255 rmwatch(&pane_r);
1256 #if defined(_SYS_INOTIFY_H)
1257 close(inotify_fd);
1258 #elif defined(_SYS_EVENT_H_)
1259 close(kq);
1260 #endif
1263 static ssize_t
1264 findbm(uint32_t event)
1266 ssize_t i;
1268 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1269 if (event == bmarks[i].ch) {
1270 if (check_dir(bmarks[i].path) != 0) {
1271 print_error(strerror(errno));
1272 return -1;
1274 return i;
1277 return -1;
1280 static void
1281 start_filter(void)
1283 if (cpane->dirc < 1)
1284 return;
1285 char *user_input;
1286 user_input = ecalloc(MAX_USRI, sizeof(char));
1287 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1288 free(user_input);
1289 return;
1291 cpane->filter = user_input;
1292 if (listdir(cpane) < 0)
1293 print_error("no match");
1294 cpane->filter = NULL;
1295 free(user_input);
1298 static void
1299 start_vmode(void)
1301 if (cpane->dirc < 1)
1302 return;
1303 struct tb_event fev;
1304 if (selection != NULL) {
1305 free(selection);
1306 selection = NULL;
1309 selection = ecalloc(cpane->dirc, sizeof(size_t));
1310 selection[0] = cpane->hdir;
1311 cont_vmode = 0;
1312 print_prompt("-- VISUAL --");
1313 tb_present();
1314 while (tb_poll_event(&fev) != 0) {
1315 switch (fev.type) {
1316 case TB_EVENT_KEY:
1317 grabkeys(&fev, vkeys, vkeyslen);
1318 if (cont_vmode == -1)
1319 return;
1320 tb_present();
1321 break;
1326 static void
1327 exit_vmode(void)
1329 refresh_pane(cpane);
1330 add_hi(cpane, cpane->hdir - 1);
1331 cont_vmode = -1;
1334 static void
1335 selup(void)
1337 mvup();
1338 print_prompt("-- VISUAL --");
1339 int index = abs(cpane->hdir - selection[0]);
1341 if (cpane->hdir < selection[0]) {
1342 selection[index] = cpane->hdir;
1343 add_hi(cpane, selection[index]);
1344 } else if (index < cpane->dirc) {
1345 selection[index + 1] = 0;
1347 if (cpane->dirc >= scrheight ||
1348 cpane->hdir <= 1) { /* rehighlight all if scrolling */
1349 selref();
1353 static void
1354 seldwn(void)
1356 mvdwn();
1357 print_prompt("-- VISUAL --");
1358 int index = abs(cpane->hdir - selection[0]);
1360 if (cpane->hdir > selection[0]) {
1361 selection[index] = cpane->hdir;
1362 add_hi(cpane, selection[index] - 2);
1363 } else {
1364 selection[index + 1] = 0;
1366 if (cpane->dirc >= scrheight ||
1367 cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1368 selref();
1372 static void
1373 selall(void)
1375 int i;
1376 for (i = 0; i < cpane->dirc; i++) {
1377 selection[i] = i + 1;
1379 selref();
1382 static void
1383 selref(void)
1385 int i;
1386 for (i = 0; i < cpane->dirc; i++) {
1387 if (selection[i] < (scrheight + cpane->firstrow) &&
1388 selection[i] >
1389 cpane->firstrow) { /* checks if in the frame of the directories */
1390 add_hi(cpane, selection[i] - 1);
1395 static void
1396 selcalc(void)
1398 int j;
1399 selection_size = 0;
1401 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1402 if (selection[j] != 0)
1403 selection_size++;
1404 else
1405 break;
1409 static void
1410 free_files(void)
1412 size_t i;
1414 if (selected_files != NULL) {
1415 for (i = 0; i < selection_size; i++) {
1416 free(selected_files[i]);
1417 selected_files[i] = NULL;
1419 free(selected_files);
1420 selected_files = NULL;
1424 static void
1425 init_files(void)
1427 size_t i;
1428 free_files();
1430 selcalc();
1431 selected_files = ecalloc(selection_size, sizeof(char *));
1433 for (i = 0; i < selection_size; i++) {
1434 selected_files[i] = ecalloc(MAX_P, sizeof(char));
1435 strncpy(selected_files[i],
1436 cpane->direntr[selection[i] - 1].name, MAX_P);
1440 static void
1441 selynk(void)
1443 init_files();
1444 refresh_pane(cpane);
1445 add_hi(cpane, cpane->hdir - 1);
1446 print_status(cprompt, "%zu files are yanked", selection_size);
1447 cont_vmode = -1;
1450 static void
1451 seldel(void)
1453 char *inp_conf;
1454 int conf_len = 4;
1455 char conf[] = "yes";
1457 inp_conf = ecalloc(conf_len, sizeof(char));
1458 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1459 (strncmp(inp_conf, conf, conf_len) != 0)) {
1460 free(inp_conf);
1461 return; /* canceled by user or wrong inp_conf */
1463 free(inp_conf);
1465 init_files();
1467 if (spawn(rm_cmd, rm_cmd_len, selected_files, selection_size, NULL) < 0)
1468 print_error(strerror(errno));
1469 else
1470 print_status(cprompt, "%zu files are deleted", selection_size);
1472 if (cpane->dirc > 0)
1473 cpane->hdir = 1;
1475 free_files();
1476 cont_vmode = -1;
1479 static void
1480 paste(void)
1482 if (selected_files == NULL) {
1483 print_error("nothing to paste");
1484 return;
1487 if (spawn(cp_cmd, cp_cmd_len, selected_files, selection_size,
1488 cpane->dirn) < 0)
1489 print_error(strerror(errno));
1490 else
1491 print_status(cprompt, "%zu files are copied", selection_size);
1493 free_files();
1496 static void
1497 selmv(void)
1499 if (selected_files == NULL) {
1500 print_error("nothing to move");
1501 return;
1504 if (spawn(mv_cmd, mv_cmd_len, selected_files, selection_size,
1505 cpane->dirn) < 0)
1506 print_error(strerror(errno));
1507 else
1508 print_status(cprompt, "%zu files are moved", selection_size);
1510 free_files();
1513 static void
1514 rname(void)
1516 if (cpane->dirc < 1)
1517 return;
1518 char new_name[MAX_P];
1519 char *input_name;
1521 input_name = ecalloc(MAX_N, sizeof(char));
1523 if (get_usrinput(input_name, MAX_N, "rename: %s",
1524 basename(CURSOR(cpane).name)) < 0) {
1525 free(input_name);
1526 return;
1529 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1530 free(input_name);
1531 print_error(strerror(errno));
1532 return;
1535 char *rename_cmd[] = { "mv", CURSOR(cpane).name, new_name };
1536 if (spawn(rename_cmd, 3, NULL, 0, NULL) < 0)
1537 print_error(strerror(errno));
1539 free(input_name);
1542 static void
1543 yank(void)
1545 if (cpane->dirc < 1)
1546 return;
1548 free_files();
1549 selection_size = 1;
1550 selected_files = ecalloc(selection_size, sizeof(char *));
1551 selected_files[0] = ecalloc(MAX_P, sizeof(char));
1552 strncpy(selected_files[0], CURSOR(cpane).name, MAX_P);
1553 print_status(cprompt, "1 file is yanked", selection_size);
1556 static void
1557 switch_pane(void)
1559 if (cpane->dirc > 0)
1560 rm_hi(cpane, cpane->hdir - 1);
1561 if (cpane == &pane_l)
1562 cpane = &pane_r;
1563 else if (cpane == &pane_r)
1564 cpane = &pane_l;
1565 if (cpane->dirc > 0) {
1566 add_hi(cpane, cpane->hdir - 1);
1567 print_info(cpane, NULL);
1568 } else {
1569 clear_status();
1573 static void
1574 quit(void)
1576 if (cont_vmode == -1) { /* check if selection was allocated */
1577 free(selection);
1578 if (selected_files != NULL)
1579 free_files();
1581 free(pane_l.direntr);
1582 free(pane_r.direntr);
1583 fsev_shdn();
1584 tb_shutdown();
1585 exit(EXIT_SUCCESS);
1588 static void
1589 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1591 size_t i;
1592 ssize_t b;
1594 for (i = 0; i < max_keys; i++) {
1595 if (event->ch != 0) {
1596 if (event->ch == key[i].evkey.ch) {
1597 key[i].func();
1598 return;
1600 } else if (event->key != 0) {
1601 if (event->key == key[i].evkey.key) {
1602 key[i].func();
1603 return;
1608 /* bookmarks */
1609 b = findbm(event->ch);
1610 if (b < 0)
1611 return;
1612 rmwatch(cpane);
1613 strncpy(cpane->dirn, bmarks[b].path, MAX_P);
1614 cpane->firstrow = 0;
1615 cpane->parent_row = 1;
1616 cpane->hdir = 1;
1617 if (listdir(cpane) < 0)
1618 print_error(strerror(errno));
1621 void *
1622 read_th(void *arg)
1624 struct timespec tim;
1625 tim.tv_sec = 0;
1626 tim.tv_nsec = 5000000L; /* 0.005 sec */
1628 while (1)
1629 if (read_events() > READEVSZ) {
1630 kill(main_pid, SIGUSR1);
1631 nanosleep(&tim, NULL);
1633 return arg;
1636 static void
1637 start_ev(void)
1639 struct tb_event ev;
1641 while (tb_poll_event(&ev) != 0) {
1642 switch (ev.type) {
1643 case TB_EVENT_KEY:
1644 grabkeys(&ev, nkeys, nkeyslen);
1645 tb_present();
1646 break;
1647 case TB_EVENT_RESIZE:
1648 t_resize();
1649 break;
1650 default:
1651 break;
1654 tb_shutdown();
1657 static void
1658 refresh_pane(Pane *pane)
1660 size_t y, dyn_max, start_from;
1661 int width;
1662 width = (twidth / 2) - 4;
1663 Cpair col;
1665 y = 1;
1666 start_from = pane->firstrow;
1667 dyn_max = MIN(pane->dirc, (scrheight - 1) + pane->firstrow);
1669 /* print each entry in directory */
1670 while (start_from < dyn_max) {
1671 get_hicol(&col, pane->direntr[start_from].mode);
1672 print_row(pane, start_from, col);
1673 start_from++;
1674 y++;
1677 if (pane->dirc > 0)
1678 print_info(pane, NULL);
1679 else
1680 clear_status();
1682 /* print current directory title */
1683 pane->dircol.fg |= TB_BOLD;
1684 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", width, pane->dirn);
1687 static void
1688 set_direntr(Pane *pane, struct dirent *entry, DIR *dir, char *filter)
1690 int i;
1691 char *tmpfull;
1692 struct stat status;
1694 #define ADD_ENTRY \
1695 tmpfull = get_fullpath(pane->dirn, entry->d_name); \
1696 strncpy(pane->direntr[i].name, tmpfull, MAX_N); \
1697 if (lstat(tmpfull, &status) == 0) { \
1698 pane->direntr[i].size = status.st_size; \
1699 pane->direntr[i].mode = status.st_mode; \
1700 pane->direntr[i].group = status.st_gid; \
1701 pane->direntr[i].user = status.st_uid; \
1702 pane->direntr[i].dt = status.st_mtime; \
1704 i++; \
1705 free(tmpfull);
1707 i = 0;
1708 pane->direntr =
1709 erealloc(pane->direntr, (10 + pane->dirc) * sizeof(Entry));
1710 while ((entry = readdir(dir)) != 0) {
1711 if ((strncmp(entry->d_name, ".", 2) == 0 ||
1712 strncmp(entry->d_name, "..", 3) == 0))
1713 continue;
1715 if (filter == NULL) {
1716 ADD_ENTRY
1717 } else if (filter != NULL) {
1718 if (strcasestr(entry->d_name, filter) != NULL) {
1719 ADD_ENTRY
1724 pane->dirc = i;
1727 static int
1728 listdir(Pane *pane)
1730 DIR *dir;
1731 struct dirent *entry;
1732 int width;
1733 int filtercount = 0;
1734 size_t oldc = pane->dirc;
1736 width = (twidth / 2) - 4;
1737 pane->dirc = 0;
1739 dir = opendir(pane->dirn);
1740 if (dir == NULL)
1741 return -1;
1743 /* get content and filter sum */
1744 while ((entry = readdir(dir)) != 0) {
1745 if (pane->filter != NULL) {
1746 if (strcasestr(entry->d_name, pane->filter) != NULL)
1747 filtercount++;
1748 } else { /* no filter */
1749 pane->dirc++;
1753 if (pane->filter == NULL) {
1754 clear_pane(pane);
1755 pane->dirc -= 2;
1758 if (pane->filter != NULL) {
1759 if (filtercount > 0) {
1760 pane->dirc = filtercount;
1761 clear_pane(pane);
1762 pane->hdir = 1;
1763 } else if (filtercount == 0) {
1764 if (closedir(dir) < 0)
1765 return -1;
1766 pane->dirc = oldc;
1767 return -1;
1771 /* print current directory title */
1772 pane->dircol.fg |= TB_BOLD;
1773 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", width, pane->dirn);
1775 if (pane->filter == NULL) /* dont't watch when filtering */
1776 if (addwatch(pane) < 0)
1777 print_error("can't add watch");
1779 /* empty directory */
1780 if (pane->dirc == 0) {
1781 clear_status();
1782 if (closedir(dir) < 0)
1783 return -1;
1784 return 0;
1787 rewinddir(dir); /* reset position */
1788 set_direntr(
1789 pane, entry, dir, pane->filter); /* create array of entries */
1790 qsort(pane->direntr, pane->dirc, sizeof(Entry), sort_name);
1791 refresh_pane(pane);
1793 if (pane == cpane) {
1794 if (pane->hdir >= pane->dirc)
1795 add_hi(pane, pane->dirc);
1796 else
1797 add_hi(pane, pane->hdir - 1);
1800 if (closedir(dir) < 0)
1801 return -1;
1802 return 0;
1805 static void
1806 t_resize(void)
1808 /* TODO need refactoring */
1809 tb_clear();
1810 draw_frame();
1811 pane_r.dirx = (twidth / 2) + 2;
1813 if (cpane == &pane_l) {
1814 refresh_pane(&pane_r);
1815 refresh_pane(&pane_l);
1816 if (cpane->dirc > 0)
1817 add_hi(&pane_l, pane_l.hdir - 1);
1818 } else if (cpane == &pane_r) {
1819 refresh_pane(&pane_l);
1820 refresh_pane(&pane_r);
1821 if (cpane->dirc > 0)
1822 add_hi(&pane_r, pane_r.hdir - 1);
1825 tb_present();
1828 static void
1829 get_editor(void)
1831 editor[0] = getenv("EDITOR");
1832 editor[1] = NULL;
1834 if (editor[0] == NULL)
1835 editor[0] = fed;
1838 static void
1839 set_panes(void)
1841 char *home;
1842 char cwd[MAX_P];
1844 home = getenv("HOME");
1845 if (home == NULL)
1846 home = "/";
1847 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1848 strncpy(cwd, home, MAX_P);
1850 pane_l.pane_id = 0;
1851 pane_l.dirx = 2;
1852 pane_l.dircol = cpanell;
1853 pane_l.firstrow = 0;
1854 pane_l.direntr = ecalloc(0, sizeof(Entry));
1855 strncpy(pane_l.dirn, cwd, MAX_P);
1856 pane_l.hdir = 1;
1857 pane_l.inotify_wd = -1;
1858 pane_l.parent_row = 1;
1860 pane_r.pane_id = 1;
1861 pane_r.dirx = (twidth / 2) + 2;
1862 pane_r.dircol = cpanelr;
1863 pane_r.firstrow = 0;
1864 pane_r.direntr = ecalloc(0, sizeof(Entry));
1865 strncpy(pane_r.dirn, home, MAX_P);
1866 pane_r.hdir = 1;
1867 pane_r.inotify_wd = -1;
1868 pane_r.parent_row = 1;
1871 static void
1872 draw_frame(void)
1874 int i;
1875 theight = tb_height();
1876 twidth = tb_width();
1877 scrheight = theight - 2;
1879 /* 2 horizontal lines */
1880 for (i = 1; i < twidth - 1; ++i) {
1881 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1882 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1885 /* 4 vertical lines */
1886 for (i = 1; i < theight - 1; ++i) {
1887 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1888 tb_change_cell(
1889 (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
1890 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1891 cframe.bg);
1892 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1895 /* 4 corners */
1896 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1897 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1898 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1899 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1901 /* 2 middel top and bottom */
1902 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1903 tb_change_cell(
1904 (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1907 void
1908 th_handler(int num)
1910 (void)num;
1911 if (cpane == &pane_l) {
1912 if (listdir(&pane_r) < 0)
1913 print_error(strerror(errno));
1914 if (listdir(&pane_l) < 0)
1915 print_error(strerror(errno));
1916 } else if (cpane == &pane_r) {
1917 if (listdir(&pane_l) < 0)
1918 print_error(strerror(errno));
1919 if (listdir(&pane_r) < 0)
1920 print_error(strerror(errno));
1922 tb_present();
1925 static int
1926 start_signal(void)
1928 struct sigaction sa;
1930 main_pid = getpid();
1931 sa.sa_handler = th_handler;
1932 sigemptyset(&sa.sa_mask);
1933 sa.sa_flags = SA_RESTART;
1934 return sigaction(SIGUSR1, &sa, NULL);
1937 static void
1938 start(void)
1940 if (tb_init() != 0)
1941 die("tb_init");
1942 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1943 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1944 die("output error");
1946 draw_frame();
1947 set_panes();
1948 get_editor();
1949 if (start_signal() < 0)
1950 print_error(strerror(errno));
1951 if (fsev_init() < 0)
1952 print_error(strerror(errno));
1953 cpane = &pane_l;
1954 if (listdir(&pane_r) < 0)
1955 print_error(strerror(errno));
1956 if (listdir(&pane_l) < 0)
1957 print_error(strerror(errno));
1958 tb_present();
1960 pthread_create(&fsev_thread, NULL, read_th, NULL);
1961 start_ev();
1965 main(int argc, char *argv[])
1967 #if defined(__OpenBSD__)
1968 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1969 NULL) == -1)
1970 die("pledge");
1971 #endif /* __OpenBSD__ */
1972 if (argc == 1)
1973 start();
1974 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1975 die("sfm-" VERSION);
1976 else
1977 die("usage: sfm [-v]");
1978 return 0;