1 /* See LICENSE file for copyright and license details. */
4 #include <sys/resource.h>
9 #include <sys/inotify.h>
10 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
11 defined(__OpenBSD__) || defined(__APPLE__)
12 #include <sys/event.h>
38 enum { AskDel
, DAskDel
}; /* delete directory */
39 enum { AddHi
, NoHi
}; /* add highlight in listdir */
40 enum { AllP
, PtP
}; /* set pane items */
59 char dirn
[MAX_P
]; // dir name cwd
60 Entry
*direntr
; // dir entries
61 int dirx
; // pane cwd x pos
62 size_t dirc
; // dir entries sum
63 size_t hdir
; // highlighted dir
65 int parent_row
; // FIX
83 uint16_t key
; /* one of the TB_KEY_* constants */
84 uint32_t ch
; /* unicode character */
92 /* function declarations */
93 static void print_tb(const char *, int, int, uint16_t, uint16_t);
94 static void printf_tb(int, int, Cpair
, const char *, ...);
95 static void print_status(Cpair
, const char *, ...);
96 static void print_xstatus(char, int);
97 static void print_error(char *);
98 static void print_prompt(char *);
99 static void print_info(void);
100 static void print_row(Pane
*, size_t, Cpair
);
101 static void clear(int, int, int, uint16_t);
102 static void clear_status(void);
103 static void clear_pane(int);
104 static void add_hi(Pane
*, size_t);
105 static void rm_hi(Pane
*, size_t);
106 static void float_to_string(float, char *);
107 static int check_dir(char *);
108 static mode_t
chech_execf(mode_t
);
109 static int sort_name(const void *const, const void *const);
110 static char *get_ext(char *);
111 static int get_fdt(char *, size_t, time_t);
112 static char *get_fgrp(gid_t
, size_t);
113 static char *get_finfo(Entry
*);
114 static char *get_fperm(mode_t
);
115 static char *get_fsize(off_t
);
116 static char *get_fullpath(char *, char *);
117 static char *get_fusr(uid_t
, size_t);
118 static void get_dirsize(char *, off_t
*);
119 static void get_hicol(Cpair
*, mode_t
);
120 static int deldir(char *, int);
121 static int delent(char *);
122 static int delf(char *);
123 static void calcdir(void);
124 static void crnd(void);
125 static void crnf(void);
126 static void delfd(void);
127 static void mvbk(void);
128 static void mvbtm(void);
129 static void mvdwn(void);
130 static void mvdwns(void);
131 static void mvfor(void);
132 static void mvmid(void);
133 static void mvtop(void);
134 static void mvup(void);
135 static void mvups(void);
136 static void scrdwn(void);
137 static void scrdwns(void);
138 static void scrup(void);
139 static void scrups(void);
140 static int get_usrinput(char *, size_t, char *);
141 static int frules(char *);
142 static int spawn(const void *, char *);
143 static int opnf(char *);
144 static int fsev_init(void);
145 static int addwatch(void);
146 static int read_events(void);
147 static void rmwatch(Pane
*);
148 static void fsev_shdn(void);
149 static ssize_t
findbm(uint32_t);
150 static void filter(void);
151 static void switch_pane(void);
152 static void quit(void);
153 static void grabkeys(struct tb_event
*);
154 static void start_ev(void);
155 static void refresh_pane(void);
156 static int listdir(int, char *);
157 static void t_resize(void);
158 static void set_panes(int);
159 static void draw_frame(void);
160 static void start(void);
162 /* global variables */
163 static Pane pane_r
, pane_l
, *cpane
;
164 static char fed
[] = "vi";
165 static size_t scrheight
;
166 #if defined _SYS_INOTIFY_H
167 static int inotify_fd
;
168 #elif defined _SYS_EVENT_H_
170 struct kevent evlist
[2]; /* events we want to monitor */
171 struct kevent chlist
[2]; /* events that were triggered */
172 static struct timespec gtimeout
;
175 /* configuration, allows nested code to access above variables */
178 /* function implementations */
180 print_tb(const char *str
, int x
, int y
, uint16_t fg
, uint16_t bg
)
182 while (*str
!= '\0') {
184 str
+= tb_utf8_char_to_unicode(&uni
, str
);
185 tb_change_cell(x
, y
, uni
, fg
, bg
);
191 printf_tb(int x
, int y
, Cpair col
, const char *fmt
, ...)
196 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
198 print_tb(buf
, x
, y
, col
.fg
, col
.bg
);
202 print_status(Cpair col
, const char *fmt
, ...)
205 height
= tb_height();
210 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
213 print_tb(buf
, 1, height
- 1, col
.fg
, col
.bg
);
217 print_xstatus(char c
, int x
)
221 height
= tb_height();
222 (void)tb_utf8_char_to_unicode(&uni
, &c
);
223 tb_change_cell(x
, height
- 1, uni
, cstatus
.fg
, cstatus
.bg
);
227 print_error(char *errmsg
)
229 print_status(cerr
, errmsg
);
233 print_prompt(char *prompt
)
235 print_status(cprompt
, prompt
);
242 fileinfo
= get_finfo(&cpane
->direntr
[cpane
->hdir
- 1]);
243 print_status(cstatus
, "%lu/%lu %s", cpane
->hdir
, cpane
->dirc
, fileinfo
);
248 print_row(Pane
*pane
, size_t entpos
, Cpair col
)
253 char lnk_full
[MAX_P
];
256 width
= (tb_width() / 2) - 4;
257 result
= pane
->direntr
[entpos
].name
;
259 y
= entpos
- cpane
->firstrow
+ 1;
261 if (S_ISLNK(pane
->direntr
[entpos
].mode
) &&
262 realpath(pane
->direntr
[entpos
].name
, buf
) != NULL
) {
263 strncpy(lnk_full
, pane
->direntr
[entpos
].name
, MAX_N
);
264 strcat(lnk_full
, " -> ");
265 strncat(lnk_full
, buf
, MAX_N
);
269 printf_tb(x
, y
, col
, "%*.*s", ~width
, width
, result
);
273 clear(int sx
, int ex
, int y
, uint16_t bg
)
275 /* clear line from to */
276 /* x = line number vertical */
277 /* y = column number horizontal */
279 for (i
= sx
; i
< ex
; i
++) {
280 tb_change_cell(i
, y
, 0x0000, TB_DEFAULT
, bg
);
289 height
= tb_height();
290 clear(1, width
- 1, height
- 1, cstatus
.bg
);
296 int i
, x
, ex
, y
, width
, height
;
298 height
= tb_height();
299 x
= 0, y
= 0, i
= 0, ex
= 0;
303 ex
= (width
/ 2) - 1;
304 } else if (pane
== (width
/ 2) + 2) {
309 while (i
< height
- 2) {
310 clear(x
, ex
, y
, TB_DEFAULT
);
315 for (y
= x
; y
< ex
; ++y
) {
316 tb_change_cell(y
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
321 add_hi(Pane
*pane
, size_t entpos
)
324 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
325 col
.fg
|= TB_REVERSE
| TB_BOLD
;
326 col
.bg
|= TB_REVERSE
;
327 print_row(pane
, entpos
, col
);
331 rm_hi(Pane
*pane
, size_t entpos
)
334 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
335 print_row(pane
, entpos
, col
);
339 float_to_string(float f
, char *r
)
341 int length
, length2
, i
, number
, position
,
342 tenth
; /* length is size of decimal part, length2 is size of tenth part */
345 f
= (float)(int)(f
* 10) / 10;
352 /* Calculate length2 tenth part */
353 while ((number2
- (float)number
) != 0.0 &&
354 !((number2
- (float)number
) < 0.0)) {
356 number2
= f
* (float)tenth
;
357 number
= (int)number2
;
362 /* Calculate length decimal part */
363 for (length
= (f
> 1.0) ? 0 : 1; f
> 1.0; length
++)
367 length
= length
+ 1 + length2
;
368 number
= (int)number2
;
371 for (i
= length
; i
>= 0; i
--) {
374 else if (i
== (position
))
377 r
[i
] = (char)(number
% 10) + '0';
383 for (i
= length
; i
>= 0; i
--) {
387 r
[i
] = (char)(number
% 10) + '0';
395 check_dir(char *path
)
401 if (errno
== ENOTDIR
) {
408 if (closedir(dir
) < 0)
415 chech_execf(mode_t mode
)
418 return (((S_IXUSR
| S_IXGRP
| S_IXOTH
) & mode
));
423 sort_name(const void *const A
, const void *const B
)
426 mode_t data1
= (*(Entry
*)A
).mode
;
427 mode_t data2
= (*(Entry
*)B
).mode
;
431 } else if (data1
== data2
) {
432 result
= strcmp((*(Entry
*)A
).name
, (*(Entry
*)B
).name
);
444 size_t counter
, len
, i
;
450 for (i
= len
- 1; i
> 0; i
--) {
458 ext
= ecalloc(counter
+ 1, sizeof(char));
459 strncpy(ext
, &str
[len
- counter
], counter
);
464 get_fdt(char *result
, size_t reslen
, time_t status
)
467 localtime_r(&status
, <
);
468 return strftime(result
, reslen
, dtfmt
, <
);
472 get_fgrp(gid_t status
, size_t len
)
477 result
= ecalloc(len
, sizeof(char));
478 gr
= getgrgid(status
);
480 (void)snprintf(result
, len
- 1, "%d", (int)status
);
482 strncpy(result
, gr
->gr_name
, len
- 1);
488 get_finfo(Entry
*cursor
)
490 char *sz
, *rst
, *ur
, *gr
, *td
, *prm
;
491 size_t szlen
, prmlen
, urlen
, grlen
, tdlen
, rstlen
;
495 urlen
= grlen
= tdlen
= 32;
496 rstlen
= szlen
+ prmlen
+ urlen
+ grlen
+ tdlen
;
497 rst
= ecalloc(rstlen
, sizeof(char));
499 if (show_perm
== 1) {
500 prm
= get_fperm(cursor
->mode
);
501 strncpy(rst
, prm
, prmlen
);
507 ur
= get_fusr(cursor
->user
, urlen
);
508 gr
= get_fgrp(cursor
->group
, grlen
);
509 strncat(rst
, ur
, urlen
);
511 strncat(rst
, gr
, grlen
);
518 td
= ecalloc(tdlen
, sizeof(char));
519 if (get_fdt(td
, tdlen
, cursor
->td
) > 0) {
520 strncat(rst
, td
, tdlen
);
526 if (show_size
== 1 && S_ISREG(cursor
->mode
)) {
527 sz
= get_fsize(cursor
->size
);
528 strncat(rst
, sz
, szlen
);
536 get_fperm(mode_t mode
)
541 const char chars
[] = "rwxrwxrwx";
542 buf
= ecalloc((size_t)11, sizeof(char));
546 else if (S_ISREG(mode
))
548 else if (S_ISLNK(mode
))
550 else if (S_ISBLK(mode
))
552 else if (S_ISCHR(mode
))
554 else if (S_ISFIFO(mode
))
556 else if (S_ISSOCK(mode
))
561 for (i
= 1; i
< 10; i
++) {
562 buf
[i
] = (mode
& (1 << (9 - i
))) ? chars
[i
- 1] : '-';
570 get_fsize(off_t size
)
572 /* need to be freed */
578 Rsize
= ecalloc((size_t)10, sizeof(char));
581 while (lsize
>= 1000.0) {
586 float_to_string(lsize
, Rsize
);
610 get_fullpath(char *first
, char *second
)
613 size_t full_path_len
;
615 full_path_len
= strlen(first
) + strlen(second
) + 2;
616 full_path
= ecalloc(full_path_len
, sizeof(char));
618 if (strcmp(first
, "/") == 0) {
619 (void)snprintf(full_path
, full_path_len
, "/%s", second
);
622 (void)snprintf(full_path
, full_path_len
, "%s/%s", first
,
630 get_fusr(uid_t status
, size_t len
)
635 result
= ecalloc(len
, sizeof(char));
636 pw
= getpwuid(status
);
638 (void)snprintf(result
, len
- 1, "%d", (int)status
);
640 strncpy(result
, pw
->pw_name
, len
- 1);
646 get_dirsize(char *fullpath
, off_t
*fullsize
)
651 struct dirent
*entry
;
654 dir
= opendir(fullpath
);
659 while ((entry
= readdir(dir
)) != 0) {
660 if ((strcmp(entry
->d_name
, ".") == 0 ||
661 strcmp(entry
->d_name
, "..") == 0))
664 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
665 if (lstat(ent_full
, &status
) == 0) {
666 mode
= status
.st_mode
;
668 get_dirsize(ent_full
, fullsize
);
671 *fullsize
+= status
.st_size
;
682 get_hicol(Cpair
*col
, mode_t mode
)
687 else if (S_ISLNK(mode
))
689 else if (chech_execf(mode
) > 0)
694 deldir(char *fullpath
, int delchoice
)
696 if (delchoice
== (int)AskDel
) {
698 confirmation
= ecalloc((size_t)2, sizeof(char));
699 if ((get_usrinput(confirmation
, (size_t)2,
700 "delete directory (Y) ?") < 0) ||
701 (strcmp(confirmation
, "Y") != 0)) {
703 return 1; /* canceled by user or wrong confirmation */
708 if (rmdir(fullpath
) == 0)
709 return 0; /* empty directory */
714 struct dirent
*entry
;
717 dir
= opendir(fullpath
);
722 while ((entry
= readdir(dir
)) != 0) {
723 if ((strcmp(entry
->d_name
, ".") == 0 ||
724 strcmp(entry
->d_name
, "..") == 0))
727 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
728 if (lstat(ent_full
, &status
) == 0) {
729 mode
= status
.st_mode
;
731 if (deldir(ent_full
, (int)DAskDel
) < 0) {
735 } else if (S_ISREG(mode
)) {
736 if (unlink(ent_full
) < 0) {
745 print_status(cstatus
, "gotit");
746 if (closedir(dir
) < 0)
749 return rmdir(fullpath
); /* directory after delete all entries */
753 delent(char *fullpath
)
758 if (lstat(fullpath
, &status
) < 0)
761 mode
= status
.st_mode
;
763 return deldir(fullpath
, (int)AskDel
);
765 return delf(fullpath
);
773 confirmation
= ecalloc((size_t)2, sizeof(char));
775 if ((get_usrinput(confirmation
, (size_t)2, "delete file (Y) ?") < 0) ||
776 (strcmp(confirmation
, "Y") != 0)) {
778 return 1; /* canceled by user or wrong confirmation */
782 return unlink(fullpath
);
792 if (S_ISDIR(cpane
->direntr
[cpane
->hdir
- 1].mode
)) {
793 fullsize
= ecalloc(50, sizeof(off_t
));
794 get_dirsize(cpane
->direntr
[cpane
->hdir
- 1].name
, fullsize
);
795 csize
= get_fsize(*fullsize
);
796 result
= get_finfo(&cpane
->direntr
[cpane
->hdir
- 1]);
799 print_status(cstatus
, "%lu/%lu %s%s", cpane
->hdir
, cpane
->dirc
,
810 char *user_input
, *path
;
813 user_input
= ecalloc(MAX_USRI
, sizeof(char));
814 if (get_usrinput(user_input
, MAX_USRI
, "new dir") < 0) {
819 pathlen
= strlen(cpane
->dirn
) + 1 + MAX_USRI
+ 1;
820 path
= ecalloc(pathlen
, sizeof(char));
821 if (snprintf(path
, pathlen
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
827 if (mkdir(path
, ndir_perm
) < 0)
828 print_error(strerror(errno
));
829 else if (listdir(AddHi
, NULL
) < 0)
830 print_error(strerror(errno
));
839 char *user_input
, *path
;
843 user_input
= ecalloc(MAX_USRI
, sizeof(char));
844 if (get_usrinput(user_input
, MAX_USRI
, "new file") < 0) {
849 pathlen
= strlen(cpane
->dirn
) + 1 + MAX_USRI
+ 1;
850 path
= ecalloc(pathlen
, sizeof(char));
851 if (snprintf(path
, pathlen
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
857 rf
= open(path
, O_CREAT
| O_EXCL
, nf_perm
);
860 print_error(strerror(errno
));
863 print_error(strerror(errno
));
864 else if (listdir(AddHi
, NULL
) < 0)
865 print_error(strerror(errno
));
875 switch (delent(cpane
->direntr
[cpane
->hdir
- 1].name
)) {
877 print_error(strerror(errno
));
880 if (BETWEEN(cpane
->hdir
- 1, 1, cpane
->dirc
)) /* last entry */
882 if (listdir(AddHi
, NULL
) < 0)
883 print_error(strerror(errno
));
893 getcwd(cpane
->dirn
, MAX_P
);
895 cpane
->hdir
= cpane
->parent_row
;
896 if (listdir(AddHi
, NULL
) < 0)
897 print_error(strerror(errno
));
898 cpane
->parent_row
= 1;
906 if (cpane
->dirc
> scrheight
) {
907 clear_pane(cpane
->dirx
);
908 rm_hi(cpane
, cpane
->hdir
- 1);
909 cpane
->hdir
= cpane
->dirc
;
910 cpane
->firstrow
= cpane
->dirc
- scrheight
+ 1;
912 add_hi(cpane
, cpane
->hdir
- 1);
914 rm_hi(cpane
, cpane
->hdir
- 1);
915 cpane
->hdir
= cpane
->dirc
;
916 add_hi(cpane
, cpane
->hdir
- 1);
926 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
927 rm_hi(cpane
, cpane
->hdir
- 1);
929 add_hi(cpane
, cpane
->hdir
- 1);
931 mvdwns(); /* scroll */
940 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
942 if (real
> scrheight
- 3 - scrsp
&& cpane
->hdir
+ scrsp
< cpane
->dirc
) {
944 clear_pane(cpane
->dirx
);
945 rm_hi(cpane
, cpane
->hdir
- 1);
948 add_hi(cpane
, cpane
->hdir
- 1);
949 } else if (cpane
->hdir
< cpane
->dirc
) {
950 rm_hi(cpane
, cpane
->hdir
- 1);
952 add_hi(cpane
, cpane
->hdir
- 1);
964 switch (check_dir(cpane
->direntr
[cpane
->hdir
- 1].name
)) {
966 chdir(cpane
->direntr
[cpane
->hdir
- 1].name
);
967 getcwd(cpane
->dirn
, MAX_P
);
968 cpane
->parent_row
= (int)cpane
->hdir
;
971 if (listdir(AddHi
, NULL
) < 0)
972 print_error(strerror(errno
));
974 case 1: /* not a directory open file */
976 s
= opnf(cpane
->direntr
[cpane
->hdir
- 1].name
);
981 print_error("process failed non-zero exit");
983 case -1: /* failed to open directory */
984 print_error(strerror(errno
));
993 rm_hi(cpane
, cpane
->hdir
- 1);
994 if (cpane
->dirc
< scrheight
/ 2)
995 cpane
->hdir
= (cpane
->dirc
+ 1) / 2;
997 cpane
->hdir
= (scrheight
/ 2) + cpane
->firstrow
;
998 add_hi(cpane
, cpane
->hdir
- 1);
1005 if (cpane
->dirc
< 1)
1007 if (cpane
->dirc
> scrheight
) {
1008 clear_pane(cpane
->dirx
);
1009 rm_hi(cpane
, cpane
->hdir
- 1);
1011 cpane
->firstrow
= 0;
1013 add_hi(cpane
, cpane
->hdir
- 1);
1015 rm_hi(cpane
, cpane
->hdir
- 1);
1017 add_hi(cpane
, cpane
->hdir
- 1);
1025 if (cpane
->dirc
< 1)
1027 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
1028 rm_hi(cpane
, cpane
->hdir
- 1);
1030 add_hi(cpane
, cpane
->hdir
- 1);
1032 mvups(); /* scroll */
1041 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
1043 if (cpane
->firstrow
> 0 && real
< 1 + scrsp
) {
1045 clear_pane(cpane
->dirx
);
1046 rm_hi(cpane
, cpane
->hdir
- 1);
1049 add_hi(cpane
, cpane
->hdir
- 1);
1050 } else if (cpane
->hdir
> 1) {
1051 rm_hi(cpane
, cpane
->hdir
- 1);
1053 add_hi(cpane
, cpane
->hdir
- 1);
1060 if (cpane
->dirc
< 1)
1062 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
1063 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
1064 rm_hi(cpane
, cpane
->hdir
- 1);
1065 cpane
->hdir
+= scrmv
;
1066 add_hi(cpane
, cpane
->hdir
- 1);
1081 real
= cpane
->hdir
- cpane
->firstrow
;
1082 dynmv
= MIN(cpane
->dirc
- cpane
->hdir
- cpane
->firstrow
, scrmv
);
1084 if (real
+ scrmv
+ 1 > scrheight
&&
1085 cpane
->hdir
+ scrsp
+ scrmv
< cpane
->dirc
) { /* scroll */
1086 cpane
->firstrow
+= dynmv
;
1087 clear_pane(cpane
->dirx
);
1088 rm_hi(cpane
, cpane
->hdir
- 1);
1089 cpane
->hdir
+= scrmv
;
1091 add_hi(cpane
, cpane
->hdir
- 1);
1093 if (cpane
->hdir
< cpane
->dirc
- scrmv
- 1) {
1094 rm_hi(cpane
, cpane
->hdir
- 1);
1095 cpane
->hdir
+= scrmv
;
1096 add_hi(cpane
, cpane
->hdir
- 1);
1106 if (cpane
->dirc
< 1)
1108 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
1109 if (cpane
->hdir
> scrmv
) {
1110 rm_hi(cpane
, cpane
->hdir
- 1);
1111 cpane
->hdir
= cpane
->hdir
- scrmv
;
1112 add_hi(cpane
, cpane
->hdir
- 1);
1127 real
= cpane
->hdir
- cpane
->firstrow
;
1128 dynmv
= MIN(cpane
->firstrow
, scrmv
);
1130 if (cpane
->firstrow
> 0 && real
< scrmv
+ scrsp
) {
1131 cpane
->firstrow
-= dynmv
;
1132 clear_pane(cpane
->dirx
);
1133 rm_hi(cpane
, cpane
->hdir
- 1);
1134 cpane
->hdir
-= scrmv
;
1136 add_hi(cpane
, cpane
->hdir
- 1);
1138 if (cpane
->hdir
> scrmv
+ 1) {
1139 rm_hi(cpane
, cpane
->hdir
- 1);
1140 cpane
->hdir
-= scrmv
;
1141 add_hi(cpane
, cpane
->hdir
- 1);
1149 get_usrinput(char *out
, size_t sout
, char *prompt
)
1151 int height
= tb_height();
1153 struct tb_event fev
;
1154 size_t counter
= (size_t)1;
1159 startat
= strlen(prompt
) + 1;
1160 print_prompt(prompt
);
1161 tb_set_cursor((int)(startat
+ 1), height
- 1);
1164 while (tb_poll_event(&fev
) != 0) {
1167 if (fev
.key
== (uint16_t)TB_KEY_ESC
) {
1168 tb_set_cursor(-1, -1);
1173 if (fev
.key
== (uint16_t)TB_KEY_BACKSPACE
||
1174 fev
.key
== (uint16_t)TB_KEY_BACKSPACE2
) {
1175 if (BETWEEN(counter
, (size_t)2, sout
)) {
1179 print_xstatus(empty
, startat
+ counter
);
1180 tb_set_cursor((int)startat
+ counter
,
1184 } else if (fev
.key
== (uint16_t)TB_KEY_ENTER
) {
1185 tb_set_cursor(-1, -1);
1186 out
[counter
- 1] = '\0';
1190 if (counter
< sout
) {
1191 print_xstatus((char)fev
.ch
,
1192 (int)(startat
+ counter
));
1193 out
[x
] = (char)fev
.ch
;
1194 tb_set_cursor((int)(startat
+ counter
+
1218 for (c
= 0; c
< LEN(rules
); c
++)
1219 for (d
= 0; d
< rules
[c
].exlen
; d
++)
1220 if (strncmp(rules
[c
].ext
[d
], ex
, MAX_EXT
) == 0)
1226 spawn(const void *v
, char *fn
)
1235 while (((char **)v
)[x
++] != NULL
)
1238 char *argv
[argc
+ 2];
1239 for ( x
= 0; x
< argc
; x
++)
1240 argv
[x
] = ((char **)v
)[x
];
1243 argv
[argc
+ 1] = NULL
;
1250 execvp(argv
[0], argv
);
1253 while ((r
= waitpid(pid
, &ws
, 0)) == -1 && errno
== EINTR
)
1257 if ((WIFEXITED(ws
) != 0) && (WEXITSTATUS(ws
) != 0))
1273 if (c
< 0) { /* extension not found open in editor */
1274 ed
[0] = getenv("EDITOR");
1278 return spawn(ed
, fn
);
1280 return spawn((char **)rules
[c
].v
, fn
);
1287 #if defined _SYS_INOTIFY_H
1288 inotify_fd
= inotify_init1(IN_NONBLOCK
| IN_CLOEXEC
);
1291 #elif defined _SYS_EVENT_H_
1302 #if defined _SYS_INOTIFY_H
1303 return cpane
->inotify_wd
= inotify_add_watch(inotify_fd
, cpane
->dirn
,
1304 IN_MODIFY
| IN_MOVED_FROM
| IN_MOVED_TO
| IN_CREATE
|
1305 IN_DELETE
| IN_DELETE_SELF
| IN_MOVE_SELF
);
1306 #elif defined _SYS_EVENT_H_
1307 cpane
->event_fd
= open(cpane
->dirn
, O_RDONLY
);
1308 if (cpane
->event_fd
< 0)
1309 return cpane
->event_fd
;
1310 EV_SET(&evlist
[cpane
->pane_id
], cpane
->event_fd
,
1311 EVFILT_VNODE
, EV_ADD
| EV_CLEAR
,
1312 NOTE_DELETE
| NOTE_EXTEND
| NOTE_LINK
|
1313 NOTE_RENAME
| NOTE_REVOKE
| NOTE_WRITE
, 0, NULL
);
1321 #if defined _SYS_INOTIFY_H
1324 struct inotify_event
*event
;
1325 const size_t events
= 32;
1326 const size_t evbuflen
=
1327 events
* (sizeof(struct inotify_event
) + MAX_N
+ 1);
1330 if (cpane
->inotify_wd
< 0)
1332 r
= read(inotify_fd
, buf
, evbuflen
);
1336 for (p
= buf
; p
< buf
+ r
;) {
1337 event
= (struct inotify_event
*)p
;
1344 p
+= sizeof(struct inotify_event
) + event
->len
;
1346 #elif defined _SYS_EVENT_H_
1347 return kevent(kq
, evlist
, 2, chlist
, 2, >imeout
);
1355 #if defined _SYS_INOTIFY_H
1356 if (pane
->inotify_wd
>= 0)
1357 inotify_rm_watch(inotify_fd
, pane
->inotify_wd
);
1358 #elif defined _SYS_EVENT_H_
1359 close(pane
->event_fd
);
1369 #if defined _SYS_INOTIFY_H
1371 #elif defined _SYS_EVENT_H_
1377 findbm(uint32_t event
)
1381 for (i
= 0; i
< (ssize_t
)LEN(bmarks
); i
++) {
1382 if (event
== bmarks
[i
].ch
) {
1383 if (check_dir(bmarks
[i
].path
) != 0) {
1384 print_error(strerror(errno
));
1396 if (cpane
->dirc
< 1)
1399 user_input
= ecalloc(MAX_USRI
, sizeof(char));
1400 if (get_usrinput(user_input
, MAX_USRI
, "filter") < 0) {
1404 if (listdir(AddHi
, user_input
) < 0)
1405 print_error("no match");
1412 if (cpane
->dirc
> 0)
1413 rm_hi(cpane
, cpane
->hdir
- 1);
1414 if (cpane
== &pane_l
)
1416 else if (cpane
== &pane_r
)
1419 if (cpane
->dirc
> 0) {
1420 add_hi(cpane
, cpane
->hdir
- 1);
1430 free(pane_l
.direntr
);
1431 free(pane_r
.direntr
);
1438 grabkeys(struct tb_event
*event
)
1443 for (i
= 0; i
< LEN(keys
); i
++) {
1444 if (event
->ch
!= 0) {
1445 if (event
->ch
== keys
[i
].evkey
.ch
) {
1449 } else if (event
->key
!= 0) {
1450 if (event
->key
== keys
[i
].evkey
.key
) {
1458 b
= findbm(event
->ch
);
1462 strcpy(cpane
->dirn
, bmarks
[b
].path
);
1463 cpane
->firstrow
= 0;
1464 cpane
->parent_row
= 1;
1466 if (listdir(AddHi
, NULL
) < 0)
1467 print_error(strerror(errno
));
1476 int t
= tb_peek_event(&ev
, 2000);
1482 if (t
== 1) /* keyboard event */
1484 else if (t
== 2) /* resize event */
1486 else if (t
== 0) /* filesystem event */
1487 if (read_events() > 0)
1488 if (listdir(AddHi
, NULL
) < 0)
1489 print_error(strerror(errno
));
1500 size_t y
, dyn_max
, start_from
;
1502 width
= (tb_width() / 2) - 4;
1506 start_from
= cpane
->firstrow
;
1507 dyn_max
= MIN(cpane
->dirc
, (scrheight
- 1) + cpane
->firstrow
);
1509 /* print each entry in directory */
1510 while (start_from
< dyn_max
) {
1511 get_hicol(&col
, cpane
->direntr
[start_from
].mode
);
1512 print_row(cpane
, start_from
, col
);
1519 /* print current directory title */
1520 cpane
->dircol
.fg
|= TB_BOLD
;
1521 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1525 listdir(int hi
, char *filter
)
1528 struct dirent
*entry
;
1532 int filtercount
= 0;
1533 size_t oldc
= cpane
->dirc
;
1535 width
= (tb_width() / 2) - 4;
1539 if (chdir(cpane
->dirn
) < 0)
1542 dir
= opendir(cpane
->dirn
);
1546 /* get content and filter sum */
1547 while ((entry
= readdir(dir
)) != 0) {
1548 if (filter
!= NULL
) {
1549 if (strstr(entry
->d_name
, filter
) != NULL
)
1551 } else { /* no filter */
1556 if (filter
== NULL
) {
1557 clear_pane(cpane
->dirx
);
1561 if (filter
!= NULL
) {
1562 if (filtercount
> 0) {
1564 cpane
->dirc
= filtercount
;
1565 clear_pane(cpane
->dirx
);
1567 } else if (filtercount
== 0) {
1568 if (closedir(dir
) < 0)
1575 /* print current directory title */
1576 cpane
->dircol
.fg
|= TB_BOLD
;
1577 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1579 /* empty directory */
1580 if (cpane
->dirc
== 0) {
1582 if (closedir(dir
) < 0)
1587 rewinddir(dir
); /* reset position */
1589 /* create array of entries */
1591 cpane
->direntr
= erealloc(cpane
->direntr
, cpane
->dirc
* sizeof(Entry
));
1592 while ((entry
= readdir(dir
)) != 0) {
1593 if ((strcmp(entry
->d_name
, ".") == 0 ||
1594 strcmp(entry
->d_name
, "..") == 0))
1597 /* list found filter */
1598 if (filter
!= NULL
) {
1599 if (strstr(entry
->d_name
, filter
) != NULL
) {
1600 strcpy(cpane
->direntr
[i
].name
, entry
->d_name
);
1601 if (lstat(entry
->d_name
, &status
) == 0) {
1602 cpane
->direntr
[i
].size
= status
.st_size
;
1603 cpane
->direntr
[i
].mode
= status
.st_mode
;
1604 cpane
->direntr
[i
].group
= status
.st_gid
;
1605 cpane
->direntr
[i
].user
= status
.st_uid
;
1606 cpane
->direntr
[i
].td
= status
.st_mtime
;
1612 strcpy(cpane
->direntr
[i
].name
, entry
->d_name
);
1613 if (lstat(entry
->d_name
, &status
) == 0) {
1614 cpane
->direntr
[i
].size
= status
.st_size
;
1615 cpane
->direntr
[i
].mode
= status
.st_mode
;
1616 cpane
->direntr
[i
].group
= status
.st_gid
;
1617 cpane
->direntr
[i
].user
= status
.st_uid
;
1618 cpane
->direntr
[i
].td
= status
.st_mtime
;
1625 print_error("can't add watch");
1627 qsort(cpane
->direntr
, cpane
->dirc
, sizeof(Entry
), sort_name
);
1631 add_hi(cpane
, cpane
->hdir
- 1);
1633 if (closedir(dir
) < 0)
1641 /* TODO need refactoring */
1646 if (cpane
== &pane_l
) {
1653 add_hi(&pane_l
, pane_l
.hdir
- 1);
1654 } else if (cpane
== &pane_r
) {
1661 add_hi(&pane_r
, pane_r
.hdir
- 1);
1668 set_panes(int paneitem
)
1673 scrheight
= tb_height() - 2;
1675 home
= getenv("HOME");
1677 if ((getcwd(cwd
, sizeof(cwd
)) == NULL
))
1684 pane_l
.dircol
= cpanell
;
1685 if (paneitem
== AllP
) {
1686 pane_l
.firstrow
= 0;
1687 pane_l
.direntr
= ecalloc(0, sizeof(Entry
));
1688 strcpy(pane_l
.dirn
, cwd
);
1690 pane_l
.inotify_wd
= -1;
1691 pane_l
.parent_row
= 1;
1695 pane_r
.dirx
= (width
/ 2) + 2;
1696 pane_r
.dircol
= cpanelr
;
1697 if (paneitem
== AllP
) {
1698 pane_r
.firstrow
= 0;
1699 pane_r
.direntr
= ecalloc(0, sizeof(Entry
));
1700 strcpy(pane_r
.dirn
, home
);
1702 pane_r
.inotify_wd
= -1;
1703 pane_r
.parent_row
= 1;
1710 int height
, width
, i
;
1713 height
= tb_height();
1715 /* 2 horizontal lines */
1716 for (i
= 1; i
< width
- 1; ++i
) {
1717 tb_change_cell(i
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
1718 tb_change_cell(i
, height
- 2, u_hl
, cframe
.fg
, cframe
.bg
);
1721 /* 3 vertical lines */
1722 for (i
= 1; i
< height
- 1; ++i
) {
1723 tb_change_cell(0, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1724 tb_change_cell((width
- 1) / 2, i
- 1, u_vl
, cframe
.fg
,
1726 tb_change_cell(width
- 1, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1730 tb_change_cell(0, 0, u_cnw
, cframe
.fg
, cframe
.bg
);
1731 tb_change_cell(width
- 1, 0, u_cne
, cframe
.fg
, cframe
.bg
);
1732 tb_change_cell(0, height
- 2, u_csw
, cframe
.fg
, cframe
.bg
);
1733 tb_change_cell(width
- 1, height
- 2, u_cse
, cframe
.fg
, cframe
.bg
);
1735 /* 2 middel top and bottom */
1736 tb_change_cell((width
- 1) / 2, 0, u_mn
, cframe
.fg
, cframe
.bg
);
1737 tb_change_cell((width
- 1) / 2, height
- 2, u_ms
, cframe
.fg
, cframe
.bg
);
1745 if (tb_select_output_mode(TB_OUTPUT_256
) != TB_OUTPUT_256
)
1746 if (tb_select_output_mode(TB_OUTPUT_NORMAL
) != TB_OUTPUT_NORMAL
)
1747 die("output error");
1751 if (fsev_init() < 0)
1752 print_error(strerror(errno
));
1754 if (listdir(NoHi
, NULL
) < 0)
1755 print_error(strerror(errno
));
1757 if (listdir(AddHi
, NULL
) < 0)
1758 print_error(strerror(errno
));
1764 main(int argc
, char *argv
[])
1767 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1770 #endif /* __OpenBSD__ */
1773 else if (argc
== 2 && strncmp("-v", argv
[1], 2) == 0)
1774 die("sfm-" VERSION
);
1776 die("usage: sfm [-v]");