[fix] get_usrinput()
[sfm.git] / sfm.c
blob1f77d096e3bcc8ebae1ee4dd96363d77e475d3ab
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 mvmid(const Arg *arg);
144 static void mvtop(const Arg *arg);
145 static void bkmrk(const Arg *arg);
146 static int get_usrinput(char *, size_t, const char *, ...);
147 static int frules(char *);
148 static int spawn(const void *, size_t, const void *, size_t, char *);
149 static int opnf(char *);
150 static int fsev_init(void);
151 static int addwatch(Pane *);
152 static int read_events(void);
153 static void rmwatch(Pane *);
154 static void fsev_shdn(void);
155 static void toggle_df(const Arg *arg);
156 static void start_filter(const Arg *arg);
157 static void start_vmode(const Arg *arg);
158 static void exit_vmode(const Arg *arg);
159 static void selup(const Arg *arg);
160 static void seldwn(const Arg *arg);
161 static void selall(const Arg *arg);
162 static void selref(void);
163 static void selynk(const Arg *arg);
164 static void selcalc(void);
165 static void paste(const Arg *arg);
166 static void selmv(const Arg *arg);
167 static void seldel(const Arg *arg);
168 static void init_files(void);
169 static void free_files(void);
170 static void yank(const Arg *arg);
171 static void rname(const Arg *arg);
172 static void switch_pane(const Arg *arg);
173 static void quit(const Arg *arg);
174 static void grabkeys(struct tb_event *, Key *, size_t);
175 static void *read_th(void *arg);
176 static void start_ev(void);
177 static void refresh_pane(Pane *);
178 static void set_direntr(Pane *, struct dirent *, DIR *, char *);
179 static int listdir(Pane *);
180 static void t_resize(void);
181 static void set_panes(void);
182 static void draw_frame(void);
183 static void start(void);
185 /* global variables */
186 static pthread_t fsev_thread;
187 static Pane panes[2];
188 static Pane *cpane;
189 static int pane_idx;
190 static char *editor[2];
191 static char fed[] = "vi";
192 static int theight, twidth, scrheight;
193 static int *sel_indexes;
194 static size_t sel_len = 0;
195 static char **sel_files;
196 static int cont_vmode = 0;
197 pid_t main_pid;
198 #if defined(_SYS_INOTIFY_H)
199 #define READEVSZ 16
200 static int inotify_fd;
201 #elif defined(_SYS_EVENT_H_)
202 #define READEVSZ 0
203 static int kq;
204 struct kevent evlist[2]; /* events we want to monitor */
205 struct kevent chlist[2]; /* events that were triggered */
206 static struct timespec gtimeout;
207 #endif
208 #if defined(__linux__) || defined(__FreeBSD__)
209 #define OFF_T "%ld"
210 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
211 #define OFF_T "%lld"
212 #endif
213 enum { Left, Right }; /* panes */
215 /* configuration, allows nested code to access above variables */
216 #include "config.h"
218 /* function implementations */
219 static void
220 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
222 while (*str != '\0') {
223 uint32_t uni = 0;
224 str += tb_utf8_char_to_unicode(&uni, str);
225 tb_change_cell(x, y, uni, fg, bg);
226 x++;
230 static void
231 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
233 char buf[MAX_LINE];
234 va_list vl;
235 va_start(vl, fmt);
236 (void)vsnprintf(buf, MAX_LINE, fmt, vl);
237 va_end(vl);
238 print_tb(buf, x, y, col.fg, col.bg);
241 static void
242 print_status(Cpair col, const char *fmt, ...)
244 char buf[MAX_STATUS];
245 va_list vl;
246 va_start(vl, fmt);
247 (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
248 va_end(vl);
249 clear_status();
250 print_tb(buf, 1, theight - 1, col.fg, col.bg);
253 static void
254 print_xstatus(char c, int x)
256 uint32_t uni = 0;
257 (void)tb_utf8_char_to_unicode(&uni, &c);
258 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
261 static void
262 print_error(char *errmsg)
264 print_status(cerr, errmsg);
267 static void
268 print_prompt(char *prompt)
270 print_status(cprompt, prompt);
273 static void
274 print_info(Pane *pane, char *dirsize)
276 char *sz, *ur, *gr, *dt, *prm;
278 dt = ecalloc(MAX_DTF, sizeof(char));
280 prm = get_fperm(CURSOR(pane).mode);
281 ur = get_fusr(CURSOR(pane).user);
282 gr = get_fgrp(CURSOR(pane).group);
284 if (get_fdt(dt, CURSOR(pane).dt) < 0)
285 *dt = '\0';
287 if (S_ISREG(CURSOR(pane).mode)) {
288 sz = get_fsize(CURSOR(pane).size);
289 } else {
290 if (dirsize == NULL) {
291 sz = ecalloc(1, sizeof(char));
292 *sz = '\0';
293 } else {
294 sz = dirsize;
298 print_status(cstatus, "%02d/%02d %s %s:%s %s %s", pane->hdir,
299 pane->dirc, prm, ur, gr, dt, sz);
301 free(prm);
302 free(ur);
303 free(gr);
304 free(dt);
305 free(sz);
308 static void
309 print_row(Pane *pane, size_t entpos, Cpair col)
311 int x, y;
312 char *result;
313 char buf[MAX_P];
314 char lnk_full[MAX_P];
315 int width;
317 width = (twidth / 2) - 4;
318 result = basename(pane->direntr[entpos].name);
319 x = pane->dirx;
320 y = entpos - pane->firstrow + 1;
322 if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
323 if (realpath(pane->direntr[entpos].name, buf) != NULL) {
324 (void)snprintf(
325 lnk_full, MAX_N, "%s -> %s", result, buf);
326 result = lnk_full;
330 printf_tb(x, y, col, "%*.*s", ~width, width, result);
333 static void
334 clear(int sx, int ex, int y, uint16_t bg)
336 /* clear line from to */
337 /* x = line number vertical */
338 /* y = column number horizontal */
339 int i;
340 for (i = sx; i < ex; i++) {
341 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
345 static void
346 clear_status(void)
348 clear(1, twidth - 1, theight - 1, cstatus.bg);
351 static void
352 clear_pane(Pane *pane)
354 int i, y;
355 y = 0, i = 0;
357 while (i < scrheight) {
358 clear(pane->dirx, pane->ex, y, TB_DEFAULT);
359 i++;
360 y++;
363 /* draw top line */
364 for (y = pane->dirx; y < pane->ex; ++y) {
365 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
369 static void
370 add_hi(Pane *pane, size_t entpos)
372 Cpair col;
373 get_hicol(&col, pane->direntr[entpos].mode);
374 col.fg |= TB_REVERSE | TB_BOLD;
375 col.bg |= TB_REVERSE;
376 print_row(pane, entpos, col);
379 static void
380 rm_hi(Pane *pane, size_t entpos)
382 Cpair col;
383 get_hicol(&col, pane->direntr[entpos].mode);
384 print_row(pane, entpos, col);
387 static int
388 check_dir(char *path)
390 DIR *dir;
391 dir = opendir(path);
393 if (dir == NULL) {
394 if (errno == ENOTDIR) {
395 return 1;
396 } else {
397 return -1;
401 if (closedir(dir) < 0)
402 return -1;
404 return 0;
407 static mode_t
408 chech_execf(mode_t mode)
410 if (S_ISREG(mode))
411 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
412 return 0;
415 static int
416 sort_name(const void *const A, const void *const B)
418 int result;
419 mode_t data1 = (*(Entry *)A).mode;
420 mode_t data2 = (*(Entry *)B).mode;
422 if (data1 < data2) {
423 return -1;
424 } else if (data1 == data2) {
425 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
426 return result;
427 } else {
428 return 1;
432 static void
433 get_dirp(char *cdir)
435 int counter, len, i;
437 counter = 0;
438 len = strnlen(cdir, MAX_P);
439 if (len == 1)
440 return;
442 for (i = len - 1; i > 1; i--) {
443 if (cdir[i] == '/')
444 break;
445 else
446 counter++;
449 cdir[len - counter - 1] = '\0';
452 static char *
453 get_ext(char *str)
455 char *ext;
456 char dot;
457 size_t counter, len, i;
459 dot = '.';
460 counter = 0;
461 len = strnlen(str, MAX_N);
463 for (i = len - 1; i > 0; i--) {
464 if (str[i] == dot) {
465 break;
466 } else {
467 counter++;
471 ext = ecalloc(MAX_EXT + 1, sizeof(char));
472 strncpy(ext, &str[len - counter], MAX_EXT);
473 ext[MAX_EXT] = '\0';
474 return ext;
477 static int
478 get_fdt(char *result, time_t status)
480 struct tm lt;
481 localtime_r(&status, &lt);
482 return strftime(result, MAX_DTF, dtfmt, &lt);
485 static char *
486 get_fgrp(gid_t status)
488 char *result;
489 struct group *gr;
491 result = ecalloc(MAX_GRPN, sizeof(char));
492 gr = getgrgid(status);
493 if (gr == NULL)
494 (void)snprintf(result, MAX_GRPN, "%u", status);
495 else
496 strncpy(result, gr->gr_name, MAX_GRPN);
498 result[MAX_GRPN - 1] = '\0';
499 return result;
502 static char *
503 get_fperm(mode_t mode)
505 char *buf;
506 size_t i;
508 const char chars[] = "rwxrwxrwx";
509 buf = ecalloc(11, sizeof(char));
511 if (S_ISDIR(mode))
512 buf[0] = 'd';
513 else if (S_ISREG(mode))
514 buf[0] = '-';
515 else if (S_ISLNK(mode))
516 buf[0] = 'l';
517 else if (S_ISBLK(mode))
518 buf[0] = 'b';
519 else if (S_ISCHR(mode))
520 buf[0] = 'c';
521 else if (S_ISFIFO(mode))
522 buf[0] = 'p';
523 else if (S_ISSOCK(mode))
524 buf[0] = 's';
525 else
526 buf[0] = '?';
528 for (i = 1; i < 10; i++) {
529 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
531 buf[10] = '\0';
533 return buf;
536 static char *
537 get_fsize(off_t size)
539 char *result; /* need to be freed */
540 char unit;
541 int result_len;
542 int counter;
544 counter = 0;
545 result_len = 6; /* 9999X/0 */
546 result = ecalloc(result_len, sizeof(char));
548 while (size >= 1000) {
549 size /= 1024;
550 ++counter;
553 switch (counter) {
554 case 0:
555 unit = 'B';
556 break;
557 case 1:
558 unit = 'K';
559 break;
560 case 2:
561 unit = 'M';
562 break;
563 case 3:
564 unit = 'G';
565 break;
566 case 4:
567 unit = 'T';
568 break;
569 default:
570 unit = '?';
573 if (snprintf(result, result_len, OFF_T "%c", size, unit) < 0)
574 strncat(result, "???", result_len);
576 return result;
579 static char *
580 get_fullpath(char *first, char *second)
582 char *full_path;
584 full_path = ecalloc(MAX_P, sizeof(char));
586 if (strncmp(first, "/", MAX_P) == 0)
587 (void)snprintf(full_path, MAX_P, "/%s", second);
588 else
589 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
591 return full_path;
594 static char *
595 get_fusr(uid_t status)
597 char *result;
598 struct passwd *pw;
600 result = ecalloc(MAX_USRN, sizeof(char));
601 pw = getpwuid(status);
602 if (pw == NULL)
603 (void)snprintf(result, MAX_USRN, "%u", status);
604 else
605 strncpy(result, pw->pw_name, MAX_USRN);
607 result[MAX_USRN - 1] = '\0';
608 return result;
611 static void
612 get_dirsize(char *fullpath, off_t *fullsize)
614 DIR *dir;
615 char *ent_full;
616 mode_t mode;
617 struct dirent *entry;
618 struct stat status;
620 dir = opendir(fullpath);
621 if (dir == NULL) {
622 return;
625 while ((entry = readdir(dir)) != 0) {
626 if ((strncmp(entry->d_name, ".", 2) == 0 ||
627 strncmp(entry->d_name, "..", 3) == 0))
628 continue;
630 ent_full = get_fullpath(fullpath, entry->d_name);
631 if (lstat(ent_full, &status) == 0) {
632 mode = status.st_mode;
633 if (S_ISDIR(mode)) {
634 get_dirsize(ent_full, fullsize);
635 free(ent_full);
636 } else {
637 *fullsize += status.st_size;
638 free(ent_full);
643 closedir(dir);
644 clear_status();
647 static void
648 get_hicol(Cpair *col, mode_t mode)
650 *col = cfile;
651 if (S_ISDIR(mode))
652 *col = cdir;
653 else if (S_ISLNK(mode))
654 *col = cother;
655 else if (chech_execf(mode) > 0)
656 *col = cexec;
659 static void
660 delent(const Arg *arg)
662 if (cpane->dirc < 1)
663 return;
664 char *inp_conf;
665 int conf_len = 4;
666 char conf[] = "yes";
668 inp_conf = ecalloc(conf_len, sizeof(char));
669 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
670 (strncmp(inp_conf, conf, conf_len) != 0)) {
671 free(inp_conf);
672 return; /* canceled by user or wrong inp_conf */
674 free(inp_conf);
676 char *tmp[1];
677 tmp[0] = CURSOR(cpane).name;
678 if (spawn(rm_cmd, rm_cmd_len, tmp, 1, NULL) < 0) {
679 print_error(strerror(errno));
680 return;
684 static void
685 calcdir(const Arg *arg)
687 if (cpane->dirc < 1)
688 return;
689 if (!S_ISDIR(CURSOR(cpane).mode))
690 return;
692 off_t *fullsize;
693 char *csize;
695 fullsize = ecalloc(1, sizeof(off_t));
696 get_dirsize(CURSOR(cpane).name, fullsize);
697 csize = get_fsize(*fullsize);
699 CURSOR(cpane).size = *fullsize;
700 print_info(cpane, csize);
701 free(fullsize);
704 static void
705 crnd(const Arg *arg)
707 char *user_input, *path;
709 user_input = ecalloc(MAX_USRI, sizeof(char));
710 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
711 free(user_input);
712 return;
715 path = ecalloc(MAX_P, sizeof(char));
716 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
717 free(user_input);
718 free(path);
719 return;
722 PERROR(mkdir(path, ndir_perm) < 0);
724 free(user_input);
725 free(path);
728 static void
729 crnf(const Arg *arg)
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);
757 static void
758 mv_ver(const Arg *arg)
761 if (cpane->dirc < 1)
762 return;
763 if (cpane->hdir - arg->i < 1) /* first line */
764 return;
766 if (cpane->hdir - arg->i > cpane->dirc) /* last line */
767 return;
769 if (cpane->firstrow > 0 && arg->i > 0 &&
770 cpane->hdir <= (cpane->firstrow + arg->i)) { /* scroll up */
771 cpane->firstrow = cpane->firstrow - arg->i;
772 rm_hi(cpane, cpane->hdir - 1);
773 cpane->hdir = cpane->hdir - arg->i;
774 refresh_pane(cpane);
775 add_hi(cpane, cpane->hdir - 1);
776 return;
779 if (cpane->hdir - cpane->firstrow >= scrheight + arg->i &&
780 arg->i < 0) { /* scroll down */
781 cpane->firstrow = cpane->firstrow - arg->i;
782 rm_hi(cpane, cpane->hdir - 1);
783 cpane->hdir = cpane->hdir - arg->i;
784 refresh_pane(cpane);
785 add_hi(cpane, cpane->hdir - 1);
786 return;
789 rm_hi(cpane, cpane->hdir - 1);
790 cpane->hdir = cpane->hdir - arg->i;
791 add_hi(cpane, cpane->hdir - 1);
792 print_info(cpane, NULL);
795 static void
796 mvbk(const Arg *arg)
798 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
799 return;
802 get_dirp(cpane->dirn);
803 if (check_dir(cpane->dirn) < 0) {
804 print_error(strerror(errno));
805 return;
808 rmwatch(cpane);
809 cpane->firstrow = cpane->parent_firstrow;
810 cpane->hdir = cpane->parent_row;
811 PERROR(listdir(cpane) < 0);
812 cpane->parent_firstrow = 0;
813 cpane->parent_row = 1;
816 static void
817 mvbtm(const Arg *arg)
819 if (cpane->dirc < 1)
820 return;
821 if (cpane->dirc > scrheight) {
822 rm_hi(cpane, cpane->hdir - 1);
823 cpane->hdir = cpane->dirc;
824 cpane->firstrow = cpane->dirc - scrheight + 1;
825 refresh_pane(cpane);
826 add_hi(cpane, cpane->hdir - 1);
827 } else {
828 rm_hi(cpane, cpane->hdir - 1);
829 cpane->hdir = cpane->dirc;
830 add_hi(cpane, cpane->hdir - 1);
832 print_info(cpane, NULL);
835 static void
836 mvfwd(const Arg *arg)
838 if (cpane->dirc < 1)
839 return;
840 int s;
842 switch (check_dir(CURSOR(cpane).name)) {
843 case 0:
844 strncpy(cpane->dirn, CURSOR(cpane).name, MAX_P);
845 cpane->parent_row = cpane->hdir;
846 cpane->parent_firstrow = cpane->firstrow;
847 cpane->hdir = 1;
848 cpane->firstrow = 0;
849 rmwatch(cpane);
850 PERROR(listdir(cpane) < 0);
851 break;
852 case 1: /* not a directory open file */
853 rmwatch(cpane);
854 tb_shutdown();
855 s = opnf(CURSOR(cpane).name);
856 if (tb_init() != 0)
857 die("tb_init");
858 t_resize();
859 if (s < 0)
860 print_error("process failed non-zero exit");
861 break;
862 case -1: /* failed to open directory */
863 print_error(strerror(errno));
867 static void
868 mvmid(const Arg *arg)
870 if (cpane->dirc < 1)
871 return;
872 rm_hi(cpane, cpane->hdir - 1);
873 if (cpane->dirc < scrheight / 2)
874 cpane->hdir = (cpane->dirc + 1) / 2;
875 else
876 cpane->hdir = (scrheight / 2) + cpane->firstrow;
877 add_hi(cpane, cpane->hdir - 1);
878 print_info(cpane, NULL);
881 static void
882 mvtop(const Arg *arg)
884 if (cpane->dirc < 1)
885 return;
886 if (cpane->dirc > scrheight) {
887 rm_hi(cpane, cpane->hdir - 1);
888 cpane->hdir = 1;
889 cpane->firstrow = 0;
890 refresh_pane(cpane);
891 add_hi(cpane, cpane->hdir - 1);
892 } else {
893 rm_hi(cpane, cpane->hdir - 1);
894 cpane->hdir = 1;
895 add_hi(cpane, cpane->hdir - 1);
896 print_info(cpane, NULL);
900 static void
901 bkmrk(const Arg *arg)
903 if (check_dir((char *)arg->v) != 0) {
904 print_error(strerror(errno));
905 return;
908 rmwatch(cpane);
909 strncpy(cpane->dirn, (char *)arg->v, MAX_P);
910 cpane->firstrow = 0;
911 cpane->parent_row = 1;
912 cpane->hdir = 1;
913 PERROR(listdir(cpane) < 0);
916 static int
917 get_usrinput(char *result, size_t max_chars, const char *fmt, ...)
919 char msg[MAX_N];
920 size_t i, cpos, startat;
921 struct tb_event fev;
922 va_list vl;
924 i = 0;
925 cpos = 1;
927 va_start(vl, fmt);
928 startat = vsnprintf(msg, MAX_N, fmt, vl) + 1;
929 va_end(vl);
931 clear_status();
932 print_tb(msg, 1, theight - 1, cprompt.fg, cprompt.bg);
933 tb_set_cursor(startat + 1, theight - 1);
934 tb_present();
936 while (tb_poll_event(&fev) != 0) {
937 switch (fev.type) {
938 case TB_EVENT_KEY:
939 if (fev.key == TB_KEY_ESC) {
940 tb_set_cursor(-1, -1);
941 clear_status();
942 return -1;
945 if (fev.key == TB_KEY_BACKSPACE ||
946 fev.key == TB_KEY_BACKSPACE2) {
947 if (BETWEEN(cpos, 2, max_chars)) {
948 result[i - 1] = '\0';
949 cpos--;
950 i--;
951 print_xstatus(' ', startat + cpos);
952 tb_set_cursor(
953 startat + cpos, theight - 1);
956 } else if (fev.key == TB_KEY_ENTER) {
957 tb_set_cursor(-1, -1);
958 result[cpos - 1] = '\0';
959 return 0;
961 } else if (fev.key) { /* disable other TB_KEY_* */
962 break;
964 } else {
965 if (cpos < max_chars) {
966 print_xstatus((char)fev.ch,
967 (startat + cpos));
968 result[i] = (char)fev.ch;
969 tb_set_cursor((startat + cpos + 1),
970 theight - 1);
971 cpos++;
972 i++;
976 tb_present();
977 break;
979 case TB_EVENT_RESIZE:
980 t_resize();
981 clear_status();
982 print_tb(msg, 1, theight - 1, cprompt.fg, cprompt.bg);
983 print_tb(result, startat + 1, theight - 1, cstatus.fg,
984 cstatus.bg);
985 tb_present();
986 break;
988 default:
989 return -1;
993 return -1;
996 static int
997 frules(char *ex)
999 size_t c, d;
1001 for (c = 0; c < LEN(rules); c++)
1002 for (d = 0; d < rules[c].exlen; d++)
1003 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1004 return c;
1005 return -1;
1008 static int
1009 spawn(const void *com_argv, size_t com_argc, const void *f_argv, size_t f_argc,
1010 char *fn)
1012 int ws;
1013 size_t argc;
1014 pid_t pid, r;
1016 argc = com_argc + f_argc + 2;
1017 char *argv[argc];
1019 memcpy(argv, com_argv, com_argc * sizeof(char *)); /* command */
1020 memcpy(&argv[com_argc], f_argv, f_argc * sizeof(char *)); /* files */
1022 argv[argc - 2] = fn;
1023 argv[argc - 1] = NULL;
1025 pid = fork();
1026 switch (pid) {
1027 case -1:
1028 return -1;
1029 case 0:
1030 execvp(argv[0], argv);
1031 exit(EXIT_SUCCESS);
1032 default:
1033 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1034 continue;
1035 if (r == -1)
1036 return -1;
1037 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1038 return -1;
1040 return 0;
1043 static int
1044 opnf(char *fn)
1046 char *ex;
1047 int c;
1049 ex = get_ext(fn);
1050 c = frules(ex);
1051 free(ex);
1053 if (c < 0) /* extension not found open in editor */
1054 return spawn(editor, 1, NULL, 0, fn);
1055 else
1056 return spawn((char **)rules[c].v, rules[c].vlen, NULL, 0, fn);
1059 static int
1060 fsev_init(void)
1062 #if defined(_SYS_INOTIFY_H)
1063 inotify_fd = inotify_init();
1064 if (inotify_fd < 0)
1065 return -1;
1066 #elif defined(_SYS_EVENT_H_)
1067 gtimeout.tv_sec = 1;
1068 kq = kqueue();
1069 if (kq < 0)
1070 return -1;
1071 #endif
1072 return 0;
1075 static int
1076 addwatch(Pane *pane)
1078 #if defined(_SYS_INOTIFY_H)
1079 return pane->inotify_wd = inotify_add_watch(inotify_fd, pane->dirn,
1080 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1081 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1082 #elif defined(_SYS_EVENT_H_)
1083 pane->event_fd = open(pane->dirn, O_RDONLY);
1084 if (pane->event_fd < 0)
1085 return pane->event_fd;
1086 EV_SET(&evlist[pane->pane_id], pane->event_fd, EVFILT_VNODE,
1087 EV_ADD | EV_CLEAR,
1088 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
1089 NOTE_REVOKE | NOTE_WRITE,
1090 0, NULL);
1091 return 0;
1092 #endif
1095 static int
1096 read_events(void)
1098 #if defined(_SYS_INOTIFY_H)
1099 char *p;
1100 ssize_t r;
1101 struct inotify_event *event;
1102 const size_t events = 32;
1103 const size_t evbuflen =
1104 events * (sizeof(struct inotify_event) + MAX_N + 1);
1105 char buf[evbuflen];
1107 if (cpane->inotify_wd < 0)
1108 return -1;
1109 r = read(inotify_fd, buf, evbuflen);
1110 if (r <= 0)
1111 return r;
1113 for (p = buf; p < buf + r;) {
1114 event = (struct inotify_event *)p;
1115 if (!event->wd)
1116 break;
1117 if (event->mask) {
1118 return r;
1121 p += sizeof(struct inotify_event) + event->len;
1123 #elif defined(_SYS_EVENT_H_)
1124 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1125 #endif
1126 return -1;
1129 static void
1130 rmwatch(Pane *pane)
1132 #if defined(_SYS_INOTIFY_H)
1133 if (pane->inotify_wd >= 0)
1134 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1135 #elif defined(_SYS_EVENT_H_)
1136 close(pane->event_fd);
1137 #endif
1140 static void
1141 fsev_shdn(void)
1143 pthread_cancel(fsev_thread);
1144 #if defined(__linux__)
1145 pthread_join(fsev_thread, NULL);
1146 #endif
1147 rmwatch(&panes[Left]);
1148 rmwatch(&panes[Right]);
1149 #if defined(_SYS_INOTIFY_H)
1150 close(inotify_fd);
1151 #elif defined(_SYS_EVENT_H_)
1152 close(kq);
1153 #endif
1156 static void
1157 toggle_df(const Arg *arg)
1159 show_dotfiles = !show_dotfiles;
1160 PERROR(listdir(&panes[Left]));
1161 PERROR(listdir(&panes[Right]));
1162 tb_present();
1165 static void
1166 start_filter(const Arg *arg)
1168 if (cpane->dirc < 1)
1169 return;
1170 char *user_input;
1171 user_input = ecalloc(MAX_USRI, sizeof(char));
1172 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1173 free(user_input);
1174 return;
1176 cpane->filter = user_input;
1177 if (listdir(cpane) < 0)
1178 print_error("no match");
1179 cpane->filter = NULL;
1180 free(user_input);
1183 static void
1184 start_vmode(const Arg *arg)
1186 if (cpane->dirc < 1)
1187 return;
1188 struct tb_event fev;
1189 if (sel_indexes != NULL) {
1190 free(sel_indexes);
1191 sel_indexes = NULL;
1194 sel_indexes = ecalloc(cpane->dirc, sizeof(size_t));
1195 sel_indexes[0] = cpane->hdir;
1196 cont_vmode = 0;
1197 print_prompt("-- VISUAL --");
1198 tb_present();
1199 while (tb_poll_event(&fev) != 0) {
1200 switch (fev.type) {
1201 case TB_EVENT_KEY:
1202 grabkeys(&fev, vkeys, vkeyslen);
1203 if (cont_vmode == -1)
1204 return;
1205 tb_present();
1206 break;
1211 static void
1212 exit_vmode(const Arg *arg)
1214 refresh_pane(cpane);
1215 add_hi(cpane, cpane->hdir - 1);
1216 cont_vmode = -1;
1219 static void
1220 selup(const Arg *arg)
1222 mv_ver(arg);
1223 print_prompt("-- VISUAL --");
1224 int index = abs(cpane->hdir - sel_indexes[0]);
1226 if (cpane->hdir < sel_indexes[0]) {
1227 sel_indexes[index] = cpane->hdir;
1228 add_hi(cpane, sel_indexes[index]);
1229 } else if (index < cpane->dirc) {
1230 sel_indexes[index + 1] = 0;
1232 if (cpane->dirc >= scrheight ||
1233 cpane->hdir <= 1) { /* rehighlight all if scrolling */
1234 selref();
1238 static void
1239 seldwn(const Arg *arg)
1241 mv_ver(arg);
1242 print_prompt("-- VISUAL --");
1243 int index = abs(cpane->hdir - sel_indexes[0]);
1245 if (cpane->hdir > sel_indexes[0]) {
1246 sel_indexes[index] = cpane->hdir;
1247 add_hi(cpane, sel_indexes[index] - 2);
1248 } else {
1249 sel_indexes[index + 1] = 0;
1251 if (cpane->dirc >= scrheight ||
1252 cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1253 selref();
1257 static void
1258 selall(const Arg *arg)
1260 int i;
1261 for (i = 0; i < cpane->dirc; i++) {
1262 sel_indexes[i] = i + 1;
1264 selref();
1267 static void
1268 selref(void)
1270 int i;
1271 for (i = 0; i < cpane->dirc; i++) {
1272 if (sel_indexes[i] < (scrheight + cpane->firstrow) &&
1273 sel_indexes[i] >
1274 cpane->firstrow) { /* checks if in the frame of the directories */
1275 add_hi(cpane, sel_indexes[i] - 1);
1280 static void
1281 selcalc(void)
1283 int j;
1284 sel_len = 0;
1286 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1287 if (sel_indexes[j] != 0)
1288 sel_len++;
1289 else
1290 break;
1294 static void
1295 free_files(void)
1297 size_t i;
1299 if (sel_files != NULL) {
1300 for (i = 0; i < sel_len; i++) {
1301 free(sel_files[i]);
1302 sel_files[i] = NULL;
1304 free(sel_files);
1305 sel_files = NULL;
1309 static void
1310 init_files(void)
1312 size_t i;
1313 free_files();
1315 selcalc();
1316 sel_files = ecalloc(sel_len, sizeof(char *));
1318 for (i = 0; i < sel_len; i++) {
1319 sel_files[i] = ecalloc(MAX_P, sizeof(char));
1320 strncpy(sel_files[i], cpane->direntr[sel_indexes[i] - 1].name,
1321 MAX_P);
1325 static void
1326 selynk(const Arg *arg)
1328 init_files();
1329 refresh_pane(cpane);
1330 add_hi(cpane, cpane->hdir - 1);
1331 print_status(cprompt, "%zu files are yanked", sel_len);
1332 cont_vmode = -1;
1335 static void
1336 seldel(const Arg *arg)
1338 char *inp_conf;
1340 inp_conf = ecalloc(delconf_len, sizeof(char));
1341 if ((get_usrinput(inp_conf, delconf_len, "delete file (yes) ?") < 0) ||
1342 (strncmp(inp_conf, delconf, delconf_len) != 0)) {
1343 free(inp_conf);
1344 return; /* canceled by user or wrong inp_conf */
1346 free(inp_conf);
1348 init_files();
1350 if (spawn(rm_cmd, rm_cmd_len, sel_files, sel_len, NULL) < 0)
1351 print_error(strerror(errno));
1352 else
1353 print_status(cprompt, "%zu files are deleted", sel_len);
1355 free_files();
1356 cont_vmode = -1;
1359 static void
1360 paste(const Arg *arg)
1362 if (sel_files == NULL) {
1363 print_error("nothing to paste");
1364 return;
1367 if (spawn(cp_cmd, cp_cmd_len, sel_files, sel_len, cpane->dirn) < 0)
1368 print_error(strerror(errno));
1369 else
1370 print_status(cprompt, "%zu files are copied", sel_len);
1372 free_files();
1375 static void
1376 selmv(const Arg *arg)
1378 if (sel_files == NULL) {
1379 print_error("nothing to move");
1380 return;
1383 if (spawn(mv_cmd, mv_cmd_len, sel_files, sel_len, cpane->dirn) < 0)
1384 print_error(strerror(errno));
1385 else
1386 print_status(cprompt, "%zu files are moved", sel_len);
1388 free_files();
1391 static void
1392 rname(const Arg *arg)
1394 if (cpane->dirc < 1)
1395 return;
1396 char new_name[MAX_P];
1397 char *input_name;
1399 input_name = ecalloc(MAX_N, sizeof(char));
1401 if (get_usrinput(input_name, MAX_N, "rename: %s",
1402 basename(CURSOR(cpane).name)) < 0) {
1403 free(input_name);
1404 return;
1407 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1408 free(input_name);
1409 print_error(strerror(errno));
1410 return;
1413 char *rename_cmd[] = { "mv", CURSOR(cpane).name, new_name };
1414 PERROR(spawn(rename_cmd, 3, NULL, 0, NULL) < 0);
1416 free(input_name);
1419 static void
1420 yank(const Arg *arg)
1422 if (cpane->dirc < 1)
1423 return;
1425 free_files();
1426 sel_len = 1;
1427 sel_files = ecalloc(sel_len, sizeof(char *));
1428 sel_files[0] = ecalloc(MAX_P, sizeof(char));
1429 strncpy(sel_files[0], CURSOR(cpane).name, MAX_P);
1430 print_status(cprompt, "1 file is yanked", sel_len);
1433 static void
1434 switch_pane(const Arg *arg)
1436 if (cpane->dirc > 0)
1437 rm_hi(cpane, cpane->hdir - 1);
1438 cpane = &panes[pane_idx ^= 1];
1439 if (cpane->dirc > 0) {
1440 add_hi(cpane, cpane->hdir - 1);
1441 print_info(cpane, NULL);
1442 } else {
1443 clear_status();
1447 static void
1448 quit(const Arg *arg)
1450 if (cont_vmode == -1) { /* check if selection was allocated */
1451 free(sel_indexes);
1452 if (sel_files != NULL)
1453 free_files();
1455 free(panes[Left].direntr);
1456 free(panes[Right].direntr);
1457 fsev_shdn();
1458 tb_shutdown();
1459 exit(EXIT_SUCCESS);
1462 static void
1463 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1465 size_t i;
1467 for (i = 0; i < max_keys; i++) {
1468 if (event->ch != 0) {
1469 if (event->ch == key[i].evkey.ch) {
1470 key[i].func(&key[i].arg);
1471 return;
1473 } else if (event->key != 0) {
1474 if (event->key == key[i].evkey.key) {
1475 key[i].func(&key[i].arg);
1476 return;
1482 void *
1483 read_th(void *arg)
1485 struct timespec tim;
1486 tim.tv_sec = 0;
1487 tim.tv_nsec = 5000000L; /* 0.005 sec */
1489 while (1)
1490 if (read_events() > READEVSZ) {
1491 kill(main_pid, SIGUSR1);
1492 nanosleep(&tim, NULL);
1494 return arg;
1497 static void
1498 start_ev(void)
1500 struct tb_event ev;
1502 while (tb_poll_event(&ev) != 0) {
1503 switch (ev.type) {
1504 case TB_EVENT_KEY:
1505 grabkeys(&ev, nkeys, nkeyslen);
1506 tb_present();
1507 break;
1508 case TB_EVENT_RESIZE:
1509 t_resize();
1510 break;
1511 default:
1512 break;
1515 tb_shutdown();
1518 static void
1519 refresh_pane(Pane *pane)
1521 size_t y, dyn_max, start_from;
1522 int width;
1523 width = (twidth / 2) - 4;
1524 Cpair col;
1526 y = 1;
1527 start_from = pane->firstrow;
1528 dyn_max = MIN(pane->dirc, (scrheight - 1) + pane->firstrow);
1530 /* print each entry in directory */
1531 while (start_from < dyn_max) {
1532 get_hicol(&col, pane->direntr[start_from].mode);
1533 print_row(pane, start_from, col);
1534 start_from++;
1535 y++;
1538 if (pane->dirc > 0)
1539 print_info(pane, NULL);
1540 else
1541 clear_status();
1543 /* print current directory title */
1544 pane->dircol.fg |= TB_BOLD;
1545 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", width, pane->dirn);
1548 static void
1549 set_direntr(Pane *pane, struct dirent *entry, DIR *dir, char *filter)
1551 int i;
1552 char *tmpfull;
1553 struct stat status;
1555 #define ADD_ENTRY \
1556 tmpfull = get_fullpath(pane->dirn, entry->d_name); \
1557 strncpy(pane->direntr[i].name, tmpfull, MAX_N); \
1558 if (lstat(tmpfull, &status) == 0) { \
1559 pane->direntr[i].size = status.st_size; \
1560 pane->direntr[i].mode = status.st_mode; \
1561 pane->direntr[i].group = status.st_gid; \
1562 pane->direntr[i].user = status.st_uid; \
1563 pane->direntr[i].dt = status.st_mtime; \
1565 i++; \
1566 free(tmpfull);
1568 i = 0;
1569 pane->direntr =
1570 erealloc(pane->direntr, (10 + pane->dirc) * sizeof(Entry));
1571 while ((entry = readdir(dir)) != 0) {
1572 if (show_dotfiles == 1) {
1573 if (entry->d_name[0] == '.' &&
1574 (entry->d_name[1] == '\0' ||
1575 entry->d_name[1] == '.'))
1576 continue;
1577 } else {
1578 if (entry->d_name[0] == '.')
1579 continue;
1582 if (filter == NULL) {
1583 ADD_ENTRY
1584 } else if (filter != NULL) {
1585 if (strcasestr(entry->d_name, filter) != NULL) {
1586 ADD_ENTRY
1591 pane->dirc = i;
1594 static int
1595 listdir(Pane *pane)
1597 DIR *dir;
1598 struct dirent *entry;
1599 int width;
1600 int filtercount = 0;
1601 size_t oldc = pane->dirc;
1603 width = (twidth / 2) - 4;
1604 pane->dirc = 0;
1606 dir = opendir(pane->dirn);
1607 if (dir == NULL)
1608 return -1;
1610 /* get content and filter sum */
1611 while ((entry = readdir(dir)) != 0) {
1612 if (pane->filter != NULL) {
1613 if (strcasestr(entry->d_name, pane->filter) != NULL)
1614 filtercount++;
1615 } else { /* no filter */
1616 pane->dirc++;
1620 if (pane->filter == NULL) {
1621 clear_pane(pane);
1622 pane->dirc -= 2;
1625 if (pane->filter != NULL) {
1626 if (filtercount > 0) {
1627 pane->dirc = filtercount;
1628 clear_pane(pane);
1629 pane->hdir = 1;
1630 } else if (filtercount == 0) {
1631 if (closedir(dir) < 0)
1632 return -1;
1633 pane->dirc = oldc;
1634 return -1;
1638 /* print current directory title */
1639 pane->dircol.fg |= TB_BOLD;
1640 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", width, pane->dirn);
1642 if (pane->filter == NULL) /* dont't watch when filtering */
1643 if (addwatch(pane) < 0)
1644 print_error("can't add watch");
1646 /* empty directory */
1647 if (pane->dirc == 0) {
1648 clear_status();
1649 if (closedir(dir) < 0)
1650 return -1;
1651 return 0;
1654 rewinddir(dir); /* reset position */
1655 set_direntr(
1656 pane, entry, dir, pane->filter); /* create array of entries */
1657 qsort(pane->direntr, pane->dirc, sizeof(Entry), sort_name);
1658 refresh_pane(pane);
1660 if (pane->hdir > pane->dirc)
1661 pane->hdir = pane->dirc;
1663 if (pane == cpane && pane->dirc > 0)
1664 add_hi(pane, pane->hdir - 1);
1666 if (closedir(dir) < 0)
1667 return -1;
1668 return 0;
1671 static void
1672 t_resize(void)
1674 tb_clear();
1675 draw_frame();
1676 panes[Right].dirx = (twidth / 2) + 2;
1677 refresh_pane(&panes[Left]);
1678 refresh_pane(&panes[Right]);
1679 if (cpane->dirc > 0)
1680 add_hi(cpane, cpane->hdir - 1);
1681 tb_present();
1684 static void
1685 get_editor(void)
1687 editor[0] = getenv("EDITOR");
1688 editor[1] = NULL;
1690 if (editor[0] == NULL)
1691 editor[0] = fed;
1694 static void
1695 set_panes(void)
1697 char *home;
1698 char cwd[MAX_P];
1700 home = getenv("HOME");
1701 if (home == NULL)
1702 home = "/";
1703 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1704 strncpy(cwd, home, MAX_P);
1706 pane_idx = Left; /* cursor pane */
1707 cpane = &panes[pane_idx];
1709 panes[Left].ex = (twidth / 2) - 1;
1710 panes[Left].pane_id = 0;
1711 panes[Left].dirx = 2;
1712 panes[Left].dircol = cpanell;
1713 panes[Left].firstrow = 0;
1714 panes[Left].direntr = ecalloc(0, sizeof(Entry));
1715 strncpy(panes[Left].dirn, cwd, MAX_P);
1716 panes[Left].hdir = 1;
1717 panes[Left].inotify_wd = -1;
1718 panes[Left].parent_row = 1;
1720 panes[Right].ex = twidth - 1;
1721 panes[Right].pane_id = 1;
1722 panes[Right].dirx = (twidth / 2) + 2;
1723 panes[Right].dircol = cpanelr;
1724 panes[Right].firstrow = 0;
1725 panes[Right].direntr = ecalloc(0, sizeof(Entry));
1726 strncpy(panes[Right].dirn, home, MAX_P);
1727 panes[Right].hdir = 1;
1728 panes[Right].inotify_wd = -1;
1729 panes[Right].parent_row = 1;
1732 static void
1733 draw_frame(void)
1735 int i;
1736 theight = tb_height();
1737 twidth = tb_width();
1738 scrheight = theight - 2;
1740 /* 2 horizontal lines */
1741 for (i = 1; i < twidth - 1; ++i) {
1742 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1743 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1746 /* 4 vertical lines */
1747 for (i = 1; i < theight - 1; ++i) {
1748 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1749 tb_change_cell(
1750 (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
1751 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1752 cframe.bg);
1753 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1756 /* 4 corners */
1757 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1758 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1759 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1760 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1762 /* 2 middel top and bottom */
1763 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1764 tb_change_cell(
1765 (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1768 void
1769 th_handler(int num)
1771 (void)num;
1772 PERROR(listdir(&panes[Left]));
1773 PERROR(listdir(&panes[Right]));
1774 tb_present();
1777 static int
1778 start_signal(void)
1780 struct sigaction sa;
1782 main_pid = getpid();
1783 sa.sa_handler = th_handler;
1784 sigemptyset(&sa.sa_mask);
1785 sa.sa_flags = SA_RESTART;
1786 return sigaction(SIGUSR1, &sa, NULL);
1789 static void
1790 start(void)
1792 FAIL_IF(tb_init() != 0, "tb_init()");
1793 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1794 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1795 die("output error");
1796 draw_frame();
1797 set_panes();
1798 get_editor();
1799 PERROR(start_signal() < 0);
1800 PERROR(fsev_init() < 0);
1801 PERROR(listdir(&panes[Left]) < 0);
1802 PERROR(listdir(&panes[Right]) < 0);
1803 tb_present();
1805 pthread_create(&fsev_thread, NULL, read_th, NULL);
1806 start_ev();
1810 main(int argc, char *argv[])
1812 #if defined(__OpenBSD__)
1813 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1814 NULL) == -1)
1815 die("pledge");
1816 #endif /* __OpenBSD__ */
1817 if (argc == 1)
1818 start();
1819 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1820 die("sfm-" VERSION);
1821 else
1822 die("usage: sfm [-v]");
1823 return 0;