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>
45 #define MAX_STATUS 255
50 #define CURSOR cpane->direntr[cpane->hdir - 1]
53 enum { AddHi
, NoHi
}; /* add highlight in listdir */
72 char dirn
[MAX_P
]; // dir name cwd
74 Entry
*direntr
; // dir entries
75 int dirx
; // pane cwd x pos
76 int dirc
; // dir entries sum
77 int hdir
; // highlighted dir
80 int parent_row
; // FIX
98 uint16_t key
; /* one of the TB_KEY_* constants */
99 uint32_t ch
; /* unicode character */
107 /* function declarations */
108 static void print_tb(const char *, int, int, uint16_t, uint16_t);
109 static void printf_tb(int, int, Cpair
, const char *, ...);
110 static void print_status(Cpair
, const char *, ...);
111 static void print_xstatus(char, int);
112 static void print_error(char *);
113 static void print_prompt(char *);
114 static void print_info(char *);
115 static void print_row(Pane
*, size_t, Cpair
);
116 static void clear(int, int, int, uint16_t);
117 static void clear_status(void);
118 static void clear_pane(void);
119 static void add_hi(Pane
*, size_t);
120 static void rm_hi(Pane
*, size_t);
121 static int check_dir(char *);
122 static mode_t
chech_execf(mode_t
);
123 static int sort_name(const void *const, const void *const);
124 static void get_dirp(char *);
125 static char *get_ext(char *);
126 static int get_fdt(char *, time_t);
127 static char *get_fgrp(gid_t
);
128 static char *get_fperm(mode_t
);
129 static char *get_fsize(off_t
);
130 static char *get_fullpath(char *, char *);
131 static char *get_fusr(uid_t
);
132 static void get_dirsize(char *, off_t
*);
133 static void get_hicol(Cpair
*, mode_t
);
134 static int delent(char *);
135 static void calcdir(void);
136 static void crnd(void);
137 static void crnf(void);
138 static void delfd(void);
139 static void mvbk(void);
140 static void mvbtm(void);
141 static void mvdwn(void);
142 static void mvdwns(void);
143 static void mvfwd(void);
144 static void mvmid(void);
145 static void mvtop(void);
146 static void mvup(void);
147 static void mvups(void);
148 static void scrdwn(void);
149 static void scrdwns(void);
150 static void scrup(void);
151 static void scrups(void);
152 static int get_usrinput(char *, size_t, const char *, ...);
153 static int frules(char *);
154 static int spawn(const void *, char *);
155 static int opnf(char *);
156 static int fsev_init(void);
157 static int addwatch(void);
158 static int read_events(void);
159 static void rmwatch(Pane
*);
160 static void fsev_shdn(void);
161 static ssize_t
findbm(uint32_t);
162 static void start_filter(void);
163 static void start_vmode(void);
164 static void exit_vmode(void);
165 static void selup(void);
166 static void seldwn(void);
167 static void selall(void);
168 static void selref(void);
169 static void selynk(void);
170 static void selcalc(void);
171 static void paste(void);
172 static void selmv(void);
173 static void seldel(void);
174 static void init_files(void);
175 static void free_files(void);
176 static void yank(void);
177 static void rname(void);
178 static void switch_pane(void);
179 static void quit(void);
180 static void grabkeys(struct tb_event
*, Key
*, size_t);
181 static void *read_th(void *arg
);
182 static void start_ev(void);
183 static void refresh_pane(void);
184 static void set_direntr(struct dirent
*, DIR *, char *);
185 static int listdir(int);
186 static void t_resize(void);
187 static void set_panes(void);
188 static void draw_frame(void);
189 static void start(void);
191 /* global variables */
192 static pthread_t fsev_thread
;
193 static Pane pane_r
, pane_l
, *cpane
;
194 static char *editor
[2];
195 static char fed
[] = "vi";
196 static int theight
, twidth
, scrheight
;
197 static size_t selection_size
= 0;
198 static char yank_file
[MAX_P
];
199 static int *selection
;
200 static int cont_vmode
= 0;
201 static char **selected_files
;
202 #if defined _SYS_INOTIFY_H
204 static int inotify_fd
;
205 #elif defined _SYS_EVENT_H_
208 struct kevent evlist
[2]; /* events we want to monitor */
209 struct kevent chlist
[2]; /* events that were triggered */
210 static struct timespec gtimeout
;
212 #if defined(__linux__) || defined(__FreeBSD__)
214 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
218 /* configuration, allows nested code to access above variables */
221 /* function implementations */
223 print_tb(const char *str
, int x
, int y
, uint16_t fg
, uint16_t bg
)
225 while (*str
!= '\0') {
227 str
+= tb_utf8_char_to_unicode(&uni
, str
);
228 tb_change_cell(x
, y
, uni
, fg
, bg
);
234 printf_tb(int x
, int y
, Cpair col
, const char *fmt
, ...)
239 (void)vsnprintf(buf
, MAX_LINE
, fmt
, vl
);
241 print_tb(buf
, x
, y
, col
.fg
, col
.bg
);
245 print_status(Cpair col
, const char *fmt
, ...)
247 char buf
[MAX_STATUS
];
250 (void)vsnprintf(buf
, MAX_STATUS
, fmt
, vl
);
253 print_tb(buf
, 1, theight
- 1, col
.fg
, col
.bg
);
257 print_xstatus(char c
, int x
)
260 (void)tb_utf8_char_to_unicode(&uni
, &c
);
261 tb_change_cell(x
, theight
- 1, uni
, cstatus
.fg
, cstatus
.bg
);
265 print_error(char *errmsg
)
267 print_status(cerr
, errmsg
);
271 print_prompt(char *prompt
)
273 print_status(cprompt
, prompt
);
277 print_info(char *dirsize
)
279 char *sz
, *ur
, *gr
, *dt
, *prm
;
281 dt
= ecalloc(MAX_DTF
, sizeof(char));
283 prm
= get_fperm(CURSOR
.mode
);
284 ur
= get_fusr(CURSOR
.user
);
285 gr
= get_fgrp(CURSOR
.group
);
287 if (get_fdt(dt
, CURSOR
.dt
) < 0)
290 if (S_ISREG(CURSOR
.mode
)) {
291 sz
= get_fsize(CURSOR
.size
);
293 if (dirsize
== NULL
) {
294 sz
= ecalloc(1, sizeof(char));
301 print_status(cstatus
, "%02d/%02d %s %s:%s %s %s", cpane
->hdir
,
302 cpane
->dirc
, prm
, ur
, gr
, dt
, sz
);
312 print_row(Pane
*pane
, size_t entpos
, Cpair col
)
317 char lnk_full
[MAX_P
];
320 width
= (twidth
/ 2) - 4;
321 result
= basename(pane
->direntr
[entpos
].name
);
323 y
= entpos
- cpane
->firstrow
+ 1;
325 if (S_ISLNK(pane
->direntr
[entpos
].mode
) != 0) {
326 if (realpath(pane
->direntr
[entpos
].name
, buf
) != NULL
) {
328 lnk_full
, MAX_N
, "%s -> %s", result
, buf
);
333 printf_tb(x
, y
, col
, "%*.*s", ~width
, width
, result
);
337 clear(int sx
, int ex
, int y
, uint16_t bg
)
339 /* clear line from to */
340 /* x = line number vertical */
341 /* y = column number horizontal */
343 for (i
= sx
; i
< ex
; i
++) {
344 tb_change_cell(i
, y
, 0x0000, TB_DEFAULT
, bg
);
351 clear(1, twidth
- 1, theight
- 1, cstatus
.bg
);
358 y
= 0, i
= 0, ex
= 0;
360 if (cpane
->pane_id
== pane_l
.pane_id
)
361 ex
= (twidth
/ 2) - 1;
362 else if (cpane
->pane_id
== pane_r
.pane_id
)
365 while (i
< scrheight
) {
366 clear(cpane
->dirx
, ex
, y
, TB_DEFAULT
);
372 for (y
= cpane
->dirx
; y
< ex
; ++y
) {
373 tb_change_cell(y
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
378 add_hi(Pane
*pane
, size_t entpos
)
381 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
382 col
.fg
|= TB_REVERSE
| TB_BOLD
;
383 col
.bg
|= TB_REVERSE
;
384 print_row(pane
, entpos
, col
);
388 rm_hi(Pane
*pane
, size_t entpos
)
391 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
392 print_row(pane
, entpos
, col
);
396 check_dir(char *path
)
402 if (errno
== ENOTDIR
) {
409 if (closedir(dir
) < 0)
416 chech_execf(mode_t mode
)
419 return (((S_IXUSR
| S_IXGRP
| S_IXOTH
) & mode
));
424 sort_name(const void *const A
, const void *const B
)
427 mode_t data1
= (*(Entry
*)A
).mode
;
428 mode_t data2
= (*(Entry
*)B
).mode
;
432 } else if (data1
== data2
) {
433 result
= strncmp((*(Entry
*)A
).name
, (*(Entry
*)B
).name
, MAX_N
);
446 len
= strnlen(cdir
, MAX_P
);
450 for (i
= len
- 1; i
> 1; i
--) {
457 cdir
[len
- counter
- 1] = '\0';
465 size_t counter
, len
, i
;
469 len
= strnlen(str
, MAX_N
);
471 for (i
= len
- 1; i
> 0; i
--) {
479 ext
= ecalloc(MAX_EXT
+ 1, sizeof(char));
480 strncpy(ext
, &str
[len
- counter
], MAX_EXT
);
486 get_fdt(char *result
, time_t status
)
489 localtime_r(&status
, <
);
490 return strftime(result
, MAX_DTF
, dtfmt
, <
);
494 get_fgrp(gid_t status
)
499 result
= ecalloc(MAX_GRPN
, sizeof(char));
500 gr
= getgrgid(status
);
502 (void)snprintf(result
, MAX_GRPN
, "%u", status
);
504 strncpy(result
, gr
->gr_name
, MAX_GRPN
);
506 result
[MAX_GRPN
- 1] = '\0';
511 get_fperm(mode_t mode
)
516 const char chars
[] = "rwxrwxrwx";
517 buf
= ecalloc(11, sizeof(char));
521 else if (S_ISREG(mode
))
523 else if (S_ISLNK(mode
))
525 else if (S_ISBLK(mode
))
527 else if (S_ISCHR(mode
))
529 else if (S_ISFIFO(mode
))
531 else if (S_ISSOCK(mode
))
536 for (i
= 1; i
< 10; i
++) {
537 buf
[i
] = (mode
& (1 << (9 - i
))) ? chars
[i
- 1] : '-';
545 get_fsize(off_t size
)
547 char *result
; /* need to be freed */
553 result_len
= 6; /* 9999X/0 */
554 result
= ecalloc(result_len
, sizeof(char));
556 while (size
>= 1000) {
581 if (snprintf(result
, result_len
, OFF_T
"%c", size
, unit
) < 0)
582 strncat(result
, "???", result_len
);
588 get_fullpath(char *first
, char *second
)
592 full_path
= ecalloc(MAX_P
, sizeof(char));
594 if (strncmp(first
, "/", MAX_P
) == 0)
595 (void)snprintf(full_path
, MAX_P
, "/%s", second
);
597 (void)snprintf(full_path
, MAX_P
, "%s/%s", first
, second
);
603 get_fusr(uid_t status
)
608 result
= ecalloc(MAX_USRN
, sizeof(char));
609 pw
= getpwuid(status
);
611 (void)snprintf(result
, MAX_USRN
, "%u", status
);
613 strncpy(result
, pw
->pw_name
, MAX_USRN
);
615 result
[MAX_USRN
- 1] = '\0';
620 get_dirsize(char *fullpath
, off_t
*fullsize
)
625 struct dirent
*entry
;
628 dir
= opendir(fullpath
);
633 while ((entry
= readdir(dir
)) != 0) {
634 if ((strncmp(entry
->d_name
, ".", 2) == 0 ||
635 strncmp(entry
->d_name
, "..", 3) == 0))
638 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
639 if (lstat(ent_full
, &status
) == 0) {
640 mode
= status
.st_mode
;
642 get_dirsize(ent_full
, fullsize
);
645 *fullsize
+= status
.st_size
;
656 get_hicol(Cpair
*col
, mode_t mode
)
661 else if (S_ISLNK(mode
))
663 else if (chech_execf(mode
) > 0)
668 delent(char *fullpath
)
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 1; /* canceled by user or wrong inp_conf */
682 return spawn(rm_cmd
, fullpath
);
688 if (!S_ISDIR(CURSOR
.mode
))
694 fullsize
= ecalloc(1, sizeof(off_t
));
695 get_dirsize(CURSOR
.name
, fullsize
);
696 csize
= get_fsize(*fullsize
);
698 CURSOR
.size
= *fullsize
;
706 char *user_input
, *path
;
708 user_input
= ecalloc(MAX_USRI
, sizeof(char));
709 if (get_usrinput(user_input
, MAX_USRI
, "new dir") < 0) {
714 path
= ecalloc(MAX_P
, sizeof(char));
715 if (snprintf(path
, MAX_P
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
721 if (mkdir(path
, ndir_perm
) < 0)
722 print_error(strerror(errno
));
731 char *user_input
, *path
;
734 user_input
= ecalloc(MAX_USRI
, sizeof(char));
735 if (get_usrinput(user_input
, MAX_USRI
, "new file") < 0) {
740 path
= ecalloc(MAX_P
, sizeof(char));
741 if (snprintf(path
, MAX_P
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
747 rf
= open(path
, O_CREAT
| O_EXCL
, nf_perm
);
750 print_error(strerror(errno
));
751 else if (close(rf
) < 0)
752 print_error(strerror(errno
));
761 switch (delent(CURSOR
.name
)) {
763 print_error(strerror(errno
));
766 if (BETWEEN(cpane
->hdir
- 1, 1, cpane
->dirc
)) /* last entry */
775 if (cpane
->dirn
[0] == '/' && cpane
->dirn
[1] == '\0') { /* cwd = / */
779 get_dirp(cpane
->dirn
);
780 if (check_dir(cpane
->dirn
) < 0) {
781 print_error(strerror(errno
));
786 cpane
->firstrow
= cpane
->parent_firstrow
;
787 cpane
->hdir
= cpane
->parent_row
;
788 if (listdir(AddHi
) < 0)
789 print_error(strerror(errno
));
790 cpane
->parent_firstrow
= 0;
791 cpane
->parent_row
= 1;
799 if (cpane
->dirc
> scrheight
) {
800 rm_hi(cpane
, cpane
->hdir
- 1);
801 cpane
->hdir
= cpane
->dirc
;
802 cpane
->firstrow
= cpane
->dirc
- scrheight
+ 1;
804 add_hi(cpane
, cpane
->hdir
- 1);
806 rm_hi(cpane
, cpane
->hdir
- 1);
807 cpane
->hdir
= cpane
->dirc
;
808 add_hi(cpane
, cpane
->hdir
- 1);
818 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
819 rm_hi(cpane
, cpane
->hdir
- 1);
821 add_hi(cpane
, cpane
->hdir
- 1);
823 mvdwns(); /* scroll */
832 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
834 if (real
> scrheight
- 3 - scrsp
&& cpane
->hdir
+ scrsp
< cpane
->dirc
) {
836 rm_hi(cpane
, cpane
->hdir
- 1);
839 add_hi(cpane
, cpane
->hdir
- 1);
840 } else if (cpane
->hdir
< cpane
->dirc
) {
841 rm_hi(cpane
, cpane
->hdir
- 1);
843 add_hi(cpane
, cpane
->hdir
- 1);
855 switch (check_dir(CURSOR
.name
)) {
857 strncpy(cpane
->dirn
, CURSOR
.name
, MAX_P
);
858 cpane
->parent_row
= cpane
->hdir
;
859 cpane
->parent_firstrow
= cpane
->firstrow
;
862 if (listdir(AddHi
) < 0)
863 print_error(strerror(errno
));
865 case 1: /* not a directory open file */
867 s
= opnf(CURSOR
.name
);
872 print_error("process failed non-zero exit");
874 case -1: /* failed to open directory */
875 print_error(strerror(errno
));
884 rm_hi(cpane
, cpane
->hdir
- 1);
885 if (cpane
->dirc
< scrheight
/ 2)
886 cpane
->hdir
= (cpane
->dirc
+ 1) / 2;
888 cpane
->hdir
= (scrheight
/ 2) + cpane
->firstrow
;
889 add_hi(cpane
, cpane
->hdir
- 1);
898 if (cpane
->dirc
> scrheight
) {
899 rm_hi(cpane
, cpane
->hdir
- 1);
903 add_hi(cpane
, cpane
->hdir
- 1);
905 rm_hi(cpane
, cpane
->hdir
- 1);
907 add_hi(cpane
, cpane
->hdir
- 1);
917 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
918 rm_hi(cpane
, cpane
->hdir
- 1);
920 add_hi(cpane
, cpane
->hdir
- 1);
922 mvups(); /* scroll */
931 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
933 if (cpane
->firstrow
> 0 && real
< 1 + scrsp
) {
935 rm_hi(cpane
, cpane
->hdir
- 1);
938 add_hi(cpane
, cpane
->hdir
- 1);
939 } else if (cpane
->hdir
> 1) {
940 rm_hi(cpane
, cpane
->hdir
- 1);
942 add_hi(cpane
, cpane
->hdir
- 1);
951 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
952 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
953 rm_hi(cpane
, cpane
->hdir
- 1);
954 cpane
->hdir
+= scrmv
;
955 add_hi(cpane
, cpane
->hdir
- 1);
970 real
= cpane
->hdir
- cpane
->firstrow
;
971 dynmv
= MIN(cpane
->dirc
- cpane
->hdir
- cpane
->firstrow
, scrmv
);
973 if (real
+ scrmv
+ 1 > scrheight
&&
974 cpane
->hdir
+ scrsp
+ scrmv
< cpane
->dirc
) { /* scroll */
975 cpane
->firstrow
+= dynmv
;
976 rm_hi(cpane
, cpane
->hdir
- 1);
977 cpane
->hdir
+= scrmv
;
979 add_hi(cpane
, cpane
->hdir
- 1);
981 if (cpane
->hdir
< cpane
->dirc
- scrmv
- 1) {
982 rm_hi(cpane
, cpane
->hdir
- 1);
983 cpane
->hdir
+= scrmv
;
984 add_hi(cpane
, cpane
->hdir
- 1);
996 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
997 if (cpane
->hdir
> scrmv
) {
998 rm_hi(cpane
, cpane
->hdir
- 1);
999 cpane
->hdir
= cpane
->hdir
- scrmv
;
1000 add_hi(cpane
, cpane
->hdir
- 1);
1014 real
= cpane
->hdir
- cpane
->firstrow
;
1015 dynmv
= MIN(cpane
->firstrow
, scrmv
);
1017 if (cpane
->firstrow
> 0 && real
< scrmv
+ scrsp
) {
1018 cpane
->firstrow
-= dynmv
;
1019 rm_hi(cpane
, cpane
->hdir
- 1);
1020 cpane
->hdir
-= scrmv
;
1022 add_hi(cpane
, cpane
->hdir
- 1);
1024 if (cpane
->hdir
> scrmv
+ 1) {
1025 rm_hi(cpane
, cpane
->hdir
- 1);
1026 cpane
->hdir
-= scrmv
;
1027 add_hi(cpane
, cpane
->hdir
- 1);
1035 get_usrinput(char *out
, size_t sout
, const char *fmt
, ...)
1037 int height
= tb_height();
1039 struct tb_event fev
;
1040 size_t counter
= (size_t)1;
1052 name_size
= vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
1054 print_tb(buf
, 1, height
- 1, col
.fg
, col
.bg
);
1055 startat
= name_size
+ 1;
1056 tb_set_cursor((int)(startat
+ 1), height
- 1);
1059 while (tb_poll_event(&fev
) != 0) {
1062 if (fev
.key
== TB_KEY_ESC
) {
1063 tb_set_cursor(-1, -1);
1068 if (fev
.key
== TB_KEY_BACKSPACE
||
1069 fev
.key
== TB_KEY_BACKSPACE2
) {
1070 if (BETWEEN(counter
, 2, sout
)) {
1074 print_xstatus(empty
, startat
+ counter
);
1076 startat
+ counter
, theight
- 1);
1079 } else if (fev
.key
== TB_KEY_ENTER
) {
1080 tb_set_cursor(-1, -1);
1081 out
[counter
- 1] = '\0';
1085 if (counter
< sout
) {
1086 print_xstatus((char)fev
.ch
,
1087 (startat
+ counter
));
1088 out
[x
] = (char)fev
.ch
;
1089 tb_set_cursor((startat
+ counter
+ 1),
1112 for (c
= 0; c
< LEN(rules
); c
++)
1113 for (d
= 0; d
< rules
[c
].exlen
; d
++)
1114 if (strncmp(rules
[c
].ext
[d
], ex
, MAX_EXT
) == 0)
1120 spawn(const void *v
, char *fn
)
1129 while (((char **)v
)[x
++] != NULL
)
1132 char *argv
[argc
+ 2];
1133 for (x
= 0; x
< argc
; x
++)
1134 argv
[x
] = ((char **)v
)[x
];
1137 argv
[argc
+ 1] = NULL
;
1144 execvp(argv
[0], argv
);
1147 while ((r
= waitpid(pid
, &ws
, 0)) == -1 && errno
== EINTR
)
1151 if ((WIFEXITED(ws
) != 0) && (WEXITSTATUS(ws
) != 0))
1167 if (c
< 0) /* extension not found open in editor */
1168 return spawn(editor
, fn
);
1170 return spawn((char **)rules
[c
].v
, fn
);
1176 #if defined _SYS_INOTIFY_H
1177 inotify_fd
= inotify_init();
1180 #elif defined _SYS_EVENT_H_
1181 gtimeout
.tv_sec
= 1;
1192 #if defined _SYS_INOTIFY_H
1193 return cpane
->inotify_wd
= inotify_add_watch(inotify_fd
, cpane
->dirn
,
1194 IN_MODIFY
| IN_MOVED_FROM
| IN_MOVED_TO
| IN_CREATE
|
1195 IN_DELETE
| IN_DELETE_SELF
| IN_MOVE_SELF
);
1196 #elif defined _SYS_EVENT_H_
1197 cpane
->event_fd
= open(cpane
->dirn
, O_RDONLY
);
1198 if (cpane
->event_fd
< 0)
1199 return cpane
->event_fd
;
1200 EV_SET(&evlist
[cpane
->pane_id
], cpane
->event_fd
, EVFILT_VNODE
,
1202 NOTE_DELETE
| NOTE_EXTEND
| NOTE_LINK
| NOTE_RENAME
|
1203 NOTE_REVOKE
| NOTE_WRITE
,
1212 #if defined _SYS_INOTIFY_H
1215 struct inotify_event
*event
;
1216 const size_t events
= 32;
1217 const size_t evbuflen
=
1218 events
* (sizeof(struct inotify_event
) + MAX_N
+ 1);
1221 if (cpane
->inotify_wd
< 0)
1223 r
= read(inotify_fd
, buf
, evbuflen
);
1227 for (p
= buf
; p
< buf
+ r
;) {
1228 event
= (struct inotify_event
*)p
;
1235 p
+= sizeof(struct inotify_event
) + event
->len
;
1237 #elif defined _SYS_EVENT_H_
1238 return kevent(kq
, evlist
, 2, chlist
, 2, >imeout
);
1246 // if (pane->inotify_wd >= 0)
1247 // inotify_rm_watch(inotify_fd, pane->inotify_wd);
1248 #if defined _SYS_EVENT_H_
1249 close(pane
->event_fd
);
1256 pthread_cancel(fsev_thread
);
1258 pthread_join(fsev_thread
, NULL
);
1262 #if defined _SYS_INOTIFY_H
1264 #elif defined _SYS_EVENT_H_
1270 findbm(uint32_t event
)
1274 for (i
= 0; i
< (ssize_t
)LEN(bmarks
); i
++) {
1275 if (event
== bmarks
[i
].ch
) {
1276 if (check_dir(bmarks
[i
].path
) != 0) {
1277 print_error(strerror(errno
));
1289 if (cpane
->dirc
< 1)
1292 user_input
= ecalloc(MAX_USRI
, sizeof(char));
1293 if (get_usrinput(user_input
, MAX_USRI
, "filter") < 0) {
1297 cpane
->filter
= user_input
;
1298 if (listdir(AddHi
) < 0)
1299 print_error("no match");
1300 cpane
->filter
= NULL
;
1307 struct tb_event fev
;
1308 if (selection
!= NULL
) {
1313 selection
= ecalloc(cpane
->dirc
, sizeof(size_t));
1314 selection
[0] = cpane
->hdir
;
1316 print_prompt("-- VISUAL --");
1318 while (tb_poll_event(&fev
) != 0) {
1321 grabkeys(&fev
, vkeys
, vkeyslen
);
1322 if (cont_vmode
== -1)
1334 add_hi(cpane
, cpane
->hdir
- 1);
1342 print_prompt("-- VISUAL --");
1343 int index
= abs(cpane
->hdir
- selection
[0]);
1345 if (cpane
->hdir
< selection
[0]) {
1346 selection
[index
] = cpane
->hdir
;
1347 add_hi(cpane
, selection
[index
]);
1348 } else if (index
< cpane
->dirc
) {
1349 selection
[index
+ 1] = 0;
1351 if (cpane
->dirc
>= scrheight
||
1352 cpane
->hdir
<= 1) { /* rehighlight all if scrolling */
1361 print_prompt("-- VISUAL --");
1362 int index
= abs(cpane
->hdir
- selection
[0]);
1364 if (cpane
->hdir
> selection
[0]) {
1365 selection
[index
] = cpane
->hdir
;
1366 add_hi(cpane
, selection
[index
] - 2);
1368 selection
[index
+ 1] = 0;
1370 if (cpane
->dirc
>= scrheight
||
1371 cpane
->hdir
>= cpane
->dirc
) { /* rehighlight all if scrolling */
1380 for (i
= 0; i
< cpane
->dirc
; i
++) {
1381 selection
[i
] = i
+ 1;
1390 for (i
= 0; i
< cpane
->dirc
; i
++) {
1391 if (selection
[i
] < (scrheight
+ cpane
->firstrow
) &&
1393 cpane
->firstrow
) { /* checks if in the frame of the directories */
1394 add_hi(cpane
, selection
[i
] - 1);
1405 for (j
= 0; j
< cpane
->dirc
; j
++) { /* calculate used selection size */
1406 if (selection
[j
] != 0)
1416 if (selected_files
== NULL
)
1418 free(selected_files
);
1419 selected_files
= NULL
;
1429 selected_files
= ecalloc(selection_size
, sizeof(char *));
1431 for (i
= 0; i
< selection_size
; i
++) {
1432 selected_files
[i
] = cpane
->direntr
[selection
[i
] - 1].name
;
1441 add_hi(cpane
, cpane
->hdir
- 1);
1442 print_status(cprompt
, "%zu files are yanked", selection_size
);
1451 char conf
[] = "yes";
1454 inp_conf
= ecalloc(conf_len
, sizeof(char));
1455 if ((get_usrinput(inp_conf
, conf_len
, "delete file (yes) ?") < 0) ||
1456 (strncmp(inp_conf
, conf
, conf_len
) != 0)) {
1458 return; /* canceled by user or wrong inp_conf */
1464 /* selected_files */
1465 char *seldel_cmd
[selection_size
+ 3];
1466 seldel_cmd
[0] = "rm";
1467 seldel_cmd
[1] = "-rf";
1468 seldel_cmd
[selection_size
+ 2] = NULL
;
1470 /* add files to array */
1471 for (i
= 2; i
< selection_size
+ 2; i
++) {
1472 seldel_cmd
[i
] = selected_files
[i
- 2];
1475 if (spawn(seldel_cmd
, NULL
) < 0)
1476 print_error(strerror(errno
));
1478 print_status(cprompt
, "%zu files are deleted", selection_size
);
1480 if (cpane
->dirc
> 0)
1481 cpane
->hdir
= cpane
->dirc
- selection_size
;
1491 if (strnlen(yank_file
, MAX_P
) != 0) {
1492 print_status(cprompt
, "coping");
1493 if (spawn(cp_cmd
, cpane
->dirn
) != 0)
1494 print_error(strerror(errno
));
1496 print_status(cprompt
, "file copied");
1497 yank_file
[0] = '\0'; /* set yank_file len 0 */
1501 print_error("nothing to paste");
1503 if (selected_files
== NULL
)
1506 /* selected_files */
1507 char *selcp_cmd
[selection_size
+ 3];
1508 selcp_cmd
[0] = "cp";
1509 selcp_cmd
[1] = "-r";
1510 selcp_cmd
[selection_size
+ 2] = NULL
;
1512 /* add files to array */
1513 for (i
= 2; i
< selection_size
+ 2; i
++) {
1514 selcp_cmd
[i
] = selected_files
[i
- 2];
1517 if (spawn(selcp_cmd
, cpane
->dirn
) < 0)
1518 print_error(strerror(errno
));
1520 print_status(cprompt
, "%zu files are copied", selection_size
);
1530 if (strnlen(yank_file
, MAX_P
) != 0) {
1531 print_status(cprompt
, "moving");
1532 if (spawn(mv_cmd
, cpane
->dirn
) != 0)
1533 print_error(strerror(errno
));
1535 print_status(cprompt
, "file moved");
1536 yank_file
[0] = '\0'; /* set yank_file len 0 */
1540 print_error("nothing to move");
1542 if (selected_files
== NULL
)
1545 /* selected_files */
1546 char *selmv_cmd
[selection_size
+ 3];
1547 selmv_cmd
[0] = "mv";
1548 selmv_cmd
[1] = "-r";
1549 selmv_cmd
[selection_size
+ 2] = NULL
;
1551 /* add files to array */
1552 for (i
= 2; i
< selection_size
+ 2; i
++) {
1553 selmv_cmd
[i
] = selected_files
[i
- 2];
1556 if (spawn(selmv_cmd
, cpane
->dirn
) < 0)
1557 print_error(strerror(errno
));
1559 print_status(cprompt
, "%zu files are moved", selection_size
);
1567 char new_name
[MAX_P
];
1570 input_name
= ecalloc(MAX_N
, sizeof(char));
1572 if (get_usrinput(input_name
, MAX_N
, "rename: %s",
1573 basename(CURSOR
.name
)) < 0) {
1578 if (snprintf(new_name
, MAX_P
, "%s/%s", cpane
->dirn
, input_name
) < 0) {
1580 print_error(strerror(errno
));
1584 char *rename_cmd
[] = { "mv", CURSOR
.name
, new_name
, NULL
};
1585 if (spawn(rename_cmd
, NULL
) < 0)
1586 print_error(strerror(errno
));
1594 strncpy(yank_file
, CURSOR
.name
, MAX_P
);
1595 print_status(cprompt
, "1 file is yanked", selection_size
);
1601 if (cpane
->dirc
> 0)
1602 rm_hi(cpane
, cpane
->hdir
- 1);
1603 if (cpane
== &pane_l
)
1605 else if (cpane
== &pane_r
)
1607 if (cpane
->dirc
> 0) {
1608 add_hi(cpane
, cpane
->hdir
- 1);
1618 if (cont_vmode
== -1) { /* check if selection was allocated */
1620 if (selected_files
!= NULL
)
1623 free(pane_l
.direntr
);
1624 free(pane_r
.direntr
);
1631 grabkeys(struct tb_event
*event
, Key
*key
, size_t max_keys
)
1636 for (i
= 0; i
< max_keys
; i
++) {
1637 if (event
->ch
!= 0) {
1638 if (event
->ch
== key
[i
].evkey
.ch
) {
1642 } else if (event
->key
!= 0) {
1643 if (event
->key
== key
[i
].evkey
.key
) {
1651 b
= findbm(event
->ch
);
1655 strncpy(cpane
->dirn
, bmarks
[b
].path
, MAX_P
);
1656 cpane
->firstrow
= 0;
1657 cpane
->parent_row
= 1;
1659 if (listdir(AddHi
) < 0)
1660 print_error(strerror(errno
));
1673 if (cpane
== &pane_l
) {
1675 if (listdir(NoHi
) < 0)
1676 print_error(strerror(errno
));
1678 if (listdir(AddHi
) < 0)
1679 print_error(strerror(errno
));
1680 } else if (cpane
== &pane_r
) {
1682 if (listdir(NoHi
) < 0)
1683 print_error(strerror(errno
));
1685 if (listdir(AddHi
) < 0)
1686 print_error(strerror(errno
));
1699 while (tb_poll_event(&ev
) != 0) {
1702 grabkeys(&ev
, nkeys
, nkeyslen
);
1705 case TB_EVENT_RESIZE
:
1718 size_t y
, dyn_max
, start_from
;
1720 width
= (twidth
/ 2) - 4;
1724 start_from
= cpane
->firstrow
;
1725 dyn_max
= MIN(cpane
->dirc
, (scrheight
- 1) + cpane
->firstrow
);
1727 /* print each entry in directory */
1728 while (start_from
< dyn_max
) {
1729 get_hicol(&col
, cpane
->direntr
[start_from
].mode
);
1730 print_row(cpane
, start_from
, col
);
1735 if (cpane
->dirc
> 0)
1740 /* print current directory title */
1741 cpane
->dircol
.fg
|= TB_BOLD
;
1742 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1746 set_direntr(struct dirent
*entry
, DIR *dir
, char *filter
)
1753 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1754 strncpy(cpane->direntr[i].name, tmpfull, MAX_N); \
1755 if (lstat(tmpfull, &status) == 0) { \
1756 cpane->direntr[i].size = status.st_size; \
1757 cpane->direntr[i].mode = status.st_mode; \
1758 cpane->direntr[i].group = status.st_gid; \
1759 cpane->direntr[i].user = status.st_uid; \
1760 cpane->direntr[i].dt = status.st_mtime; \
1766 cpane
->direntr
= erealloc(cpane
->direntr
, cpane
->dirc
* sizeof(Entry
));
1767 while ((entry
= readdir(dir
)) != 0) {
1768 if ((strncmp(entry
->d_name
, ".", 2) == 0 ||
1769 strncmp(entry
->d_name
, "..", 3) == 0))
1772 if (filter
== NULL
) {
1774 } else if (filter
!= NULL
) {
1775 if (strcasestr(entry
->d_name
, filter
) != NULL
) {
1788 struct dirent
*entry
;
1790 int filtercount
= 0;
1791 size_t oldc
= cpane
->dirc
;
1793 width
= (twidth
/ 2) - 4;
1796 dir
= opendir(cpane
->dirn
);
1800 /* get content and filter sum */
1801 while ((entry
= readdir(dir
)) != 0) {
1802 if (cpane
->filter
!= NULL
) {
1803 if (strcasestr(entry
->d_name
, cpane
->filter
) != NULL
)
1805 } else { /* no filter */
1810 if (cpane
->filter
== NULL
) {
1815 if (cpane
->filter
!= NULL
) {
1816 if (filtercount
> 0) {
1817 cpane
->dirc
= filtercount
;
1820 } else if (filtercount
== 0) {
1821 if (closedir(dir
) < 0)
1828 /* print current directory title */
1829 cpane
->dircol
.fg
|= TB_BOLD
;
1830 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1832 if (cpane
->filter
== NULL
) /* dont't watch when filtering */
1834 print_error("can't add watch");
1836 /* empty directory */
1837 if (cpane
->dirc
== 0) {
1839 if (closedir(dir
) < 0)
1844 rewinddir(dir
); /* reset position */
1845 set_direntr(entry
, dir
, cpane
->filter
); /* create array of entries */
1846 qsort(cpane
->direntr
, cpane
->dirc
, sizeof(Entry
), sort_name
);
1849 if (hi
== AddHi
&& cpane
->dirc
> 0)
1850 add_hi(cpane
, cpane
->hdir
- 1);
1852 if (closedir(dir
) < 0)
1860 /* TODO need refactoring */
1863 pane_r
.dirx
= (twidth
/ 2) + 2;
1865 if (cpane
== &pane_l
) {
1870 if (cpane
->dirc
> 0)
1871 add_hi(&pane_l
, pane_l
.hdir
- 1);
1872 } else if (cpane
== &pane_r
) {
1877 if (cpane
->dirc
> 0)
1878 add_hi(&pane_r
, pane_r
.hdir
- 1);
1887 editor
[0] = getenv("EDITOR");
1890 if (editor
[0] == NULL
)
1900 home
= getenv("HOME");
1903 if ((getcwd(cwd
, sizeof(cwd
)) == NULL
))
1904 strncpy(cwd
, home
, MAX_P
);
1908 pane_l
.dircol
= cpanell
;
1909 pane_l
.firstrow
= 0;
1910 pane_l
.direntr
= ecalloc(0, sizeof(Entry
));
1911 strncpy(pane_l
.dirn
, cwd
, MAX_P
);
1913 pane_l
.inotify_wd
= -1;
1914 pane_l
.parent_row
= 1;
1917 pane_r
.dirx
= (twidth
/ 2) + 2;
1918 pane_r
.dircol
= cpanelr
;
1919 pane_r
.firstrow
= 0;
1920 pane_r
.direntr
= ecalloc(0, sizeof(Entry
));
1921 strncpy(pane_r
.dirn
, home
, MAX_P
);
1923 pane_r
.inotify_wd
= -1;
1924 pane_r
.parent_row
= 1;
1931 theight
= tb_height();
1932 twidth
= tb_width();
1933 scrheight
= theight
- 2;
1935 /* 2 horizontal lines */
1936 for (i
= 1; i
< twidth
- 1; ++i
) {
1937 tb_change_cell(i
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
1938 tb_change_cell(i
, theight
- 2, u_hl
, cframe
.fg
, cframe
.bg
);
1941 /* 4 vertical lines */
1942 for (i
= 1; i
< theight
- 1; ++i
) {
1943 tb_change_cell(0, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1945 (twidth
- 1) / 2, i
- 1, u_vl
, cframe
.fg
, cframe
.bg
);
1946 tb_change_cell(((twidth
- 1) / 2) + 1, i
- 1, u_vl
, cframe
.fg
,
1948 tb_change_cell(twidth
- 1, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1952 tb_change_cell(0, 0, u_cnw
, cframe
.fg
, cframe
.bg
);
1953 tb_change_cell(twidth
- 1, 0, u_cne
, cframe
.fg
, cframe
.bg
);
1954 tb_change_cell(0, theight
- 2, u_csw
, cframe
.fg
, cframe
.bg
);
1955 tb_change_cell(twidth
- 1, theight
- 2, u_cse
, cframe
.fg
, cframe
.bg
);
1957 /* 2 middel top and bottom */
1958 tb_change_cell((twidth
- 1) / 2, 0, u_mn
, cframe
.fg
, cframe
.bg
);
1960 (twidth
- 1) / 2, theight
- 2, u_ms
, cframe
.fg
, cframe
.bg
);
1968 if (tb_select_output_mode(TB_OUTPUT_256
) != TB_OUTPUT_256
)
1969 if (tb_select_output_mode(TB_OUTPUT_NORMAL
) != TB_OUTPUT_NORMAL
)
1970 die("output error");
1975 if (fsev_init() < 0)
1976 print_error(strerror(errno
));
1978 if (listdir(NoHi
) < 0)
1979 print_error(strerror(errno
));
1981 if (listdir(AddHi
) < 0)
1982 print_error(strerror(errno
));
1985 pthread_create(&fsev_thread
, NULL
, read_th
, NULL
);
1990 main(int argc
, char *argv
[])
1993 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1996 #endif /* __OpenBSD__ */
1999 else if (argc
== 2 && strncmp("-v", argv
[1], 2) == 0)
2000 die("sfm-" VERSION
);
2002 die("usage: sfm [-v]");