add linux/macOS preprocossor defines for signals
[sfm.git] / sfm.c
blob946524541b757ed8cb12bc33266903e1676810b7
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 #endif
8 #include <sys/types.h>
9 #include <sys/resource.h>
10 #include <sys/stat.h>
11 #include <sys/time.h>
12 #include <sys/wait.h>
13 #if defined(__linux__)
14 #include <sys/inotify.h>
15 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
16 defined(__OpenBSD__) || defined(__APPLE__)
17 #include <sys/event.h>
18 #endif
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <grp.h>
24 #include <pwd.h>
25 #include <stdarg.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <libgen.h>
34 #include "termbox.h"
35 #include "util.h"
37 /* macros */
38 #define MAX_P 4096
39 #define MAX_N 255
40 #define MAX_USRI 32
41 #define MAX_EXT 4
42 #define MAX_STATUS 255
43 #define MAX_LINE 4096
44 #define MAX_USRN 32
45 #define MAX_GRPN 32
46 #define MAX_DTF 32
47 #define CURSOR cpane->direntr[cpane->hdir - 1]
49 /* enums */
50 enum { AddHi, NoHi }; /* add highlight in listdir */
52 /* typedef */
53 typedef struct {
54 char name[MAX_N];
55 gid_t group;
56 mode_t mode;
57 off_t size;
58 time_t dt;
59 uid_t user;
60 } Entry;
62 typedef struct {
63 uint16_t fg;
64 uint16_t bg;
65 } Cpair;
67 typedef struct {
68 int pane_id;
69 char dirn[MAX_P]; // dir name cwd
70 char *filter;
71 Entry *direntr; // dir entries
72 int dirx; // pane cwd x pos
73 int dirc; // dir entries sum
74 int hdir; // highlighted dir
75 int firstrow;
76 int parent_firstrow;
77 int parent_row; // FIX
78 Cpair dircol;
79 int inotify_wd;
80 int event_fd;
81 } Pane;
83 typedef struct {
84 uint32_t ch;
85 char path[MAX_P];
86 } Bookmark;
88 typedef struct {
89 const char **ext;
90 size_t exlen;
91 const void *v;
92 } Rule;
94 typedef union {
95 uint16_t key; /* one of the TB_KEY_* constants */
96 uint32_t ch; /* unicode character */
97 } Evkey;
99 typedef struct {
100 const Evkey evkey;
101 void (*func)(void);
102 } Key;
104 /* function declarations */
105 static void print_tb(const char *, int, int, uint16_t, uint16_t);
106 static void printf_tb(int, int, Cpair, const char *, ...);
107 static void print_status(Cpair, const char *, ...);
108 static void print_xstatus(char, int);
109 static void print_error(char *);
110 static void print_prompt(char *);
111 static void print_info(char *);
112 static void print_row(Pane *, size_t, Cpair);
113 static void clear(int, int, int, uint16_t);
114 static void clear_status(void);
115 static void clear_pane(void);
116 static void add_hi(Pane *, size_t);
117 static void rm_hi(Pane *, size_t);
118 static int check_dir(char *);
119 static mode_t chech_execf(mode_t);
120 static int sort_name(const void *const, const void *const);
121 static void get_dirp(char *);
122 static char *get_ext(char *);
123 static int get_fdt(char *, time_t);
124 static char *get_fgrp(gid_t);
125 static char *get_fperm(mode_t);
126 static char *get_fsize(off_t);
127 static char *get_fullpath(char *, char *);
128 static char *get_fusr(uid_t);
129 static void get_dirsize(char *, off_t *);
130 static void get_hicol(Cpair *, mode_t);
131 static int delent(char *);
132 static void calcdir(void);
133 static void crnd(void);
134 static void crnf(void);
135 static void delfd(void);
136 static void mvbk(void);
137 static void mvbtm(void);
138 static void mvdwn(void);
139 static void mvdwns(void);
140 static void mvfwd(void);
141 static void mvmid(void);
142 static void mvtop(void);
143 static void mvup(void);
144 static void mvups(void);
145 static void scrdwn(void);
146 static void scrdwns(void);
147 static void scrup(void);
148 static void scrups(void);
149 static int get_usrinput(char*, size_t, const char*, ...);
150 static int frules(char *);
151 static int spawn(const void *, char *);
152 static int opnf(char *);
153 static int fsev_init(void);
154 static int addwatch(void);
155 static int read_events(void);
156 static void rmwatch(Pane *);
157 static void fsev_shdn(void);
158 static ssize_t findbm(uint32_t);
159 static void start_filter(void);
160 static void start_vmode(void);
161 static void exit_vmode(void);
162 static void selup(void);
163 static void seldwn(void);
164 static void selall(void);
165 static void selref(void);
166 static void selynk(void);
167 static void selcalc(void);
168 static void paste(void);
169 static void selmv(void);
170 static void seldel(void);
171 static void init_files(void);
172 static void free_files(void);
173 static void yank(void);
174 static void rname(void);
175 static void switch_pane(void);
176 static void quit(void);
177 static void grabkeys(struct tb_event*, Key*, size_t);
178 static void start_ev(void);
179 static void refresh_pane(void);
180 static void set_direntr(struct dirent *, DIR *, char *);
181 static int listdir(int);
182 static void t_resize(void);
183 static void set_panes(void);
184 static void draw_frame(void);
185 static void start(void);
187 /* global variables */
188 static Pane pane_r, pane_l, *cpane;
189 static char *editor[2];
190 static char fed[] = "vi";
191 static int theight, twidth, scrheight;
192 static size_t selection_size = 0;
193 static char yank_file[MAX_P];
194 static int *selection;
195 static int cont_vmode = 0;
196 static char **selected_files;
197 #if defined _SYS_INOTIFY_H
198 static int inotify_fd;
199 #elif defined _SYS_EVENT_H_
200 static int kq;
201 struct kevent evlist[2]; /* events we want to monitor */
202 struct kevent chlist[2]; /* events that were triggered */
203 static struct timespec gtimeout;
204 #endif
206 /* configuration, allows nested code to access above variables */
207 #include "config.h"
209 /* function implementations */
210 static void
211 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
213 while (*str != '\0') {
214 uint32_t uni = 0;
215 str += tb_utf8_char_to_unicode(&uni, str);
216 tb_change_cell(x, y, uni, fg, bg);
217 x++;
221 static void
222 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
224 char buf[MAX_LINE];
225 va_list vl;
226 va_start(vl, fmt);
227 (void)vsnprintf(buf, MAX_LINE, fmt, vl);
228 va_end(vl);
229 print_tb(buf, x, y, col.fg, col.bg);
232 static void
233 print_status(Cpair col, const char *fmt, ...)
235 char buf[MAX_STATUS];
236 va_list vl;
237 va_start(vl, fmt);
238 (void)vsnprintf(buf, MAX_STATUS, fmt, vl);
239 va_end(vl);
240 clear_status();
241 print_tb(buf, 1, theight - 1, col.fg, col.bg);
244 static void
245 print_xstatus(char c, int x)
247 uint32_t uni = 0;
248 (void)tb_utf8_char_to_unicode(&uni, &c);
249 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
252 static void
253 print_error(char *errmsg)
255 print_status(cerr, errmsg);
258 static void
259 print_prompt(char *prompt)
261 print_status(cprompt, prompt);
264 static void
265 print_info(char *dirsize)
267 char *sz, *ur, *gr, *dt, *prm;
269 dt = ecalloc(MAX_DTF, sizeof(char));
271 prm = get_fperm(CURSOR.mode);
272 ur = get_fusr(CURSOR.user);
273 gr = get_fgrp(CURSOR.group);
275 if (get_fdt(dt, CURSOR.dt) < 0)
276 *dt = '\0';
278 if (S_ISREG(CURSOR.mode)) {
279 sz = get_fsize(CURSOR.size);
280 } else {
281 if (dirsize == NULL) {
282 sz = ecalloc(1, sizeof(char));
283 *sz = '\0';
284 } else {
285 sz = dirsize;
289 print_status(cstatus, "%02d/%02d %s %s:%s %s %s",
290 cpane->hdir, cpane->dirc, prm, ur, gr, dt, sz);
292 free(prm);
293 free(ur);
294 free(gr);
295 free(dt);
296 free(sz);
299 static void
300 print_row(Pane *pane, size_t entpos, Cpair col)
302 int x, y;
303 char *result;
304 char buf[MAX_P];
305 char lnk_full[MAX_P];
306 int width;
308 width = (twidth / 2) - 4;
309 result = basename(pane->direntr[entpos].name);
310 x = pane->dirx;
311 y = entpos - cpane->firstrow + 1;
313 if (S_ISLNK(pane->direntr[entpos].mode) != 0) {
314 if (realpath(pane->direntr[entpos].name, buf) != NULL) {
315 (void)snprintf(lnk_full, MAX_N,
316 "%s -> %s", result, buf);
317 result = lnk_full;
321 printf_tb(x, y, col, "%*.*s", ~width, width, result);
324 static void
325 clear(int sx, int ex, int y, uint16_t bg)
327 /* clear line from to */
328 /* x = line number vertical */
329 /* y = column number horizontal */
330 int i;
331 for (i = sx; i < ex; i++) {
332 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
336 static void
337 clear_status(void)
339 clear(1, twidth - 1, theight - 1, cstatus.bg);
342 static void
343 clear_pane(void)
345 int i, ex, y;
346 y = 0, i = 0, ex = 0;
348 if (cpane->pane_id == pane_l.pane_id)
349 ex = (twidth / 2) - 1;
350 else if (cpane->pane_id == pane_r.pane_id)
351 ex = twidth - 1;
353 while (i < scrheight) {
354 clear(cpane->dirx, ex, y, TB_DEFAULT);
355 i++;
356 y++;
359 /* draw top line */
360 for (y = cpane->dirx; y < ex; ++y) {
361 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
365 static void
366 add_hi(Pane *pane, size_t entpos)
368 Cpair col;
369 get_hicol(&col, pane->direntr[entpos].mode);
370 col.fg |= TB_REVERSE | TB_BOLD;
371 col.bg |= TB_REVERSE;
372 print_row(pane, entpos, col);
375 static void
376 rm_hi(Pane *pane, size_t entpos)
378 Cpair col;
379 get_hicol(&col, pane->direntr[entpos].mode);
380 print_row(pane, entpos, col);
383 static int
384 check_dir(char *path)
386 DIR *dir;
387 dir = opendir(path);
389 if (dir == NULL) {
390 if (errno == ENOTDIR) {
391 return 1;
392 } else {
393 return -1;
397 if (closedir(dir) < 0)
398 return -1;
400 return 0;
403 static mode_t
404 chech_execf(mode_t mode)
406 if (S_ISREG(mode))
407 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
408 return 0;
411 static int
412 sort_name(const void *const A, const void *const B)
414 int result;
415 mode_t data1 = (*(Entry *)A).mode;
416 mode_t data2 = (*(Entry *)B).mode;
418 if (data1 < data2) {
419 return -1;
420 } else if (data1 == data2) {
421 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
422 return result;
423 } else {
424 return 1;
428 static void
429 get_dirp(char *cdir)
431 int counter, len, i;
433 counter = 0;
434 len = strnlen(cdir, MAX_P);
435 if (len == 1)
436 return;
438 for (i = len - 1; i > 1; i--) {
439 if (cdir[i] == '/')
440 break;
441 else
442 counter++;
445 cdir[len-counter-1] = '\0';
448 static char *
449 get_ext(char *str)
451 char *ext;
452 char dot;
453 size_t counter, len, i;
455 dot = '.';
456 counter = 0;
457 len = strnlen(str, MAX_N);
459 for (i = len - 1; i > 0; i--) {
460 if (str[i] == dot) {
461 break;
462 } else {
463 counter++;
467 ext = ecalloc(MAX_EXT + 1, sizeof(char));
468 strncpy(ext, &str[len - counter], MAX_EXT);
469 ext[MAX_EXT] = '\0';
470 return ext;
473 static int
474 get_fdt(char *result, time_t status)
476 struct tm lt;
477 localtime_r(&status, &lt);
478 return strftime(result, MAX_DTF, dtfmt, &lt);
481 static char *
482 get_fgrp(gid_t status)
484 char *result;
485 struct group *gr;
487 result = ecalloc(MAX_GRPN, sizeof(char));
488 gr = getgrgid(status);
489 if (gr == NULL)
490 (void)snprintf(result, MAX_GRPN, "%u", status);
491 else
492 strncpy(result, gr->gr_name, MAX_GRPN);
494 result[MAX_GRPN - 1] = '\0';
495 return result;
498 static char *
499 get_fperm(mode_t mode)
501 char *buf;
502 size_t i;
504 const char chars[] = "rwxrwxrwx";
505 buf = ecalloc(11, sizeof(char));
507 if (S_ISDIR(mode))
508 buf[0] = 'd';
509 else if (S_ISREG(mode))
510 buf[0] = '-';
511 else if (S_ISLNK(mode))
512 buf[0] = 'l';
513 else if (S_ISBLK(mode))
514 buf[0] = 'b';
515 else if (S_ISCHR(mode))
516 buf[0] = 'c';
517 else if (S_ISFIFO(mode))
518 buf[0] = 'p';
519 else if (S_ISSOCK(mode))
520 buf[0] = 's';
521 else
522 buf[0] = '?';
524 for (i = 1; i < 10; i++) {
525 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
527 buf[10] = '\0';
529 return buf;
532 static char *
533 get_fsize(off_t size)
535 char *result; /* need to be freed */
536 char unit;
537 int result_len;
538 int counter;
540 counter = 0;
541 result_len = 6; /* 9999X/0 */
542 result = ecalloc(result_len, sizeof(char));
544 while (size >= 1000) {
545 size /= 1024;
546 ++counter;
549 switch (counter) {
550 case 0:
551 unit = 'B';
552 break;
553 case 1:
554 unit = 'K';
555 break;
556 case 2:
557 unit = 'M';
558 break;
559 case 3:
560 unit = 'G';
561 break;
562 case 4:
563 unit = 'T';
564 break;
565 default:
566 unit = '?';
569 if (snprintf(result, result_len, "%ld%c", size, unit) < 0)
570 strncat(result, "???", result_len);
572 return result;
575 static char *
576 get_fullpath(char *first, char *second)
578 char *full_path;
580 full_path = ecalloc(MAX_P, sizeof(char));
582 if (strncmp(first, "/", MAX_P) == 0)
583 (void)snprintf(full_path, MAX_P, "/%s", second);
584 else
585 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
587 return full_path;
590 static char *
591 get_fusr(uid_t status)
593 char *result;
594 struct passwd *pw;
596 result = ecalloc(MAX_USRN, sizeof(char));
597 pw = getpwuid(status);
598 if (pw == NULL)
599 (void)snprintf(result, MAX_USRN, "%u", status);
600 else
601 strncpy(result, pw->pw_name, MAX_USRN);
603 result[MAX_USRN - 1] = '\0';
604 return result;
607 static void
608 get_dirsize(char *fullpath, off_t *fullsize)
610 DIR *dir;
611 char *ent_full;
612 mode_t mode;
613 struct dirent *entry;
614 struct stat status;
616 dir = opendir(fullpath);
617 if (dir == NULL) {
618 return;
621 while ((entry = readdir(dir)) != 0) {
622 if ((strncmp(entry->d_name, ".", 2) == 0 ||
623 strncmp(entry->d_name, "..", 3) == 0))
624 continue;
626 ent_full = get_fullpath(fullpath, entry->d_name);
627 if (lstat(ent_full, &status) == 0) {
628 mode = status.st_mode;
629 if (S_ISDIR(mode)) {
630 get_dirsize(ent_full, fullsize);
631 free(ent_full);
632 } else {
633 *fullsize += status.st_size;
634 free(ent_full);
639 closedir(dir);
640 clear_status();
643 static void
644 get_hicol(Cpair *col, mode_t mode)
646 *col = cfile;
647 if (S_ISDIR(mode))
648 *col = cdir;
649 else if (S_ISLNK(mode))
650 *col = cother;
651 else if (chech_execf(mode) > 0)
652 *col = cexec;
655 static int
656 delent(char *fullpath)
658 char *inp_conf;
659 int conf_len = 4;
660 char conf[] = "yes";
662 inp_conf = ecalloc(conf_len, sizeof(char));
663 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
664 (strncmp(inp_conf, conf, conf_len) != 0)) {
665 free(inp_conf);
666 return 1; /* canceled by user or wrong inp_conf */
668 free(inp_conf);
670 return spawn(rm_cmd, fullpath);
673 static void
674 calcdir(void)
676 if (!S_ISDIR(CURSOR.mode))
677 return;
679 off_t *fullsize;
680 char *csize;
682 fullsize = ecalloc(1, sizeof(off_t));
683 get_dirsize(CURSOR.name, fullsize);
684 csize = get_fsize(*fullsize);
686 CURSOR.size = *fullsize;
687 print_info(csize);
688 free(fullsize);
691 static void
692 crnd(void)
694 char *user_input, *path;
696 user_input = ecalloc(MAX_USRI, sizeof(char));
697 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
698 free(user_input);
699 return;
702 path = ecalloc(MAX_P, sizeof(char));
703 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
704 free(user_input);
705 free(path);
706 return;
709 if (mkdir(path, ndir_perm) < 0)
710 print_error(strerror(errno));
712 free(user_input);
713 free(path);
716 static void
717 crnf(void)
719 char *user_input, *path;
720 int rf;
722 user_input = ecalloc(MAX_USRI, sizeof(char));
723 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
724 free(user_input);
725 return;
728 path = ecalloc(MAX_P, sizeof(char));
729 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
730 free(user_input);
731 free(path);
732 return;
735 rf = open(path, O_CREAT | O_EXCL, nf_perm);
737 if (rf < 0)
738 print_error(strerror(errno));
739 else
740 if (close(rf) < 0)
741 print_error(strerror(errno));
743 free(user_input);
744 free(path);
747 static void
748 delfd(void)
750 switch (delent(CURSOR.name)) {
751 case -1:
752 print_error(strerror(errno));
753 break;
754 case 0:
755 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
756 cpane->hdir--;
757 break;
761 static void
762 mvbk(void)
764 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
765 return;
768 get_dirp(cpane->dirn);
769 if (check_dir(cpane->dirn) < 0) {
770 print_error(strerror(errno));
771 return;
774 rmwatch(cpane);
775 cpane->firstrow = cpane->parent_firstrow;
776 cpane->hdir = cpane->parent_row;
777 if (listdir(AddHi) < 0)
778 print_error(strerror(errno));
779 cpane->parent_firstrow = 0;
780 cpane->parent_row = 1;
783 static void
784 mvbtm(void)
786 if (cpane->dirc < 1)
787 return;
788 if (cpane->dirc > scrheight) {
789 rm_hi(cpane, cpane->hdir - 1);
790 cpane->hdir = cpane->dirc;
791 cpane->firstrow = cpane->dirc - scrheight + 1;
792 refresh_pane();
793 add_hi(cpane, cpane->hdir - 1);
794 } else {
795 rm_hi(cpane, cpane->hdir - 1);
796 cpane->hdir = cpane->dirc;
797 add_hi(cpane, cpane->hdir - 1);
799 print_info(NULL);
802 static void
803 mvdwn(void)
805 if (cpane->dirc < 1)
806 return;
807 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
808 rm_hi(cpane, cpane->hdir - 1);
809 cpane->hdir++;
810 add_hi(cpane, cpane->hdir - 1);
811 } else {
812 mvdwns(); /* scroll */
814 print_info(NULL);
817 static void
818 mvdwns(void)
820 int real;
821 real = cpane->hdir - 1 - cpane->firstrow;
823 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
824 cpane->firstrow++;
825 rm_hi(cpane, cpane->hdir - 1);
826 cpane->hdir++;
827 refresh_pane();
828 add_hi(cpane, cpane->hdir - 1);
829 } else if (cpane->hdir < cpane->dirc) {
830 rm_hi(cpane, cpane->hdir - 1);
831 cpane->hdir++;
832 add_hi(cpane, cpane->hdir - 1);
836 static void
837 mvfwd(void)
839 rmwatch(cpane);
840 if (cpane->dirc < 1)
841 return;
842 int s;
844 switch (check_dir(CURSOR.name)) {
845 case 0:
846 strncpy(cpane->dirn, CURSOR.name, MAX_P);
847 cpane->parent_row = cpane->hdir;
848 cpane->parent_firstrow = cpane->firstrow;
849 cpane->hdir = 1;
850 cpane->firstrow = 0;
851 if (listdir(AddHi) < 0)
852 print_error(strerror(errno));
853 break;
854 case 1: /* not a directory open file */
855 tb_shutdown();
856 s = opnf(CURSOR.name);
857 if (tb_init() != 0)
858 die("tb_init");
859 t_resize();
860 if (s < 0)
861 print_error("process failed non-zero exit");
862 break;
863 case -1: /* failed to open directory */
864 print_error(strerror(errno));
868 static void
869 mvmid(void)
871 if (cpane->dirc < 1)
872 return;
873 rm_hi(cpane, cpane->hdir - 1);
874 if (cpane->dirc < scrheight / 2)
875 cpane->hdir = (cpane->dirc + 1) / 2;
876 else
877 cpane->hdir = (scrheight / 2) + cpane->firstrow;
878 add_hi(cpane, cpane->hdir - 1);
879 print_info(NULL);
882 static void
883 mvtop(void)
885 if (cpane->dirc < 1)
886 return;
887 if (cpane->dirc > scrheight) {
888 rm_hi(cpane, cpane->hdir - 1);
889 cpane->hdir = 1;
890 cpane->firstrow = 0;
891 refresh_pane();
892 add_hi(cpane, cpane->hdir - 1);
893 } else {
894 rm_hi(cpane, cpane->hdir - 1);
895 cpane->hdir = 1;
896 add_hi(cpane, cpane->hdir - 1);
897 print_info(NULL);
901 static void
902 mvup(void)
904 if (cpane->dirc < 1)
905 return;
906 if (cpane->dirc < scrheight && cpane->hdir > 1) {
907 rm_hi(cpane, cpane->hdir - 1);
908 cpane->hdir--;
909 add_hi(cpane, cpane->hdir - 1);
910 } else {
911 mvups(); /* scroll */
913 print_info(NULL);
916 static void
917 mvups(void)
919 size_t real;
920 real = cpane->hdir - 1 - cpane->firstrow;
922 if (cpane->firstrow > 0 && real < 1 + scrsp) {
923 cpane->firstrow--;
924 rm_hi(cpane, cpane->hdir - 1);
925 cpane->hdir--;
926 refresh_pane();
927 add_hi(cpane, cpane->hdir - 1);
928 } else if (cpane->hdir > 1) {
929 rm_hi(cpane, cpane->hdir - 1);
930 cpane->hdir--;
931 add_hi(cpane, cpane->hdir - 1);
935 static void
936 scrdwn(void)
938 if (cpane->dirc < 1)
939 return;
940 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
941 if (cpane->hdir < cpane->dirc - scrmv) {
942 rm_hi(cpane, cpane->hdir - 1);
943 cpane->hdir += scrmv;
944 add_hi(cpane, cpane->hdir - 1);
945 } else {
946 mvbtm();
948 } else {
949 scrdwns();
951 print_info(NULL);
954 static void
955 scrdwns(void)
957 int real, dynmv;
959 real = cpane->hdir - cpane->firstrow;
960 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
962 if (real + scrmv + 1 > scrheight &&
963 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
964 cpane->firstrow += dynmv;
965 rm_hi(cpane, cpane->hdir - 1);
966 cpane->hdir += scrmv;
967 refresh_pane();
968 add_hi(cpane, cpane->hdir - 1);
969 } else {
970 if (cpane->hdir < cpane->dirc - scrmv - 1) {
971 rm_hi(cpane, cpane->hdir - 1);
972 cpane->hdir += scrmv;
973 add_hi(cpane, cpane->hdir - 1);
974 } else {
975 mvbtm();
980 static void
981 scrup(void)
983 if (cpane->dirc < 1)
984 return;
985 if (cpane->dirc < scrheight && cpane->hdir > 1) {
986 if (cpane->hdir > scrmv) {
987 rm_hi(cpane, cpane->hdir - 1);
988 cpane->hdir = cpane->hdir - scrmv;
989 add_hi(cpane, cpane->hdir - 1);
990 print_info(NULL);
991 } else {
992 mvtop();
994 } else {
995 scrups();
999 static void
1000 scrups(void)
1002 int real, dynmv;
1003 real = cpane->hdir - cpane->firstrow;
1004 dynmv = MIN(cpane->firstrow, scrmv);
1006 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1007 cpane->firstrow -= dynmv;
1008 rm_hi(cpane, cpane->hdir - 1);
1009 cpane->hdir -= scrmv;
1010 refresh_pane();
1011 add_hi(cpane, cpane->hdir - 1);
1012 } else {
1013 if (cpane->hdir > scrmv + 1) {
1014 rm_hi(cpane, cpane->hdir - 1);
1015 cpane->hdir -= scrmv;
1016 add_hi(cpane, cpane->hdir - 1);
1017 } else {
1018 mvtop();
1023 static int
1024 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1026 int height = tb_height();
1027 size_t startat;
1028 struct tb_event fev;
1029 size_t counter = (size_t)1;
1030 char empty = ' ';
1031 int x = 0;
1032 int name_size = 0;
1033 char buf[256];
1035 clear_status();
1037 va_list vl;
1038 Cpair col;
1039 col = cprompt;
1040 va_start(vl, fmt);
1041 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1042 va_end(vl);
1043 print_tb(buf, 1, height-1, col.fg, col.bg);
1044 startat = name_size + 1;
1045 tb_set_cursor((int)(startat + 1), height-1);
1046 tb_present();
1048 while (tb_poll_event(&fev) != 0) {
1049 switch (fev.type) {
1050 case TB_EVENT_KEY:
1051 if (fev.key == TB_KEY_ESC) {
1052 tb_set_cursor(-1, -1);
1053 clear_status();
1054 return -1;
1057 if (fev.key == TB_KEY_BACKSPACE ||
1058 fev.key == TB_KEY_BACKSPACE2) {
1059 if (BETWEEN(counter, 2, sout)) {
1060 out[x - 1] = '\0';
1061 counter--;
1062 x--;
1063 print_xstatus(empty, startat + counter);
1064 tb_set_cursor(startat + counter,
1065 theight - 1);
1068 } else if (fev.key == TB_KEY_ENTER) {
1069 tb_set_cursor(-1, -1);
1070 out[counter - 1] = '\0';
1071 return 0;
1073 } else {
1074 if (counter < sout) {
1075 print_xstatus((char)fev.ch,
1076 (startat + counter));
1077 out[x] = (char)fev.ch;
1078 tb_set_cursor((startat + counter + 1),
1079 theight - 1);
1080 counter++;
1081 x++;
1085 tb_present();
1086 break;
1088 default:
1089 return -1;
1093 return -1;
1096 static int
1097 frules(char *ex)
1099 size_t c, d;
1101 for (c = 0; c < LEN(rules); c++)
1102 for (d = 0; d < rules[c].exlen; d++)
1103 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1104 return c;
1105 return -1;
1108 static int
1109 spawn(const void *v, char *fn)
1111 int ws, x, argc, fd;
1112 pid_t pid, r;
1114 x = 0;
1115 argc = 0;
1117 /* count args */
1118 while (((char **)v)[x++] != NULL)
1119 argc++;
1121 char *argv[argc + 2];
1122 for ( x = 0; x < argc; x++)
1123 argv[x] = ((char **)v)[x];
1125 argv[argc] = fn;
1126 argv[argc + 1] = NULL;
1128 pid = fork();
1129 switch (pid) {
1130 case -1:
1131 return -1;
1132 case 0:
1133 fd = open("/dev/null",O_WRONLY);
1134 dup2(fd, STDERR_FILENO);
1135 execvp(argv[0], argv);
1136 exit(EXIT_SUCCESS);
1137 close(fd);
1138 default:
1139 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1140 continue;
1141 if (r == -1)
1142 return -1;
1143 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1144 return -1;
1146 return 0;
1149 static int
1150 opnf(char *fn)
1152 char *ex;
1153 int c;
1155 ex = get_ext(fn);
1156 c = frules(ex);
1157 free(ex);
1159 if (c < 0) /* extension not found open in editor */
1160 return spawn(editor, fn);
1161 else
1162 return spawn((char **)rules[c].v, fn);
1165 static int
1166 fsev_init(void)
1168 #if defined _SYS_INOTIFY_H
1169 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1170 if (inotify_fd < 0)
1171 return -1;
1172 #elif defined _SYS_EVENT_H_
1173 kq = kqueue();
1174 if (kq < 0)
1175 return -1;
1176 #endif
1177 return 0;
1180 static int
1181 addwatch(void)
1183 #if defined _SYS_INOTIFY_H
1184 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1185 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1186 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1187 #elif defined _SYS_EVENT_H_
1188 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1189 if (cpane->event_fd < 0)
1190 return cpane->event_fd;
1191 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1192 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1193 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1194 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1195 return 0;
1196 #endif
1199 static int
1200 read_events(void)
1202 #if defined _SYS_INOTIFY_H
1203 char *p;
1204 ssize_t r;
1205 struct inotify_event *event;
1206 const size_t events = 32;
1207 const size_t evbuflen =
1208 events * (sizeof(struct inotify_event) + MAX_N + 1);
1209 char buf[evbuflen];
1211 if (cpane->inotify_wd < 0)
1212 return -1;
1213 r = read(inotify_fd, buf, evbuflen);
1214 if (r <= 0)
1215 return r;
1217 for (p = buf; p < buf + r;) {
1218 event = (struct inotify_event *)p;
1219 if (!event->wd)
1220 break;
1221 if (event->mask) {
1222 return r;
1225 p += sizeof(struct inotify_event) + event->len;
1227 #elif defined _SYS_EVENT_H_
1228 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1229 #endif
1230 return -1;
1233 static void
1234 rmwatch(Pane *pane)
1236 #if defined _SYS_INOTIFY_H
1237 if (pane->inotify_wd >= 0)
1238 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1239 #elif defined _SYS_EVENT_H_
1240 close(pane->event_fd);
1241 return;
1242 #endif
1245 static void
1246 fsev_shdn(void)
1248 rmwatch(&pane_l);
1249 rmwatch(&pane_r);
1250 #if defined _SYS_INOTIFY_H
1251 close(inotify_fd);
1252 #elif defined _SYS_EVENT_H_
1253 close(kq);
1254 #endif
1257 static ssize_t
1258 findbm(uint32_t event)
1260 ssize_t i;
1262 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1263 if (event == bmarks[i].ch) {
1264 if (check_dir(bmarks[i].path) != 0) {
1265 print_error(strerror(errno));
1266 return -1;
1268 return i;
1271 return -1;
1274 static void
1275 start_filter(void)
1277 if (cpane->dirc < 1)
1278 return;
1279 char *user_input;
1280 user_input = ecalloc(MAX_USRI, sizeof(char));
1281 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1282 free(user_input);
1283 return;
1285 cpane->filter = user_input;
1286 if (listdir(AddHi) < 0)
1287 print_error("no match");
1288 cpane->filter = NULL;
1289 free(user_input);
1292 static void
1293 start_vmode(void)
1295 struct tb_event fev;
1296 if (selection != NULL) {
1297 free(selection);
1298 selection = NULL;
1301 selection = ecalloc(cpane->dirc, sizeof(size_t));
1302 selection[0] = cpane->hdir;
1303 cont_vmode = 0;
1304 print_prompt("-- VISUAL --");
1305 tb_present();
1306 while (tb_poll_event(&fev) != 0) {
1307 switch (fev.type) {
1308 case TB_EVENT_KEY:
1309 grabkeys(&fev, vkeys, vkeyslen);
1310 if (cont_vmode == -1)
1311 return;
1312 tb_present();
1313 break;
1318 static void
1319 exit_vmode(void)
1321 refresh_pane();
1322 add_hi(cpane, cpane->hdir - 1);
1323 cont_vmode = -1;
1326 static void
1327 selup(void)
1329 mvup();
1330 print_prompt("-- VISUAL --");
1331 int index = abs(cpane->hdir - selection[0]);
1333 if (cpane->hdir < selection[0]) {
1334 selection[index] = cpane->hdir;
1335 add_hi(cpane, selection[index]);
1336 } else if (index < cpane->dirc) {
1337 selection[index + 1] = 0;
1339 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1340 selref();
1344 static void
1345 seldwn(void)
1347 mvdwn();
1348 print_prompt("-- VISUAL --");
1349 int index = abs(cpane->hdir - selection[0]);
1351 if (cpane->hdir > selection[0]) {
1352 selection[index] = cpane->hdir;
1353 add_hi(cpane, selection[index] - 2);
1354 } else {
1355 selection[index + 1] = 0;
1357 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1358 selref();
1362 static void
1363 selall(void)
1365 int i;
1366 for (i = 0; i < cpane->dirc; i++) {
1367 selection[i] = i + 1;
1369 selref();
1372 static void
1373 selref(void)
1375 int i;
1376 for (i = 0; i < cpane->dirc; i++) {
1377 if (selection[i] < (scrheight + cpane->firstrow) && selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1378 add_hi(cpane, selection[i] - 1);
1383 static void
1384 selcalc(void)
1386 int j;
1387 selection_size = 0;
1389 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1390 if (selection[j] != 0)
1391 selection_size++;
1392 else
1393 break;
1397 static void
1398 free_files(void)
1400 size_t i;
1402 if (selected_files != NULL) {
1403 for (i = 0; i < selection_size; i++) {
1404 free(selected_files[i]);
1405 selected_files[i] = NULL;
1407 free(selected_files);
1408 selected_files = NULL;
1412 static void
1413 init_files(void)
1415 size_t i;
1416 free_files();
1418 selcalc();
1419 selected_files = ecalloc(selection_size, sizeof(char*));
1421 for (i = 0; i < selection_size; i++) {
1422 selected_files[i] = ecalloc(MAX_P, sizeof(char));
1423 strncpy(selected_files[i], cpane->direntr[selection[i] -1].name, MAX_P); /* TODO use pointer */
1427 static void
1428 selynk(void)
1430 init_files();
1431 refresh_pane();
1432 add_hi(cpane, cpane->hdir -1);
1433 print_status(cprompt, "%zu files are yanked", selection_size);
1434 cont_vmode = -1;
1437 static void
1438 seldel(void)
1440 char *inp_conf;
1441 int conf_len = 4;
1442 char conf[] = "yes";
1443 size_t i;
1445 inp_conf = ecalloc(conf_len, sizeof(char));
1446 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1447 (strncmp(inp_conf, conf, conf_len) != 0)) {
1448 free(inp_conf);
1449 return; /* canceled by user or wrong inp_conf */
1451 free(inp_conf);
1453 init_files();
1454 for (i = 0; i < selection_size; i++) {
1455 spawn(rm_cmd, selected_files[i]);
1458 cpane->hdir = cpane->dirc - selection_size;
1459 print_status(cprompt, "%zu files are deleted", selection_size);
1460 free_files();
1461 cont_vmode = -1;
1464 static void
1465 paste(void)
1467 size_t i;
1468 if (strnlen(yank_file, MAX_P) != 0) {
1469 print_status(cprompt, "coping");
1470 if (spawn(cp_cmd, cpane->dirn) != 0)
1471 print_error("coping failed");
1472 else
1473 print_status(cprompt, "file copied");
1474 yank_file[0] = '\0'; /* set yank_file len 0 */
1475 return;
1478 print_error("nothing to paste");
1480 if (selected_files == NULL)
1481 return;
1483 for (i = 0; i < selection_size; i++) {
1484 char *selcp_cmd[] = { "cp", "-r", selected_files[i], cpane->dirn, NULL };
1485 spawn(selcp_cmd,NULL);
1487 print_status(cprompt, "%zu files are copied", selection_size);
1488 free_files();
1491 static void
1492 selmv(void)
1494 size_t i;
1496 if (strnlen(yank_file, MAX_P) != 0) {
1497 print_status(cprompt, "moving");
1498 if (spawn(mv_cmd, cpane->dirn) != 0)
1499 print_error("moving failed");
1500 else
1501 print_status(cprompt, "file moved");
1502 yank_file[0] = '\0'; /* set yank_file len 0 */
1503 return;
1506 print_error("nothing to move");
1508 if (selected_files == NULL)
1509 return;
1511 for (i = 0; i < selection_size; i++) {
1512 char *selmv_cmd[] = { "mv", selected_files[i], cpane->dirn, NULL };
1513 spawn(selmv_cmd,NULL);
1515 print_status(cprompt, "%zu files are moved", selection_size);
1516 free_files();
1519 static void
1520 rname(void)
1522 char new_name[MAX_P];
1523 char *input_name;
1525 input_name = ecalloc(MAX_N, sizeof(char));
1527 if (get_usrinput(input_name, MAX_N, "rename: %s", basename(CURSOR.name)) < 0) {
1528 free(input_name);
1529 return;
1532 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1533 free(input_name);
1534 print_error(strerror(errno));
1535 return;
1538 char *rename_cmd[] = { "mv", CURSOR.name, new_name, NULL };
1539 if (spawn(rename_cmd, NULL) < 0)
1540 print_error(strerror(errno));
1542 free(input_name);
1545 static void
1546 yank(void)
1548 strncpy(yank_file, CURSOR.name, MAX_P);
1549 print_status(cprompt, "1 file is yanked", selection_size);
1553 static void
1554 switch_pane(void)
1556 if (cpane->dirc > 0)
1557 rm_hi(cpane, cpane->hdir - 1);
1558 if (cpane == &pane_l)
1559 cpane = &pane_r;
1560 else if (cpane == &pane_r)
1561 cpane = &pane_l;
1562 if (cpane->dirc > 0) {
1563 add_hi(cpane, cpane->hdir - 1);
1564 print_info(NULL);
1565 } else {
1566 clear_status();
1570 static void
1571 quit(void)
1573 if (cont_vmode == -1) { /* check if selection was allocated */
1574 free(selection);
1575 if (selected_files != NULL)
1576 free_files();
1578 free(pane_l.direntr);
1579 free(pane_r.direntr);
1580 fsev_shdn();
1581 tb_shutdown();
1582 exit(EXIT_SUCCESS);
1585 static void
1586 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1588 size_t i;
1589 ssize_t b;
1591 for (i = 0; i < max_keys; i++) {
1592 if (event->ch != 0) {
1593 if (event->ch == key[i].evkey.ch) {
1594 key[i].func();
1595 return;
1597 } else if (event->key != 0) {
1598 if (event->key == key[i].evkey.key) {
1599 key[i].func();
1600 return;
1605 /* bookmarks */
1606 b = findbm(event->ch);
1607 if (b < 0)
1608 return;
1609 rmwatch(cpane);
1610 strncpy(cpane->dirn, bmarks[b].path, MAX_P);
1611 cpane->firstrow = 0;
1612 cpane->parent_row = 1;
1613 cpane->hdir = 1;
1614 if (listdir(AddHi) < 0)
1615 print_error(strerror(errno));
1618 static void
1619 start_ev(void)
1621 struct tb_event ev;
1623 for (;;) {
1624 int t = tb_peek_event(&ev, 2000);
1625 if (t < 0) {
1626 tb_shutdown();
1627 return;
1630 if (t == 1) /* keyboard event */
1631 grabkeys(&ev, nkeys, nkeyslen);
1632 else if (t == 2) /* resize event */
1633 t_resize();
1634 else if (t == 0) /* filesystem event */
1635 if (read_events() > 0) { /* TODO need refactoring */
1636 if (cpane == &pane_l) {
1637 cpane = &pane_r;
1638 if (listdir(NoHi) < 0)
1639 print_error(strerror(errno));
1640 cpane = &pane_l;
1641 if (listdir(AddHi) < 0)
1642 print_error(strerror(errno));
1643 } else if (cpane == &pane_r) {
1644 cpane = &pane_l;
1645 if (listdir(NoHi) < 0)
1646 print_error(strerror(errno));
1647 cpane = &pane_r;
1648 if (listdir(AddHi) < 0)
1649 print_error(strerror(errno));
1653 tb_present();
1654 continue;
1656 tb_shutdown();
1659 static void
1660 refresh_pane(void)
1662 size_t y, dyn_max, start_from;
1663 int width;
1664 width = (twidth / 2) - 4;
1665 Cpair col;
1667 y = 1;
1668 start_from = cpane->firstrow;
1669 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1671 /* print each entry in directory */
1672 while (start_from < dyn_max) {
1673 get_hicol(&col, cpane->direntr[start_from].mode);
1674 print_row(cpane, start_from, col);
1675 start_from++;
1676 y++;
1679 if (cpane->dirc > 0)
1680 print_info(NULL);
1681 else
1682 clear_status();
1684 /* print current directory title */
1685 cpane->dircol.fg |= TB_BOLD;
1686 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1689 static void
1690 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1692 int i;
1693 char *tmpfull;
1694 struct stat status;
1697 #define ADD_ENTRY \
1698 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1699 strncpy(cpane->direntr[i].name, tmpfull, MAX_N); \
1700 if (lstat(tmpfull, &status) == 0) { \
1701 cpane->direntr[i].size = status.st_size; \
1702 cpane->direntr[i].mode = status.st_mode; \
1703 cpane->direntr[i].group = status.st_gid; \
1704 cpane->direntr[i].user = status.st_uid; \
1705 cpane->direntr[i].dt = status.st_mtime; \
1706 }i++;free(tmpfull);
1708 i = 0;
1709 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1710 while ((entry = readdir(dir)) != 0) {
1711 if ((strncmp(entry->d_name, ".", 2) == 0 ||
1712 strncmp(entry->d_name, "..", 3) == 0))
1713 continue;
1715 if (filter == NULL) {
1716 ADD_ENTRY
1717 } else if (filter != NULL) {
1718 if (strcasestr(entry->d_name, filter) != NULL) {
1719 ADD_ENTRY
1725 cpane->dirc = i;
1728 static int
1729 listdir(int hi)
1731 DIR *dir;
1732 struct dirent *entry;
1733 int width;
1734 int filtercount = 0;
1735 size_t oldc = cpane->dirc;
1737 width = (twidth / 2) - 4;
1738 cpane->dirc = 0;
1740 dir = opendir(cpane->dirn);
1741 if (dir == NULL)
1742 return -1;
1744 /* get content and filter sum */
1745 while ((entry = readdir(dir)) != 0) {
1746 if (cpane->filter != NULL) {
1747 if (strcasestr(entry->d_name, cpane->filter) != NULL)
1748 filtercount++;
1749 } else { /* no filter */
1750 cpane->dirc++;
1754 if (cpane->filter == NULL) {
1755 clear_pane();
1756 cpane->dirc -= 2;
1759 if (cpane->filter != NULL) {
1760 if (filtercount > 0) {
1761 cpane->dirc = filtercount;
1762 clear_pane();
1763 cpane->hdir = 1;
1764 } else if (filtercount == 0) {
1765 if (closedir(dir) < 0)
1766 return -1;
1767 cpane->dirc = oldc;
1768 return -1;
1772 /* print current directory title */
1773 cpane->dircol.fg |= TB_BOLD;
1774 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1776 if (cpane->filter == NULL) /* dont't watch when filtering */
1777 if (addwatch() < 0)
1778 print_error("can't add watch");
1780 /* empty directory */
1781 if (cpane->dirc == 0) {
1782 clear_status();
1783 if (closedir(dir) < 0)
1784 return -1;
1785 return 0;
1788 rewinddir(dir); /* reset position */
1789 set_direntr(entry, dir, cpane->filter); /* create array of entries */
1790 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1791 refresh_pane();
1793 if (hi == AddHi)
1794 add_hi(cpane, cpane->hdir - 1);
1796 if (closedir(dir) < 0)
1797 return -1;
1798 return 0;
1801 static void
1802 t_resize(void)
1804 /* TODO need refactoring */
1805 tb_clear();
1806 draw_frame();
1807 pane_r.dirx = (twidth / 2) + 2;
1809 if (cpane == &pane_l) {
1810 cpane = &pane_r;
1811 refresh_pane();
1812 cpane = &pane_l;
1813 refresh_pane();
1814 if (cpane->dirc > 0)
1815 add_hi(&pane_l, pane_l.hdir - 1);
1816 } else if (cpane == &pane_r) {
1817 cpane = &pane_l;
1818 refresh_pane();
1819 cpane = &pane_r;
1820 refresh_pane();
1821 if (cpane->dirc > 0)
1822 add_hi(&pane_r, pane_r.hdir - 1);
1825 tb_present();
1828 static void
1829 get_editor(void)
1831 editor[0] = getenv("EDITOR");
1832 editor[1] = NULL;
1834 if (editor[0] == NULL)
1835 editor[0] = fed;
1838 static void
1839 set_panes(void)
1841 char *home;
1842 char cwd[MAX_P];
1844 home = getenv("HOME");
1845 if (home == NULL)
1846 home = "/";
1847 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1848 strncpy(cwd, home, MAX_P);
1850 pane_l.pane_id = 0;
1851 pane_l.dirx = 2;
1852 pane_l.dircol = cpanell;
1853 pane_l.firstrow = 0;
1854 pane_l.direntr = ecalloc(0, sizeof(Entry));
1855 strncpy(pane_l.dirn, cwd, MAX_P);
1856 pane_l.hdir = 1;
1857 pane_l.inotify_wd = -1;
1858 pane_l.parent_row = 1;
1860 pane_r.pane_id = 1;
1861 pane_r.dirx = (twidth / 2) + 2;
1862 pane_r.dircol = cpanelr;
1863 pane_r.firstrow = 0;
1864 pane_r.direntr = ecalloc(0, sizeof(Entry));
1865 strncpy(pane_r.dirn, home, MAX_P);
1866 pane_r.hdir = 1;
1867 pane_r.inotify_wd = -1;
1868 pane_r.parent_row = 1;
1871 static void
1872 draw_frame(void)
1874 int i;
1875 theight = tb_height();
1876 twidth = tb_width();
1877 scrheight = theight - 2;
1879 /* 2 horizontal lines */
1880 for (i = 1; i < twidth - 1; ++i) {
1881 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1882 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1885 /* 4 vertical lines */
1886 for (i = 1; i < theight - 1; ++i) {
1887 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1888 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1889 cframe.bg);
1890 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1891 cframe.bg);
1892 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1895 /* 4 corners */
1896 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1897 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1898 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1899 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1901 /* 2 middel top and bottom */
1902 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1903 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1906 static void
1907 start(void)
1909 if (tb_init() != 0)
1910 die("tb_init");
1911 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1912 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1913 die("output error");
1915 draw_frame();
1916 set_panes();
1917 get_editor();
1918 if (fsev_init() < 0)
1919 print_error(strerror(errno));
1920 cpane = &pane_r;
1921 if (listdir(NoHi) < 0)
1922 print_error(strerror(errno));
1923 cpane = &pane_l;
1924 if (listdir(AddHi) < 0)
1925 print_error(strerror(errno));
1926 tb_present();
1927 start_ev();
1931 main(int argc, char *argv[])
1933 #ifdef __OpenBSD__
1934 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1935 NULL) == -1)
1936 die("pledge");
1937 #endif /* __OpenBSD__ */
1938 if (argc == 1)
1939 start();
1940 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1941 die("sfm-" VERSION);
1942 else
1943 die("usage: sfm [-v]");
1944 return 0;