[ref] mvbk() return if cwd is /
[sfm.git] / sfm.c
blob884283759eec06f3962a260f18457824f36b246d
1 /* See LICENSE file for copyright and license details. */
3 #if defined(__linux__)
4 #define _GNU_SOURCE
5 #endif
6 #include <sys/types.h>
7 #include <sys/resource.h>
8 #include <sys/stat.h>
9 #include <sys/time.h>
10 #include <sys/wait.h>
11 #if defined(__linux__)
12 #include <sys/inotify.h>
13 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
14 defined(__OpenBSD__) || defined(__APPLE__)
15 #include <sys/event.h>
16 #endif
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <grp.h>
22 #include <pwd.h>
23 #include <stdarg.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <libgen.h>
32 #include "termbox.h"
33 #include "util.h"
35 /* macros */
36 #define MAX_P 4096
37 #define MAX_N 255
38 #define MAX_USRI 32
39 #define MAX_EXT 4
40 #define CURSOR_NAME cpane->direntr[cpane->hdir - 1].name
42 /* enums */
43 enum { AddHi, NoHi }; /* add highlight in listdir */
45 /* typedef */
46 typedef struct {
47 char name[MAX_N];
48 gid_t group;
49 mode_t mode;
50 off_t size;
51 time_t td;
52 uid_t user;
53 } Entry;
55 typedef struct {
56 uint16_t fg;
57 uint16_t bg;
58 } Cpair;
60 typedef struct {
61 int pane_id;
62 char dirn[MAX_P]; // dir name cwd
63 char *filter;
64 Entry *direntr; // dir entries
65 int dirx; // pane cwd x pos
66 int dirc; // dir entries sum
67 int hdir; // highlighted dir
68 int firstrow;
69 int parent_firstrow;
70 int parent_row; // FIX
71 Cpair dircol;
72 int inotify_wd;
73 int event_fd;
74 } Pane;
76 typedef struct {
77 uint32_t ch;
78 char path[MAX_P];
79 } Bookmark;
81 typedef struct {
82 const char **ext;
83 size_t exlen;
84 const void *v;
85 } Rule;
87 typedef union {
88 uint16_t key; /* one of the TB_KEY_* constants */
89 uint32_t ch; /* unicode character */
90 } Evkey;
92 typedef struct {
93 const Evkey evkey;
94 void (*func)(void);
95 } Key;
97 /* function declarations */
98 static void print_tb(const char *, int, int, uint16_t, uint16_t);
99 static void printf_tb(int, int, Cpair, const char *, ...);
100 static void print_status(Cpair, const char *, ...);
101 static void print_xstatus(char, int);
102 static void print_error(char *);
103 static void print_prompt(char *);
104 static void print_info(void);
105 static void print_row(Pane *, size_t, Cpair);
106 static void clear(int, int, int, uint16_t);
107 static void clear_status(void);
108 static void clear_pane(void);
109 static void add_hi(Pane *, size_t);
110 static void rm_hi(Pane *, size_t);
111 static int check_dir(char *);
112 static mode_t chech_execf(mode_t);
113 static int sort_name(const void *const, const void *const);
114 static void get_dirp(char *);
115 static char *get_ext(char *);
116 static int get_fdt(char *, size_t, time_t);
117 static char *get_fgrp(gid_t, size_t);
118 static char *get_finfo(Entry *);
119 static char *get_fperm(mode_t);
120 static char *get_fsize(off_t);
121 static char *get_fullpath(char *, char *);
122 static char *get_fusr(uid_t, size_t);
123 static void get_dirsize(char *, off_t *);
124 static void get_hicol(Cpair *, mode_t);
125 static int delent(char *);
126 static void calcdir(void);
127 static void crnd(void);
128 static void crnf(void);
129 static void delfd(void);
130 static void mvbk(void);
131 static void mvbtm(void);
132 static void mvdwn(void);
133 static void mvdwns(void);
134 static void mvfor(void);
135 static void mvmid(void);
136 static void mvtop(void);
137 static void mvup(void);
138 static void mvups(void);
139 static void scrdwn(void);
140 static void scrdwns(void);
141 static void scrup(void);
142 static void scrups(void);
143 static int get_usrinput(char*, size_t, const char*, ...);
144 static int frules(char *);
145 static int spawn(const void *, char *);
146 static int opnf(char *);
147 static int fsev_init(void);
148 static int addwatch(void);
149 static int read_events(void);
150 static void rmwatch(Pane *);
151 static void fsev_shdn(void);
152 static ssize_t findbm(uint32_t);
153 static void start_filter(void);
154 static void start_vmode(void);
155 static void exit_vmode(void);
156 static void selup(void);
157 static void seldwn(void);
158 static void selall(void);
159 static void selref(void);
160 static void selynk(void);
161 static void selcalc(void);
162 static void paste(void);
163 static void selmv(void);
164 static void seldel(void);
165 static void init_files(void);
166 static void free_files(void);
167 static void yank(void);
168 static void rname(void);
169 static void switch_pane(void);
170 static void quit(void);
171 static void grabkeys(struct tb_event*, Key*, size_t);
172 static void start_ev(void);
173 static void refresh_pane(void);
174 static void set_direntr(struct dirent *, DIR *, char *);
175 static int listdir(int);
176 static void t_resize(void);
177 static void set_panes(void);
178 static void draw_frame(void);
179 static void start(void);
181 /* global variables */
182 static Pane pane_r, pane_l, *cpane;
183 static char *editor[2];
184 static char fed[] = "vi";
185 static int theight, twidth, scrheight;
186 static size_t selection_size = 0;
187 static char yank_file[MAX_P];
188 static int *selection;
189 static int cont_vmode = 0;
190 static char **selected_files;
191 #if defined _SYS_INOTIFY_H
192 static int inotify_fd;
193 #elif defined _SYS_EVENT_H_
194 static int kq;
195 struct kevent evlist[2]; /* events we want to monitor */
196 struct kevent chlist[2]; /* events that were triggered */
197 static struct timespec gtimeout;
198 #endif
200 /* configuration, allows nested code to access above variables */
201 #include "config.h"
203 /* function implementations */
204 static void
205 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
207 while (*str != '\0') {
208 uint32_t uni = 0;
209 str += tb_utf8_char_to_unicode(&uni, str);
210 tb_change_cell(x, y, uni, fg, bg);
211 x++;
215 static void
216 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
218 char buf[4096];
219 va_list vl;
220 va_start(vl, fmt);
221 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
222 va_end(vl);
223 print_tb(buf, x, y, col.fg, col.bg);
226 static void
227 print_status(Cpair col, const char *fmt, ...)
229 char buf[256];
230 va_list vl;
231 va_start(vl, fmt);
232 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
233 va_end(vl);
234 clear_status();
235 print_tb(buf, 1, theight - 1, col.fg, col.bg);
238 static void
239 print_xstatus(char c, int x)
241 uint32_t uni = 0;
242 (void)tb_utf8_char_to_unicode(&uni, &c);
243 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
246 static void
247 print_error(char *errmsg)
249 print_status(cerr, errmsg);
252 static void
253 print_prompt(char *prompt)
255 print_status(cprompt, prompt);
258 static void
259 print_info(void)
261 char *fileinfo;
262 fileinfo = get_finfo(&cpane->direntr[cpane->hdir - 1]);
263 print_status(cstatus, "%d/%d %s", cpane->hdir, cpane->dirc, fileinfo);
264 free(fileinfo);
267 static void
268 print_row(Pane *pane, size_t entpos, Cpair col)
270 int x, y;
271 char *result;
272 char buf[MAX_P];
273 char lnk_full[MAX_P];
274 int width;
276 width = (twidth / 2) - 4;
277 result = basename(pane->direntr[entpos].name);
278 x = pane->dirx;
279 y = entpos - cpane->firstrow + 1;
281 if (S_ISLNK(pane->direntr[entpos].mode) &&
282 realpath(pane->direntr[entpos].name, buf) != NULL) {
283 (void)snprintf(lnk_full, MAX_N, "%s -> %s",
284 result, buf);
285 result = lnk_full;
288 printf_tb(x, y, col, "%*.*s", ~width, width, result);
291 static void
292 clear(int sx, int ex, int y, uint16_t bg)
294 /* clear line from to */
295 /* x = line number vertical */
296 /* y = column number horizontal */
297 int i;
298 for (i = sx; i < ex; i++) {
299 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
303 static void
304 clear_status(void)
306 clear(1, twidth - 1, theight - 1, cstatus.bg);
309 static void
310 clear_pane(void)
312 int i, ex, y;
313 y = 0, i = 0, ex = 0;
315 if (cpane->pane_id == pane_l.pane_id)
316 ex = (twidth / 2) - 1;
317 else if (cpane->pane_id == pane_r.pane_id)
318 ex = twidth - 1;
320 while (i < scrheight) {
321 clear(cpane->dirx, ex, y, TB_DEFAULT);
322 i++;
323 y++;
326 /* draw top line */
327 for (y = cpane->dirx; y < ex; ++y) {
328 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
332 static void
333 add_hi(Pane *pane, size_t entpos)
335 Cpair col;
336 get_hicol(&col, pane->direntr[entpos].mode);
337 col.fg |= TB_REVERSE | TB_BOLD;
338 col.bg |= TB_REVERSE;
339 print_row(pane, entpos, col);
342 static void
343 rm_hi(Pane *pane, size_t entpos)
345 Cpair col;
346 get_hicol(&col, pane->direntr[entpos].mode);
347 print_row(pane, entpos, col);
350 static int
351 check_dir(char *path)
353 DIR *dir;
354 dir = opendir(path);
356 if (dir == NULL) {
357 if (errno == ENOTDIR) {
358 return 1;
359 } else {
360 return -1;
364 if (closedir(dir) < 0)
365 return -1;
367 return 0;
370 static mode_t
371 chech_execf(mode_t mode)
373 if (S_ISREG(mode))
374 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
375 return 0;
378 static int
379 sort_name(const void *const A, const void *const B)
381 int result;
382 mode_t data1 = (*(Entry *)A).mode;
383 mode_t data2 = (*(Entry *)B).mode;
385 if (data1 < data2) {
386 return -1;
387 } else if (data1 == data2) {
388 result = strncmp((*(Entry *)A).name, (*(Entry *)B).name, MAX_N);
389 return result;
390 } else {
391 return 1;
395 static void
396 get_dirp(char *cdir)
398 int counter, len, i;
400 counter = 0;
401 len = strnlen(cdir, MAX_P);
402 if (len == 1)
403 return;
405 for (i = len - 1; i > 1; i--) {
406 if (cdir[i] == '/')
407 break;
408 else
409 counter++;
412 cdir[len-counter-1] = '\0';
415 static char *
416 get_ext(char *str)
418 char *ext;
419 char dot;
420 size_t counter, len, i;
422 dot = '.';
423 counter = 0;
424 len = strnlen(str, MAX_N);
426 for (i = len - 1; i > 0; i--) {
427 if (str[i] == dot) {
428 break;
429 } else {
430 counter++;
434 ext = ecalloc(MAX_EXT + 1, sizeof(char));
435 strncpy(ext, &str[len - counter], MAX_EXT);
436 ext[MAX_EXT] = '\0';
437 return ext;
440 static int
441 get_fdt(char *result, size_t reslen, time_t status)
443 struct tm lt;
444 localtime_r(&status, &lt);
445 return strftime(result, reslen, dtfmt, &lt);
448 static char *
449 get_fgrp(gid_t status, size_t len)
451 char *result;
452 struct group *gr;
454 result = ecalloc(len, sizeof(char));
455 gr = getgrgid(status);
456 if (gr == NULL)
457 (void)snprintf(result, len - 1, "%u", status);
458 else
459 strncpy(result, gr->gr_name, len - 1);
461 return result;
464 static char *
465 get_finfo(Entry *cursor)
467 char *sz, *rst, *ur, *gr, *td, *prm;
468 size_t szlen, prmlen, urlen, grlen, tdlen, rstlen;
470 szlen = 9;
471 prmlen = 11;
472 urlen = grlen = tdlen = 32;
473 rstlen = szlen + prmlen + urlen + grlen + tdlen;
474 rst = ecalloc(rstlen, sizeof(char));
476 if (show_perm == 1) {
477 prm = get_fperm(cursor->mode);
478 strncpy(rst, prm, prmlen);
479 strcat(rst, " ");
480 free(prm);
483 if (show_ug == 1) {
484 ur = get_fusr(cursor->user, urlen);
485 gr = get_fgrp(cursor->group, grlen);
486 strncat(rst, ur, urlen);
487 strcat(rst, ":");
488 strncat(rst, gr, grlen);
489 strcat(rst, " ");
490 free(ur);
491 free(gr);
494 if (show_dt == 1) {
495 td = ecalloc(tdlen, sizeof(char));
496 if (get_fdt(td, tdlen, cursor->td) > 0) {
497 strncat(rst, td, tdlen);
498 strcat(rst, " ");
500 free(td);
503 if (show_size == 1 && S_ISREG(cursor->mode)) {
504 sz = get_fsize(cursor->size);
505 strncat(rst, sz, szlen);
506 free(sz);
509 return rst;
512 static char *
513 get_fperm(mode_t mode)
515 char *buf;
516 size_t i;
518 const char chars[] = "rwxrwxrwx";
519 buf = ecalloc(11, sizeof(char));
521 if (S_ISDIR(mode))
522 buf[0] = 'd';
523 else if (S_ISREG(mode))
524 buf[0] = '-';
525 else if (S_ISLNK(mode))
526 buf[0] = 'l';
527 else if (S_ISBLK(mode))
528 buf[0] = 'b';
529 else if (S_ISCHR(mode))
530 buf[0] = 'c';
531 else if (S_ISFIFO(mode))
532 buf[0] = 'p';
533 else if (S_ISSOCK(mode))
534 buf[0] = 's';
535 else
536 buf[0] = '?';
538 for (i = 1; i < 10; i++) {
539 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
541 buf[10] = '\0';
543 return buf;
546 static char *
547 get_fsize(off_t size)
549 char *result; /* need to be freed */
550 char unit;
551 int result_len;
552 int counter;
554 counter = 0;
555 result_len = 6; /* 9999X/0 */
556 result = ecalloc(result_len, sizeof(char));
558 while (size >= 1000) {
559 size /= 1024;
560 ++counter;
563 switch (counter) {
564 case 0:
565 unit = 'B';
566 break;
567 case 1:
568 unit = 'K';
569 break;
570 case 2:
571 unit = 'M';
572 break;
573 case 3:
574 unit = 'G';
575 break;
576 case 4:
577 unit = 'T';
578 break;
579 default:
580 unit = '?';
583 if (snprintf(result, result_len, "%ld%c", size, unit) < 0)
584 strncat(result, "???", result_len);
586 return result;
589 static char *
590 get_fullpath(char *first, char *second)
592 char *full_path;
594 full_path = ecalloc(MAX_P, sizeof(char));
596 if (strncmp(first, "/", MAX_P) == 0)
597 (void)snprintf(full_path, MAX_P, "/%s", second);
598 else
599 (void)snprintf(full_path, MAX_P, "%s/%s", first, second);
601 return full_path;
604 static char *
605 get_fusr(uid_t status, size_t len)
607 char *result;
608 struct passwd *pw;
610 result = ecalloc(len, sizeof(char));
611 pw = getpwuid(status);
612 if (pw == NULL)
613 (void)snprintf(result, len - 1, "%u", status);
614 else
615 strncpy(result, pw->pw_name, len - 1);
617 return result;
620 static void
621 get_dirsize(char *fullpath, off_t *fullsize)
623 DIR *dir;
624 char *ent_full;
625 mode_t mode;
626 struct dirent *entry;
627 struct stat status;
629 dir = opendir(fullpath);
630 if (dir == NULL) {
631 return;
634 while ((entry = readdir(dir)) != 0) {
635 if ((strncmp(entry->d_name, ".", 2) == 0 ||
636 strncmp(entry->d_name, "..", 3) == 0))
637 continue;
639 ent_full = get_fullpath(fullpath, entry->d_name);
640 if (lstat(ent_full, &status) == 0) {
641 mode = status.st_mode;
642 if (S_ISDIR(mode)) {
643 get_dirsize(ent_full, fullsize);
644 free(ent_full);
645 } else {
646 *fullsize += status.st_size;
647 free(ent_full);
652 closedir(dir);
653 clear_status();
656 static void
657 get_hicol(Cpair *col, mode_t mode)
659 *col = cfile;
660 if (S_ISDIR(mode))
661 *col = cdir;
662 else if (S_ISLNK(mode))
663 *col = cother;
664 else if (chech_execf(mode) > 0)
665 *col = cexec;
668 static int
669 delent(char *fullpath)
671 char *inp_conf;
672 int conf_len = 4;
673 char conf[] = "yes";
675 inp_conf = ecalloc(conf_len, sizeof(char));
676 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
677 (strncmp(inp_conf, conf, conf_len) != 0)) {
678 free(inp_conf);
679 return 1; /* canceled by user or wrong inp_conf */
681 free(inp_conf);
683 return spawn(rm_cmd, fullpath);
686 static void
687 calcdir(void)
689 if (!S_ISDIR(cpane->direntr[cpane->hdir - 1].mode))
690 return;
692 off_t *fullsize;
693 char *csize;
694 char *result;
696 fullsize = ecalloc(50, sizeof(off_t));
697 get_dirsize(CURSOR_NAME, fullsize);
698 csize = get_fsize(*fullsize);
699 result = get_finfo(&cpane->direntr[cpane->hdir - 1]);
701 clear_status();
702 print_status(cstatus, "%d/%d %s%s",
703 cpane->hdir, cpane->dirc, result, csize);
704 free(csize);
705 free(fullsize);
706 free(result);
709 static void
710 crnd(void)
712 char *user_input, *path;
714 user_input = ecalloc(MAX_USRI, sizeof(char));
715 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
716 free(user_input);
717 return;
720 path = ecalloc(MAX_P, sizeof(char));
721 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
722 free(user_input);
723 free(path);
724 return;
727 if (mkdir(path, ndir_perm) < 0)
728 print_error(strerror(errno));
730 free(user_input);
731 free(path);
734 static void
735 crnf(void)
737 char *user_input, *path;
738 int rf;
740 user_input = ecalloc(MAX_USRI, sizeof(char));
741 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
742 free(user_input);
743 return;
746 path = ecalloc(MAX_P, sizeof(char));
747 if (snprintf(path, MAX_P, "%s/%s", cpane->dirn, user_input) < 0) {
748 free(user_input);
749 free(path);
750 return;
753 rf = open(path, O_CREAT | O_EXCL, nf_perm);
755 if (rf < 0)
756 print_error(strerror(errno));
757 else
758 if (close(rf) < 0)
759 print_error(strerror(errno));
761 free(user_input);
762 free(path);
765 static void
766 delfd(void)
768 switch (delent(CURSOR_NAME)) {
769 case -1:
770 print_error(strerror(errno));
771 break;
772 case 0:
773 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
774 cpane->hdir--;
775 break;
779 static void
780 mvbk(void)
782 if (cpane->dirn[0] == '/' && cpane->dirn[1] == '\0') { /* cwd = / */
783 return;
786 get_dirp(cpane->dirn);
787 if (check_dir(cpane->dirn) < 0) {
788 print_error(strerror(errno));
789 return;
792 rmwatch(cpane);
793 cpane->firstrow = cpane->parent_firstrow;
794 cpane->hdir = cpane->parent_row;
795 if (listdir(AddHi) < 0)
796 print_error(strerror(errno));
797 cpane->parent_firstrow = 0;
798 cpane->parent_row = 1;
801 static void
802 mvbtm(void)
804 if (cpane->dirc < 1)
805 return;
806 if (cpane->dirc > scrheight) {
807 rm_hi(cpane, cpane->hdir - 1);
808 cpane->hdir = cpane->dirc;
809 cpane->firstrow = cpane->dirc - scrheight + 1;
810 refresh_pane();
811 add_hi(cpane, cpane->hdir - 1);
812 } else {
813 rm_hi(cpane, cpane->hdir - 1);
814 cpane->hdir = cpane->dirc;
815 add_hi(cpane, cpane->hdir - 1);
817 print_info();
820 static void
821 mvdwn(void)
823 if (cpane->dirc < 1)
824 return;
825 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
826 rm_hi(cpane, cpane->hdir - 1);
827 cpane->hdir++;
828 add_hi(cpane, cpane->hdir - 1);
829 } else {
830 mvdwns(); /* scroll */
832 print_info();
835 static void
836 mvdwns(void)
838 int real;
839 real = cpane->hdir - 1 - cpane->firstrow;
841 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
842 cpane->firstrow++;
843 rm_hi(cpane, cpane->hdir - 1);
844 cpane->hdir++;
845 refresh_pane();
846 add_hi(cpane, cpane->hdir - 1);
847 } else if (cpane->hdir < cpane->dirc) {
848 rm_hi(cpane, cpane->hdir - 1);
849 cpane->hdir++;
850 add_hi(cpane, cpane->hdir - 1);
854 static void
855 mvfor(void)
857 rmwatch(cpane);
858 if (cpane->dirc < 1)
859 return;
860 int s;
862 switch (check_dir(CURSOR_NAME)) {
863 case 0:
864 strncpy(cpane->dirn, CURSOR_NAME, MAX_P);
865 cpane->parent_row = cpane->hdir;
866 cpane->parent_firstrow = cpane->firstrow;
867 cpane->hdir = 1;
868 cpane->firstrow = 0;
869 if (listdir(AddHi) < 0)
870 print_error(strerror(errno));
871 break;
872 case 1: /* not a directory open file */
873 tb_shutdown();
874 s = opnf(CURSOR_NAME);
875 if (tb_init() != 0)
876 die("tb_init");
877 t_resize();
878 if (s < 0)
879 print_error("process failed non-zero exit");
880 break;
881 case -1: /* failed to open directory */
882 print_error(strerror(errno));
886 static void
887 mvmid(void)
889 if (cpane->dirc < 1)
890 return;
891 rm_hi(cpane, cpane->hdir - 1);
892 if (cpane->dirc < scrheight / 2)
893 cpane->hdir = (cpane->dirc + 1) / 2;
894 else
895 cpane->hdir = (scrheight / 2) + cpane->firstrow;
896 add_hi(cpane, cpane->hdir - 1);
897 print_info();
900 static void
901 mvtop(void)
903 if (cpane->dirc < 1)
904 return;
905 if (cpane->dirc > scrheight) {
906 rm_hi(cpane, cpane->hdir - 1);
907 cpane->hdir = 1;
908 cpane->firstrow = 0;
909 refresh_pane();
910 add_hi(cpane, cpane->hdir - 1);
911 } else {
912 rm_hi(cpane, cpane->hdir - 1);
913 cpane->hdir = 1;
914 add_hi(cpane, cpane->hdir - 1);
915 print_info();
919 static void
920 mvup(void)
922 if (cpane->dirc < 1)
923 return;
924 if (cpane->dirc < scrheight && cpane->hdir > 1) {
925 rm_hi(cpane, cpane->hdir - 1);
926 cpane->hdir--;
927 add_hi(cpane, cpane->hdir - 1);
928 } else {
929 mvups(); /* scroll */
931 print_info();
934 static void
935 mvups(void)
937 size_t real;
938 real = cpane->hdir - 1 - cpane->firstrow;
940 if (cpane->firstrow > 0 && real < 1 + scrsp) {
941 cpane->firstrow--;
942 rm_hi(cpane, cpane->hdir - 1);
943 cpane->hdir--;
944 refresh_pane();
945 add_hi(cpane, cpane->hdir - 1);
946 } else if (cpane->hdir > 1) {
947 rm_hi(cpane, cpane->hdir - 1);
948 cpane->hdir--;
949 add_hi(cpane, cpane->hdir - 1);
953 static void
954 scrdwn(void)
956 if (cpane->dirc < 1)
957 return;
958 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
959 if (cpane->hdir < cpane->dirc - scrmv) {
960 rm_hi(cpane, cpane->hdir - 1);
961 cpane->hdir += scrmv;
962 add_hi(cpane, cpane->hdir - 1);
963 } else {
964 mvbtm();
966 } else {
967 scrdwns();
969 print_info();
972 static void
973 scrdwns(void)
975 int real, dynmv;
977 real = cpane->hdir - cpane->firstrow;
978 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
980 if (real + scrmv + 1 > scrheight &&
981 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
982 cpane->firstrow += dynmv;
983 rm_hi(cpane, cpane->hdir - 1);
984 cpane->hdir += scrmv;
985 refresh_pane();
986 add_hi(cpane, cpane->hdir - 1);
987 } else {
988 if (cpane->hdir < cpane->dirc - scrmv - 1) {
989 rm_hi(cpane, cpane->hdir - 1);
990 cpane->hdir += scrmv;
991 add_hi(cpane, cpane->hdir - 1);
992 } else {
993 mvbtm();
998 static void
999 scrup(void)
1001 if (cpane->dirc < 1)
1002 return;
1003 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1004 if (cpane->hdir > scrmv) {
1005 rm_hi(cpane, cpane->hdir - 1);
1006 cpane->hdir = cpane->hdir - scrmv;
1007 add_hi(cpane, cpane->hdir - 1);
1008 print_info();
1009 } else {
1010 mvtop();
1012 } else {
1013 scrups();
1017 static void
1018 scrups(void)
1020 int real, dynmv;
1021 real = cpane->hdir - cpane->firstrow;
1022 dynmv = MIN(cpane->firstrow, scrmv);
1024 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1025 cpane->firstrow -= dynmv;
1026 rm_hi(cpane, cpane->hdir - 1);
1027 cpane->hdir -= scrmv;
1028 refresh_pane();
1029 add_hi(cpane, cpane->hdir - 1);
1030 } else {
1031 if (cpane->hdir > scrmv + 1) {
1032 rm_hi(cpane, cpane->hdir - 1);
1033 cpane->hdir -= scrmv;
1034 add_hi(cpane, cpane->hdir - 1);
1035 } else {
1036 mvtop();
1041 static int
1042 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1044 int height = tb_height();
1045 size_t startat;
1046 struct tb_event fev;
1047 size_t counter = (size_t)1;
1048 char empty = ' ';
1049 int x = 0;
1050 int name_size = 0;
1051 char buf[256];
1053 clear_status();
1055 va_list vl;
1056 Cpair col;
1057 col = cprompt;
1058 va_start(vl, fmt);
1059 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1060 va_end(vl);
1061 print_tb(buf, 1, height-1, col.fg, col.bg);
1062 startat = name_size + 1;
1063 tb_set_cursor((int)(startat + 1), height-1);
1064 tb_present();
1066 while (tb_poll_event(&fev) != 0) {
1067 switch (fev.type) {
1068 case TB_EVENT_KEY:
1069 if (fev.key == TB_KEY_ESC) {
1070 tb_set_cursor(-1, -1);
1071 clear_status();
1072 return -1;
1075 if (fev.key == TB_KEY_BACKSPACE ||
1076 fev.key == TB_KEY_BACKSPACE2) {
1077 if (BETWEEN(counter, 2, sout)) {
1078 out[x - 1] = '\0';
1079 counter--;
1080 x--;
1081 print_xstatus(empty, startat + counter);
1082 tb_set_cursor(startat + counter,
1083 theight - 1);
1086 } else if (fev.key == TB_KEY_ENTER) {
1087 tb_set_cursor(-1, -1);
1088 out[counter - 1] = '\0';
1089 return 0;
1091 } else {
1092 if (counter < sout) {
1093 print_xstatus((char)fev.ch,
1094 (startat + counter));
1095 out[x] = (char)fev.ch;
1096 tb_set_cursor((startat + counter + 1),
1097 theight - 1);
1098 counter++;
1099 x++;
1103 tb_present();
1104 break;
1106 default:
1107 return -1;
1111 return -1;
1114 static int
1115 frules(char *ex)
1117 size_t c, d;
1119 for (c = 0; c < LEN(rules); c++)
1120 for (d = 0; d < rules[c].exlen; d++)
1121 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1122 return c;
1123 return -1;
1126 static int
1127 spawn(const void *v, char *fn)
1129 int ws, x, argc, fd;
1130 pid_t pid, r;
1132 x = 0;
1133 argc = 0;
1135 /* count args */
1136 while (((char **)v)[x++] != NULL)
1137 argc++;
1139 char *argv[argc + 2];
1140 for ( x = 0; x < argc; x++)
1141 argv[x] = ((char **)v)[x];
1143 argv[argc] = fn;
1144 argv[argc + 1] = NULL;
1146 pid = fork();
1147 switch (pid) {
1148 case -1:
1149 return -1;
1150 case 0:
1151 fd = open("/dev/null",O_WRONLY);
1152 dup2(fd, STDERR_FILENO);
1153 execvp(argv[0], argv);
1154 exit(EXIT_SUCCESS);
1155 close(fd);
1156 default:
1157 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1158 continue;
1159 if (r == -1)
1160 return -1;
1161 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1162 return -1;
1164 return 0;
1167 static int
1168 opnf(char *fn)
1170 char *ex;
1171 int c;
1173 ex = get_ext(fn);
1174 c = frules(ex);
1175 free(ex);
1177 if (c < 0) /* extension not found open in editor */
1178 return spawn(editor, fn);
1179 else
1180 return spawn((char **)rules[c].v, fn);
1183 static int
1184 fsev_init(void)
1186 #if defined _SYS_INOTIFY_H
1187 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1188 if (inotify_fd < 0)
1189 return -1;
1190 #elif defined _SYS_EVENT_H_
1191 kq = kqueue();
1192 if (kq < 0)
1193 return -1;
1194 #endif
1195 return 0;
1198 static int
1199 addwatch(void)
1201 #if defined _SYS_INOTIFY_H
1202 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1203 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1204 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1205 #elif defined _SYS_EVENT_H_
1206 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1207 if (cpane->event_fd < 0)
1208 return cpane->event_fd;
1209 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1210 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1211 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1212 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1213 return 0;
1214 #endif
1217 static int
1218 read_events(void)
1220 #if defined _SYS_INOTIFY_H
1221 char *p;
1222 ssize_t r;
1223 struct inotify_event *event;
1224 const size_t events = 32;
1225 const size_t evbuflen =
1226 events * (sizeof(struct inotify_event) + MAX_N + 1);
1227 char buf[evbuflen];
1229 if (cpane->inotify_wd < 0)
1230 return -1;
1231 r = read(inotify_fd, buf, evbuflen);
1232 if (r <= 0)
1233 return r;
1235 for (p = buf; p < buf + r;) {
1236 event = (struct inotify_event *)p;
1237 if (!event->wd)
1238 break;
1239 if (event->mask) {
1240 return r;
1243 p += sizeof(struct inotify_event) + event->len;
1245 #elif defined _SYS_EVENT_H_
1246 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1247 #endif
1248 return -1;
1251 static void
1252 rmwatch(Pane *pane)
1254 #if defined _SYS_INOTIFY_H
1255 if (pane->inotify_wd >= 0)
1256 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1257 #elif defined _SYS_EVENT_H_
1258 close(pane->event_fd);
1259 return;
1260 #endif
1263 static void
1264 fsev_shdn(void)
1266 rmwatch(&pane_l);
1267 rmwatch(&pane_r);
1268 #if defined _SYS_INOTIFY_H
1269 close(inotify_fd);
1270 #elif defined _SYS_EVENT_H_
1271 close(kq);
1272 #endif
1275 static ssize_t
1276 findbm(uint32_t event)
1278 ssize_t i;
1280 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1281 if (event == bmarks[i].ch) {
1282 if (check_dir(bmarks[i].path) != 0) {
1283 print_error(strerror(errno));
1284 return -1;
1286 return i;
1289 return -1;
1292 static void
1293 start_filter(void)
1295 if (cpane->dirc < 1)
1296 return;
1297 char *user_input;
1298 user_input = ecalloc(MAX_USRI, sizeof(char));
1299 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1300 free(user_input);
1301 return;
1303 cpane->filter = user_input;
1304 if (listdir(AddHi) < 0)
1305 print_error("no match");
1306 cpane->filter = NULL;
1307 free(user_input);
1310 static void
1311 start_vmode(void)
1313 struct tb_event fev;
1314 if (selection != NULL) {
1315 free(selection);
1316 selection = NULL;
1319 selection = ecalloc(cpane->dirc, sizeof(size_t));
1320 selection[0] = cpane->hdir;
1321 cont_vmode = 0;
1322 print_prompt("-- VISUAL --");
1323 tb_present();
1324 while (tb_poll_event(&fev) != 0) {
1325 switch (fev.type) {
1326 case TB_EVENT_KEY:
1327 grabkeys(&fev, skeys, skeyslen);
1328 if (cont_vmode == -1)
1329 return;
1330 tb_present();
1331 break;
1336 static void
1337 exit_vmode(void)
1339 refresh_pane();
1340 add_hi(cpane, cpane->hdir - 1);
1341 cont_vmode = -1;
1344 static void
1345 selup(void)
1347 mvup();
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]);
1354 } else if (index < cpane->dirc) {
1355 selection[index + 1] = 0;
1357 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1358 selref();
1362 static void
1363 seldwn(void)
1365 mvdwn();
1366 print_prompt("-- VISUAL --");
1367 int index = abs(cpane->hdir - selection[0]);
1369 if (cpane->hdir > selection[0]) {
1370 selection[index] = cpane->hdir;
1371 add_hi(cpane, selection[index] - 2);
1372 } else {
1373 selection[index + 1] = 0;
1375 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1376 selref();
1380 static void
1381 selall(void)
1383 int i;
1384 for (i = 0; i < cpane->dirc; i++) {
1385 selection[i] = i + 1;
1387 selref();
1390 static void
1391 selref(void)
1393 int i;
1394 for (i = 0; i < cpane->dirc; i++) {
1395 if (selection[i] < (scrheight + cpane->firstrow) && selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1396 add_hi(cpane, selection[i] - 1);
1401 static void
1402 selcalc(void)
1404 int j;
1405 selection_size = 0;
1407 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1408 if (selection[j] != 0)
1409 selection_size++;
1410 else
1411 break;
1415 static void
1416 free_files(void)
1418 size_t i;
1420 if (selected_files != NULL) {
1421 for (i = 0; i < selection_size; i++) {
1422 free(selected_files[i]);
1423 selected_files[i] = NULL;
1425 free(selected_files);
1426 selected_files = NULL;
1430 static void
1431 init_files(void)
1433 size_t i;
1434 free_files();
1436 selcalc();
1437 selected_files = ecalloc(selection_size, sizeof(char*));
1439 for (i = 0; i < selection_size; i++) {
1440 selected_files[i] = ecalloc(MAX_P, sizeof(char));
1441 strncpy(selected_files[i], cpane->direntr[selection[i] -1].name, MAX_P); /* TODO use pointer */
1445 static void
1446 selynk(void)
1448 init_files();
1449 refresh_pane();
1450 add_hi(cpane, cpane->hdir -1);
1451 print_status(cprompt, "%zu files are yanked", selection_size);
1452 cont_vmode = -1;
1455 static void
1456 seldel(void)
1458 char *inp_conf;
1459 int conf_len = 4;
1460 char conf[] = "yes";
1461 size_t i;
1463 inp_conf = ecalloc(conf_len, sizeof(char));
1464 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1465 (strncmp(inp_conf, conf, conf_len) != 0)) {
1466 free(inp_conf);
1467 return; /* canceled by user or wrong inp_conf */
1469 free(inp_conf);
1471 init_files();
1472 for (i = 0; i < selection_size; i++) {
1473 spawn(rm_cmd, selected_files[i]);
1476 cpane->hdir = cpane->dirc - selection_size;
1477 print_status(cprompt, "%zu files are deleted", selection_size);
1478 free_files();
1479 cont_vmode = -1;
1482 static void
1483 paste(void)
1485 size_t i;
1486 if (strnlen(yank_file, MAX_P) != 0) {
1487 print_status(cprompt, "coping");
1488 if (spawn(cp_cmd, cpane->dirn) != 0)
1489 print_error("coping failed");
1490 else
1491 print_status(cprompt, "file copied");
1492 yank_file[0] = '\0'; /* set yank_file len 0 */
1493 return;
1496 print_error("nothing to paste");
1498 if (selected_files == NULL)
1499 return;
1501 for (i = 0; i < selection_size; i++) {
1502 char *selcp_cmd[] = { "cp", "-r", selected_files[i], cpane->dirn, NULL };
1503 spawn(selcp_cmd,NULL);
1505 print_status(cprompt, "%zu files are copied", selection_size);
1506 free_files();
1509 static void
1510 selmv(void)
1512 size_t i;
1514 if (strnlen(yank_file, MAX_P) != 0) {
1515 print_status(cprompt, "moving");
1516 if (spawn(mv_cmd, cpane->dirn) != 0)
1517 print_error("moving failed");
1518 else
1519 print_status(cprompt, "file moved");
1520 yank_file[0] = '\0'; /* set yank_file len 0 */
1521 return;
1524 print_error("nothing to move");
1526 if (selected_files == NULL)
1527 return;
1529 for (i = 0; i < selection_size; i++) {
1530 char *selmv_cmd[] = { "mv", selected_files[i], cpane->dirn, NULL };
1531 spawn(selmv_cmd,NULL);
1533 print_status(cprompt, "%zu files are moved", selection_size);
1534 free_files();
1537 static void
1538 rname(void)
1540 char new_name[MAX_P];
1541 char *input_name;
1543 input_name = ecalloc(MAX_N, sizeof(char));
1545 if (get_usrinput(input_name, MAX_N, "rename: %s", basename(CURSOR_NAME)) < 0) {
1546 free(input_name);
1547 return;
1550 if (snprintf(new_name, MAX_P, "%s/%s", cpane->dirn, input_name) < 0) {
1551 free(input_name);
1552 print_error(strerror(errno));
1553 return;
1556 char *rename_cmd[] = { "mv", CURSOR_NAME, new_name, NULL };
1557 if (spawn(rename_cmd, NULL) < 0)
1558 print_error(strerror(errno));
1560 free(input_name);
1563 static void
1564 yank(void)
1566 strncpy(yank_file, CURSOR_NAME, MAX_P);
1567 print_status(cprompt, "1 file is yanked", selection_size);
1571 static void
1572 switch_pane(void)
1574 if (cpane->dirc > 0)
1575 rm_hi(cpane, cpane->hdir - 1);
1576 if (cpane == &pane_l)
1577 cpane = &pane_r;
1578 else if (cpane == &pane_r)
1579 cpane = &pane_l;
1580 if (cpane->dirc > 0) {
1581 add_hi(cpane, cpane->hdir - 1);
1582 print_info();
1583 } else {
1584 clear_status();
1588 static void
1589 quit(void)
1591 if (cont_vmode == -1) { /* check if selection was allocated */
1592 free(selection);
1593 if (selected_files != NULL)
1594 free_files();
1596 free(pane_l.direntr);
1597 free(pane_r.direntr);
1598 fsev_shdn();
1599 tb_shutdown();
1600 exit(EXIT_SUCCESS);
1603 static void
1604 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1606 size_t i;
1607 ssize_t b;
1609 for (i = 0; i < max_keys; i++) {
1610 if (event->ch != 0) {
1611 if (event->ch == key[i].evkey.ch) {
1612 key[i].func();
1613 return;
1615 } else if (event->key != 0) {
1616 if (event->key == key[i].evkey.key) {
1617 key[i].func();
1618 return;
1623 /* bookmarks */
1624 b = findbm(event->ch);
1625 if (b < 0)
1626 return;
1627 rmwatch(cpane);
1628 strncpy(cpane->dirn, bmarks[b].path, MAX_P);
1629 cpane->firstrow = 0;
1630 cpane->parent_row = 1;
1631 cpane->hdir = 1;
1632 if (listdir(AddHi) < 0)
1633 print_error(strerror(errno));
1636 static void
1637 start_ev(void)
1639 struct tb_event ev;
1641 for (;;) {
1642 int t = tb_peek_event(&ev, 2000);
1643 if (t < 0) {
1644 tb_shutdown();
1645 return;
1648 if (t == 1) /* keyboard event */
1649 grabkeys(&ev, nkeys, nkeyslen);
1650 else if (t == 2) /* resize event */
1651 t_resize();
1652 else if (t == 0) /* filesystem event */
1653 if (read_events() > 0) { /* TODO need refactoring */
1654 if (cpane == &pane_l) {
1655 cpane = &pane_r;
1656 if (listdir(NoHi) < 0)
1657 print_error(strerror(errno));
1658 cpane = &pane_l;
1659 if (listdir(AddHi) < 0)
1660 print_error(strerror(errno));
1661 } else if (cpane == &pane_r) {
1662 cpane = &pane_l;
1663 if (listdir(NoHi) < 0)
1664 print_error(strerror(errno));
1665 cpane = &pane_r;
1666 if (listdir(AddHi) < 0)
1667 print_error(strerror(errno));
1671 tb_present();
1672 continue;
1674 tb_shutdown();
1677 static void
1678 refresh_pane(void)
1680 size_t y, dyn_max, start_from;
1681 int width;
1682 width = (twidth / 2) - 4;
1683 Cpair col;
1685 y = 1;
1686 start_from = cpane->firstrow;
1687 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1689 /* print each entry in directory */
1690 while (start_from < dyn_max) {
1691 get_hicol(&col, cpane->direntr[start_from].mode);
1692 print_row(cpane, start_from, col);
1693 start_from++;
1694 y++;
1697 if (cpane->dirc > 0)
1698 print_info();
1699 else
1700 clear_status();
1702 /* print current directory title */
1703 cpane->dircol.fg |= TB_BOLD;
1704 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1707 static void
1708 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1710 int i;
1711 char *tmpfull;
1712 struct stat status;
1715 #define ADD_ENTRY \
1716 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1717 strncpy(cpane->direntr[i].name, tmpfull, MAX_N); \
1718 if (lstat(tmpfull, &status) == 0) { \
1719 cpane->direntr[i].size = status.st_size; \
1720 cpane->direntr[i].mode = status.st_mode; \
1721 cpane->direntr[i].group = status.st_gid; \
1722 cpane->direntr[i].user = status.st_uid; \
1723 cpane->direntr[i].td = status.st_mtime; \
1724 }i++;free(tmpfull);
1726 i = 0;
1727 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1728 while ((entry = readdir(dir)) != 0) {
1729 if ((strncmp(entry->d_name, ".", 2) == 0 ||
1730 strncmp(entry->d_name, "..", 3) == 0))
1731 continue;
1733 if (filter == NULL) {
1734 ADD_ENTRY
1735 } else if (filter != NULL) {
1736 if (strcasestr(entry->d_name, filter) != NULL) {
1737 ADD_ENTRY
1743 cpane->dirc = i;
1746 static int
1747 listdir(int hi)
1749 DIR *dir;
1750 struct dirent *entry;
1751 int width;
1752 int filtercount = 0;
1753 size_t oldc = cpane->dirc;
1755 width = (twidth / 2) - 4;
1756 cpane->dirc = 0;
1758 dir = opendir(cpane->dirn);
1759 if (dir == NULL)
1760 return -1;
1762 /* get content and filter sum */
1763 while ((entry = readdir(dir)) != 0) {
1764 if (cpane->filter != NULL) {
1765 if (strcasestr(entry->d_name, cpane->filter) != NULL)
1766 filtercount++;
1767 } else { /* no filter */
1768 cpane->dirc++;
1772 if (cpane->filter == NULL) {
1773 clear_pane();
1774 cpane->dirc -= 2;
1777 if (cpane->filter != NULL) {
1778 if (filtercount > 0) {
1779 cpane->dirc = filtercount;
1780 clear_pane();
1781 cpane->hdir = 1;
1782 } else if (filtercount == 0) {
1783 if (closedir(dir) < 0)
1784 return -1;
1785 cpane->dirc = oldc;
1786 return -1;
1790 /* print current directory title */
1791 cpane->dircol.fg |= TB_BOLD;
1792 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1794 if (cpane->filter == NULL) /* dont't watch when filtering */
1795 if (addwatch() < 0)
1796 print_error("can't add watch");
1798 /* empty directory */
1799 if (cpane->dirc == 0) {
1800 clear_status();
1801 if (closedir(dir) < 0)
1802 return -1;
1803 return 0;
1806 rewinddir(dir); /* reset position */
1807 set_direntr(entry, dir, cpane->filter); /* create array of entries */
1808 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1809 refresh_pane();
1811 if (hi == AddHi)
1812 add_hi(cpane, cpane->hdir - 1);
1814 if (closedir(dir) < 0)
1815 return -1;
1816 return 0;
1819 static void
1820 t_resize(void)
1822 /* TODO need refactoring */
1823 tb_clear();
1824 draw_frame();
1825 pane_r.dirx = (twidth / 2) + 2;
1827 if (cpane == &pane_l) {
1828 cpane = &pane_r;
1829 refresh_pane();
1830 cpane = &pane_l;
1831 refresh_pane();
1832 if (cpane->dirc > 0)
1833 add_hi(&pane_l, pane_l.hdir - 1);
1834 } else if (cpane == &pane_r) {
1835 cpane = &pane_l;
1836 refresh_pane();
1837 cpane = &pane_r;
1838 refresh_pane();
1839 if (cpane->dirc > 0)
1840 add_hi(&pane_r, pane_r.hdir - 1);
1843 tb_present();
1846 static void
1847 get_editor(void)
1849 editor[0] = getenv("EDITOR");
1850 editor[1] = NULL;
1852 if (editor[0] == NULL)
1853 editor[0] = fed;
1856 static void
1857 set_panes(void)
1859 char *home;
1860 char cwd[MAX_P];
1862 home = getenv("HOME");
1863 if (home == NULL)
1864 home = "/";
1865 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1866 strncpy(cwd, home, MAX_P);
1868 pane_l.pane_id = 0;
1869 pane_l.dirx = 2;
1870 pane_l.dircol = cpanell;
1871 pane_l.firstrow = 0;
1872 pane_l.direntr = ecalloc(0, sizeof(Entry));
1873 strncpy(pane_l.dirn, cwd, MAX_P);
1874 pane_l.hdir = 1;
1875 pane_l.inotify_wd = -1;
1876 pane_l.parent_row = 1;
1878 pane_r.pane_id = 1;
1879 pane_r.dirx = (twidth / 2) + 2;
1880 pane_r.dircol = cpanelr;
1881 pane_r.firstrow = 0;
1882 pane_r.direntr = ecalloc(0, sizeof(Entry));
1883 strncpy(pane_r.dirn, home, MAX_P);
1884 pane_r.hdir = 1;
1885 pane_r.inotify_wd = -1;
1886 pane_r.parent_row = 1;
1889 static void
1890 draw_frame(void)
1892 int i;
1893 theight = tb_height();
1894 twidth = tb_width();
1895 scrheight = theight - 2;
1897 /* 2 horizontal lines */
1898 for (i = 1; i < twidth - 1; ++i) {
1899 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1900 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1903 /* 4 vertical lines */
1904 for (i = 1; i < theight - 1; ++i) {
1905 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1906 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1907 cframe.bg);
1908 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1909 cframe.bg);
1910 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1913 /* 4 corners */
1914 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1915 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1916 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1917 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1919 /* 2 middel top and bottom */
1920 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1921 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1924 static void
1925 start(void)
1927 if (tb_init() != 0)
1928 die("tb_init");
1929 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1930 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1931 die("output error");
1933 draw_frame();
1934 set_panes();
1935 get_editor();
1936 if (fsev_init() < 0)
1937 print_error(strerror(errno));
1938 cpane = &pane_r;
1939 if (listdir(NoHi) < 0)
1940 print_error(strerror(errno));
1941 cpane = &pane_l;
1942 if (listdir(AddHi) < 0)
1943 print_error(strerror(errno));
1944 tb_present();
1945 start_ev();
1949 main(int argc, char *argv[])
1951 #ifdef __OpenBSD__
1952 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1953 NULL) == -1)
1954 die("pledge");
1955 #endif /* __OpenBSD__ */
1956 if (argc == 1)
1957 start();
1958 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1959 die("sfm-" VERSION);
1960 else
1961 die("usage: sfm [-v]");
1962 return 0;