1 /* See LICENSE file for copyright and license details. */
7 #include <sys/resource.h>
11 #if defined(__linux__)
12 #include <sys/inotify.h>
13 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
14 defined(__OpenBSD__) || defined(__APPLE__)
15 #include <sys/event.h>
40 #define CURSOR_NAME cpane->direntr[cpane->hdir - 1].name
43 enum { AddHi
, NoHi
}; /* add highlight in listdir */
62 char dirn
[MAX_P
]; // dir name cwd
64 Entry
*direntr
; // dir entries
65 int dirx
; // pane cwd x pos
66 int dirc
; // dir entries sum
67 int hdir
; // highlighted dir
70 int parent_row
; // FIX
88 uint16_t key
; /* one of the TB_KEY_* constants */
89 uint32_t ch
; /* unicode character */
97 /* function declarations */
98 static void print_tb(const char *, int, int, uint16_t, uint16_t);
99 static void printf_tb(int, int, Cpair
, const char *, ...);
100 static void print_status(Cpair
, const char *, ...);
101 static void print_xstatus(char, int);
102 static void print_error(char *);
103 static void print_prompt(char *);
104 static void print_info(void);
105 static void print_row(Pane
*, size_t, Cpair
);
106 static void clear(int, int, int, uint16_t);
107 static void clear_status(void);
108 static void clear_pane(void);
109 static void add_hi(Pane
*, size_t);
110 static void rm_hi(Pane
*, size_t);
111 static int check_dir(char *);
112 static mode_t
chech_execf(mode_t
);
113 static int sort_name(const void *const, const void *const);
114 static void get_dirp(char *);
115 static char *get_ext(char *);
116 static int get_fdt(char *, size_t, time_t);
117 static char *get_fgrp(gid_t
, size_t);
118 static char *get_finfo(Entry
*);
119 static char *get_fperm(mode_t
);
120 static char *get_fsize(off_t
);
121 static char *get_fullpath(char *, char *);
122 static char *get_fusr(uid_t
, size_t);
123 static void get_dirsize(char *, off_t
*);
124 static void get_hicol(Cpair
*, mode_t
);
125 static int delent(char *);
126 static void calcdir(void);
127 static void crnd(void);
128 static void crnf(void);
129 static void delfd(void);
130 static void mvbk(void);
131 static void mvbtm(void);
132 static void mvdwn(void);
133 static void mvdwns(void);
134 static void mvfor(void);
135 static void mvmid(void);
136 static void mvtop(void);
137 static void mvup(void);
138 static void mvups(void);
139 static void scrdwn(void);
140 static void scrdwns(void);
141 static void scrup(void);
142 static void scrups(void);
143 static int get_usrinput(char*, size_t, const char*, ...);
144 static int frules(char *);
145 static int spawn(const void *, char *);
146 static int opnf(char *);
147 static int fsev_init(void);
148 static int addwatch(void);
149 static int read_events(void);
150 static void rmwatch(Pane
*);
151 static void fsev_shdn(void);
152 static ssize_t
findbm(uint32_t);
153 static void start_filter(void);
154 static void start_vmode(void);
155 static void exit_vmode(void);
156 static void selup(void);
157 static void seldwn(void);
158 static void selall(void);
159 static void selref(void);
160 static void selynk(void);
161 static void selcalc(void);
162 static void paste(void);
163 static void selmv(void);
164 static void seldel(void);
165 static void init_files(void);
166 static void free_files(void);
167 static void yank(void);
168 static void rname(void);
169 static void switch_pane(void);
170 static void quit(void);
171 static void grabkeys(struct tb_event
*, Key
*, size_t);
172 static void start_ev(void);
173 static void refresh_pane(void);
174 static void set_direntr(struct dirent
*, DIR *, char *);
175 static int listdir(int);
176 static void t_resize(void);
177 static void set_panes(void);
178 static void draw_frame(void);
179 static void start(void);
181 /* global variables */
182 static Pane pane_r
, pane_l
, *cpane
;
183 static char *editor
[2];
184 static char fed
[] = "vi";
185 static int theight
, twidth
, scrheight
;
186 static size_t selection_size
= 0;
187 static char yank_file
[MAX_P
];
188 static int *selection
;
189 static int cont_vmode
= 0;
190 static char **selected_files
;
191 #if defined _SYS_INOTIFY_H
192 static int inotify_fd
;
193 #elif defined _SYS_EVENT_H_
195 struct kevent evlist
[2]; /* events we want to monitor */
196 struct kevent chlist
[2]; /* events that were triggered */
197 static struct timespec gtimeout
;
200 /* configuration, allows nested code to access above variables */
203 /* function implementations */
205 print_tb(const char *str
, int x
, int y
, uint16_t fg
, uint16_t bg
)
207 while (*str
!= '\0') {
209 str
+= tb_utf8_char_to_unicode(&uni
, str
);
210 tb_change_cell(x
, y
, uni
, fg
, bg
);
216 printf_tb(int x
, int y
, Cpair col
, const char *fmt
, ...)
221 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
223 print_tb(buf
, x
, y
, col
.fg
, col
.bg
);
227 print_status(Cpair col
, const char *fmt
, ...)
232 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
235 print_tb(buf
, 1, theight
- 1, col
.fg
, col
.bg
);
239 print_xstatus(char c
, int x
)
242 (void)tb_utf8_char_to_unicode(&uni
, &c
);
243 tb_change_cell(x
, theight
- 1, uni
, cstatus
.fg
, cstatus
.bg
);
247 print_error(char *errmsg
)
249 print_status(cerr
, errmsg
);
253 print_prompt(char *prompt
)
255 print_status(cprompt
, prompt
);
262 fileinfo
= get_finfo(&cpane
->direntr
[cpane
->hdir
- 1]);
263 print_status(cstatus
, "%d/%d %s", cpane
->hdir
, cpane
->dirc
, fileinfo
);
268 print_row(Pane
*pane
, size_t entpos
, Cpair col
)
273 char lnk_full
[MAX_P
];
276 width
= (twidth
/ 2) - 4;
277 result
= basename(pane
->direntr
[entpos
].name
);
279 y
= entpos
- cpane
->firstrow
+ 1;
281 if (S_ISLNK(pane
->direntr
[entpos
].mode
) &&
282 realpath(pane
->direntr
[entpos
].name
, buf
) != NULL
) {
283 (void)snprintf(lnk_full
, MAX_N
, "%s -> %s",
288 printf_tb(x
, y
, col
, "%*.*s", ~width
, width
, result
);
292 clear(int sx
, int ex
, int y
, uint16_t bg
)
294 /* clear line from to */
295 /* x = line number vertical */
296 /* y = column number horizontal */
298 for (i
= sx
; i
< ex
; i
++) {
299 tb_change_cell(i
, y
, 0x0000, TB_DEFAULT
, bg
);
306 clear(1, twidth
- 1, theight
- 1, cstatus
.bg
);
313 y
= 0, i
= 0, ex
= 0;
315 if (cpane
->pane_id
== pane_l
.pane_id
)
316 ex
= (twidth
/ 2) - 1;
317 else if (cpane
->pane_id
== pane_r
.pane_id
)
320 while (i
< scrheight
) {
321 clear(cpane
->dirx
, ex
, y
, TB_DEFAULT
);
327 for (y
= cpane
->dirx
; y
< ex
; ++y
) {
328 tb_change_cell(y
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
333 add_hi(Pane
*pane
, size_t entpos
)
336 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
337 col
.fg
|= TB_REVERSE
| TB_BOLD
;
338 col
.bg
|= TB_REVERSE
;
339 print_row(pane
, entpos
, col
);
343 rm_hi(Pane
*pane
, size_t entpos
)
346 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
347 print_row(pane
, entpos
, col
);
351 check_dir(char *path
)
357 if (errno
== ENOTDIR
) {
364 if (closedir(dir
) < 0)
371 chech_execf(mode_t mode
)
374 return (((S_IXUSR
| S_IXGRP
| S_IXOTH
) & mode
));
379 sort_name(const void *const A
, const void *const B
)
382 mode_t data1
= (*(Entry
*)A
).mode
;
383 mode_t data2
= (*(Entry
*)B
).mode
;
387 } else if (data1
== data2
) {
388 result
= strncmp((*(Entry
*)A
).name
, (*(Entry
*)B
).name
, MAX_N
);
401 len
= strnlen(cdir
, MAX_P
);
405 for (i
= len
- 1; i
> 1; i
--) {
412 cdir
[len
-counter
-1] = '\0';
420 size_t counter
, len
, i
;
424 len
= strnlen(str
, MAX_N
);
426 for (i
= len
- 1; i
> 0; i
--) {
434 ext
= ecalloc(MAX_EXT
+ 1, sizeof(char));
435 strncpy(ext
, &str
[len
- counter
], MAX_EXT
);
441 get_fdt(char *result
, size_t reslen
, time_t status
)
444 localtime_r(&status
, <
);
445 return strftime(result
, reslen
, dtfmt
, <
);
449 get_fgrp(gid_t status
, size_t len
)
454 result
= ecalloc(len
, sizeof(char));
455 gr
= getgrgid(status
);
457 (void)snprintf(result
, len
- 1, "%u", status
);
459 strncpy(result
, gr
->gr_name
, len
- 1);
465 get_finfo(Entry
*cursor
)
467 char *sz
, *rst
, *ur
, *gr
, *td
, *prm
;
468 size_t szlen
, prmlen
, urlen
, grlen
, tdlen
, rstlen
;
472 urlen
= grlen
= tdlen
= 32;
473 rstlen
= szlen
+ prmlen
+ urlen
+ grlen
+ tdlen
;
474 rst
= ecalloc(rstlen
, sizeof(char));
476 if (show_perm
== 1) {
477 prm
= get_fperm(cursor
->mode
);
478 strncpy(rst
, prm
, prmlen
);
484 ur
= get_fusr(cursor
->user
, urlen
);
485 gr
= get_fgrp(cursor
->group
, grlen
);
486 strncat(rst
, ur
, urlen
);
488 strncat(rst
, gr
, grlen
);
495 td
= ecalloc(tdlen
, sizeof(char));
496 if (get_fdt(td
, tdlen
, cursor
->td
) > 0) {
497 strncat(rst
, td
, tdlen
);
503 if (show_size
== 1 && S_ISREG(cursor
->mode
)) {
504 sz
= get_fsize(cursor
->size
);
505 strncat(rst
, sz
, szlen
);
513 get_fperm(mode_t mode
)
518 const char chars
[] = "rwxrwxrwx";
519 buf
= ecalloc(11, sizeof(char));
523 else if (S_ISREG(mode
))
525 else if (S_ISLNK(mode
))
527 else if (S_ISBLK(mode
))
529 else if (S_ISCHR(mode
))
531 else if (S_ISFIFO(mode
))
533 else if (S_ISSOCK(mode
))
538 for (i
= 1; i
< 10; i
++) {
539 buf
[i
] = (mode
& (1 << (9 - i
))) ? chars
[i
- 1] : '-';
547 get_fsize(off_t size
)
549 char *result
; /* need to be freed */
555 result_len
= 6; /* 9999X/0 */
556 result
= ecalloc(result_len
, sizeof(char));
558 while (size
>= 1000) {
583 if (snprintf(result
, result_len
, "%ld%c", size
, unit
) < 0)
584 strncat(result
, "???", result_len
);
590 get_fullpath(char *first
, char *second
)
594 full_path
= ecalloc(MAX_P
, sizeof(char));
596 if (strncmp(first
, "/", MAX_P
) == 0)
597 (void)snprintf(full_path
, MAX_P
, "/%s", second
);
599 (void)snprintf(full_path
, MAX_P
, "%s/%s", first
, second
);
605 get_fusr(uid_t status
, size_t len
)
610 result
= ecalloc(len
, sizeof(char));
611 pw
= getpwuid(status
);
613 (void)snprintf(result
, len
- 1, "%u", status
);
615 strncpy(result
, pw
->pw_name
, len
- 1);
621 get_dirsize(char *fullpath
, off_t
*fullsize
)
626 struct dirent
*entry
;
629 dir
= opendir(fullpath
);
634 while ((entry
= readdir(dir
)) != 0) {
635 if ((strncmp(entry
->d_name
, ".", 2) == 0 ||
636 strncmp(entry
->d_name
, "..", 3) == 0))
639 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
640 if (lstat(ent_full
, &status
) == 0) {
641 mode
= status
.st_mode
;
643 get_dirsize(ent_full
, fullsize
);
646 *fullsize
+= status
.st_size
;
657 get_hicol(Cpair
*col
, mode_t mode
)
662 else if (S_ISLNK(mode
))
664 else if (chech_execf(mode
) > 0)
669 delent(char *fullpath
)
675 inp_conf
= ecalloc(conf_len
, sizeof(char));
676 if ((get_usrinput(inp_conf
, conf_len
, "delete file (yes) ?") < 0) ||
677 (strncmp(inp_conf
, conf
, conf_len
) != 0)) {
679 return 1; /* canceled by user or wrong inp_conf */
683 return spawn(rm_cmd
, fullpath
);
689 if (!S_ISDIR(cpane
->direntr
[cpane
->hdir
- 1].mode
))
696 fullsize
= ecalloc(50, sizeof(off_t
));
697 get_dirsize(CURSOR_NAME
, fullsize
);
698 csize
= get_fsize(*fullsize
);
699 result
= get_finfo(&cpane
->direntr
[cpane
->hdir
- 1]);
702 print_status(cstatus
, "%d/%d %s%s",
703 cpane
->hdir
, cpane
->dirc
, result
, csize
);
712 char *user_input
, *path
;
714 user_input
= ecalloc(MAX_USRI
, sizeof(char));
715 if (get_usrinput(user_input
, MAX_USRI
, "new dir") < 0) {
720 path
= ecalloc(MAX_P
, sizeof(char));
721 if (snprintf(path
, MAX_P
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
727 if (mkdir(path
, ndir_perm
) < 0)
728 print_error(strerror(errno
));
737 char *user_input
, *path
;
740 user_input
= ecalloc(MAX_USRI
, sizeof(char));
741 if (get_usrinput(user_input
, MAX_USRI
, "new file") < 0) {
746 path
= ecalloc(MAX_P
, sizeof(char));
747 if (snprintf(path
, MAX_P
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
753 rf
= open(path
, O_CREAT
| O_EXCL
, nf_perm
);
756 print_error(strerror(errno
));
759 print_error(strerror(errno
));
768 switch (delent(CURSOR_NAME
)) {
770 print_error(strerror(errno
));
773 if (BETWEEN(cpane
->hdir
- 1, 1, cpane
->dirc
)) /* last entry */
782 if (cpane
->dirn
[0] == '/' && cpane
->dirn
[1] == '\0') { /* cwd = / */
786 get_dirp(cpane
->dirn
);
787 if (check_dir(cpane
->dirn
) < 0) {
788 print_error(strerror(errno
));
793 cpane
->firstrow
= cpane
->parent_firstrow
;
794 cpane
->hdir
= cpane
->parent_row
;
795 if (listdir(AddHi
) < 0)
796 print_error(strerror(errno
));
797 cpane
->parent_firstrow
= 0;
798 cpane
->parent_row
= 1;
806 if (cpane
->dirc
> scrheight
) {
807 rm_hi(cpane
, cpane
->hdir
- 1);
808 cpane
->hdir
= cpane
->dirc
;
809 cpane
->firstrow
= cpane
->dirc
- scrheight
+ 1;
811 add_hi(cpane
, cpane
->hdir
- 1);
813 rm_hi(cpane
, cpane
->hdir
- 1);
814 cpane
->hdir
= cpane
->dirc
;
815 add_hi(cpane
, cpane
->hdir
- 1);
825 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
826 rm_hi(cpane
, cpane
->hdir
- 1);
828 add_hi(cpane
, cpane
->hdir
- 1);
830 mvdwns(); /* scroll */
839 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
841 if (real
> scrheight
- 3 - scrsp
&& cpane
->hdir
+ scrsp
< cpane
->dirc
) {
843 rm_hi(cpane
, cpane
->hdir
- 1);
846 add_hi(cpane
, cpane
->hdir
- 1);
847 } else if (cpane
->hdir
< cpane
->dirc
) {
848 rm_hi(cpane
, cpane
->hdir
- 1);
850 add_hi(cpane
, cpane
->hdir
- 1);
862 switch (check_dir(CURSOR_NAME
)) {
864 strncpy(cpane
->dirn
, CURSOR_NAME
, MAX_P
);
865 cpane
->parent_row
= cpane
->hdir
;
866 cpane
->parent_firstrow
= cpane
->firstrow
;
869 if (listdir(AddHi
) < 0)
870 print_error(strerror(errno
));
872 case 1: /* not a directory open file */
874 s
= opnf(CURSOR_NAME
);
879 print_error("process failed non-zero exit");
881 case -1: /* failed to open directory */
882 print_error(strerror(errno
));
891 rm_hi(cpane
, cpane
->hdir
- 1);
892 if (cpane
->dirc
< scrheight
/ 2)
893 cpane
->hdir
= (cpane
->dirc
+ 1) / 2;
895 cpane
->hdir
= (scrheight
/ 2) + cpane
->firstrow
;
896 add_hi(cpane
, cpane
->hdir
- 1);
905 if (cpane
->dirc
> scrheight
) {
906 rm_hi(cpane
, cpane
->hdir
- 1);
910 add_hi(cpane
, cpane
->hdir
- 1);
912 rm_hi(cpane
, cpane
->hdir
- 1);
914 add_hi(cpane
, cpane
->hdir
- 1);
924 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
925 rm_hi(cpane
, cpane
->hdir
- 1);
927 add_hi(cpane
, cpane
->hdir
- 1);
929 mvups(); /* scroll */
938 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
940 if (cpane
->firstrow
> 0 && real
< 1 + scrsp
) {
942 rm_hi(cpane
, cpane
->hdir
- 1);
945 add_hi(cpane
, cpane
->hdir
- 1);
946 } else if (cpane
->hdir
> 1) {
947 rm_hi(cpane
, cpane
->hdir
- 1);
949 add_hi(cpane
, cpane
->hdir
- 1);
958 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
959 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
960 rm_hi(cpane
, cpane
->hdir
- 1);
961 cpane
->hdir
+= scrmv
;
962 add_hi(cpane
, cpane
->hdir
- 1);
977 real
= cpane
->hdir
- cpane
->firstrow
;
978 dynmv
= MIN(cpane
->dirc
- cpane
->hdir
- cpane
->firstrow
, scrmv
);
980 if (real
+ scrmv
+ 1 > scrheight
&&
981 cpane
->hdir
+ scrsp
+ scrmv
< cpane
->dirc
) { /* scroll */
982 cpane
->firstrow
+= dynmv
;
983 rm_hi(cpane
, cpane
->hdir
- 1);
984 cpane
->hdir
+= scrmv
;
986 add_hi(cpane
, cpane
->hdir
- 1);
988 if (cpane
->hdir
< cpane
->dirc
- scrmv
- 1) {
989 rm_hi(cpane
, cpane
->hdir
- 1);
990 cpane
->hdir
+= scrmv
;
991 add_hi(cpane
, cpane
->hdir
- 1);
1001 if (cpane
->dirc
< 1)
1003 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
1004 if (cpane
->hdir
> scrmv
) {
1005 rm_hi(cpane
, cpane
->hdir
- 1);
1006 cpane
->hdir
= cpane
->hdir
- scrmv
;
1007 add_hi(cpane
, cpane
->hdir
- 1);
1021 real
= cpane
->hdir
- cpane
->firstrow
;
1022 dynmv
= MIN(cpane
->firstrow
, scrmv
);
1024 if (cpane
->firstrow
> 0 && real
< scrmv
+ scrsp
) {
1025 cpane
->firstrow
-= dynmv
;
1026 rm_hi(cpane
, cpane
->hdir
- 1);
1027 cpane
->hdir
-= scrmv
;
1029 add_hi(cpane
, cpane
->hdir
- 1);
1031 if (cpane
->hdir
> scrmv
+ 1) {
1032 rm_hi(cpane
, cpane
->hdir
- 1);
1033 cpane
->hdir
-= scrmv
;
1034 add_hi(cpane
, cpane
->hdir
- 1);
1042 get_usrinput(char *out
, size_t sout
, const char *fmt
, ...)
1044 int height
= tb_height();
1046 struct tb_event fev
;
1047 size_t counter
= (size_t)1;
1059 name_size
= vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
1061 print_tb(buf
, 1, height
-1, col
.fg
, col
.bg
);
1062 startat
= name_size
+ 1;
1063 tb_set_cursor((int)(startat
+ 1), height
-1);
1066 while (tb_poll_event(&fev
) != 0) {
1069 if (fev
.key
== TB_KEY_ESC
) {
1070 tb_set_cursor(-1, -1);
1075 if (fev
.key
== TB_KEY_BACKSPACE
||
1076 fev
.key
== TB_KEY_BACKSPACE2
) {
1077 if (BETWEEN(counter
, 2, sout
)) {
1081 print_xstatus(empty
, startat
+ counter
);
1082 tb_set_cursor(startat
+ counter
,
1086 } else if (fev
.key
== TB_KEY_ENTER
) {
1087 tb_set_cursor(-1, -1);
1088 out
[counter
- 1] = '\0';
1092 if (counter
< sout
) {
1093 print_xstatus((char)fev
.ch
,
1094 (startat
+ counter
));
1095 out
[x
] = (char)fev
.ch
;
1096 tb_set_cursor((startat
+ counter
+ 1),
1119 for (c
= 0; c
< LEN(rules
); c
++)
1120 for (d
= 0; d
< rules
[c
].exlen
; d
++)
1121 if (strncmp(rules
[c
].ext
[d
], ex
, MAX_EXT
) == 0)
1127 spawn(const void *v
, char *fn
)
1129 int ws
, x
, argc
, fd
;
1136 while (((char **)v
)[x
++] != NULL
)
1139 char *argv
[argc
+ 2];
1140 for ( x
= 0; x
< argc
; x
++)
1141 argv
[x
] = ((char **)v
)[x
];
1144 argv
[argc
+ 1] = NULL
;
1151 fd
= open("/dev/null",O_WRONLY
);
1152 dup2(fd
, STDERR_FILENO
);
1153 execvp(argv
[0], argv
);
1157 while ((r
= waitpid(pid
, &ws
, 0)) == -1 && errno
== EINTR
)
1161 if ((WIFEXITED(ws
) != 0) && (WEXITSTATUS(ws
) != 0))
1177 if (c
< 0) /* extension not found open in editor */
1178 return spawn(editor
, fn
);
1180 return spawn((char **)rules
[c
].v
, fn
);
1186 #if defined _SYS_INOTIFY_H
1187 inotify_fd
= inotify_init1(IN_NONBLOCK
| IN_CLOEXEC
);
1190 #elif defined _SYS_EVENT_H_
1201 #if defined _SYS_INOTIFY_H
1202 return cpane
->inotify_wd
= inotify_add_watch(inotify_fd
, cpane
->dirn
,
1203 IN_MODIFY
| IN_MOVED_FROM
| IN_MOVED_TO
| IN_CREATE
|
1204 IN_DELETE
| IN_DELETE_SELF
| IN_MOVE_SELF
);
1205 #elif defined _SYS_EVENT_H_
1206 cpane
->event_fd
= open(cpane
->dirn
, O_RDONLY
);
1207 if (cpane
->event_fd
< 0)
1208 return cpane
->event_fd
;
1209 EV_SET(&evlist
[cpane
->pane_id
], cpane
->event_fd
,
1210 EVFILT_VNODE
, EV_ADD
| EV_CLEAR
,
1211 NOTE_DELETE
| NOTE_EXTEND
| NOTE_LINK
|
1212 NOTE_RENAME
| NOTE_REVOKE
| NOTE_WRITE
, 0, NULL
);
1220 #if defined _SYS_INOTIFY_H
1223 struct inotify_event
*event
;
1224 const size_t events
= 32;
1225 const size_t evbuflen
=
1226 events
* (sizeof(struct inotify_event
) + MAX_N
+ 1);
1229 if (cpane
->inotify_wd
< 0)
1231 r
= read(inotify_fd
, buf
, evbuflen
);
1235 for (p
= buf
; p
< buf
+ r
;) {
1236 event
= (struct inotify_event
*)p
;
1243 p
+= sizeof(struct inotify_event
) + event
->len
;
1245 #elif defined _SYS_EVENT_H_
1246 return kevent(kq
, evlist
, 2, chlist
, 2, >imeout
);
1254 #if defined _SYS_INOTIFY_H
1255 if (pane
->inotify_wd
>= 0)
1256 inotify_rm_watch(inotify_fd
, pane
->inotify_wd
);
1257 #elif defined _SYS_EVENT_H_
1258 close(pane
->event_fd
);
1268 #if defined _SYS_INOTIFY_H
1270 #elif defined _SYS_EVENT_H_
1276 findbm(uint32_t event
)
1280 for (i
= 0; i
< (ssize_t
)LEN(bmarks
); i
++) {
1281 if (event
== bmarks
[i
].ch
) {
1282 if (check_dir(bmarks
[i
].path
) != 0) {
1283 print_error(strerror(errno
));
1295 if (cpane
->dirc
< 1)
1298 user_input
= ecalloc(MAX_USRI
, sizeof(char));
1299 if (get_usrinput(user_input
, MAX_USRI
, "filter") < 0) {
1303 cpane
->filter
= user_input
;
1304 if (listdir(AddHi
) < 0)
1305 print_error("no match");
1306 cpane
->filter
= NULL
;
1313 struct tb_event fev
;
1314 if (selection
!= NULL
) {
1319 selection
= ecalloc(cpane
->dirc
, sizeof(size_t));
1320 selection
[0] = cpane
->hdir
;
1322 print_prompt("-- VISUAL --");
1324 while (tb_poll_event(&fev
) != 0) {
1327 grabkeys(&fev
, skeys
, skeyslen
);
1328 if (cont_vmode
== -1)
1340 add_hi(cpane
, cpane
->hdir
- 1);
1348 print_prompt("-- VISUAL --");
1349 int index
= abs(cpane
->hdir
- selection
[0]);
1351 if (cpane
->hdir
< selection
[0]) {
1352 selection
[index
] = cpane
->hdir
;
1353 add_hi(cpane
, selection
[index
]);
1354 } else if (index
< cpane
->dirc
) {
1355 selection
[index
+ 1] = 0;
1357 if (cpane
->dirc
>= scrheight
|| cpane
->hdir
<= 1) { /* rehighlight all if scrolling */
1366 print_prompt("-- VISUAL --");
1367 int index
= abs(cpane
->hdir
- selection
[0]);
1369 if (cpane
->hdir
> selection
[0]) {
1370 selection
[index
] = cpane
->hdir
;
1371 add_hi(cpane
, selection
[index
] - 2);
1373 selection
[index
+ 1] = 0;
1375 if (cpane
->dirc
>= scrheight
|| cpane
->hdir
>= cpane
->dirc
) { /* rehighlight all if scrolling */
1384 for (i
= 0; i
< cpane
->dirc
; i
++) {
1385 selection
[i
] = i
+ 1;
1394 for (i
= 0; i
< cpane
->dirc
; i
++) {
1395 if (selection
[i
] < (scrheight
+ cpane
->firstrow
) && selection
[i
] > cpane
->firstrow
) { /* checks if in the frame of the directories */
1396 add_hi(cpane
, selection
[i
] - 1);
1407 for (j
= 0; j
< cpane
->dirc
; j
++) { /* calculate used selection size */
1408 if (selection
[j
] != 0)
1420 if (selected_files
!= NULL
) {
1421 for (i
= 0; i
< selection_size
; i
++) {
1422 free(selected_files
[i
]);
1423 selected_files
[i
] = NULL
;
1425 free(selected_files
);
1426 selected_files
= NULL
;
1437 selected_files
= ecalloc(selection_size
, sizeof(char*));
1439 for (i
= 0; i
< selection_size
; i
++) {
1440 selected_files
[i
] = ecalloc(MAX_P
, sizeof(char));
1441 strncpy(selected_files
[i
], cpane
->direntr
[selection
[i
] -1].name
, MAX_P
); /* TODO use pointer */
1450 add_hi(cpane
, cpane
->hdir
-1);
1451 print_status(cprompt
, "%zu files are yanked", selection_size
);
1460 char conf
[] = "yes";
1463 inp_conf
= ecalloc(conf_len
, sizeof(char));
1464 if ((get_usrinput(inp_conf
, conf_len
, "delete file (yes) ?") < 0) ||
1465 (strncmp(inp_conf
, conf
, conf_len
) != 0)) {
1467 return; /* canceled by user or wrong inp_conf */
1472 for (i
= 0; i
< selection_size
; i
++) {
1473 spawn(rm_cmd
, selected_files
[i
]);
1476 cpane
->hdir
= cpane
->dirc
- selection_size
;
1477 print_status(cprompt
, "%zu files are deleted", selection_size
);
1486 if (strnlen(yank_file
, MAX_P
) != 0) {
1487 print_status(cprompt
, "coping");
1488 if (spawn(cp_cmd
, cpane
->dirn
) != 0)
1489 print_error("coping failed");
1491 print_status(cprompt
, "file copied");
1492 yank_file
[0] = '\0'; /* set yank_file len 0 */
1496 print_error("nothing to paste");
1498 if (selected_files
== NULL
)
1501 for (i
= 0; i
< selection_size
; i
++) {
1502 char *selcp_cmd
[] = { "cp", "-r", selected_files
[i
], cpane
->dirn
, NULL
};
1503 spawn(selcp_cmd
,NULL
);
1505 print_status(cprompt
, "%zu files are copied", selection_size
);
1514 if (strnlen(yank_file
, MAX_P
) != 0) {
1515 print_status(cprompt
, "moving");
1516 if (spawn(mv_cmd
, cpane
->dirn
) != 0)
1517 print_error("moving failed");
1519 print_status(cprompt
, "file moved");
1520 yank_file
[0] = '\0'; /* set yank_file len 0 */
1524 print_error("nothing to move");
1526 if (selected_files
== NULL
)
1529 for (i
= 0; i
< selection_size
; i
++) {
1530 char *selmv_cmd
[] = { "mv", selected_files
[i
], cpane
->dirn
, NULL
};
1531 spawn(selmv_cmd
,NULL
);
1533 print_status(cprompt
, "%zu files are moved", selection_size
);
1540 char new_name
[MAX_P
];
1543 input_name
= ecalloc(MAX_N
, sizeof(char));
1545 if (get_usrinput(input_name
, MAX_N
, "rename: %s", basename(CURSOR_NAME
)) < 0) {
1550 if (snprintf(new_name
, MAX_P
, "%s/%s", cpane
->dirn
, input_name
) < 0) {
1552 print_error(strerror(errno
));
1556 char *rename_cmd
[] = { "mv", CURSOR_NAME
, new_name
, NULL
};
1557 if (spawn(rename_cmd
, NULL
) < 0)
1558 print_error(strerror(errno
));
1566 strncpy(yank_file
, CURSOR_NAME
, MAX_P
);
1567 print_status(cprompt
, "1 file is yanked", selection_size
);
1574 if (cpane
->dirc
> 0)
1575 rm_hi(cpane
, cpane
->hdir
- 1);
1576 if (cpane
== &pane_l
)
1578 else if (cpane
== &pane_r
)
1580 if (cpane
->dirc
> 0) {
1581 add_hi(cpane
, cpane
->hdir
- 1);
1591 if (cont_vmode
== -1) { /* check if selection was allocated */
1593 if (selected_files
!= NULL
)
1596 free(pane_l
.direntr
);
1597 free(pane_r
.direntr
);
1604 grabkeys(struct tb_event
*event
, Key
*key
, size_t max_keys
)
1609 for (i
= 0; i
< max_keys
; i
++) {
1610 if (event
->ch
!= 0) {
1611 if (event
->ch
== key
[i
].evkey
.ch
) {
1615 } else if (event
->key
!= 0) {
1616 if (event
->key
== key
[i
].evkey
.key
) {
1624 b
= findbm(event
->ch
);
1628 strncpy(cpane
->dirn
, bmarks
[b
].path
, MAX_P
);
1629 cpane
->firstrow
= 0;
1630 cpane
->parent_row
= 1;
1632 if (listdir(AddHi
) < 0)
1633 print_error(strerror(errno
));
1642 int t
= tb_peek_event(&ev
, 2000);
1648 if (t
== 1) /* keyboard event */
1649 grabkeys(&ev
, nkeys
, nkeyslen
);
1650 else if (t
== 2) /* resize event */
1652 else if (t
== 0) /* filesystem event */
1653 if (read_events() > 0) { /* TODO need refactoring */
1654 if (cpane
== &pane_l
) {
1656 if (listdir(NoHi
) < 0)
1657 print_error(strerror(errno
));
1659 if (listdir(AddHi
) < 0)
1660 print_error(strerror(errno
));
1661 } else if (cpane
== &pane_r
) {
1663 if (listdir(NoHi
) < 0)
1664 print_error(strerror(errno
));
1666 if (listdir(AddHi
) < 0)
1667 print_error(strerror(errno
));
1680 size_t y
, dyn_max
, start_from
;
1682 width
= (twidth
/ 2) - 4;
1686 start_from
= cpane
->firstrow
;
1687 dyn_max
= MIN(cpane
->dirc
, (scrheight
- 1) + cpane
->firstrow
);
1689 /* print each entry in directory */
1690 while (start_from
< dyn_max
) {
1691 get_hicol(&col
, cpane
->direntr
[start_from
].mode
);
1692 print_row(cpane
, start_from
, col
);
1697 if (cpane
->dirc
> 0)
1702 /* print current directory title */
1703 cpane
->dircol
.fg
|= TB_BOLD
;
1704 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1708 set_direntr(struct dirent
*entry
, DIR *dir
, char *filter
)
1716 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1717 strncpy(cpane->direntr[i].name, tmpfull, MAX_N); \
1718 if (lstat(tmpfull, &status) == 0) { \
1719 cpane->direntr[i].size = status.st_size; \
1720 cpane->direntr[i].mode = status.st_mode; \
1721 cpane->direntr[i].group = status.st_gid; \
1722 cpane->direntr[i].user = status.st_uid; \
1723 cpane->direntr[i].td = status.st_mtime; \
1727 cpane
->direntr
= erealloc(cpane
->direntr
, cpane
->dirc
* sizeof(Entry
));
1728 while ((entry
= readdir(dir
)) != 0) {
1729 if ((strncmp(entry
->d_name
, ".", 2) == 0 ||
1730 strncmp(entry
->d_name
, "..", 3) == 0))
1733 if (filter
== NULL
) {
1735 } else if (filter
!= NULL
) {
1736 if (strcasestr(entry
->d_name
, filter
) != NULL
) {
1750 struct dirent
*entry
;
1752 int filtercount
= 0;
1753 size_t oldc
= cpane
->dirc
;
1755 width
= (twidth
/ 2) - 4;
1758 dir
= opendir(cpane
->dirn
);
1762 /* get content and filter sum */
1763 while ((entry
= readdir(dir
)) != 0) {
1764 if (cpane
->filter
!= NULL
) {
1765 if (strcasestr(entry
->d_name
, cpane
->filter
) != NULL
)
1767 } else { /* no filter */
1772 if (cpane
->filter
== NULL
) {
1777 if (cpane
->filter
!= NULL
) {
1778 if (filtercount
> 0) {
1779 cpane
->dirc
= filtercount
;
1782 } else if (filtercount
== 0) {
1783 if (closedir(dir
) < 0)
1790 /* print current directory title */
1791 cpane
->dircol
.fg
|= TB_BOLD
;
1792 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1794 if (cpane
->filter
== NULL
) /* dont't watch when filtering */
1796 print_error("can't add watch");
1798 /* empty directory */
1799 if (cpane
->dirc
== 0) {
1801 if (closedir(dir
) < 0)
1806 rewinddir(dir
); /* reset position */
1807 set_direntr(entry
, dir
, cpane
->filter
); /* create array of entries */
1808 qsort(cpane
->direntr
, cpane
->dirc
, sizeof(Entry
), sort_name
);
1812 add_hi(cpane
, cpane
->hdir
- 1);
1814 if (closedir(dir
) < 0)
1822 /* TODO need refactoring */
1825 pane_r
.dirx
= (twidth
/ 2) + 2;
1827 if (cpane
== &pane_l
) {
1832 if (cpane
->dirc
> 0)
1833 add_hi(&pane_l
, pane_l
.hdir
- 1);
1834 } else if (cpane
== &pane_r
) {
1839 if (cpane
->dirc
> 0)
1840 add_hi(&pane_r
, pane_r
.hdir
- 1);
1849 editor
[0] = getenv("EDITOR");
1852 if (editor
[0] == NULL
)
1862 home
= getenv("HOME");
1865 if ((getcwd(cwd
, sizeof(cwd
)) == NULL
))
1866 strncpy(cwd
, home
, MAX_P
);
1870 pane_l
.dircol
= cpanell
;
1871 pane_l
.firstrow
= 0;
1872 pane_l
.direntr
= ecalloc(0, sizeof(Entry
));
1873 strncpy(pane_l
.dirn
, cwd
, MAX_P
);
1875 pane_l
.inotify_wd
= -1;
1876 pane_l
.parent_row
= 1;
1879 pane_r
.dirx
= (twidth
/ 2) + 2;
1880 pane_r
.dircol
= cpanelr
;
1881 pane_r
.firstrow
= 0;
1882 pane_r
.direntr
= ecalloc(0, sizeof(Entry
));
1883 strncpy(pane_r
.dirn
, home
, MAX_P
);
1885 pane_r
.inotify_wd
= -1;
1886 pane_r
.parent_row
= 1;
1893 theight
= tb_height();
1894 twidth
= tb_width();
1895 scrheight
= theight
- 2;
1897 /* 2 horizontal lines */
1898 for (i
= 1; i
< twidth
- 1; ++i
) {
1899 tb_change_cell(i
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
1900 tb_change_cell(i
, theight
- 2, u_hl
, cframe
.fg
, cframe
.bg
);
1903 /* 4 vertical lines */
1904 for (i
= 1; i
< theight
- 1; ++i
) {
1905 tb_change_cell(0, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1906 tb_change_cell((twidth
- 1) / 2, i
- 1, u_vl
, cframe
.fg
,
1908 tb_change_cell(((twidth
- 1) / 2) + 1, i
- 1, u_vl
, cframe
.fg
,
1910 tb_change_cell(twidth
- 1, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1914 tb_change_cell(0, 0, u_cnw
, cframe
.fg
, cframe
.bg
);
1915 tb_change_cell(twidth
- 1, 0, u_cne
, cframe
.fg
, cframe
.bg
);
1916 tb_change_cell(0, theight
- 2, u_csw
, cframe
.fg
, cframe
.bg
);
1917 tb_change_cell(twidth
- 1, theight
- 2, u_cse
, cframe
.fg
, cframe
.bg
);
1919 /* 2 middel top and bottom */
1920 tb_change_cell((twidth
- 1) / 2, 0, u_mn
, cframe
.fg
, cframe
.bg
);
1921 tb_change_cell((twidth
- 1) / 2, theight
- 2, u_ms
, cframe
.fg
, cframe
.bg
);
1929 if (tb_select_output_mode(TB_OUTPUT_256
) != TB_OUTPUT_256
)
1930 if (tb_select_output_mode(TB_OUTPUT_NORMAL
) != TB_OUTPUT_NORMAL
)
1931 die("output error");
1936 if (fsev_init() < 0)
1937 print_error(strerror(errno
));
1939 if (listdir(NoHi
) < 0)
1940 print_error(strerror(errno
));
1942 if (listdir(AddHi
) < 0)
1943 print_error(strerror(errno
));
1949 main(int argc
, char *argv
[])
1952 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1955 #endif /* __OpenBSD__ */
1958 else if (argc
== 2 && strncmp("-v", argv
[1], 2) == 0)
1959 die("sfm-" VERSION
);
1961 die("usage: sfm [-v]");