1 /* See LICENSE file for copyright and license details. */
5 #elif defined(__APPLE__)
6 #define _DARWIN_C_SOURCE
7 #elif defined(__FreeBSD__)
8 #define __BSD_VISIBLE 1
10 #include <sys/types.h>
11 #include <sys/resource.h>
15 #if defined(__linux__)
16 #include <sys/inotify.h>
17 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
19 #include <sys/event.h>
46 #define MAX_STATUS 255
51 #define CURSOR(x) (x)->direntr[(x)->hdir - 1]
70 char dirn
[MAX_P
]; // dir name cwd
72 Entry
*direntr
; // dir entries
73 int dirx
; // pane cwd x pos
74 int dirc
; // dir entries sum
75 int hdir
; // highlighted dir
78 int parent_row
; // FIX
97 uint16_t key
; /* one of the TB_KEY_* constants */
98 uint32_t ch
; /* unicode character */
106 /* function declarations */
107 static void print_tb(const char *, int, int, uint16_t, uint16_t);
108 static void printf_tb(int, int, Cpair
, const char *, ...);
109 static void print_status(Cpair
, const char *, ...);
110 static void print_xstatus(char, int);
111 static void print_error(char *);
112 static void print_prompt(char *);
113 static void print_info(Pane
*, char *);
114 static void print_row(Pane
*, size_t, Cpair
);
115 static void clear(int, int, int, uint16_t);
116 static void clear_status(void);
117 static void clear_pane(Pane
*);
118 static void add_hi(Pane
*, size_t);
119 static void rm_hi(Pane
*, size_t);
120 static int check_dir(char *);
121 static mode_t
chech_execf(mode_t
);
122 static int sort_name(const void *const, const void *const);
123 static void get_dirp(char *);
124 static char *get_ext(char *);
125 static int get_fdt(char *, time_t);
126 static char *get_fgrp(gid_t
);
127 static char *get_fperm(mode_t
);
128 static char *get_fsize(off_t
);
129 static char *get_fullpath(char *, char *);
130 static char *get_fusr(uid_t
);
131 static void get_dirsize(char *, off_t
*);
132 static void get_hicol(Cpair
*, mode_t
);
133 static void delent(void);
134 static void calcdir(void);
135 static void crnd(void);
136 static void crnf(void);
137 static void mvbk(void);
138 static void mvbtm(void);
139 static void mvdwn(void);
140 static void mvdwns(void);
141 static void mvfwd(void);
142 static void mvmid(void);
143 static void mvtop(void);
144 static void mvup(void);
145 static void mvups(void);
146 static void scrdwn(void);
147 static void scrdwns(void);
148 static void scrup(void);
149 static void scrups(void);
150 static int get_usrinput(char *, size_t, const char *, ...);
151 static int frules(char *);
152 static int spawn(const void *, size_t, const void *, size_t, char *);
153 static int opnf(char *);
154 static int fsev_init(void);
155 static int addwatch(Pane
*);
156 static int read_events(void);
157 static void rmwatch(Pane
*);
158 static void fsev_shdn(void);
159 static ssize_t
findbm(uint32_t);
160 static void start_filter(void);
161 static void start_vmode(void);
162 static void exit_vmode(void);
163 static void selup(void);
164 static void seldwn(void);
165 static void selall(void);
166 static void selref(void);
167 static void selynk(void);
168 static void selcalc(void);
169 static void paste(void);
170 static void selmv(void);
171 static void seldel(void);
172 static void init_files(void);
173 static void free_files(void);
174 static void yank(void);
175 static void rname(void);
176 static void switch_pane(void);
177 static void quit(void);
178 static void grabkeys(struct tb_event
*, Key
*, size_t);
179 static void *read_th(void *arg
);
180 static void start_ev(void);
181 static void refresh_pane(Pane
*);
182 static void set_direntr(Pane
*, struct dirent
*, DIR *, char *);
183 static int listdir(Pane
*);
184 static void t_resize(void);
185 static void set_panes(void);
186 static void draw_frame(void);
187 static void start(void);
189 /* global variables */
190 static pthread_t fsev_thread
;
191 static Pane pane_r
, pane_l
, *cpane
;
192 static char *editor
[2];
193 static char fed
[] = "vi";
194 static int theight
, twidth
, scrheight
;
195 static size_t selection_size
= 0;
196 static int *selection
;
197 static int cont_vmode
= 0;
198 static char **selected_files
;
200 #if defined(_SYS_INOTIFY_H)
202 static int inotify_fd
;
203 #elif defined(_SYS_EVENT_H_)
206 struct kevent evlist
[2]; /* events we want to monitor */
207 struct kevent chlist
[2]; /* events that were triggered */
208 static struct timespec gtimeout
;
210 #if defined(__linux__) || defined(__FreeBSD__)
212 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
216 /* configuration, allows nested code to access above variables */
219 /* function implementations */
221 print_tb(const char *str
, int x
, int y
, uint16_t fg
, uint16_t bg
)
223 while (*str
!= '\0') {
225 str
+= tb_utf8_char_to_unicode(&uni
, str
);
226 tb_change_cell(x
, y
, uni
, fg
, bg
);
232 printf_tb(int x
, int y
, Cpair col
, const char *fmt
, ...)
237 (void)vsnprintf(buf
, MAX_LINE
, fmt
, vl
);
239 print_tb(buf
, x
, y
, col
.fg
, col
.bg
);
243 print_status(Cpair col
, const char *fmt
, ...)
245 char buf
[MAX_STATUS
];
248 (void)vsnprintf(buf
, MAX_STATUS
, fmt
, vl
);
251 print_tb(buf
, 1, theight
- 1, col
.fg
, col
.bg
);
255 print_xstatus(char c
, int x
)
258 (void)tb_utf8_char_to_unicode(&uni
, &c
);
259 tb_change_cell(x
, theight
- 1, uni
, cstatus
.fg
, cstatus
.bg
);
263 print_error(char *errmsg
)
265 print_status(cerr
, errmsg
);
269 print_prompt(char *prompt
)
271 print_status(cprompt
, prompt
);
275 print_info(Pane
*pane
, char *dirsize
)
277 char *sz
, *ur
, *gr
, *dt
, *prm
;
279 dt
= ecalloc(MAX_DTF
, sizeof(char));
281 prm
= get_fperm(CURSOR(pane
).mode
);
282 ur
= get_fusr(CURSOR(pane
).user
);
283 gr
= get_fgrp(CURSOR(pane
).group
);
285 if (get_fdt(dt
, CURSOR(pane
).dt
) < 0)
288 if (S_ISREG(CURSOR(pane
).mode
)) {
289 sz
= get_fsize(CURSOR(pane
).size
);
291 if (dirsize
== NULL
) {
292 sz
= ecalloc(1, sizeof(char));
299 print_status(cstatus
, "%02d/%02d %s %s:%s %s %s", pane
->hdir
,
300 pane
->dirc
, prm
, ur
, gr
, dt
, sz
);
310 print_row(Pane
*pane
, size_t entpos
, Cpair col
)
315 char lnk_full
[MAX_P
];
318 width
= (twidth
/ 2) - 4;
319 result
= basename(pane
->direntr
[entpos
].name
);
321 y
= entpos
- pane
->firstrow
+ 1;
323 if (S_ISLNK(pane
->direntr
[entpos
].mode
) != 0) {
324 if (realpath(pane
->direntr
[entpos
].name
, buf
) != NULL
) {
326 lnk_full
, MAX_N
, "%s -> %s", result
, buf
);
331 printf_tb(x
, y
, col
, "%*.*s", ~width
, width
, result
);
335 clear(int sx
, int ex
, int y
, uint16_t bg
)
337 /* clear line from to */
338 /* x = line number vertical */
339 /* y = column number horizontal */
341 for (i
= sx
; i
< ex
; i
++) {
342 tb_change_cell(i
, y
, 0x0000, TB_DEFAULT
, bg
);
349 clear(1, twidth
- 1, theight
- 1, cstatus
.bg
);
353 clear_pane(Pane
*pane
)
356 y
= 0, i
= 0, ex
= 0;
359 ex
= (twidth
/ 2) - 1;
360 else if (pane
== &pane_r
)
363 while (i
< scrheight
) {
364 clear(pane
->dirx
, ex
, y
, TB_DEFAULT
);
370 for (y
= pane
->dirx
; y
< ex
; ++y
) {
371 tb_change_cell(y
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
376 add_hi(Pane
*pane
, size_t entpos
)
379 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
380 col
.fg
|= TB_REVERSE
| TB_BOLD
;
381 col
.bg
|= TB_REVERSE
;
382 print_row(pane
, entpos
, col
);
386 rm_hi(Pane
*pane
, size_t entpos
)
389 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
390 print_row(pane
, entpos
, col
);
394 check_dir(char *path
)
400 if (errno
== ENOTDIR
) {
407 if (closedir(dir
) < 0)
414 chech_execf(mode_t mode
)
417 return (((S_IXUSR
| S_IXGRP
| S_IXOTH
) & mode
));
422 sort_name(const void *const A
, const void *const B
)
425 mode_t data1
= (*(Entry
*)A
).mode
;
426 mode_t data2
= (*(Entry
*)B
).mode
;
430 } else if (data1
== data2
) {
431 result
= strncmp((*(Entry
*)A
).name
, (*(Entry
*)B
).name
, MAX_N
);
444 len
= strnlen(cdir
, MAX_P
);
448 for (i
= len
- 1; i
> 1; i
--) {
455 cdir
[len
- counter
- 1] = '\0';
463 size_t counter
, len
, i
;
467 len
= strnlen(str
, MAX_N
);
469 for (i
= len
- 1; i
> 0; i
--) {
477 ext
= ecalloc(MAX_EXT
+ 1, sizeof(char));
478 strncpy(ext
, &str
[len
- counter
], MAX_EXT
);
484 get_fdt(char *result
, time_t status
)
487 localtime_r(&status
, <
);
488 return strftime(result
, MAX_DTF
, dtfmt
, <
);
492 get_fgrp(gid_t status
)
497 result
= ecalloc(MAX_GRPN
, sizeof(char));
498 gr
= getgrgid(status
);
500 (void)snprintf(result
, MAX_GRPN
, "%u", status
);
502 strncpy(result
, gr
->gr_name
, MAX_GRPN
);
504 result
[MAX_GRPN
- 1] = '\0';
509 get_fperm(mode_t mode
)
514 const char chars
[] = "rwxrwxrwx";
515 buf
= ecalloc(11, sizeof(char));
519 else if (S_ISREG(mode
))
521 else if (S_ISLNK(mode
))
523 else if (S_ISBLK(mode
))
525 else if (S_ISCHR(mode
))
527 else if (S_ISFIFO(mode
))
529 else if (S_ISSOCK(mode
))
534 for (i
= 1; i
< 10; i
++) {
535 buf
[i
] = (mode
& (1 << (9 - i
))) ? chars
[i
- 1] : '-';
543 get_fsize(off_t size
)
545 char *result
; /* need to be freed */
551 result_len
= 6; /* 9999X/0 */
552 result
= ecalloc(result_len
, sizeof(char));
554 while (size
>= 1000) {
579 if (snprintf(result
, result_len
, OFF_T
"%c", size
, unit
) < 0)
580 strncat(result
, "???", result_len
);
586 get_fullpath(char *first
, char *second
)
590 full_path
= ecalloc(MAX_P
, sizeof(char));
592 if (strncmp(first
, "/", MAX_P
) == 0)
593 (void)snprintf(full_path
, MAX_P
, "/%s", second
);
595 (void)snprintf(full_path
, MAX_P
, "%s/%s", first
, second
);
601 get_fusr(uid_t status
)
606 result
= ecalloc(MAX_USRN
, sizeof(char));
607 pw
= getpwuid(status
);
609 (void)snprintf(result
, MAX_USRN
, "%u", status
);
611 strncpy(result
, pw
->pw_name
, MAX_USRN
);
613 result
[MAX_USRN
- 1] = '\0';
618 get_dirsize(char *fullpath
, off_t
*fullsize
)
623 struct dirent
*entry
;
626 dir
= opendir(fullpath
);
631 while ((entry
= readdir(dir
)) != 0) {
632 if ((strncmp(entry
->d_name
, ".", 2) == 0 ||
633 strncmp(entry
->d_name
, "..", 3) == 0))
636 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
637 if (lstat(ent_full
, &status
) == 0) {
638 mode
= status
.st_mode
;
640 get_dirsize(ent_full
, fullsize
);
643 *fullsize
+= status
.st_size
;
654 get_hicol(Cpair
*col
, mode_t mode
)
659 else if (S_ISLNK(mode
))
661 else if (chech_execf(mode
) > 0)
674 inp_conf
= ecalloc(conf_len
, sizeof(char));
675 if ((get_usrinput(inp_conf
, conf_len
, "delete file (yes) ?") < 0) ||
676 (strncmp(inp_conf
, conf
, conf_len
) != 0)) {
678 return; /* canceled by user or wrong inp_conf */
683 tmp
[0] = CURSOR(cpane
).name
;
684 if (spawn(rm_cmd
, rm_cmd_len
, tmp
, 1, NULL
) < 0) {
685 print_error(strerror(errno
));
689 if (BETWEEN(cpane
->hdir
- 1, 1, cpane
->dirc
)) /* last entry */
698 if (!S_ISDIR(CURSOR(cpane
).mode
))
704 fullsize
= ecalloc(1, sizeof(off_t
));
705 get_dirsize(CURSOR(cpane
).name
, fullsize
);
706 csize
= get_fsize(*fullsize
);
708 CURSOR(cpane
).size
= *fullsize
;
709 print_info(cpane
, csize
);
716 char *user_input
, *path
;
718 user_input
= ecalloc(MAX_USRI
, sizeof(char));
719 if (get_usrinput(user_input
, MAX_USRI
, "new dir") < 0) {
724 path
= ecalloc(MAX_P
, sizeof(char));
725 if (snprintf(path
, MAX_P
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
731 if (mkdir(path
, ndir_perm
) < 0)
732 print_error(strerror(errno
));
741 char *user_input
, *path
;
744 user_input
= ecalloc(MAX_USRI
, sizeof(char));
745 if (get_usrinput(user_input
, MAX_USRI
, "new file") < 0) {
750 path
= ecalloc(MAX_P
, sizeof(char));
751 if (snprintf(path
, MAX_P
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
757 rf
= open(path
, O_CREAT
| O_EXCL
, nf_perm
);
760 print_error(strerror(errno
));
761 else if (close(rf
) < 0)
762 print_error(strerror(errno
));
771 if (cpane
->dirn
[0] == '/' && cpane
->dirn
[1] == '\0') { /* cwd = / */
775 get_dirp(cpane
->dirn
);
776 if (check_dir(cpane
->dirn
) < 0) {
777 print_error(strerror(errno
));
782 cpane
->firstrow
= cpane
->parent_firstrow
;
783 cpane
->hdir
= cpane
->parent_row
;
784 if (listdir(cpane
) < 0)
785 print_error(strerror(errno
));
786 cpane
->parent_firstrow
= 0;
787 cpane
->parent_row
= 1;
795 if (cpane
->dirc
> scrheight
) {
796 rm_hi(cpane
, cpane
->hdir
- 1);
797 cpane
->hdir
= cpane
->dirc
;
798 cpane
->firstrow
= cpane
->dirc
- scrheight
+ 1;
800 add_hi(cpane
, cpane
->hdir
- 1);
802 rm_hi(cpane
, cpane
->hdir
- 1);
803 cpane
->hdir
= cpane
->dirc
;
804 add_hi(cpane
, cpane
->hdir
- 1);
806 print_info(cpane
, NULL
);
814 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
815 rm_hi(cpane
, cpane
->hdir
- 1);
817 add_hi(cpane
, cpane
->hdir
- 1);
819 mvdwns(); /* scroll */
821 print_info(cpane
, NULL
);
828 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
830 if (real
> scrheight
- 3 - scrsp
&& cpane
->hdir
+ scrsp
< cpane
->dirc
) {
832 rm_hi(cpane
, cpane
->hdir
- 1);
835 add_hi(cpane
, cpane
->hdir
- 1);
836 } else if (cpane
->hdir
< cpane
->dirc
) {
837 rm_hi(cpane
, cpane
->hdir
- 1);
839 add_hi(cpane
, cpane
->hdir
- 1);
851 switch (check_dir(CURSOR(cpane
).name
)) {
853 strncpy(cpane
->dirn
, CURSOR(cpane
).name
, MAX_P
);
854 cpane
->parent_row
= cpane
->hdir
;
855 cpane
->parent_firstrow
= cpane
->firstrow
;
858 if (listdir(cpane
) < 0)
859 print_error(strerror(errno
));
861 case 1: /* not a directory open file */
863 s
= opnf(CURSOR(cpane
).name
);
868 print_error("process failed non-zero exit");
870 case -1: /* failed to open directory */
871 print_error(strerror(errno
));
880 rm_hi(cpane
, cpane
->hdir
- 1);
881 if (cpane
->dirc
< scrheight
/ 2)
882 cpane
->hdir
= (cpane
->dirc
+ 1) / 2;
884 cpane
->hdir
= (scrheight
/ 2) + cpane
->firstrow
;
885 add_hi(cpane
, cpane
->hdir
- 1);
886 print_info(cpane
, NULL
);
894 if (cpane
->dirc
> scrheight
) {
895 rm_hi(cpane
, cpane
->hdir
- 1);
899 add_hi(cpane
, cpane
->hdir
- 1);
901 rm_hi(cpane
, cpane
->hdir
- 1);
903 add_hi(cpane
, cpane
->hdir
- 1);
904 print_info(cpane
, NULL
);
913 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
914 rm_hi(cpane
, cpane
->hdir
- 1);
916 add_hi(cpane
, cpane
->hdir
- 1);
918 mvups(); /* scroll */
920 print_info(cpane
, NULL
);
927 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
929 if (cpane
->firstrow
> 0 && real
< 1 + scrsp
) {
931 rm_hi(cpane
, cpane
->hdir
- 1);
934 add_hi(cpane
, cpane
->hdir
- 1);
935 } else if (cpane
->hdir
> 1) {
936 rm_hi(cpane
, cpane
->hdir
- 1);
938 add_hi(cpane
, cpane
->hdir
- 1);
947 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
948 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
949 rm_hi(cpane
, cpane
->hdir
- 1);
950 cpane
->hdir
+= scrmv
;
951 add_hi(cpane
, cpane
->hdir
- 1);
958 print_info(cpane
, NULL
);
966 real
= cpane
->hdir
- cpane
->firstrow
;
967 dynmv
= MIN(cpane
->dirc
- cpane
->hdir
- cpane
->firstrow
, scrmv
);
969 if (real
+ scrmv
+ 1 > scrheight
&&
970 cpane
->hdir
+ scrsp
+ scrmv
< cpane
->dirc
) { /* scroll */
971 cpane
->firstrow
+= dynmv
;
972 rm_hi(cpane
, cpane
->hdir
- 1);
973 cpane
->hdir
+= scrmv
;
975 add_hi(cpane
, cpane
->hdir
- 1);
977 if (cpane
->hdir
< cpane
->dirc
- scrmv
- 1) {
978 rm_hi(cpane
, cpane
->hdir
- 1);
979 cpane
->hdir
+= scrmv
;
980 add_hi(cpane
, cpane
->hdir
- 1);
992 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
993 if (cpane
->hdir
> scrmv
) {
994 rm_hi(cpane
, cpane
->hdir
- 1);
995 cpane
->hdir
= cpane
->hdir
- scrmv
;
996 add_hi(cpane
, cpane
->hdir
- 1);
997 print_info(cpane
, NULL
);
1010 real
= cpane
->hdir
- cpane
->firstrow
;
1011 dynmv
= MIN(cpane
->firstrow
, scrmv
);
1013 if (cpane
->firstrow
> 0 && real
< scrmv
+ scrsp
) {
1014 cpane
->firstrow
-= dynmv
;
1015 rm_hi(cpane
, cpane
->hdir
- 1);
1016 cpane
->hdir
-= scrmv
;
1017 refresh_pane(cpane
);
1018 add_hi(cpane
, cpane
->hdir
- 1);
1020 if (cpane
->hdir
> scrmv
+ 1) {
1021 rm_hi(cpane
, cpane
->hdir
- 1);
1022 cpane
->hdir
-= scrmv
;
1023 add_hi(cpane
, cpane
->hdir
- 1);
1031 get_usrinput(char *out
, size_t sout
, const char *fmt
, ...)
1033 int height
= tb_height();
1035 struct tb_event fev
;
1036 size_t counter
= (size_t)1;
1048 name_size
= vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
1050 print_tb(buf
, 1, height
- 1, col
.fg
, col
.bg
);
1051 startat
= name_size
+ 1;
1052 tb_set_cursor((int)(startat
+ 1), height
- 1);
1055 while (tb_poll_event(&fev
) != 0) {
1058 if (fev
.key
== TB_KEY_ESC
) {
1059 tb_set_cursor(-1, -1);
1064 if (fev
.key
== TB_KEY_BACKSPACE
||
1065 fev
.key
== TB_KEY_BACKSPACE2
) {
1066 if (BETWEEN(counter
, 2, sout
)) {
1070 print_xstatus(empty
, startat
+ counter
);
1072 startat
+ counter
, theight
- 1);
1075 } else if (fev
.key
== TB_KEY_ENTER
) {
1076 tb_set_cursor(-1, -1);
1077 out
[counter
- 1] = '\0';
1081 if (counter
< sout
) {
1082 print_xstatus((char)fev
.ch
,
1083 (startat
+ counter
));
1084 out
[x
] = (char)fev
.ch
;
1085 tb_set_cursor((startat
+ counter
+ 1),
1108 for (c
= 0; c
< LEN(rules
); c
++)
1109 for (d
= 0; d
< rules
[c
].exlen
; d
++)
1110 if (strncmp(rules
[c
].ext
[d
], ex
, MAX_EXT
) == 0)
1116 spawn(const void *com_argv
, size_t com_argc
, const void *f_argv
, size_t f_argc
,
1123 argc
= com_argc
+ f_argc
+ 2;
1126 memcpy(argv
, com_argv
, com_argc
* sizeof(char *)); /* command */
1127 memcpy(&argv
[com_argc
], f_argv
, f_argc
* sizeof(char *)); /* files */
1129 argv
[argc
- 2] = fn
;
1130 argv
[argc
- 1] = NULL
;
1137 execvp(argv
[0], argv
);
1140 while ((r
= waitpid(pid
, &ws
, 0)) == -1 && errno
== EINTR
)
1144 if ((WIFEXITED(ws
) != 0) && (WEXITSTATUS(ws
) != 0))
1160 if (c
< 0) /* extension not found open in editor */
1161 return spawn(editor
, 1, NULL
, 0, fn
);
1163 return spawn((char **)rules
[c
].v
, rules
[c
].vlen
, NULL
, 0, fn
);
1169 #if defined(_SYS_INOTIFY_H)
1170 inotify_fd
= inotify_init();
1173 #elif defined(_SYS_EVENT_H_)
1174 gtimeout
.tv_sec
= 1;
1183 addwatch(Pane
*pane
)
1185 #if defined(_SYS_INOTIFY_H)
1186 return pane
->inotify_wd
= inotify_add_watch(inotify_fd
, pane
->dirn
,
1187 IN_MODIFY
| IN_MOVED_FROM
| IN_MOVED_TO
| IN_CREATE
|
1188 IN_DELETE
| IN_DELETE_SELF
| IN_MOVE_SELF
);
1189 #elif defined(_SYS_EVENT_H_)
1190 pane
->event_fd
= open(pane
->dirn
, O_RDONLY
);
1191 if (pane
->event_fd
< 0)
1192 return pane
->event_fd
;
1193 EV_SET(&evlist
[pane
->pane_id
], pane
->event_fd
, EVFILT_VNODE
,
1195 NOTE_DELETE
| NOTE_EXTEND
| NOTE_LINK
| NOTE_RENAME
|
1196 NOTE_REVOKE
| NOTE_WRITE
,
1205 #if defined(_SYS_INOTIFY_H)
1208 struct inotify_event
*event
;
1209 const size_t events
= 32;
1210 const size_t evbuflen
=
1211 events
* (sizeof(struct inotify_event
) + MAX_N
+ 1);
1214 if (cpane
->inotify_wd
< 0)
1216 r
= read(inotify_fd
, buf
, evbuflen
);
1220 for (p
= buf
; p
< buf
+ r
;) {
1221 event
= (struct inotify_event
*)p
;
1228 p
+= sizeof(struct inotify_event
) + event
->len
;
1230 #elif defined(_SYS_EVENT_H_)
1231 return kevent(kq
, evlist
, 2, chlist
, 2, >imeout
);
1239 #if defined(_SYS_INOTIFY_H)
1240 if (pane
->inotify_wd
>= 0)
1241 inotify_rm_watch(inotify_fd
, pane
->inotify_wd
);
1242 #elif defined(_SYS_EVENT_H_)
1243 close(pane
->event_fd
);
1250 pthread_cancel(fsev_thread
);
1251 #if defined(__linux__)
1252 pthread_join(fsev_thread
, NULL
);
1256 #if defined(_SYS_INOTIFY_H)
1258 #elif defined(_SYS_EVENT_H_)
1264 findbm(uint32_t event
)
1268 for (i
= 0; i
< (ssize_t
)LEN(bmarks
); i
++) {
1269 if (event
== bmarks
[i
].ch
) {
1270 if (check_dir(bmarks
[i
].path
) != 0) {
1271 print_error(strerror(errno
));
1283 if (cpane
->dirc
< 1)
1286 user_input
= ecalloc(MAX_USRI
, sizeof(char));
1287 if (get_usrinput(user_input
, MAX_USRI
, "filter") < 0) {
1291 cpane
->filter
= user_input
;
1292 if (listdir(cpane
) < 0)
1293 print_error("no match");
1294 cpane
->filter
= NULL
;
1301 if (cpane
->dirc
< 1)
1303 struct tb_event fev
;
1304 if (selection
!= NULL
) {
1309 selection
= ecalloc(cpane
->dirc
, sizeof(size_t));
1310 selection
[0] = cpane
->hdir
;
1312 print_prompt("-- VISUAL --");
1314 while (tb_poll_event(&fev
) != 0) {
1317 grabkeys(&fev
, vkeys
, vkeyslen
);
1318 if (cont_vmode
== -1)
1329 refresh_pane(cpane
);
1330 add_hi(cpane
, cpane
->hdir
- 1);
1338 print_prompt("-- VISUAL --");
1339 int index
= abs(cpane
->hdir
- selection
[0]);
1341 if (cpane
->hdir
< selection
[0]) {
1342 selection
[index
] = cpane
->hdir
;
1343 add_hi(cpane
, selection
[index
]);
1344 } else if (index
< cpane
->dirc
) {
1345 selection
[index
+ 1] = 0;
1347 if (cpane
->dirc
>= scrheight
||
1348 cpane
->hdir
<= 1) { /* rehighlight all if scrolling */
1357 print_prompt("-- VISUAL --");
1358 int index
= abs(cpane
->hdir
- selection
[0]);
1360 if (cpane
->hdir
> selection
[0]) {
1361 selection
[index
] = cpane
->hdir
;
1362 add_hi(cpane
, selection
[index
] - 2);
1364 selection
[index
+ 1] = 0;
1366 if (cpane
->dirc
>= scrheight
||
1367 cpane
->hdir
>= cpane
->dirc
) { /* rehighlight all if scrolling */
1376 for (i
= 0; i
< cpane
->dirc
; i
++) {
1377 selection
[i
] = i
+ 1;
1386 for (i
= 0; i
< cpane
->dirc
; i
++) {
1387 if (selection
[i
] < (scrheight
+ cpane
->firstrow
) &&
1389 cpane
->firstrow
) { /* checks if in the frame of the directories */
1390 add_hi(cpane
, selection
[i
] - 1);
1401 for (j
= 0; j
< cpane
->dirc
; j
++) { /* calculate used selection size */
1402 if (selection
[j
] != 0)
1414 if (selected_files
!= NULL
) {
1415 for (i
= 0; i
< selection_size
; i
++) {
1416 free(selected_files
[i
]);
1417 selected_files
[i
] = NULL
;
1419 free(selected_files
);
1420 selected_files
= NULL
;
1431 selected_files
= ecalloc(selection_size
, sizeof(char *));
1433 for (i
= 0; i
< selection_size
; i
++) {
1434 selected_files
[i
] = ecalloc(MAX_P
, sizeof(char));
1435 strncpy(selected_files
[i
],
1436 cpane
->direntr
[selection
[i
] - 1].name
, MAX_P
);
1444 refresh_pane(cpane
);
1445 add_hi(cpane
, cpane
->hdir
- 1);
1446 print_status(cprompt
, "%zu files are yanked", selection_size
);
1455 char conf
[] = "yes";
1457 inp_conf
= ecalloc(conf_len
, sizeof(char));
1458 if ((get_usrinput(inp_conf
, conf_len
, "delete file (yes) ?") < 0) ||
1459 (strncmp(inp_conf
, conf
, conf_len
) != 0)) {
1461 return; /* canceled by user or wrong inp_conf */
1467 if (spawn(rm_cmd
, rm_cmd_len
, selected_files
, selection_size
, NULL
) < 0)
1468 print_error(strerror(errno
));
1470 print_status(cprompt
, "%zu files are deleted", selection_size
);
1472 if (cpane
->dirc
> 0)
1482 if (selected_files
== NULL
) {
1483 print_error("nothing to paste");
1487 if (spawn(cp_cmd
, cp_cmd_len
, selected_files
, selection_size
,
1489 print_error(strerror(errno
));
1491 print_status(cprompt
, "%zu files are copied", selection_size
);
1499 if (selected_files
== NULL
) {
1500 print_error("nothing to move");
1504 if (spawn(mv_cmd
, mv_cmd_len
, selected_files
, selection_size
,
1506 print_error(strerror(errno
));
1508 print_status(cprompt
, "%zu files are moved", selection_size
);
1516 if (cpane
->dirc
< 1)
1518 char new_name
[MAX_P
];
1521 input_name
= ecalloc(MAX_N
, sizeof(char));
1523 if (get_usrinput(input_name
, MAX_N
, "rename: %s",
1524 basename(CURSOR(cpane
).name
)) < 0) {
1529 if (snprintf(new_name
, MAX_P
, "%s/%s", cpane
->dirn
, input_name
) < 0) {
1531 print_error(strerror(errno
));
1535 char *rename_cmd
[] = { "mv", CURSOR(cpane
).name
, new_name
};
1536 if (spawn(rename_cmd
, 3, NULL
, 0, NULL
) < 0)
1537 print_error(strerror(errno
));
1545 if (cpane
->dirc
< 1)
1550 selected_files
= ecalloc(selection_size
, sizeof(char *));
1551 selected_files
[0] = ecalloc(MAX_P
, sizeof(char));
1552 strncpy(selected_files
[0], CURSOR(cpane
).name
, MAX_P
);
1553 print_status(cprompt
, "1 file is yanked", selection_size
);
1559 if (cpane
->dirc
> 0)
1560 rm_hi(cpane
, cpane
->hdir
- 1);
1561 if (cpane
== &pane_l
)
1563 else if (cpane
== &pane_r
)
1565 if (cpane
->dirc
> 0) {
1566 add_hi(cpane
, cpane
->hdir
- 1);
1567 print_info(cpane
, NULL
);
1576 if (cont_vmode
== -1) { /* check if selection was allocated */
1578 if (selected_files
!= NULL
)
1581 free(pane_l
.direntr
);
1582 free(pane_r
.direntr
);
1589 grabkeys(struct tb_event
*event
, Key
*key
, size_t max_keys
)
1594 for (i
= 0; i
< max_keys
; i
++) {
1595 if (event
->ch
!= 0) {
1596 if (event
->ch
== key
[i
].evkey
.ch
) {
1600 } else if (event
->key
!= 0) {
1601 if (event
->key
== key
[i
].evkey
.key
) {
1609 b
= findbm(event
->ch
);
1613 strncpy(cpane
->dirn
, bmarks
[b
].path
, MAX_P
);
1614 cpane
->firstrow
= 0;
1615 cpane
->parent_row
= 1;
1617 if (listdir(cpane
) < 0)
1618 print_error(strerror(errno
));
1624 struct timespec tim
;
1626 tim
.tv_nsec
= 5000000L; /* 0.005 sec */
1629 if (read_events() > READEVSZ
) {
1630 kill(main_pid
, SIGUSR1
);
1631 nanosleep(&tim
, NULL
);
1641 while (tb_poll_event(&ev
) != 0) {
1644 grabkeys(&ev
, nkeys
, nkeyslen
);
1647 case TB_EVENT_RESIZE
:
1658 refresh_pane(Pane
*pane
)
1660 size_t y
, dyn_max
, start_from
;
1662 width
= (twidth
/ 2) - 4;
1666 start_from
= pane
->firstrow
;
1667 dyn_max
= MIN(pane
->dirc
, (scrheight
- 1) + pane
->firstrow
);
1669 /* print each entry in directory */
1670 while (start_from
< dyn_max
) {
1671 get_hicol(&col
, pane
->direntr
[start_from
].mode
);
1672 print_row(pane
, start_from
, col
);
1678 print_info(pane
, NULL
);
1682 /* print current directory title */
1683 pane
->dircol
.fg
|= TB_BOLD
;
1684 printf_tb(pane
->dirx
, 0, pane
->dircol
, " %.*s ", width
, pane
->dirn
);
1688 set_direntr(Pane
*pane
, struct dirent
*entry
, DIR *dir
, char *filter
)
1695 tmpfull = get_fullpath(pane->dirn, entry->d_name); \
1696 strncpy(pane->direntr[i].name, tmpfull, MAX_N); \
1697 if (lstat(tmpfull, &status) == 0) { \
1698 pane->direntr[i].size = status.st_size; \
1699 pane->direntr[i].mode = status.st_mode; \
1700 pane->direntr[i].group = status.st_gid; \
1701 pane->direntr[i].user = status.st_uid; \
1702 pane->direntr[i].dt = status.st_mtime; \
1709 erealloc(pane
->direntr
, (10 + pane
->dirc
) * sizeof(Entry
));
1710 while ((entry
= readdir(dir
)) != 0) {
1711 if ((strncmp(entry
->d_name
, ".", 2) == 0 ||
1712 strncmp(entry
->d_name
, "..", 3) == 0))
1715 if (filter
== NULL
) {
1717 } else if (filter
!= NULL
) {
1718 if (strcasestr(entry
->d_name
, filter
) != NULL
) {
1731 struct dirent
*entry
;
1733 int filtercount
= 0;
1734 size_t oldc
= pane
->dirc
;
1736 width
= (twidth
/ 2) - 4;
1739 dir
= opendir(pane
->dirn
);
1743 /* get content and filter sum */
1744 while ((entry
= readdir(dir
)) != 0) {
1745 if (pane
->filter
!= NULL
) {
1746 if (strcasestr(entry
->d_name
, pane
->filter
) != NULL
)
1748 } else { /* no filter */
1753 if (pane
->filter
== NULL
) {
1758 if (pane
->filter
!= NULL
) {
1759 if (filtercount
> 0) {
1760 pane
->dirc
= filtercount
;
1763 } else if (filtercount
== 0) {
1764 if (closedir(dir
) < 0)
1771 /* print current directory title */
1772 pane
->dircol
.fg
|= TB_BOLD
;
1773 printf_tb(pane
->dirx
, 0, pane
->dircol
, " %.*s ", width
, pane
->dirn
);
1775 if (pane
->filter
== NULL
) /* dont't watch when filtering */
1776 if (addwatch(pane
) < 0)
1777 print_error("can't add watch");
1779 /* empty directory */
1780 if (pane
->dirc
== 0) {
1782 if (closedir(dir
) < 0)
1787 rewinddir(dir
); /* reset position */
1789 pane
, entry
, dir
, pane
->filter
); /* create array of entries */
1790 qsort(pane
->direntr
, pane
->dirc
, sizeof(Entry
), sort_name
);
1793 if (pane
== cpane
) {
1794 if (pane
->hdir
>= pane
->dirc
)
1795 add_hi(pane
, pane
->dirc
);
1797 add_hi(pane
, pane
->hdir
- 1);
1800 if (closedir(dir
) < 0)
1808 /* TODO need refactoring */
1811 pane_r
.dirx
= (twidth
/ 2) + 2;
1813 if (cpane
== &pane_l
) {
1814 refresh_pane(&pane_r
);
1815 refresh_pane(&pane_l
);
1816 if (cpane
->dirc
> 0)
1817 add_hi(&pane_l
, pane_l
.hdir
- 1);
1818 } else if (cpane
== &pane_r
) {
1819 refresh_pane(&pane_l
);
1820 refresh_pane(&pane_r
);
1821 if (cpane
->dirc
> 0)
1822 add_hi(&pane_r
, pane_r
.hdir
- 1);
1831 editor
[0] = getenv("EDITOR");
1834 if (editor
[0] == NULL
)
1844 home
= getenv("HOME");
1847 if ((getcwd(cwd
, sizeof(cwd
)) == NULL
))
1848 strncpy(cwd
, home
, MAX_P
);
1852 pane_l
.dircol
= cpanell
;
1853 pane_l
.firstrow
= 0;
1854 pane_l
.direntr
= ecalloc(0, sizeof(Entry
));
1855 strncpy(pane_l
.dirn
, cwd
, MAX_P
);
1857 pane_l
.inotify_wd
= -1;
1858 pane_l
.parent_row
= 1;
1861 pane_r
.dirx
= (twidth
/ 2) + 2;
1862 pane_r
.dircol
= cpanelr
;
1863 pane_r
.firstrow
= 0;
1864 pane_r
.direntr
= ecalloc(0, sizeof(Entry
));
1865 strncpy(pane_r
.dirn
, home
, MAX_P
);
1867 pane_r
.inotify_wd
= -1;
1868 pane_r
.parent_row
= 1;
1875 theight
= tb_height();
1876 twidth
= tb_width();
1877 scrheight
= theight
- 2;
1879 /* 2 horizontal lines */
1880 for (i
= 1; i
< twidth
- 1; ++i
) {
1881 tb_change_cell(i
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
1882 tb_change_cell(i
, theight
- 2, u_hl
, cframe
.fg
, cframe
.bg
);
1885 /* 4 vertical lines */
1886 for (i
= 1; i
< theight
- 1; ++i
) {
1887 tb_change_cell(0, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1889 (twidth
- 1) / 2, i
- 1, u_vl
, cframe
.fg
, cframe
.bg
);
1890 tb_change_cell(((twidth
- 1) / 2) + 1, i
- 1, u_vl
, cframe
.fg
,
1892 tb_change_cell(twidth
- 1, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1896 tb_change_cell(0, 0, u_cnw
, cframe
.fg
, cframe
.bg
);
1897 tb_change_cell(twidth
- 1, 0, u_cne
, cframe
.fg
, cframe
.bg
);
1898 tb_change_cell(0, theight
- 2, u_csw
, cframe
.fg
, cframe
.bg
);
1899 tb_change_cell(twidth
- 1, theight
- 2, u_cse
, cframe
.fg
, cframe
.bg
);
1901 /* 2 middel top and bottom */
1902 tb_change_cell((twidth
- 1) / 2, 0, u_mn
, cframe
.fg
, cframe
.bg
);
1904 (twidth
- 1) / 2, theight
- 2, u_ms
, cframe
.fg
, cframe
.bg
);
1911 if (cpane
== &pane_l
) {
1912 if (listdir(&pane_r
) < 0)
1913 print_error(strerror(errno
));
1914 if (listdir(&pane_l
) < 0)
1915 print_error(strerror(errno
));
1916 } else if (cpane
== &pane_r
) {
1917 if (listdir(&pane_l
) < 0)
1918 print_error(strerror(errno
));
1919 if (listdir(&pane_r
) < 0)
1920 print_error(strerror(errno
));
1928 struct sigaction sa
;
1930 main_pid
= getpid();
1931 sa
.sa_handler
= th_handler
;
1932 sigemptyset(&sa
.sa_mask
);
1933 sa
.sa_flags
= SA_RESTART
;
1934 return sigaction(SIGUSR1
, &sa
, NULL
);
1942 if (tb_select_output_mode(TB_OUTPUT_256
) != TB_OUTPUT_256
)
1943 if (tb_select_output_mode(TB_OUTPUT_NORMAL
) != TB_OUTPUT_NORMAL
)
1944 die("output error");
1949 if (start_signal() < 0)
1950 print_error(strerror(errno
));
1951 if (fsev_init() < 0)
1952 print_error(strerror(errno
));
1954 if (listdir(&pane_r
) < 0)
1955 print_error(strerror(errno
));
1956 if (listdir(&pane_l
) < 0)
1957 print_error(strerror(errno
));
1960 pthread_create(&fsev_thread
, NULL
, read_th
, NULL
);
1965 main(int argc
, char *argv
[])
1967 #if defined(__OpenBSD__)
1968 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1971 #endif /* __OpenBSD__ */
1974 else if (argc
== 2 && strncmp("-v", argv
[1], 2) == 0)
1975 die("sfm-" VERSION
);
1977 die("usage: sfm [-v]");