[doc] update clone urls to afify.dev/sfm
[sfm.git] / sfm.c
blobb8b373e25e4291551b111e144d15f0a2a26820df
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 ex;
77 int firstrow;
78 int parent_firstrow;
79 int parent_row; // FIX
80 Cpair dircol;
81 int inotify_wd;
82 int event_fd;
83 } Pane;
85 typedef struct {
86 const char **ext;
87 size_t exlen;
88 const void *v;
89 size_t vlen;
90 } Rule;
92 typedef union {
93 uint16_t key; /* one of the TB_KEY_* constants */
94 uint32_t ch; /* unicode character */
95 } Evkey;
97 typedef union {
98 int i;
99 const void *v;
100 } Arg;
102 typedef struct {
103 const Evkey evkey;
104 void (*func)(const Arg *);
105 const Arg arg;
106 } Key;
108 /* function declarations */
109 static void print_tb(const char *, int, int, uint16_t, uint16_t);
110 static void printf_tb(int, int, Cpair, const char *, ...);
111 static void print_status(Cpair, const char *, ...);
112 static void print_xstatus(char, int);
113 static void print_error(char *);
114 static void print_prompt(char *);
115 static void print_info(Pane *, char *);
116 static void print_row(Pane *, size_t, Cpair);
117 static void clear(int, int, int, uint16_t);
118 static void clear_status(void);
119 static void clear_pane(Pane *);
120 static void add_hi(Pane *, size_t);
121 static void rm_hi(Pane *, size_t);
122 static int check_dir(char *);
123 static mode_t chech_execf(mode_t);
124 static int sort_name(const void *const, const void *const);
125 static void get_dirp(char *);
126 static char *get_ext(char *);
127 static int get_fdt(char *, time_t);
128 static char *get_fgrp(gid_t);
129 static char *get_fperm(mode_t);
130 static char *get_fsize(off_t);
131 static char *get_fullpath(char *, char *);
132 static char *get_fusr(uid_t);
133 static void get_dirsize(char *, off_t *);
134 static void get_hicol(Cpair *, mode_t);
135 static void delent(const Arg *arg);
136 static void calcdir(const Arg *arg);
137 static void crnd(const Arg *arg);
138 static void crnf(const Arg *arg);
139 static void mv_ver(const Arg *arg);
140 static void mvbk(const Arg *arg);
141 static void mvbtm(const Arg *arg);
142 static void mvfwd(const Arg *arg);
143 static void mvtop(const Arg *arg);
144 static void bkmrk(const Arg *arg);
145 static int get_usrinput(char *, size_t, const char *, ...);
146 static int frules(char *);
147 static int spawn(const void *, size_t, const void *, size_t, char *);
148 static int opnf(char *);
149 static int fsev_init(void);
150 static int addwatch(Pane *);
151 static int read_events(void);
152 static void rmwatch(Pane *);
153 static void fsev_shdn(void);
154 static void toggle_df(const Arg *arg);
155 static void start_filter(const Arg *arg);
156 static void start_vmode(const Arg *arg);
157 static void exit_vmode(const Arg *arg);
158 static void selup(const Arg *arg);
159 static void seldwn(const Arg *arg);
160 static void selall(const Arg *arg);
161 static void selref(void);
162 static void selynk(const Arg *arg);
163 static void selcalc(void);
164 static void paste(const Arg *arg);
165 static void selmv(const Arg *arg);
166 static void seldel(const Arg *arg);
167 static void init_files(void);
168 static void free_files(void);
169 static void yank(const Arg *arg);
170 static void rname(const Arg *arg);
171 static void switch_pane(const Arg *arg);
172 static void quit(const Arg *arg);
173 static void grabkeys(struct tb_event *, Key *, size_t);
174 static void *read_th(void *arg);
175 static void start_ev(void);
176 static void refresh_pane(Pane *);
177 static void set_direntr(Pane *, struct dirent *, DIR *, char *);
178 static int listdir(Pane *);
179 static void t_resize(void);
180 static void set_panes(void);
181 static void draw_frame(void);
182 static void start(void);
184 /* global variables */
185 static pthread_t fsev_thread;
186 static Pane panes[2];
187 static Pane *cpane;
188 static int pane_idx;
189 static char *editor[2];
190 static char fed[] = "vi";
191 static int theight, twidth, hwidth, scrheight;
192 static int *sel_indexes;
193 static size_t sel_len = 0;
194 static char **sel_files;
195 static int cont_vmode = 0;
196 pid_t main_pid;
197 #if defined(_SYS_INOTIFY_H)
198 #define READEVSZ 16
199 static int inotify_fd;
200 #elif defined(_SYS_EVENT_H_)
201 #define READEVSZ 0
202 static int kq;
203 struct kevent evlist[2]; /* events we want to monitor */
204 struct kevent chlist[2]; /* events that were triggered */
205 static struct timespec gtimeout;
206 #endif
207 #if defined(__linux__) || defined(__FreeBSD__)
208 #define OFF_T "%ld"
209 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
210 #define OFF_T "%lld"
211 #endif
212 enum { Left, Right }; /* panes */
214 /* configuration, allows nested code to access above variables */
215 #include "config.h"
217 /* function implementations */
218 static void
219 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
221 while (*str != '\0') {
222 uint32_t uni = 0;
223 str += tb_utf8_char_to_unicode(&uni, str);
224 tb_change_cell(x, y, uni, fg, bg);
225 x++;
229 static void
230 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
232 char buf[MAX_LINE];
233 va_list vl;
234 va_start(vl, fmt);
235 (void)vsnprintf(buf, MAX_LINE, fmt, vl);
236 va_end(vl);
237 print_tb(buf, x, y, col.fg, col.bg);
240 static void
241 print_status(Cpair col, const char *fmt, ...)
243 char buf[MAX_STATUS];
244 va_list vl;
245 va_start(vl, fmt);
246 (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
247 va_end(vl);
248 clear_status();
249 print_tb(buf, 1, theight - 1, col.fg, col.bg);
252 static void
253 print_xstatus(char c, int x)
255 uint32_t uni = 0;
256 (void)tb_utf8_char_to_unicode(&uni, &c);
257 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
260 static void
261 print_error(char *errmsg)
263 print_status(cerr, errmsg);
266 static void
267 print_prompt(char *prompt)
269 print_status(cprompt, prompt);
272 static void
273 print_info(Pane *pane, char *dirsize)
275 char *sz, *ur, *gr, *dt, *prm;
277 dt = ecalloc(MAX_DTF, sizeof(char));
279 prm = get_fperm(CURSOR(pane).mode);
280 ur = get_fusr(CURSOR(pane).user);
281 gr = get_fgrp(CURSOR(pane).group);
283 if (get_fdt(dt, CURSOR(pane).dt) < 0)
284 *dt = '\0';
286 if (S_ISREG(CURSOR(pane).mode)) {
287 sz = get_fsize(CURSOR(pane).size);
288 } else {
289 if (dirsize == NULL) {
290 sz = ecalloc(1, sizeof(char));
291 *sz = '\0';
292 } else {
293 sz = dirsize;
297 print_status(cstatus, "%02d/%02d %s %s:%s %s %s", pane->hdir,
298 pane->dirc, prm, ur, gr, dt, sz);
300 free(prm);
301 free(ur);
302 free(gr);
303 free(dt);
304 free(sz);
307 static void
308 print_row(Pane *pane, size_t entpos, Cpair col)
310 int x, y;
311 char *full_str;
312 char rez_pth[MAX_N - 5];
313 char lnk_full[MAX_N];
315 full_str = basename(pane->direntr[entpos].name);
316 x = pane->dirx;
317 y = entpos - pane->firstrow + 1;
319 if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
320 if (realpath(pane->direntr[entpos].name, rez_pth) != NULL) {
321 snprintf(
322 lnk_full, MAX_N, "%s -> %s", full_str, rez_pth);
323 full_str = lnk_full;
327 printf_tb(x, y, col, "%*.*s", ~hwidth, hwidth, full_str);
330 static void
331 clear(int sx, int ex, int y, uint16_t bg)
333 /* clear line from to */
334 /* x = line number vertical */
335 /* y = column number horizontal */
336 int i;
337 for (i = sx; i < ex; i++) {
338 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
342 static void
343 clear_status(void)
345 clear(1, twidth - 1, theight - 1, cstatus.bg);
348 static void
349 clear_pane(Pane *pane)
351 int i, y;
352 y = 0, i = 0;
354 while (i < scrheight) {
355 clear(pane->dirx, pane->ex, y, TB_DEFAULT);
356 i++;
357 y++;
360 /* draw top line */
361 for (y = pane->dirx; y < pane->ex; ++y) {
362 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
366 static void
367 add_hi(Pane *pane, size_t entpos)
369 Cpair col;
370 get_hicol(&col, pane->direntr[entpos].mode);
371 col.fg |= TB_REVERSE | TB_BOLD;
372 col.bg |= TB_REVERSE;
373 print_row(pane, entpos, col);
376 static void
377 rm_hi(Pane *pane, size_t entpos)
379 Cpair col;
380 get_hicol(&col, pane->direntr[entpos].mode);
381 print_row(pane, entpos, col);
384 static int
385 check_dir(char *path)
387 DIR *dir;
388 dir = opendir(path);
390 if (dir == NULL) {
391 if (errno == ENOTDIR) {
392 return 1;
393 } else {
394 return -1;
398 if (closedir(dir) < 0)
399 return -1;
401 return 0;
404 static mode_t
405 chech_execf(mode_t mode)
407 if (S_ISREG(mode))
408 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
409 return 0;
412 static int
413 sort_name(const void *const A, const void *const B)
415 int result;
416 mode_t data1 = (*(Entry *)A).mode;
417 mode_t data2 = (*(Entry *)B).mode;
419 if (data1 < data2) {
420 return -1;
421 } else if (data1 == data2) {
422 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
423 return result;
424 } else {
425 return 1;
429 static void
430 get_dirp(char *cdir)
432 int counter, len, i;
434 counter = 0;
435 len = strnlen(cdir, MAX_P);
436 if (len == 1)
437 return;
439 for (i = len - 1; i > 1; i--) {
440 if (cdir[i] == '/')
441 break;
442 else
443 counter++;
446 cdir[len - counter - 1] = '\0';
449 static char *
450 get_ext(char *str)
452 char *ext;
453 char dot;
454 size_t counter, len, i;
456 dot = '.';
457 counter = 0;
458 len = strnlen(str, MAX_N);
460 for (i = len - 1; i > 0; i--) {
461 if (str[i] == dot) {
462 break;
463 } else {
464 counter++;
468 ext = ecalloc(MAX_EXT + 1, sizeof(char));
469 strncpy(ext, &str[len - counter], MAX_EXT);
470 ext[MAX_EXT] = '\0';
471 return ext;
474 static int
475 get_fdt(char *result, time_t status)
477 struct tm lt;
478 localtime_r(&status, &lt);
479 return strftime(result, MAX_DTF, dtfmt, &lt);
482 static char *
483 get_fgrp(gid_t status)
485 char *result;
486 struct group *gr;
488 result = ecalloc(MAX_GRPN, sizeof(char));
489 gr = getgrgid(status);
490 if (gr == NULL)
491 (void)snprintf(result, MAX_GRPN, "%u", status);
492 else
493 strncpy(result, gr->gr_name, MAX_GRPN);
495 result[MAX_GRPN - 1] = '\0';
496 return result;
499 static char *
500 get_fperm(mode_t mode)
502 char *buf;
503 size_t i;
505 const char chars[] = "rwxrwxrwx";
506 buf = ecalloc(11, sizeof(char));
508 if (S_ISDIR(mode))
509 buf[0] = 'd';
510 else if (S_ISREG(mode))
511 buf[0] = '-';
512 else if (S_ISLNK(mode))
513 buf[0] = 'l';
514 else if (S_ISBLK(mode))
515 buf[0] = 'b';
516 else if (S_ISCHR(mode))
517 buf[0] = 'c';
518 else if (S_ISFIFO(mode))
519 buf[0] = 'p';
520 else if (S_ISSOCK(mode))
521 buf[0] = 's';
522 else
523 buf[0] = '?';
525 for (i = 1; i < 10; i++) {
526 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
528 buf[10] = '\0';
530 return buf;
533 static char *
534 get_fsize(off_t size)
536 char *result; /* need to be freed */
537 char unit;
538 int result_len;
539 int counter;
541 counter = 0;
542 result_len = 6; /* 9999X/0 */
543 result = ecalloc(result_len, sizeof(char));
545 while (size >= 1000) {
546 size /= 1024;
547 ++counter;
550 switch (counter) {
551 case 0:
552 unit = 'B';
553 break;
554 case 1:
555 unit = 'K';
556 break;
557 case 2:
558 unit = 'M';
559 break;
560 case 3:
561 unit = 'G';
562 break;
563 case 4:
564 unit = 'T';
565 break;
566 default:
567 unit = '?';
570 if (snprintf(result, result_len, OFF_T "%c", size, unit) < 0)
571 strncat(result, "???", result_len);
573 return result;
576 static char *
577 get_fullpath(char *first, char *second)
579 char *full_path;
581 full_path = ecalloc(MAX_P, sizeof(char));
583 if (strncmp(first, "/", MAX_P) == 0)
584 (void)snprintf(full_path, MAX_P, "/%s", second);
585 else
586 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
588 return full_path;
591 static char *
592 get_fusr(uid_t status)
594 char *result;
595 struct passwd *pw;
597 result = ecalloc(MAX_USRN, sizeof(char));
598 pw = getpwuid(status);
599 if (pw == NULL)
600 (void)snprintf(result, MAX_USRN, "%u", status);
601 else
602 strncpy(result, pw->pw_name, MAX_USRN);
604 result[MAX_USRN - 1] = '\0';
605 return result;
608 static void
609 get_dirsize(char *fullpath, off_t *fullsize)
611 DIR *dir;
612 char *ent_full;
613 mode_t mode;
614 struct dirent *entry;
615 struct stat status;
617 dir = opendir(fullpath);
618 if (dir == NULL) {
619 return;
622 while ((entry = readdir(dir)) != 0) {
623 if ((strncmp(entry->d_name, ".", 2) == 0 ||
624 strncmp(entry->d_name, "..", 3) == 0))
625 continue;
627 ent_full = get_fullpath(fullpath, entry->d_name);
628 if (lstat(ent_full, &status) == 0) {
629 mode = status.st_mode;
630 if (S_ISDIR(mode)) {
631 get_dirsize(ent_full, fullsize);
632 free(ent_full);
633 } else {
634 *fullsize += status.st_size;
635 free(ent_full);
640 closedir(dir);
641 clear_status();
644 static void
645 get_hicol(Cpair *col, mode_t mode)
647 *col = cfile;
648 if (S_ISDIR(mode))
649 *col = cdir;
650 else if (S_ISLNK(mode))
651 *col = cother;
652 else if (chech_execf(mode) > 0)
653 *col = cexec;
656 static void
657 delent(const Arg *arg)
659 if (cpane->dirc < 1)
660 return;
661 char *inp_conf;
662 int conf_len = 4;
663 char conf[] = "yes";
665 inp_conf = ecalloc(conf_len, sizeof(char));
666 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
667 (strncmp(inp_conf, conf, conf_len) != 0)) {
668 free(inp_conf);
669 return; /* canceled by user or wrong inp_conf */
671 free(inp_conf);
673 char *tmp[1];
674 tmp[0] = CURSOR(cpane).name;
675 if (spawn(rm_cmd, rm_cmd_len, tmp, 1, NULL) < 0) {
676 print_error(strerror(errno));
677 return;
681 static void
682 calcdir(const Arg *arg)
684 if (cpane->dirc < 1)
685 return;
686 if (!S_ISDIR(CURSOR(cpane).mode))
687 return;
689 off_t *fullsize;
690 char *csize;
692 fullsize = ecalloc(1, sizeof(off_t));
693 get_dirsize(CURSOR(cpane).name, fullsize);
694 csize = get_fsize(*fullsize);
696 CURSOR(cpane).size = *fullsize;
697 print_info(cpane, csize);
698 free(fullsize);
701 static void
702 crnd(const Arg *arg)
704 char *user_input, *path;
706 user_input = ecalloc(MAX_USRI, sizeof(char));
707 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
708 free(user_input);
709 return;
712 path = ecalloc(MAX_P, sizeof(char));
713 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
714 free(user_input);
715 free(path);
716 return;
719 PERROR(mkdir(path, ndir_perm) < 0);
721 free(user_input);
722 free(path);
725 static void
726 crnf(const Arg *arg)
728 char *user_input, *path;
729 int rf;
731 user_input = ecalloc(MAX_USRI, sizeof(char));
732 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
733 free(user_input);
734 return;
737 path = ecalloc(MAX_P, sizeof(char));
738 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
739 free(user_input);
740 free(path);
741 return;
744 rf = open(path, O_CREAT | O_EXCL, nf_perm);
746 if (rf < 0)
747 print_error(strerror(errno));
748 else if (close(rf) < 0)
749 print_error(strerror(errno));
751 free(user_input);
752 free(path);
754 static void
755 mv_ver(const Arg *arg)
758 if (cpane->dirc < 1)
759 return;
760 if (cpane->hdir - arg->i < 1) /* first line */
761 return;
763 if (cpane->hdir - arg->i > cpane->dirc) /* last line */
764 return;
766 if (cpane->firstrow > 0 && arg->i > 0 &&
767 cpane->hdir <= (cpane->firstrow + arg->i)) { /* scroll up */
768 cpane->firstrow = cpane->firstrow - arg->i;
769 rm_hi(cpane, cpane->hdir - 1);
770 cpane->hdir = cpane->hdir - arg->i;
771 refresh_pane(cpane);
772 add_hi(cpane, cpane->hdir - 1);
773 return;
776 if (cpane->hdir - cpane->firstrow >= scrheight + arg->i &&
777 arg->i < 0) { /* scroll down */
778 cpane->firstrow = cpane->firstrow - arg->i;
779 rm_hi(cpane, cpane->hdir - 1);
780 cpane->hdir = cpane->hdir - arg->i;
781 refresh_pane(cpane);
782 add_hi(cpane, cpane->hdir - 1);
783 return;
786 rm_hi(cpane, cpane->hdir - 1);
787 cpane->hdir = cpane->hdir - arg->i;
788 add_hi(cpane, cpane->hdir - 1);
789 print_info(cpane, NULL);
792 static void
793 mvbk(const Arg *arg)
795 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
796 return;
799 get_dirp(cpane->dirn);
800 if (check_dir(cpane->dirn) < 0) {
801 print_error(strerror(errno));
802 return;
805 rmwatch(cpane);
806 cpane->firstrow = cpane->parent_firstrow;
807 cpane->hdir = cpane->parent_row;
808 PERROR(listdir(cpane) < 0);
809 cpane->parent_firstrow = 0;
810 cpane->parent_row = 1;
813 static void
814 mvbtm(const Arg *arg)
816 if (cpane->dirc < 1)
817 return;
818 if (cpane->dirc > scrheight) {
819 rm_hi(cpane, cpane->hdir - 1);
820 cpane->hdir = cpane->dirc;
821 cpane->firstrow = cpane->dirc - scrheight + 1;
822 refresh_pane(cpane);
823 add_hi(cpane, cpane->hdir - 1);
824 } else {
825 rm_hi(cpane, cpane->hdir - 1);
826 cpane->hdir = cpane->dirc;
827 add_hi(cpane, cpane->hdir - 1);
829 print_info(cpane, NULL);
832 static void
833 mvfwd(const Arg *arg)
835 if (cpane->dirc < 1)
836 return;
837 int s;
839 switch (check_dir(CURSOR(cpane).name)) {
840 case 0:
841 strncpy(cpane->dirn, CURSOR(cpane).name, MAX_P);
842 cpane->parent_row = cpane->hdir;
843 cpane->parent_firstrow = cpane->firstrow;
844 cpane->hdir = 1;
845 cpane->firstrow = 0;
846 rmwatch(cpane);
847 PERROR(listdir(cpane) < 0);
848 break;
849 case 1: /* not a directory open file */
850 rmwatch(cpane);
851 tb_shutdown();
852 s = opnf(CURSOR(cpane).name);
853 if (tb_init() != 0)
854 die("tb_init");
855 t_resize();
856 if (s < 0)
857 print_error("process failed non-zero exit");
858 break;
859 case -1: /* failed to open directory */
860 print_error(strerror(errno));
864 static void
865 mvtop(const Arg *arg)
867 if (cpane->dirc < 1)
868 return;
869 if (cpane->dirc > scrheight) {
870 rm_hi(cpane, cpane->hdir - 1);
871 cpane->hdir = 1;
872 cpane->firstrow = 0;
873 refresh_pane(cpane);
874 add_hi(cpane, cpane->hdir - 1);
875 } else {
876 rm_hi(cpane, cpane->hdir - 1);
877 cpane->hdir = 1;
878 add_hi(cpane, cpane->hdir - 1);
879 print_info(cpane, NULL);
883 static void
884 bkmrk(const Arg *arg)
886 if (check_dir((char *)arg->v) != 0) {
887 print_error(strerror(errno));
888 return;
891 rmwatch(cpane);
892 strncpy(cpane->dirn, (char *)arg->v, MAX_P);
893 cpane->firstrow = 0;
894 cpane->parent_row = 1;
895 cpane->hdir = 1;
896 PERROR(listdir(cpane) < 0);
899 static int
900 get_usrinput(char *result, size_t max_chars, const char *fmt, ...)
902 char msg[MAX_N];
903 size_t i, cpos, startat;
904 struct tb_event fev;
905 va_list vl;
907 i = 0;
908 cpos = 1;
910 va_start(vl, fmt);
911 startat = vsnprintf(msg, MAX_N, fmt, vl) + 1;
912 va_end(vl);
914 clear_status();
915 print_tb(msg, 1, theight - 1, cprompt.fg, cprompt.bg);
916 tb_set_cursor(startat + 1, theight - 1);
917 tb_present();
919 while (tb_poll_event(&fev) != 0) {
920 switch (fev.type) {
921 case TB_EVENT_KEY:
922 if (fev.key == TB_KEY_ESC) {
923 tb_set_cursor(-1, -1);
924 clear_status();
925 return -1;
928 if (fev.key == TB_KEY_BACKSPACE ||
929 fev.key == TB_KEY_BACKSPACE2) {
930 if (BETWEEN(cpos, 2, max_chars)) {
931 result[i - 1] = '\0';
932 cpos--;
933 i--;
934 print_xstatus(' ', startat + cpos);
935 tb_set_cursor(
936 startat + cpos, theight - 1);
939 } else if (fev.key == TB_KEY_ENTER) {
940 tb_set_cursor(-1, -1);
941 result[cpos - 1] = '\0';
942 return 0;
944 } else if (fev.key) { /* disable other TB_KEY_* */
945 break;
947 } else {
948 if (cpos < max_chars) {
949 print_xstatus(
950 (char)fev.ch, (startat + cpos));
951 result[i] = (char)fev.ch;
952 tb_set_cursor((startat + cpos + 1),
953 theight - 1);
954 cpos++;
955 i++;
959 tb_present();
960 break;
962 case TB_EVENT_RESIZE:
963 t_resize();
964 clear_status();
965 print_tb(msg, 1, theight - 1, cprompt.fg, cprompt.bg);
966 print_tb(result, startat + 1, theight - 1, cstatus.fg,
967 cstatus.bg);
968 tb_present();
969 break;
971 default:
972 return -1;
976 return -1;
979 static int
980 frules(char *ex)
982 size_t c, d;
984 for (c = 0; c < LEN(rules); c++)
985 for (d = 0; d < rules[c].exlen; d++)
986 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
987 return c;
988 return -1;
991 static int
992 spawn(const void *com_argv, size_t com_argc, const void *f_argv, size_t f_argc,
993 char *fn)
995 int ws;
996 size_t argc;
997 pid_t pid, r;
999 argc = com_argc + f_argc + 2;
1000 char *argv[argc];
1002 memcpy(argv, com_argv, com_argc * sizeof(char *)); /* command */
1003 memcpy(&argv[com_argc], f_argv, f_argc * sizeof(char *)); /* files */
1005 argv[argc - 2] = fn;
1006 argv[argc - 1] = NULL;
1008 pid = fork();
1009 switch (pid) {
1010 case -1:
1011 return -1;
1012 case 0:
1013 execvp(argv[0], argv);
1014 exit(EXIT_SUCCESS);
1015 default:
1016 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1017 continue;
1018 if (r == -1)
1019 return -1;
1020 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1021 return -1;
1023 return 0;
1026 static int
1027 opnf(char *fn)
1029 char *ex;
1030 int c;
1032 ex = get_ext(fn);
1033 c = frules(ex);
1034 free(ex);
1036 if (c < 0) /* extension not found open in editor */
1037 return spawn(editor, 1, NULL, 0, fn);
1038 else
1039 return spawn((char **)rules[c].v, rules[c].vlen, NULL, 0, fn);
1042 static int
1043 fsev_init(void)
1045 #if defined(_SYS_INOTIFY_H)
1046 inotify_fd = inotify_init();
1047 if (inotify_fd < 0)
1048 return -1;
1049 #elif defined(_SYS_EVENT_H_)
1050 gtimeout.tv_sec = 1;
1051 kq = kqueue();
1052 if (kq < 0)
1053 return -1;
1054 #endif
1055 return 0;
1058 static int
1059 addwatch(Pane *pane)
1061 #if defined(_SYS_INOTIFY_H)
1062 return pane->inotify_wd = inotify_add_watch(inotify_fd, pane->dirn,
1063 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1064 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1065 #elif defined(_SYS_EVENT_H_)
1066 pane->event_fd = open(pane->dirn, O_RDONLY);
1067 if (pane->event_fd < 0)
1068 return pane->event_fd;
1069 EV_SET(&evlist[pane->pane_id], pane->event_fd, EVFILT_VNODE,
1070 EV_ADD | EV_CLEAR,
1071 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
1072 NOTE_REVOKE | NOTE_WRITE,
1073 0, NULL);
1074 return 0;
1075 #endif
1078 static int
1079 read_events(void)
1081 #if defined(_SYS_INOTIFY_H)
1082 char *p;
1083 ssize_t r;
1084 struct inotify_event *event;
1085 const size_t events = 32;
1086 const size_t evbuflen =
1087 events * (sizeof(struct inotify_event) + MAX_N + 1);
1088 char buf[evbuflen];
1090 if (cpane->inotify_wd < 0)
1091 return -1;
1092 r = read(inotify_fd, buf, evbuflen);
1093 if (r <= 0)
1094 return r;
1096 for (p = buf; p < buf + r;) {
1097 event = (struct inotify_event *)p;
1098 if (!event->wd)
1099 break;
1100 if (event->mask) {
1101 return r;
1104 p += sizeof(struct inotify_event) + event->len;
1106 #elif defined(_SYS_EVENT_H_)
1107 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1108 #endif
1109 return -1;
1112 static void
1113 rmwatch(Pane *pane)
1115 #if defined(_SYS_INOTIFY_H)
1116 if (pane->inotify_wd >= 0)
1117 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1118 #elif defined(_SYS_EVENT_H_)
1119 close(pane->event_fd);
1120 #endif
1123 static void
1124 fsev_shdn(void)
1126 pthread_cancel(fsev_thread);
1127 #if defined(__linux__)
1128 pthread_join(fsev_thread, NULL);
1129 #endif
1130 rmwatch(&panes[Left]);
1131 rmwatch(&panes[Right]);
1132 #if defined(_SYS_INOTIFY_H)
1133 close(inotify_fd);
1134 #elif defined(_SYS_EVENT_H_)
1135 close(kq);
1136 #endif
1139 static void
1140 toggle_df(const Arg *arg)
1142 show_dotfiles = !show_dotfiles;
1143 PERROR(listdir(&panes[Left]));
1144 PERROR(listdir(&panes[Right]));
1145 tb_present();
1148 static void
1149 start_filter(const Arg *arg)
1151 if (cpane->dirc < 1)
1152 return;
1153 char *user_input;
1154 user_input = ecalloc(MAX_USRI, sizeof(char));
1155 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1156 free(user_input);
1157 return;
1159 cpane->filter = user_input;
1160 if (listdir(cpane) < 0)
1161 print_error("no match");
1162 cpane->filter = NULL;
1163 free(user_input);
1166 static void
1167 start_vmode(const Arg *arg)
1169 if (cpane->dirc < 1)
1170 return;
1171 struct tb_event fev;
1172 if (sel_indexes != NULL) {
1173 free(sel_indexes);
1174 sel_indexes = NULL;
1177 sel_indexes = ecalloc(cpane->dirc, sizeof(size_t));
1178 sel_indexes[0] = cpane->hdir;
1179 cont_vmode = 0;
1180 print_prompt("-- VISUAL --");
1181 tb_present();
1182 while (tb_poll_event(&fev) != 0) {
1183 switch (fev.type) {
1184 case TB_EVENT_KEY:
1185 grabkeys(&fev, vkeys, vkeyslen);
1186 if (cont_vmode == -1)
1187 return;
1188 tb_present();
1189 break;
1194 static void
1195 exit_vmode(const Arg *arg)
1197 refresh_pane(cpane);
1198 add_hi(cpane, cpane->hdir - 1);
1199 cont_vmode = -1;
1202 static void
1203 selup(const Arg *arg)
1205 mv_ver(arg);
1206 print_prompt("-- VISUAL --");
1207 int index = abs(cpane->hdir - sel_indexes[0]);
1209 if (cpane->hdir < sel_indexes[0]) {
1210 sel_indexes[index] = cpane->hdir;
1211 add_hi(cpane, sel_indexes[index]);
1212 } else if (index < cpane->dirc) {
1213 sel_indexes[index + 1] = 0;
1215 if (cpane->dirc >= scrheight ||
1216 cpane->hdir <= 1) { /* rehighlight all if scrolling */
1217 selref();
1221 static void
1222 seldwn(const Arg *arg)
1224 mv_ver(arg);
1225 print_prompt("-- VISUAL --");
1226 int index = abs(cpane->hdir - sel_indexes[0]);
1228 if (cpane->hdir > sel_indexes[0]) {
1229 sel_indexes[index] = cpane->hdir;
1230 add_hi(cpane, sel_indexes[index] - 2);
1231 } else {
1232 sel_indexes[index + 1] = 0;
1234 if (cpane->dirc >= scrheight ||
1235 cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1236 selref();
1240 static void
1241 selall(const Arg *arg)
1243 int i;
1244 for (i = 0; i < cpane->dirc; i++) {
1245 sel_indexes[i] = i + 1;
1247 selref();
1250 static void
1251 selref(void)
1253 int i;
1254 for (i = 0; i < cpane->dirc; i++) {
1255 if (sel_indexes[i] < (scrheight + cpane->firstrow) &&
1256 sel_indexes[i] >
1257 cpane->firstrow) { /* checks if in the frame of the directories */
1258 add_hi(cpane, sel_indexes[i] - 1);
1263 static void
1264 selcalc(void)
1266 int j;
1267 sel_len = 0;
1269 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1270 if (sel_indexes[j] != 0)
1271 sel_len++;
1272 else
1273 break;
1277 static void
1278 free_files(void)
1280 size_t i;
1282 if (sel_files != NULL) {
1283 for (i = 0; i < sel_len; i++) {
1284 free(sel_files[i]);
1285 sel_files[i] = NULL;
1287 free(sel_files);
1288 sel_files = NULL;
1292 static void
1293 init_files(void)
1295 size_t i;
1296 free_files();
1298 selcalc();
1299 sel_files = ecalloc(sel_len, sizeof(char *));
1301 for (i = 0; i < sel_len; i++) {
1302 sel_files[i] = ecalloc(MAX_P, sizeof(char));
1303 strncpy(sel_files[i], cpane->direntr[sel_indexes[i] - 1].name,
1304 MAX_P);
1308 static void
1309 selynk(const Arg *arg)
1311 init_files();
1312 refresh_pane(cpane);
1313 add_hi(cpane, cpane->hdir - 1);
1314 print_status(cprompt, "%zu files are yanked", sel_len);
1315 cont_vmode = -1;
1318 static void
1319 seldel(const Arg *arg)
1321 char *inp_conf;
1323 inp_conf = ecalloc(delconf_len, sizeof(char));
1324 if ((get_usrinput(inp_conf, delconf_len, "delete file (yes) ?") < 0) ||
1325 (strncmp(inp_conf, delconf, delconf_len) != 0)) {
1326 free(inp_conf);
1327 return; /* canceled by user or wrong inp_conf */
1329 free(inp_conf);
1331 init_files();
1333 if (spawn(rm_cmd, rm_cmd_len, sel_files, sel_len, NULL) < 0)
1334 print_error(strerror(errno));
1335 else
1336 print_status(cprompt, "%zu files are deleted", sel_len);
1338 free_files();
1339 cont_vmode = -1;
1342 static void
1343 paste(const Arg *arg)
1345 if (sel_files == NULL) {
1346 print_error("nothing to paste");
1347 return;
1350 if (spawn(cp_cmd, cp_cmd_len, sel_files, sel_len, cpane->dirn) < 0)
1351 print_error(strerror(errno));
1352 else
1353 print_status(cprompt, "%zu files are copied", sel_len);
1355 free_files();
1358 static void
1359 selmv(const Arg *arg)
1361 if (sel_files == NULL) {
1362 print_error("nothing to move");
1363 return;
1366 if (spawn(mv_cmd, mv_cmd_len, sel_files, sel_len, cpane->dirn) < 0)
1367 print_error(strerror(errno));
1368 else
1369 print_status(cprompt, "%zu files are moved", sel_len);
1371 free_files();
1374 static void
1375 rname(const Arg *arg)
1377 if (cpane->dirc < 1)
1378 return;
1379 char new_name[MAX_P];
1380 char *input_name;
1382 input_name = ecalloc(MAX_N, sizeof(char));
1384 if (get_usrinput(input_name, MAX_N, "rename: %s",
1385 basename(CURSOR(cpane).name)) < 0) {
1386 free(input_name);
1387 return;
1390 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1391 free(input_name);
1392 print_error(strerror(errno));
1393 return;
1396 char *rename_cmd[] = { "mv", CURSOR(cpane).name, new_name };
1397 PERROR(spawn(rename_cmd, 3, NULL, 0, NULL) < 0);
1399 free(input_name);
1402 static void
1403 yank(const Arg *arg)
1405 if (cpane->dirc < 1)
1406 return;
1408 free_files();
1409 sel_len = 1;
1410 sel_files = ecalloc(sel_len, sizeof(char *));
1411 sel_files[0] = ecalloc(MAX_P, sizeof(char));
1412 strncpy(sel_files[0], CURSOR(cpane).name, MAX_P);
1413 print_status(cprompt, "1 file is yanked", sel_len);
1416 static void
1417 switch_pane(const Arg *arg)
1419 if (cpane->dirc > 0)
1420 rm_hi(cpane, cpane->hdir - 1);
1421 cpane = &panes[pane_idx ^= 1];
1422 if (cpane->dirc > 0) {
1423 add_hi(cpane, cpane->hdir - 1);
1424 print_info(cpane, NULL);
1425 } else {
1426 clear_status();
1430 static void
1431 quit(const Arg *arg)
1433 if (cont_vmode == -1) { /* check if selection was allocated */
1434 free(sel_indexes);
1435 if (sel_files != NULL)
1436 free_files();
1438 free(panes[Left].direntr);
1439 free(panes[Right].direntr);
1440 fsev_shdn();
1441 tb_shutdown();
1442 exit(EXIT_SUCCESS);
1445 static void
1446 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1448 size_t i;
1450 for (i = 0; i < max_keys; i++) {
1451 if (event->ch != 0) {
1452 if (event->ch == key[i].evkey.ch) {
1453 key[i].func(&key[i].arg);
1454 return;
1456 } else if (event->key != 0) {
1457 if (event->key == key[i].evkey.key) {
1458 key[i].func(&key[i].arg);
1459 return;
1465 void *
1466 read_th(void *arg)
1468 struct timespec tim;
1469 tim.tv_sec = 0;
1470 tim.tv_nsec = 5000000L; /* 0.005 sec */
1472 while (1)
1473 if (read_events() > READEVSZ) {
1474 kill(main_pid, SIGUSR1);
1475 nanosleep(&tim, NULL);
1477 return arg;
1480 static void
1481 start_ev(void)
1483 struct tb_event ev;
1485 while (tb_poll_event(&ev) != 0) {
1486 switch (ev.type) {
1487 case TB_EVENT_KEY:
1488 grabkeys(&ev, nkeys, nkeyslen);
1489 tb_present();
1490 break;
1491 case TB_EVENT_RESIZE:
1492 t_resize();
1493 break;
1494 default:
1495 break;
1498 tb_shutdown();
1501 static void
1502 refresh_pane(Pane *pane)
1504 size_t y, dyn_max, start_from;
1505 hwidth = (twidth / 2) - 4;
1506 Cpair col;
1508 y = 1;
1509 start_from = pane->firstrow;
1510 dyn_max = MIN(pane->dirc, (scrheight - 1) + pane->firstrow);
1512 /* print each entry in directory */
1513 while (start_from < dyn_max) {
1514 get_hicol(&col, pane->direntr[start_from].mode);
1515 print_row(pane, start_from, col);
1516 start_from++;
1517 y++;
1520 if (pane->dirc > 0)
1521 print_info(pane, NULL);
1522 else
1523 clear_status();
1525 /* print current directory title */
1526 pane->dircol.fg |= TB_BOLD;
1527 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", hwidth, pane->dirn);
1530 static void
1531 set_direntr(Pane *pane, struct dirent *entry, DIR *dir, char *filter)
1533 int i;
1534 char *tmpfull;
1535 struct stat status;
1537 #define ADD_ENTRY \
1538 tmpfull = get_fullpath(pane->dirn, entry->d_name); \
1539 strncpy(pane->direntr[i].name, tmpfull, MAX_N); \
1540 if (lstat(tmpfull, &status) == 0) { \
1541 pane->direntr[i].size = status.st_size; \
1542 pane->direntr[i].mode = status.st_mode; \
1543 pane->direntr[i].group = status.st_gid; \
1544 pane->direntr[i].user = status.st_uid; \
1545 pane->direntr[i].dt = status.st_mtime; \
1547 i++; \
1548 free(tmpfull);
1550 i = 0;
1551 pane->direntr =
1552 erealloc(pane->direntr, (10 + pane->dirc) * sizeof(Entry));
1553 while ((entry = readdir(dir)) != 0) {
1554 if (show_dotfiles == 1) {
1555 if (entry->d_name[0] == '.' &&
1556 (entry->d_name[1] == '\0' ||
1557 entry->d_name[1] == '.'))
1558 continue;
1559 } else {
1560 if (entry->d_name[0] == '.')
1561 continue;
1564 if (filter == NULL) {
1565 ADD_ENTRY
1566 } else if (filter != NULL) {
1567 if (strcasestr(entry->d_name, filter) != NULL) {
1568 ADD_ENTRY
1573 pane->dirc = i;
1576 static int
1577 listdir(Pane *pane)
1579 DIR *dir;
1580 struct dirent *entry;
1581 int filtercount = 0;
1582 size_t oldc = pane->dirc;
1584 pane->dirc = 0;
1586 dir = opendir(pane->dirn);
1587 if (dir == NULL)
1588 return -1;
1590 /* get content and filter sum */
1591 while ((entry = readdir(dir)) != 0) {
1592 if (pane->filter != NULL) {
1593 if (strcasestr(entry->d_name, pane->filter) != NULL)
1594 filtercount++;
1595 } else { /* no filter */
1596 pane->dirc++;
1600 if (pane->filter == NULL) {
1601 clear_pane(pane);
1602 pane->dirc -= 2;
1605 if (pane->filter != NULL) {
1606 if (filtercount > 0) {
1607 pane->dirc = filtercount;
1608 clear_pane(pane);
1609 pane->hdir = 1;
1610 } else if (filtercount == 0) {
1611 if (closedir(dir) < 0)
1612 return -1;
1613 pane->dirc = oldc;
1614 return -1;
1618 /* print current directory title */
1619 pane->dircol.fg |= TB_BOLD;
1620 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", hwidth, pane->dirn);
1622 if (pane->filter == NULL) /* dont't watch when filtering */
1623 if (addwatch(pane) < 0)
1624 print_error("can't add watch");
1626 /* empty directory */
1627 if (pane->dirc == 0) {
1628 clear_status();
1629 if (closedir(dir) < 0)
1630 return -1;
1631 return 0;
1634 rewinddir(dir); /* reset position */
1635 set_direntr(
1636 pane, entry, dir, pane->filter); /* create array of entries */
1637 qsort(pane->direntr, pane->dirc, sizeof(Entry), sort_name);
1638 refresh_pane(pane);
1640 if (pane->hdir > pane->dirc)
1641 pane->hdir = pane->dirc;
1643 if (pane == cpane && pane->dirc > 0)
1644 add_hi(pane, pane->hdir - 1);
1646 if (closedir(dir) < 0)
1647 return -1;
1648 return 0;
1651 static void
1652 t_resize(void)
1654 tb_clear();
1655 draw_frame();
1656 panes[Right].dirx = (twidth / 2) + 2;
1657 refresh_pane(&panes[Left]);
1658 refresh_pane(&panes[Right]);
1659 if (cpane->dirc > 0)
1660 add_hi(cpane, cpane->hdir - 1);
1661 tb_present();
1664 static void
1665 get_editor(void)
1667 editor[0] = getenv("EDITOR");
1668 editor[1] = NULL;
1670 if (editor[0] == NULL)
1671 editor[0] = fed;
1674 static void
1675 set_panes(void)
1677 char *home;
1678 char cwd[MAX_P];
1680 home = getenv("HOME");
1681 if (home == NULL)
1682 home = "/";
1683 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1684 strncpy(cwd, home, MAX_P);
1686 pane_idx = Left; /* cursor pane */
1687 cpane = &panes[pane_idx];
1689 panes[Left].ex = (twidth / 2) - 1;
1690 panes[Left].pane_id = 0;
1691 panes[Left].dirx = 2;
1692 panes[Left].dircol = cpanell;
1693 panes[Left].firstrow = 0;
1694 panes[Left].direntr = ecalloc(0, sizeof(Entry));
1695 strncpy(panes[Left].dirn, cwd, MAX_P);
1696 panes[Left].hdir = 1;
1697 panes[Left].inotify_wd = -1;
1698 panes[Left].parent_row = 1;
1700 panes[Right].ex = twidth - 1;
1701 panes[Right].pane_id = 1;
1702 panes[Right].dirx = (twidth / 2) + 2;
1703 panes[Right].dircol = cpanelr;
1704 panes[Right].firstrow = 0;
1705 panes[Right].direntr = ecalloc(0, sizeof(Entry));
1706 strncpy(panes[Right].dirn, home, MAX_P);
1707 panes[Right].hdir = 1;
1708 panes[Right].inotify_wd = -1;
1709 panes[Right].parent_row = 1;
1712 static void
1713 draw_frame(void)
1715 int i;
1716 theight = tb_height();
1717 twidth = tb_width();
1718 hwidth = (twidth / 2) - 4;
1719 scrheight = theight - 2;
1721 /* 2 horizontal lines */
1722 for (i = 1; i < twidth - 1; ++i) {
1723 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1724 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1727 /* 4 vertical lines */
1728 for (i = 1; i < theight - 1; ++i) {
1729 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1730 tb_change_cell(
1731 (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
1732 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1733 cframe.bg);
1734 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1737 /* 4 corners */
1738 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1739 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1740 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1741 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1743 /* 2 middel top and bottom */
1744 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1745 tb_change_cell(
1746 (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1749 void
1750 th_handler(int num)
1752 (void)num;
1753 PERROR(listdir(&panes[Left]));
1754 PERROR(listdir(&panes[Right]));
1755 tb_present();
1758 static int
1759 start_signal(void)
1761 struct sigaction sa;
1763 main_pid = getpid();
1764 sa.sa_handler = th_handler;
1765 sigemptyset(&sa.sa_mask);
1766 sa.sa_flags = SA_RESTART;
1767 return sigaction(SIGUSR1, &sa, NULL);
1770 static void
1771 start(void)
1773 switch (tb_init()) {
1774 case TB_EFAILED_TO_OPEN_TTY:
1775 die("TB_EFAILED_TO_OPEN_TTY");
1776 case TB_EUNSUPPORTED_TERMINAL:
1777 die("TB_EUNSUPPORTED_TERMINAL");
1778 case TB_EPIPE_TRAP_ERROR:
1779 die("TB_EUNSUPPORTED_TERMINAL");
1780 case 0:
1781 break;
1782 default:
1783 die("UNKNOWN FAILURE");
1786 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1787 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1788 die("output error");
1789 draw_frame();
1790 set_panes();
1791 get_editor();
1792 PERROR(start_signal() < 0);
1793 PERROR(fsev_init() < 0);
1794 PERROR(listdir(&panes[Left]) < 0);
1795 PERROR(listdir(&panes[Right]) < 0);
1796 tb_present();
1798 pthread_create(&fsev_thread, NULL, read_th, NULL);
1799 start_ev();
1803 main(int argc, char *argv[])
1805 #if defined(__OpenBSD__)
1806 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1807 NULL) == -1)
1808 die("pledge");
1809 #endif /* __OpenBSD__ */
1810 if (argc == 1)
1811 start();
1812 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1813 die("sfm-" VERSION);
1814 else
1815 die("usage: sfm [-v]");
1816 return 0;