[ref] simple mv cmd
[sfm.git] / sfm.c
blob93505e84270e226a03e9ce320e8b7ac1cec9ba59
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 int *selection;
68 Cpair dircol;
69 int inotify_wd;
70 int event_fd;
71 } Pane;
73 typedef struct {
74 uint32_t ch;
75 char path[MAX_P];
76 } Bookmark;
78 typedef struct {
79 const char **ext;
80 size_t exlen;
81 const void *v;
82 } Rule;
84 typedef union {
85 uint16_t key; /* one of the TB_KEY_* constants */
86 uint32_t ch; /* unicode character */
87 } Evkey;
89 typedef struct {
90 const Evkey evkey;
91 void (*func)(void);
92 } Key;
94 /* function declarations */
95 static void print_tb(const char *, int, int, uint16_t, uint16_t);
96 static void printf_tb(int, int, Cpair, const char *, ...);
97 static void print_status(Cpair, const char *, ...);
98 static void print_xstatus(char, int);
99 static void print_error(char *);
100 static void print_prompt(char *);
101 static void print_info(void);
102 static void print_row(Pane *, size_t, Cpair);
103 static void clear(int, int, int, uint16_t);
104 static void clear_status(void);
105 static void clear_pane(void);
106 static void add_hi(Pane *, size_t);
107 static void rm_hi(Pane *, size_t);
108 static void float_to_string(float, char *);
109 static int check_dir(char *);
110 static mode_t chech_execf(mode_t);
111 static int sort_name(const void *const, const void *const);
112 static void get_dirp(char *);
113 static char *get_ext(char *);
114 static int get_fdt(char *, size_t, time_t);
115 static char *get_fgrp(gid_t, size_t);
116 static char *get_finfo(Entry *);
117 static char *get_fperm(mode_t);
118 static char *get_fsize(off_t);
119 static char *get_fullpath(char *, char *);
120 static char *get_fusr(uid_t, size_t);
121 static void get_dirsize(char *, off_t *);
122 static void get_hicol(Cpair *, mode_t);
123 static int delent(char *);
124 static void calcdir(void);
125 static void crnd(void);
126 static void crnf(void);
127 static void delfd(void);
128 static void mvbk(void);
129 static void mvbtm(void);
130 static void mvdwn(void);
131 static void mvdwns(void);
132 static void mvfor(void);
133 static void mvmid(void);
134 static void mvtop(void);
135 static void mvup(void);
136 static void mvups(void);
137 static void scrdwn(void);
138 static void scrdwns(void);
139 static void scrup(void);
140 static void scrups(void);
141 static int get_usrinput(char*, size_t, const char*, ...);
142 static int frules(char *);
143 static int spawn(const void *, char *);
144 static int opnf(char *);
145 static int fsev_init(void);
146 static int addwatch(void);
147 static int read_events(void);
148 static void rmwatch(Pane *);
149 static void fsev_shdn(void);
150 static ssize_t findbm(uint32_t);
151 static void filter(void);
152 static void selection(void);
153 static void selup(void);
154 static void seldwn(void);
155 static void selynk(void);
156 static void selcan(void);
157 static void selall(void);
158 static void selref(void);
159 static void selcalc(void);
160 static void selpst(void);
161 static void selmv(void);
162 static void seldel(void);
163 static void selrename(void);
164 static char *get_path_hdir(int);
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 xrename(char *);
170 static void switch_pane(void);
171 static void quit(void);
172 static void grabkeys(struct tb_event*, Key*, size_t);
173 static void start_ev(void);
174 static void refresh_pane(void);
175 static void set_direntr(struct dirent *, DIR *);
176 static int listdir(int, char *);
177 static void t_resize(void);
178 static void set_panes(void);
179 static void draw_frame(void);
180 static void start(void);
182 /* global variables */
183 static Pane pane_r, pane_l, *cpane;
184 static char *editor[2];
185 static char fed[] = "vi";
186 static int theight, twidth, scrheight;
187 static size_t selection_size = 0;
188 static char yank_file[MAX_P];
189 static int sl = 0;
190 static char **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 strncpy(lnk_full, pane->direntr[entpos].name, MAX_N);
284 strcat(lnk_full, " -> ");
285 strncat(lnk_full, buf, MAX_N);
286 result = lnk_full;
289 printf_tb(x, y, col, "%*.*s", ~width, width, result);
292 static void
293 clear(int sx, int ex, int y, uint16_t bg)
295 /* clear line from to */
296 /* x = line number vertical */
297 /* y = column number horizontal */
298 int i;
299 for (i = sx; i < ex; i++) {
300 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
304 static void
305 clear_status(void)
307 clear(1, twidth - 1, theight - 1, cstatus.bg);
310 static void
311 clear_pane(void)
313 int i, ex, y;
314 y = 0, i = 0, ex = 0;
316 if (cpane->pane_id == pane_l.pane_id)
317 ex = (twidth / 2) - 1;
318 else if (cpane->pane_id == pane_r.pane_id)
319 ex = twidth - 1;
321 while (i < scrheight) {
322 clear(cpane->dirx, ex, y, TB_DEFAULT);
323 i++;
324 y++;
327 /* draw top line */
328 for (y = cpane->dirx; y < ex; ++y) {
329 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
333 static void
334 add_hi(Pane *pane, size_t entpos)
336 Cpair col;
337 get_hicol(&col, pane->direntr[entpos].mode);
338 col.fg |= TB_REVERSE | TB_BOLD;
339 col.bg |= TB_REVERSE;
340 print_row(pane, entpos, col);
343 static void
344 rm_hi(Pane *pane, size_t entpos)
346 Cpair col;
347 get_hicol(&col, pane->direntr[entpos].mode);
348 print_row(pane, entpos, col);
351 static void
352 float_to_string(float f, char *r)
354 int length, length2, i, number, position,
355 tenth; /* length is size of decimal part, length2 is size of tenth part */
356 float number2;
358 f = (float)(int)(f * 10) / 10;
360 number2 = f;
361 number = (int)f;
362 length2 = 0;
363 tenth = 1;
365 /* Calculate length2 tenth part */
366 while ((number2 - (float)number) != 0.0 &&
367 !((number2 - (float)number) < 0.0)) {
368 tenth *= 10.0;
369 number2 = f * (float)tenth;
370 number = (int)number2;
372 length2++;
375 /* Calculate length decimal part */
376 for (length = (f > 1.0) ? 0 : 1; f > 1.0; length++)
377 f /= 10.0;
379 position = length;
380 length = length + 1 + length2;
381 number = (int)number2;
383 if (length2 > 0) {
384 for (i = length; i >= 0; i--) {
385 if (i == (length))
386 r[i] = '\0';
387 else if (i == (position))
388 r[i] = '.';
389 else {
390 r[i] = (char)(number % 10) + '0';
391 number /= 10;
394 } else {
395 length--;
396 for (i = length; i >= 0; i--) {
397 if (i == (length))
398 r[i] = '\0';
399 else {
400 r[i] = (char)(number % 10) + '0';
401 number /= 10;
407 static int
408 check_dir(char *path)
410 DIR *dir;
411 dir = opendir(path);
413 if (dir == NULL) {
414 if (errno == ENOTDIR) {
415 return 1;
416 } else {
417 return -1;
421 if (closedir(dir) < 0)
422 return -1;
424 return 0;
427 static mode_t
428 chech_execf(mode_t mode)
430 if (S_ISREG(mode))
431 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
432 return 0;
435 static int
436 sort_name(const void *const A, const void *const B)
438 int result;
439 mode_t data1 = (*(Entry *)A).mode;
440 mode_t data2 = (*(Entry *)B).mode;
442 if (data1 < data2) {
443 return -1;
444 } else if (data1 == data2) {
445 result = strcmp((*(Entry *)A).name, (*(Entry *)B).name);
446 return result;
447 } else {
448 return 1;
453 static void
454 get_dirp(char *cdir)
456 int counter, len, i;
458 counter = 0;
459 len = strlen(cdir);
460 if (len ==1)
461 return;
463 for (i = len - 1; i > 1; i--) {
464 if (cdir[i] == '/')
465 break;
466 else
467 counter++;
470 cdir[len-counter-1] = '\0';
473 static char *
474 get_ext(char *str)
476 char *ext;
477 char dot;
478 size_t counter, len, i;
480 dot = '.';
481 counter = 0;
482 len = strlen(str);
484 for (i = len - 1; i > 0; i--) {
485 if (str[i] == dot) {
486 break;
487 } else {
488 counter++;
492 ext = ecalloc(counter + 1, sizeof(char));
493 strncpy(ext, &str[len - counter], counter);
494 return ext;
497 static int
498 get_fdt(char *result, size_t reslen, time_t status)
500 struct tm lt;
501 localtime_r(&status, &lt);
502 return strftime(result, reslen, dtfmt, &lt);
505 static char *
506 get_fgrp(gid_t status, size_t len)
508 char *result;
509 struct group *gr;
511 result = ecalloc(len, sizeof(char));
512 gr = getgrgid(status);
513 if (gr == NULL)
514 (void)snprintf(result, len - 1, "%u", status);
515 else
516 strncpy(result, gr->gr_name, len - 1);
518 return result;
521 static char *
522 get_finfo(Entry *cursor)
524 char *sz, *rst, *ur, *gr, *td, *prm;
525 size_t szlen, prmlen, urlen, grlen, tdlen, rstlen;
527 szlen = 9;
528 prmlen = 11;
529 urlen = grlen = tdlen = 32;
530 rstlen = szlen + prmlen + urlen + grlen + tdlen;
531 rst = ecalloc(rstlen, sizeof(char));
533 if (show_perm == 1) {
534 prm = get_fperm(cursor->mode);
535 strncpy(rst, prm, prmlen);
536 strcat(rst, " ");
537 free(prm);
540 if (show_ug == 1) {
541 ur = get_fusr(cursor->user, urlen);
542 gr = get_fgrp(cursor->group, grlen);
543 strncat(rst, ur, urlen);
544 strcat(rst, ":");
545 strncat(rst, gr, grlen);
546 strcat(rst, " ");
547 free(ur);
548 free(gr);
551 if (show_dt == 1) {
552 td = ecalloc(tdlen, sizeof(char));
553 if (get_fdt(td, tdlen, cursor->td) > 0) {
554 strncat(rst, td, tdlen);
555 strcat(rst, " ");
557 free(td);
560 if (show_size == 1 && S_ISREG(cursor->mode)) {
561 sz = get_fsize(cursor->size);
562 strncat(rst, sz, szlen);
563 free(sz);
566 return rst;
569 static char *
570 get_fperm(mode_t mode)
572 char *buf;
573 size_t i;
575 const char chars[] = "rwxrwxrwx";
576 buf = ecalloc(11, sizeof(char));
578 if (S_ISDIR(mode))
579 buf[0] = 'd';
580 else if (S_ISREG(mode))
581 buf[0] = '-';
582 else if (S_ISLNK(mode))
583 buf[0] = 'l';
584 else if (S_ISBLK(mode))
585 buf[0] = 'b';
586 else if (S_ISCHR(mode))
587 buf[0] = 'c';
588 else if (S_ISFIFO(mode))
589 buf[0] = 'p';
590 else if (S_ISSOCK(mode))
591 buf[0] = 's';
592 else
593 buf[0] = '?';
595 for (i = 1; i < 10; i++) {
596 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
598 buf[10] = '\0';
600 return buf;
603 static char *
604 get_fsize(off_t size)
606 /* need to be freed */
607 char *Rsize;
608 float lsize;
609 int counter;
610 counter = 0;
612 Rsize = ecalloc(10, sizeof(char));
613 lsize = (float)size;
615 while (lsize >= 1000.0) {
616 lsize /= 1024.0;
617 ++counter;
620 float_to_string(lsize, Rsize);
622 switch (counter) {
623 case 0:
624 strcat(Rsize, "B");
625 break;
626 case 1:
627 strcat(Rsize, "K");
628 break;
629 case 2:
630 strcat(Rsize, "M");
631 break;
632 case 3:
633 strcat(Rsize, "G");
634 break;
635 case 4:
636 strcat(Rsize, "T");
637 break;
640 return Rsize;
643 static char *
644 get_fullpath(char *first, char *second)
646 char *full_path;
647 size_t full_path_len;
649 full_path_len = strlen(first) + strlen(second) + 2;
650 full_path = ecalloc(full_path_len, sizeof(char));
652 if (strcmp(first, "/") == 0) {
653 (void)snprintf(full_path, full_path_len, "/%s", second);
655 } else {
656 (void)snprintf(full_path, full_path_len, "%s/%s", first,
657 second);
660 return full_path;
663 static char *
664 get_fusr(uid_t status, size_t len)
666 char *result;
667 struct passwd *pw;
669 result = ecalloc(len, sizeof(char));
670 pw = getpwuid(status);
671 if (pw == NULL)
672 (void)snprintf(result, len - 1, "%u", status);
673 else
674 strncpy(result, pw->pw_name, len - 1);
676 return result;
679 static void
680 get_dirsize(char *fullpath, off_t *fullsize)
682 DIR *dir;
683 char *ent_full;
684 mode_t mode;
685 struct dirent *entry;
686 struct stat status;
688 dir = opendir(fullpath);
689 if (dir == NULL) {
690 return;
693 while ((entry = readdir(dir)) != 0) {
694 if ((strcmp(entry->d_name, ".") == 0 ||
695 strcmp(entry->d_name, "..") == 0))
696 continue;
698 ent_full = get_fullpath(fullpath, entry->d_name);
699 if (lstat(ent_full, &status) == 0) {
700 mode = status.st_mode;
701 if (S_ISDIR(mode)) {
702 get_dirsize(ent_full, fullsize);
703 free(ent_full);
704 } else {
705 *fullsize += status.st_size;
706 free(ent_full);
711 closedir(dir);
712 clear_status();
715 static void
716 get_hicol(Cpair *col, mode_t mode)
718 *col = cfile;
719 if (S_ISDIR(mode))
720 *col = cdir;
721 else if (S_ISLNK(mode))
722 *col = cother;
723 else if (chech_execf(mode) > 0)
724 *col = cexec;
727 static int
728 delent(char *fullpath)
730 char *confirmation;
731 char *rm_cmd[] = { "rm", "-rf", NULL };
733 confirmation = ecalloc(2, sizeof(char));
734 if ((get_usrinput(confirmation, 2, "delete file (Y) ?") < 0) ||
735 (strcmp(confirmation, "Y") != 0)) {
736 free(confirmation);
737 return 1; /* canceled by user or wrong confirmation */
739 free(confirmation);
741 return spawn(rm_cmd, fullpath);
744 static void
745 calcdir(void)
747 off_t *fullsize;
748 char *csize;
749 char *result;
751 if (S_ISDIR(cpane->direntr[cpane->hdir - 1].mode)) {
752 fullsize = ecalloc(50, sizeof(off_t));
753 get_dirsize(CURSOR_NAME, fullsize);
754 csize = get_fsize(*fullsize);
755 result = get_finfo(&cpane->direntr[cpane->hdir - 1]);
757 clear_status();
758 print_status(cstatus, "%d/%d %s%s", cpane->hdir, cpane->dirc,
759 result, csize);
760 free(csize);
761 free(fullsize);
762 free(result);
766 static void
767 crnd(void)
769 char *user_input, *path;
770 size_t pathlen;
772 user_input = ecalloc(MAX_USRI, sizeof(char));
773 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
774 free(user_input);
775 return;
778 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
779 path = ecalloc(pathlen, sizeof(char));
780 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
781 free(user_input);
782 free(path);
783 return;
786 if (mkdir(path, ndir_perm) < 0)
787 print_error(strerror(errno));
788 else if (listdir(AddHi, NULL) < 0)
789 print_error(strerror(errno));
791 free(user_input);
792 free(path);
795 static void
796 crnf(void)
798 char *user_input, *path;
799 size_t pathlen;
800 int rf;
802 user_input = ecalloc(MAX_USRI, sizeof(char));
803 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
804 free(user_input);
805 return;
808 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
809 path = ecalloc(pathlen, sizeof(char));
810 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
811 free(user_input);
812 free(path);
813 return;
816 rf = open(path, O_CREAT | O_EXCL, nf_perm);
818 if (rf < 0) {
819 print_error(strerror(errno));
820 } else {
821 if (close(rf) < 0)
822 print_error(strerror(errno));
823 else if (listdir(AddHi, NULL) < 0)
824 print_error(strerror(errno));
827 free(user_input);
828 free(path);
831 static void
832 delfd(void)
834 switch (delent(CURSOR_NAME)) {
835 case -1:
836 print_error(strerror(errno));
837 break;
838 case 0:
839 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
840 cpane->hdir--;
841 if (listdir(AddHi, NULL) < 0)
842 print_error(strerror(errno));
843 break;
847 static void
848 mvbk(void)
850 get_dirp(cpane->dirn);
851 if (check_dir(cpane->dirn) < 0) {
852 print_error(strerror(errno));
853 return;
856 rmwatch(cpane);
857 cpane->firstrow = cpane->parent_firstrow;
858 cpane->hdir = cpane->parent_row;
859 if (listdir(AddHi, NULL) < 0)
860 print_error(strerror(errno));
861 cpane->parent_firstrow = 0;
862 cpane->parent_row = 1;
865 static void
866 mvbtm(void)
868 if (cpane->dirc < 1)
869 return;
870 if (cpane->dirc > scrheight) {
871 rm_hi(cpane, cpane->hdir - 1);
872 cpane->hdir = cpane->dirc;
873 cpane->firstrow = cpane->dirc - scrheight + 1;
874 refresh_pane();
875 add_hi(cpane, cpane->hdir - 1);
876 } else {
877 rm_hi(cpane, cpane->hdir - 1);
878 cpane->hdir = cpane->dirc;
879 add_hi(cpane, cpane->hdir - 1);
881 print_info();
884 static void
885 mvdwn(void)
887 if (cpane->dirc < 1)
888 return;
889 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
890 rm_hi(cpane, cpane->hdir - 1);
891 cpane->hdir++;
892 add_hi(cpane, cpane->hdir - 1);
893 } else {
894 mvdwns(); /* scroll */
896 print_info();
899 static void
900 mvdwns(void)
902 int real;
903 real = cpane->hdir - 1 - cpane->firstrow;
905 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
906 cpane->firstrow++;
907 rm_hi(cpane, cpane->hdir - 1);
908 cpane->hdir++;
909 refresh_pane();
910 add_hi(cpane, cpane->hdir - 1);
911 } else if (cpane->hdir < cpane->dirc) {
912 rm_hi(cpane, cpane->hdir - 1);
913 cpane->hdir++;
914 add_hi(cpane, cpane->hdir - 1);
918 static void
919 mvfor(void)
921 rmwatch(cpane);
922 if (cpane->dirc < 1)
923 return;
924 int s;
926 switch (check_dir(CURSOR_NAME)) {
927 case 0:
928 strcpy(cpane->dirn, CURSOR_NAME);
929 cpane->parent_row = cpane->hdir;
930 cpane->parent_firstrow = cpane->firstrow;
931 cpane->hdir = 1;
932 cpane->firstrow = 0;
933 if (listdir(AddHi, NULL) < 0)
934 print_error(strerror(errno));
935 break;
936 case 1: /* not a directory open file */
937 tb_shutdown();
938 s = opnf(CURSOR_NAME);
939 if (tb_init() != 0)
940 die("tb_init");
941 t_resize();
942 if (s < 0)
943 print_error("process failed non-zero exit");
944 break;
945 case -1: /* failed to open directory */
946 print_error(strerror(errno));
950 static void
951 mvmid(void)
953 if (cpane->dirc < 1)
954 return;
955 rm_hi(cpane, cpane->hdir - 1);
956 if (cpane->dirc < scrheight / 2)
957 cpane->hdir = (cpane->dirc + 1) / 2;
958 else
959 cpane->hdir = (scrheight / 2) + cpane->firstrow;
960 add_hi(cpane, cpane->hdir - 1);
961 print_info();
964 static void
965 mvtop(void)
967 if (cpane->dirc < 1)
968 return;
969 if (cpane->dirc > scrheight) {
970 rm_hi(cpane, cpane->hdir - 1);
971 cpane->hdir = 1;
972 cpane->firstrow = 0;
973 refresh_pane();
974 add_hi(cpane, cpane->hdir - 1);
975 } else {
976 rm_hi(cpane, cpane->hdir - 1);
977 cpane->hdir = 1;
978 add_hi(cpane, cpane->hdir - 1);
979 print_info();
983 static void
984 mvup(void)
986 if (cpane->dirc < 1)
987 return;
988 if (cpane->dirc < scrheight && cpane->hdir > 1) {
989 rm_hi(cpane, cpane->hdir - 1);
990 cpane->hdir--;
991 add_hi(cpane, cpane->hdir - 1);
992 } else {
993 mvups(); /* scroll */
995 print_info();
998 static void
999 mvups(void)
1001 size_t real;
1002 real = cpane->hdir - 1 - cpane->firstrow;
1004 if (cpane->firstrow > 0 && real < 1 + scrsp) {
1005 cpane->firstrow--;
1006 rm_hi(cpane, cpane->hdir - 1);
1007 cpane->hdir--;
1008 refresh_pane();
1009 add_hi(cpane, cpane->hdir - 1);
1010 } else if (cpane->hdir > 1) {
1011 rm_hi(cpane, cpane->hdir - 1);
1012 cpane->hdir--;
1013 add_hi(cpane, cpane->hdir - 1);
1017 static void
1018 scrdwn(void)
1020 if (cpane->dirc < 1)
1021 return;
1022 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
1023 if (cpane->hdir < cpane->dirc - scrmv) {
1024 rm_hi(cpane, cpane->hdir - 1);
1025 cpane->hdir += scrmv;
1026 add_hi(cpane, cpane->hdir - 1);
1027 } else {
1028 mvbtm();
1030 } else {
1031 scrdwns();
1033 print_info();
1036 static void
1037 scrdwns(void)
1039 int real, dynmv;
1041 real = cpane->hdir - cpane->firstrow;
1042 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
1044 if (real + scrmv + 1 > scrheight &&
1045 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
1046 cpane->firstrow += dynmv;
1047 rm_hi(cpane, cpane->hdir - 1);
1048 cpane->hdir += scrmv;
1049 refresh_pane();
1050 add_hi(cpane, cpane->hdir - 1);
1051 } else {
1052 if (cpane->hdir < cpane->dirc - scrmv - 1) {
1053 rm_hi(cpane, cpane->hdir - 1);
1054 cpane->hdir += scrmv;
1055 add_hi(cpane, cpane->hdir - 1);
1056 } else {
1057 mvbtm();
1062 static void
1063 scrup(void)
1065 if (cpane->dirc < 1)
1066 return;
1067 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1068 if (cpane->hdir > scrmv) {
1069 rm_hi(cpane, cpane->hdir - 1);
1070 cpane->hdir = cpane->hdir - scrmv;
1071 add_hi(cpane, cpane->hdir - 1);
1072 print_info();
1073 } else {
1074 mvtop();
1076 } else {
1077 scrups();
1081 static void
1082 scrups(void)
1084 int real, dynmv;
1085 real = cpane->hdir - cpane->firstrow;
1086 dynmv = MIN(cpane->firstrow, scrmv);
1088 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1089 cpane->firstrow -= dynmv;
1090 rm_hi(cpane, cpane->hdir - 1);
1091 cpane->hdir -= scrmv;
1092 refresh_pane();
1093 add_hi(cpane, cpane->hdir - 1);
1094 } else {
1095 if (cpane->hdir > scrmv + 1) {
1096 rm_hi(cpane, cpane->hdir - 1);
1097 cpane->hdir -= scrmv;
1098 add_hi(cpane, cpane->hdir - 1);
1099 } else {
1100 mvtop();
1105 static int
1106 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1108 int height = tb_height();
1109 size_t startat;
1110 struct tb_event fev;
1111 size_t counter = (size_t)1;
1112 char empty = ' ';
1113 int x = 0;
1114 int name_size = 0;
1115 char buf[256];
1117 clear_status();
1119 va_list vl;
1120 Cpair col;
1121 col = cprompt;
1122 va_start(vl, fmt);
1123 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1124 va_end(vl);
1125 print_tb(buf, 1, height-1, col.fg, col.bg);
1126 startat = name_size + 1;
1127 tb_set_cursor((int)(startat + 1), height-1);
1128 tb_present();
1130 while (tb_poll_event(&fev) != 0) {
1131 switch (fev.type) {
1132 case TB_EVENT_KEY:
1133 if (fev.key == TB_KEY_ESC) {
1134 tb_set_cursor(-1, -1);
1135 clear_status();
1136 return -1;
1139 if (fev.key == TB_KEY_BACKSPACE ||
1140 fev.key == TB_KEY_BACKSPACE2) {
1141 if (BETWEEN(counter, 2, sout)) {
1142 out[x - 1] = '\0';
1143 counter--;
1144 x--;
1145 print_xstatus(empty, startat + counter);
1146 tb_set_cursor(startat + counter,
1147 theight - 1);
1150 } else if (fev.key == TB_KEY_ENTER) {
1151 tb_set_cursor(-1, -1);
1152 out[counter - 1] = '\0';
1153 return 0;
1155 } else {
1156 if (counter < sout) {
1157 print_xstatus((char)fev.ch,
1158 (startat + counter));
1159 out[x] = (char)fev.ch;
1160 tb_set_cursor((startat + counter + 1),
1161 theight - 1);
1162 counter++;
1163 x++;
1167 tb_present();
1168 break;
1170 default:
1171 return -1;
1175 return -1;
1178 static int
1179 frules(char *ex)
1181 size_t c, d;
1183 for (c = 0; c < LEN(rules); c++)
1184 for (d = 0; d < rules[c].exlen; d++)
1185 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1186 return c;
1187 return -1;
1190 static int
1191 spawn(const void *v, char *fn)
1193 int ws, x, argc, fd;
1194 pid_t pid, r;
1196 x = 0;
1197 argc = 0;
1199 /* count args */
1200 while (((char **)v)[x++] != NULL)
1201 argc++;
1203 char *argv[argc + 2];
1204 for ( x = 0; x < argc; x++)
1205 argv[x] = ((char **)v)[x];
1207 argv[argc] = fn;
1208 argv[argc + 1] = NULL;
1210 pid = fork();
1211 switch (pid) {
1212 case -1:
1213 return -1;
1214 case 0:
1215 fd = open("/dev/null",O_WRONLY);
1216 dup2(fd, STDERR_FILENO);
1217 execvp(argv[0], argv);
1218 exit(EXIT_SUCCESS);
1219 close(fd);
1220 default:
1221 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1222 continue;
1223 if (r == -1)
1224 return -1;
1225 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1226 return -1;
1228 return 0;
1231 static int
1232 opnf(char *fn)
1234 char *ex;
1235 int c;
1237 ex = get_ext(fn);
1238 c = frules(ex);
1239 free(ex);
1241 if (c < 0) /* extension not found open in editor */
1242 return spawn(editor, fn);
1243 else
1244 return spawn((char **)rules[c].v, fn);
1247 static int
1248 fsev_init(void)
1250 #if defined _SYS_INOTIFY_H
1251 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1252 if (inotify_fd < 0)
1253 return -1;
1254 #elif defined _SYS_EVENT_H_
1255 kq = kqueue();
1256 if (kq < 0)
1257 return -1;
1258 #endif
1259 return 0;
1262 static int
1263 addwatch(void)
1265 #if defined _SYS_INOTIFY_H
1266 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1267 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1268 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1269 #elif defined _SYS_EVENT_H_
1270 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1271 if (cpane->event_fd < 0)
1272 return cpane->event_fd;
1273 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1274 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1275 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1276 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1277 return 0;
1278 #endif
1281 static int
1282 read_events(void)
1284 #if defined _SYS_INOTIFY_H
1285 char *p;
1286 ssize_t r;
1287 struct inotify_event *event;
1288 const size_t events = 32;
1289 const size_t evbuflen =
1290 events * (sizeof(struct inotify_event) + MAX_N + 1);
1291 char buf[evbuflen];
1293 if (cpane->inotify_wd < 0)
1294 return -1;
1295 r = read(inotify_fd, buf, evbuflen);
1296 if (r <= 0)
1297 return r;
1299 for (p = buf; p < buf + r;) {
1300 event = (struct inotify_event *)p;
1301 if (!event->wd)
1302 break;
1303 if (event->mask) {
1304 return r;
1307 p += sizeof(struct inotify_event) + event->len;
1309 #elif defined _SYS_EVENT_H_
1310 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1311 #endif
1312 return -1;
1315 static void
1316 rmwatch(Pane *pane)
1318 #if defined _SYS_INOTIFY_H
1319 if (pane->inotify_wd >= 0)
1320 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1321 #elif defined _SYS_EVENT_H_
1322 close(pane->event_fd);
1323 return;
1324 #endif
1327 static void
1328 fsev_shdn(void)
1330 rmwatch(&pane_l);
1331 rmwatch(&pane_r);
1332 #if defined _SYS_INOTIFY_H
1333 close(inotify_fd);
1334 #elif defined _SYS_EVENT_H_
1335 close(kq);
1336 #endif
1339 static ssize_t
1340 findbm(uint32_t event)
1342 ssize_t i;
1344 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1345 if (event == bmarks[i].ch) {
1346 if (check_dir(bmarks[i].path) != 0) {
1347 print_error(strerror(errno));
1348 return -1;
1350 return i;
1353 return -1;
1356 static void
1357 filter(void)
1359 if (cpane->dirc < 1)
1360 return;
1361 char *user_input;
1362 user_input = ecalloc(MAX_USRI, sizeof(char));
1363 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1364 free(user_input);
1365 return;
1367 if (listdir(AddHi, user_input) < 0)
1368 print_error("no match");
1369 free(user_input);
1372 static void
1373 selection(void)
1375 struct tb_event fev;
1376 if (cpane->selection != NULL) {
1377 free(cpane->selection);
1378 cpane->selection = NULL;
1380 cpane->selection = ecalloc(cpane->dirc, sizeof(size_t));
1381 cpane->selection[0] = cpane->hdir;
1382 add_hi(cpane, cpane->selection[0] - 1);
1383 sl = 0;
1384 while (tb_poll_event(&fev) != 0) {
1385 switch (fev.type) {
1386 case TB_EVENT_KEY:
1387 grabkeys(&fev, skeys, skeyslen);
1388 if(sl == -1)
1389 return;
1390 tb_present();
1391 break;
1396 static void
1397 seldwn(void)
1399 mvdwn();
1400 print_prompt("VISUAL");
1401 int index = abs(cpane->hdir - cpane->selection[0]);
1403 if (cpane->hdir > cpane->selection[0]) {
1404 cpane->selection[index] = cpane->hdir;
1405 add_hi(cpane, cpane->selection[index] - 2);
1406 } else {
1407 cpane->selection[index + 1] = 0;
1409 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1410 selref();
1414 static void
1415 selup(void)
1417 mvup();
1418 print_prompt("VISUAL");
1419 int index = abs(cpane->hdir - cpane->selection[0]);
1421 if (cpane->hdir < cpane->selection[0]) {
1422 cpane->selection[index] = cpane->hdir;
1423 add_hi(cpane, cpane->selection[index]);
1424 } else if (index < cpane->dirc) {
1425 cpane->selection[index + 1] = 0;
1427 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1428 selref();
1432 static void
1433 selref(void)
1435 int i;
1436 for (i = 0; i < cpane->dirc; i++) {
1437 if (cpane->selection[i] < (scrheight + cpane->firstrow) && cpane->selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1438 add_hi(cpane, cpane->selection[i] - 1);
1443 static void
1444 selcan(void)
1446 refresh_pane();
1447 add_hi(cpane, cpane->hdir - 1);
1448 print_prompt("Cancel");
1449 sl = -1;
1452 static void
1453 selall(void)
1455 int i;
1456 for (i = 0; i < cpane->dirc; i++) {
1457 cpane->selection[i] = i + 1;
1459 selref();
1462 static void
1463 selcalc(void)
1465 selection_size = 0;
1466 int j;
1468 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1469 if (cpane->selection[j] != 0)
1470 selection_size++;
1471 else
1472 break;
1476 static void
1477 free_files(void)
1479 for (size_t i = 0; i < selection_size; i++) {
1480 free(files[i]);
1481 files[i] = NULL;
1483 free(files);
1484 files = NULL;
1487 static void
1488 init_files(void)
1490 if (files != NULL)
1491 free_files();
1493 selcalc();
1494 files = ecalloc(selection_size, sizeof(char*));
1496 for (size_t i = 0; i < selection_size; i++) {
1497 files[i] = ecalloc(MAX_P, sizeof(char));
1498 char *tmp = get_path_hdir(cpane->selection[i]);
1499 strcpy(files[i], tmp);
1500 free(tmp);
1504 static void
1505 selynk(void)
1507 init_files();
1508 if (listdir(AddHi, NULL) < 0)
1509 print_error(strerror(errno));
1510 print_status(cprompt, "%d files are yanked", selection_size);
1511 sl = -1;
1514 static void
1515 seldel(void)
1517 char *confirmation;
1518 size_t i;
1520 confirmation = ecalloc((size_t)2, sizeof(char));
1521 if ((get_usrinput(
1522 confirmation, (size_t)2,"delete directory (Y) ?") < 0) ||
1523 (strcmp(confirmation, "Y") != 0)) {
1524 free(confirmation);
1525 if (listdir(AddHi, NULL) < 0)
1526 print_error(strerror(errno));
1527 sl = -1;
1528 return;
1530 free(confirmation);
1532 init_files();
1533 for (i = 0; i < selection_size; i++) {
1534 if (delent(files[i]) < 0)
1535 print_error(strerror(errno));
1538 print_status(cprompt, "%d files are deleted", selection_size);
1539 free_files();
1540 sl = -1;
1543 static void
1544 selpst(void)
1546 if (strlen(yank_file) != 0) {
1547 char *cp_cmd[] = { "cp", "-r", yank_file, NULL };
1548 print_status(cprompt, "coping");
1549 if (spawn(cp_cmd, cpane->dirn) != 0)
1550 print_error("coping failed");
1551 else
1552 print_status(cprompt, "files are copied");
1553 yank_file[0] = '\0'; /* set yank_file len 0 */
1554 return;
1557 print_error("nothing to paste");
1559 // if (files == NULL) {
1560 // return;
1561 // }
1563 // size_t i;
1565 // for (i = 0; i < selection_size; i++) {
1566 // char *cp_cmd[] = { "cp", "-rf", files[i], cpane->dirn, NULL };
1567 // spawn(cp_cmd, NULL);
1568 // }
1570 // free_files();
1571 // print_status(cprompt, "%d files are copied", selection_size);
1575 static void
1576 selmv(void)
1578 if (strlen(yank_file) != 0) {
1579 char *mv_cmd[] = { "mv", yank_file, NULL };
1580 print_status(cprompt, "moving");
1581 if (spawn(mv_cmd, cpane->dirn) != 0)
1582 print_error("moving failed");
1583 else
1584 print_status(cprompt, "file moved");
1585 yank_file[0] = '\0'; /* set yank_file len 0 */
1586 return;
1589 print_error("nothing to move");
1593 // if (files == NULL) {
1594 // return;
1595 // }
1597 // size_t i;
1599 // for (i = 0; i < selection_size; i++) {
1600 // char *mv_cmd[] = { "mv", files[i], cpane->dirn, NULL };
1601 // spawn(mv_cmd, NULL);
1602 // }
1604 // free_files();
1605 // print_status(cprompt, "%d files are copied", selection_size);
1609 static void
1610 selrename(void)
1612 init_files();
1613 for (size_t i = 0; i < selection_size; i++) {
1614 xrename(files[i]);
1616 if (listdir(AddHi, NULL) < 0)
1617 print_error(strerror(errno));
1618 free_files();
1619 sl = -1;
1622 static void
1623 xrename(char *path)
1625 char *new_name;
1626 new_name = ecalloc(MAX_N, sizeof(char));
1628 if (get_usrinput(new_name, MAX_N, "rename: %s", basename(path)) < 0) {
1629 free(new_name);
1630 return;
1632 char *rename_cmd[] = {"mv", path, new_name, NULL};
1634 if (spawn(rename_cmd, NULL) < 0)
1635 print_error(strerror(errno));
1637 free(new_name);
1640 static char*
1641 get_path_hdir(int Ndir)
1643 char *fullpath;
1644 fullpath = ecalloc(MAX_P, sizeof(char));
1645 strcpy(fullpath, cpane->dirn);
1646 strcat(fullpath, "/");
1647 strcat(fullpath, cpane->direntr[Ndir-1].name);
1649 return fullpath;
1652 static void
1653 rname(void)
1655 if (cpane->selection != NULL) {
1656 free(cpane->selection);
1657 cpane->selection = NULL;
1659 cpane->selection = ecalloc(2, sizeof(size_t));
1660 cpane->selection[0] = cpane->hdir;
1661 selrename();
1664 static void
1665 yank(void)
1667 strncpy(yank_file, CURSOR_NAME, MAX_P);
1668 print_status(cprompt, "1 file is yanked", selection_size);
1672 static void
1673 switch_pane(void)
1675 if (cpane->dirc > 0)
1676 rm_hi(cpane, cpane->hdir - 1);
1677 if (cpane == &pane_l)
1678 cpane = &pane_r;
1679 else if (cpane == &pane_r)
1680 cpane = &pane_l;
1681 if (cpane->dirc > 0) {
1682 add_hi(cpane, cpane->hdir - 1);
1683 print_info();
1684 } else {
1685 clear_status();
1689 static void
1690 quit(void)
1692 if (sl == -1) { /* check if selection was allocated */
1693 free(cpane->selection);
1694 if (files != NULL)
1695 free_files();
1697 free(pane_l.direntr);
1698 free(pane_r.direntr);
1699 fsev_shdn();
1700 tb_shutdown();
1701 exit(EXIT_SUCCESS);
1704 static void
1705 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1707 size_t i;
1708 ssize_t b;
1710 for (i = 0; i < max_keys; i++) {
1711 if (event->ch != 0) {
1712 if (event->ch == key[i].evkey.ch) {
1713 key[i].func();
1714 return;
1716 } else if (event->key != 0) {
1717 if (event->key == key[i].evkey.key) {
1718 key[i].func();
1719 return;
1724 /* bookmarks */
1725 b = findbm(event->ch);
1726 if (b < 0)
1727 return;
1728 rmwatch(cpane);
1729 strcpy(cpane->dirn, bmarks[b].path);
1730 cpane->firstrow = 0;
1731 cpane->parent_row = 1;
1732 cpane->hdir = 1;
1733 if (listdir(AddHi, NULL) < 0)
1734 print_error(strerror(errno));
1737 static void
1738 start_ev(void)
1740 struct tb_event ev;
1742 for (;;) {
1743 int t = tb_peek_event(&ev, 2000);
1744 if (t < 0) {
1745 tb_shutdown();
1746 return;
1749 if (t == 1) /* keyboard event */
1750 grabkeys(&ev, nkeys, nkeyslen);
1751 else if (t == 2) /* resize event */
1752 t_resize();
1753 else if (t == 0) /* filesystem event */
1754 if (read_events() > 0)
1755 if (listdir(AddHi, NULL) < 0)
1756 print_error(strerror(errno));
1758 tb_present();
1759 continue;
1761 tb_shutdown();
1764 static void
1765 refresh_pane(void)
1767 size_t y, dyn_max, start_from;
1768 int width;
1769 width = (twidth / 2) - 4;
1770 Cpair col;
1772 y = 1;
1773 start_from = cpane->firstrow;
1774 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1776 /* print each entry in directory */
1777 while (start_from < dyn_max) {
1778 get_hicol(&col, cpane->direntr[start_from].mode);
1779 print_row(cpane, start_from, col);
1780 start_from++;
1781 y++;
1784 if (cpane->dirc > 0)
1785 print_info();
1786 else
1787 clear_status();
1789 /* print current directory title */
1790 cpane->dircol.fg |= TB_BOLD;
1791 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1794 static void
1795 set_direntr(struct dirent *entry, DIR *dir)
1797 int i;
1798 struct stat status;
1800 i = 0;
1801 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1802 while ((entry = readdir(dir)) != 0) {
1803 if ((strcmp(entry->d_name, ".") == 0 ||
1804 strcmp(entry->d_name, "..") == 0))
1805 continue;
1807 char *full = get_fullpath(cpane->dirn, entry->d_name);
1808 strcpy(cpane->direntr[i].name, full);
1809 if (lstat(full, &status) == 0) {
1810 cpane->direntr[i].size = status.st_size;
1811 cpane->direntr[i].mode = status.st_mode;
1812 cpane->direntr[i].group = status.st_gid;
1813 cpane->direntr[i].user = status.st_uid;
1814 cpane->direntr[i].td = status.st_mtime;
1816 i++;
1817 free(full);
1818 // }
1820 cpane->dirc = i;
1823 static int
1824 listdir(int hi, char *filter)
1826 DIR *dir;
1827 struct dirent *entry;
1828 int width;
1829 size_t i;
1830 int filtercount = 0;
1831 size_t oldc = cpane->dirc;
1833 width = (twidth / 2) - 4;
1834 cpane->dirc = 0;
1835 i = 0;
1837 dir = opendir(cpane->dirn);
1838 if (dir == NULL)
1839 return -1;
1841 /* get content and filter sum */
1842 while ((entry = readdir(dir)) != 0) {
1843 if (filter != NULL) {
1844 if (strstr(entry->d_name, filter) != NULL)
1845 filtercount++;
1846 } else { /* no filter */
1847 cpane->dirc++;
1851 if (filter == NULL) {
1852 clear_pane();
1853 cpane->dirc -= 2;
1856 if (filter != NULL) {
1857 if (filtercount > 0) {
1858 cpane->dirc -= 2;
1859 cpane->dirc = filtercount;
1860 clear_pane();
1861 cpane->hdir = 1;
1862 } else if (filtercount == 0) {
1863 if (closedir(dir) < 0)
1864 return -1;
1865 cpane->dirc = oldc;
1866 return -1;
1870 /* print current directory title */
1871 cpane->dircol.fg |= TB_BOLD;
1872 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1874 if (addwatch() < 0)
1875 print_error("can't add watch");
1877 /* empty directory */
1878 if (cpane->dirc == 0) {
1879 clear_status();
1880 if (closedir(dir) < 0)
1881 return -1;
1882 return 0;
1885 rewinddir(dir); /* reset position */
1886 set_direntr(entry, dir); /* create array of entries */
1887 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1888 refresh_pane();
1890 if (hi == AddHi)
1891 add_hi(cpane, cpane->hdir - 1);
1893 if (closedir(dir) < 0)
1894 return -1;
1895 return 0;
1898 static void
1899 t_resize(void)
1901 /* TODO need refactoring */
1902 tb_clear();
1903 draw_frame();
1904 pane_r.dirx = (twidth / 2) + 2;
1906 if (cpane == &pane_l) {
1907 cpane = &pane_r;
1908 refresh_pane();
1909 cpane = &pane_l;
1910 refresh_pane();
1911 if (cpane->dirc > 0)
1912 add_hi(&pane_l, pane_l.hdir - 1);
1913 } else if (cpane == &pane_r) {
1914 cpane = &pane_l;
1915 refresh_pane();
1916 cpane = &pane_r;
1917 refresh_pane();
1918 if (cpane->dirc > 0)
1919 add_hi(&pane_r, pane_r.hdir - 1);
1922 tb_present();
1925 static void
1926 get_editor(void)
1928 editor[0] = getenv("EDITOR");
1929 editor[1] = NULL;
1931 if (editor[0] == NULL)
1932 editor[0] = fed;
1935 static void
1936 set_panes(void)
1938 char *home;
1939 char cwd[MAX_P];
1941 home = getenv("HOME");
1942 if (home == NULL)
1943 home = "/";
1944 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1945 strncpy(cwd, home, MAX_P);
1947 pane_l.pane_id = 0;
1948 pane_l.dirx = 2;
1949 pane_l.dircol = cpanell;
1950 pane_l.firstrow = 0;
1951 pane_l.direntr = ecalloc(0, sizeof(Entry));
1952 strcpy(pane_l.dirn, cwd);
1953 pane_l.hdir = 1;
1954 pane_l.inotify_wd = -1;
1955 pane_l.parent_row = 1;
1957 pane_r.pane_id = 1;
1958 pane_r.dirx = (twidth / 2) + 2;
1959 pane_r.dircol = cpanelr;
1960 pane_r.firstrow = 0;
1961 pane_r.direntr = ecalloc(0, sizeof(Entry));
1962 strcpy(pane_r.dirn, home);
1963 pane_r.hdir = 1;
1964 pane_r.inotify_wd = -1;
1965 pane_r.parent_row = 1;
1968 static void
1969 draw_frame(void)
1971 int i;
1972 theight = tb_height();
1973 twidth = tb_width();
1974 scrheight = theight - 2;
1976 /* 2 horizontal lines */
1977 for (i = 1; i < twidth - 1; ++i) {
1978 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1979 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1982 /* 3 vertical lines */
1983 for (i = 1; i < theight - 1; ++i) {
1984 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1985 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1986 cframe.bg);
1987 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1990 /* 4 corners */
1991 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1992 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1993 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1994 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1996 /* 2 middel top and bottom */
1997 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1998 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
2001 static void
2002 start(void)
2004 if (tb_init() != 0)
2005 die("tb_init");
2006 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
2007 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
2008 die("output error");
2010 draw_frame();
2011 set_panes();
2012 get_editor();
2013 if (fsev_init() < 0)
2014 print_error(strerror(errno));
2015 cpane = &pane_r;
2016 if (listdir(NoHi, NULL) < 0)
2017 print_error(strerror(errno));
2018 cpane = &pane_l;
2019 if (listdir(AddHi, NULL) < 0)
2020 print_error(strerror(errno));
2021 tb_present();
2022 start_ev();
2026 main(int argc, char *argv[])
2028 #ifdef __OpenBSD__
2029 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
2030 NULL) == -1)
2031 die("pledge");
2032 #endif /* __OpenBSD__ */
2033 if (argc == 1)
2034 start();
2035 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
2036 die("sfm-" VERSION);
2037 else
2038 die("usage: sfm [-v]");
2039 return 0;