1 /* See LICENSE file for copyright and license details. */
13 #include <sys/resource.h>
16 #include <sys/types.h>
22 #include <sys/inotify.h>
24 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
27 #include <sys/event.h>
28 #endif /* filesystem events */
38 #define EV_BUF_LEN (EVENTS * (sizeof(struct inotify_event) + MAX_N + 1))
41 enum { AskDel
, DAskDel
}; /* delete directory */
42 enum { AddHi
, NoHi
}; /* add highlight in listdir */
43 enum { AllP
, PtP
}; /* set pane items */
61 char dirn
[MAX_P
]; // dir name cwd
62 Entry
*direntr
; // dir entries
63 int dirx
; // pane cwd x pos
64 size_t dirc
; // dir entries sum
65 size_t hdir
; // highlighted dir
82 uint16_t key
; /* one of the TB_KEY_* constants */
83 uint32_t ch
; /* unicode character */
91 /* function declarations */
92 static void print_tb(const char*, int, int, uint16_t, uint16_t);
93 static void printf_tb(int, int, Cpair
, const char*, ...);
94 static void print_status(Cpair
, const char*, ...);
95 static void print_xstatus(char, int);
96 static void print_error(char*);
97 static void print_prompt(char*);
98 static void print_info(void);
99 static void print_row(Pane
*, size_t, Cpair
);
100 static void clear(int, int, int, uint16_t);
101 static void clear_status(void);
102 static void clear_pane(int);
103 static void add_hi(Pane
*, size_t);
104 static void rm_hi(Pane
*, size_t);
105 static void float_to_string(float, char*);
106 static int check_dir(char*);
107 static mode_t
chech_execf(mode_t
);
108 static int sort_name(const void *const, const void *const);
109 static char *get_ext(char*);
110 static int get_fdt(char*, size_t, time_t);
111 static char *get_fgrp(gid_t
, size_t);
112 static char *get_finfo(Entry
*);
113 static char *get_fperm(mode_t
);
114 static char *get_fsize(off_t
);
115 static char *get_fullpath(char*, char*);
116 static char *get_fusr(uid_t
, size_t);
117 static void get_dirsize(char*, off_t
*);
118 static void get_hicol(Cpair
*, mode_t
);
119 static int deldir(char*, int);
120 static int delent(char *);
121 static int delf(char*);
122 static void calcdir(void);
123 static void crnd(void);
124 static void crnf(void);
125 static void delfd(void);
126 static void mvbk(void);
127 static void mvbtm(void);
128 static void mvdwn(void);
129 static void mvdwns(void);
130 static void mvfor(void);
131 static void mvmid(void);
132 static void mvtop(void);
133 static void mvup(void);
134 static void mvups(void);
135 static void scrdwn(void);
136 static void scrdwns(void);
137 static void scrup(void);
138 static void scrups(void);
139 static int get_usrinput(char*, size_t, char*);
140 static char *frules(char *);
141 static char *getsw(char*);
142 static int opnf(char*);
143 static ssize_t
findbm(uint32_t);
144 static void filter(void);
145 static void switch_pane(void);
146 static void quit(void);
147 static void grabkeys(struct tb_event
*);
148 static void start_ev(void);
149 static void refresh_pane(void);
150 static int listdir(int, char*);
151 static void t_resize(void);
152 static void set_panes(int);
153 static void draw_frame(void);
154 static void start(void);
156 /* global variables */
157 static Pane pane_r
, pane_l
, *cpane
;
158 static int parent_row
= 1; // FIX
159 static size_t scrheight
;
160 static char fed
[] = "vi";
162 static const uint32_t INOTIFY_MASK
= IN_CREATE
| IN_DELETE
| IN_DELETE_SELF \
163 | IN_MODIFY
| IN_MOVE_SELF
| IN_MOVED_FROM
| IN_MOVED_TO
;
165 /* configuration, allows nested code to access above variables */
168 /* function implementations */
170 print_tb(const char *str
, int x
, int y
, uint16_t fg
, uint16_t bg
)
172 while (*str
!= '\0') {
174 str
+= tb_utf8_char_to_unicode(&uni
, str
);
175 tb_change_cell(x
, y
, uni
, fg
, bg
);
181 printf_tb(int x
, int y
, Cpair col
, const char *fmt
, ...)
186 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
188 print_tb(buf
, x
, y
, col
.fg
, col
.bg
);
192 print_status(Cpair col
, const char *fmt
, ...)
195 height
= tb_height();
200 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vl
);
203 print_tb(buf
, 1, height
-1, col
.fg
, col
.bg
);
208 print_xstatus(char c
, int x
)
212 height
= tb_height();
213 (void)tb_utf8_char_to_unicode(&uni
, &c
);
214 tb_change_cell(x
, height
-1, uni
, cstatus
.fg
, cstatus
.bg
);
218 print_error(char *errmsg
)
220 print_status(cerr
, errmsg
);
224 print_prompt(char *prompt
)
226 print_status(cprompt
, prompt
);
233 fileinfo
= get_finfo(&cpane
->direntr
[cpane
->hdir
-1]);
234 print_status(cstatus
, "%lu/%lu %s",
235 cpane
->hdir
, cpane
->dirc
, fileinfo
);
240 print_row(Pane
*pane
, size_t entpos
, Cpair col
)
245 char lnk_full
[MAX_P
];
248 width
= (tb_width() / 2) - 4;
249 result
= pane
->direntr
[entpos
].name
;
251 y
= entpos
- cpane
->firstrow
+ 1;
253 if (S_ISLNK(pane
->direntr
[entpos
].mode
) &&
254 realpath(pane
->direntr
[entpos
].name
, buf
) != NULL
) {
255 strncpy(lnk_full
, pane
->direntr
[entpos
].name
, MAX_N
);
256 strcat(lnk_full
, " -> ");
257 strncat(lnk_full
, buf
, MAX_N
);
261 printf_tb(x
, y
, col
, "%*.*s", ~width
, width
, result
);
265 clear(int sx
, int ex
, int y
, uint16_t bg
)
267 /* clear line from to */
268 /* x = line number vertical */
269 /* y = column number horizontal */
271 for (i
= sx
; i
< ex
; i
++) {
272 tb_change_cell(i
, y
, 0x0000, TB_DEFAULT
, bg
);
282 height
= tb_height();
283 clear(1, width
-1, height
-1, cstatus
.bg
);
289 int i
, x
, ex
, y
, width
, height
;
291 height
= tb_height();
292 x
= 0, y
= 0, i
= 0, ex
= 0;
297 } else if (pane
== (width
/2) + 2) {
302 while (i
< height
-2) {
303 clear(x
, ex
, y
, TB_DEFAULT
);
308 for (y
= x
; y
< ex
; ++y
) {
309 tb_change_cell(y
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
315 add_hi(Pane
*pane
, size_t entpos
)
318 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
319 col
.fg
|= TB_REVERSE
|TB_BOLD
;
320 col
.bg
|= TB_REVERSE
;
321 print_row(pane
, entpos
, col
);
325 rm_hi(Pane
*pane
, size_t entpos
)
328 get_hicol(&col
, pane
->direntr
[entpos
].mode
);
329 print_row(pane
, entpos
, col
);
333 float_to_string(float f
, char *r
)
335 int length
, length2
, i
, number
, position
, tenth
; /* length is size of decimal part, length2 is size of tenth part */
338 f
= (float)(int)(f
* 10) / 10;
345 /* Calculate length2 tenth part */
346 while ((number2
- (float)number
) != 0.0 && !((number2
- (float)number
) < 0.0))
349 number2
= f
* (float)tenth
;
350 number
= (int)number2
;
355 /* Calculate length decimal part */
356 for (length
= (f
> 1.0) ? 0 : 1; f
> 1.0; length
++)
360 length
= length
+ 1 + length2
;
361 number
= (int)number2
;
365 for (i
= length
; i
>= 0; i
--)
369 else if (i
== (position
))
373 r
[i
] = (char)(number
% 10) + '0';
381 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
)
573 /* need to be freed */
579 Rsize
= ecalloc((size_t)10, sizeof(char));
582 while (lsize
>= 1000.0)
588 float_to_string(lsize
, Rsize
);
613 get_fullpath(char *first
, char *second
)
616 size_t full_path_len
;
618 full_path_len
= strlen(first
) + strlen(second
) + 2;
619 full_path
= ecalloc(full_path_len
, sizeof(char));
621 if (strcmp(first
, "/") == 0) {
622 (void)snprintf(full_path
, full_path_len
, "/%s", second
);
626 full_path
, full_path_len
, "%s/%s", first
, second
);
633 get_fusr(uid_t status
, size_t len
)
638 result
= ecalloc(len
, sizeof(char));
639 pw
= getpwuid(status
);
641 (void)snprintf(result
, len
-1, "%d", (int)status
);
643 strncpy(result
, pw
->pw_name
, len
-1);
649 get_dirsize(char *fullpath
, off_t
*fullsize
)
654 struct dirent
*entry
;
657 dir
= opendir(fullpath
);
663 while ((entry
= readdir(dir
)) != 0)
665 if ((strcmp(entry
->d_name
, ".") == 0 ||
666 strcmp(entry
->d_name
, "..") == 0))
669 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
670 if (lstat(ent_full
, &status
) == 0) {
671 mode
= status
.st_mode
;
673 get_dirsize(ent_full
, fullsize
);
677 *fullsize
+= status
.st_size
;
688 get_hicol(Cpair
*col
, mode_t mode
)
693 else if (S_ISLNK(mode
))
695 else if (chech_execf(mode
) > 0)
700 deldir(char *fullpath
, int delchoice
)
702 if (delchoice
== (int)AskDel
) {
704 confirmation
= ecalloc((size_t)2, sizeof(char));
706 confirmation
, (size_t)2,"delete directory (Y) ?") < 0) ||
707 (strcmp(confirmation
, "Y") != 0)) {
709 return 1; /* canceled by user or wrong confirmation */
714 if (rmdir(fullpath
) == 0)
715 return 0; /* empty directory */
720 struct dirent
*entry
;
723 dir
= opendir(fullpath
);
728 while ((entry
= readdir(dir
)) != 0) {
729 if ((strcmp(entry
->d_name
, ".") == 0 ||
730 strcmp(entry
->d_name
, "..") == 0))
733 ent_full
= get_fullpath(fullpath
, entry
->d_name
);
734 if (lstat(ent_full
, &status
) == 0) {
735 mode
= status
.st_mode
;
737 if (deldir(ent_full
, (int)DAskDel
) < 0) {
741 } else if (S_ISREG(mode
)) {
742 if (unlink(ent_full
) < 0) {
751 print_status(cstatus
, "gotit");
752 if (closedir(dir
) < 0)
755 return rmdir(fullpath
); /* directory after delete all entries */
759 delent(char *fullpath
)
764 if (lstat(fullpath
, &status
) < 0)
767 mode
= status
.st_mode
;
769 return deldir(fullpath
, (int)AskDel
);
771 return delf(fullpath
);
780 confirmation
= ecalloc((size_t)2, sizeof(char));
782 if ((get_usrinput(confirmation
, (size_t)2, "delete file (Y) ?") < 0) ||
783 (strcmp(confirmation
, "Y") != 0)) {
785 return 1; /* canceled by user or wrong confirmation */
789 return unlink(fullpath
);
799 if (S_ISDIR(cpane
->direntr
[cpane
->hdir
-1].mode
)) {
800 fullsize
= ecalloc(50, sizeof(off_t
));
801 get_dirsize(cpane
->direntr
[cpane
->hdir
-1].name
, fullsize
);
802 csize
= get_fsize(*fullsize
);
803 result
= get_finfo(&cpane
->direntr
[cpane
->hdir
-1]);
806 print_status(cstatus
, "%lu/%lu %s%s",
807 cpane
->hdir
, cpane
->dirc
, result
, csize
);
817 char *user_input
, *path
;
820 user_input
= ecalloc(MAX_USRI
, sizeof(char));
821 if (get_usrinput(user_input
, MAX_USRI
, "new dir") < 0) {
826 pathlen
= strlen(cpane
->dirn
) + 1 + MAX_USRI
+ 1;
827 path
= ecalloc(pathlen
, sizeof(char));
828 if (snprintf(path
, pathlen
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
834 if (mkdir(path
, ndir_perm
) < 0)
835 print_error(strerror(errno
));
837 if (listdir(AddHi
, NULL
) < 0)
838 print_error(strerror(errno
));
847 char *user_input
, *path
;
851 user_input
= ecalloc(MAX_USRI
, sizeof(char));
852 if (get_usrinput(user_input
, MAX_USRI
, "new file") < 0) {
857 pathlen
= strlen(cpane
->dirn
) + 1 + MAX_USRI
+ 1;
858 path
= ecalloc(pathlen
, sizeof(char));
859 if (snprintf(path
, pathlen
, "%s/%s", cpane
->dirn
, user_input
) < 0) {
865 rf
= open(path
, O_CREAT
|O_EXCL
, nf_perm
);
868 print_error(strerror(errno
));
871 print_error(strerror(errno
));
873 if (listdir(AddHi
, NULL
) < 0)
874 print_error(strerror(errno
));
884 switch (delent(cpane
->direntr
[cpane
->hdir
-1].name
)) {
886 print_error(strerror(errno
));
889 if (BETWEEN(cpane
->hdir
-1, 1, cpane
->dirc
)) /* last entry */
891 if (listdir(AddHi
, NULL
) < 0)
892 print_error(strerror(errno
));
901 getcwd(cpane
->dirn
, MAX_P
);
903 cpane
->hdir
= parent_row
;
904 if (listdir(AddHi
, NULL
) < 0)
905 print_error(strerror(errno
));
914 if (cpane
->dirc
> scrheight
) {
915 clear_pane(cpane
->dirx
);
916 rm_hi(cpane
, cpane
->hdir
-1);
917 cpane
->hdir
= cpane
->dirc
;
918 cpane
->firstrow
= cpane
->dirc
- scrheight
+ 1;
920 add_hi(cpane
, cpane
->hdir
-1);
922 rm_hi(cpane
, cpane
->hdir
-1);
923 cpane
->hdir
= cpane
->dirc
;
924 add_hi(cpane
, cpane
->hdir
-1);
934 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
935 rm_hi(cpane
, cpane
->hdir
-1);
937 add_hi(cpane
, cpane
->hdir
-1);
939 mvdwns(); /* scroll */
948 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
950 if (real
> scrheight
- 3 - scrsp
&& cpane
->hdir
+ scrsp
< cpane
->dirc
) {
952 clear_pane(cpane
->dirx
);
953 rm_hi(cpane
, cpane
->hdir
-1);
956 add_hi(cpane
, cpane
->hdir
-1);
957 } else if (cpane
->hdir
< cpane
->dirc
) {
958 rm_hi(cpane
, cpane
->hdir
-1);
960 add_hi(cpane
, cpane
->hdir
-1);
971 switch (check_dir(cpane
->direntr
[cpane
->hdir
-1].name
)) {
973 chdir(cpane
->direntr
[cpane
->hdir
-1].name
);
974 getcwd(cpane
->dirn
, MAX_P
);
975 parent_row
= (int)cpane
->hdir
;
978 if (listdir(AddHi
, NULL
) < 0)
979 print_error(strerror(errno
));
981 case 1: /* not a directory open file */
983 s
= opnf(cpane
->direntr
[cpane
->hdir
-1].name
);
988 print_error("process failed non-zero exit");
990 case -1: /* failed to open directory */
991 print_error(strerror(errno
));
1000 rm_hi(cpane
, cpane
->hdir
- 1);
1001 cpane
->hdir
= (scrheight
/ 2) + cpane
->firstrow
;
1002 add_hi(cpane
, cpane
->hdir
- 1);
1009 if (cpane
->dirc
< 1)
1011 if (cpane
->dirc
> scrheight
) {
1012 clear_pane(cpane
->dirx
);
1013 rm_hi(cpane
, cpane
->hdir
-1);
1015 cpane
->firstrow
= 0;
1017 add_hi(cpane
, cpane
->hdir
-1);
1019 rm_hi(cpane
, cpane
->hdir
-1);
1021 add_hi(cpane
, cpane
->hdir
-1);
1029 if (cpane
->dirc
< 1)
1031 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
1032 rm_hi(cpane
, cpane
->hdir
-1);
1034 add_hi(cpane
, cpane
->hdir
-1);
1036 mvups(); /* scroll */
1045 real
= cpane
->hdir
- 1 - cpane
->firstrow
;
1047 if (cpane
->firstrow
> 0 && real
< 1 + scrsp
) {
1049 clear_pane(cpane
->dirx
);
1050 rm_hi(cpane
, cpane
->hdir
-1);
1053 add_hi(cpane
, cpane
->hdir
-1);
1054 } else if (cpane
->hdir
> 1) {
1055 rm_hi(cpane
, cpane
->hdir
-1);
1057 add_hi(cpane
, cpane
->hdir
-1);
1064 if (cpane
->dirc
< 1)
1066 if (cpane
->dirc
< scrheight
&& cpane
->hdir
< cpane
->dirc
) {
1067 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
1068 rm_hi(cpane
, cpane
->hdir
-1);
1069 cpane
->hdir
+= scrmv
;
1070 add_hi(cpane
, cpane
->hdir
-1);
1085 real
= cpane
->hdir
- cpane
->firstrow
;
1086 dynmv
= MIN(cpane
->dirc
- cpane
->hdir
- cpane
->firstrow
, scrmv
);
1088 if (real
+ scrmv
+ 1 > scrheight
&&
1089 cpane
->hdir
+ scrsp
+ scrmv
< cpane
->dirc
) { /* scroll */
1090 cpane
->firstrow
+= dynmv
;
1091 clear_pane(cpane
->dirx
);
1092 rm_hi(cpane
, cpane
->hdir
-1);
1093 cpane
->hdir
+= scrmv
;
1095 add_hi(cpane
, cpane
->hdir
-1);
1097 if (cpane
->hdir
< cpane
->dirc
- scrmv
) {
1098 rm_hi(cpane
, cpane
->hdir
-1);
1099 cpane
->hdir
+= scrmv
;
1100 add_hi(cpane
, cpane
->hdir
-1);
1110 if (cpane
->dirc
< 1)
1112 if (cpane
->dirc
< scrheight
&& cpane
->hdir
> 1) {
1113 if (cpane
->hdir
> scrmv
) {
1114 rm_hi(cpane
, cpane
->hdir
-1);
1115 cpane
->hdir
= cpane
->hdir
- scrmv
;
1116 add_hi(cpane
, cpane
->hdir
-1);
1131 real
= cpane
->hdir
- cpane
->firstrow
;
1132 dynmv
= MIN(cpane
->firstrow
, scrmv
);
1134 if (cpane
->firstrow
> 0 && real
< scrmv
+ scrsp
) {
1135 cpane
->firstrow
-= dynmv
;
1136 clear_pane(cpane
->dirx
);
1137 rm_hi(cpane
, cpane
->hdir
-1);
1138 cpane
->hdir
-= scrmv
;
1140 add_hi(cpane
, cpane
->hdir
-1);
1142 if (cpane
->hdir
> scrmv
+ 1) {
1143 rm_hi(cpane
, cpane
->hdir
-1);
1144 cpane
->hdir
-= scrmv
;
1145 add_hi(cpane
, cpane
->hdir
-1);
1153 get_usrinput(char *out
, size_t sout
, char *prompt
)
1155 int height
= tb_height();
1157 struct tb_event fev
;
1158 size_t counter
= (size_t)1;
1163 startat
= strlen(prompt
) + 1;
1164 print_prompt(prompt
);
1165 tb_set_cursor((int)(startat
+ 1), height
-1);
1168 while (tb_poll_event(&fev
) != 0) {
1171 if (fev
.key
== (uint16_t)TB_KEY_ESC
) {
1172 tb_set_cursor(-1, -1);
1177 if (fev
.key
== (uint16_t)TB_KEY_BACKSPACE
||
1178 fev
.key
== (uint16_t)TB_KEY_BACKSPACE2
) {
1179 if (BETWEEN(counter
, (size_t)2, sout
)) {
1183 print_xstatus(empty
, startat
+ counter
);
1185 (int)startat
+ counter
, height
- 1);
1188 } else if (fev
.key
== (uint16_t)TB_KEY_ENTER
) {
1189 tb_set_cursor(-1, -1);
1190 out
[counter
-1] = '\0';
1194 if (counter
< sout
) {
1195 print_xstatus((char)fev
.ch
, (int)(startat
+counter
));
1196 out
[x
] = (char)fev
.ch
;
1197 tb_set_cursor((int)(startat
+ counter
+ 1),height
-1);
1220 for (c
= 0; c
< LEN(rules
); c
++)
1221 for (d
= 0; d
< rules
[c
].len
; d
++)
1222 if (strcmp(rules
[c
].ext
[d
], ex
) == 0)
1223 return rules
[c
].soft
;
1232 ed
= getenv("EDITOR");
1251 char *filex
[] = {sw
, fn
, NULL
};
1258 execvp(filex
[0], filex
);
1261 while ((r
= waitpid(pid
, &ws
, 0)) == -1 && errno
== EINTR
)
1265 if ((WIFEXITED(ws
) != 0) && (WEXITSTATUS(ws
) != 0))
1272 findbm(uint32_t event
)
1276 for (i
= 0; i
< (ssize_t
)LEN(bmarks
); i
++) {
1277 if (event
== bmarks
[i
].ch
) {
1278 if (check_dir(bmarks
[i
].path
) != 0) {
1279 print_error(strerror(errno
));
1291 if (cpane
->dirc
< 1)
1294 user_input
= ecalloc(MAX_USRI
, sizeof(char));
1295 if (get_usrinput(user_input
, MAX_USRI
, "filter") < 0) {
1299 if (listdir(AddHi
, user_input
) < 0)
1300 print_error("no match");
1307 if (cpane
->dirc
> 0)
1308 rm_hi(cpane
, cpane
->hdir
-1);
1309 if (cpane
== &pane_l
)
1311 else if (cpane
== &pane_r
)
1314 if (cpane
->dirc
> 0) {
1315 add_hi(cpane
, cpane
->hdir
-1);
1325 free(pane_l
.direntr
);
1326 free(pane_r
.direntr
);
1332 grabkeys(struct tb_event
*event
)
1337 for (i
= 0; i
< LEN(keys
); i
++) {
1338 if (event
->ch
!= 0) {
1339 if (event
->ch
== keys
[i
].evkey
.ch
) {
1343 } else if (event
->key
!= 0) {
1344 if (event
->key
== keys
[i
].evkey
.key
) {
1352 b
= findbm(event
->ch
);
1355 strcpy(cpane
->dirn
, bmarks
[b
].path
);
1356 cpane
->firstrow
= 0;
1358 if (listdir(AddHi
, NULL
) < 0)
1359 print_error(strerror(errno
));
1367 while (tb_poll_event(&ev
) != 0) {
1373 case TB_EVENT_RESIZE
:
1386 size_t y
, dyn_max
, start_from
;
1388 width
= (tb_width() / 2) - 4;
1392 start_from
= cpane
->firstrow
;
1393 dyn_max
= MIN(cpane
->dirc
, (scrheight
- 1) + cpane
->firstrow
);
1395 /* print each entry in directory */
1396 while (start_from
< dyn_max
) {
1397 get_hicol(&col
, cpane
->direntr
[start_from
].mode
);
1398 print_row(cpane
, start_from
, col
);
1405 /* print current directory title */
1406 cpane
->dircol
.fg
|= TB_BOLD
;
1407 printf_tb(cpane
->dirx
, 0, cpane
->dircol
," %.*s ", width
, cpane
->dirn
);
1411 listdir(int hi
, char *filter
)
1414 struct dirent
*entry
;
1418 int filtercount
= 0;
1419 size_t oldc
= cpane
->dirc
;
1421 width
= (tb_width() / 2) - 4;
1425 if (chdir(cpane
->dirn
) < 0)
1428 dir
= opendir(cpane
->dirn
);
1432 /* get content and filter sum */
1433 while ((entry
= readdir(dir
)) != 0) {
1434 if (filter
!= NULL
) {
1435 if (strstr(entry
->d_name
, filter
) != NULL
)
1437 } else { /* no filter */
1442 if (filter
== NULL
) {
1443 clear_pane(cpane
->dirx
);
1447 if (filter
!= NULL
) {
1448 if (filtercount
> 0) {
1450 cpane
->dirc
= filtercount
;
1451 clear_pane(cpane
->dirx
);
1453 } else if (filtercount
== 0) {
1454 if (closedir(dir
) < 0)
1461 /* print current directory title */
1462 cpane
->dircol
.fg
|= TB_BOLD
;
1463 printf_tb(cpane
->dirx
, 0, cpane
->dircol
," %.*s ", width
, cpane
->dirn
);
1465 /* empty directory */
1466 if (cpane
->dirc
== 0) {
1468 if (closedir(dir
) < 0)
1473 rewinddir(dir
); /* reset position */
1475 /* create array of entries */
1477 cpane
->direntr
= erealloc(cpane
->direntr
, cpane
->dirc
* sizeof(Entry
));
1478 while ((entry
= readdir(dir
)) != 0) {
1479 if ((strcmp(entry
->d_name
, ".") == 0 ||
1480 strcmp(entry
->d_name
, "..") == 0))
1483 /* list found filter */
1484 if (filter
!= NULL
) {
1485 if (strstr(entry
->d_name
, filter
) != NULL
) {
1486 strcpy(cpane
->direntr
[i
].name
, entry
->d_name
);
1487 if (lstat(entry
->d_name
, &status
) == 0) {
1488 cpane
->direntr
[i
].size
= status
.st_size
;
1489 cpane
->direntr
[i
].mode
= status
.st_mode
;
1490 cpane
->direntr
[i
].group
= status
.st_gid
;
1491 cpane
->direntr
[i
].user
= status
.st_uid
;
1492 cpane
->direntr
[i
].td
= status
.st_mtime
;
1498 strcpy(cpane
->direntr
[i
].name
, entry
->d_name
);
1499 if (lstat(entry
->d_name
, &status
) == 0) {
1500 cpane
->direntr
[i
].size
= status
.st_size
;
1501 cpane
->direntr
[i
].mode
= status
.st_mode
;
1502 cpane
->direntr
[i
].group
= status
.st_gid
;
1503 cpane
->direntr
[i
].user
= status
.st_uid
;
1504 cpane
->direntr
[i
].td
= status
.st_mtime
;
1511 qsort(cpane
->direntr
, cpane
->dirc
, sizeof(Entry
), sort_name
);
1515 add_hi(cpane
, cpane
->hdir
-1);
1517 if (closedir(dir
) < 0)
1525 /* TODO need refactoring */
1530 if (cpane
== &pane_l
) {
1537 add_hi(&pane_l
, pane_l
.hdir
-1);
1538 } else if (cpane
== &pane_r
) {
1545 add_hi(&pane_r
, pane_r
.hdir
-1);
1552 set_panes(int paneitem
)
1557 scrheight
= tb_height() - 2;
1559 home
= getenv("HOME");
1561 if ((getcwd(cwd
, sizeof(cwd
)) == NULL
))
1567 pane_l
.dircol
= cpanell
;
1568 pane_l
.firstrow
= 0;
1569 if (paneitem
== AllP
) {
1570 pane_l
.direntr
= ecalloc(0, sizeof(Entry
));
1571 strcpy(pane_l
.dirn
, cwd
);
1575 pane_r
.dirx
= (width
/ 2) + 2;
1576 pane_r
.dircol
= cpanelr
;
1577 pane_r
.firstrow
= 0;
1578 if (paneitem
== AllP
) {
1579 pane_r
.direntr
= ecalloc(0, sizeof(Entry
));
1580 strcpy(pane_r
.dirn
, home
);
1588 int height
, width
, i
;
1591 height
= tb_height();
1593 /* 2 horizontal lines */
1594 for (i
= 1; i
< width
-1 ; ++i
) {
1595 tb_change_cell(i
, 0, u_hl
, cframe
.fg
, cframe
.bg
);
1596 tb_change_cell(i
, height
-2, u_hl
, cframe
.fg
, cframe
.bg
);
1599 /* 3 vertical lines */
1600 for (i
= 1; i
< height
-1 ; ++i
) {
1601 tb_change_cell(0, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1602 tb_change_cell((width
-1)/2, i
-1, u_vl
, cframe
.fg
, cframe
.bg
);
1603 tb_change_cell(width
-1, i
, u_vl
, cframe
.fg
, cframe
.bg
);
1607 tb_change_cell(0, 0, u_cnw
, cframe
.fg
, cframe
.bg
);
1608 tb_change_cell(width
-1, 0, u_cne
, cframe
.fg
, cframe
.bg
);
1609 tb_change_cell(0, height
-2, u_csw
, cframe
.fg
, cframe
.bg
);
1610 tb_change_cell(width
-1, height
-2, u_cse
, cframe
.fg
, cframe
.bg
);
1612 /* 2 middel top and bottom */
1613 tb_change_cell((width
-1)/2, 0, u_mn
, cframe
.fg
, cframe
.bg
);
1614 tb_change_cell((width
-1)/2, height
-2, u_ms
, cframe
.fg
, cframe
.bg
);
1622 if (tb_select_output_mode(TB_OUTPUT_256
) != TB_OUTPUT_256
)
1623 if (tb_select_output_mode(TB_OUTPUT_NORMAL
) != TB_OUTPUT_NORMAL
)
1624 die("output error");
1629 if (listdir(NoHi
, NULL
) < 0)
1630 print_error(strerror(errno
));
1632 if (listdir(AddHi
, NULL
) < 0)
1633 print_error(strerror(errno
));
1639 main(int argc
, char *argv
[])
1642 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath", NULL
) == -1)
1644 #endif /* __OpenBSD__ */
1648 argc
== 2 && strlen(argv
[1]) == (size_t)2 &&
1649 strcmp("-v", argv
[1]) == 0) {
1652 die("usage: sfm [-v]");