[fix] print basename, mvbk
[sfm.git] / sfm.c
blob5fdbea51fb1905cf912f6bebf15a68e98dacaca5
1 /* See LICENSE file for copyright and license details. */
3 #include <sys/types.h>
4 #include <sys/resource.h>
5 #include <sys/stat.h>
6 #include <sys/time.h>
7 #include <sys/wait.h>
8 #if defined(__linux__)
9 #include <sys/inotify.h>
10 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
11 defined(__OpenBSD__) || defined(__APPLE__)
12 #include <sys/event.h>
13 #endif
15 #include <dirent.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <grp.h>
19 #include <pwd.h>
20 #include <stdarg.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <libgen.h>
29 #include "termbox.h"
30 #include "util.h"
32 /* macros */
33 #define MAX_P 4095
34 #define MAX_N 255
35 #define MAX_USRI 32
36 #define MAX_EXT 4
37 #define CURSOR_NAME cpane->direntr[cpane->hdir - 1].name
39 /* enums */
40 enum { AddHi, NoHi }; /* add highlight in listdir */
42 /* typedef */
43 typedef struct {
44 char name[MAX_N];
45 gid_t group;
46 mode_t mode;
47 off_t size;
48 time_t td;
49 uid_t user;
50 } Entry;
52 typedef struct {
53 uint16_t fg;
54 uint16_t bg;
55 } Cpair;
57 typedef struct {
58 int pane_id;
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
64 int firstrow;
65 int parent_row; // FIX
66 int *selection;
67 Cpair dircol;
68 int inotify_wd;
69 int event_fd;
70 } Pane;
72 typedef struct {
73 uint32_t ch;
74 char path[MAX_P];
75 } Bookmark;
77 typedef struct {
78 const char **ext;
79 size_t exlen;
80 const void *v;
81 } Rule;
83 typedef union {
84 uint16_t key; /* one of the TB_KEY_* constants */
85 uint32_t ch; /* unicode character */
86 } Evkey;
88 typedef struct {
89 const Evkey evkey;
90 void (*func)(void);
91 } Key;
93 /* function declarations */
94 static void print_tb(const char *, int, int, uint16_t, uint16_t);
95 static void printf_tb(int, int, Cpair, const char *, ...);
96 static void print_status(Cpair, const char *, ...);
97 static void print_xstatus(char, int);
98 static void print_error(char *);
99 static void print_prompt(char *);
100 static void print_info(void);
101 static void print_row(Pane *, size_t, Cpair);
102 static void clear(int, int, int, uint16_t);
103 static void clear_status(void);
104 static void clear_pane(void);
105 static void add_hi(Pane *, size_t);
106 static void rm_hi(Pane *, size_t);
107 static void float_to_string(float, char *);
108 static int check_dir(char *);
109 static mode_t chech_execf(mode_t);
110 static int sort_name(const void *const, const void *const);
111 static void get_dirp(char *);
112 static char *get_ext(char *);
113 static int get_fdt(char *, size_t, time_t);
114 static char *get_fgrp(gid_t, size_t);
115 static char *get_finfo(Entry *);
116 static char *get_fperm(mode_t);
117 static char *get_fsize(off_t);
118 static char *get_fullpath(char *, char *);
119 static char *get_fusr(uid_t, size_t);
120 static void get_dirsize(char *, off_t *);
121 static void get_hicol(Cpair *, mode_t);
122 static int delent(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, const 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 selection(void);
152 static void selup(void);
153 static void seldwn(void);
154 static void selynk(void);
155 static void selcan(void);
156 static void selall(void);
157 static void selref(void);
158 static void selcalc(void);
159 static void selpst(void);
160 static void selmv(void);
161 static void seldel(void);
162 static void selrename(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 xrename(char *);
169 static void switch_pane(void);
170 static void quit(void);
171 static void grabkeys(struct tb_event*, Key*, size_t);
172 static void start_ev(void);
173 static void refresh_pane(void);
174 static void set_direntr(struct dirent *, DIR *);
175 static int listdir(int, char *);
176 static void t_resize(void);
177 static void set_panes(void);
178 static void draw_frame(void);
179 static void start(void);
181 /* global variables */
182 static Pane pane_r, pane_l, *cpane;
183 static char *editor[2];
184 static char fed[] = "vi";
185 static int theight, twidth, scrheight;
186 static size_t selection_size = 0;
187 static int sl = 0;
188 static char **files;
189 #if defined _SYS_INOTIFY_H
190 static int inotify_fd;
191 #elif defined _SYS_EVENT_H_
192 static int kq;
193 struct kevent evlist[2]; /* events we want to monitor */
194 struct kevent chlist[2]; /* events that were triggered */
195 static struct timespec gtimeout;
196 #endif
198 /* configuration, allows nested code to access above variables */
199 #include "config.h"
201 /* function implementations */
202 static void
203 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
205 while (*str != '\0') {
206 uint32_t uni = 0;
207 str += tb_utf8_char_to_unicode(&uni, str);
208 tb_change_cell(x, y, uni, fg, bg);
209 x++;
213 static void
214 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
216 char buf[4096];
217 va_list vl;
218 va_start(vl, fmt);
219 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
220 va_end(vl);
221 print_tb(buf, x, y, col.fg, col.bg);
224 static void
225 print_status(Cpair col, const char *fmt, ...)
227 char buf[256];
228 va_list vl;
229 va_start(vl, fmt);
230 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
231 va_end(vl);
232 clear_status();
233 print_tb(buf, 1, theight - 1, col.fg, col.bg);
236 static void
237 print_xstatus(char c, int x)
239 uint32_t uni = 0;
240 (void)tb_utf8_char_to_unicode(&uni, &c);
241 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
244 static void
245 print_error(char *errmsg)
247 print_status(cerr, errmsg);
250 static void
251 print_prompt(char *prompt)
253 print_status(cprompt, prompt);
256 static void
257 print_info(void)
259 char *fileinfo;
260 fileinfo = get_finfo(&cpane->direntr[cpane->hdir - 1]);
261 print_status(cstatus, "%d/%d %s", cpane->hdir, cpane->dirc, fileinfo);
262 free(fileinfo);
265 static void
266 print_row(Pane *pane, size_t entpos, Cpair col)
268 int x, y;
269 char *result;
270 char buf[MAX_P];
271 char lnk_full[MAX_P];
272 int width;
274 width = (twidth / 2) - 4;
275 result = basename(pane->direntr[entpos].name);
276 x = pane->dirx;
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);
284 result = lnk_full;
287 printf_tb(x, y, col, "%*.*s", ~width, width, result);
290 static void
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 */
296 int i;
297 for (i = sx; i < ex; i++) {
298 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
302 static void
303 clear_status(void)
305 clear(1, twidth - 1, theight - 1, cstatus.bg);
308 static void
309 clear_pane(void)
311 int i, ex, y;
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)
317 ex = twidth - 1;
319 while (i < scrheight) {
320 clear(cpane->dirx, ex, y, TB_DEFAULT);
321 i++;
322 y++;
325 /* draw top line */
326 for (y = cpane->dirx; y < ex; ++y) {
327 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
331 static void
332 add_hi(Pane *pane, size_t entpos)
334 Cpair col;
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);
341 static void
342 rm_hi(Pane *pane, size_t entpos)
344 Cpair col;
345 get_hicol(&col, pane->direntr[entpos].mode);
346 print_row(pane, entpos, col);
349 static void
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 */
354 float number2;
356 f = (float)(int)(f * 10) / 10;
358 number2 = f;
359 number = (int)f;
360 length2 = 0;
361 tenth = 1;
363 /* Calculate length2 tenth part */
364 while ((number2 - (float)number) != 0.0 &&
365 !((number2 - (float)number) < 0.0)) {
366 tenth *= 10.0;
367 number2 = f * (float)tenth;
368 number = (int)number2;
370 length2++;
373 /* Calculate length decimal part */
374 for (length = (f > 1.0) ? 0 : 1; f > 1.0; length++)
375 f /= 10.0;
377 position = length;
378 length = length + 1 + length2;
379 number = (int)number2;
381 if (length2 > 0) {
382 for (i = length; i >= 0; i--) {
383 if (i == (length))
384 r[i] = '\0';
385 else if (i == (position))
386 r[i] = '.';
387 else {
388 r[i] = (char)(number % 10) + '0';
389 number /= 10;
392 } else {
393 length--;
394 for (i = length; i >= 0; i--) {
395 if (i == (length))
396 r[i] = '\0';
397 else {
398 r[i] = (char)(number % 10) + '0';
399 number /= 10;
405 static int
406 check_dir(char *path)
408 DIR *dir;
409 dir = opendir(path);
411 if (dir == NULL) {
412 if (errno == ENOTDIR) {
413 return 1;
414 } else {
415 return -1;
419 if (closedir(dir) < 0)
420 return -1;
422 return 0;
425 static mode_t
426 chech_execf(mode_t mode)
428 if (S_ISREG(mode))
429 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
430 return 0;
433 static int
434 sort_name(const void *const A, const void *const B)
436 int result;
437 mode_t data1 = (*(Entry *)A).mode;
438 mode_t data2 = (*(Entry *)B).mode;
440 if (data1 < data2) {
441 return -1;
442 } else if (data1 == data2) {
443 result = strcmp((*(Entry *)A).name, (*(Entry *)B).name);
444 return result;
445 } else {
446 return 1;
451 static void
452 get_dirp(char *cdir)
454 int counter, len, i;
456 counter = 0;
457 len = strlen(cdir);
458 if (len ==1)
459 return;
461 for (i = len - 1; i > 1; i--) {
462 if (cdir[i] == '/')
463 break;
464 else
465 counter++;
468 cdir[len-counter-1] = '\0';
471 static char *
472 get_ext(char *str)
474 char *ext;
475 char dot;
476 size_t counter, len, i;
478 dot = '.';
479 counter = 0;
480 len = strlen(str);
482 for (i = len - 1; i > 0; i--) {
483 if (str[i] == dot) {
484 break;
485 } else {
486 counter++;
490 ext = ecalloc(counter + 1, sizeof(char));
491 strncpy(ext, &str[len - counter], counter);
492 return ext;
495 static int
496 get_fdt(char *result, size_t reslen, time_t status)
498 struct tm lt;
499 localtime_r(&status, &lt);
500 return strftime(result, reslen, dtfmt, &lt);
503 static char *
504 get_fgrp(gid_t status, size_t len)
506 char *result;
507 struct group *gr;
509 result = ecalloc(len, sizeof(char));
510 gr = getgrgid(status);
511 if (gr == NULL)
512 (void)snprintf(result, len - 1, "%u", status);
513 else
514 strncpy(result, gr->gr_name, len - 1);
516 return result;
519 static char *
520 get_finfo(Entry *cursor)
522 char *sz, *rst, *ur, *gr, *td, *prm;
523 size_t szlen, prmlen, urlen, grlen, tdlen, rstlen;
525 szlen = 9;
526 prmlen = 11;
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);
534 strcat(rst, " ");
535 free(prm);
538 if (show_ug == 1) {
539 ur = get_fusr(cursor->user, urlen);
540 gr = get_fgrp(cursor->group, grlen);
541 strncat(rst, ur, urlen);
542 strcat(rst, ":");
543 strncat(rst, gr, grlen);
544 strcat(rst, " ");
545 free(ur);
546 free(gr);
549 if (show_dt == 1) {
550 td = ecalloc(tdlen, sizeof(char));
551 if (get_fdt(td, tdlen, cursor->td) > 0) {
552 strncat(rst, td, tdlen);
553 strcat(rst, " ");
555 free(td);
558 if (show_size == 1 && S_ISREG(cursor->mode)) {
559 sz = get_fsize(cursor->size);
560 strncat(rst, sz, szlen);
561 free(sz);
564 return rst;
567 static char *
568 get_fperm(mode_t mode)
570 char *buf;
571 size_t i;
573 const char chars[] = "rwxrwxrwx";
574 buf = ecalloc(11, sizeof(char));
576 if (S_ISDIR(mode))
577 buf[0] = 'd';
578 else if (S_ISREG(mode))
579 buf[0] = '-';
580 else if (S_ISLNK(mode))
581 buf[0] = 'l';
582 else if (S_ISBLK(mode))
583 buf[0] = 'b';
584 else if (S_ISCHR(mode))
585 buf[0] = 'c';
586 else if (S_ISFIFO(mode))
587 buf[0] = 'p';
588 else if (S_ISSOCK(mode))
589 buf[0] = 's';
590 else
591 buf[0] = '?';
593 for (i = 1; i < 10; i++) {
594 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
596 buf[10] = '\0';
598 return buf;
601 static char *
602 get_fsize(off_t size)
604 /* need to be freed */
605 char *Rsize;
606 float lsize;
607 int counter;
608 counter = 0;
610 Rsize = ecalloc(10, sizeof(char));
611 lsize = (float)size;
613 while (lsize >= 1000.0) {
614 lsize /= 1024.0;
615 ++counter;
618 float_to_string(lsize, Rsize);
620 switch (counter) {
621 case 0:
622 strcat(Rsize, "B");
623 break;
624 case 1:
625 strcat(Rsize, "K");
626 break;
627 case 2:
628 strcat(Rsize, "M");
629 break;
630 case 3:
631 strcat(Rsize, "G");
632 break;
633 case 4:
634 strcat(Rsize, "T");
635 break;
638 return Rsize;
641 static char *
642 get_fullpath(char *first, char *second)
644 char *full_path;
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);
653 } else {
654 (void)snprintf(full_path, full_path_len, "%s/%s", first,
655 second);
658 return full_path;
661 static char *
662 get_fusr(uid_t status, size_t len)
664 char *result;
665 struct passwd *pw;
667 result = ecalloc(len, sizeof(char));
668 pw = getpwuid(status);
669 if (pw == NULL)
670 (void)snprintf(result, len - 1, "%u", status);
671 else
672 strncpy(result, pw->pw_name, len - 1);
674 return result;
677 static void
678 get_dirsize(char *fullpath, off_t *fullsize)
680 DIR *dir;
681 char *ent_full;
682 mode_t mode;
683 struct dirent *entry;
684 struct stat status;
686 dir = opendir(fullpath);
687 if (dir == NULL) {
688 return;
691 while ((entry = readdir(dir)) != 0) {
692 if ((strcmp(entry->d_name, ".") == 0 ||
693 strcmp(entry->d_name, "..") == 0))
694 continue;
696 ent_full = get_fullpath(fullpath, entry->d_name);
697 if (lstat(ent_full, &status) == 0) {
698 mode = status.st_mode;
699 if (S_ISDIR(mode)) {
700 get_dirsize(ent_full, fullsize);
701 free(ent_full);
702 } else {
703 *fullsize += status.st_size;
704 free(ent_full);
709 closedir(dir);
710 clear_status();
713 static void
714 get_hicol(Cpair *col, mode_t mode)
716 *col = cfile;
717 if (S_ISDIR(mode))
718 *col = cdir;
719 else if (S_ISLNK(mode))
720 *col = cother;
721 else if (chech_execf(mode) > 0)
722 *col = cexec;
725 static int
726 delent(char *fullpath)
728 char *confirmation;
729 char *rm_cmd[] = { "rm", "-rf", NULL };
731 confirmation = ecalloc(2, sizeof(char));
732 if ((get_usrinput(confirmation, 2, "delete file (Y) ?") < 0) ||
733 (strcmp(confirmation, "Y") != 0)) {
734 free(confirmation);
735 return 1; /* canceled by user or wrong confirmation */
737 free(confirmation);
739 return spawn(rm_cmd, fullpath);
742 static void
743 calcdir(void)
745 off_t *fullsize;
746 char *csize;
747 char *result;
749 if (S_ISDIR(cpane->direntr[cpane->hdir - 1].mode)) {
750 fullsize = ecalloc(50, sizeof(off_t));
751 get_dirsize(CURSOR_NAME, fullsize);
752 csize = get_fsize(*fullsize);
753 result = get_finfo(&cpane->direntr[cpane->hdir - 1]);
755 clear_status();
756 print_status(cstatus, "%d/%d %s%s", cpane->hdir, cpane->dirc,
757 result, csize);
758 free(csize);
759 free(fullsize);
760 free(result);
764 static void
765 crnd(void)
767 char *user_input, *path;
768 size_t pathlen;
770 user_input = ecalloc(MAX_USRI, sizeof(char));
771 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
772 free(user_input);
773 return;
776 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
777 path = ecalloc(pathlen, sizeof(char));
778 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
779 free(user_input);
780 free(path);
781 return;
784 if (mkdir(path, ndir_perm) < 0)
785 print_error(strerror(errno));
786 else if (listdir(AddHi, NULL) < 0)
787 print_error(strerror(errno));
789 free(user_input);
790 free(path);
793 static void
794 crnf(void)
796 char *user_input, *path;
797 size_t pathlen;
798 int rf;
800 user_input = ecalloc(MAX_USRI, sizeof(char));
801 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
802 free(user_input);
803 return;
806 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
807 path = ecalloc(pathlen, sizeof(char));
808 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
809 free(user_input);
810 free(path);
811 return;
814 rf = open(path, O_CREAT | O_EXCL, nf_perm);
816 if (rf < 0) {
817 print_error(strerror(errno));
818 } else {
819 if (close(rf) < 0)
820 print_error(strerror(errno));
821 else if (listdir(AddHi, NULL) < 0)
822 print_error(strerror(errno));
825 free(user_input);
826 free(path);
829 static void
830 delfd(void)
832 switch (delent(CURSOR_NAME)) {
833 case -1:
834 print_error(strerror(errno));
835 break;
836 case 0:
837 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
838 cpane->hdir--;
839 if (listdir(AddHi, NULL) < 0)
840 print_error(strerror(errno));
841 break;
845 static void
846 mvbk(void)
848 get_dirp(cpane->dirn);
849 if (check_dir(cpane->dirn) < 0) {
850 print_error(strerror(errno));
851 return;
854 rmwatch(cpane);
855 cpane->firstrow = 0;
856 cpane->hdir = cpane->parent_row;
857 if (listdir(AddHi, NULL) < 0)
858 print_error(strerror(errno));
859 cpane->parent_row = 1;
862 static void
863 mvbtm(void)
865 if (cpane->dirc < 1)
866 return;
867 if (cpane->dirc > scrheight) {
868 rm_hi(cpane, cpane->hdir - 1);
869 cpane->hdir = cpane->dirc;
870 cpane->firstrow = cpane->dirc - scrheight + 1;
871 refresh_pane();
872 add_hi(cpane, cpane->hdir - 1);
873 } else {
874 rm_hi(cpane, cpane->hdir - 1);
875 cpane->hdir = cpane->dirc;
876 add_hi(cpane, cpane->hdir - 1);
878 print_info();
881 static void
882 mvdwn(void)
884 if (cpane->dirc < 1)
885 return;
886 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
887 rm_hi(cpane, cpane->hdir - 1);
888 cpane->hdir++;
889 add_hi(cpane, cpane->hdir - 1);
890 } else {
891 mvdwns(); /* scroll */
893 print_info();
896 static void
897 mvdwns(void)
899 int real;
900 real = cpane->hdir - 1 - cpane->firstrow;
902 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
903 cpane->firstrow++;
904 rm_hi(cpane, cpane->hdir - 1);
905 cpane->hdir++;
906 refresh_pane();
907 add_hi(cpane, cpane->hdir - 1);
908 } else if (cpane->hdir < cpane->dirc) {
909 rm_hi(cpane, cpane->hdir - 1);
910 cpane->hdir++;
911 add_hi(cpane, cpane->hdir - 1);
915 static void
916 mvfor(void)
918 rmwatch(cpane);
919 if (cpane->dirc < 1)
920 return;
921 int s;
923 switch (check_dir(CURSOR_NAME)) {
924 case 0:
925 strcpy(cpane->dirn, CURSOR_NAME);
926 cpane->parent_row = cpane->hdir;
927 cpane->hdir = 1;
928 cpane->firstrow = 0;
929 if (listdir(AddHi, NULL) < 0)
930 print_error(strerror(errno));
931 break;
932 case 1: /* not a directory open file */
933 tb_shutdown();
934 s = opnf(CURSOR_NAME);
935 if (tb_init() != 0)
936 die("tb_init");
937 t_resize();
938 if (s < 0)
939 print_error("process failed non-zero exit");
940 break;
941 case -1: /* failed to open directory */
942 print_error(strerror(errno));
946 static void
947 mvmid(void)
949 if (cpane->dirc < 1)
950 return;
951 rm_hi(cpane, cpane->hdir - 1);
952 if (cpane->dirc < scrheight / 2)
953 cpane->hdir = (cpane->dirc + 1) / 2;
954 else
955 cpane->hdir = (scrheight / 2) + cpane->firstrow;
956 add_hi(cpane, cpane->hdir - 1);
957 print_info();
960 static void
961 mvtop(void)
963 if (cpane->dirc < 1)
964 return;
965 if (cpane->dirc > scrheight) {
966 rm_hi(cpane, cpane->hdir - 1);
967 cpane->hdir = 1;
968 cpane->firstrow = 0;
969 refresh_pane();
970 add_hi(cpane, cpane->hdir - 1);
971 } else {
972 rm_hi(cpane, cpane->hdir - 1);
973 cpane->hdir = 1;
974 add_hi(cpane, cpane->hdir - 1);
975 print_info();
979 static void
980 mvup(void)
982 if (cpane->dirc < 1)
983 return;
984 if (cpane->dirc < scrheight && cpane->hdir > 1) {
985 rm_hi(cpane, cpane->hdir - 1);
986 cpane->hdir--;
987 add_hi(cpane, cpane->hdir - 1);
988 } else {
989 mvups(); /* scroll */
991 print_info();
994 static void
995 mvups(void)
997 size_t real;
998 real = cpane->hdir - 1 - cpane->firstrow;
1000 if (cpane->firstrow > 0 && real < 1 + scrsp) {
1001 cpane->firstrow--;
1002 rm_hi(cpane, cpane->hdir - 1);
1003 cpane->hdir--;
1004 refresh_pane();
1005 add_hi(cpane, cpane->hdir - 1);
1006 } else if (cpane->hdir > 1) {
1007 rm_hi(cpane, cpane->hdir - 1);
1008 cpane->hdir--;
1009 add_hi(cpane, cpane->hdir - 1);
1013 static void
1014 scrdwn(void)
1016 if (cpane->dirc < 1)
1017 return;
1018 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
1019 if (cpane->hdir < cpane->dirc - scrmv) {
1020 rm_hi(cpane, cpane->hdir - 1);
1021 cpane->hdir += scrmv;
1022 add_hi(cpane, cpane->hdir - 1);
1023 } else {
1024 mvbtm();
1026 } else {
1027 scrdwns();
1029 print_info();
1032 static void
1033 scrdwns(void)
1035 int real, dynmv;
1037 real = cpane->hdir - cpane->firstrow;
1038 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
1040 if (real + scrmv + 1 > scrheight &&
1041 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
1042 cpane->firstrow += dynmv;
1043 rm_hi(cpane, cpane->hdir - 1);
1044 cpane->hdir += scrmv;
1045 refresh_pane();
1046 add_hi(cpane, cpane->hdir - 1);
1047 } else {
1048 if (cpane->hdir < cpane->dirc - scrmv - 1) {
1049 rm_hi(cpane, cpane->hdir - 1);
1050 cpane->hdir += scrmv;
1051 add_hi(cpane, cpane->hdir - 1);
1052 } else {
1053 mvbtm();
1058 static void
1059 scrup(void)
1061 if (cpane->dirc < 1)
1062 return;
1063 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1064 if (cpane->hdir > scrmv) {
1065 rm_hi(cpane, cpane->hdir - 1);
1066 cpane->hdir = cpane->hdir - scrmv;
1067 add_hi(cpane, cpane->hdir - 1);
1068 print_info();
1069 } else {
1070 mvtop();
1072 } else {
1073 scrups();
1077 static void
1078 scrups(void)
1080 int real, dynmv;
1081 real = cpane->hdir - cpane->firstrow;
1082 dynmv = MIN(cpane->firstrow, scrmv);
1084 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1085 cpane->firstrow -= dynmv;
1086 rm_hi(cpane, cpane->hdir - 1);
1087 cpane->hdir -= scrmv;
1088 refresh_pane();
1089 add_hi(cpane, cpane->hdir - 1);
1090 } else {
1091 if (cpane->hdir > scrmv + 1) {
1092 rm_hi(cpane, cpane->hdir - 1);
1093 cpane->hdir -= scrmv;
1094 add_hi(cpane, cpane->hdir - 1);
1095 } else {
1096 mvtop();
1101 static int
1102 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1104 int height = tb_height();
1105 size_t startat;
1106 struct tb_event fev;
1107 size_t counter = (size_t)1;
1108 char empty = ' ';
1109 int x = 0;
1110 int name_size = 0;
1111 char buf[256];
1113 clear_status();
1115 va_list vl;
1116 Cpair col;
1117 col = cprompt;
1118 va_start(vl, fmt);
1119 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1120 va_end(vl);
1121 print_tb(buf, 1, height-1, col.fg, col.bg);
1122 startat = name_size + 1;
1123 tb_set_cursor((int)(startat + 1), height-1);
1124 tb_present();
1126 while (tb_poll_event(&fev) != 0) {
1127 switch (fev.type) {
1128 case TB_EVENT_KEY:
1129 if (fev.key == TB_KEY_ESC) {
1130 tb_set_cursor(-1, -1);
1131 clear_status();
1132 return -1;
1135 if (fev.key == TB_KEY_BACKSPACE ||
1136 fev.key == TB_KEY_BACKSPACE2) {
1137 if (BETWEEN(counter, 2, sout)) {
1138 out[x - 1] = '\0';
1139 counter--;
1140 x--;
1141 print_xstatus(empty, startat + counter);
1142 tb_set_cursor(startat + counter,
1143 theight - 1);
1146 } else if (fev.key == TB_KEY_ENTER) {
1147 tb_set_cursor(-1, -1);
1148 out[counter - 1] = '\0';
1149 return 0;
1151 } else {
1152 if (counter < sout) {
1153 print_xstatus((char)fev.ch,
1154 (startat + counter));
1155 out[x] = (char)fev.ch;
1156 tb_set_cursor((startat + counter + 1),
1157 theight - 1);
1158 counter++;
1159 x++;
1163 tb_present();
1164 break;
1166 default:
1167 return -1;
1171 return -1;
1174 static int
1175 frules(char *ex)
1177 size_t c, d;
1179 for (c = 0; c < LEN(rules); c++)
1180 for (d = 0; d < rules[c].exlen; d++)
1181 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1182 return c;
1183 return -1;
1186 static int
1187 spawn(const void *v, char *fn)
1189 int ws, x, argc;
1190 pid_t pid, r;
1192 x = 0;
1193 argc = 0;
1195 /* count args */
1196 while (((char **)v)[x++] != NULL)
1197 argc++;
1199 char *argv[argc + 2];
1200 for ( x = 0; x < argc; x++)
1201 argv[x] = ((char **)v)[x];
1203 argv[argc] = fn;
1204 argv[argc + 1] = NULL;
1206 pid = fork();
1207 switch (pid) {
1208 case -1:
1209 return -1;
1210 case 0:
1211 execvp(argv[0], argv);
1212 exit(EXIT_SUCCESS);
1213 default:
1214 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1215 continue;
1216 if (r == -1)
1217 return -1;
1218 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1219 return -1;
1221 return 0;
1224 static int
1225 opnf(char *fn)
1227 char *ex;
1228 int c;
1230 ex = get_ext(fn);
1231 c = frules(ex);
1232 free(ex);
1234 if (c < 0) /* extension not found open in editor */
1235 return spawn(editor, fn);
1236 else
1237 return spawn((char **)rules[c].v, fn);
1240 static int
1241 fsev_init(void)
1243 #if defined _SYS_INOTIFY_H
1244 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1245 if (inotify_fd < 0)
1246 return -1;
1247 #elif defined _SYS_EVENT_H_
1248 kq = kqueue();
1249 if (kq < 0)
1250 return -1;
1251 #endif
1252 return 0;
1255 static int
1256 addwatch(void)
1258 #if defined _SYS_INOTIFY_H
1259 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1260 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1261 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1262 #elif defined _SYS_EVENT_H_
1263 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1264 if (cpane->event_fd < 0)
1265 return cpane->event_fd;
1266 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1267 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1268 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1269 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1270 return 0;
1271 #endif
1274 static int
1275 read_events(void)
1277 #if defined _SYS_INOTIFY_H
1278 char *p;
1279 ssize_t r;
1280 struct inotify_event *event;
1281 const size_t events = 32;
1282 const size_t evbuflen =
1283 events * (sizeof(struct inotify_event) + MAX_N + 1);
1284 char buf[evbuflen];
1286 if (cpane->inotify_wd < 0)
1287 return -1;
1288 r = read(inotify_fd, buf, evbuflen);
1289 if (r <= 0)
1290 return r;
1292 for (p = buf; p < buf + r;) {
1293 event = (struct inotify_event *)p;
1294 if (!event->wd)
1295 break;
1296 if (event->mask) {
1297 return r;
1300 p += sizeof(struct inotify_event) + event->len;
1302 #elif defined _SYS_EVENT_H_
1303 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1304 #endif
1305 return -1;
1308 static void
1309 rmwatch(Pane *pane)
1311 #if defined _SYS_INOTIFY_H
1312 if (pane->inotify_wd >= 0)
1313 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1314 #elif defined _SYS_EVENT_H_
1315 close(pane->event_fd);
1316 return;
1317 #endif
1320 static void
1321 fsev_shdn(void)
1323 rmwatch(&pane_l);
1324 rmwatch(&pane_r);
1325 #if defined _SYS_INOTIFY_H
1326 close(inotify_fd);
1327 #elif defined _SYS_EVENT_H_
1328 close(kq);
1329 #endif
1332 static ssize_t
1333 findbm(uint32_t event)
1335 ssize_t i;
1337 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1338 if (event == bmarks[i].ch) {
1339 if (check_dir(bmarks[i].path) != 0) {
1340 print_error(strerror(errno));
1341 return -1;
1343 return i;
1346 return -1;
1349 static void
1350 filter(void)
1352 if (cpane->dirc < 1)
1353 return;
1354 char *user_input;
1355 user_input = ecalloc(MAX_USRI, sizeof(char));
1356 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1357 free(user_input);
1358 return;
1360 if (listdir(AddHi, user_input) < 0)
1361 print_error("no match");
1362 free(user_input);
1365 static void
1366 selection(void)
1368 struct tb_event fev;
1369 if (cpane->selection != NULL) {
1370 free(cpane->selection);
1371 cpane->selection = NULL;
1373 cpane->selection = ecalloc(cpane->dirc, sizeof(size_t));
1374 cpane->selection[0] = cpane->hdir;
1375 add_hi(cpane, cpane->selection[0] - 1);
1376 sl = 0;
1377 while (tb_poll_event(&fev) != 0) {
1378 switch (fev.type) {
1379 case TB_EVENT_KEY:
1380 grabkeys(&fev, skeys, skeyslen);
1381 if(sl == -1)
1382 return;
1383 tb_present();
1384 break;
1389 static void
1390 seldwn(void)
1392 mvdwn();
1393 print_prompt("VISUAL");
1394 int index = abs(cpane->hdir - cpane->selection[0]);
1396 if (cpane->hdir > cpane->selection[0]) {
1397 cpane->selection[index] = cpane->hdir;
1398 add_hi(cpane, cpane->selection[index] - 2);
1399 } else {
1400 cpane->selection[index + 1] = 0;
1402 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1403 selref();
1407 static void
1408 selup(void)
1410 mvup();
1411 print_prompt("VISUAL");
1412 int index = abs(cpane->hdir - cpane->selection[0]);
1414 if (cpane->hdir < cpane->selection[0]) {
1415 cpane->selection[index] = cpane->hdir;
1416 add_hi(cpane, cpane->selection[index]);
1417 } else if (index < cpane->dirc) {
1418 cpane->selection[index + 1] = 0;
1420 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1421 selref();
1425 static void
1426 selref(void)
1428 int i;
1429 for (i = 0; i < cpane->dirc; i++) {
1430 if (cpane->selection[i] < (scrheight + cpane->firstrow) && cpane->selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1431 add_hi(cpane, cpane->selection[i] - 1);
1436 static void
1437 selcan(void)
1439 refresh_pane();
1440 add_hi(cpane, cpane->hdir - 1);
1441 print_prompt("Cancel");
1442 sl = -1;
1445 static void
1446 selall(void)
1448 int i;
1449 for (i = 0; i < cpane->dirc; i++) {
1450 cpane->selection[i] = i + 1;
1452 selref();
1455 static void
1456 selcalc(void)
1458 selection_size = 0;
1459 int j;
1461 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1462 if (cpane->selection[j] != 0)
1463 selection_size++;
1464 else
1465 break;
1469 static void
1470 free_files(void)
1472 for (size_t i = 0; i < selection_size; i++) {
1473 free(files[i]);
1474 files[i] = NULL;
1476 free(files);
1477 files = NULL;
1480 static void
1481 init_files(void)
1483 if (files != NULL)
1484 free_files();
1486 selcalc();
1487 files = ecalloc(selection_size, sizeof(char*));
1489 for (size_t i = 0; i < selection_size; i++) {
1490 files[i] = ecalloc(MAX_P, sizeof(char));
1491 char *tmp = get_path_hdir(cpane->selection[i]);
1492 strcpy(files[i], tmp);
1493 free(tmp);
1497 static void
1498 selynk(void)
1500 init_files();
1501 if (listdir(AddHi, NULL) < 0)
1502 print_error(strerror(errno));
1503 print_status(cprompt, "%d files are yanked", selection_size);
1504 sl = -1;
1507 static void
1508 seldel(void)
1510 char *confirmation;
1511 size_t i;
1513 confirmation = ecalloc((size_t)2, sizeof(char));
1514 if ((get_usrinput(
1515 confirmation, (size_t)2,"delete directory (Y) ?") < 0) ||
1516 (strcmp(confirmation, "Y") != 0)) {
1517 free(confirmation);
1518 if (listdir(AddHi, NULL) < 0)
1519 print_error(strerror(errno));
1520 sl = -1;
1521 return;
1523 free(confirmation);
1525 init_files();
1526 for (i = 0; i < selection_size; i++) {
1527 if (delent(files[i]) < 0)
1528 print_error(strerror(errno));
1531 print_status(cprompt, "%d files are deleted", selection_size);
1532 free_files();
1533 sl = -1;
1536 static void
1537 selpst(void)
1539 if (files == NULL) {
1540 return;
1543 size_t i;
1545 for (i = 0; i < selection_size; i++) {
1546 char *cp_cmd[] = { "cp", "-rf", files[i], cpane->dirn, NULL };
1547 spawn(cp_cmd, NULL);
1550 free_files();
1551 print_status(cprompt, "%d files are copied", selection_size);
1555 static void
1556 selmv(void)
1558 if (files == NULL) {
1559 return;
1562 size_t i;
1564 for (i = 0; i < selection_size; i++) {
1565 char *mv_cmd[] = { "mv", files[i], cpane->dirn, NULL };
1566 spawn(mv_cmd, NULL);
1569 free_files();
1570 print_status(cprompt, "%d files are copied", selection_size);
1574 static void
1575 selrename(void)
1577 init_files();
1578 for (size_t i = 0; i < selection_size; i++) {
1579 xrename(files[i]);
1581 if (listdir(AddHi, NULL) < 0)
1582 print_error(strerror(errno));
1583 free_files();
1584 sl = -1;
1587 static void
1588 xrename(char *path)
1590 char *new_name;
1591 new_name = ecalloc(MAX_N, sizeof(char));
1593 if (get_usrinput(new_name, MAX_N, "rename: %s", basename(path)) < 0) {
1594 free(new_name);
1595 return;
1597 char *rename_cmd[] = {"mv", path, new_name, NULL};
1599 if (spawn(rename_cmd, NULL) < 0)
1600 print_error(strerror(errno));
1602 free(new_name);
1605 static char*
1606 get_path_hdir(int Ndir)
1608 char *fullpath;
1609 fullpath = ecalloc(MAX_P, sizeof(char));
1610 strcpy(fullpath, cpane->dirn);
1611 strcat(fullpath, "/");
1612 strcat(fullpath, cpane->direntr[Ndir-1].name);
1614 return fullpath;
1617 static void
1618 rname(void)
1620 if (cpane->selection != NULL) {
1621 free(cpane->selection);
1622 cpane->selection = NULL;
1624 cpane->selection = ecalloc(2, sizeof(size_t));
1625 cpane->selection[0] = cpane->hdir;
1626 selrename();
1629 static void
1630 yank(void)
1632 if (cpane->selection != NULL) {
1633 free(cpane->selection);
1634 cpane->selection = NULL;
1636 cpane->selection = ecalloc(2, sizeof(size_t));
1637 cpane->selection[0] = cpane->hdir;
1638 selynk();
1641 static void
1642 switch_pane(void)
1644 if (cpane->dirc > 0)
1645 rm_hi(cpane, cpane->hdir - 1);
1646 if (cpane == &pane_l)
1647 cpane = &pane_r;
1648 else if (cpane == &pane_r)
1649 cpane = &pane_l;
1650 if (cpane->dirc > 0) {
1651 add_hi(cpane, cpane->hdir - 1);
1652 print_info();
1653 } else {
1654 clear_status();
1658 static void
1659 quit(void)
1661 if (sl == -1) { /* check if selection was allocated */
1662 free(cpane->selection);
1663 if (files != NULL)
1664 free_files();
1666 free(pane_l.direntr);
1667 free(pane_r.direntr);
1668 fsev_shdn();
1669 tb_shutdown();
1670 exit(EXIT_SUCCESS);
1673 static void
1674 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1676 size_t i;
1677 ssize_t b;
1679 for (i = 0; i < max_keys; i++) {
1680 if (event->ch != 0) {
1681 if (event->ch == key[i].evkey.ch) {
1682 key[i].func();
1683 return;
1685 } else if (event->key != 0) {
1686 if (event->key == key[i].evkey.key) {
1687 key[i].func();
1688 return;
1693 /* bookmarks */
1694 b = findbm(event->ch);
1695 if (b < 0)
1696 return;
1697 rmwatch(cpane);
1698 strcpy(cpane->dirn, bmarks[b].path);
1699 cpane->firstrow = 0;
1700 cpane->parent_row = 1;
1701 cpane->hdir = 1;
1702 if (listdir(AddHi, NULL) < 0)
1703 print_error(strerror(errno));
1706 static void
1707 start_ev(void)
1709 struct tb_event ev;
1711 for (;;) {
1712 int t = tb_peek_event(&ev, 2000);
1713 if (t < 0) {
1714 tb_shutdown();
1715 return;
1718 if (t == 1) /* keyboard event */
1719 grabkeys(&ev, nkeys, nkeyslen);
1720 else if (t == 2) /* resize event */
1721 t_resize();
1722 else if (t == 0) /* filesystem event */
1723 if (read_events() > 0)
1724 if (listdir(AddHi, NULL) < 0)
1725 print_error(strerror(errno));
1727 tb_present();
1728 continue;
1730 tb_shutdown();
1733 static void
1734 refresh_pane(void)
1736 size_t y, dyn_max, start_from;
1737 int width;
1738 width = (twidth / 2) - 4;
1739 Cpair col;
1741 y = 1;
1742 start_from = cpane->firstrow;
1743 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1745 /* print each entry in directory */
1746 while (start_from < dyn_max) {
1747 get_hicol(&col, cpane->direntr[start_from].mode);
1748 print_row(cpane, start_from, col);
1749 start_from++;
1750 y++;
1753 if (cpane->dirc > 0)
1754 print_info();
1755 else
1756 clear_status();
1758 /* print current directory title */
1759 cpane->dircol.fg |= TB_BOLD;
1760 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1763 static void
1764 set_direntr(struct dirent *entry, DIR *dir)
1766 int i;
1767 struct stat status;
1769 i = 0;
1770 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1771 while ((entry = readdir(dir)) != 0) {
1772 if ((strcmp(entry->d_name, ".") == 0 ||
1773 strcmp(entry->d_name, "..") == 0))
1774 continue;
1776 char *full = get_fullpath(cpane->dirn, entry->d_name);
1777 strcpy(cpane->direntr[i].name, full);
1778 if (lstat(full, &status) == 0) {
1779 cpane->direntr[i].size = status.st_size;
1780 cpane->direntr[i].mode = status.st_mode;
1781 cpane->direntr[i].group = status.st_gid;
1782 cpane->direntr[i].user = status.st_uid;
1783 cpane->direntr[i].td = status.st_mtime;
1785 i++;
1786 free(full);
1787 // }
1789 cpane->dirc = i;
1792 static int
1793 listdir(int hi, char *filter)
1795 DIR *dir;
1796 struct dirent *entry;
1797 int width;
1798 size_t i;
1799 int filtercount = 0;
1800 size_t oldc = cpane->dirc;
1802 width = (twidth / 2) - 4;
1803 cpane->dirc = 0;
1804 i = 0;
1806 dir = opendir(cpane->dirn);
1807 if (dir == NULL)
1808 return -1;
1810 /* get content and filter sum */
1811 while ((entry = readdir(dir)) != 0) {
1812 if (filter != NULL) {
1813 if (strstr(entry->d_name, filter) != NULL)
1814 filtercount++;
1815 } else { /* no filter */
1816 cpane->dirc++;
1820 if (filter == NULL) {
1821 clear_pane();
1822 cpane->dirc -= 2;
1825 if (filter != NULL) {
1826 if (filtercount > 0) {
1827 cpane->dirc -= 2;
1828 cpane->dirc = filtercount;
1829 clear_pane();
1830 cpane->hdir = 1;
1831 } else if (filtercount == 0) {
1832 if (closedir(dir) < 0)
1833 return -1;
1834 cpane->dirc = oldc;
1835 return -1;
1839 /* print current directory title */
1840 cpane->dircol.fg |= TB_BOLD;
1841 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1843 if (addwatch() < 0)
1844 print_error("can't add watch");
1846 /* empty directory */
1847 if (cpane->dirc == 0) {
1848 clear_status();
1849 if (closedir(dir) < 0)
1850 return -1;
1851 return 0;
1854 rewinddir(dir); /* reset position */
1855 set_direntr(entry, dir); /* create array of entries */
1856 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1857 refresh_pane();
1859 if (hi == AddHi)
1860 add_hi(cpane, cpane->hdir - 1);
1862 if (closedir(dir) < 0)
1863 return -1;
1864 return 0;
1867 static void
1868 t_resize(void)
1870 /* TODO need refactoring */
1871 tb_clear();
1872 draw_frame();
1873 pane_r.dirx = (twidth / 2) + 2;
1875 if (cpane == &pane_l) {
1876 cpane = &pane_r;
1877 refresh_pane();
1878 cpane = &pane_l;
1879 refresh_pane();
1880 if (cpane->dirc > 0)
1881 add_hi(&pane_l, pane_l.hdir - 1);
1882 } else if (cpane == &pane_r) {
1883 cpane = &pane_l;
1884 refresh_pane();
1885 cpane = &pane_r;
1886 refresh_pane();
1887 if (cpane->dirc > 0)
1888 add_hi(&pane_r, pane_r.hdir - 1);
1891 tb_present();
1894 static void
1895 get_editor(void)
1897 editor[0] = getenv("EDITOR");
1898 editor[1] = NULL;
1900 if (editor[0] == NULL)
1901 editor[0] = fed;
1904 static void
1905 set_panes(void)
1907 char *home;
1908 char cwd[MAX_P];
1910 home = getenv("HOME");
1911 if (home == NULL)
1912 home = "/";
1913 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1914 strncpy(cwd, home, MAX_P);
1916 pane_l.pane_id = 0;
1917 pane_l.dirx = 2;
1918 pane_l.dircol = cpanell;
1919 pane_l.firstrow = 0;
1920 pane_l.direntr = ecalloc(0, sizeof(Entry));
1921 strcpy(pane_l.dirn, cwd);
1922 pane_l.hdir = 1;
1923 pane_l.inotify_wd = -1;
1924 pane_l.parent_row = 1;
1926 pane_r.pane_id = 1;
1927 pane_r.dirx = (twidth / 2) + 2;
1928 pane_r.dircol = cpanelr;
1929 pane_r.firstrow = 0;
1930 pane_r.direntr = ecalloc(0, sizeof(Entry));
1931 strcpy(pane_r.dirn, home);
1932 pane_r.hdir = 1;
1933 pane_r.inotify_wd = -1;
1934 pane_r.parent_row = 1;
1937 static void
1938 draw_frame(void)
1940 int i;
1941 theight = tb_height();
1942 twidth = tb_width();
1943 scrheight = theight - 2;
1945 /* 2 horizontal lines */
1946 for (i = 1; i < twidth - 1; ++i) {
1947 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1948 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1951 /* 3 vertical lines */
1952 for (i = 1; i < theight - 1; ++i) {
1953 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1954 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1955 cframe.bg);
1956 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1959 /* 4 corners */
1960 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1961 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1962 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1963 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1965 /* 2 middel top and bottom */
1966 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1967 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1970 static void
1971 start(void)
1973 if (tb_init() != 0)
1974 die("tb_init");
1975 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1976 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1977 die("output error");
1979 draw_frame();
1980 set_panes();
1981 get_editor();
1982 if (fsev_init() < 0)
1983 print_error(strerror(errno));
1984 cpane = &pane_r;
1985 if (listdir(NoHi, NULL) < 0)
1986 print_error(strerror(errno));
1987 cpane = &pane_l;
1988 if (listdir(AddHi, NULL) < 0)
1989 print_error(strerror(errno));
1990 tb_present();
1991 start_ev();
1995 main(int argc, char *argv[])
1997 #ifdef __OpenBSD__
1998 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1999 NULL) == -1)
2000 die("pledge");
2001 #endif /* __OpenBSD__ */
2002 if (argc == 1)
2003 start();
2004 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
2005 die("sfm-" VERSION);
2006 else
2007 die("usage: sfm [-v]");
2008 return 0;