[ref] remove float_to_string()
[sfm.git] / sfm.c
blobcf7d2039d33bb400ea3a536fec3921179a373b83
1 /* See LICENSE file for copyright and license details. */
3 #if defined(__linux__)
4 #define _GNU_SOURCE
5 #endif
6 #include <sys/types.h>
7 #include <sys/resource.h>
8 #include <sys/stat.h>
9 #include <sys/time.h>
10 #include <sys/wait.h>
11 #if defined(__linux__)
12 #include <sys/inotify.h>
13 #elif defined(__FreeBSD__) || defined(__NetBSD__) ||\
14 defined(__OpenBSD__) || defined(__APPLE__)
15 #include <sys/event.h>
16 #endif
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <grp.h>
22 #include <pwd.h>
23 #include <stdarg.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <libgen.h>
32 #include "termbox.h"
33 #include "util.h"
35 /* macros */
36 #define MAX_P 4096
37 #define MAX_N 255
38 #define MAX_USRI 32
39 #define MAX_EXT 4
40 #define CURSOR_NAME cpane->direntr[cpane->hdir - 1].name
42 /* enums */
43 enum { AddHi, NoHi }; /* add highlight in listdir */
45 /* typedef */
46 typedef struct {
47 char name[MAX_N];
48 gid_t group;
49 mode_t mode;
50 off_t size;
51 time_t td;
52 uid_t user;
53 } Entry;
55 typedef struct {
56 uint16_t fg;
57 uint16_t bg;
58 } Cpair;
60 typedef struct {
61 int pane_id;
62 char dirn[MAX_P]; // dir name cwd
63 char *filter;
64 Entry *direntr; // dir entries
65 int dirx; // pane cwd x pos
66 int dirc; // dir entries sum
67 int hdir; // highlighted dir
68 int firstrow;
69 int parent_firstrow;
70 int parent_row; // FIX
71 Cpair dircol;
72 int inotify_wd;
73 int event_fd;
74 } Pane;
76 typedef struct {
77 uint32_t ch;
78 char path[MAX_P];
79 } Bookmark;
81 typedef struct {
82 const char **ext;
83 size_t exlen;
84 const void *v;
85 } Rule;
87 typedef union {
88 uint16_t key; /* one of the TB_KEY_* constants */
89 uint32_t ch; /* unicode character */
90 } Evkey;
92 typedef struct {
93 const Evkey evkey;
94 void (*func)(void);
95 } Key;
97 /* function declarations */
98 static void print_tb(const char *, int, int, uint16_t, uint16_t);
99 static void printf_tb(int, int, Cpair, const char *, ...);
100 static void print_status(Cpair, const char *, ...);
101 static void print_xstatus(char, int);
102 static void print_error(char *);
103 static void print_prompt(char *);
104 static void print_info(void);
105 static void print_row(Pane *, size_t, Cpair);
106 static void clear(int, int, int, uint16_t);
107 static void clear_status(void);
108 static void clear_pane(void);
109 static void add_hi(Pane *, size_t);
110 static void rm_hi(Pane *, size_t);
111 static int check_dir(char *);
112 static mode_t chech_execf(mode_t);
113 static int sort_name(const void *const, const void *const);
114 static void get_dirp(char *);
115 static char *get_ext(char *);
116 static int get_fdt(char *, size_t, time_t);
117 static char *get_fgrp(gid_t, size_t);
118 static char *get_finfo(Entry *);
119 static char *get_fperm(mode_t);
120 static char *get_fsize(off_t);
121 static char *get_fullpath(char *, char *);
122 static char *get_fusr(uid_t, size_t);
123 static void get_dirsize(char *, off_t *);
124 static void get_hicol(Cpair *, mode_t);
125 static int delent(char *);
126 static void calcdir(void);
127 static void crnd(void);
128 static void crnf(void);
129 static void delfd(void);
130 static void mvbk(void);
131 static void mvbtm(void);
132 static void mvdwn(void);
133 static void mvdwns(void);
134 static void mvfor(void);
135 static void mvmid(void);
136 static void mvtop(void);
137 static void mvup(void);
138 static void mvups(void);
139 static void scrdwn(void);
140 static void scrdwns(void);
141 static void scrup(void);
142 static void scrups(void);
143 static int get_usrinput(char*, size_t, const char*, ...);
144 static int frules(char *);
145 static int spawn(const void *, char *);
146 static int opnf(char *);
147 static int fsev_init(void);
148 static int addwatch(void);
149 static int read_events(void);
150 static void rmwatch(Pane *);
151 static void fsev_shdn(void);
152 static ssize_t findbm(uint32_t);
153 static void start_filter(void);
154 static void start_vmode(void);
155 static void exit_vmode(void);
156 static void selup(void);
157 static void seldwn(void);
158 static void selall(void);
159 static void selref(void);
160 static void selynk(void);
161 static void selcalc(void);
162 static void paste(void);
163 static void selmv(void);
164 static void seldel(void);
165 static void init_files(void);
166 static void free_files(void);
167 static void yank(void);
168 static void rname(void);
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 *, char *);
175 static int listdir(int);
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 char yank_file[MAX_P];
188 static int *selection;
189 static int cont_vmode = 0;
190 static char **selected_files;
191 #if defined _SYS_INOTIFY_H
192 static int inotify_fd;
193 #elif defined _SYS_EVENT_H_
194 static int kq;
195 struct kevent evlist[2]; /* events we want to monitor */
196 struct kevent chlist[2]; /* events that were triggered */
197 static struct timespec gtimeout;
198 #endif
200 /* configuration, allows nested code to access above variables */
201 #include "config.h"
203 /* function implementations */
204 static void
205 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
207 while (*str != '\0') {
208 uint32_t uni = 0;
209 str += tb_utf8_char_to_unicode(&uni, str);
210 tb_change_cell(x, y, uni, fg, bg);
211 x++;
215 static void
216 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
218 char buf[4096];
219 va_list vl;
220 va_start(vl, fmt);
221 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
222 va_end(vl);
223 print_tb(buf, x, y, col.fg, col.bg);
226 static void
227 print_status(Cpair col, const char *fmt, ...)
229 char buf[256];
230 va_list vl;
231 va_start(vl, fmt);
232 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
233 va_end(vl);
234 clear_status();
235 print_tb(buf, 1, theight - 1, col.fg, col.bg);
238 static void
239 print_xstatus(char c, int x)
241 uint32_t uni = 0;
242 (void)tb_utf8_char_to_unicode(&uni, &c);
243 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
246 static void
247 print_error(char *errmsg)
249 print_status(cerr, errmsg);
252 static void
253 print_prompt(char *prompt)
255 print_status(cprompt, prompt);
258 static void
259 print_info(void)
261 char *fileinfo;
262 fileinfo = get_finfo(&cpane->direntr[cpane->hdir - 1]);
263 print_status(cstatus, "%d/%d %s", cpane->hdir, cpane->dirc, fileinfo);
264 free(fileinfo);
267 static void
268 print_row(Pane *pane, size_t entpos, Cpair col)
270 int x, y;
271 char *result;
272 char buf[MAX_P];
273 char lnk_full[MAX_P];
274 int width;
276 width = (twidth / 2) - 4;
277 result = basename(pane->direntr[entpos].name);
278 x = pane->dirx;
279 y = entpos - cpane->firstrow + 1;
281 if (S_ISLNK(pane->direntr[entpos].mode) &&
282 realpath(pane->direntr[entpos].name, buf) != NULL) {
283 strncpy(lnk_full, pane->direntr[entpos].name, MAX_N);
284 strcat(lnk_full, " -> ");
285 strncat(lnk_full, buf, MAX_N);
286 result = lnk_full;
289 printf_tb(x, y, col, "%*.*s", ~width, width, result);
292 static void
293 clear(int sx, int ex, int y, uint16_t bg)
295 /* clear line from to */
296 /* x = line number vertical */
297 /* y = column number horizontal */
298 int i;
299 for (i = sx; i < ex; i++) {
300 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
304 static void
305 clear_status(void)
307 clear(1, twidth - 1, theight - 1, cstatus.bg);
310 static void
311 clear_pane(void)
313 int i, ex, y;
314 y = 0, i = 0, ex = 0;
316 if (cpane->pane_id == pane_l.pane_id)
317 ex = (twidth / 2) - 1;
318 else if (cpane->pane_id == pane_r.pane_id)
319 ex = twidth - 1;
321 while (i < scrheight) {
322 clear(cpane->dirx, ex, y, TB_DEFAULT);
323 i++;
324 y++;
327 /* draw top line */
328 for (y = cpane->dirx; y < ex; ++y) {
329 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
333 static void
334 add_hi(Pane *pane, size_t entpos)
336 Cpair col;
337 get_hicol(&col, pane->direntr[entpos].mode);
338 col.fg |= TB_REVERSE | TB_BOLD;
339 col.bg |= TB_REVERSE;
340 print_row(pane, entpos, col);
343 static void
344 rm_hi(Pane *pane, size_t entpos)
346 Cpair col;
347 get_hicol(&col, pane->direntr[entpos].mode);
348 print_row(pane, entpos, col);
351 static int
352 check_dir(char *path)
354 DIR *dir;
355 dir = opendir(path);
357 if (dir == NULL) {
358 if (errno == ENOTDIR) {
359 return 1;
360 } else {
361 return -1;
365 if (closedir(dir) < 0)
366 return -1;
368 return 0;
371 static mode_t
372 chech_execf(mode_t mode)
374 if (S_ISREG(mode))
375 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
376 return 0;
379 static int
380 sort_name(const void *const A, const void *const B)
382 int result;
383 mode_t data1 = (*(Entry *)A).mode;
384 mode_t data2 = (*(Entry *)B).mode;
386 if (data1 < data2) {
387 return -1;
388 } else if (data1 == data2) {
389 result = strcmp((*(Entry *)A).name, (*(Entry *)B).name);
390 return result;
391 } else {
392 return 1;
397 static void
398 get_dirp(char *cdir)
400 int counter, len, i;
402 counter = 0;
403 len = strlen(cdir);
404 if (len ==1)
405 return;
407 for (i = len - 1; i > 1; i--) {
408 if (cdir[i] == '/')
409 break;
410 else
411 counter++;
414 cdir[len-counter-1] = '\0';
417 static char *
418 get_ext(char *str)
420 char *ext;
421 char dot;
422 size_t counter, len, i;
424 dot = '.';
425 counter = 0;
426 len = strlen(str);
428 for (i = len - 1; i > 0; i--) {
429 if (str[i] == dot) {
430 break;
431 } else {
432 counter++;
436 ext = ecalloc(counter + 1, sizeof(char));
437 strncpy(ext, &str[len - counter], counter);
438 return ext;
441 static int
442 get_fdt(char *result, size_t reslen, time_t status)
444 struct tm lt;
445 localtime_r(&status, &lt);
446 return strftime(result, reslen, dtfmt, &lt);
449 static char *
450 get_fgrp(gid_t status, size_t len)
452 char *result;
453 struct group *gr;
455 result = ecalloc(len, sizeof(char));
456 gr = getgrgid(status);
457 if (gr == NULL)
458 (void)snprintf(result, len - 1, "%u", status);
459 else
460 strncpy(result, gr->gr_name, len - 1);
462 return result;
465 static char *
466 get_finfo(Entry *cursor)
468 char *sz, *rst, *ur, *gr, *td, *prm;
469 size_t szlen, prmlen, urlen, grlen, tdlen, rstlen;
471 szlen = 9;
472 prmlen = 11;
473 urlen = grlen = tdlen = 32;
474 rstlen = szlen + prmlen + urlen + grlen + tdlen;
475 rst = ecalloc(rstlen, sizeof(char));
477 if (show_perm == 1) {
478 prm = get_fperm(cursor->mode);
479 strncpy(rst, prm, prmlen);
480 strcat(rst, " ");
481 free(prm);
484 if (show_ug == 1) {
485 ur = get_fusr(cursor->user, urlen);
486 gr = get_fgrp(cursor->group, grlen);
487 strncat(rst, ur, urlen);
488 strcat(rst, ":");
489 strncat(rst, gr, grlen);
490 strcat(rst, " ");
491 free(ur);
492 free(gr);
495 if (show_dt == 1) {
496 td = ecalloc(tdlen, sizeof(char));
497 if (get_fdt(td, tdlen, cursor->td) > 0) {
498 strncat(rst, td, tdlen);
499 strcat(rst, " ");
501 free(td);
504 if (show_size == 1 && S_ISREG(cursor->mode)) {
505 sz = get_fsize(cursor->size);
506 strncat(rst, sz, szlen);
507 free(sz);
510 return rst;
513 static char *
514 get_fperm(mode_t mode)
516 char *buf;
517 size_t i;
519 const char chars[] = "rwxrwxrwx";
520 buf = ecalloc(11, sizeof(char));
522 if (S_ISDIR(mode))
523 buf[0] = 'd';
524 else if (S_ISREG(mode))
525 buf[0] = '-';
526 else if (S_ISLNK(mode))
527 buf[0] = 'l';
528 else if (S_ISBLK(mode))
529 buf[0] = 'b';
530 else if (S_ISCHR(mode))
531 buf[0] = 'c';
532 else if (S_ISFIFO(mode))
533 buf[0] = 'p';
534 else if (S_ISSOCK(mode))
535 buf[0] = 's';
536 else
537 buf[0] = '?';
539 for (i = 1; i < 10; i++) {
540 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
542 buf[10] = '\0';
544 return buf;
547 static char *
548 get_fsize(off_t size)
550 char *result; /* need to be freed */
551 char hsize;
552 int result_len;
553 int lsize;
554 int counter;
556 counter = 0;
557 result_len = 6; /* 9999X/0 */
558 result = ecalloc(result_len, sizeof(char));
559 lsize = (int)size;
561 while (lsize >= 1000) {
562 lsize /= 1024;
563 ++counter;
566 switch (counter) {
567 case 0:
568 hsize = 'B';
569 break;
570 case 1:
571 hsize = 'K';
572 break;
573 case 2:
574 hsize = 'M';
575 break;
576 case 3:
577 hsize = 'G';
578 break;
579 case 4:
580 hsize = 'T';
581 break;
582 default:
583 hsize = '?';
586 (void)snprintf(result, result_len, "%d%c", lsize, hsize);
588 return result;
591 static char *
592 get_fullpath(char *first, char *second)
594 char *full_path;
595 size_t full_path_len;
597 full_path_len = strlen(first) + strlen(second) + 2;
598 full_path = ecalloc(full_path_len, sizeof(char));
600 if (strcmp(first, "/") == 0) {
601 (void)snprintf(full_path, full_path_len, "/%s", second);
603 } else {
604 (void)snprintf(full_path, full_path_len, "%s/%s", first,
605 second);
608 return full_path;
611 static char *
612 get_fusr(uid_t status, size_t len)
614 char *result;
615 struct passwd *pw;
617 result = ecalloc(len, sizeof(char));
618 pw = getpwuid(status);
619 if (pw == NULL)
620 (void)snprintf(result, len - 1, "%u", status);
621 else
622 strncpy(result, pw->pw_name, len - 1);
624 return result;
627 static void
628 get_dirsize(char *fullpath, off_t *fullsize)
630 DIR *dir;
631 char *ent_full;
632 mode_t mode;
633 struct dirent *entry;
634 struct stat status;
636 dir = opendir(fullpath);
637 if (dir == NULL) {
638 return;
641 while ((entry = readdir(dir)) != 0) {
642 if ((strcmp(entry->d_name, ".") == 0 ||
643 strcmp(entry->d_name, "..") == 0))
644 continue;
646 ent_full = get_fullpath(fullpath, entry->d_name);
647 if (lstat(ent_full, &status) == 0) {
648 mode = status.st_mode;
649 if (S_ISDIR(mode)) {
650 get_dirsize(ent_full, fullsize);
651 free(ent_full);
652 } else {
653 *fullsize += status.st_size;
654 free(ent_full);
659 closedir(dir);
660 clear_status();
663 static void
664 get_hicol(Cpair *col, mode_t mode)
666 *col = cfile;
667 if (S_ISDIR(mode))
668 *col = cdir;
669 else if (S_ISLNK(mode))
670 *col = cother;
671 else if (chech_execf(mode) > 0)
672 *col = cexec;
675 static int
676 delent(char *fullpath)
678 char *inp_conf;
679 int conf_len = 4;
680 char conf[] = "yes";
682 inp_conf = ecalloc(conf_len, sizeof(char));
683 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
684 (strncmp(inp_conf, conf, conf_len) != 0)) {
685 free(inp_conf);
686 return 1; /* canceled by user or wrong inp_conf */
688 free(inp_conf);
690 return spawn(rm_cmd, fullpath);
693 static void
694 calcdir(void)
696 off_t *fullsize;
697 char *csize;
698 char *result;
700 if (S_ISDIR(cpane->direntr[cpane->hdir - 1].mode)) {
701 fullsize = ecalloc(50, sizeof(off_t));
702 get_dirsize(CURSOR_NAME, fullsize);
703 csize = get_fsize(*fullsize);
704 result = get_finfo(&cpane->direntr[cpane->hdir - 1]);
706 clear_status();
707 print_status(cstatus, "%d/%d %s%s", cpane->hdir, cpane->dirc,
708 result, csize);
709 free(csize);
710 free(fullsize);
711 free(result);
715 static void
716 crnd(void)
718 char *user_input, *path;
719 size_t pathlen;
721 user_input = ecalloc(MAX_USRI, sizeof(char));
722 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
723 free(user_input);
724 return;
727 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
728 path = ecalloc(pathlen, sizeof(char));
729 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
730 free(user_input);
731 free(path);
732 return;
735 if (mkdir(path, ndir_perm) < 0)
736 print_error(strerror(errno));
738 free(user_input);
739 free(path);
742 static void
743 crnf(void)
745 char *user_input, *path;
746 size_t pathlen;
747 int rf;
749 user_input = ecalloc(MAX_USRI, sizeof(char));
750 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
751 free(user_input);
752 return;
755 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
756 path = ecalloc(pathlen, sizeof(char));
757 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
758 free(user_input);
759 free(path);
760 return;
763 rf = open(path, O_CREAT | O_EXCL, nf_perm);
765 if (rf < 0)
766 print_error(strerror(errno));
767 else
768 if (close(rf) < 0)
769 print_error(strerror(errno));
771 free(user_input);
772 free(path);
775 static void
776 delfd(void)
778 switch (delent(CURSOR_NAME)) {
779 case -1:
780 print_error(strerror(errno));
781 break;
782 case 0:
783 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
784 cpane->hdir--;
785 break;
789 static void
790 mvbk(void)
792 get_dirp(cpane->dirn);
793 if (check_dir(cpane->dirn) < 0) {
794 print_error(strerror(errno));
795 return;
798 rmwatch(cpane);
799 cpane->firstrow = cpane->parent_firstrow;
800 cpane->hdir = cpane->parent_row;
801 if (listdir(AddHi) < 0)
802 print_error(strerror(errno));
803 cpane->parent_firstrow = 0;
804 cpane->parent_row = 1;
807 static void
808 mvbtm(void)
810 if (cpane->dirc < 1)
811 return;
812 if (cpane->dirc > scrheight) {
813 rm_hi(cpane, cpane->hdir - 1);
814 cpane->hdir = cpane->dirc;
815 cpane->firstrow = cpane->dirc - scrheight + 1;
816 refresh_pane();
817 add_hi(cpane, cpane->hdir - 1);
818 } else {
819 rm_hi(cpane, cpane->hdir - 1);
820 cpane->hdir = cpane->dirc;
821 add_hi(cpane, cpane->hdir - 1);
823 print_info();
826 static void
827 mvdwn(void)
829 if (cpane->dirc < 1)
830 return;
831 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
832 rm_hi(cpane, cpane->hdir - 1);
833 cpane->hdir++;
834 add_hi(cpane, cpane->hdir - 1);
835 } else {
836 mvdwns(); /* scroll */
838 print_info();
841 static void
842 mvdwns(void)
844 int real;
845 real = cpane->hdir - 1 - cpane->firstrow;
847 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
848 cpane->firstrow++;
849 rm_hi(cpane, cpane->hdir - 1);
850 cpane->hdir++;
851 refresh_pane();
852 add_hi(cpane, cpane->hdir - 1);
853 } else if (cpane->hdir < cpane->dirc) {
854 rm_hi(cpane, cpane->hdir - 1);
855 cpane->hdir++;
856 add_hi(cpane, cpane->hdir - 1);
860 static void
861 mvfor(void)
863 rmwatch(cpane);
864 if (cpane->dirc < 1)
865 return;
866 int s;
868 switch (check_dir(CURSOR_NAME)) {
869 case 0:
870 strcpy(cpane->dirn, CURSOR_NAME);
871 cpane->parent_row = cpane->hdir;
872 cpane->parent_firstrow = cpane->firstrow;
873 cpane->hdir = 1;
874 cpane->firstrow = 0;
875 if (listdir(AddHi) < 0)
876 print_error(strerror(errno));
877 break;
878 case 1: /* not a directory open file */
879 tb_shutdown();
880 s = opnf(CURSOR_NAME);
881 if (tb_init() != 0)
882 die("tb_init");
883 t_resize();
884 if (s < 0)
885 print_error("process failed non-zero exit");
886 break;
887 case -1: /* failed to open directory */
888 print_error(strerror(errno));
892 static void
893 mvmid(void)
895 if (cpane->dirc < 1)
896 return;
897 rm_hi(cpane, cpane->hdir - 1);
898 if (cpane->dirc < scrheight / 2)
899 cpane->hdir = (cpane->dirc + 1) / 2;
900 else
901 cpane->hdir = (scrheight / 2) + cpane->firstrow;
902 add_hi(cpane, cpane->hdir - 1);
903 print_info();
906 static void
907 mvtop(void)
909 if (cpane->dirc < 1)
910 return;
911 if (cpane->dirc > scrheight) {
912 rm_hi(cpane, cpane->hdir - 1);
913 cpane->hdir = 1;
914 cpane->firstrow = 0;
915 refresh_pane();
916 add_hi(cpane, cpane->hdir - 1);
917 } else {
918 rm_hi(cpane, cpane->hdir - 1);
919 cpane->hdir = 1;
920 add_hi(cpane, cpane->hdir - 1);
921 print_info();
925 static void
926 mvup(void)
928 if (cpane->dirc < 1)
929 return;
930 if (cpane->dirc < scrheight && cpane->hdir > 1) {
931 rm_hi(cpane, cpane->hdir - 1);
932 cpane->hdir--;
933 add_hi(cpane, cpane->hdir - 1);
934 } else {
935 mvups(); /* scroll */
937 print_info();
940 static void
941 mvups(void)
943 size_t real;
944 real = cpane->hdir - 1 - cpane->firstrow;
946 if (cpane->firstrow > 0 && real < 1 + scrsp) {
947 cpane->firstrow--;
948 rm_hi(cpane, cpane->hdir - 1);
949 cpane->hdir--;
950 refresh_pane();
951 add_hi(cpane, cpane->hdir - 1);
952 } else if (cpane->hdir > 1) {
953 rm_hi(cpane, cpane->hdir - 1);
954 cpane->hdir--;
955 add_hi(cpane, cpane->hdir - 1);
959 static void
960 scrdwn(void)
962 if (cpane->dirc < 1)
963 return;
964 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
965 if (cpane->hdir < cpane->dirc - scrmv) {
966 rm_hi(cpane, cpane->hdir - 1);
967 cpane->hdir += scrmv;
968 add_hi(cpane, cpane->hdir - 1);
969 } else {
970 mvbtm();
972 } else {
973 scrdwns();
975 print_info();
978 static void
979 scrdwns(void)
981 int real, dynmv;
983 real = cpane->hdir - cpane->firstrow;
984 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
986 if (real + scrmv + 1 > scrheight &&
987 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
988 cpane->firstrow += dynmv;
989 rm_hi(cpane, cpane->hdir - 1);
990 cpane->hdir += scrmv;
991 refresh_pane();
992 add_hi(cpane, cpane->hdir - 1);
993 } else {
994 if (cpane->hdir < cpane->dirc - scrmv - 1) {
995 rm_hi(cpane, cpane->hdir - 1);
996 cpane->hdir += scrmv;
997 add_hi(cpane, cpane->hdir - 1);
998 } else {
999 mvbtm();
1004 static void
1005 scrup(void)
1007 if (cpane->dirc < 1)
1008 return;
1009 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1010 if (cpane->hdir > scrmv) {
1011 rm_hi(cpane, cpane->hdir - 1);
1012 cpane->hdir = cpane->hdir - scrmv;
1013 add_hi(cpane, cpane->hdir - 1);
1014 print_info();
1015 } else {
1016 mvtop();
1018 } else {
1019 scrups();
1023 static void
1024 scrups(void)
1026 int real, dynmv;
1027 real = cpane->hdir - cpane->firstrow;
1028 dynmv = MIN(cpane->firstrow, scrmv);
1030 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1031 cpane->firstrow -= dynmv;
1032 rm_hi(cpane, cpane->hdir - 1);
1033 cpane->hdir -= scrmv;
1034 refresh_pane();
1035 add_hi(cpane, cpane->hdir - 1);
1036 } else {
1037 if (cpane->hdir > scrmv + 1) {
1038 rm_hi(cpane, cpane->hdir - 1);
1039 cpane->hdir -= scrmv;
1040 add_hi(cpane, cpane->hdir - 1);
1041 } else {
1042 mvtop();
1047 static int
1048 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1050 int height = tb_height();
1051 size_t startat;
1052 struct tb_event fev;
1053 size_t counter = (size_t)1;
1054 char empty = ' ';
1055 int x = 0;
1056 int name_size = 0;
1057 char buf[256];
1059 clear_status();
1061 va_list vl;
1062 Cpair col;
1063 col = cprompt;
1064 va_start(vl, fmt);
1065 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1066 va_end(vl);
1067 print_tb(buf, 1, height-1, col.fg, col.bg);
1068 startat = name_size + 1;
1069 tb_set_cursor((int)(startat + 1), height-1);
1070 tb_present();
1072 while (tb_poll_event(&fev) != 0) {
1073 switch (fev.type) {
1074 case TB_EVENT_KEY:
1075 if (fev.key == TB_KEY_ESC) {
1076 tb_set_cursor(-1, -1);
1077 clear_status();
1078 return -1;
1081 if (fev.key == TB_KEY_BACKSPACE ||
1082 fev.key == TB_KEY_BACKSPACE2) {
1083 if (BETWEEN(counter, 2, sout)) {
1084 out[x - 1] = '\0';
1085 counter--;
1086 x--;
1087 print_xstatus(empty, startat + counter);
1088 tb_set_cursor(startat + counter,
1089 theight - 1);
1092 } else if (fev.key == TB_KEY_ENTER) {
1093 tb_set_cursor(-1, -1);
1094 out[counter - 1] = '\0';
1095 return 0;
1097 } else {
1098 if (counter < sout) {
1099 print_xstatus((char)fev.ch,
1100 (startat + counter));
1101 out[x] = (char)fev.ch;
1102 tb_set_cursor((startat + counter + 1),
1103 theight - 1);
1104 counter++;
1105 x++;
1109 tb_present();
1110 break;
1112 default:
1113 return -1;
1117 return -1;
1120 static int
1121 frules(char *ex)
1123 size_t c, d;
1125 for (c = 0; c < LEN(rules); c++)
1126 for (d = 0; d < rules[c].exlen; d++)
1127 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1128 return c;
1129 return -1;
1132 static int
1133 spawn(const void *v, char *fn)
1135 int ws, x, argc, fd;
1136 pid_t pid, r;
1138 x = 0;
1139 argc = 0;
1141 /* count args */
1142 while (((char **)v)[x++] != NULL)
1143 argc++;
1145 char *argv[argc + 2];
1146 for ( x = 0; x < argc; x++)
1147 argv[x] = ((char **)v)[x];
1149 argv[argc] = fn;
1150 argv[argc + 1] = NULL;
1152 pid = fork();
1153 switch (pid) {
1154 case -1:
1155 return -1;
1156 case 0:
1157 fd = open("/dev/null",O_WRONLY);
1158 dup2(fd, STDERR_FILENO);
1159 execvp(argv[0], argv);
1160 exit(EXIT_SUCCESS);
1161 close(fd);
1162 default:
1163 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1164 continue;
1165 if (r == -1)
1166 return -1;
1167 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1168 return -1;
1170 return 0;
1173 static int
1174 opnf(char *fn)
1176 char *ex;
1177 int c;
1179 ex = get_ext(fn);
1180 c = frules(ex);
1181 free(ex);
1183 if (c < 0) /* extension not found open in editor */
1184 return spawn(editor, fn);
1185 else
1186 return spawn((char **)rules[c].v, fn);
1189 static int
1190 fsev_init(void)
1192 #if defined _SYS_INOTIFY_H
1193 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1194 if (inotify_fd < 0)
1195 return -1;
1196 #elif defined _SYS_EVENT_H_
1197 kq = kqueue();
1198 if (kq < 0)
1199 return -1;
1200 #endif
1201 return 0;
1204 static int
1205 addwatch(void)
1207 #if defined _SYS_INOTIFY_H
1208 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1209 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1210 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1211 #elif defined _SYS_EVENT_H_
1212 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1213 if (cpane->event_fd < 0)
1214 return cpane->event_fd;
1215 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1216 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1217 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1218 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1219 return 0;
1220 #endif
1223 static int
1224 read_events(void)
1226 #if defined _SYS_INOTIFY_H
1227 char *p;
1228 ssize_t r;
1229 struct inotify_event *event;
1230 const size_t events = 32;
1231 const size_t evbuflen =
1232 events * (sizeof(struct inotify_event) + MAX_N + 1);
1233 char buf[evbuflen];
1235 if (cpane->inotify_wd < 0)
1236 return -1;
1237 r = read(inotify_fd, buf, evbuflen);
1238 if (r <= 0)
1239 return r;
1241 for (p = buf; p < buf + r;) {
1242 event = (struct inotify_event *)p;
1243 if (!event->wd)
1244 break;
1245 if (event->mask) {
1246 return r;
1249 p += sizeof(struct inotify_event) + event->len;
1251 #elif defined _SYS_EVENT_H_
1252 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1253 #endif
1254 return -1;
1257 static void
1258 rmwatch(Pane *pane)
1260 #if defined _SYS_INOTIFY_H
1261 if (pane->inotify_wd >= 0)
1262 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1263 #elif defined _SYS_EVENT_H_
1264 close(pane->event_fd);
1265 return;
1266 #endif
1269 static void
1270 fsev_shdn(void)
1272 rmwatch(&pane_l);
1273 rmwatch(&pane_r);
1274 #if defined _SYS_INOTIFY_H
1275 close(inotify_fd);
1276 #elif defined _SYS_EVENT_H_
1277 close(kq);
1278 #endif
1281 static ssize_t
1282 findbm(uint32_t event)
1284 ssize_t i;
1286 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1287 if (event == bmarks[i].ch) {
1288 if (check_dir(bmarks[i].path) != 0) {
1289 print_error(strerror(errno));
1290 return -1;
1292 return i;
1295 return -1;
1298 static void
1299 start_filter(void)
1301 if (cpane->dirc < 1)
1302 return;
1303 char *user_input;
1304 user_input = ecalloc(MAX_USRI, sizeof(char));
1305 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1306 free(user_input);
1307 return;
1309 cpane->filter = user_input;
1310 if (listdir(AddHi) < 0)
1311 print_error("no match");
1312 cpane->filter = NULL;
1313 free(user_input);
1316 static void
1317 start_vmode(void)
1319 struct tb_event fev;
1320 if (selection != NULL) {
1321 free(selection);
1322 selection = NULL;
1325 selection = ecalloc(cpane->dirc, sizeof(size_t));
1326 selection[0] = cpane->hdir;
1327 cont_vmode = 0;
1328 print_prompt("-- VISUAL --");
1329 tb_present();
1330 while (tb_poll_event(&fev) != 0) {
1331 switch (fev.type) {
1332 case TB_EVENT_KEY:
1333 grabkeys(&fev, skeys, skeyslen);
1334 if (cont_vmode == -1)
1335 return;
1336 tb_present();
1337 break;
1342 static void
1343 exit_vmode(void)
1345 refresh_pane();
1346 add_hi(cpane, cpane->hdir - 1);
1347 cont_vmode = -1;
1350 static void
1351 selup(void)
1353 mvup();
1354 print_prompt("-- VISUAL --");
1355 int index = abs(cpane->hdir - selection[0]);
1357 if (cpane->hdir < selection[0]) {
1358 selection[index] = cpane->hdir;
1359 add_hi(cpane, selection[index]);
1360 } else if (index < cpane->dirc) {
1361 selection[index + 1] = 0;
1363 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1364 selref();
1368 static void
1369 seldwn(void)
1371 mvdwn();
1372 print_prompt("-- VISUAL --");
1373 int index = abs(cpane->hdir - selection[0]);
1375 if (cpane->hdir > selection[0]) {
1376 selection[index] = cpane->hdir;
1377 add_hi(cpane, selection[index] - 2);
1378 } else {
1379 selection[index + 1] = 0;
1381 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1382 selref();
1386 static void
1387 selall(void)
1389 int i;
1390 for (i = 0; i < cpane->dirc; i++) {
1391 selection[i] = i + 1;
1393 selref();
1396 static void
1397 selref(void)
1399 int i;
1400 for (i = 0; i < cpane->dirc; i++) {
1401 if (selection[i] < (scrheight + cpane->firstrow) && selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1402 add_hi(cpane, selection[i] - 1);
1407 static void
1408 selcalc(void)
1410 int j;
1411 selection_size = 0;
1413 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1414 if (selection[j] != 0)
1415 selection_size++;
1416 else
1417 break;
1421 static void
1422 free_files(void)
1424 size_t i;
1426 if (selected_files != NULL) {
1427 for (i = 0; i < selection_size; i++) {
1428 free(selected_files[i]);
1429 selected_files[i] = NULL;
1431 free(selected_files);
1432 selected_files = NULL;
1436 static void
1437 init_files(void)
1439 size_t i;
1440 free_files();
1442 selcalc();
1443 selected_files = ecalloc(selection_size, sizeof(char*));
1445 for (i = 0; i < selection_size; i++) {
1446 selected_files[i] = ecalloc(MAX_P, sizeof(char));
1447 // strcpy(selected_files[i], "\"");
1448 strcpy(selected_files[i], cpane->direntr[selection[i] -1].name);
1449 // strcat(selected_files[i], "\"");
1453 static void
1454 selynk(void)
1456 init_files();
1457 refresh_pane();
1458 add_hi(cpane, cpane->hdir -1);
1459 print_status(cprompt, "%zu files are yanked", selection_size);
1460 cont_vmode = -1;
1463 static void
1464 seldel(void)
1466 char *inp_conf;
1467 int conf_len = 4;
1468 char conf[] = "yes";
1469 size_t i;
1471 inp_conf = ecalloc(conf_len, sizeof(char));
1472 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1473 (strncmp(inp_conf, conf, conf_len) != 0)) {
1474 free(inp_conf);
1475 return; /* canceled by user or wrong inp_conf */
1477 free(inp_conf);
1479 init_files();
1480 for (i = 0; i < selection_size; i++) {
1481 spawn(rm_cmd, selected_files[i]);
1484 cpane->hdir = cpane->dirc - selection_size;
1485 print_status(cprompt, "%zu files are deleted", selection_size);
1486 free_files();
1487 cont_vmode = -1;
1490 static void
1491 paste(void)
1493 size_t i;
1494 if (strlen(yank_file) != 0) {
1495 print_status(cprompt, "coping");
1496 if (spawn(cp_cmd, cpane->dirn) != 0)
1497 print_error("coping failed");
1498 else
1499 print_status(cprompt, "file copied");
1500 yank_file[0] = '\0'; /* set yank_file len 0 */
1501 return;
1504 print_error("nothing to paste");
1506 if (selected_files == NULL)
1507 return;
1509 for (i = 0; i < selection_size; i++) {
1510 char *selcp_cmd[] = { "cp", "-r", selected_files[i], cpane->dirn, NULL };
1511 spawn(selcp_cmd,NULL);
1513 print_status(cprompt, "%zu files are copied", selection_size);
1514 free_files();
1517 static void
1518 selmv(void)
1520 size_t i;
1522 if (strlen(yank_file) != 0) {
1523 print_status(cprompt, "moving");
1524 if (spawn(mv_cmd, cpane->dirn) != 0)
1525 print_error("moving failed");
1526 else
1527 print_status(cprompt, "file moved");
1528 yank_file[0] = '\0'; /* set yank_file len 0 */
1529 return;
1532 print_error("nothing to move");
1534 if (selected_files == NULL)
1535 return;
1537 for (i = 0; i < selection_size; i++) {
1538 char *selmv_cmd[] = { "mv", selected_files[i], cpane->dirn, NULL };
1539 spawn(selmv_cmd,NULL);
1541 print_status(cprompt, "%zu files are moved", selection_size);
1542 free_files();
1545 static void
1546 rname(void)
1548 char new_name[MAX_P];
1549 char *input_name;
1551 input_name = ecalloc(MAX_N, sizeof(char));
1553 if (get_usrinput(input_name, MAX_N, "rename: %s", basename(CURSOR_NAME)) < 0) {
1554 free(input_name);
1555 return;
1558 strcpy(new_name, cpane->dirn);
1559 strcat(new_name, "/");
1560 strcat(new_name, input_name);
1562 char *rename_cmd[] = { "mv", CURSOR_NAME, new_name, NULL };
1563 if (spawn(rename_cmd, NULL) < 0)
1564 print_error(strerror(errno));
1566 free(input_name);
1569 static void
1570 yank(void)
1572 strncpy(yank_file, CURSOR_NAME, MAX_P);
1573 print_status(cprompt, "1 file is yanked", selection_size);
1577 static void
1578 switch_pane(void)
1580 if (cpane->dirc > 0)
1581 rm_hi(cpane, cpane->hdir - 1);
1582 if (cpane == &pane_l)
1583 cpane = &pane_r;
1584 else if (cpane == &pane_r)
1585 cpane = &pane_l;
1586 if (cpane->dirc > 0) {
1587 add_hi(cpane, cpane->hdir - 1);
1588 print_info();
1589 } else {
1590 clear_status();
1594 static void
1595 quit(void)
1597 if (cont_vmode == -1) { /* check if selection was allocated */
1598 free(selection);
1599 if (selected_files != NULL)
1600 free_files();
1602 free(pane_l.direntr);
1603 free(pane_r.direntr);
1604 fsev_shdn();
1605 tb_shutdown();
1606 exit(EXIT_SUCCESS);
1609 static void
1610 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1612 size_t i;
1613 ssize_t b;
1615 for (i = 0; i < max_keys; i++) {
1616 if (event->ch != 0) {
1617 if (event->ch == key[i].evkey.ch) {
1618 key[i].func();
1619 return;
1621 } else if (event->key != 0) {
1622 if (event->key == key[i].evkey.key) {
1623 key[i].func();
1624 return;
1629 /* bookmarks */
1630 b = findbm(event->ch);
1631 if (b < 0)
1632 return;
1633 rmwatch(cpane);
1634 strcpy(cpane->dirn, bmarks[b].path);
1635 cpane->firstrow = 0;
1636 cpane->parent_row = 1;
1637 cpane->hdir = 1;
1638 if (listdir(AddHi) < 0)
1639 print_error(strerror(errno));
1642 static void
1643 start_ev(void)
1645 struct tb_event ev;
1647 for (;;) {
1648 int t = tb_peek_event(&ev, 2000);
1649 if (t < 0) {
1650 tb_shutdown();
1651 return;
1654 if (t == 1) /* keyboard event */
1655 grabkeys(&ev, nkeys, nkeyslen);
1656 else if (t == 2) /* resize event */
1657 t_resize();
1658 else if (t == 0) /* filesystem event */
1659 if (read_events() > 0) { /* TODO need refactoring */
1660 if (cpane == &pane_l) {
1661 cpane = &pane_r;
1662 if (listdir(NoHi) < 0)
1663 print_error(strerror(errno));
1664 cpane = &pane_l;
1665 if (listdir(AddHi) < 0)
1666 print_error(strerror(errno));
1667 } else if (cpane == &pane_r) {
1668 cpane = &pane_l;
1669 if (listdir(NoHi) < 0)
1670 print_error(strerror(errno));
1671 cpane = &pane_r;
1672 if (listdir(AddHi) < 0)
1673 print_error(strerror(errno));
1677 tb_present();
1678 continue;
1680 tb_shutdown();
1683 static void
1684 refresh_pane(void)
1686 size_t y, dyn_max, start_from;
1687 int width;
1688 width = (twidth / 2) - 4;
1689 Cpair col;
1691 y = 1;
1692 start_from = cpane->firstrow;
1693 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1695 /* print each entry in directory */
1696 while (start_from < dyn_max) {
1697 get_hicol(&col, cpane->direntr[start_from].mode);
1698 print_row(cpane, start_from, col);
1699 start_from++;
1700 y++;
1703 if (cpane->dirc > 0)
1704 print_info();
1705 else
1706 clear_status();
1708 /* print current directory title */
1709 cpane->dircol.fg |= TB_BOLD;
1710 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1713 static void
1714 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1716 int i;
1717 char *tmpfull;
1718 struct stat status;
1721 #define ADD_ENTRY \
1722 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1723 strcpy(cpane->direntr[i].name, tmpfull); \
1724 if (lstat(tmpfull, &status) == 0) { \
1725 cpane->direntr[i].size = status.st_size; \
1726 cpane->direntr[i].mode = status.st_mode; \
1727 cpane->direntr[i].group = status.st_gid; \
1728 cpane->direntr[i].user = status.st_uid; \
1729 cpane->direntr[i].td = status.st_mtime; \
1730 }i++;free(tmpfull);
1732 i = 0;
1733 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1734 while ((entry = readdir(dir)) != 0) {
1735 if ((strcmp(entry->d_name, ".") == 0 ||
1736 strcmp(entry->d_name, "..") == 0))
1737 continue;
1739 if (filter != NULL) {
1740 if (strcasestr(entry->d_name, filter) != NULL) {
1741 ADD_ENTRY
1745 if (filter == NULL) {
1746 ADD_ENTRY
1750 cpane->dirc = i;
1753 static int
1754 listdir(int hi)
1756 DIR *dir;
1757 struct dirent *entry;
1758 int width;
1759 int filtercount = 0;
1760 size_t oldc = cpane->dirc;
1762 width = (twidth / 2) - 4;
1763 cpane->dirc = 0;
1765 dir = opendir(cpane->dirn);
1766 if (dir == NULL)
1767 return -1;
1769 /* get content and filter sum */
1770 while ((entry = readdir(dir)) != 0) {
1771 if (cpane->filter != NULL) {
1772 if (strcasestr(entry->d_name, cpane->filter) != NULL)
1773 filtercount++;
1774 } else { /* no filter */
1775 cpane->dirc++;
1779 if (cpane->filter == NULL) {
1780 clear_pane();
1781 cpane->dirc -= 2;
1784 if (cpane->filter != NULL) {
1785 if (filtercount > 0) {
1786 cpane->dirc -= 2;
1787 cpane->dirc = filtercount;
1788 clear_pane();
1789 cpane->hdir = 1;
1790 } else if (filtercount == 0) {
1791 if (closedir(dir) < 0)
1792 return -1;
1793 cpane->dirc = oldc;
1794 return -1;
1798 /* print current directory title */
1799 cpane->dircol.fg |= TB_BOLD;
1800 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1802 if (cpane->filter == NULL) /* dont't watch when filtering */
1803 if (addwatch() < 0)
1804 print_error("can't add watch");
1806 /* empty directory */
1807 if (cpane->dirc == 0) {
1808 clear_status();
1809 if (closedir(dir) < 0)
1810 return -1;
1811 return 0;
1814 rewinddir(dir); /* reset position */
1815 set_direntr(entry, dir, cpane->filter); /* create array of entries */
1816 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1817 refresh_pane();
1819 if (hi == AddHi)
1820 add_hi(cpane, cpane->hdir - 1);
1822 if (closedir(dir) < 0)
1823 return -1;
1824 return 0;
1827 static void
1828 t_resize(void)
1830 /* TODO need refactoring */
1831 tb_clear();
1832 draw_frame();
1833 pane_r.dirx = (twidth / 2) + 2;
1835 if (cpane == &pane_l) {
1836 cpane = &pane_r;
1837 refresh_pane();
1838 cpane = &pane_l;
1839 refresh_pane();
1840 if (cpane->dirc > 0)
1841 add_hi(&pane_l, pane_l.hdir - 1);
1842 } else if (cpane == &pane_r) {
1843 cpane = &pane_l;
1844 refresh_pane();
1845 cpane = &pane_r;
1846 refresh_pane();
1847 if (cpane->dirc > 0)
1848 add_hi(&pane_r, pane_r.hdir - 1);
1851 tb_present();
1854 static void
1855 get_editor(void)
1857 editor[0] = getenv("EDITOR");
1858 editor[1] = NULL;
1860 if (editor[0] == NULL)
1861 editor[0] = fed;
1864 static void
1865 set_panes(void)
1867 char *home;
1868 char cwd[MAX_P];
1870 home = getenv("HOME");
1871 if (home == NULL)
1872 home = "/";
1873 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1874 strncpy(cwd, home, MAX_P);
1876 pane_l.pane_id = 0;
1877 pane_l.dirx = 2;
1878 pane_l.dircol = cpanell;
1879 pane_l.firstrow = 0;
1880 pane_l.direntr = ecalloc(0, sizeof(Entry));
1881 strcpy(pane_l.dirn, cwd);
1882 pane_l.hdir = 1;
1883 pane_l.inotify_wd = -1;
1884 pane_l.parent_row = 1;
1886 pane_r.pane_id = 1;
1887 pane_r.dirx = (twidth / 2) + 2;
1888 pane_r.dircol = cpanelr;
1889 pane_r.firstrow = 0;
1890 pane_r.direntr = ecalloc(0, sizeof(Entry));
1891 strcpy(pane_r.dirn, home);
1892 pane_r.hdir = 1;
1893 pane_r.inotify_wd = -1;
1894 pane_r.parent_row = 1;
1897 static void
1898 draw_frame(void)
1900 int i;
1901 theight = tb_height();
1902 twidth = tb_width();
1903 scrheight = theight - 2;
1905 /* 2 horizontal lines */
1906 for (i = 1; i < twidth - 1; ++i) {
1907 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1908 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1911 /* 4 vertical lines */
1912 for (i = 1; i < theight - 1; ++i) {
1913 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1914 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1915 cframe.bg);
1916 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1917 cframe.bg);
1918 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1921 /* 4 corners */
1922 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1923 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1924 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1925 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1927 /* 2 middel top and bottom */
1928 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1929 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1932 static void
1933 start(void)
1935 if (tb_init() != 0)
1936 die("tb_init");
1937 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1938 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1939 die("output error");
1941 draw_frame();
1942 set_panes();
1943 get_editor();
1944 if (fsev_init() < 0)
1945 print_error(strerror(errno));
1946 cpane = &pane_r;
1947 if (listdir(NoHi) < 0)
1948 print_error(strerror(errno));
1949 cpane = &pane_l;
1950 if (listdir(AddHi) < 0)
1951 print_error(strerror(errno));
1952 tb_present();
1953 start_ev();
1957 main(int argc, char *argv[])
1959 #ifdef __OpenBSD__
1960 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
1961 NULL) == -1)
1962 die("pledge");
1963 #endif /* __OpenBSD__ */
1964 if (argc == 1)
1965 start();
1966 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
1967 die("sfm-" VERSION);
1968 else
1969 die("usage: sfm [-v]");
1970 return 0;