[ref] spawn use memcpy instead of for loops
[sfm.git] / sfm.c
blob610c820ef65a89ec29af4ed61647d7259a3da23d
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 cpane->direntr[cpane->hdir - 1]
53 /* enums */
54 enum { AddHi, NoHi }; /* add highlight in listdir */
56 /* typedef */
57 typedef struct {
58 char name[MAX_N];
59 gid_t group;
60 mode_t mode;
61 off_t size;
62 time_t dt;
63 uid_t user;
64 } Entry;
66 typedef struct {
67 uint16_t fg;
68 uint16_t bg;
69 } Cpair;
71 typedef struct {
72 int pane_id;
73 char dirn[MAX_P]; // dir name cwd
74 char *filter;
75 Entry *direntr; // dir entries
76 int dirx; // pane cwd x pos
77 int dirc; // dir entries sum
78 int hdir; // highlighted dir
79 int firstrow;
80 int parent_firstrow;
81 int parent_row; // FIX
82 Cpair dircol;
83 int inotify_wd;
84 int event_fd;
85 } Pane;
87 typedef struct {
88 uint32_t ch;
89 char path[MAX_P];
90 } Bookmark;
92 typedef struct {
93 const char **ext;
94 size_t exlen;
95 const void *v;
96 size_t vlen;
97 } Rule;
99 typedef union {
100 uint16_t key; /* one of the TB_KEY_* constants */
101 uint32_t ch; /* unicode character */
102 } Evkey;
104 typedef struct {
105 const Evkey evkey;
106 void (*func)(void);
107 } Key;
109 /* function declarations */
110 static void print_tb(const char *, int, int, uint16_t, uint16_t);
111 static void printf_tb(int, int, Cpair, const char *, ...);
112 static void print_status(Cpair, const char *, ...);
113 static void print_xstatus(char, int);
114 static void print_error(char *);
115 static void print_prompt(char *);
116 static void print_info(char *);
117 static void print_row(Pane *, size_t, Cpair);
118 static void clear(int, int, int, uint16_t);
119 static void clear_status(void);
120 static void clear_pane(void);
121 static void add_hi(Pane *, size_t);
122 static void rm_hi(Pane *, size_t);
123 static int check_dir(char *);
124 static mode_t chech_execf(mode_t);
125 static int sort_name(const void *const, const void *const);
126 static void get_dirp(char *);
127 static char *get_ext(char *);
128 static int get_fdt(char *, time_t);
129 static char *get_fgrp(gid_t);
130 static char *get_fperm(mode_t);
131 static char *get_fsize(off_t);
132 static char *get_fullpath(char *, char *);
133 static char *get_fusr(uid_t);
134 static void get_dirsize(char *, off_t *);
135 static void get_hicol(Cpair *, mode_t);
136 static int delent(char *);
137 static void calcdir(void);
138 static void crnd(void);
139 static void crnf(void);
140 static void delfd(void);
141 static void mvbk(void);
142 static void mvbtm(void);
143 static void mvdwn(void);
144 static void mvdwns(void);
145 static void mvfwd(void);
146 static void mvmid(void);
147 static void mvtop(void);
148 static void mvup(void);
149 static void mvups(void);
150 static void scrdwn(void);
151 static void scrdwns(void);
152 static void scrup(void);
153 static void scrups(void);
154 static int get_usrinput(char *, size_t, const char *, ...);
155 static int frules(char *);
156 static int spawn(const void *, size_t, const void *, size_t, char *);
157 static int opnf(char *);
158 static int fsev_init(void);
159 static int addwatch(void);
160 static int read_events(void);
161 static void rmwatch(Pane *);
162 static void fsev_shdn(void);
163 static ssize_t findbm(uint32_t);
164 static void start_filter(void);
165 static void start_vmode(void);
166 static void exit_vmode(void);
167 static void selup(void);
168 static void seldwn(void);
169 static void selall(void);
170 static void selref(void);
171 static void selynk(void);
172 static void selcalc(void);
173 static void paste(void);
174 static void selmv(void);
175 static void seldel(void);
176 static void init_files(void);
177 static void free_files(void);
178 static void yank(void);
179 static void rname(void);
180 static void switch_pane(void);
181 static void quit(void);
182 static void grabkeys(struct tb_event *, Key *, size_t);
183 static void *read_th(void *arg);
184 static void start_ev(void);
185 static void refresh_pane(void);
186 static void set_direntr(struct dirent *, DIR *, char *);
187 static int listdir(int);
188 static void t_resize(void);
189 static void set_panes(void);
190 static void draw_frame(void);
191 static void start(void);
193 /* global variables */
194 static pthread_t fsev_thread;
195 static Pane pane_r, pane_l, *cpane;
196 static char *editor[2];
197 static char fed[] = "vi";
198 static int theight, twidth, scrheight;
199 static size_t selection_size = 0;
200 static char *yank_file[MAX_P];
201 static int *selection;
202 static int cont_vmode = 0;
203 static char **selected_files;
204 pid_t main_pid;
205 #if defined _SYS_INOTIFY_H
206 #define READEVSZ 16
207 static int inotify_fd;
208 #elif defined _SYS_EVENT_H_
209 #define READEVSZ 0
210 static int kq;
211 struct kevent evlist[2]; /* events we want to monitor */
212 struct kevent chlist[2]; /* events that were triggered */
213 static struct timespec gtimeout;
214 #endif
215 #if defined(__linux__) || defined(__FreeBSD__)
216 #define OFF_T "%ld"
217 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
218 #define OFF_T "%lld"
219 #endif
221 /* configuration, allows nested code to access above variables */
222 #include "config.h"
224 /* function implementations */
225 static void
226 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
228 while (*str != '\0') {
229 uint32_t uni = 0;
230 str += tb_utf8_char_to_unicode(&uni, str);
231 tb_change_cell(x, y, uni, fg, bg);
232 x++;
236 static void
237 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
239 char buf[MAX_LINE];
240 va_list vl;
241 va_start(vl, fmt);
242 (void)vsnprintf(buf, MAX_LINE, fmt, vl);
243 va_end(vl);
244 print_tb(buf, x, y, col.fg, col.bg);
247 static void
248 print_status(Cpair col, const char *fmt, ...)
250 char buf[MAX_STATUS];
251 va_list vl;
252 va_start(vl, fmt);
253 (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
254 va_end(vl);
255 clear_status();
256 print_tb(buf, 1, theight - 1, col.fg, col.bg);
259 static void
260 print_xstatus(char c, int x)
262 uint32_t uni = 0;
263 (void)tb_utf8_char_to_unicode(&uni, &c);
264 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
267 static void
268 print_error(char *errmsg)
270 print_status(cerr, errmsg);
273 static void
274 print_prompt(char *prompt)
276 print_status(cprompt, prompt);
279 static void
280 print_info(char *dirsize)
282 char *sz, *ur, *gr, *dt, *prm;
284 dt = ecalloc(MAX_DTF, sizeof(char));
286 prm = get_fperm(CURSOR.mode);
287 ur = get_fusr(CURSOR.user);
288 gr = get_fgrp(CURSOR.group);
290 if (get_fdt(dt, CURSOR.dt) < 0)
291 *dt = '\0';
293 if (S_ISREG(CURSOR.mode)) {
294 sz = get_fsize(CURSOR.size);
295 } else {
296 if (dirsize == NULL) {
297 sz = ecalloc(1, sizeof(char));
298 *sz = '\0';
299 } else {
300 sz = dirsize;
304 print_status(cstatus, "%02d/%02d %s %s:%s %s %s", cpane->hdir,
305 cpane->dirc, prm, ur, gr, dt, sz);
307 free(prm);
308 free(ur);
309 free(gr);
310 free(dt);
311 free(sz);
314 static void
315 print_row(Pane *pane, size_t entpos, Cpair col)
317 int x, y;
318 char *result;
319 char buf[MAX_P];
320 char lnk_full[MAX_P];
321 int width;
323 width = (twidth / 2) - 4;
324 result = basename(pane->direntr[entpos].name);
325 x = pane->dirx;
326 y = entpos - cpane->firstrow + 1;
328 if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
329 if (realpath(pane->direntr[entpos].name, buf) != NULL) {
330 (void)snprintf(
331 lnk_full, MAX_N, "%s -> %s", result, buf);
332 result = lnk_full;
336 printf_tb(x, y, col, "%*.*s", ~width, width, result);
339 static void
340 clear(int sx, int ex, int y, uint16_t bg)
342 /* clear line from to */
343 /* x = line number vertical */
344 /* y = column number horizontal */
345 int i;
346 for (i = sx; i < ex; i++) {
347 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
351 static void
352 clear_status(void)
354 clear(1, twidth - 1, theight - 1, cstatus.bg);
357 static void
358 clear_pane(void)
360 int i, ex, y;
361 y = 0, i = 0, ex = 0;
363 if (cpane->pane_id == pane_l.pane_id)
364 ex = (twidth / 2) - 1;
365 else if (cpane->pane_id == pane_r.pane_id)
366 ex = twidth - 1;
368 while (i < scrheight) {
369 clear(cpane->dirx, ex, y, TB_DEFAULT);
370 i++;
371 y++;
374 /* draw top line */
375 for (y = cpane->dirx; y < ex; ++y) {
376 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
380 static void
381 add_hi(Pane *pane, size_t entpos)
383 Cpair col;
384 get_hicol(&col, pane->direntr[entpos].mode);
385 col.fg |= TB_REVERSE | TB_BOLD;
386 col.bg |= TB_REVERSE;
387 print_row(pane, entpos, col);
390 static void
391 rm_hi(Pane *pane, size_t entpos)
393 Cpair col;
394 get_hicol(&col, pane->direntr[entpos].mode);
395 print_row(pane, entpos, col);
398 static int
399 check_dir(char *path)
401 DIR *dir;
402 dir = opendir(path);
404 if (dir == NULL) {
405 if (errno == ENOTDIR) {
406 return 1;
407 } else {
408 return -1;
412 if (closedir(dir) < 0)
413 return -1;
415 return 0;
418 static mode_t
419 chech_execf(mode_t mode)
421 if (S_ISREG(mode))
422 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
423 return 0;
426 static int
427 sort_name(const void *const A, const void *const B)
429 int result;
430 mode_t data1 = (*(Entry *)A).mode;
431 mode_t data2 = (*(Entry *)B).mode;
433 if (data1 < data2) {
434 return -1;
435 } else if (data1 == data2) {
436 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
437 return result;
438 } else {
439 return 1;
443 static void
444 get_dirp(char *cdir)
446 int counter, len, i;
448 counter = 0;
449 len = strnlen(cdir, MAX_P);
450 if (len == 1)
451 return;
453 for (i = len - 1; i > 1; i--) {
454 if (cdir[i] == '/')
455 break;
456 else
457 counter++;
460 cdir[len - counter - 1] = '\0';
463 static char *
464 get_ext(char *str)
466 char *ext;
467 char dot;
468 size_t counter, len, i;
470 dot = '.';
471 counter = 0;
472 len = strnlen(str, MAX_N);
474 for (i = len - 1; i > 0; i--) {
475 if (str[i] == dot) {
476 break;
477 } else {
478 counter++;
482 ext = ecalloc(MAX_EXT + 1, sizeof(char));
483 strncpy(ext, &str[len - counter], MAX_EXT);
484 ext[MAX_EXT] = '\0';
485 return ext;
488 static int
489 get_fdt(char *result, time_t status)
491 struct tm lt;
492 localtime_r(&status, &lt);
493 return strftime(result, MAX_DTF, dtfmt, &lt);
496 static char *
497 get_fgrp(gid_t status)
499 char *result;
500 struct group *gr;
502 result = ecalloc(MAX_GRPN, sizeof(char));
503 gr = getgrgid(status);
504 if (gr == NULL)
505 (void)snprintf(result, MAX_GRPN, "%u", status);
506 else
507 strncpy(result, gr->gr_name, MAX_GRPN);
509 result[MAX_GRPN - 1] = '\0';
510 return result;
513 static char *
514 get_fperm(mode_t mode)
516 char *buf;
517 size_t i;
519 const char chars[] = "rwxrwxrwx";
520 buf = ecalloc(11, sizeof(char));
522 if (S_ISDIR(mode))
523 buf[0] = 'd';
524 else if (S_ISREG(mode))
525 buf[0] = '-';
526 else if (S_ISLNK(mode))
527 buf[0] = 'l';
528 else if (S_ISBLK(mode))
529 buf[0] = 'b';
530 else if (S_ISCHR(mode))
531 buf[0] = 'c';
532 else if (S_ISFIFO(mode))
533 buf[0] = 'p';
534 else if (S_ISSOCK(mode))
535 buf[0] = 's';
536 else
537 buf[0] = '?';
539 for (i = 1; i < 10; i++) {
540 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
542 buf[10] = '\0';
544 return buf;
547 static char *
548 get_fsize(off_t size)
550 char *result; /* need to be freed */
551 char unit;
552 int result_len;
553 int counter;
555 counter = 0;
556 result_len = 6; /* 9999X/0 */
557 result = ecalloc(result_len, sizeof(char));
559 while (size >= 1000) {
560 size /= 1024;
561 ++counter;
564 switch (counter) {
565 case 0:
566 unit = 'B';
567 break;
568 case 1:
569 unit = 'K';
570 break;
571 case 2:
572 unit = 'M';
573 break;
574 case 3:
575 unit = 'G';
576 break;
577 case 4:
578 unit = 'T';
579 break;
580 default:
581 unit = '?';
584 if (snprintf(result, result_len, OFF_T "%c", size, unit) < 0)
585 strncat(result, "???", result_len);
587 return result;
590 static char *
591 get_fullpath(char *first, char *second)
593 char *full_path;
595 full_path = ecalloc(MAX_P, sizeof(char));
597 if (strncmp(first, "/", MAX_P) == 0)
598 (void)snprintf(full_path, MAX_P, "/%s", second);
599 else
600 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
602 return full_path;
605 static char *
606 get_fusr(uid_t status)
608 char *result;
609 struct passwd *pw;
611 result = ecalloc(MAX_USRN, sizeof(char));
612 pw = getpwuid(status);
613 if (pw == NULL)
614 (void)snprintf(result, MAX_USRN, "%u", status);
615 else
616 strncpy(result, pw->pw_name, MAX_USRN);
618 result[MAX_USRN - 1] = '\0';
619 return result;
622 static void
623 get_dirsize(char *fullpath, off_t *fullsize)
625 DIR *dir;
626 char *ent_full;
627 mode_t mode;
628 struct dirent *entry;
629 struct stat status;
631 dir = opendir(fullpath);
632 if (dir == NULL) {
633 return;
636 while ((entry = readdir(dir)) != 0) {
637 if ((strncmp(entry->d_name, ".", 2) == 0 ||
638 strncmp(entry->d_name, "..", 3) == 0))
639 continue;
641 ent_full = get_fullpath(fullpath, entry->d_name);
642 if (lstat(ent_full, &status) == 0) {
643 mode = status.st_mode;
644 if (S_ISDIR(mode)) {
645 get_dirsize(ent_full, fullsize);
646 free(ent_full);
647 } else {
648 *fullsize += status.st_size;
649 free(ent_full);
654 closedir(dir);
655 clear_status();
658 static void
659 get_hicol(Cpair *col, mode_t mode)
661 *col = cfile;
662 if (S_ISDIR(mode))
663 *col = cdir;
664 else if (S_ISLNK(mode))
665 *col = cother;
666 else if (chech_execf(mode) > 0)
667 *col = cexec;
670 static int
671 delent(char *fullpath)
673 char *inp_conf;
674 int conf_len = 4;
675 char conf[] = "yes";
677 inp_conf = ecalloc(conf_len, sizeof(char));
678 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
679 (strncmp(inp_conf, conf, conf_len) != 0)) {
680 free(inp_conf);
681 return 1; /* canceled by user or wrong inp_conf */
683 free(inp_conf);
685 return spawn(rm_cmd, rm_cmd_len, yank_file, 1, fullpath);
688 static void
689 calcdir(void)
691 if (!S_ISDIR(CURSOR.mode))
692 return;
694 off_t *fullsize;
695 char *csize;
697 fullsize = ecalloc(1, sizeof(off_t));
698 get_dirsize(CURSOR.name, fullsize);
699 csize = get_fsize(*fullsize);
701 CURSOR.size = *fullsize;
702 print_info(csize);
703 free(fullsize);
706 static void
707 crnd(void)
709 char *user_input, *path;
711 user_input = ecalloc(MAX_USRI, sizeof(char));
712 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
713 free(user_input);
714 return;
717 path = ecalloc(MAX_P, sizeof(char));
718 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
719 free(user_input);
720 free(path);
721 return;
724 if (mkdir(path, ndir_perm) < 0)
725 print_error(strerror(errno));
727 free(user_input);
728 free(path);
731 static void
732 crnf(void)
734 char *user_input, *path;
735 int rf;
737 user_input = ecalloc(MAX_USRI, sizeof(char));
738 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
739 free(user_input);
740 return;
743 path = ecalloc(MAX_P, sizeof(char));
744 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
745 free(user_input);
746 free(path);
747 return;
750 rf = open(path, O_CREAT | O_EXCL, nf_perm);
752 if (rf < 0)
753 print_error(strerror(errno));
754 else if (close(rf) < 0)
755 print_error(strerror(errno));
757 free(user_input);
758 free(path);
761 static void
762 delfd(void)
764 switch (delent(CURSOR.name)) {
765 case -1:
766 print_error(strerror(errno));
767 break;
768 case 0:
769 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
770 cpane->hdir--;
771 break;
775 static void
776 mvbk(void)
778 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
779 return;
782 get_dirp(cpane->dirn);
783 if (check_dir(cpane->dirn) < 0) {
784 print_error(strerror(errno));
785 return;
788 rmwatch(cpane);
789 cpane->firstrow = cpane->parent_firstrow;
790 cpane->hdir = cpane->parent_row;
791 if (listdir(AddHi) < 0)
792 print_error(strerror(errno));
793 cpane->parent_firstrow = 0;
794 cpane->parent_row = 1;
797 static void
798 mvbtm(void)
800 if (cpane->dirc < 1)
801 return;
802 if (cpane->dirc > scrheight) {
803 rm_hi(cpane, cpane->hdir - 1);
804 cpane->hdir = cpane->dirc;
805 cpane->firstrow = cpane->dirc - scrheight + 1;
806 refresh_pane();
807 add_hi(cpane, cpane->hdir - 1);
808 } else {
809 rm_hi(cpane, cpane->hdir - 1);
810 cpane->hdir = cpane->dirc;
811 add_hi(cpane, cpane->hdir - 1);
813 print_info(NULL);
816 static void
817 mvdwn(void)
819 if (cpane->dirc < 1)
820 return;
821 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
822 rm_hi(cpane, cpane->hdir - 1);
823 cpane->hdir++;
824 add_hi(cpane, cpane->hdir - 1);
825 } else {
826 mvdwns(); /* scroll */
828 print_info(NULL);
831 static void
832 mvdwns(void)
834 int real;
835 real = cpane->hdir - 1 - cpane->firstrow;
837 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
838 cpane->firstrow++;
839 rm_hi(cpane, cpane->hdir - 1);
840 cpane->hdir++;
841 refresh_pane();
842 add_hi(cpane, cpane->hdir - 1);
843 } else if (cpane->hdir < cpane->dirc) {
844 rm_hi(cpane, cpane->hdir - 1);
845 cpane->hdir++;
846 add_hi(cpane, cpane->hdir - 1);
850 static void
851 mvfwd(void)
853 rmwatch(cpane);
854 if (cpane->dirc < 1)
855 return;
856 int s;
858 switch (check_dir(CURSOR.name)) {
859 case 0:
860 strncpy(cpane->dirn, CURSOR.name, MAX_P);
861 cpane->parent_row = cpane->hdir;
862 cpane->parent_firstrow = cpane->firstrow;
863 cpane->hdir = 1;
864 cpane->firstrow = 0;
865 if (listdir(AddHi) < 0)
866 print_error(strerror(errno));
867 break;
868 case 1: /* not a directory open file */
869 tb_shutdown();
870 s = opnf(CURSOR.name);
871 if (tb_init() != 0)
872 die("tb_init");
873 t_resize();
874 if (s < 0)
875 print_error("process failed non-zero exit");
876 break;
877 case -1: /* failed to open directory */
878 print_error(strerror(errno));
882 static void
883 mvmid(void)
885 if (cpane->dirc < 1)
886 return;
887 rm_hi(cpane, cpane->hdir - 1);
888 if (cpane->dirc < scrheight / 2)
889 cpane->hdir = (cpane->dirc + 1) / 2;
890 else
891 cpane->hdir = (scrheight / 2) + cpane->firstrow;
892 add_hi(cpane, cpane->hdir - 1);
893 print_info(NULL);
896 static void
897 mvtop(void)
899 if (cpane->dirc < 1)
900 return;
901 if (cpane->dirc > scrheight) {
902 rm_hi(cpane, cpane->hdir - 1);
903 cpane->hdir = 1;
904 cpane->firstrow = 0;
905 refresh_pane();
906 add_hi(cpane, cpane->hdir - 1);
907 } else {
908 rm_hi(cpane, cpane->hdir - 1);
909 cpane->hdir = 1;
910 add_hi(cpane, cpane->hdir - 1);
911 print_info(NULL);
915 static void
916 mvup(void)
918 if (cpane->dirc < 1)
919 return;
920 if (cpane->dirc < scrheight && cpane->hdir > 1) {
921 rm_hi(cpane, cpane->hdir - 1);
922 cpane->hdir--;
923 add_hi(cpane, cpane->hdir - 1);
924 } else {
925 mvups(); /* scroll */
927 print_info(NULL);
930 static void
931 mvups(void)
933 size_t real;
934 real = cpane->hdir - 1 - cpane->firstrow;
936 if (cpane->firstrow > 0 && real < 1 + scrsp) {
937 cpane->firstrow--;
938 rm_hi(cpane, cpane->hdir - 1);
939 cpane->hdir--;
940 refresh_pane();
941 add_hi(cpane, cpane->hdir - 1);
942 } else if (cpane->hdir > 1) {
943 rm_hi(cpane, cpane->hdir - 1);
944 cpane->hdir--;
945 add_hi(cpane, cpane->hdir - 1);
949 static void
950 scrdwn(void)
952 if (cpane->dirc < 1)
953 return;
954 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
955 if (cpane->hdir < cpane->dirc - scrmv) {
956 rm_hi(cpane, cpane->hdir - 1);
957 cpane->hdir += scrmv;
958 add_hi(cpane, cpane->hdir - 1);
959 } else {
960 mvbtm();
962 } else {
963 scrdwns();
965 print_info(NULL);
968 static void
969 scrdwns(void)
971 int real, dynmv;
973 real = cpane->hdir - cpane->firstrow;
974 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
976 if (real + scrmv + 1 > scrheight &&
977 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
978 cpane->firstrow += dynmv;
979 rm_hi(cpane, cpane->hdir - 1);
980 cpane->hdir += scrmv;
981 refresh_pane();
982 add_hi(cpane, cpane->hdir - 1);
983 } else {
984 if (cpane->hdir < cpane->dirc - scrmv - 1) {
985 rm_hi(cpane, cpane->hdir - 1);
986 cpane->hdir += scrmv;
987 add_hi(cpane, cpane->hdir - 1);
988 } else {
989 mvbtm();
994 static void
995 scrup(void)
997 if (cpane->dirc < 1)
998 return;
999 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1000 if (cpane->hdir > scrmv) {
1001 rm_hi(cpane, cpane->hdir - 1);
1002 cpane->hdir = cpane->hdir - scrmv;
1003 add_hi(cpane, cpane->hdir - 1);
1004 print_info(NULL);
1005 } else {
1006 mvtop();
1008 } else {
1009 scrups();
1013 static void
1014 scrups(void)
1016 int real, dynmv;
1017 real = cpane->hdir - cpane->firstrow;
1018 dynmv = MIN(cpane->firstrow, scrmv);
1020 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1021 cpane->firstrow -= dynmv;
1022 rm_hi(cpane, cpane->hdir - 1);
1023 cpane->hdir -= scrmv;
1024 refresh_pane();
1025 add_hi(cpane, cpane->hdir - 1);
1026 } else {
1027 if (cpane->hdir > scrmv + 1) {
1028 rm_hi(cpane, cpane->hdir - 1);
1029 cpane->hdir -= scrmv;
1030 add_hi(cpane, cpane->hdir - 1);
1031 } else {
1032 mvtop();
1037 static int
1038 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1040 int height = tb_height();
1041 size_t startat;
1042 struct tb_event fev;
1043 size_t counter = (size_t)1;
1044 char empty = ' ';
1045 int x = 0;
1046 int name_size = 0;
1047 char buf[256];
1049 clear_status();
1051 va_list vl;
1052 Cpair col;
1053 col = cprompt;
1054 va_start(vl, fmt);
1055 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1056 va_end(vl);
1057 print_tb(buf, 1, height - 1, col.fg, col.bg);
1058 startat = name_size + 1;
1059 tb_set_cursor((int)(startat + 1), height - 1);
1060 tb_present();
1062 while (tb_poll_event(&fev) != 0) {
1063 switch (fev.type) {
1064 case TB_EVENT_KEY:
1065 if (fev.key == TB_KEY_ESC) {
1066 tb_set_cursor(-1, -1);
1067 clear_status();
1068 return -1;
1071 if (fev.key == TB_KEY_BACKSPACE ||
1072 fev.key == TB_KEY_BACKSPACE2) {
1073 if (BETWEEN(counter, 2, sout)) {
1074 out[x - 1] = '\0';
1075 counter--;
1076 x--;
1077 print_xstatus(empty, startat + counter);
1078 tb_set_cursor(
1079 startat + counter, theight - 1);
1082 } else if (fev.key == TB_KEY_ENTER) {
1083 tb_set_cursor(-1, -1);
1084 out[counter - 1] = '\0';
1085 return 0;
1087 } else {
1088 if (counter < sout) {
1089 print_xstatus((char)fev.ch,
1090 (startat + counter));
1091 out[x] = (char)fev.ch;
1092 tb_set_cursor((startat + counter + 1),
1093 theight - 1);
1094 counter++;
1095 x++;
1099 tb_present();
1100 break;
1102 default:
1103 return -1;
1107 return -1;
1110 static int
1111 frules(char *ex)
1113 size_t c, d;
1115 for (c = 0; c < LEN(rules); c++)
1116 for (d = 0; d < rules[c].exlen; d++)
1117 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1118 return c;
1119 return -1;
1122 static int
1123 spawn(const void *com_argv, size_t com_argc, const void *f_argv, size_t f_argc,
1124 char *fn)
1126 int ws;
1127 size_t argc;
1128 pid_t pid, r;
1130 argc = com_argc + f_argc + 2;
1131 char *argv[argc];
1133 memcpy(argv, com_argv, com_argc * sizeof(char *)); /* command */
1134 memcpy(&argv[com_argc], f_argv, f_argc * sizeof(char *)); /* files */
1136 argv[argc - 2] = fn;
1137 argv[argc - 1] = NULL;
1139 pid = fork();
1140 switch (pid) {
1141 case -1:
1142 return -1;
1143 case 0:
1144 execvp(argv[0], argv);
1145 exit(EXIT_SUCCESS);
1146 default:
1147 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1148 continue;
1149 if (r == -1)
1150 return -1;
1151 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1152 return -1;
1154 return 0;
1157 static int
1158 opnf(char *fn)
1160 char *ex;
1161 int c;
1163 ex = get_ext(fn);
1164 c = frules(ex);
1165 free(ex);
1167 if (c < 0) /* extension not found open in editor */
1168 return spawn(editor, 1, NULL, 0, fn);
1169 else
1170 return spawn((char **)rules[c].v, rules[c].vlen, NULL, 0, fn);
1173 static int
1174 fsev_init(void)
1176 #if defined _SYS_INOTIFY_H
1177 inotify_fd = inotify_init();
1178 if (inotify_fd < 0)
1179 return -1;
1180 #elif defined _SYS_EVENT_H_
1181 gtimeout.tv_sec = 1;
1182 kq = kqueue();
1183 if (kq < 0)
1184 return -1;
1185 #endif
1186 return 0;
1189 static int
1190 addwatch(void)
1192 #if defined _SYS_INOTIFY_H
1193 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1194 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1195 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1196 #elif defined _SYS_EVENT_H_
1197 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1198 if (cpane->event_fd < 0)
1199 return cpane->event_fd;
1200 EV_SET(&evlist[cpane->pane_id], cpane->event_fd, EVFILT_VNODE,
1201 EV_ADD | EV_CLEAR,
1202 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK | NOTE_RENAME |
1203 NOTE_REVOKE | NOTE_WRITE,
1204 0, NULL);
1205 return 0;
1206 #endif
1209 static int
1210 read_events(void)
1212 #if defined _SYS_INOTIFY_H
1213 char *p;
1214 ssize_t r;
1215 struct inotify_event *event;
1216 const size_t events = 32;
1217 const size_t evbuflen =
1218 events * (sizeof(struct inotify_event) + MAX_N + 1);
1219 char buf[evbuflen];
1221 if (cpane->inotify_wd < 0)
1222 return -1;
1223 r = read(inotify_fd, buf, evbuflen);
1224 if (r <= 0)
1225 return r;
1227 for (p = buf; p < buf + r;) {
1228 event = (struct inotify_event *)p;
1229 if (!event->wd)
1230 break;
1231 if (event->mask) {
1232 return r;
1235 p += sizeof(struct inotify_event) + event->len;
1237 #elif defined _SYS_EVENT_H_
1238 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1239 #endif
1240 return -1;
1243 static void
1244 rmwatch(Pane *pane)
1246 #if defined _SYS_INOTIFY_H
1247 if (pane->inotify_wd >= 0)
1248 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1249 #elif defined _SYS_EVENT_H_
1250 close(pane->event_fd);
1251 #endif
1254 static void
1255 fsev_shdn(void)
1257 pthread_cancel(fsev_thread);
1258 #ifndef __APPLE__
1259 pthread_join(fsev_thread, NULL);
1260 #endif
1261 rmwatch(&pane_l);
1262 rmwatch(&pane_r);
1263 #if defined _SYS_INOTIFY_H
1264 close(inotify_fd);
1265 #elif defined _SYS_EVENT_H_
1266 close(kq);
1267 #endif
1270 static ssize_t
1271 findbm(uint32_t event)
1273 ssize_t i;
1275 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1276 if (event == bmarks[i].ch) {
1277 if (check_dir(bmarks[i].path) != 0) {
1278 print_error(strerror(errno));
1279 return -1;
1281 return i;
1284 return -1;
1287 static void
1288 start_filter(void)
1290 if (cpane->dirc < 1)
1291 return;
1292 char *user_input;
1293 user_input = ecalloc(MAX_USRI, sizeof(char));
1294 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1295 free(user_input);
1296 return;
1298 cpane->filter = user_input;
1299 if (listdir(AddHi) < 0)
1300 print_error("no match");
1301 cpane->filter = NULL;
1302 free(user_input);
1305 static void
1306 start_vmode(void)
1308 struct tb_event fev;
1309 if (selection != NULL) {
1310 free(selection);
1311 selection = NULL;
1314 selection = ecalloc(cpane->dirc, sizeof(size_t));
1315 selection[0] = cpane->hdir;
1316 cont_vmode = 0;
1317 print_prompt("-- VISUAL --");
1318 tb_present();
1319 while (tb_poll_event(&fev) != 0) {
1320 switch (fev.type) {
1321 case TB_EVENT_KEY:
1322 grabkeys(&fev, vkeys, vkeyslen);
1323 if (cont_vmode == -1)
1324 return;
1325 tb_present();
1326 break;
1331 static void
1332 exit_vmode(void)
1334 refresh_pane();
1335 add_hi(cpane, cpane->hdir - 1);
1336 cont_vmode = -1;
1339 static void
1340 selup(void)
1342 mvup();
1343 print_prompt("-- VISUAL --");
1344 int index = abs(cpane->hdir - selection[0]);
1346 if (cpane->hdir < selection[0]) {
1347 selection[index] = cpane->hdir;
1348 add_hi(cpane, selection[index]);
1349 } else if (index < cpane->dirc) {
1350 selection[index + 1] = 0;
1352 if (cpane->dirc >= scrheight ||
1353 cpane->hdir <= 1) { /* rehighlight all if scrolling */
1354 selref();
1358 static void
1359 seldwn(void)
1361 mvdwn();
1362 print_prompt("-- VISUAL --");
1363 int index = abs(cpane->hdir - selection[0]);
1365 if (cpane->hdir > selection[0]) {
1366 selection[index] = cpane->hdir;
1367 add_hi(cpane, selection[index] - 2);
1368 } else {
1369 selection[index + 1] = 0;
1371 if (cpane->dirc >= scrheight ||
1372 cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1373 selref();
1377 static void
1378 selall(void)
1380 int i;
1381 for (i = 0; i < cpane->dirc; i++) {
1382 selection[i] = i + 1;
1384 selref();
1387 static void
1388 selref(void)
1390 int i;
1391 for (i = 0; i < cpane->dirc; i++) {
1392 if (selection[i] < (scrheight + cpane->firstrow) &&
1393 selection[i] >
1394 cpane->firstrow) { /* checks if in the frame of the directories */
1395 add_hi(cpane, selection[i] - 1);
1400 static void
1401 selcalc(void)
1403 int j;
1404 selection_size = 0;
1406 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1407 if (selection[j] != 0)
1408 selection_size++;
1409 else
1410 break;
1414 static void
1415 free_files(void)
1417 if (selected_files == NULL)
1418 return;
1419 free(selected_files);
1420 selected_files = NULL;
1423 static void
1424 init_files(void)
1426 size_t i;
1427 free_files();
1429 selcalc();
1430 selected_files = ecalloc(selection_size, sizeof(char *));
1432 for (i = 0; i < selection_size; i++) {
1433 selected_files[i] = cpane->direntr[selection[i] - 1].name;
1437 static void
1438 selynk(void)
1440 init_files();
1441 refresh_pane();
1442 add_hi(cpane, cpane->hdir - 1);
1443 print_status(cprompt, "%zu files are yanked", selection_size);
1444 cont_vmode = -1;
1447 static void
1448 seldel(void)
1450 char *inp_conf;
1451 int conf_len = 4;
1452 char conf[] = "yes";
1454 inp_conf = ecalloc(conf_len, sizeof(char));
1455 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1456 (strncmp(inp_conf, conf, conf_len) != 0)) {
1457 free(inp_conf);
1458 return; /* canceled by user or wrong inp_conf */
1460 free(inp_conf);
1462 init_files();
1464 if (spawn(rm_cmd, rm_cmd_len, selected_files, selection_size, NULL) < 0)
1465 print_error(strerror(errno));
1466 else
1467 print_status(cprompt, "%zu files are deleted", selection_size);
1469 if (cpane->dirc > 0)
1470 cpane->hdir = 1;
1472 free_files();
1473 cont_vmode = -1;
1476 static void
1477 paste(void)
1479 if (yank_file[0] != NULL) {
1480 print_status(cprompt, "coping");
1481 if (spawn(cp_cmd, cp_cmd_len, &yank_file, 1, cpane->dirn) != 0)
1482 print_error(strerror(errno));
1483 else
1484 print_status(cprompt, "file copied");
1485 yank_file[0] = NULL; /* set yank_file len 0 */
1486 return;
1489 print_error("nothing to paste");
1491 if (selected_files == NULL)
1492 return;
1494 if (spawn(cp_cmd, cp_cmd_len, selected_files, selection_size,
1495 cpane->dirn) < 0)
1496 print_error(strerror(errno));
1497 else
1498 print_status(cprompt, "%zu files are copied", selection_size);
1500 free_files();
1503 static void
1504 selmv(void)
1506 if (yank_file[0] != NULL) {
1507 print_status(cprompt, "moving");
1508 if (spawn(mv_cmd, mv_cmd_len, yank_file, 1, cpane->dirn) != 0)
1509 print_error(strerror(errno));
1510 else
1511 print_status(cprompt, "file moved");
1512 yank_file[0] = NULL; /* set yank_file len 0 */
1513 return;
1516 print_error("nothing to move");
1518 if (selected_files == NULL)
1519 return;
1521 if (spawn(mv_cmd, mv_cmd_len, selected_files, selection_size,
1522 cpane->dirn) < 0)
1523 print_error(strerror(errno));
1524 else
1525 print_status(cprompt, "%zu files are moved", selection_size);
1527 free_files();
1530 static void
1531 rname(void)
1533 char new_name[MAX_P];
1534 char *input_name;
1536 input_name = ecalloc(MAX_N, sizeof(char));
1538 if (get_usrinput(input_name, MAX_N, "rename: %s",
1539 basename(CURSOR.name)) < 0) {
1540 free(input_name);
1541 return;
1544 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1545 free(input_name);
1546 print_error(strerror(errno));
1547 return;
1550 char *rename_cmd[] = { "mv", CURSOR.name, new_name };
1551 if (spawn(rename_cmd, 3, NULL, 0, NULL) < 0)
1552 print_error(strerror(errno));
1554 free(input_name);
1557 static void
1558 yank(void)
1560 yank_file[0] = CURSOR.name;
1561 print_status(cprompt, "1 file is yanked", selection_size);
1564 static void
1565 switch_pane(void)
1567 if (cpane->dirc > 0)
1568 rm_hi(cpane, cpane->hdir - 1);
1569 if (cpane == &pane_l)
1570 cpane = &pane_r;
1571 else if (cpane == &pane_r)
1572 cpane = &pane_l;
1573 if (cpane->dirc > 0) {
1574 add_hi(cpane, cpane->hdir - 1);
1575 print_info(NULL);
1576 } else {
1577 clear_status();
1581 static void
1582 quit(void)
1584 if (cont_vmode == -1) { /* check if selection was allocated */
1585 free(selection);
1586 if (selected_files != NULL)
1587 free_files();
1589 free(pane_l.direntr);
1590 free(pane_r.direntr);
1591 fsev_shdn();
1592 tb_shutdown();
1593 exit(EXIT_SUCCESS);
1596 static void
1597 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1599 size_t i;
1600 ssize_t b;
1602 for (i = 0; i < max_keys; i++) {
1603 if (event->ch != 0) {
1604 if (event->ch == key[i].evkey.ch) {
1605 key[i].func();
1606 return;
1608 } else if (event->key != 0) {
1609 if (event->key == key[i].evkey.key) {
1610 key[i].func();
1611 return;
1616 /* bookmarks */
1617 b = findbm(event->ch);
1618 if (b < 0)
1619 return;
1620 rmwatch(cpane);
1621 strncpy(cpane->dirn, bmarks[b].path, MAX_P);
1622 cpane->firstrow = 0;
1623 cpane->parent_row = 1;
1624 cpane->hdir = 1;
1625 if (listdir(AddHi) < 0)
1626 print_error(strerror(errno));
1629 void *
1630 read_th(void *arg)
1632 struct timespec tim;
1633 tim.tv_sec = 0;
1634 tim.tv_nsec = 5000000L; /* 0.005 sec */
1636 while (1)
1637 if (read_events() > READEVSZ) {
1638 kill(main_pid, SIGUSR1);
1639 nanosleep(&tim, NULL);
1641 return arg;
1644 static void
1645 start_ev(void)
1647 struct tb_event ev;
1649 while (tb_poll_event(&ev) != 0) {
1650 switch (ev.type) {
1651 case TB_EVENT_KEY:
1652 grabkeys(&ev, nkeys, nkeyslen);
1653 tb_present();
1654 break;
1655 case TB_EVENT_RESIZE:
1656 t_resize();
1657 break;
1658 default:
1659 break;
1662 tb_shutdown();
1665 static void
1666 refresh_pane(void)
1668 size_t y, dyn_max, start_from;
1669 int width;
1670 width = (twidth / 2) - 4;
1671 Cpair col;
1673 y = 1;
1674 start_from = cpane->firstrow;
1675 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1677 /* print each entry in directory */
1678 while (start_from < dyn_max) {
1679 get_hicol(&col, cpane->direntr[start_from].mode);
1680 print_row(cpane, start_from, col);
1681 start_from++;
1682 y++;
1685 if (cpane->dirc > 0)
1686 print_info(NULL);
1687 else
1688 clear_status();
1690 /* print current directory title */
1691 cpane->dircol.fg |= TB_BOLD;
1692 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1695 static void
1696 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1698 int i;
1699 char *tmpfull;
1700 struct stat status;
1702 #define ADD_ENTRY \
1703 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1704 strncpy(cpane->direntr[i].name, tmpfull, MAX_N); \
1705 if (lstat(tmpfull, &status) == 0) { \
1706 cpane->direntr[i].size = status.st_size; \
1707 cpane->direntr[i].mode = status.st_mode; \
1708 cpane->direntr[i].group = status.st_gid; \
1709 cpane->direntr[i].user = status.st_uid; \
1710 cpane->direntr[i].dt = status.st_mtime; \
1712 i++; \
1713 free(tmpfull);
1715 i = 0;
1716 cpane->direntr =
1717 erealloc(cpane->direntr, (10 + cpane->dirc) * sizeof(Entry));
1718 while ((entry = readdir(dir)) != 0) {
1719 if ((strncmp(entry->d_name, ".", 2) == 0 ||
1720 strncmp(entry->d_name, "..", 3) == 0))
1721 continue;
1723 if (filter == NULL) {
1724 ADD_ENTRY
1725 } else if (filter != NULL) {
1726 if (strcasestr(entry->d_name, filter) != NULL) {
1727 ADD_ENTRY
1732 cpane->dirc = i;
1735 static int
1736 listdir(int hi)
1738 DIR *dir;
1739 struct dirent *entry;
1740 int width;
1741 int filtercount = 0;
1742 size_t oldc = cpane->dirc;
1744 width = (twidth / 2) - 4;
1745 cpane->dirc = 0;
1747 dir = opendir(cpane->dirn);
1748 if (dir == NULL)
1749 return -1;
1751 /* get content and filter sum */
1752 while ((entry = readdir(dir)) != 0) {
1753 if (cpane->filter != NULL) {
1754 if (strcasestr(entry->d_name, cpane->filter) != NULL)
1755 filtercount++;
1756 } else { /* no filter */
1757 cpane->dirc++;
1761 if (cpane->filter == NULL) {
1762 clear_pane();
1763 cpane->dirc -= 2;
1766 if (cpane->filter != NULL) {
1767 if (filtercount > 0) {
1768 cpane->dirc = filtercount;
1769 clear_pane();
1770 cpane->hdir = 1;
1771 } else if (filtercount == 0) {
1772 if (closedir(dir) < 0)
1773 return -1;
1774 cpane->dirc = oldc;
1775 return -1;
1779 /* print current directory title */
1780 cpane->dircol.fg |= TB_BOLD;
1781 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1783 if (cpane->filter == NULL) /* dont't watch when filtering */
1784 if (addwatch() < 0)
1785 print_error("can't add watch");
1787 /* empty directory */
1788 if (cpane->dirc == 0) {
1789 clear_status();
1790 if (closedir(dir) < 0)
1791 return -1;
1792 return 0;
1795 rewinddir(dir); /* reset position */
1796 set_direntr(entry, dir, cpane->filter); /* create array of entries */
1797 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1798 refresh_pane();
1800 if (hi == AddHi && cpane->dirc > 0)
1801 add_hi(cpane, cpane->hdir - 1);
1803 if (closedir(dir) < 0)
1804 return -1;
1805 return 0;
1808 static void
1809 t_resize(void)
1811 /* TODO need refactoring */
1812 tb_clear();
1813 draw_frame();
1814 pane_r.dirx = (twidth / 2) + 2;
1816 if (cpane == &pane_l) {
1817 cpane = &pane_r;
1818 refresh_pane();
1819 cpane = &pane_l;
1820 refresh_pane();
1821 if (cpane->dirc > 0)
1822 add_hi(&pane_l, pane_l.hdir - 1);
1823 } else if (cpane == &pane_r) {
1824 cpane = &pane_l;
1825 refresh_pane();
1826 cpane = &pane_r;
1827 refresh_pane();
1828 if (cpane->dirc > 0)
1829 add_hi(&pane_r, pane_r.hdir - 1);
1832 tb_present();
1835 static void
1836 get_editor(void)
1838 editor[0] = getenv("EDITOR");
1839 editor[1] = NULL;
1841 if (editor[0] == NULL)
1842 editor[0] = fed;
1845 static void
1846 set_panes(void)
1848 char *home;
1849 char cwd[MAX_P];
1851 home = getenv("HOME");
1852 if (home == NULL)
1853 home = "/";
1854 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1855 strncpy(cwd, home, MAX_P);
1857 pane_l.pane_id = 0;
1858 pane_l.dirx = 2;
1859 pane_l.dircol = cpanell;
1860 pane_l.firstrow = 0;
1861 pane_l.direntr = ecalloc(0, sizeof(Entry));
1862 strncpy(pane_l.dirn, cwd, MAX_P);
1863 pane_l.hdir = 1;
1864 pane_l.inotify_wd = -1;
1865 pane_l.parent_row = 1;
1867 pane_r.pane_id = 1;
1868 pane_r.dirx = (twidth / 2) + 2;
1869 pane_r.dircol = cpanelr;
1870 pane_r.firstrow = 0;
1871 pane_r.direntr = ecalloc(0, sizeof(Entry));
1872 strncpy(pane_r.dirn, home, MAX_P);
1873 pane_r.hdir = 1;
1874 pane_r.inotify_wd = -1;
1875 pane_r.parent_row = 1;
1878 static void
1879 draw_frame(void)
1881 int i;
1882 theight = tb_height();
1883 twidth = tb_width();
1884 scrheight = theight - 2;
1886 /* 2 horizontal lines */
1887 for (i = 1; i < twidth - 1; ++i) {
1888 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1889 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1892 /* 4 vertical lines */
1893 for (i = 1; i < theight - 1; ++i) {
1894 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1895 tb_change_cell(
1896 (twidth - 1) / 2, i - 1, u_vl, cframe.fg, cframe.bg);
1897 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1898 cframe.bg);
1899 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1902 /* 4 corners */
1903 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1904 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1905 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1906 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1908 /* 2 middel top and bottom */
1909 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1910 tb_change_cell(
1911 (twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1914 void
1915 th_handler(int num)
1917 (void)num;
1918 if (cpane == &pane_l) {
1919 cpane = &pane_r;
1920 if (listdir(NoHi) < 0)
1921 print_error(strerror(errno));
1922 cpane = &pane_l;
1923 if (listdir(AddHi) < 0)
1924 print_error(strerror(errno));
1925 } else if (cpane == &pane_r) {
1926 cpane = &pane_l;
1927 if (listdir(NoHi) < 0)
1928 print_error(strerror(errno));
1929 cpane = &pane_r;
1930 if (listdir(AddHi) < 0)
1931 print_error(strerror(errno));
1933 tb_present();
1936 static int
1937 start_signal(void)
1939 struct sigaction sa;
1941 main_pid = getpid();
1942 sa.sa_handler = th_handler;
1943 sigemptyset(&sa.sa_mask);
1944 sa.sa_flags = SA_RESTART;
1945 return sigaction(SIGUSR1, &sa, NULL);
1948 static void
1949 start(void)
1951 if (tb_init() != 0)
1952 die("tb_init");
1953 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1954 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1955 die("output error");
1957 draw_frame();
1958 set_panes();
1959 get_editor();
1960 if (start_signal() < 0)
1961 print_error(strerror(errno));
1962 if (fsev_init() < 0)
1963 print_error(strerror(errno));
1964 cpane = &pane_r;
1965 if (listdir(NoHi) < 0)
1966 print_error(strerror(errno));
1967 cpane = &pane_l;
1968 if (listdir(AddHi) < 0)
1969 print_error(strerror(errno));
1970 tb_present();
1972 pthread_create(&fsev_thread, NULL, read_th, NULL);
1973 start_ev();
1977 main(int argc, char *argv[])
1979 #ifdef __OpenBSD__
1980 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1981 NULL) == -1)
1982 die("pledge");
1983 #endif /* __OpenBSD__ */
1984 if (argc == 1)
1985 start();
1986 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1987 die("sfm-" VERSION);
1988 else
1989 die("usage: sfm [-v]");
1990 return 0;