[fix] filter
[sfm.git] / sfm.c
blobb716028c1d0bae9410934fe8e7b587290c8ab04a
1 /* See LICENSE file for copyright and license details. */
3 #include <sys/types.h>
4 #include <sys/resource.h>
5 #include <sys/stat.h>
6 #include <sys/time.h>
7 #include <sys/wait.h>
8 #if defined(__linux__)
9 #include <sys/inotify.h>
10 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
11 defined(__OpenBSD__) || defined(__APPLE__)
12 #include <sys/event.h>
13 #endif
15 #include <dirent.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <grp.h>
19 #include <pwd.h>
20 #include <stdarg.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <libgen.h>
29 #include "termbox.h"
30 #include "util.h"
32 /* macros */
33 #define MAX_P 4095
34 #define MAX_N 255
35 #define MAX_USRI 32
36 #define MAX_EXT 4
37 #define CURSOR_NAME cpane->direntr[cpane->hdir - 1].name
39 /* enums */
40 enum { AddHi, NoHi }; /* add highlight in listdir */
42 /* typedef */
43 typedef struct {
44 char name[MAX_N];
45 gid_t group;
46 mode_t mode;
47 off_t size;
48 time_t td;
49 uid_t user;
50 } Entry;
52 typedef struct {
53 uint16_t fg;
54 uint16_t bg;
55 } Cpair;
57 typedef struct {
58 int pane_id;
59 char dirn[MAX_P]; // dir name cwd
60 Entry *direntr; // dir entries
61 int dirx; // pane cwd x pos
62 int dirc; // dir entries sum
63 int hdir; // highlighted dir
64 int firstrow;
65 int parent_firstrow;
66 int parent_row; // FIX
67 Cpair dircol;
68 int inotify_wd;
69 int event_fd;
70 } Pane;
72 typedef struct {
73 uint32_t ch;
74 char path[MAX_P];
75 } Bookmark;
77 typedef struct {
78 const char **ext;
79 size_t exlen;
80 const void *v;
81 } Rule;
83 typedef union {
84 uint16_t key; /* one of the TB_KEY_* constants */
85 uint32_t ch; /* unicode character */
86 } Evkey;
88 typedef struct {
89 const Evkey evkey;
90 void (*func)(void);
91 } Key;
93 /* function declarations */
94 static void print_tb(const char *, int, int, uint16_t, uint16_t);
95 static void printf_tb(int, int, Cpair, const char *, ...);
96 static void print_status(Cpair, const char *, ...);
97 static void print_xstatus(char, int);
98 static void print_error(char *);
99 static void print_prompt(char *);
100 static void print_info(void);
101 static void print_row(Pane *, size_t, Cpair);
102 static void clear(int, int, int, uint16_t);
103 static void clear_status(void);
104 static void clear_pane(void);
105 static void add_hi(Pane *, size_t);
106 static void rm_hi(Pane *, size_t);
107 static void float_to_string(float, char *);
108 static int check_dir(char *);
109 static mode_t chech_execf(mode_t);
110 static int sort_name(const void *const, const void *const);
111 static void get_dirp(char *);
112 static char *get_ext(char *);
113 static int get_fdt(char *, size_t, time_t);
114 static char *get_fgrp(gid_t, size_t);
115 static char *get_finfo(Entry *);
116 static char *get_fperm(mode_t);
117 static char *get_fsize(off_t);
118 static char *get_fullpath(char *, char *);
119 static char *get_fusr(uid_t, size_t);
120 static void get_dirsize(char *, off_t *);
121 static void get_hicol(Cpair *, mode_t);
122 static int delent(char *);
123 static void calcdir(void);
124 static void crnd(void);
125 static void crnf(void);
126 static void delfd(void);
127 static void mvbk(void);
128 static void mvbtm(void);
129 static void mvdwn(void);
130 static void mvdwns(void);
131 static void mvfor(void);
132 static void mvmid(void);
133 static void mvtop(void);
134 static void mvup(void);
135 static void mvups(void);
136 static void scrdwn(void);
137 static void scrdwns(void);
138 static void scrup(void);
139 static void scrups(void);
140 static int get_usrinput(char*, size_t, const char*, ...);
141 static int frules(char *);
142 static int spawn(const void *, char *);
143 static int opnf(char *);
144 static int fsev_init(void);
145 static int addwatch(void);
146 static int read_events(void);
147 static void rmwatch(Pane *);
148 static void fsev_shdn(void);
149 static ssize_t findbm(uint32_t);
150 static void filter(void);
151 static void start_vmode(void);
152 static void exit_vmode(void);
153 static void selup(void);
154 static void seldwn(void);
155 static void selall(void);
156 static void selref(void);
157 static void selynk(void);
158 static void selcalc(void);
159 static void paste(void);
160 static void selmv(void);
161 static void seldel(void);
162 static void init_files(void);
163 static void free_files(void);
164 static void yank(void);
165 static void rname(void);
166 static void switch_pane(void);
167 static void quit(void);
168 static void grabkeys(struct tb_event*, Key*, size_t);
169 static void start_ev(void);
170 static void refresh_pane(void);
171 static void set_direntr(struct dirent *, DIR *, char *);
172 static int listdir(int, char *);
173 static void t_resize(void);
174 static void set_panes(void);
175 static void draw_frame(void);
176 static void start(void);
178 /* global variables */
179 static Pane pane_r, pane_l, *cpane;
180 static char *editor[2];
181 static char fed[] = "vi";
182 static int theight, twidth, scrheight;
183 static size_t selection_size = 0;
184 static char yank_file[MAX_P];
185 static int *selection;
186 static int cont_vmode = 0;
187 static char **selected_files;
188 #if defined _SYS_INOTIFY_H
189 static int inotify_fd;
190 #elif defined _SYS_EVENT_H_
191 static int kq;
192 struct kevent evlist[2]; /* events we want to monitor */
193 struct kevent chlist[2]; /* events that were triggered */
194 static struct timespec gtimeout;
195 #endif
197 /* configuration, allows nested code to access above variables */
198 #include "config.h"
200 /* function implementations */
201 static void
202 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
204 while (*str != '\0') {
205 uint32_t uni = 0;
206 str += tb_utf8_char_to_unicode(&uni, str);
207 tb_change_cell(x, y, uni, fg, bg);
208 x++;
212 static void
213 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
215 char buf[4096];
216 va_list vl;
217 va_start(vl, fmt);
218 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
219 va_end(vl);
220 print_tb(buf, x, y, col.fg, col.bg);
223 static void
224 print_status(Cpair col, const char *fmt, ...)
226 char buf[256];
227 va_list vl;
228 va_start(vl, fmt);
229 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
230 va_end(vl);
231 clear_status();
232 print_tb(buf, 1, theight - 1, col.fg, col.bg);
235 static void
236 print_xstatus(char c, int x)
238 uint32_t uni = 0;
239 (void)tb_utf8_char_to_unicode(&uni, &c);
240 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
243 static void
244 print_error(char *errmsg)
246 print_status(cerr, errmsg);
249 static void
250 print_prompt(char *prompt)
252 print_status(cprompt, prompt);
255 static void
256 print_info(void)
258 char *fileinfo;
259 fileinfo = get_finfo(&cpane->direntr[cpane->hdir - 1]);
260 print_status(cstatus, "%d/%d %s", cpane->hdir, cpane->dirc, fileinfo);
261 free(fileinfo);
264 static void
265 print_row(Pane *pane, size_t entpos, Cpair col)
267 int x, y;
268 char *result;
269 char buf[MAX_P];
270 char lnk_full[MAX_P];
271 int width;
273 width = (twidth / 2) - 4;
274 result = basename(pane->direntr[entpos].name);
275 x = pane->dirx;
276 y = entpos - cpane->firstrow + 1;
278 if (S_ISLNK(pane->direntr[entpos].mode) &&
279 realpath(pane->direntr[entpos].name, buf) != NULL) {
280 strncpy(lnk_full, pane->direntr[entpos].name, MAX_N);
281 strcat(lnk_full, " -> ");
282 strncat(lnk_full, buf, MAX_N);
283 result = lnk_full;
286 printf_tb(x, y, col, "%*.*s", ~width, width, result);
289 static void
290 clear(int sx, int ex, int y, uint16_t bg)
292 /* clear line from to */
293 /* x = line number vertical */
294 /* y = column number horizontal */
295 int i;
296 for (i = sx; i < ex; i++) {
297 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
301 static void
302 clear_status(void)
304 clear(1, twidth - 1, theight - 1, cstatus.bg);
307 static void
308 clear_pane(void)
310 int i, ex, y;
311 y = 0, i = 0, ex = 0;
313 if (cpane->pane_id == pane_l.pane_id)
314 ex = (twidth / 2) - 1;
315 else if (cpane->pane_id == pane_r.pane_id)
316 ex = twidth - 1;
318 while (i < scrheight) {
319 clear(cpane->dirx, ex, y, TB_DEFAULT);
320 i++;
321 y++;
324 /* draw top line */
325 for (y = cpane->dirx; y < ex; ++y) {
326 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
330 static void
331 add_hi(Pane *pane, size_t entpos)
333 Cpair col;
334 get_hicol(&col, pane->direntr[entpos].mode);
335 col.fg |= TB_REVERSE | TB_BOLD;
336 col.bg |= TB_REVERSE;
337 print_row(pane, entpos, col);
340 static void
341 rm_hi(Pane *pane, size_t entpos)
343 Cpair col;
344 get_hicol(&col, pane->direntr[entpos].mode);
345 print_row(pane, entpos, col);
348 static void
349 float_to_string(float f, char *r)
351 int length, length2, i, number, position,
352 tenth; /* length is size of decimal part, length2 is size of tenth part */
353 float number2;
355 f = (float)(int)(f * 10) / 10;
357 number2 = f;
358 number = (int)f;
359 length2 = 0;
360 tenth = 1;
362 /* Calculate length2 tenth part */
363 while ((number2 - (float)number) != 0.0 &&
364 !((number2 - (float)number) < 0.0)) {
365 tenth *= 10.0;
366 number2 = f * (float)tenth;
367 number = (int)number2;
369 length2++;
372 /* Calculate length decimal part */
373 for (length = (f > 1.0) ? 0 : 1; f > 1.0; length++)
374 f /= 10.0;
376 position = length;
377 length = length + 1 + length2;
378 number = (int)number2;
380 if (length2 > 0) {
381 for (i = length; i >= 0; i--) {
382 if (i == (length))
383 r[i] = '\0';
384 else if (i == (position))
385 r[i] = '.';
386 else {
387 r[i] = (char)(number % 10) + '0';
388 number /= 10;
391 } else {
392 length--;
393 for (i = length; i >= 0; i--) {
394 if (i == (length))
395 r[i] = '\0';
396 else {
397 r[i] = (char)(number % 10) + '0';
398 number /= 10;
404 static int
405 check_dir(char *path)
407 DIR *dir;
408 dir = opendir(path);
410 if (dir == NULL) {
411 if (errno == ENOTDIR) {
412 return 1;
413 } else {
414 return -1;
418 if (closedir(dir) < 0)
419 return -1;
421 return 0;
424 static mode_t
425 chech_execf(mode_t mode)
427 if (S_ISREG(mode))
428 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
429 return 0;
432 static int
433 sort_name(const void *const A, const void *const B)
435 int result;
436 mode_t data1 = (*(Entry *)A).mode;
437 mode_t data2 = (*(Entry *)B).mode;
439 if (data1 < data2) {
440 return -1;
441 } else if (data1 == data2) {
442 result = strcmp((*(Entry *)A).name, (*(Entry *)B).name);
443 return result;
444 } else {
445 return 1;
450 static void
451 get_dirp(char *cdir)
453 int counter, len, i;
455 counter = 0;
456 len = strlen(cdir);
457 if (len ==1)
458 return;
460 for (i = len - 1; i > 1; i--) {
461 if (cdir[i] == '/')
462 break;
463 else
464 counter++;
467 cdir[len-counter-1] = '\0';
470 static char *
471 get_ext(char *str)
473 char *ext;
474 char dot;
475 size_t counter, len, i;
477 dot = '.';
478 counter = 0;
479 len = strlen(str);
481 for (i = len - 1; i > 0; i--) {
482 if (str[i] == dot) {
483 break;
484 } else {
485 counter++;
489 ext = ecalloc(counter + 1, sizeof(char));
490 strncpy(ext, &str[len - counter], counter);
491 return ext;
494 static int
495 get_fdt(char *result, size_t reslen, time_t status)
497 struct tm lt;
498 localtime_r(&status, &lt);
499 return strftime(result, reslen, dtfmt, &lt);
502 static char *
503 get_fgrp(gid_t status, size_t len)
505 char *result;
506 struct group *gr;
508 result = ecalloc(len, sizeof(char));
509 gr = getgrgid(status);
510 if (gr == NULL)
511 (void)snprintf(result, len - 1, "%u", status);
512 else
513 strncpy(result, gr->gr_name, len - 1);
515 return result;
518 static char *
519 get_finfo(Entry *cursor)
521 char *sz, *rst, *ur, *gr, *td, *prm;
522 size_t szlen, prmlen, urlen, grlen, tdlen, rstlen;
524 szlen = 9;
525 prmlen = 11;
526 urlen = grlen = tdlen = 32;
527 rstlen = szlen + prmlen + urlen + grlen + tdlen;
528 rst = ecalloc(rstlen, sizeof(char));
530 if (show_perm == 1) {
531 prm = get_fperm(cursor->mode);
532 strncpy(rst, prm, prmlen);
533 strcat(rst, " ");
534 free(prm);
537 if (show_ug == 1) {
538 ur = get_fusr(cursor->user, urlen);
539 gr = get_fgrp(cursor->group, grlen);
540 strncat(rst, ur, urlen);
541 strcat(rst, ":");
542 strncat(rst, gr, grlen);
543 strcat(rst, " ");
544 free(ur);
545 free(gr);
548 if (show_dt == 1) {
549 td = ecalloc(tdlen, sizeof(char));
550 if (get_fdt(td, tdlen, cursor->td) > 0) {
551 strncat(rst, td, tdlen);
552 strcat(rst, " ");
554 free(td);
557 if (show_size == 1 && S_ISREG(cursor->mode)) {
558 sz = get_fsize(cursor->size);
559 strncat(rst, sz, szlen);
560 free(sz);
563 return rst;
566 static char *
567 get_fperm(mode_t mode)
569 char *buf;
570 size_t i;
572 const char chars[] = "rwxrwxrwx";
573 buf = ecalloc(11, sizeof(char));
575 if (S_ISDIR(mode))
576 buf[0] = 'd';
577 else if (S_ISREG(mode))
578 buf[0] = '-';
579 else if (S_ISLNK(mode))
580 buf[0] = 'l';
581 else if (S_ISBLK(mode))
582 buf[0] = 'b';
583 else if (S_ISCHR(mode))
584 buf[0] = 'c';
585 else if (S_ISFIFO(mode))
586 buf[0] = 'p';
587 else if (S_ISSOCK(mode))
588 buf[0] = 's';
589 else
590 buf[0] = '?';
592 for (i = 1; i < 10; i++) {
593 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
595 buf[10] = '\0';
597 return buf;
600 static char *
601 get_fsize(off_t size)
603 /* need to be freed */
604 char *Rsize;
605 float lsize;
606 int counter;
607 counter = 0;
609 Rsize = ecalloc(10, sizeof(char));
610 lsize = (float)size;
612 while (lsize >= 1000.0) {
613 lsize /= 1024.0;
614 ++counter;
617 float_to_string(lsize, Rsize);
619 switch (counter) {
620 case 0:
621 strcat(Rsize, "B");
622 break;
623 case 1:
624 strcat(Rsize, "K");
625 break;
626 case 2:
627 strcat(Rsize, "M");
628 break;
629 case 3:
630 strcat(Rsize, "G");
631 break;
632 case 4:
633 strcat(Rsize, "T");
634 break;
637 return Rsize;
640 static char *
641 get_fullpath(char *first, char *second)
643 char *full_path;
644 size_t full_path_len;
646 full_path_len = strlen(first) + strlen(second) + 2;
647 full_path = ecalloc(full_path_len, sizeof(char));
649 if (strcmp(first, "/") == 0) {
650 (void)snprintf(full_path, full_path_len, "/%s", second);
652 } else {
653 (void)snprintf(full_path, full_path_len, "%s/%s", first,
654 second);
657 return full_path;
660 static char *
661 get_fusr(uid_t status, size_t len)
663 char *result;
664 struct passwd *pw;
666 result = ecalloc(len, sizeof(char));
667 pw = getpwuid(status);
668 if (pw == NULL)
669 (void)snprintf(result, len - 1, "%u", status);
670 else
671 strncpy(result, pw->pw_name, len - 1);
673 return result;
676 static void
677 get_dirsize(char *fullpath, off_t *fullsize)
679 DIR *dir;
680 char *ent_full;
681 mode_t mode;
682 struct dirent *entry;
683 struct stat status;
685 dir = opendir(fullpath);
686 if (dir == NULL) {
687 return;
690 while ((entry = readdir(dir)) != 0) {
691 if ((strcmp(entry->d_name, ".") == 0 ||
692 strcmp(entry->d_name, "..") == 0))
693 continue;
695 ent_full = get_fullpath(fullpath, entry->d_name);
696 if (lstat(ent_full, &status) == 0) {
697 mode = status.st_mode;
698 if (S_ISDIR(mode)) {
699 get_dirsize(ent_full, fullsize);
700 free(ent_full);
701 } else {
702 *fullsize += status.st_size;
703 free(ent_full);
708 closedir(dir);
709 clear_status();
712 static void
713 get_hicol(Cpair *col, mode_t mode)
715 *col = cfile;
716 if (S_ISDIR(mode))
717 *col = cdir;
718 else if (S_ISLNK(mode))
719 *col = cother;
720 else if (chech_execf(mode) > 0)
721 *col = cexec;
724 static int
725 delent(char *fullpath)
727 char *inp_conf;
728 int conf_len = 4;
729 char conf[] = "yes";
731 inp_conf = ecalloc(conf_len, sizeof(char));
732 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
733 (strncmp(inp_conf, conf, conf_len) != 0)) {
734 free(inp_conf);
735 return 1; /* canceled by user or wrong inp_conf */
737 free(inp_conf);
739 return spawn(rm_cmd, fullpath);
742 static void
743 calcdir(void)
745 off_t *fullsize;
746 char *csize;
747 char *result;
749 if (S_ISDIR(cpane->direntr[cpane->hdir - 1].mode)) {
750 fullsize = ecalloc(50, sizeof(off_t));
751 get_dirsize(CURSOR_NAME, fullsize);
752 csize = get_fsize(*fullsize);
753 result = get_finfo(&cpane->direntr[cpane->hdir - 1]);
755 clear_status();
756 print_status(cstatus, "%d/%d %s%s", cpane->hdir, cpane->dirc,
757 result, csize);
758 free(csize);
759 free(fullsize);
760 free(result);
764 static void
765 crnd(void)
767 char *user_input, *path;
768 size_t pathlen;
770 user_input = ecalloc(MAX_USRI, sizeof(char));
771 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
772 free(user_input);
773 return;
776 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
777 path = ecalloc(pathlen, sizeof(char));
778 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
779 free(user_input);
780 free(path);
781 return;
784 if (mkdir(path, ndir_perm) < 0)
785 print_error(strerror(errno));
787 free(user_input);
788 free(path);
791 static void
792 crnf(void)
794 char *user_input, *path;
795 size_t pathlen;
796 int rf;
798 user_input = ecalloc(MAX_USRI, sizeof(char));
799 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
800 free(user_input);
801 return;
804 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
805 path = ecalloc(pathlen, sizeof(char));
806 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
807 free(user_input);
808 free(path);
809 return;
812 rf = open(path, O_CREAT | O_EXCL, nf_perm);
814 if (rf < 0)
815 print_error(strerror(errno));
816 else
817 if (close(rf) < 0)
818 print_error(strerror(errno));
820 free(user_input);
821 free(path);
824 static void
825 delfd(void)
827 switch (delent(CURSOR_NAME)) {
828 case -1:
829 print_error(strerror(errno));
830 break;
831 case 0:
832 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
833 cpane->hdir--;
834 break;
838 static void
839 mvbk(void)
841 get_dirp(cpane->dirn);
842 if (check_dir(cpane->dirn) < 0) {
843 print_error(strerror(errno));
844 return;
847 rmwatch(cpane);
848 cpane->firstrow = cpane->parent_firstrow;
849 cpane->hdir = cpane->parent_row;
850 if (listdir(AddHi, NULL) < 0)
851 print_error(strerror(errno));
852 cpane->parent_firstrow = 0;
853 cpane->parent_row = 1;
856 static void
857 mvbtm(void)
859 if (cpane->dirc < 1)
860 return;
861 if (cpane->dirc > scrheight) {
862 rm_hi(cpane, cpane->hdir - 1);
863 cpane->hdir = cpane->dirc;
864 cpane->firstrow = cpane->dirc - scrheight + 1;
865 refresh_pane();
866 add_hi(cpane, cpane->hdir - 1);
867 } else {
868 rm_hi(cpane, cpane->hdir - 1);
869 cpane->hdir = cpane->dirc;
870 add_hi(cpane, cpane->hdir - 1);
872 print_info();
875 static void
876 mvdwn(void)
878 if (cpane->dirc < 1)
879 return;
880 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
881 rm_hi(cpane, cpane->hdir - 1);
882 cpane->hdir++;
883 add_hi(cpane, cpane->hdir - 1);
884 } else {
885 mvdwns(); /* scroll */
887 print_info();
890 static void
891 mvdwns(void)
893 int real;
894 real = cpane->hdir - 1 - cpane->firstrow;
896 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
897 cpane->firstrow++;
898 rm_hi(cpane, cpane->hdir - 1);
899 cpane->hdir++;
900 refresh_pane();
901 add_hi(cpane, cpane->hdir - 1);
902 } else if (cpane->hdir < cpane->dirc) {
903 rm_hi(cpane, cpane->hdir - 1);
904 cpane->hdir++;
905 add_hi(cpane, cpane->hdir - 1);
909 static void
910 mvfor(void)
912 rmwatch(cpane);
913 if (cpane->dirc < 1)
914 return;
915 int s;
917 switch (check_dir(CURSOR_NAME)) {
918 case 0:
919 strcpy(cpane->dirn, CURSOR_NAME);
920 cpane->parent_row = cpane->hdir;
921 cpane->parent_firstrow = cpane->firstrow;
922 cpane->hdir = 1;
923 cpane->firstrow = 0;
924 if (listdir(AddHi, NULL) < 0)
925 print_error(strerror(errno));
926 break;
927 case 1: /* not a directory open file */
928 tb_shutdown();
929 s = opnf(CURSOR_NAME);
930 if (tb_init() != 0)
931 die("tb_init");
932 t_resize();
933 if (s < 0)
934 print_error("process failed non-zero exit");
935 break;
936 case -1: /* failed to open directory */
937 print_error(strerror(errno));
941 static void
942 mvmid(void)
944 if (cpane->dirc < 1)
945 return;
946 rm_hi(cpane, cpane->hdir - 1);
947 if (cpane->dirc < scrheight / 2)
948 cpane->hdir = (cpane->dirc + 1) / 2;
949 else
950 cpane->hdir = (scrheight / 2) + cpane->firstrow;
951 add_hi(cpane, cpane->hdir - 1);
952 print_info();
955 static void
956 mvtop(void)
958 if (cpane->dirc < 1)
959 return;
960 if (cpane->dirc > scrheight) {
961 rm_hi(cpane, cpane->hdir - 1);
962 cpane->hdir = 1;
963 cpane->firstrow = 0;
964 refresh_pane();
965 add_hi(cpane, cpane->hdir - 1);
966 } else {
967 rm_hi(cpane, cpane->hdir - 1);
968 cpane->hdir = 1;
969 add_hi(cpane, cpane->hdir - 1);
970 print_info();
974 static void
975 mvup(void)
977 if (cpane->dirc < 1)
978 return;
979 if (cpane->dirc < scrheight && cpane->hdir > 1) {
980 rm_hi(cpane, cpane->hdir - 1);
981 cpane->hdir--;
982 add_hi(cpane, cpane->hdir - 1);
983 } else {
984 mvups(); /* scroll */
986 print_info();
989 static void
990 mvups(void)
992 size_t real;
993 real = cpane->hdir - 1 - cpane->firstrow;
995 if (cpane->firstrow > 0 && real < 1 + scrsp) {
996 cpane->firstrow--;
997 rm_hi(cpane, cpane->hdir - 1);
998 cpane->hdir--;
999 refresh_pane();
1000 add_hi(cpane, cpane->hdir - 1);
1001 } else if (cpane->hdir > 1) {
1002 rm_hi(cpane, cpane->hdir - 1);
1003 cpane->hdir--;
1004 add_hi(cpane, cpane->hdir - 1);
1008 static void
1009 scrdwn(void)
1011 if (cpane->dirc < 1)
1012 return;
1013 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
1014 if (cpane->hdir < cpane->dirc - scrmv) {
1015 rm_hi(cpane, cpane->hdir - 1);
1016 cpane->hdir += scrmv;
1017 add_hi(cpane, cpane->hdir - 1);
1018 } else {
1019 mvbtm();
1021 } else {
1022 scrdwns();
1024 print_info();
1027 static void
1028 scrdwns(void)
1030 int real, dynmv;
1032 real = cpane->hdir - cpane->firstrow;
1033 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
1035 if (real + scrmv + 1 > scrheight &&
1036 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
1037 cpane->firstrow += dynmv;
1038 rm_hi(cpane, cpane->hdir - 1);
1039 cpane->hdir += scrmv;
1040 refresh_pane();
1041 add_hi(cpane, cpane->hdir - 1);
1042 } else {
1043 if (cpane->hdir < cpane->dirc - scrmv - 1) {
1044 rm_hi(cpane, cpane->hdir - 1);
1045 cpane->hdir += scrmv;
1046 add_hi(cpane, cpane->hdir - 1);
1047 } else {
1048 mvbtm();
1053 static void
1054 scrup(void)
1056 if (cpane->dirc < 1)
1057 return;
1058 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1059 if (cpane->hdir > scrmv) {
1060 rm_hi(cpane, cpane->hdir - 1);
1061 cpane->hdir = cpane->hdir - scrmv;
1062 add_hi(cpane, cpane->hdir - 1);
1063 print_info();
1064 } else {
1065 mvtop();
1067 } else {
1068 scrups();
1072 static void
1073 scrups(void)
1075 int real, dynmv;
1076 real = cpane->hdir - cpane->firstrow;
1077 dynmv = MIN(cpane->firstrow, scrmv);
1079 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1080 cpane->firstrow -= dynmv;
1081 rm_hi(cpane, cpane->hdir - 1);
1082 cpane->hdir -= scrmv;
1083 refresh_pane();
1084 add_hi(cpane, cpane->hdir - 1);
1085 } else {
1086 if (cpane->hdir > scrmv + 1) {
1087 rm_hi(cpane, cpane->hdir - 1);
1088 cpane->hdir -= scrmv;
1089 add_hi(cpane, cpane->hdir - 1);
1090 } else {
1091 mvtop();
1096 static int
1097 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1099 int height = tb_height();
1100 size_t startat;
1101 struct tb_event fev;
1102 size_t counter = (size_t)1;
1103 char empty = ' ';
1104 int x = 0;
1105 int name_size = 0;
1106 char buf[256];
1108 clear_status();
1110 va_list vl;
1111 Cpair col;
1112 col = cprompt;
1113 va_start(vl, fmt);
1114 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1115 va_end(vl);
1116 print_tb(buf, 1, height-1, col.fg, col.bg);
1117 startat = name_size + 1;
1118 tb_set_cursor((int)(startat + 1), height-1);
1119 tb_present();
1121 while (tb_poll_event(&fev) != 0) {
1122 switch (fev.type) {
1123 case TB_EVENT_KEY:
1124 if (fev.key == TB_KEY_ESC) {
1125 tb_set_cursor(-1, -1);
1126 clear_status();
1127 return -1;
1130 if (fev.key == TB_KEY_BACKSPACE ||
1131 fev.key == TB_KEY_BACKSPACE2) {
1132 if (BETWEEN(counter, 2, sout)) {
1133 out[x - 1] = '\0';
1134 counter--;
1135 x--;
1136 print_xstatus(empty, startat + counter);
1137 tb_set_cursor(startat + counter,
1138 theight - 1);
1141 } else if (fev.key == TB_KEY_ENTER) {
1142 tb_set_cursor(-1, -1);
1143 out[counter - 1] = '\0';
1144 return 0;
1146 } else {
1147 if (counter < sout) {
1148 print_xstatus((char)fev.ch,
1149 (startat + counter));
1150 out[x] = (char)fev.ch;
1151 tb_set_cursor((startat + counter + 1),
1152 theight - 1);
1153 counter++;
1154 x++;
1158 tb_present();
1159 break;
1161 default:
1162 return -1;
1166 return -1;
1169 static int
1170 frules(char *ex)
1172 size_t c, d;
1174 for (c = 0; c < LEN(rules); c++)
1175 for (d = 0; d < rules[c].exlen; d++)
1176 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1177 return c;
1178 return -1;
1181 static int
1182 spawn(const void *v, char *fn)
1184 int ws, x, argc, fd;
1185 pid_t pid, r;
1187 x = 0;
1188 argc = 0;
1190 /* count args */
1191 while (((char **)v)[x++] != NULL)
1192 argc++;
1194 char *argv[argc + 2];
1195 for ( x = 0; x < argc; x++)
1196 argv[x] = ((char **)v)[x];
1198 argv[argc] = fn;
1199 argv[argc + 1] = NULL;
1201 pid = fork();
1202 switch (pid) {
1203 case -1:
1204 return -1;
1205 case 0:
1206 fd = open("/dev/null",O_WRONLY);
1207 dup2(fd, STDERR_FILENO);
1208 execvp(argv[0], argv);
1209 exit(EXIT_SUCCESS);
1210 close(fd);
1211 default:
1212 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1213 continue;
1214 if (r == -1)
1215 return -1;
1216 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1217 return -1;
1219 return 0;
1222 static int
1223 opnf(char *fn)
1225 char *ex;
1226 int c;
1228 ex = get_ext(fn);
1229 c = frules(ex);
1230 free(ex);
1232 if (c < 0) /* extension not found open in editor */
1233 return spawn(editor, fn);
1234 else
1235 return spawn((char **)rules[c].v, fn);
1238 static int
1239 fsev_init(void)
1241 #if defined _SYS_INOTIFY_H
1242 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1243 if (inotify_fd < 0)
1244 return -1;
1245 #elif defined _SYS_EVENT_H_
1246 kq = kqueue();
1247 if (kq < 0)
1248 return -1;
1249 #endif
1250 return 0;
1253 static int
1254 addwatch(void)
1256 #if defined _SYS_INOTIFY_H
1257 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1258 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1259 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1260 #elif defined _SYS_EVENT_H_
1261 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1262 if (cpane->event_fd < 0)
1263 return cpane->event_fd;
1264 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1265 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1266 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1267 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1268 return 0;
1269 #endif
1272 static int
1273 read_events(void)
1275 #if defined _SYS_INOTIFY_H
1276 char *p;
1277 ssize_t r;
1278 struct inotify_event *event;
1279 const size_t events = 32;
1280 const size_t evbuflen =
1281 events * (sizeof(struct inotify_event) + MAX_N + 1);
1282 char buf[evbuflen];
1284 if (cpane->inotify_wd < 0)
1285 return -1;
1286 r = read(inotify_fd, buf, evbuflen);
1287 if (r <= 0)
1288 return r;
1290 for (p = buf; p < buf + r;) {
1291 event = (struct inotify_event *)p;
1292 if (!event->wd)
1293 break;
1294 if (event->mask) {
1295 return r;
1298 p += sizeof(struct inotify_event) + event->len;
1300 #elif defined _SYS_EVENT_H_
1301 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1302 #endif
1303 return -1;
1306 static void
1307 rmwatch(Pane *pane)
1309 #if defined _SYS_INOTIFY_H
1310 if (pane->inotify_wd >= 0)
1311 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1312 #elif defined _SYS_EVENT_H_
1313 close(pane->event_fd);
1314 return;
1315 #endif
1318 static void
1319 fsev_shdn(void)
1321 rmwatch(&pane_l);
1322 rmwatch(&pane_r);
1323 #if defined _SYS_INOTIFY_H
1324 close(inotify_fd);
1325 #elif defined _SYS_EVENT_H_
1326 close(kq);
1327 #endif
1330 static ssize_t
1331 findbm(uint32_t event)
1333 ssize_t i;
1335 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1336 if (event == bmarks[i].ch) {
1337 if (check_dir(bmarks[i].path) != 0) {
1338 print_error(strerror(errno));
1339 return -1;
1341 return i;
1344 return -1;
1347 static void
1348 filter(void)
1350 if (cpane->dirc < 1)
1351 return;
1352 char *user_input;
1353 user_input = ecalloc(MAX_USRI, sizeof(char));
1354 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1355 free(user_input);
1356 return;
1358 if (listdir(AddHi, user_input) < 0)
1359 print_error("no match");
1360 free(user_input);
1363 static void
1364 start_vmode(void)
1366 struct tb_event fev;
1367 if (selection != NULL) {
1368 free(selection);
1369 selection = NULL;
1372 selection = ecalloc(cpane->dirc, sizeof(size_t));
1373 selection[0] = cpane->hdir;
1374 cont_vmode = 0;
1375 print_prompt("-- VISUAL --");
1376 tb_present();
1377 while (tb_poll_event(&fev) != 0) {
1378 switch (fev.type) {
1379 case TB_EVENT_KEY:
1380 grabkeys(&fev, skeys, skeyslen);
1381 if (cont_vmode == -1)
1382 return;
1383 tb_present();
1384 break;
1389 static void
1390 exit_vmode(void)
1392 refresh_pane();
1393 add_hi(cpane, cpane->hdir - 1);
1394 cont_vmode = -1;
1397 static void
1398 selup(void)
1400 mvup();
1401 print_prompt("-- VISUAL --");
1402 int index = abs(cpane->hdir - selection[0]);
1404 if (cpane->hdir < selection[0]) {
1405 selection[index] = cpane->hdir;
1406 add_hi(cpane, selection[index]);
1407 } else if (index < cpane->dirc) {
1408 selection[index + 1] = 0;
1410 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1411 selref();
1415 static void
1416 seldwn(void)
1418 mvdwn();
1419 print_prompt("-- VISUAL --");
1420 int index = abs(cpane->hdir - selection[0]);
1422 if (cpane->hdir > selection[0]) {
1423 selection[index] = cpane->hdir;
1424 add_hi(cpane, selection[index] - 2);
1425 } else {
1426 selection[index + 1] = 0;
1428 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1429 selref();
1433 static void
1434 selall(void)
1436 int i;
1437 for (i = 0; i < cpane->dirc; i++) {
1438 selection[i] = i + 1;
1440 selref();
1443 static void
1444 selref(void)
1446 int i;
1447 for (i = 0; i < cpane->dirc; i++) {
1448 if (selection[i] < (scrheight + cpane->firstrow) && selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1449 add_hi(cpane, selection[i] - 1);
1454 static void
1455 selcalc(void)
1457 int j;
1458 selection_size = 0;
1460 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1461 if (selection[j] != 0)
1462 selection_size++;
1463 else
1464 break;
1468 static void
1469 free_files(void)
1471 size_t i;
1473 if (selected_files != NULL) {
1474 for (i = 0; i < selection_size; i++) {
1475 free(selected_files[i]);
1476 selected_files[i] = NULL;
1478 free(selected_files);
1479 selected_files = NULL;
1483 static void
1484 init_files(void)
1486 size_t i;
1487 free_files();
1489 selcalc();
1490 selected_files = ecalloc(selection_size, sizeof(char*));
1492 for (i = 0; i < selection_size; i++) {
1493 selected_files[i] = ecalloc(MAX_P, sizeof(char));
1494 // strcpy(selected_files[i], "\"");
1495 strcpy(selected_files[i], cpane->direntr[selection[i] -1].name);
1496 // strcat(selected_files[i], "\"");
1500 static void
1501 selynk(void)
1503 init_files();
1504 refresh_pane();
1505 add_hi(cpane, cpane->hdir -1);
1506 print_status(cprompt, "%zu files are yanked", selection_size);
1507 cont_vmode = -1;
1510 static void
1511 seldel(void)
1513 char *inp_conf;
1514 int conf_len = 4;
1515 char conf[] = "yes";
1516 size_t i;
1518 inp_conf = ecalloc(conf_len, sizeof(char));
1519 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1520 (strncmp(inp_conf, conf, conf_len) != 0)) {
1521 free(inp_conf);
1522 return; /* canceled by user or wrong inp_conf */
1524 free(inp_conf);
1526 init_files();
1527 for (i = 0; i < selection_size; i++) {
1528 spawn(rm_cmd, selected_files[i]);
1531 cpane->hdir = cpane->dirc - selection_size;
1532 print_status(cprompt, "%zu files are deleted", selection_size);
1533 free_files();
1534 cont_vmode = -1;
1537 static void
1538 paste(void)
1540 size_t i;
1541 if (strlen(yank_file) != 0) {
1542 print_status(cprompt, "coping");
1543 if (spawn(cp_cmd, cpane->dirn) != 0)
1544 print_error("coping failed");
1545 else
1546 print_status(cprompt, "file copied");
1547 yank_file[0] = '\0'; /* set yank_file len 0 */
1548 return;
1551 print_error("nothing to paste");
1553 if (selected_files == NULL)
1554 return;
1556 for (i = 0; i < selection_size; i++) {
1557 char *selcp_cmd[] = { "cp", "-r", selected_files[i], cpane->dirn, NULL };
1558 spawn(selcp_cmd,NULL);
1560 print_status(cprompt, "%zu files are copied", selection_size);
1561 free_files();
1564 static void
1565 selmv(void)
1567 size_t i;
1569 if (strlen(yank_file) != 0) {
1570 print_status(cprompt, "moving");
1571 if (spawn(mv_cmd, cpane->dirn) != 0)
1572 print_error("moving failed");
1573 else
1574 print_status(cprompt, "file moved");
1575 yank_file[0] = '\0'; /* set yank_file len 0 */
1576 return;
1579 print_error("nothing to move");
1581 if (selected_files == NULL)
1582 return;
1584 for (i = 0; i < selection_size; i++) {
1585 char *selmv_cmd[] = { "mv", selected_files[i], cpane->dirn, NULL };
1586 spawn(selmv_cmd,NULL);
1588 print_status(cprompt, "%zu files are moved", selection_size);
1589 free_files();
1592 static void
1593 rname(void)
1595 char new_name[MAX_P];
1596 char *input_name;
1598 input_name = ecalloc(MAX_N, sizeof(char));
1600 if (get_usrinput(input_name, MAX_N, "rename: %s", basename(CURSOR_NAME)) < 0) {
1601 free(input_name);
1602 return;
1605 strcpy(new_name, cpane->dirn);
1606 strcat(new_name, "/");
1607 strcat(new_name, input_name);
1609 char *rename_cmd[] = { "mv", CURSOR_NAME, new_name, NULL };
1610 if (spawn(rename_cmd, NULL) < 0)
1611 print_error(strerror(errno));
1613 free(input_name);
1616 static void
1617 yank(void)
1619 strncpy(yank_file, CURSOR_NAME, MAX_P);
1620 print_status(cprompt, "1 file is yanked", selection_size);
1624 static void
1625 switch_pane(void)
1627 if (cpane->dirc > 0)
1628 rm_hi(cpane, cpane->hdir - 1);
1629 if (cpane == &pane_l)
1630 cpane = &pane_r;
1631 else if (cpane == &pane_r)
1632 cpane = &pane_l;
1633 if (cpane->dirc > 0) {
1634 add_hi(cpane, cpane->hdir - 1);
1635 print_info();
1636 } else {
1637 clear_status();
1641 static void
1642 quit(void)
1644 if (cont_vmode == -1) { /* check if selection was allocated */
1645 free(selection);
1646 if (selected_files != NULL)
1647 free_files();
1649 free(pane_l.direntr);
1650 free(pane_r.direntr);
1651 fsev_shdn();
1652 tb_shutdown();
1653 exit(EXIT_SUCCESS);
1656 static void
1657 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1659 size_t i;
1660 ssize_t b;
1662 for (i = 0; i < max_keys; i++) {
1663 if (event->ch != 0) {
1664 if (event->ch == key[i].evkey.ch) {
1665 key[i].func();
1666 return;
1668 } else if (event->key != 0) {
1669 if (event->key == key[i].evkey.key) {
1670 key[i].func();
1671 return;
1676 /* bookmarks */
1677 b = findbm(event->ch);
1678 if (b < 0)
1679 return;
1680 rmwatch(cpane);
1681 strcpy(cpane->dirn, bmarks[b].path);
1682 cpane->firstrow = 0;
1683 cpane->parent_row = 1;
1684 cpane->hdir = 1;
1685 if (listdir(AddHi, NULL) < 0)
1686 print_error(strerror(errno));
1689 static void
1690 start_ev(void)
1692 struct tb_event ev;
1694 for (;;) {
1695 int t = tb_peek_event(&ev, 2000);
1696 if (t < 0) {
1697 tb_shutdown();
1698 return;
1701 if (t == 1) /* keyboard event */
1702 grabkeys(&ev, nkeys, nkeyslen);
1703 else if (t == 2) /* resize event */
1704 t_resize();
1705 else if (t == 0) /* filesystem event */
1706 if (read_events() > 0) { /* TODO need refactoring */
1707 if (cpane == &pane_l) {
1708 cpane = &pane_r;
1709 if (listdir(NoHi, NULL) < 0)
1710 print_error(strerror(errno));
1711 cpane = &pane_l;
1712 if (listdir(AddHi, NULL) < 0)
1713 print_error(strerror(errno));
1714 } else if (cpane == &pane_r) {
1715 cpane = &pane_l;
1716 if (listdir(NoHi, NULL) < 0)
1717 print_error(strerror(errno));
1718 cpane = &pane_r;
1719 if (listdir(AddHi, NULL) < 0)
1720 print_error(strerror(errno));
1724 tb_present();
1725 continue;
1727 tb_shutdown();
1730 static void
1731 refresh_pane(void)
1733 size_t y, dyn_max, start_from;
1734 int width;
1735 width = (twidth / 2) - 4;
1736 Cpair col;
1738 y = 1;
1739 start_from = cpane->firstrow;
1740 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1742 /* print each entry in directory */
1743 while (start_from < dyn_max) {
1744 get_hicol(&col, cpane->direntr[start_from].mode);
1745 print_row(cpane, start_from, col);
1746 start_from++;
1747 y++;
1750 if (cpane->dirc > 0)
1751 print_info();
1752 else
1753 clear_status();
1755 /* print current directory title */
1756 cpane->dircol.fg |= TB_BOLD;
1757 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1760 static void
1761 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1763 int i;
1764 char *tmpfull;
1765 struct stat status;
1768 #define ADD_ENTRY \
1769 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1770 strcpy(cpane->direntr[i].name, tmpfull); \
1771 if (lstat(tmpfull, &status) == 0) { \
1772 cpane->direntr[i].size = status.st_size; \
1773 cpane->direntr[i].mode = status.st_mode; \
1774 cpane->direntr[i].group = status.st_gid; \
1775 cpane->direntr[i].user = status.st_uid; \
1776 cpane->direntr[i].td = status.st_mtime; \
1777 }i++;free(tmpfull);
1779 i = 0;
1780 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1781 while ((entry = readdir(dir)) != 0) {
1782 if ((strcmp(entry->d_name, ".") == 0 ||
1783 strcmp(entry->d_name, "..") == 0))
1784 continue;
1786 if (filter != NULL) {
1787 if (strstr(entry->d_name, filter) != NULL) {
1788 ADD_ENTRY
1792 if (filter == NULL) {
1793 ADD_ENTRY
1797 cpane->dirc = i;
1800 static int
1801 listdir(int hi, char *filter)
1803 DIR *dir;
1804 struct dirent *entry;
1805 int width;
1806 int filtercount = 0;
1807 size_t oldc = cpane->dirc;
1809 width = (twidth / 2) - 4;
1810 cpane->dirc = 0;
1812 dir = opendir(cpane->dirn);
1813 if (dir == NULL)
1814 return -1;
1816 /* get content and filter sum */
1817 while ((entry = readdir(dir)) != 0) {
1818 if (filter != NULL) {
1819 if (strstr(entry->d_name, filter) != NULL)
1820 filtercount++;
1821 } else { /* no filter */
1822 cpane->dirc++;
1826 if (filter == NULL) {
1827 clear_pane();
1828 cpane->dirc -= 2;
1831 if (filter != NULL) {
1832 if (filtercount > 0) {
1833 cpane->dirc -= 2;
1834 cpane->dirc = filtercount;
1835 clear_pane();
1836 cpane->hdir = 1;
1837 } else if (filtercount == 0) {
1838 if (closedir(dir) < 0)
1839 return -1;
1840 cpane->dirc = oldc;
1841 return -1;
1845 /* print current directory title */
1846 cpane->dircol.fg |= TB_BOLD;
1847 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1849 if (filter == NULL) /* dont't watch when filtering */
1850 if (addwatch() < 0)
1851 print_error("can't add watch");
1853 /* empty directory */
1854 if (cpane->dirc == 0) {
1855 clear_status();
1856 if (closedir(dir) < 0)
1857 return -1;
1858 return 0;
1861 rewinddir(dir); /* reset position */
1862 set_direntr(entry, dir, filter); /* create array of entries */
1863 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1864 refresh_pane();
1866 if (hi == AddHi)
1867 add_hi(cpane, cpane->hdir - 1);
1869 if (closedir(dir) < 0)
1870 return -1;
1871 return 0;
1874 static void
1875 t_resize(void)
1877 /* TODO need refactoring */
1878 tb_clear();
1879 draw_frame();
1880 pane_r.dirx = (twidth / 2) + 2;
1882 if (cpane == &pane_l) {
1883 cpane = &pane_r;
1884 refresh_pane();
1885 cpane = &pane_l;
1886 refresh_pane();
1887 if (cpane->dirc > 0)
1888 add_hi(&pane_l, pane_l.hdir - 1);
1889 } else if (cpane == &pane_r) {
1890 cpane = &pane_l;
1891 refresh_pane();
1892 cpane = &pane_r;
1893 refresh_pane();
1894 if (cpane->dirc > 0)
1895 add_hi(&pane_r, pane_r.hdir - 1);
1898 tb_present();
1901 static void
1902 get_editor(void)
1904 editor[0] = getenv("EDITOR");
1905 editor[1] = NULL;
1907 if (editor[0] == NULL)
1908 editor[0] = fed;
1911 static void
1912 set_panes(void)
1914 char *home;
1915 char cwd[MAX_P];
1917 home = getenv("HOME");
1918 if (home == NULL)
1919 home = "/";
1920 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1921 strncpy(cwd, home, MAX_P);
1923 pane_l.pane_id = 0;
1924 pane_l.dirx = 2;
1925 pane_l.dircol = cpanell;
1926 pane_l.firstrow = 0;
1927 pane_l.direntr = ecalloc(0, sizeof(Entry));
1928 strcpy(pane_l.dirn, cwd);
1929 pane_l.hdir = 1;
1930 pane_l.inotify_wd = -1;
1931 pane_l.parent_row = 1;
1933 pane_r.pane_id = 1;
1934 pane_r.dirx = (twidth / 2) + 2;
1935 pane_r.dircol = cpanelr;
1936 pane_r.firstrow = 0;
1937 pane_r.direntr = ecalloc(0, sizeof(Entry));
1938 strcpy(pane_r.dirn, home);
1939 pane_r.hdir = 1;
1940 pane_r.inotify_wd = -1;
1941 pane_r.parent_row = 1;
1944 static void
1945 draw_frame(void)
1947 int i;
1948 theight = tb_height();
1949 twidth = tb_width();
1950 scrheight = theight - 2;
1952 /* 2 horizontal lines */
1953 for (i = 1; i < twidth - 1; ++i) {
1954 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1955 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1958 /* 4 vertical lines */
1959 for (i = 1; i < theight - 1; ++i) {
1960 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1961 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1962 cframe.bg);
1963 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1964 cframe.bg);
1965 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1968 /* 4 corners */
1969 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1970 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1971 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1972 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1974 /* 2 middel top and bottom */
1975 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1976 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1979 static void
1980 start(void)
1982 if (tb_init() != 0)
1983 die("tb_init");
1984 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1985 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1986 die("output error");
1988 draw_frame();
1989 set_panes();
1990 get_editor();
1991 if (fsev_init() < 0)
1992 print_error(strerror(errno));
1993 cpane = &pane_r;
1994 if (listdir(NoHi, NULL) < 0)
1995 print_error(strerror(errno));
1996 cpane = &pane_l;
1997 if (listdir(AddHi, NULL) < 0)
1998 print_error(strerror(errno));
1999 tb_present();
2000 start_ev();
2004 main(int argc, char *argv[])
2006 #ifdef __OpenBSD__
2007 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
2008 NULL) == -1)
2009 die("pledge");
2010 #endif /* __OpenBSD__ */
2011 if (argc == 1)
2012 start();
2013 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
2014 die("sfm-" VERSION);
2015 else
2016 die("usage: sfm [-v]");
2017 return 0;