[feat] color exec files
[sfm.git] / sfm.c
bloba6b94914c3575df9dbe4e5523d4ab3f6485e3d4c
1 /* See LICENSE file for copyright and license details. */
3 #include <dirent.h>
4 #include <errno.h>
5 #include <stdarg.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/resource.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <time.h>
17 #include <pwd.h>
18 #include <grp.h>
20 #include "termbox.h"
21 #include "util.h"
23 /* macros */
24 #define MAX_P 4095
25 #define MAX_N 255
26 #define MAX_USRI 32
28 /* enums */
29 enum { AskDel, DAskDel }; /* delete directory */
31 /* typedef */
32 typedef struct {
33 char name[MAX_N];
34 char full[MAX_P];
35 off_t size;
36 mode_t mode;
37 time_t td;
38 uid_t user;
39 gid_t group;
40 } Entry;
42 typedef struct {
43 char dirn[MAX_P]; // directory name
44 char high_dir[MAX_P]; // highlighted_dir fullpath
45 int dirx; // position of title
46 size_t hdir; // highlighted_dir
47 size_t dirc; // total files in dir
48 uint16_t dir_bg;
49 uint16_t dir_fg;
50 } Pane;
52 typedef struct {
53 char key;
54 char path[MAX_P];
55 } Bookmark;
57 typedef struct {
58 char *soft;
59 const char **ext;
60 size_t len;
61 } Rule;
63 #include "config.h"
65 /* function declarations */
66 static void print_tb(const char*, int, int, uint16_t, uint16_t);
67 static void printf_tb(int, int, uint16_t, uint16_t, const char*, ...);
68 static void print_status(const char*, ...);
69 static void print_xstatus(char, int);
70 static void print_error(const char*, ...);
71 static void clear(int, int, int, uint16_t);
72 static void clear_status(void);
73 static void clear_error(void);
74 static void clear_pane(int);
75 static char *get_extentions(char*);
76 static char *get_full_path(char*, char*);
77 static char *get_parent(char*);
78 static char *get_file_info(Entry*);
79 static char *get_file_size(off_t);
80 static char *get_file_date_time(time_t);
81 static char *get_file_userowner(uid_t, size_t);
82 static char *get_file_groupowner(gid_t, size_t);
83 static char *get_file_perm(mode_t);
84 static int create_new_dir(char*, char*);
85 static int create_new_file(char*, char*);
86 static int delete_ent(char *fullpath);
87 static int delete_file(char*);
88 static int delete_dir(char*, int);
89 static int check_dir(char*);
90 static int chech_execf(mode_t mode);
91 static int open_files(char*);
92 static int sort_name(const void *const, const void *const);
93 static void float_to_string(float, char*);
94 static size_t findbm(char);
95 static int get_user_input(char*, size_t, char*);
96 static void print_col(Entry*, size_t, size_t, size_t, int, int, char*);
97 static size_t scroll(size_t, size_t, size_t);
98 static int listdir(Pane*, char*);
99 static void press(struct tb_event*, Pane*, Pane*);
100 static void t_resize(Pane*, Pane*);
101 static int set_panes(Pane*, Pane*, int);
102 static void draw_frame(void);
103 static int start(void);
105 /* global variables */
106 static int parent_row = 1; // FIX
108 /* function implementations */
109 static void
110 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
112 while (*str != '\0') {
113 uint32_t uni = 0;
114 str += tb_utf8_char_to_unicode(&uni, str);
115 tb_change_cell(x, y, uni, fg, bg);
116 x++;
120 static void
121 printf_tb(int x, int y, uint16_t fg, uint16_t bg, const char *fmt, ...)
123 char buf[4096];
124 va_list vl;
125 va_start(vl, fmt);
126 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
127 va_end(vl);
128 print_tb(buf, x, y, fg, bg);
131 static void
132 print_status(const char *fmt, ...)
134 int height;
135 height = tb_height();
137 char buf[256];
138 va_list vl;
139 va_start(vl, fmt);
140 vsnprintf(buf, sizeof(buf), fmt, vl);
141 va_end(vl);
142 clear_status();
143 print_tb(buf, 2, height-2, TB_DEFAULT, status_b);
147 static void
148 print_xstatus(char c, int x)
150 int height;
151 uint32_t uni = 0;
152 height = tb_height();
153 (void)tb_utf8_char_to_unicode(&uni, &c);
154 tb_change_cell(x, height-2, uni, TB_DEFAULT, status_b);
157 static void
158 print_error(const char *fmt, ...)
160 int height, width;
161 width = tb_width();
162 height = tb_height();
164 char buf[256];
165 va_list vl;
166 va_start(vl, fmt);
167 vsnprintf(buf, sizeof(buf), fmt, vl);
168 va_end(vl);
169 clear_error();
170 print_tb(buf, width-20, height-2, 124, status_b);
174 static void
175 clear(int sx, int ex, int y, uint16_t bg)
177 /* clear line from to */
178 /* x = line number vertical */
179 /* y = column number horizontal */
180 int i;
181 for (i = sx; i < ex; i++) {
182 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
187 static void
188 clear_status(void)
190 int width, height;
191 width = tb_width();
192 height = tb_height();
193 clear(1, width-20, height-2, status_b);
196 static void
197 clear_error(void)
199 int width, height;
200 width = tb_width();
201 height = tb_height();
202 clear(width-30, width-1, height-2, status_b);
205 static void
206 clear_pane(int pane)
208 int i, x, ex, y, width, height;
209 width = tb_width();
210 height = tb_height();
211 x = 0, y = 0, i = 0, ex = 0;
213 if (pane == 2){
214 x = 2;
215 ex = (width/2) - 1;
216 } else if (pane == (width/2) + 2) {
217 x = (width/2) + 1;
218 ex = width -1;
221 while (i < height-2) {
222 clear(x, ex, y, TB_DEFAULT);
223 i++;
224 y++;
226 /* draw top line */
227 for (y = x; y < ex ; ++y) {
228 tb_change_cell(y, 0, u_hl, frame_f, frame_b);
233 static char *
234 get_extentions(char *str)
236 char *ext;
237 char dot;
238 size_t counter, len, i;
240 dot = '.';
241 counter = 0;
242 len = strlen(str);
244 for (i = len-1; i > 0; i--) {
245 if (str[i] == dot) {
246 break;
247 } else {
248 counter++;
252 ext = ecalloc(counter+1, sizeof(char));
253 strncpy(ext, &str[len-counter], counter);
254 return ext;
257 static char *
258 get_full_path(char *first, char *second)
260 char *full_path;
261 size_t full_path_len;
263 full_path_len = strlen(first) + strlen(second) + 2;
264 full_path = ecalloc(full_path_len, sizeof(char));
266 if (strcmp(first, "/") == 0) {
267 (void)snprintf(full_path, full_path_len, "/%s", second);
269 } else {
270 (void)snprintf(
271 full_path, full_path_len, "%s/%s", first, second);
274 return full_path;
277 static char *
278 get_parent(char *dir)
280 char *parent;
281 char dot;
282 size_t counter, len, i;
284 dot = '/';
285 counter = 0;
286 len = strlen(dir);
288 for (i = len-1; i > 0; i--) {
289 if (dir[i] == dot) {
290 break;
291 } else {
292 counter++;
296 i = len - counter - 1;
297 if (i == 0)
298 i = len - counter;
299 parent = ecalloc(i+1, sizeof(char));
300 strncpy(parent, dir, i);
302 return parent;
305 static char *
306 get_file_info(Entry *cursor)
308 char *size, *result, *ur, *gr, *td, *perm;
309 size_t size_len = (size_t)9;
310 size_t perm_len = (size_t)11;
311 size_t ur_len = (size_t)32;
312 size_t gr_len = (size_t)32;
313 size_t td_len = (size_t)14;
314 size_t result_chars = size_len + perm_len + ur_len + gr_len + td_len;
315 result = ecalloc(result_chars, sizeof(char));
317 size = get_file_size(cursor->size);
318 td = get_file_date_time(cursor->td);
319 perm = get_file_perm(cursor->mode);
321 strncpy(result, perm, perm_len);
322 strcat(result, " ");
323 strncat(result, size, size_len);
324 strcat(result, " ");
326 if (show_ug == 1) {
327 ur = get_file_userowner(cursor->user, ur_len);
328 gr = get_file_groupowner(cursor->group, gr_len);
329 strncat(result, ur, ur_len);
330 strcat(result, ":");
331 strncat(result, gr, gr_len);
332 strcat(result, " ");
333 free(ur);
334 free(gr);
337 strncat(result, td, td_len);
339 free(size);
340 free(td);
341 free(perm);
343 return result;
346 static char *
347 get_file_size(off_t size)
349 /* need to be freed */
350 char *Rsize;
351 double lsize;
352 int counter;
353 counter = 0;
355 Rsize = ecalloc(10, sizeof(char));
356 lsize = size;
358 while (lsize >= 1000)
360 lsize /= 1024.0;
361 ++counter;
364 float_to_string(lsize, Rsize);
366 switch (counter)
368 case 0:
369 strcat(Rsize, "B");
370 break;
371 case 1:
372 strcat(Rsize, "KB");
373 break;
374 case 2:
375 strcat(Rsize, "MB");
376 break;
377 case 3:
378 strcat(Rsize, "GB");
379 break;
380 case 4:
381 strcat(Rsize, "TB");
382 break;
385 return Rsize;
388 static char *
389 get_file_date_time(time_t status)
391 char *result;
392 struct tm lt;
393 size_t result_len;
395 result_len = (size_t)14;
396 result = ecalloc(result_len, sizeof(char));
397 localtime_r(&status, &lt);
399 if (strftime(result, result_len, dt_fmt, &lt) != sizeof(dt_fmt)-1) {
400 free(result);
401 return NULL;
404 return result;
407 static char *
408 get_file_userowner(uid_t status, size_t len)
410 char *result;
411 struct passwd *pw;
413 result = ecalloc(len, sizeof(char));
414 pw = getpwuid(status);
415 if (pw == NULL)
416 (void)snprintf(result, len-1, "%d", (int)status);
417 else
418 strncpy(result, pw->pw_name, len-1);
420 return result;
423 static char *
424 get_file_groupowner(gid_t status, size_t len)
426 char *result;
427 struct group *gr;
429 result = ecalloc(len, sizeof(char));
430 gr = getgrgid(status);
431 if (gr == NULL)
432 (void)snprintf(result, len-1, "%d", (int)status);
433 else
434 strncpy(result, gr->gr_name, len-1);
436 return result;
439 static char *
440 get_file_perm(mode_t mode)
442 char *buf;
443 size_t i;
445 const char chars[] = "rwxrwxrwx";
446 buf = ecalloc(11, sizeof(char));
448 if(S_ISDIR(mode))
449 buf[0] = 'd';
450 else if(S_ISREG(mode))
451 buf[0] = '-';
452 else if(S_ISLNK(mode))
453 buf[0] = 'l';
454 else if(S_ISBLK(mode))
455 buf[0] = 'b';
456 else if(S_ISCHR(mode))
457 buf[0] = 'c';
458 else if(S_ISFIFO(mode))
459 buf[0] = 'p';
460 else if(S_ISSOCK(mode))
461 buf[0] = 's';
462 else
463 buf[0] = '?';
465 for (i = 1; i < 10; i++) {
466 buf[i] = (mode & (1 << (9-i))) ? chars[i-1] : '-';
468 buf[10] = '\0';
470 return buf;
473 static int
474 create_new_dir(char *cwd, char *user_input)
476 char *path;
477 path = ecalloc(strlen(cwd)+strlen(user_input)+2, sizeof(char));
478 strcpy(path, cwd);
479 strcat(path, "/");
480 strcat(path, user_input);
482 if(mkdir(path, new_dir_perm) < 0) {
483 free(path);
484 return -1;
487 free(path);
488 return 0;
491 static int
492 create_new_file(char *cwd, char *user_input)
494 char *path;
495 path = ecalloc(strlen(cwd)+strlen(user_input)+2, sizeof(char));
496 strcpy(path, cwd);
497 strcat(path, "/");
498 strcat(path, user_input);
500 FILE* file_ptr;
501 file_ptr = fopen(path, "w");
502 free(path);
504 if (file_ptr != NULL) {
505 (void)fclose(file_ptr);
506 return 0;
509 return -1;
513 static int
514 delete_ent(char *fullpath)
516 struct stat status;
517 mode_t mode;
519 if (lstat(fullpath, &status) < 0)
520 return -1;
522 mode = status.st_mode;
523 if(S_ISDIR(mode)) {
524 return delete_dir(fullpath, AskDel);
525 } else {
526 return delete_file(fullpath);
531 static int
532 delete_dir(char *fullpath, int delchoice)
534 if (delchoice == AskDel) {
535 char *confirmation;
536 confirmation = ecalloc((size_t)2, sizeof(char));
537 if ((get_user_input(
538 confirmation, 2,"delete directory (Y) ?") < 0) ||
539 (strcmp(confirmation, "Y") != 0)) {
540 free(confirmation);
541 return 1; /* canceled by user or wrong confirmation */
543 free(confirmation);
546 if (rmdir(fullpath) == 0)
547 return 0; /* empty directory */
549 DIR *dir;
550 char *ent_full;
551 mode_t mode;
552 struct dirent *entry;
553 struct stat status;
555 dir = opendir(fullpath);
556 if (dir == NULL) {
557 return -1;
560 while ((entry = readdir(dir)) != 0) {
561 if ((strcmp(entry->d_name, ".") == 0 ||
562 strcmp(entry->d_name, "..") == 0))
563 continue;
565 ent_full = get_full_path(fullpath, entry->d_name);
566 if (lstat(ent_full, &status) == 0) {
567 mode = status.st_mode;
568 if (S_ISDIR(mode)) {
569 if (delete_dir(ent_full, DAskDel) < 0) {
570 free(ent_full);
571 return -1;
573 } else if (S_ISREG(mode)) {
574 if (unlink(ent_full) < 0) {
575 free(ent_full);
576 return -1;
580 free(ent_full);
583 print_status("gotit");
584 if (closedir(dir) < 0)
585 return -1;
587 return rmdir(fullpath); /* directory after delete all entries */
590 static int
591 delete_file(char *fullpath)
593 char *confirmation;
594 confirmation = ecalloc((size_t)2, sizeof(char));
596 if ((get_user_input(confirmation, 2, "delete file (Y) ?") < 0) ||
597 (strcmp(confirmation, "Y") != 0)) {
598 free(confirmation);
599 return 1; /* canceled by user or wrong confirmation */
602 free(confirmation);
603 return unlink(fullpath);
606 static int
607 check_dir(char *path)
609 DIR *dir;
610 dir = opendir(path);
612 if (dir == NULL) {
613 if (errno == ENOTDIR) {
614 return 1;
615 } else {
616 return -1;
620 if (closedir(dir) < 0)
621 return -1;
623 return 0;
626 static int
627 chech_execf(mode_t mode)
629 if (S_ISREG(mode))
630 return ((S_IXUSR | S_IXGRP | S_IXOTH) & mode);
631 return 0;
634 static int
635 open_files(char *filename)
637 // TODO
638 /* open editor in other window */
639 /* wait option */
640 char *editor, *file_ex, *software, *term;
641 int status;
642 size_t d, c;
643 pid_t pid, r;
645 editor = getenv("EDITOR");
646 term = getenv("TERM");
647 file_ex = get_extentions(filename);
648 software = NULL;
650 /* find software in rules */
651 for (c = 0; c < LEN(rules); c++) {
652 for (d = 0; d < rules[c].len; d++){
653 if (strcmp(rules[c].ext[d], file_ex) == 0) {
654 software = rules[c].soft;
659 /* default softwares */
660 if (term == NULL)
661 term = "xterm-256color";
662 if (editor == NULL)
663 editor = "vi";
664 if (software == NULL)
665 software = editor;
667 free(file_ex);
668 char *filex[] = {software, filename, NULL};
669 tb_shutdown();
670 pid = fork();
672 switch (pid) {
673 case -1:
674 return -1;
675 case 0:
676 (void)execvp(filex[0], filex);
677 exit(EXIT_SUCCESS);
678 default:
679 while ((r = waitpid(pid, &status, 0)) == -1 && errno == EINTR)
680 continue;
681 if (r == -1)
682 return -1;
683 if ((WIFEXITED(status) != 0) && (WEXITSTATUS(status) != 0))
684 return -1;
687 return 0;
690 static int
691 sort_name(const void *const A, const void *const B)
693 int result;
694 mode_t data1 = (*(Entry*) A).mode;
695 mode_t data2 = (*(Entry*) B).mode;
697 if (data1 < data2) {
698 return -1;
699 } else if (data1 == data2) {
700 result = strcmp((*(Entry*) A).name,(*(Entry*) B).name);
701 return result;
702 } else {
703 return 1;
707 static void
708 float_to_string(float f, char *r)
710 int length, length2, i, number, position, tenth; /* length is size of decimal part, length2 is size of tenth part */
711 float number2;
713 f = (float)(int)(f * 10) / 10;
715 number2 = f;
716 number = (int)f;
717 length2 = 0;
718 tenth = 1;
720 /* Calculate length2 tenth part */
721 while ((number2 - (float)number) != 0.0 && !((number2 - (float)number) < 0.0))
723 tenth *= 10.0;
724 number2 = f * (float)tenth;
725 number = (int)number2;
727 length2++;
730 /* Calculate length decimal part */
731 for (length = (f > 1.0) ? 0 : 1; f > 1.0; length++)
732 f /= 10.0;
734 position = length;
735 length = length + 1 + length2;
736 number = (int)number2;
738 if (length2 > 0)
740 for (i = length; i >= 0; i--)
742 if (i == (length))
743 r[i] = '\0';
744 else if (i == (position))
745 r[i] = '.';
746 else
748 r[i] = (char)(number % 10) + '0';
749 number /= 10;
753 else
755 length--;
756 for (i = length; i >= 0; i--)
758 if (i == (length))
759 r[i] = '\0';
760 else
762 r[i] = (char)(number % 10) + '0';
763 number /= 10;
769 static size_t
770 findbm(char event)
772 size_t i;
774 for (i = 0; i < LEN(bmarks); i++) {
775 if (event == bmarks[i].key) {
776 if (check_dir(bmarks[i].path) != 0) {
777 print_error("%s", strerror(errno));
778 return -1;
780 return i;
783 return -1;
786 static int
787 get_user_input(char *out, size_t sout, char *prompt)
789 int height = tb_height();
790 size_t startat;
791 struct tb_event fev;
792 size_t counter = 1;
793 char empty = ' ';
794 int x = 0;
796 clear_status();
797 startat = strlen(prompt) + 3;
798 print_status("%s:", prompt);
799 tb_set_cursor(startat + 1, height-2);
800 tb_present();
802 while (tb_poll_event(&fev) != 0) {
803 switch (fev.type) {
804 case TB_EVENT_KEY:
805 if (fev.key == TB_KEY_ESC) {
806 tb_set_cursor(-1, -1);
807 clear_status();
808 return -1;
811 if (fev.key == TB_KEY_BACKSPACE ||
812 fev.key == TB_KEY_BACKSPACE2) {
813 if (BETWEEN(counter, 2, sout)) {
814 out[x-1] = '\0';
815 counter--;
816 x--;
817 print_xstatus(empty, startat + counter);
818 tb_set_cursor(
819 startat + counter, height - 2);
822 } else if (fev.key == TB_KEY_ENTER) {
823 tb_set_cursor(-1, -1);
824 out[counter-1] = '\0';
825 return 0;
827 } else {
828 if (counter < sout) {
829 print_xstatus((char)fev.ch, startat+counter);
830 out[x] = (char)fev.ch;
831 tb_set_cursor(startat + counter + 1,height-2);
832 counter++;
833 x++;
837 tb_present();
838 break;
840 default:
841 return -1;
845 return -1;
849 static void
850 print_col(Entry *entry, size_t hdir, size_t x, size_t y, int dyn_y, int width, char *filter)
852 uint16_t bg, fg;
853 char buf[MAX_P];
854 bg = file_b;
855 fg = file_f;
857 if (S_ISDIR(entry->mode)) {
858 bg = dir_b;
859 fg = dir_f;
860 } else if (S_ISLNK(entry->mode)) {
861 realpath(entry->full, buf);
862 strcat(entry->name, " -> ");
863 strncat(entry->name, buf, MAX_N - strlen(entry->name)-1);
864 bg = other_b;
865 fg = other_f;
868 /* highlight executable files */
869 if (chech_execf(entry->mode))
870 fg = exec_f;
872 /* highlighted (cursor) */
873 if (y + dyn_y == hdir) {
874 bg = bg | TB_REVERSE;
875 fg = fg | TB_REVERSE | TB_BOLD;
878 /* highlight found filter */
879 if (filter != NULL) {
880 if (strstr(entry->name, filter) != NULL) {
881 bg = search_b;
882 fg = search_f | TB_BOLD;
886 /* print each element in directory */
887 printf_tb((int)x, (int)y, fg, bg, "%*.*s", ~width, width, entry->name);
891 static size_t
892 scroll(size_t height, size_t dirc, size_t hdir)
894 size_t result, limit;
896 result = 0;
897 limit = (height -1) / 2;
898 if (dirc > height - 1) {
899 if (hdir < limit){
900 result = 0;
901 } else if (hdir > dirc - limit) {
902 result = dirc - height + 1;
903 } else {
904 result = hdir - limit;
907 return result;
910 static int
911 listdir(Pane *cpane, char *filter)
913 DIR *dir;
914 struct dirent *entry;
915 struct stat status;
916 Entry *list;
917 char *fullpath;
918 char *fileinfo;
919 int width;
920 size_t y, i, height, dyn_max, dyn_y, start_from;
922 height = (size_t)tb_height() - 2;
923 width = (tb_width() / 2) - 4;
924 cpane->dirc = - 2; /* dont't count . .. */
925 i = 0;
927 dir = opendir(cpane->dirn);
928 if (dir == NULL)
929 return -1;
931 /* get content sum */
932 while ((entry = readdir(dir)) != 0) {
933 cpane->dirc++;
936 /* empty directory */
937 if (cpane->dirc == 0) {
938 if (closedir(dir) < 0)
939 return -1;
941 /* print current directory title */
942 printf_tb(cpane->dirx, 0, cpane->dir_fg | TB_BOLD,
943 cpane->dir_bg, " %.*s ", width, cpane->dirn);
944 return 0;
947 rewinddir(dir); /* reset position */
949 /* create array of entries */
950 i = 0;
951 list = ecalloc(cpane->dirc, sizeof(Entry));
952 while ((entry = readdir(dir)) != 0) {
953 if ((strcmp(entry->d_name, ".") == 0 ||
954 strcmp(entry->d_name, "..") == 0))
955 continue;
957 strcpy(list[i].name, entry->d_name);
958 fullpath = get_full_path(cpane->dirn, entry->d_name);
959 strcpy(list[i].full, fullpath);
961 if (lstat(fullpath, &status) == 0) {
962 list[i].size = status.st_size;
963 list[i].mode = status.st_mode;
964 list[i].group = status.st_gid;
965 list[i].user = status.st_uid;
966 list[i].td = status.st_mtime;
969 free(fullpath);
971 i++;
974 qsort(list, cpane->dirc, sizeof(Entry), sort_name);
976 /* scroll */
977 y = 1;
978 dyn_y = 0;
979 start_from = scroll(height, cpane->dirc, cpane->hdir);
980 dyn_y = start_from;
981 dyn_max = MIN(cpane->dirc, (height - 1) + start_from);
983 /* get full path of cursor */
984 fullpath = get_full_path(cpane->dirn,
985 list[cpane->hdir-1].name);
986 strncpy(cpane->high_dir, fullpath, (size_t)MAX_P);
987 free(fullpath);
989 /* print each entry in directory */
990 while (start_from < dyn_max) {
991 print_col(&list[start_from], cpane->hdir,
992 (size_t)cpane->dirx, y, (int)dyn_y, width, filter);
993 start_from++;
994 y++;
997 fileinfo = get_file_info(&list[cpane->hdir-1]);
999 /* print info in statusbar */
1000 print_status("%lu/%lu %s",
1001 cpane->hdir,
1002 cpane->dirc,
1003 fileinfo);
1005 /* print current directory title */
1006 printf_tb(cpane->dirx, 0, cpane->dir_fg | TB_BOLD, cpane->dir_bg,
1007 " %.*s ", width, cpane->dirn);
1009 free(fileinfo);
1010 free(list);
1012 if (closedir(dir) < 0)
1013 return -1;
1015 // print_error("mem (%d)", get_memory_usage());
1017 return 0;
1020 static void
1021 press(struct tb_event *ev, Pane *cpane, Pane *opane)
1023 char *parent;
1024 int b;
1025 // clear_error();
1027 if (ev->ch == 'j') {
1028 if (cpane->hdir < cpane->dirc) {
1029 cpane->hdir++;
1030 (void)listdir(cpane, NULL);
1032 } else if (ev->ch == 'k') {
1033 if (cpane->hdir > 1) {
1034 cpane->hdir--;
1035 (void)listdir(cpane, NULL);
1037 } else if (ev->ch == 'h') {
1038 parent = get_parent(cpane->dirn);
1039 if (check_dir(parent) < 0) { /* failed to open directory */
1040 print_error("%s", strerror(errno));
1041 } else {
1042 strcpy(cpane->dirn, parent);
1043 clear_pane(cpane->dirx);
1044 cpane->hdir = (size_t)parent_row;
1045 (void)listdir(cpane, NULL);
1046 parent_row = 1;
1048 free(parent);
1049 } else if (ev->ch == 'l') {
1050 switch (check_dir(cpane->high_dir)) {
1051 case 0:
1052 strcpy(cpane->dirn, cpane->high_dir);
1053 clear_pane(cpane->dirx);
1054 parent_row = (int)cpane->hdir;
1055 cpane->hdir = 1;
1056 (void)listdir(cpane, NULL);
1057 break;
1058 case 1:
1059 /* is not a directory open file */
1060 if (open_files(cpane->high_dir) < 0) {
1061 print_error("procces failed");
1062 return;
1064 if (tb_init() != 0)
1065 die("tb_init");
1066 if (cpane->dirx == 2) /* if current left pane */
1067 t_resize(cpane, opane);
1068 else
1069 t_resize(opane, cpane);
1070 break;
1071 case -1:
1072 /* failed to open directory */
1073 print_error("%s", strerror(errno));
1075 } else if (ev->ch == 'g') {
1076 cpane->hdir = 1;
1077 (void)listdir(cpane, NULL);
1078 } else if (ev->ch == 'G') {
1079 cpane->hdir = cpane->dirc;
1080 (void)listdir(cpane, NULL);
1081 } else if (ev->ch == 'M') {
1082 cpane->hdir = (cpane->dirc/2);
1083 (void)listdir(cpane, NULL);
1084 } else if (ev->key == TB_KEY_CTRL_U) {
1085 if (cpane->hdir > move_ud)
1086 cpane->hdir = cpane->hdir - move_ud;
1087 else
1088 cpane->hdir = 1;
1089 (void)listdir(cpane, NULL);
1090 } else if (ev->key == TB_KEY_CTRL_D) {
1091 if (cpane->hdir < cpane->dirc - move_ud)
1092 cpane->hdir = cpane->hdir + move_ud;
1093 else
1094 cpane->hdir = cpane->dirc;
1095 (void)listdir(cpane, NULL);
1096 } else if (ev->ch == 'n') {
1097 char *user_input;
1098 user_input = ecalloc(MAX_USRI, sizeof(char));
1099 if (get_user_input(user_input, MAX_USRI, "new file") < 0) {
1100 free(user_input);
1101 return;
1103 if (create_new_file(cpane->dirn, user_input) < 0)
1104 print_error("%s", strerror(errno));
1105 else
1106 listdir(cpane, NULL);
1107 free(user_input);
1108 } else if (ev->ch == 'N') {
1109 char *user_input;
1110 user_input = ecalloc(MAX_USRI, sizeof(char));
1111 if (get_user_input(user_input, MAX_USRI, "new directory") < 0) {
1112 free(user_input);
1113 return;
1115 if (create_new_dir(cpane->dirn, user_input) < 0)
1116 print_error("%s", strerror(errno));
1117 else
1118 listdir(cpane, NULL);
1119 free(user_input);
1121 } else if (ev->ch == 'D') {
1122 switch (delete_ent(cpane->high_dir)) {
1123 case -1:
1124 print_error("%s", strerror(errno));
1125 break;
1126 case 0:
1127 clear_pane(cpane->dirx);
1128 if (cpane->hdir == cpane->dirc) /* last entry */
1129 cpane->hdir--;
1130 listdir(cpane, NULL);
1131 break;
1133 } else if (ev->ch == '/') {
1134 char *user_input;
1135 user_input = ecalloc(MAX_USRI, sizeof(char));
1136 if (get_user_input(user_input, MAX_USRI, "filter") < 0) {
1137 free(user_input);
1138 return;
1140 print_error("out -> %s", user_input);
1141 listdir(cpane, user_input);
1142 free(user_input);
1143 } else {
1144 /* bookmarks */
1145 b = (int)findbm((char)ev->ch);
1146 if (b < 0)
1147 return;
1149 strcpy(cpane->dirn, bmarks[b].path);
1150 clear_pane(cpane->dirx);
1151 cpane->hdir = 1;
1152 (void)listdir(cpane, NULL);
1157 static void
1158 t_resize(Pane *cpane, Pane *opane)
1160 tb_clear();
1161 draw_frame();
1162 (void)set_panes(cpane, opane, 1);
1163 (void)listdir(cpane, NULL);
1164 (void)listdir(opane, NULL);
1165 tb_present();
1169 static int
1170 set_panes(Pane *pane_l, Pane *pane_r, int resize)
1172 int width;
1173 char *home;
1174 char cwd[MAX_P];
1176 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1177 return -1;
1179 home = getenv("HOME");
1180 width = tb_width();
1182 if (home == NULL)
1183 home = "/";
1185 pane_l->dirx = 2;
1186 pane_l->dir_fg = pane_l_f;
1187 pane_l->dir_bg = pane_l_b;
1188 if (resize == 0) {
1189 strcpy(pane_l->dirn, cwd);
1190 pane_l->hdir = 1;
1193 pane_r->dirx = (width / 2) + 2;
1194 pane_r->dir_fg = pane_r_f;
1195 pane_r->dir_bg = pane_r_b;
1197 if (resize == 0) {
1198 strcpy(pane_r->dirn, home);
1199 pane_r->hdir = 1;
1202 return 0;
1205 static void
1206 draw_frame(void)
1208 int height, width, i;
1210 width = tb_width();
1211 height = tb_height();
1213 /* 2 horizontal lines */
1214 for (i = 1; i < width-1 ; ++i) {
1215 tb_change_cell(i, 0, u_hl, frame_f, frame_b);
1216 tb_change_cell(i, height-1, u_hl, frame_f, frame_b);
1219 /* 3 vertical lines */
1220 for (i = 1; i < height-1 ; ++i) {
1221 tb_change_cell(0, i, u_vl, frame_f, frame_b);
1222 tb_change_cell((width-1)/2, i-1, u_vl, frame_f, frame_b);
1223 tb_change_cell(width-1, i, u_vl, frame_f, frame_b);
1226 /* 4 corners */
1227 tb_change_cell(0, 0, u_cnw, frame_f, frame_b);
1228 tb_change_cell(width-1, 0, u_cne, frame_f, frame_b);
1229 tb_change_cell(0, height-1, u_csw, frame_f, frame_b);
1230 tb_change_cell(width-1, height-1, u_cse, frame_f, frame_b);
1232 /* 2 middel top and bottom */
1233 tb_change_cell((width-1)/2, 0, u_mn, frame_f, frame_b);
1237 static int
1238 start(void)
1240 int init_height, init_width;
1241 struct tb_event ev;
1242 Pane pane_r, pane_l;
1243 int current_pane = 0;
1245 if (tb_init()!= 0)
1246 die("tb_init");
1248 init_width = tb_width();
1249 init_height = tb_height();
1251 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1252 (void)tb_select_output_mode(TB_OUTPUT_NORMAL);
1254 tb_clear();
1255 draw_frame();
1256 (void)set_panes(&pane_l, &pane_r, 0);
1257 (void)listdir(&pane_r, NULL);
1258 (void)listdir(&pane_l, NULL);
1259 tb_present();
1261 while (tb_poll_event(&ev) != 0) {
1262 switch (ev.type) {
1263 case TB_EVENT_KEY:
1265 if (ev.ch == 'q') {
1266 tb_shutdown();
1267 return 0;
1268 } else if (ev.key == TB_KEY_SPACE) {
1269 current_pane = !current_pane;
1272 if (current_pane == 0) {
1273 press(&ev, &pane_l, &pane_r);
1275 } else if (current_pane == 1) {
1276 press(&ev, &pane_r, &pane_l);
1279 tb_present();
1280 break;
1282 case TB_EVENT_RESIZE:
1283 t_resize(&pane_l, &pane_r);
1284 break;
1286 default:
1287 break;
1291 tb_shutdown();
1292 return 0;
1296 main(int argc, char *argv[])
1298 #ifdef __OpenBSD__
1299 if (pledge("stdio tty rpath proc exec", NULL) == -1)
1300 die("pledge");
1301 #endif /* __OpenBSD__ */
1302 if (argc == 1) {
1303 if (start() != 0)
1304 die("start failed");
1305 } else if (
1306 argc == 2 && strlen(argv[1]) == (size_t)2 &&
1307 strcmp("-v", argv[1]) == 0) {
1308 die("sfm-"VERSION);
1309 } else {
1310 die("usage: sfm [-v]");
1312 return 0;