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>
37 #define CURSOR_NAME cpane->direntr[cpane->hdir - 1].name
40 enum { AddHi
, NoHi
}; /* add highlight in listdir */
59 char dirn
[MAX_P
]; // dir name cwd
60 Entry
*direntr
; // dir entries
61 int dirx
; // pane cwd x pos
62 int dirc
; // dir entries sum
63 int hdir
; // highlighted dir
66 int parent_row
; // FIX
85 uint16_t key
; /* one of the TB_KEY_* constants */
86 uint32_t ch
; /* unicode character */
94 /* function declarations */
95 static void print_tb(const char *, int, int, uint16_t, uint16_t);
96 static void printf_tb(int, int, Cpair
, const char *, ...);
97 static void print_status(Cpair
, const char *, ...);
98 static void print_xstatus(char, int);
99 static void print_error(char *);
100 static void print_prompt(char *);
101 static void print_info(void);
102 static void print_row(Pane
*, size_t, Cpair
);
103 static void clear(int, int, int, uint16_t);
104 static void clear_status(void);
105 static void clear_pane(void);
106 static void add_hi(Pane
*, size_t);
107 static void rm_hi(Pane
*, size_t);
108 static void float_to_string(float, char *);
109 static int check_dir(char *);
110 static mode_t
chech_execf(mode_t
);
111 static int sort_name(const void *const, const void *const);
112 static void get_dirp(char *);
113 static char *get_ext(char *);
114 static int get_fdt(char *, size_t, time_t);
115 static char *get_fgrp(gid_t
, size_t);
116 static char *get_finfo(Entry
*);
117 static char *get_fperm(mode_t
);
118 static char *get_fsize(off_t
);
119 static char *get_fullpath(char *, char *);
120 static char *get_fusr(uid_t
, size_t);
121 static void get_dirsize(char *, off_t
*);
122 static void get_hicol(Cpair
*, mode_t
);
123 static int delent(char *);
124 static void calcdir(void);
125 static void crnd(void);
126 static void crnf(void);
127 static void delfd(void);
128 static void mvbk(void);
129 static void mvbtm(void);
130 static void mvdwn(void);
131 static void mvdwns(void);
132 static void mvfor(void);
133 static void mvmid(void);
134 static void mvtop(void);
135 static void mvup(void);
136 static void mvups(void);
137 static void scrdwn(void);
138 static void scrdwns(void);
139 static void scrup(void);
140 static void scrups(void);
141 static int get_usrinput(char*, size_t, const char*, ...);
142 static int frules(char *);
143 static int spawn(const void *, char *);
144 static int opnf(char *);
145 static int fsev_init(void);
146 static int addwatch(void);
147 static int read_events(void);
148 static void rmwatch(Pane
*);
149 static void fsev_shdn(void);
150 static ssize_t
findbm(uint32_t);
151 static void filter(void);
152 static void selection(void);
153 static void selup(void);
154 static void seldwn(void);
155 static void selynk(void);
156 static void selcan(void);
157 static void selall(void);
158 static void selref(void);
159 static void selcalc(void);
160 static void selpst(void);
161 static void selmv(void);
162 static void seldel(void);
163 static char *get_path_hdir(int);
164 static void init_files(void);
165 static void free_files(void);
166 static void yank(void);
167 static void rname(void);
168 static void switch_pane(void);
169 static void quit(void);
170 static void grabkeys(struct tb_event
*, Key
*, size_t);
171 static void start_ev(void);
172 static void refresh_pane(void);
173 static void set_direntr(struct dirent
*, DIR *);
174 static int listdir(int, char *);
175 static void t_resize(void);
176 static void set_panes(void);
177 static void draw_frame(void);
178 static void start(void);
180 /* global variables */
181 static Pane pane_r
, pane_l
, *cpane
;
182 static char *editor
[2];
183 static char fed
[] = "vi";
184 static int theight
, twidth
, scrheight
;
185 static size_t selection_size
= 0;
186 static char yank_file
[MAX_P
];
189 #if defined _SYS_INOTIFY_H
190 static int inotify_fd
;
191 #elif defined _SYS_EVENT_H_
193 struct kevent evlist
[2]; /* events we want to monitor */
194 struct kevent chlist
[2]; /* events that were triggered */
195 static struct timespec gtimeout
;
198 /* configuration, allows nested code to access above variables */
201 /* function implementations */
203 print_tb(const char *str
, int x
, int y
, uint16_t fg
, uint16_t bg
)
205 while (*str
!= '\0') {
207 str
+= tb_utf8_char_to_unicode(&uni
, str
);
208 tb_change_cell(x
, y
, uni
, fg
, bg
);
214 printf_tb(int x
, int y
, Cpair col
, const char *fmt
, ...)
219 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
221 print_tb(buf
, x
, y
, col
.fg
, col
.bg
);
225 print_status(Cpair col
, const char *fmt
, ...)
230 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
233 print_tb(buf
, 1, theight
- 1, col
.fg
, col
.bg
);
237 print_xstatus(char c
, int x
)
240 (void)tb_utf8_char_to_unicode(&uni
, &c
);
241 tb_change_cell(x
, theight
- 1, uni
, cstatus
.fg
, cstatus
.bg
);
245 print_error(char *errmsg
)
247 print_status(cerr
, errmsg
);
251 print_prompt(char *prompt
)
253 print_status(cprompt
, prompt
);
260 fileinfo
= get_finfo(&cpane
->direntr
[cpane
->hdir
- 1]);
261 print_status(cstatus
, "%d/%d %s", cpane
->hdir
, cpane
->dirc
, fileinfo
);
266 print_row(Pane
*pane
, size_t entpos
, Cpair col
)
271 char lnk_full
[MAX_P
];
274 width
= (twidth
/ 2) - 4;
275 result
= basename(pane
->direntr
[entpos
].name
);
277 y
= entpos
- cpane
->firstrow
+ 1;
279 if (S_ISLNK(pane
->direntr
[entpos
].mode
) &&
280 realpath(pane
->direntr
[entpos
].name
, buf
) != NULL
) {
281 strncpy(lnk_full
, pane
->direntr
[entpos
].name
, MAX_N
);
282 strcat(lnk_full
, " -> ");
283 strncat(lnk_full
, buf
, MAX_N
);
287 printf_tb(x
, y
, col
, "%*.*s", ~width
, width
, result
);
291 clear(int sx
, int ex
, int y
, uint16_t bg
)
293 /* clear line from to */
294 /* x = line number vertical */
295 /* y = column number horizontal */
297 for (i
= sx
; i
< ex
; i
++) {
298 tb_change_cell(i
, y
, 0x0000, TB_DEFAULT
, bg
);
305 clear(1, twidth
- 1, theight
- 1, cstatus
.bg
);
312 y
= 0, i
= 0, ex
= 0;
314 if (cpane
->pane_id
== pane_l
.pane_id
)
315 ex
= (twidth
/ 2) - 1;
316 else if (cpane
->pane_id
== pane_r
.pane_id
)
319 while (i
< scrheight
) {
320 clear(cpane
->dirx
, ex
, y
, TB_DEFAULT
);
326 for (y
= cpane
->dirx
; y
< ex
; ++y
) {
327 tb_change_cell(y
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
332 add_hi(Pane
*pane
, size_t entpos
)
335 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
336 col
.fg
|= TB_REVERSE
| TB_BOLD
;
337 col
.bg
|= TB_REVERSE
;
338 print_row(pane
, entpos
, col
);
342 rm_hi(Pane
*pane
, size_t entpos
)
345 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
346 print_row(pane
, entpos
, col
);
350 float_to_string(float f
, char *r
)
352 int length
, length2
, i
, number
, position
,
353 tenth
; /* length is size of decimal part, length2 is size of tenth part */
356 f
= (float)(int)(f
* 10) / 10;
363 /* Calculate length2 tenth part */
364 while ((number2
- (float)number
) != 0.0 &&
365 !((number2
- (float)number
) < 0.0)) {
367 number2
= f
* (float)tenth
;
368 number
= (int)number2
;
373 /* Calculate length decimal part */
374 for (length
= (f
> 1.0) ? 0 : 1; f
> 1.0; length
++)
378 length
= length
+ 1 + length2
;
379 number
= (int)number2
;
382 for (i
= length
; i
>= 0; i
--) {
385 else if (i
== (position
))
388 r
[i
] = (char)(number
% 10) + '0';
394 for (i
= length
; i
>= 0; i
--) {
398 r
[i
] = (char)(number
% 10) + '0';
406 check_dir(char *path
)
412 if (errno
== ENOTDIR
) {
419 if (closedir(dir
) < 0)
426 chech_execf(mode_t mode
)
429 return (((S_IXUSR
| S_IXGRP
| S_IXOTH
) & mode
));
434 sort_name(const void *const A
, const void *const B
)
437 mode_t data1
= (*(Entry
*)A
).mode
;
438 mode_t data2
= (*(Entry
*)B
).mode
;
442 } else if (data1
== data2
) {
443 result
= strcmp((*(Entry
*)A
).name
, (*(Entry
*)B
).name
);
461 for (i
= len
- 1; i
> 1; i
--) {
468 cdir
[len
-counter
-1] = '\0';
476 size_t counter
, len
, i
;
482 for (i
= len
- 1; i
> 0; i
--) {
490 ext
= ecalloc(counter
+ 1, sizeof(char));
491 strncpy(ext
, &str
[len
- counter
], counter
);
496 get_fdt(char *result
, size_t reslen
, time_t status
)
499 localtime_r(&status
, <
);
500 return strftime(result
, reslen
, dtfmt
, <
);
504 get_fgrp(gid_t status
, size_t len
)
509 result
= ecalloc(len
, sizeof(char));
510 gr
= getgrgid(status
);
512 (void)snprintf(result
, len
- 1, "%u", status
);
514 strncpy(result
, gr
->gr_name
, len
- 1);
520 get_finfo(Entry
*cursor
)
522 char *sz
, *rst
, *ur
, *gr
, *td
, *prm
;
523 size_t szlen
, prmlen
, urlen
, grlen
, tdlen
, rstlen
;
527 urlen
= grlen
= tdlen
= 32;
528 rstlen
= szlen
+ prmlen
+ urlen
+ grlen
+ tdlen
;
529 rst
= ecalloc(rstlen
, sizeof(char));
531 if (show_perm
== 1) {
532 prm
= get_fperm(cursor
->mode
);
533 strncpy(rst
, prm
, prmlen
);
539 ur
= get_fusr(cursor
->user
, urlen
);
540 gr
= get_fgrp(cursor
->group
, grlen
);
541 strncat(rst
, ur
, urlen
);
543 strncat(rst
, gr
, grlen
);
550 td
= ecalloc(tdlen
, sizeof(char));
551 if (get_fdt(td
, tdlen
, cursor
->td
) > 0) {
552 strncat(rst
, td
, tdlen
);
558 if (show_size
== 1 && S_ISREG(cursor
->mode
)) {
559 sz
= get_fsize(cursor
->size
);
560 strncat(rst
, sz
, szlen
);
568 get_fperm(mode_t mode
)
573 const char chars
[] = "rwxrwxrwx";
574 buf
= ecalloc(11, sizeof(char));
578 else if (S_ISREG(mode
))
580 else if (S_ISLNK(mode
))
582 else if (S_ISBLK(mode
))
584 else if (S_ISCHR(mode
))
586 else if (S_ISFIFO(mode
))
588 else if (S_ISSOCK(mode
))
593 for (i
= 1; i
< 10; i
++) {
594 buf
[i
] = (mode
& (1 << (9 - i
))) ? chars
[i
- 1] : '-';
602 get_fsize(off_t size
)
604 /* need to be freed */
610 Rsize
= ecalloc(10, sizeof(char));
613 while (lsize
>= 1000.0) {
618 float_to_string(lsize
, Rsize
);
642 get_fullpath(char *first
, char *second
)
645 size_t full_path_len
;
647 full_path_len
= strlen(first
) + strlen(second
) + 2;
648 full_path
= ecalloc(full_path_len
, sizeof(char));
650 if (strcmp(first
, "/") == 0) {
651 (void)snprintf(full_path
, full_path_len
, "/%s", second
);
654 (void)snprintf(full_path
, full_path_len
, "%s/%s", first
,
662 get_fusr(uid_t status
, size_t len
)
667 result
= ecalloc(len
, sizeof(char));
668 pw
= getpwuid(status
);
670 (void)snprintf(result
, len
- 1, "%u", status
);
672 strncpy(result
, pw
->pw_name
, len
- 1);
678 get_dirsize(char *fullpath
, off_t
*fullsize
)
683 struct dirent
*entry
;
686 dir
= opendir(fullpath
);
691 while ((entry
= readdir(dir
)) != 0) {
692 if ((strcmp(entry
->d_name
, ".") == 0 ||
693 strcmp(entry
->d_name
, "..") == 0))
696 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
697 if (lstat(ent_full
, &status
) == 0) {
698 mode
= status
.st_mode
;
700 get_dirsize(ent_full
, fullsize
);
703 *fullsize
+= status
.st_size
;
714 get_hicol(Cpair
*col
, mode_t mode
)
719 else if (S_ISLNK(mode
))
721 else if (chech_execf(mode
) > 0)
726 delent(char *fullpath
)
729 char *rm_cmd
[] = { "rm", "-rf", NULL
};
733 inp_conf
= ecalloc(conf_len
, sizeof(char));
734 if ((get_usrinput(inp_conf
, conf_len
, "delete file (yes) ?") < 0) ||
735 (strncmp(inp_conf
, conf
, conf_len
) != 0)) {
737 return 1; /* canceled by user or wrong inp_conf */
741 return spawn(rm_cmd
, fullpath
);
751 if (S_ISDIR(cpane
->direntr
[cpane
->hdir
- 1].mode
)) {
752 fullsize
= ecalloc(50, sizeof(off_t
));
753 get_dirsize(CURSOR_NAME
, fullsize
);
754 csize
= get_fsize(*fullsize
);
755 result
= get_finfo(&cpane
->direntr
[cpane
->hdir
- 1]);
758 print_status(cstatus
, "%d/%d %s%s", cpane
->hdir
, cpane
->dirc
,
769 char *user_input
, *path
;
772 user_input
= ecalloc(MAX_USRI
, sizeof(char));
773 if (get_usrinput(user_input
, MAX_USRI
, "new dir") < 0) {
778 pathlen
= strlen(cpane
->dirn
) + 1 + MAX_USRI
+ 1;
779 path
= ecalloc(pathlen
, sizeof(char));
780 if (snprintf(path
, pathlen
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
786 if (mkdir(path
, ndir_perm
) < 0)
787 print_error(strerror(errno
));
788 else if (listdir(AddHi
, NULL
) < 0)
789 print_error(strerror(errno
));
798 char *user_input
, *path
;
802 user_input
= ecalloc(MAX_USRI
, sizeof(char));
803 if (get_usrinput(user_input
, MAX_USRI
, "new file") < 0) {
808 pathlen
= strlen(cpane
->dirn
) + 1 + MAX_USRI
+ 1;
809 path
= ecalloc(pathlen
, sizeof(char));
810 if (snprintf(path
, pathlen
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
816 rf
= open(path
, O_CREAT
| O_EXCL
, nf_perm
);
819 print_error(strerror(errno
));
822 print_error(strerror(errno
));
823 else if (listdir(AddHi
, NULL
) < 0)
824 print_error(strerror(errno
));
834 switch (delent(CURSOR_NAME
)) {
836 print_error(strerror(errno
));
839 if (BETWEEN(cpane
->hdir
- 1, 1, cpane
->dirc
)) /* last entry */
841 if (listdir(AddHi
, NULL
) < 0)
842 print_error(strerror(errno
));
850 get_dirp(cpane
->dirn
);
851 if (check_dir(cpane
->dirn
) < 0) {
852 print_error(strerror(errno
));
857 cpane
->firstrow
= cpane
->parent_firstrow
;
858 cpane
->hdir
= cpane
->parent_row
;
859 if (listdir(AddHi
, NULL
) < 0)
860 print_error(strerror(errno
));
861 cpane
->parent_firstrow
= 0;
862 cpane
->parent_row
= 1;
870 if (cpane
->dirc
> scrheight
) {
871 rm_hi(cpane
, cpane
->hdir
- 1);
872 cpane
->hdir
= cpane
->dirc
;
873 cpane
->firstrow
= cpane
->dirc
- scrheight
+ 1;
875 add_hi(cpane
, cpane
->hdir
- 1);
877 rm_hi(cpane
, cpane
->hdir
- 1);
878 cpane
->hdir
= cpane
->dirc
;
879 add_hi(cpane
, cpane
->hdir
- 1);
889 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
890 rm_hi(cpane
, cpane
->hdir
- 1);
892 add_hi(cpane
, cpane
->hdir
- 1);
894 mvdwns(); /* scroll */
903 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
905 if (real
> scrheight
- 3 - scrsp
&& cpane
->hdir
+ scrsp
< cpane
->dirc
) {
907 rm_hi(cpane
, cpane
->hdir
- 1);
910 add_hi(cpane
, cpane
->hdir
- 1);
911 } else if (cpane
->hdir
< cpane
->dirc
) {
912 rm_hi(cpane
, cpane
->hdir
- 1);
914 add_hi(cpane
, cpane
->hdir
- 1);
926 switch (check_dir(CURSOR_NAME
)) {
928 strcpy(cpane
->dirn
, CURSOR_NAME
);
929 cpane
->parent_row
= cpane
->hdir
;
930 cpane
->parent_firstrow
= cpane
->firstrow
;
933 if (listdir(AddHi
, NULL
) < 0)
934 print_error(strerror(errno
));
936 case 1: /* not a directory open file */
938 s
= opnf(CURSOR_NAME
);
943 print_error("process failed non-zero exit");
945 case -1: /* failed to open directory */
946 print_error(strerror(errno
));
955 rm_hi(cpane
, cpane
->hdir
- 1);
956 if (cpane
->dirc
< scrheight
/ 2)
957 cpane
->hdir
= (cpane
->dirc
+ 1) / 2;
959 cpane
->hdir
= (scrheight
/ 2) + cpane
->firstrow
;
960 add_hi(cpane
, cpane
->hdir
- 1);
969 if (cpane
->dirc
> scrheight
) {
970 rm_hi(cpane
, cpane
->hdir
- 1);
974 add_hi(cpane
, cpane
->hdir
- 1);
976 rm_hi(cpane
, cpane
->hdir
- 1);
978 add_hi(cpane
, cpane
->hdir
- 1);
988 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
989 rm_hi(cpane
, cpane
->hdir
- 1);
991 add_hi(cpane
, cpane
->hdir
- 1);
993 mvups(); /* scroll */
1002 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
1004 if (cpane
->firstrow
> 0 && real
< 1 + scrsp
) {
1006 rm_hi(cpane
, cpane
->hdir
- 1);
1009 add_hi(cpane
, cpane
->hdir
- 1);
1010 } else if (cpane
->hdir
> 1) {
1011 rm_hi(cpane
, cpane
->hdir
- 1);
1013 add_hi(cpane
, cpane
->hdir
- 1);
1020 if (cpane
->dirc
< 1)
1022 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
1023 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
1024 rm_hi(cpane
, cpane
->hdir
- 1);
1025 cpane
->hdir
+= scrmv
;
1026 add_hi(cpane
, cpane
->hdir
- 1);
1041 real
= cpane
->hdir
- cpane
->firstrow
;
1042 dynmv
= MIN(cpane
->dirc
- cpane
->hdir
- cpane
->firstrow
, scrmv
);
1044 if (real
+ scrmv
+ 1 > scrheight
&&
1045 cpane
->hdir
+ scrsp
+ scrmv
< cpane
->dirc
) { /* scroll */
1046 cpane
->firstrow
+= dynmv
;
1047 rm_hi(cpane
, cpane
->hdir
- 1);
1048 cpane
->hdir
+= scrmv
;
1050 add_hi(cpane
, cpane
->hdir
- 1);
1052 if (cpane
->hdir
< cpane
->dirc
- scrmv
- 1) {
1053 rm_hi(cpane
, cpane
->hdir
- 1);
1054 cpane
->hdir
+= scrmv
;
1055 add_hi(cpane
, cpane
->hdir
- 1);
1065 if (cpane
->dirc
< 1)
1067 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
1068 if (cpane
->hdir
> scrmv
) {
1069 rm_hi(cpane
, cpane
->hdir
- 1);
1070 cpane
->hdir
= cpane
->hdir
- scrmv
;
1071 add_hi(cpane
, cpane
->hdir
- 1);
1085 real
= cpane
->hdir
- cpane
->firstrow
;
1086 dynmv
= MIN(cpane
->firstrow
, scrmv
);
1088 if (cpane
->firstrow
> 0 && real
< scrmv
+ scrsp
) {
1089 cpane
->firstrow
-= dynmv
;
1090 rm_hi(cpane
, cpane
->hdir
- 1);
1091 cpane
->hdir
-= scrmv
;
1093 add_hi(cpane
, cpane
->hdir
- 1);
1095 if (cpane
->hdir
> scrmv
+ 1) {
1096 rm_hi(cpane
, cpane
->hdir
- 1);
1097 cpane
->hdir
-= scrmv
;
1098 add_hi(cpane
, cpane
->hdir
- 1);
1106 get_usrinput(char *out
, size_t sout
, const char *fmt
, ...)
1108 int height
= tb_height();
1110 struct tb_event fev
;
1111 size_t counter
= (size_t)1;
1123 name_size
= vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
1125 print_tb(buf
, 1, height
-1, col
.fg
, col
.bg
);
1126 startat
= name_size
+ 1;
1127 tb_set_cursor((int)(startat
+ 1), height
-1);
1130 while (tb_poll_event(&fev
) != 0) {
1133 if (fev
.key
== TB_KEY_ESC
) {
1134 tb_set_cursor(-1, -1);
1139 if (fev
.key
== TB_KEY_BACKSPACE
||
1140 fev
.key
== TB_KEY_BACKSPACE2
) {
1141 if (BETWEEN(counter
, 2, sout
)) {
1145 print_xstatus(empty
, startat
+ counter
);
1146 tb_set_cursor(startat
+ counter
,
1150 } else if (fev
.key
== TB_KEY_ENTER
) {
1151 tb_set_cursor(-1, -1);
1152 out
[counter
- 1] = '\0';
1156 if (counter
< sout
) {
1157 print_xstatus((char)fev
.ch
,
1158 (startat
+ counter
));
1159 out
[x
] = (char)fev
.ch
;
1160 tb_set_cursor((startat
+ counter
+ 1),
1183 for (c
= 0; c
< LEN(rules
); c
++)
1184 for (d
= 0; d
< rules
[c
].exlen
; d
++)
1185 if (strncmp(rules
[c
].ext
[d
], ex
, MAX_EXT
) == 0)
1191 spawn(const void *v
, char *fn
)
1193 int ws
, x
, argc
, fd
;
1200 while (((char **)v
)[x
++] != NULL
)
1203 char *argv
[argc
+ 2];
1204 for ( x
= 0; x
< argc
; x
++)
1205 argv
[x
] = ((char **)v
)[x
];
1208 argv
[argc
+ 1] = NULL
;
1215 fd
= open("/dev/null",O_WRONLY
);
1216 dup2(fd
, STDERR_FILENO
);
1217 execvp(argv
[0], argv
);
1221 while ((r
= waitpid(pid
, &ws
, 0)) == -1 && errno
== EINTR
)
1225 if ((WIFEXITED(ws
) != 0) && (WEXITSTATUS(ws
) != 0))
1241 if (c
< 0) /* extension not found open in editor */
1242 return spawn(editor
, fn
);
1244 return spawn((char **)rules
[c
].v
, fn
);
1250 #if defined _SYS_INOTIFY_H
1251 inotify_fd
= inotify_init1(IN_NONBLOCK
| IN_CLOEXEC
);
1254 #elif defined _SYS_EVENT_H_
1265 #if defined _SYS_INOTIFY_H
1266 return cpane
->inotify_wd
= inotify_add_watch(inotify_fd
, cpane
->dirn
,
1267 IN_MODIFY
| IN_MOVED_FROM
| IN_MOVED_TO
| IN_CREATE
|
1268 IN_DELETE
| IN_DELETE_SELF
| IN_MOVE_SELF
);
1269 #elif defined _SYS_EVENT_H_
1270 cpane
->event_fd
= open(cpane
->dirn
, O_RDONLY
);
1271 if (cpane
->event_fd
< 0)
1272 return cpane
->event_fd
;
1273 EV_SET(&evlist
[cpane
->pane_id
], cpane
->event_fd
,
1274 EVFILT_VNODE
, EV_ADD
| EV_CLEAR
,
1275 NOTE_DELETE
| NOTE_EXTEND
| NOTE_LINK
|
1276 NOTE_RENAME
| NOTE_REVOKE
| NOTE_WRITE
, 0, NULL
);
1284 #if defined _SYS_INOTIFY_H
1287 struct inotify_event
*event
;
1288 const size_t events
= 32;
1289 const size_t evbuflen
=
1290 events
* (sizeof(struct inotify_event
) + MAX_N
+ 1);
1293 if (cpane
->inotify_wd
< 0)
1295 r
= read(inotify_fd
, buf
, evbuflen
);
1299 for (p
= buf
; p
< buf
+ r
;) {
1300 event
= (struct inotify_event
*)p
;
1307 p
+= sizeof(struct inotify_event
) + event
->len
;
1309 #elif defined _SYS_EVENT_H_
1310 return kevent(kq
, evlist
, 2, chlist
, 2, >imeout
);
1318 #if defined _SYS_INOTIFY_H
1319 if (pane
->inotify_wd
>= 0)
1320 inotify_rm_watch(inotify_fd
, pane
->inotify_wd
);
1321 #elif defined _SYS_EVENT_H_
1322 close(pane
->event_fd
);
1332 #if defined _SYS_INOTIFY_H
1334 #elif defined _SYS_EVENT_H_
1340 findbm(uint32_t event
)
1344 for (i
= 0; i
< (ssize_t
)LEN(bmarks
); i
++) {
1345 if (event
== bmarks
[i
].ch
) {
1346 if (check_dir(bmarks
[i
].path
) != 0) {
1347 print_error(strerror(errno
));
1359 if (cpane
->dirc
< 1)
1362 user_input
= ecalloc(MAX_USRI
, sizeof(char));
1363 if (get_usrinput(user_input
, MAX_USRI
, "filter") < 0) {
1367 if (listdir(AddHi
, user_input
) < 0)
1368 print_error("no match");
1375 struct tb_event fev
;
1376 if (cpane
->selection
!= NULL
) {
1377 free(cpane
->selection
);
1378 cpane
->selection
= NULL
;
1380 cpane
->selection
= ecalloc(cpane
->dirc
, sizeof(size_t));
1381 cpane
->selection
[0] = cpane
->hdir
;
1382 add_hi(cpane
, cpane
->selection
[0] - 1);
1384 while (tb_poll_event(&fev
) != 0) {
1387 grabkeys(&fev
, skeys
, skeyslen
);
1400 print_prompt("VISUAL");
1401 int index
= abs(cpane
->hdir
- cpane
->selection
[0]);
1403 if (cpane
->hdir
> cpane
->selection
[0]) {
1404 cpane
->selection
[index
] = cpane
->hdir
;
1405 add_hi(cpane
, cpane
->selection
[index
] - 2);
1407 cpane
->selection
[index
+ 1] = 0;
1409 if (cpane
->dirc
>= scrheight
|| cpane
->hdir
>= cpane
->dirc
) { /* rehighlight all if scrolling */
1418 print_prompt("VISUAL");
1419 int index
= abs(cpane
->hdir
- cpane
->selection
[0]);
1421 if (cpane
->hdir
< cpane
->selection
[0]) {
1422 cpane
->selection
[index
] = cpane
->hdir
;
1423 add_hi(cpane
, cpane
->selection
[index
]);
1424 } else if (index
< cpane
->dirc
) {
1425 cpane
->selection
[index
+ 1] = 0;
1427 if (cpane
->dirc
>= scrheight
|| cpane
->hdir
<= 1) { /* rehighlight all if scrolling */
1436 for (i
= 0; i
< cpane
->dirc
; i
++) {
1437 if (cpane
->selection
[i
] < (scrheight
+ cpane
->firstrow
) && cpane
->selection
[i
] > cpane
->firstrow
) { /* checks if in the frame of the directories */
1438 add_hi(cpane
, cpane
->selection
[i
] - 1);
1447 add_hi(cpane
, cpane
->hdir
- 1);
1448 print_prompt("Cancel");
1456 for (i
= 0; i
< cpane
->dirc
; i
++) {
1457 cpane
->selection
[i
] = i
+ 1;
1468 for (j
= 0; j
< cpane
->dirc
; j
++) { /* calculate used selection size */
1469 if (cpane
->selection
[j
] != 0)
1479 for (size_t i
= 0; i
< selection_size
; i
++) {
1494 files
= ecalloc(selection_size
, sizeof(char*));
1496 for (size_t i
= 0; i
< selection_size
; i
++) {
1497 files
[i
] = ecalloc(MAX_P
, sizeof(char));
1498 char *tmp
= get_path_hdir(cpane
->selection
[i
]);
1499 strcpy(files
[i
], tmp
);
1508 if (listdir(AddHi
, NULL
) < 0)
1509 print_error(strerror(errno
));
1510 print_status(cprompt
, "%d files are yanked", selection_size
);
1520 confirmation
= ecalloc((size_t)2, sizeof(char));
1522 confirmation
, (size_t)2,"delete directory (Y) ?") < 0) ||
1523 (strcmp(confirmation
, "Y") != 0)) {
1525 if (listdir(AddHi
, NULL
) < 0)
1526 print_error(strerror(errno
));
1533 for (i
= 0; i
< selection_size
; i
++) {
1534 if (delent(files
[i
]) < 0)
1535 print_error(strerror(errno
));
1538 print_status(cprompt
, "%d files are deleted", selection_size
);
1546 if (strlen(yank_file
) != 0) {
1547 char *cp_cmd
[] = { "cp", "-r", yank_file
, NULL
};
1548 print_status(cprompt
, "coping");
1549 if (spawn(cp_cmd
, cpane
->dirn
) != 0)
1550 print_error("coping failed");
1552 print_status(cprompt
, "files are copied");
1553 yank_file
[0] = '\0'; /* set yank_file len 0 */
1557 print_error("nothing to paste");
1559 // if (files == NULL) {
1565 // for (i = 0; i < selection_size; i++) {
1566 // char *cp_cmd[] = { "cp", "-rf", files[i], cpane->dirn, NULL };
1567 // spawn(cp_cmd, NULL);
1571 // print_status(cprompt, "%d files are copied", selection_size);
1578 if (strlen(yank_file
) != 0) {
1579 char *mv_cmd
[] = { "mv", yank_file
, NULL
};
1580 print_status(cprompt
, "moving");
1581 if (spawn(mv_cmd
, cpane
->dirn
) != 0)
1582 print_error("moving failed");
1584 print_status(cprompt
, "file moved");
1585 yank_file
[0] = '\0'; /* set yank_file len 0 */
1589 print_error("nothing to move");
1593 // if (files == NULL) {
1599 // for (i = 0; i < selection_size; i++) {
1600 // char *mv_cmd[] = { "mv", files[i], cpane->dirn, NULL };
1601 // spawn(mv_cmd, NULL);
1605 // print_status(cprompt, "%d files are copied", selection_size);
1610 get_path_hdir(int Ndir
)
1613 fullpath
= ecalloc(MAX_P
, sizeof(char));
1614 strcpy(fullpath
, cpane
->dirn
);
1615 strcat(fullpath
, "/");
1616 strcat(fullpath
, cpane
->direntr
[Ndir
-1].name
);
1624 char new_name
[MAX_P
];
1627 input_name
= ecalloc(MAX_N
, sizeof(char));
1629 if (get_usrinput(input_name
, MAX_N
, "rename: %s", basename(CURSOR_NAME
)) < 0) {
1634 strcpy(new_name
, cpane
->dirn
);
1635 strcat(new_name
, "/");
1636 strcat(new_name
, input_name
);
1638 char *rename_cmd
[] = { "mv", CURSOR_NAME
, new_name
, NULL
};
1639 if (spawn(rename_cmd
, NULL
) < 0)
1640 print_error(strerror(errno
));
1648 strncpy(yank_file
, CURSOR_NAME
, MAX_P
);
1649 print_status(cprompt
, "1 file is yanked", selection_size
);
1656 if (cpane
->dirc
> 0)
1657 rm_hi(cpane
, cpane
->hdir
- 1);
1658 if (cpane
== &pane_l
)
1660 else if (cpane
== &pane_r
)
1662 if (cpane
->dirc
> 0) {
1663 add_hi(cpane
, cpane
->hdir
- 1);
1673 if (sl
== -1) { /* check if selection was allocated */
1674 free(cpane
->selection
);
1678 free(pane_l
.direntr
);
1679 free(pane_r
.direntr
);
1686 grabkeys(struct tb_event
*event
, Key
*key
, size_t max_keys
)
1691 for (i
= 0; i
< max_keys
; i
++) {
1692 if (event
->ch
!= 0) {
1693 if (event
->ch
== key
[i
].evkey
.ch
) {
1697 } else if (event
->key
!= 0) {
1698 if (event
->key
== key
[i
].evkey
.key
) {
1706 b
= findbm(event
->ch
);
1710 strcpy(cpane
->dirn
, bmarks
[b
].path
);
1711 cpane
->firstrow
= 0;
1712 cpane
->parent_row
= 1;
1714 if (listdir(AddHi
, NULL
) < 0)
1715 print_error(strerror(errno
));
1724 int t
= tb_peek_event(&ev
, 2000);
1730 if (t
== 1) /* keyboard event */
1731 grabkeys(&ev
, nkeys
, nkeyslen
);
1732 else if (t
== 2) /* resize event */
1734 else if (t
== 0) /* filesystem event */
1735 if (read_events() > 0)
1736 if (listdir(AddHi
, NULL
) < 0)
1737 print_error(strerror(errno
));
1748 size_t y
, dyn_max
, start_from
;
1750 width
= (twidth
/ 2) - 4;
1754 start_from
= cpane
->firstrow
;
1755 dyn_max
= MIN(cpane
->dirc
, (scrheight
- 1) + cpane
->firstrow
);
1757 /* print each entry in directory */
1758 while (start_from
< dyn_max
) {
1759 get_hicol(&col
, cpane
->direntr
[start_from
].mode
);
1760 print_row(cpane
, start_from
, col
);
1765 if (cpane
->dirc
> 0)
1770 /* print current directory title */
1771 cpane
->dircol
.fg
|= TB_BOLD
;
1772 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1776 set_direntr(struct dirent
*entry
, DIR *dir
)
1782 cpane
->direntr
= erealloc(cpane
->direntr
, cpane
->dirc
* sizeof(Entry
));
1783 while ((entry
= readdir(dir
)) != 0) {
1784 if ((strcmp(entry
->d_name
, ".") == 0 ||
1785 strcmp(entry
->d_name
, "..") == 0))
1788 char *full
= get_fullpath(cpane
->dirn
, entry
->d_name
);
1789 strcpy(cpane
->direntr
[i
].name
, full
);
1790 if (lstat(full
, &status
) == 0) {
1791 cpane
->direntr
[i
].size
= status
.st_size
;
1792 cpane
->direntr
[i
].mode
= status
.st_mode
;
1793 cpane
->direntr
[i
].group
= status
.st_gid
;
1794 cpane
->direntr
[i
].user
= status
.st_uid
;
1795 cpane
->direntr
[i
].td
= status
.st_mtime
;
1805 listdir(int hi
, char *filter
)
1808 struct dirent
*entry
;
1811 int filtercount
= 0;
1812 size_t oldc
= cpane
->dirc
;
1814 width
= (twidth
/ 2) - 4;
1818 dir
= opendir(cpane
->dirn
);
1822 /* get content and filter sum */
1823 while ((entry
= readdir(dir
)) != 0) {
1824 if (filter
!= NULL
) {
1825 if (strstr(entry
->d_name
, filter
) != NULL
)
1827 } else { /* no filter */
1832 if (filter
== NULL
) {
1837 if (filter
!= NULL
) {
1838 if (filtercount
> 0) {
1840 cpane
->dirc
= filtercount
;
1843 } else if (filtercount
== 0) {
1844 if (closedir(dir
) < 0)
1851 /* print current directory title */
1852 cpane
->dircol
.fg
|= TB_BOLD
;
1853 printf_tb(cpane
->dirx
, 0, cpane
->dircol
, " %.*s ", width
, cpane
->dirn
);
1856 print_error("can't add watch");
1858 /* empty directory */
1859 if (cpane
->dirc
== 0) {
1861 if (closedir(dir
) < 0)
1866 rewinddir(dir
); /* reset position */
1867 set_direntr(entry
, dir
); /* create array of entries */
1868 qsort(cpane
->direntr
, cpane
->dirc
, sizeof(Entry
), sort_name
);
1872 add_hi(cpane
, cpane
->hdir
- 1);
1874 if (closedir(dir
) < 0)
1882 /* TODO need refactoring */
1885 pane_r
.dirx
= (twidth
/ 2) + 2;
1887 if (cpane
== &pane_l
) {
1892 if (cpane
->dirc
> 0)
1893 add_hi(&pane_l
, pane_l
.hdir
- 1);
1894 } else if (cpane
== &pane_r
) {
1899 if (cpane
->dirc
> 0)
1900 add_hi(&pane_r
, pane_r
.hdir
- 1);
1909 editor
[0] = getenv("EDITOR");
1912 if (editor
[0] == NULL
)
1922 home
= getenv("HOME");
1925 if ((getcwd(cwd
, sizeof(cwd
)) == NULL
))
1926 strncpy(cwd
, home
, MAX_P
);
1930 pane_l
.dircol
= cpanell
;
1931 pane_l
.firstrow
= 0;
1932 pane_l
.direntr
= ecalloc(0, sizeof(Entry
));
1933 strcpy(pane_l
.dirn
, cwd
);
1935 pane_l
.inotify_wd
= -1;
1936 pane_l
.parent_row
= 1;
1939 pane_r
.dirx
= (twidth
/ 2) + 2;
1940 pane_r
.dircol
= cpanelr
;
1941 pane_r
.firstrow
= 0;
1942 pane_r
.direntr
= ecalloc(0, sizeof(Entry
));
1943 strcpy(pane_r
.dirn
, home
);
1945 pane_r
.inotify_wd
= -1;
1946 pane_r
.parent_row
= 1;
1953 theight
= tb_height();
1954 twidth
= tb_width();
1955 scrheight
= theight
- 2;
1957 /* 2 horizontal lines */
1958 for (i
= 1; i
< twidth
- 1; ++i
) {
1959 tb_change_cell(i
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
1960 tb_change_cell(i
, theight
- 2, u_hl
, cframe
.fg
, cframe
.bg
);
1963 /* 4 vertical lines */
1964 for (i
= 1; i
< theight
- 1; ++i
) {
1965 tb_change_cell(0, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1966 tb_change_cell((twidth
- 1) / 2, i
- 1, u_vl
, cframe
.fg
,
1968 tb_change_cell(((twidth
- 1) / 2) + 1, i
- 1, u_vl
, cframe
.fg
,
1970 tb_change_cell(twidth
- 1, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1974 tb_change_cell(0, 0, u_cnw
, cframe
.fg
, cframe
.bg
);
1975 tb_change_cell(twidth
- 1, 0, u_cne
, cframe
.fg
, cframe
.bg
);
1976 tb_change_cell(0, theight
- 2, u_csw
, cframe
.fg
, cframe
.bg
);
1977 tb_change_cell(twidth
- 1, theight
- 2, u_cse
, cframe
.fg
, cframe
.bg
);
1979 /* 2 middel top and bottom */
1980 tb_change_cell((twidth
- 1) / 2, 0, u_mn
, cframe
.fg
, cframe
.bg
);
1981 tb_change_cell((twidth
- 1) / 2, theight
- 2, u_ms
, cframe
.fg
, cframe
.bg
);
1989 if (tb_select_output_mode(TB_OUTPUT_256
) != TB_OUTPUT_256
)
1990 if (tb_select_output_mode(TB_OUTPUT_NORMAL
) != TB_OUTPUT_NORMAL
)
1991 die("output error");
1996 if (fsev_init() < 0)
1997 print_error(strerror(errno
));
1999 if (listdir(NoHi
, NULL
) < 0)
2000 print_error(strerror(errno
));
2002 if (listdir(AddHi
, NULL
) < 0)
2003 print_error(strerror(errno
));
2009 main(int argc
, char *argv
[])
2012 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
2015 #endif /* __OpenBSD__ */
2018 else if (argc
== 2 && strncmp("-v", argv
[1], 2) == 0)
2019 die("sfm-" VERSION
);
2021 die("usage: sfm [-v]");