[fix] resize term update right pane x
[sfm.git] / sfm.c
blobf2ef7d8ee4cccbd214a65abfff101ab2d1d26b56
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 - cpane->firstrow - arg->i < 1) { /* move to the top */
764 if (arg->i > 1)
765 mvtop(arg);
766 return;
768 if (cpane->hdir - arg->i > cpane->dirc || /* move to the bottom */
769 cpane->hdir - cpane->firstrow - arg->i > scrheight - 1) {
770 if (arg->i < 1)
771 mvbtm(arg);
772 return;
775 if (cpane->firstrow > 1 && arg->i > 0 &&
776 cpane->hdir < (cpane->firstrow + arg->i)) { /* scroll up */
777 cpane->firstrow = cpane->firstrow - arg->i;
778 rm_hi(cpane, cpane->hdir - 1);
779 cpane->hdir = cpane->hdir - arg->i;
780 refresh_pane(cpane);
781 add_hi(cpane, cpane->hdir - 1);
782 return;
785 if (cpane->hdir - cpane->firstrow >= scrheight - 1 &&
786 arg->i < 0) { /* scroll down */
787 cpane->firstrow = cpane->firstrow - arg->i;
788 rm_hi(cpane, cpane->hdir - 1);
789 cpane->hdir = cpane->hdir - arg->i;
790 refresh_pane(cpane);
791 add_hi(cpane, cpane->hdir - 1);
792 return;
795 rm_hi(cpane, cpane->hdir - 1);
796 cpane->hdir = cpane->hdir - arg->i;
797 add_hi(cpane, cpane->hdir - 1);
798 print_info(cpane, NULL);
801 static void
802 mvbk(const Arg *arg)
804 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
805 return;
808 get_dirp(cpane->dirn);
809 if (check_dir(cpane->dirn) < 0) {
810 print_error(strerror(errno));
811 return;
814 rmwatch(cpane);
815 cpane->firstrow = cpane->parent_firstrow;
816 cpane->hdir = cpane->parent_row;
817 PERROR(listdir(cpane) < 0);
818 cpane->parent_firstrow = 0;
819 cpane->parent_row = 1;
822 static void
823 mvbtm(const Arg *arg)
825 if (cpane->dirc < 1)
826 return;
827 if (cpane->dirc > scrheight) {
828 rm_hi(cpane, cpane->hdir - 1);
829 cpane->hdir = cpane->dirc;
830 cpane->firstrow = cpane->dirc - scrheight + 1;
831 refresh_pane(cpane);
832 add_hi(cpane, cpane->hdir - 1);
833 } else {
834 rm_hi(cpane, cpane->hdir - 1);
835 cpane->hdir = cpane->dirc;
836 add_hi(cpane, cpane->hdir - 1);
838 print_info(cpane, NULL);
841 static void
842 mvfwd(const Arg *arg)
844 if (cpane->dirc < 1)
845 return;
846 int s;
848 switch (check_dir(CURSOR(cpane).name)) {
849 case 0:
850 strncpy(cpane->dirn, CURSOR(cpane).name, MAX_P);
851 cpane->parent_row = cpane->hdir;
852 cpane->parent_firstrow = cpane->firstrow;
853 cpane->hdir = 1;
854 cpane->firstrow = 0;
855 rmwatch(cpane);
856 PERROR(listdir(cpane) < 0);
857 break;
858 case 1: /* not a directory open file */
859 rmwatch(cpane);
860 tb_shutdown();
861 s = opnf(CURSOR(cpane).name);
862 if (tb_init() != 0)
863 die("tb_init");
864 t_resize();
865 if (s < 0)
866 print_error("process failed non-zero exit");
867 break;
868 case -1: /* failed to open directory */
869 print_error(strerror(errno));
873 static void
874 mvmid(const Arg *arg)
876 if (cpane->dirc < 1)
877 return;
878 rm_hi(cpane, cpane->hdir - 1);
879 if (cpane->dirc < scrheight / 2)
880 cpane->hdir = (cpane->dirc + 1) / 2;
881 else
882 cpane->hdir = (scrheight / 2) + cpane->firstrow;
883 add_hi(cpane, cpane->hdir - 1);
884 print_info(cpane, NULL);
887 static void
888 mvtop(const Arg *arg)
890 if (cpane->dirc < 1)
891 return;
892 if (cpane->dirc > scrheight) {
893 rm_hi(cpane, cpane->hdir - 1);
894 cpane->hdir = 1;
895 cpane->firstrow = 0;
896 refresh_pane(cpane);
897 add_hi(cpane, cpane->hdir - 1);
898 } else {
899 rm_hi(cpane, cpane->hdir - 1);
900 cpane->hdir = 1;
901 add_hi(cpane, cpane->hdir - 1);
902 print_info(cpane, NULL);
906 static void
907 bkmrk(const Arg *arg)
909 if (check_dir((char *)arg->v) != 0) {
910 print_error(strerror(errno));
911 return;
914 rmwatch(cpane);
915 strncpy(cpane->dirn, (char *)arg->v, MAX_P);
916 cpane->firstrow = 0;
917 cpane->parent_row = 1;
918 cpane->hdir = 1;
919 PERROR(listdir(cpane) < 0);
922 static int
923 get_usrinput(char *out, size_t sout, const char *fmt, ...)
925 int height = tb_height();
926 size_t startat;
927 struct tb_event fev;
928 size_t counter = (size_t)1;
929 char empty = ' ';
930 int x = 0;
931 int name_size = 0;
932 char buf[256];
934 clear_status();
936 va_list vl;
937 Cpair col;
938 col = cprompt;
939 va_start(vl, fmt);
940 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
941 va_end(vl);
942 print_tb(buf, 1, height - 1, col.fg, col.bg);
943 startat = name_size + 1;
944 tb_set_cursor((int)(startat + 1), height - 1);
945 tb_present();
947 while (tb_poll_event(&fev) != 0) {
948 switch (fev.type) {
949 case TB_EVENT_KEY:
950 if (fev.key == TB_KEY_ESC) {
951 tb_set_cursor(-1, -1);
952 clear_status();
953 return -1;
956 if (fev.key == TB_KEY_BACKSPACE ||
957 fev.key == TB_KEY_BACKSPACE2) {
958 if (BETWEEN(counter, 2, sout)) {
959 out[x - 1] = '\0';
960 counter--;
961 x--;
962 print_xstatus(empty, startat + counter);
963 tb_set_cursor(
964 startat + counter, theight - 1);
967 } else if (fev.key == TB_KEY_ENTER) {
968 tb_set_cursor(-1, -1);
969 out[counter - 1] = '\0';
970 return 0;
972 } else {
973 if (counter < sout) {
974 print_xstatus((char)fev.ch,
975 (startat + counter));
976 out[x] = (char)fev.ch;
977 tb_set_cursor((startat + counter + 1),
978 theight - 1);
979 counter++;
980 x++;
984 tb_present();
985 break;
987 default:
988 return -1;
992 return -1;
995 static int
996 frules(char *ex)
998 size_t c, d;
1000 for (c = 0; c < LEN(rules); c++)
1001 for (d = 0; d < rules[c].exlen; d++)
1002 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1003 return c;
1004 return -1;
1007 static int
1008 spawn(const void *com_argv, size_t com_argc, const void *f_argv, size_t f_argc,
1009 char *fn)
1011 int ws;
1012 size_t argc;
1013 pid_t pid, r;
1015 argc = com_argc + f_argc + 2;
1016 char *argv[argc];
1018 memcpy(argv, com_argv, com_argc * sizeof(char *)); /* command */
1019 memcpy(&argv[com_argc], f_argv, f_argc * sizeof(char *)); /* files */
1021 argv[argc - 2] = fn;
1022 argv[argc - 1] = NULL;
1024 pid = fork();
1025 switch (pid) {
1026 case -1:
1027 return -1;
1028 case 0:
1029 execvp(argv[0], argv);
1030 exit(EXIT_SUCCESS);
1031 default:
1032 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1033 continue;
1034 if (r == -1)
1035 return -1;
1036 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1037 return -1;
1039 return 0;
1042 static int
1043 opnf(char *fn)
1045 char *ex;
1046 int c;
1048 ex = get_ext(fn);
1049 c = frules(ex);
1050 free(ex);
1052 if (c < 0) /* extension not found open in editor */
1053 return spawn(editor, 1, NULL, 0, fn);
1054 else
1055 return spawn((char **)rules[c].v, rules[c].vlen, NULL, 0, fn);
1058 static int
1059 fsev_init(void)
1061 #if defined(_SYS_INOTIFY_H)
1062 inotify_fd = inotify_init();
1063 if (inotify_fd < 0)
1064 return -1;
1065 #elif defined(_SYS_EVENT_H_)
1066 gtimeout.tv_sec = 1;
1067 kq = kqueue();
1068 if (kq < 0)
1069 return -1;
1070 #endif
1071 return 0;
1074 static int
1075 addwatch(Pane *pane)
1077 #if defined(_SYS_INOTIFY_H)
1078 return pane->inotify_wd = inotify_add_watch(inotify_fd, pane->dirn,
1079 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1080 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1081 #elif defined(_SYS_EVENT_H_)
1082 pane->event_fd = open(pane->dirn, O_RDONLY);
1083 if (pane->event_fd < 0)
1084 return pane->event_fd;
1085 EV_SET(&evlist[pane->pane_id], pane->event_fd, EVFILT_VNODE,
1086 EV_ADD | EV_CLEAR,
1087 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
1088 NOTE_REVOKE | NOTE_WRITE,
1089 0, NULL);
1090 return 0;
1091 #endif
1094 static int
1095 read_events(void)
1097 #if defined(_SYS_INOTIFY_H)
1098 char *p;
1099 ssize_t r;
1100 struct inotify_event *event;
1101 const size_t events = 32;
1102 const size_t evbuflen =
1103 events * (sizeof(struct inotify_event) + MAX_N + 1);
1104 char buf[evbuflen];
1106 if (cpane->inotify_wd < 0)
1107 return -1;
1108 r = read(inotify_fd, buf, evbuflen);
1109 if (r <= 0)
1110 return r;
1112 for (p = buf; p < buf + r;) {
1113 event = (struct inotify_event *)p;
1114 if (!event->wd)
1115 break;
1116 if (event->mask) {
1117 return r;
1120 p += sizeof(struct inotify_event) + event->len;
1122 #elif defined(_SYS_EVENT_H_)
1123 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1124 #endif
1125 return -1;
1128 static void
1129 rmwatch(Pane *pane)
1131 #if defined(_SYS_INOTIFY_H)
1132 if (pane->inotify_wd >= 0)
1133 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1134 #elif defined(_SYS_EVENT_H_)
1135 close(pane->event_fd);
1136 #endif
1139 static void
1140 fsev_shdn(void)
1142 pthread_cancel(fsev_thread);
1143 #if defined(__linux__)
1144 pthread_join(fsev_thread, NULL);
1145 #endif
1146 rmwatch(&panes[Left]);
1147 rmwatch(&panes[Right]);
1148 #if defined(_SYS_INOTIFY_H)
1149 close(inotify_fd);
1150 #elif defined(_SYS_EVENT_H_)
1151 close(kq);
1152 #endif
1155 static void
1156 toggle_df(const Arg *arg)
1158 show_dotfiles = !show_dotfiles;
1159 PERROR(listdir(&panes[Left]));
1160 PERROR(listdir(&panes[Right]));
1161 tb_present();
1164 static void
1165 start_filter(const Arg *arg)
1167 if (cpane->dirc < 1)
1168 return;
1169 char *user_input;
1170 user_input = ecalloc(MAX_USRI, sizeof(char));
1171 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1172 free(user_input);
1173 return;
1175 cpane->filter = user_input;
1176 if (listdir(cpane) < 0)
1177 print_error("no match");
1178 cpane->filter = NULL;
1179 free(user_input);
1182 static void
1183 start_vmode(const Arg *arg)
1185 if (cpane->dirc < 1)
1186 return;
1187 struct tb_event fev;
1188 if (sel_indexes != NULL) {
1189 free(sel_indexes);
1190 sel_indexes = NULL;
1193 sel_indexes = ecalloc(cpane->dirc, sizeof(size_t));
1194 sel_indexes[0] = cpane->hdir;
1195 cont_vmode = 0;
1196 print_prompt("-- VISUAL --");
1197 tb_present();
1198 while (tb_poll_event(&fev) != 0) {
1199 switch (fev.type) {
1200 case TB_EVENT_KEY:
1201 grabkeys(&fev, vkeys, vkeyslen);
1202 if (cont_vmode == -1)
1203 return;
1204 tb_present();
1205 break;
1210 static void
1211 exit_vmode(const Arg *arg)
1213 refresh_pane(cpane);
1214 add_hi(cpane, cpane->hdir - 1);
1215 cont_vmode = -1;
1218 static void
1219 selup(const Arg *arg)
1221 mv_ver(arg);
1222 print_prompt("-- VISUAL --");
1223 int index = abs(cpane->hdir - sel_indexes[0]);
1225 if (cpane->hdir < sel_indexes[0]) {
1226 sel_indexes[index] = cpane->hdir;
1227 add_hi(cpane, sel_indexes[index]);
1228 } else if (index < cpane->dirc) {
1229 sel_indexes[index + 1] = 0;
1231 if (cpane->dirc >= scrheight ||
1232 cpane->hdir <= 1) { /* rehighlight all if scrolling */
1233 selref();
1237 static void
1238 seldwn(const Arg *arg)
1240 mv_ver(arg);
1241 print_prompt("-- VISUAL --");
1242 int index = abs(cpane->hdir - sel_indexes[0]);
1244 if (cpane->hdir > sel_indexes[0]) {
1245 sel_indexes[index] = cpane->hdir;
1246 add_hi(cpane, sel_indexes[index] - 2);
1247 } else {
1248 sel_indexes[index + 1] = 0;
1250 if (cpane->dirc >= scrheight ||
1251 cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1252 selref();
1256 static void
1257 selall(const Arg *arg)
1259 int i;
1260 for (i = 0; i < cpane->dirc; i++) {
1261 sel_indexes[i] = i + 1;
1263 selref();
1266 static void
1267 selref(void)
1269 int i;
1270 for (i = 0; i < cpane->dirc; i++) {
1271 if (sel_indexes[i] < (scrheight + cpane->firstrow) &&
1272 sel_indexes[i] >
1273 cpane->firstrow) { /* checks if in the frame of the directories */
1274 add_hi(cpane, sel_indexes[i] - 1);
1279 static void
1280 selcalc(void)
1282 int j;
1283 sel_len = 0;
1285 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1286 if (sel_indexes[j] != 0)
1287 sel_len++;
1288 else
1289 break;
1293 static void
1294 free_files(void)
1296 size_t i;
1298 if (sel_files != NULL) {
1299 for (i = 0; i < sel_len; i++) {
1300 free(sel_files[i]);
1301 sel_files[i] = NULL;
1303 free(sel_files);
1304 sel_files = NULL;
1308 static void
1309 init_files(void)
1311 size_t i;
1312 free_files();
1314 selcalc();
1315 sel_files = ecalloc(sel_len, sizeof(char *));
1317 for (i = 0; i < sel_len; i++) {
1318 sel_files[i] = ecalloc(MAX_P, sizeof(char));
1319 strncpy(sel_files[i], cpane->direntr[sel_indexes[i] - 1].name,
1320 MAX_P);
1324 static void
1325 selynk(const Arg *arg)
1327 init_files();
1328 refresh_pane(cpane);
1329 add_hi(cpane, cpane->hdir - 1);
1330 print_status(cprompt, "%zu files are yanked", sel_len);
1331 cont_vmode = -1;
1334 static void
1335 seldel(const Arg *arg)
1337 char *inp_conf;
1339 inp_conf = ecalloc(delconf_len, sizeof(char));
1340 if ((get_usrinput(inp_conf, delconf_len, "delete file (yes) ?") < 0) ||
1341 (strncmp(inp_conf, delconf, delconf_len) != 0)) {
1342 free(inp_conf);
1343 return; /* canceled by user or wrong inp_conf */
1345 free(inp_conf);
1347 init_files();
1349 if (spawn(rm_cmd, rm_cmd_len, sel_files, sel_len, NULL) < 0)
1350 print_error(strerror(errno));
1351 else
1352 print_status(cprompt, "%zu files are deleted", sel_len);
1354 free_files();
1355 cont_vmode = -1;
1358 static void
1359 paste(const Arg *arg)
1361 if (sel_files == NULL) {
1362 print_error("nothing to paste");
1363 return;
1366 if (spawn(cp_cmd, cp_cmd_len, sel_files, sel_len, cpane->dirn) < 0)
1367 print_error(strerror(errno));
1368 else
1369 print_status(cprompt, "%zu files are copied", sel_len);
1371 free_files();
1374 static void
1375 selmv(const Arg *arg)
1377 if (sel_files == NULL) {
1378 print_error("nothing to move");
1379 return;
1382 if (spawn(mv_cmd, mv_cmd_len, sel_files, sel_len, cpane->dirn) < 0)
1383 print_error(strerror(errno));
1384 else
1385 print_status(cprompt, "%zu files are moved", sel_len);
1387 free_files();
1390 static void
1391 rname(const Arg *arg)
1393 if (cpane->dirc < 1)
1394 return;
1395 char new_name[MAX_P];
1396 char *input_name;
1398 input_name = ecalloc(MAX_N, sizeof(char));
1400 if (get_usrinput(input_name, MAX_N, "rename: %s",
1401 basename(CURSOR(cpane).name)) < 0) {
1402 free(input_name);
1403 return;
1406 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1407 free(input_name);
1408 print_error(strerror(errno));
1409 return;
1412 char *rename_cmd[] = { "mv", CURSOR(cpane).name, new_name };
1413 PERROR(spawn(rename_cmd, 3, NULL, 0, NULL) < 0);
1415 free(input_name);
1418 static void
1419 yank(const Arg *arg)
1421 if (cpane->dirc < 1)
1422 return;
1424 free_files();
1425 sel_len = 1;
1426 sel_files = ecalloc(sel_len, sizeof(char *));
1427 sel_files[0] = ecalloc(MAX_P, sizeof(char));
1428 strncpy(sel_files[0], CURSOR(cpane).name, MAX_P);
1429 print_status(cprompt, "1 file is yanked", sel_len);
1432 static void
1433 switch_pane(const Arg *arg)
1435 if (cpane->dirc > 0)
1436 rm_hi(cpane, cpane->hdir - 1);
1437 cpane = &panes[pane_idx ^= 1];
1438 if (cpane->dirc > 0) {
1439 add_hi(cpane, cpane->hdir - 1);
1440 print_info(cpane, NULL);
1441 } else {
1442 clear_status();
1446 static void
1447 quit(const Arg *arg)
1449 if (cont_vmode == -1) { /* check if selection was allocated */
1450 free(sel_indexes);
1451 if (sel_files != NULL)
1452 free_files();
1454 free(panes[Left].direntr);
1455 free(panes[Right].direntr);
1456 fsev_shdn();
1457 tb_shutdown();
1458 exit(EXIT_SUCCESS);
1461 static void
1462 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1464 size_t i;
1466 for (i = 0; i < max_keys; i++) {
1467 if (event->ch != 0) {
1468 if (event->ch == key[i].evkey.ch) {
1469 key[i].func(&key[i].arg);
1470 return;
1472 } else if (event->key != 0) {
1473 if (event->key == key[i].evkey.key) {
1474 key[i].func(&key[i].arg);
1475 return;
1481 void *
1482 read_th(void *arg)
1484 struct timespec tim;
1485 tim.tv_sec = 0;
1486 tim.tv_nsec = 5000000L; /* 0.005 sec */
1488 while (1)
1489 if (read_events() > READEVSZ) {
1490 kill(main_pid, SIGUSR1);
1491 nanosleep(&tim, NULL);
1493 return arg;
1496 static void
1497 start_ev(void)
1499 struct tb_event ev;
1501 while (tb_poll_event(&ev) != 0) {
1502 switch (ev.type) {
1503 case TB_EVENT_KEY:
1504 grabkeys(&ev, nkeys, nkeyslen);
1505 tb_present();
1506 break;
1507 case TB_EVENT_RESIZE:
1508 t_resize();
1509 break;
1510 default:
1511 break;
1514 tb_shutdown();
1517 static void
1518 refresh_pane(Pane *pane)
1520 size_t y, dyn_max, start_from;
1521 int width;
1522 width = (twidth / 2) - 4;
1523 Cpair col;
1525 y = 1;
1526 start_from = pane->firstrow;
1527 dyn_max = MIN(pane->dirc, (scrheight - 1) + pane->firstrow);
1529 /* print each entry in directory */
1530 while (start_from < dyn_max) {
1531 get_hicol(&col, pane->direntr[start_from].mode);
1532 print_row(pane, start_from, col);
1533 start_from++;
1534 y++;
1537 if (pane->dirc > 0)
1538 print_info(pane, NULL);
1539 else
1540 clear_status();
1542 /* print current directory title */
1543 pane->dircol.fg |= TB_BOLD;
1544 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", width, pane->dirn);
1547 static void
1548 set_direntr(Pane *pane, struct dirent *entry, DIR *dir, char *filter)
1550 int i;
1551 char *tmpfull;
1552 struct stat status;
1554 #define ADD_ENTRY \
1555 tmpfull = get_fullpath(pane->dirn, entry->d_name); \
1556 strncpy(pane->direntr[i].name, tmpfull, MAX_N); \
1557 if (lstat(tmpfull, &status) == 0) { \
1558 pane->direntr[i].size = status.st_size; \
1559 pane->direntr[i].mode = status.st_mode; \
1560 pane->direntr[i].group = status.st_gid; \
1561 pane->direntr[i].user = status.st_uid; \
1562 pane->direntr[i].dt = status.st_mtime; \
1564 i++; \
1565 free(tmpfull);
1567 i = 0;
1568 pane->direntr =
1569 erealloc(pane->direntr, (10 + pane->dirc) * sizeof(Entry));
1570 while ((entry = readdir(dir)) != 0) {
1571 if (show_dotfiles == 1) {
1572 if (entry->d_name[0] == '.' &&
1573 (entry->d_name[1] == '\0' ||
1574 entry->d_name[1] == '.'))
1575 continue;
1576 } else {
1577 if (entry->d_name[0] == '.')
1578 continue;
1581 if (filter == NULL) {
1582 ADD_ENTRY
1583 } else if (filter != NULL) {
1584 if (strcasestr(entry->d_name, filter) != NULL) {
1585 ADD_ENTRY
1590 pane->dirc = i;
1593 static int
1594 listdir(Pane *pane)
1596 DIR *dir;
1597 struct dirent *entry;
1598 int width;
1599 int filtercount = 0;
1600 size_t oldc = pane->dirc;
1602 width = (twidth / 2) - 4;
1603 pane->dirc = 0;
1605 dir = opendir(pane->dirn);
1606 if (dir == NULL)
1607 return -1;
1609 /* get content and filter sum */
1610 while ((entry = readdir(dir)) != 0) {
1611 if (pane->filter != NULL) {
1612 if (strcasestr(entry->d_name, pane->filter) != NULL)
1613 filtercount++;
1614 } else { /* no filter */
1615 pane->dirc++;
1619 if (pane->filter == NULL) {
1620 clear_pane(pane);
1621 pane->dirc -= 2;
1624 if (pane->filter != NULL) {
1625 if (filtercount > 0) {
1626 pane->dirc = filtercount;
1627 clear_pane(pane);
1628 pane->hdir = 1;
1629 } else if (filtercount == 0) {
1630 if (closedir(dir) < 0)
1631 return -1;
1632 pane->dirc = oldc;
1633 return -1;
1637 /* print current directory title */
1638 pane->dircol.fg |= TB_BOLD;
1639 printf_tb(pane->dirx, 0, pane->dircol, " %.*s ", width, pane->dirn);
1641 if (pane->filter == NULL) /* dont't watch when filtering */
1642 if (addwatch(pane) < 0)
1643 print_error("can't add watch");
1645 /* empty directory */
1646 if (pane->dirc == 0) {
1647 clear_status();
1648 if (closedir(dir) < 0)
1649 return -1;
1650 return 0;
1653 rewinddir(dir); /* reset position */
1654 set_direntr(
1655 pane, entry, dir, pane->filter); /* create array of entries */
1656 qsort(pane->direntr, pane->dirc, sizeof(Entry), sort_name);
1657 refresh_pane(pane);
1659 if (pane->hdir > pane->dirc)
1660 pane->hdir = pane->dirc;
1662 if (pane == cpane && pane->dirc > 0)
1663 add_hi(pane, pane->hdir - 1);
1665 if (closedir(dir) < 0)
1666 return -1;
1667 return 0;
1670 static void
1671 t_resize(void)
1673 tb_clear();
1674 draw_frame();
1675 panes[Right].dirx = (twidth / 2) + 2;
1676 refresh_pane(&panes[Left]);
1677 refresh_pane(&panes[Right]);
1678 if (cpane->dirc > 0)
1679 add_hi(cpane, cpane->hdir - 1);
1680 tb_present();
1683 static void
1684 get_editor(void)
1686 editor[0] = getenv("EDITOR");
1687 editor[1] = NULL;
1689 if (editor[0] == NULL)
1690 editor[0] = fed;
1693 static void
1694 set_panes(void)
1696 char *home;
1697 char cwd[MAX_P];
1699 home = getenv("HOME");
1700 if (home == NULL)
1701 home = "/";
1702 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1703 strncpy(cwd, home, MAX_P);
1705 pane_idx = Left; /* cursor pane */
1706 cpane = &panes[pane_idx];
1708 panes[Left].ex = (twidth / 2) - 1;
1709 panes[Left].pane_id = 0;
1710 panes[Left].dirx = 2;
1711 panes[Left].dircol = cpanell;
1712 panes[Left].firstrow = 0;
1713 panes[Left].direntr = ecalloc(0, sizeof(Entry));
1714 strncpy(panes[Left].dirn, cwd, MAX_P);
1715 panes[Left].hdir = 1;
1716 panes[Left].inotify_wd = -1;
1717 panes[Left].parent_row = 1;
1719 panes[Right].ex = twidth - 1;
1720 panes[Right].pane_id = 1;
1721 panes[Right].dirx = (twidth / 2) + 2;
1722 panes[Right].dircol = cpanelr;
1723 panes[Right].firstrow = 0;
1724 panes[Right].direntr = ecalloc(0, sizeof(Entry));
1725 strncpy(panes[Right].dirn, home, MAX_P);
1726 panes[Right].hdir = 1;
1727 panes[Right].inotify_wd = -1;
1728 panes[Right].parent_row = 1;
1731 static void
1732 draw_frame(void)
1734 int i;
1735 theight = tb_height();
1736 twidth = tb_width();
1737 scrheight = theight - 2;
1739 /* 2 horizontal lines */
1740 for (i = 1; i < twidth - 1; ++i) {
1741 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1742 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1745 /* 4 vertical lines */
1746 for (i = 1; i < theight - 1; ++i) {
1747 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1748 tb_change_cell(
1749 (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
1750 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1751 cframe.bg);
1752 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1755 /* 4 corners */
1756 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1757 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1758 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1759 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1761 /* 2 middel top and bottom */
1762 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1763 tb_change_cell(
1764 (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1767 void
1768 th_handler(int num)
1770 (void)num;
1772 PERROR(listdir(&panes[Left]));
1773 PERROR(listdir(&panes[Right]));
1774 if (cpane->dirc > 0)
1775 add_hi(cpane, cpane->hdir - 1);
1776 tb_present();
1779 static int
1780 start_signal(void)
1782 struct sigaction sa;
1784 main_pid = getpid();
1785 sa.sa_handler = th_handler;
1786 sigemptyset(&sa.sa_mask);
1787 sa.sa_flags = SA_RESTART;
1788 return sigaction(SIGUSR1, &sa, NULL);
1791 static void
1792 start(void)
1794 FAIL_IF(tb_init() != 0, "tb_init()");
1795 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1796 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1797 die("output error");
1798 draw_frame();
1799 set_panes();
1800 get_editor();
1801 PERROR(start_signal() < 0);
1802 PERROR(start_signal() < 0);
1803 PERROR(fsev_init() < 0);
1804 PERROR(listdir(&panes[Left]) < 0);
1805 PERROR(listdir(&panes[Right]) < 0);
1806 tb_present();
1808 pthread_create(&fsev_thread, NULL, read_th, NULL);
1809 start_ev();
1813 main(int argc, char *argv[])
1815 #if defined(__OpenBSD__)
1816 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1817 NULL) == -1)
1818 die("pledge");
1819 #endif /* __OpenBSD__ */
1820 if (argc == 1)
1821 start();
1822 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1823 die("sfm-" VERSION);
1824 else
1825 die("usage: sfm [-v]");
1826 return 0;