[fix] buffer overflow at realpath()
[sfm.git] / sfm.c
blobbe0db1dae3223dcd4e784a9f4a5af7a11c8e8754
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 void float_to_string(float, char *);
112 static int check_dir(char *);
113 static mode_t chech_execf(mode_t);
114 static int sort_name(const void *const, const void *const);
115 static void get_dirp(char *);
116 static char *get_ext(char *);
117 static int get_fdt(char *, size_t, time_t);
118 static char *get_fgrp(gid_t, size_t);
119 static char *get_finfo(Entry *);
120 static char *get_fperm(mode_t);
121 static char *get_fsize(off_t);
122 static char *get_fullpath(char *, char *);
123 static char *get_fusr(uid_t, size_t);
124 static void get_dirsize(char *, off_t *);
125 static void get_hicol(Cpair *, mode_t);
126 static int delent(char *);
127 static void calcdir(void);
128 static void crnd(void);
129 static void crnf(void);
130 static void delfd(void);
131 static void mvbk(void);
132 static void mvbtm(void);
133 static void mvdwn(void);
134 static void mvdwns(void);
135 static void mvfor(void);
136 static void mvmid(void);
137 static void mvtop(void);
138 static void mvup(void);
139 static void mvups(void);
140 static void scrdwn(void);
141 static void scrdwns(void);
142 static void scrup(void);
143 static void scrups(void);
144 static int get_usrinput(char*, size_t, const char*, ...);
145 static int frules(char *);
146 static int spawn(const void *, char *);
147 static int opnf(char *);
148 static int fsev_init(void);
149 static int addwatch(void);
150 static int read_events(void);
151 static void rmwatch(Pane *);
152 static void fsev_shdn(void);
153 static ssize_t findbm(uint32_t);
154 static void filter(void);
155 static void start_vmode(void);
156 static void exit_vmode(void);
157 static void selup(void);
158 static void seldwn(void);
159 static void selall(void);
160 static void selref(void);
161 static void selynk(void);
162 static void selcalc(void);
163 static void paste(void);
164 static void selmv(void);
165 static void seldel(void);
166 static void init_files(void);
167 static void free_files(void);
168 static void yank(void);
169 static void rname(void);
170 static void switch_pane(void);
171 static void quit(void);
172 static void grabkeys(struct tb_event*, Key*, size_t);
173 static void start_ev(void);
174 static void refresh_pane(void);
175 static void set_direntr(struct dirent *, DIR *, char *);
176 static int listdir(int);
177 static void t_resize(void);
178 static void set_panes(void);
179 static void draw_frame(void);
180 static void start(void);
182 /* global variables */
183 static Pane pane_r, pane_l, *cpane;
184 static char *editor[2];
185 static char fed[] = "vi";
186 static int theight, twidth, scrheight;
187 static size_t selection_size = 0;
188 static char yank_file[MAX_P];
189 static int *selection;
190 static int cont_vmode = 0;
191 static char **selected_files;
192 #if defined _SYS_INOTIFY_H
193 static int inotify_fd;
194 #elif defined _SYS_EVENT_H_
195 static int kq;
196 struct kevent evlist[2]; /* events we want to monitor */
197 struct kevent chlist[2]; /* events that were triggered */
198 static struct timespec gtimeout;
199 #endif
201 /* configuration, allows nested code to access above variables */
202 #include "config.h"
204 /* function implementations */
205 static void
206 print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
208 while (*str != '\0') {
209 uint32_t uni = 0;
210 str += tb_utf8_char_to_unicode(&uni, str);
211 tb_change_cell(x, y, uni, fg, bg);
212 x++;
216 static void
217 printf_tb(int x, int y, Cpair col, const char *fmt, ...)
219 char buf[4096];
220 va_list vl;
221 va_start(vl, fmt);
222 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
223 va_end(vl);
224 print_tb(buf, x, y, col.fg, col.bg);
227 static void
228 print_status(Cpair col, const char *fmt, ...)
230 char buf[256];
231 va_list vl;
232 va_start(vl, fmt);
233 (void)vsnprintf(buf, sizeof(buf), fmt, vl);
234 va_end(vl);
235 clear_status();
236 print_tb(buf, 1, theight - 1, col.fg, col.bg);
239 static void
240 print_xstatus(char c, int x)
242 uint32_t uni = 0;
243 (void)tb_utf8_char_to_unicode(&uni, &c);
244 tb_change_cell(x, theight - 1, uni, cstatus.fg, cstatus.bg);
247 static void
248 print_error(char *errmsg)
250 print_status(cerr, errmsg);
253 static void
254 print_prompt(char *prompt)
256 print_status(cprompt, prompt);
259 static void
260 print_info(void)
262 char *fileinfo;
263 fileinfo = get_finfo(&cpane->direntr[cpane->hdir - 1]);
264 print_status(cstatus, "%d/%d %s", cpane->hdir, cpane->dirc, fileinfo);
265 free(fileinfo);
268 static void
269 print_row(Pane *pane, size_t entpos, Cpair col)
271 int x, y;
272 char *result;
273 char buf[MAX_P];
274 char lnk_full[MAX_P];
275 int width;
277 width = (twidth / 2) - 4;
278 result = basename(pane->direntr[entpos].name);
279 x = pane->dirx;
280 y = entpos - cpane->firstrow + 1;
282 if (S_ISLNK(pane->direntr[entpos].mode) &&
283 realpath(pane->direntr[entpos].name, buf) != NULL) {
284 strncpy(lnk_full, pane->direntr[entpos].name, MAX_N);
285 strcat(lnk_full, " -> ");
286 strncat(lnk_full, buf, MAX_N);
287 result = lnk_full;
290 printf_tb(x, y, col, "%*.*s", ~width, width, result);
293 static void
294 clear(int sx, int ex, int y, uint16_t bg)
296 /* clear line from to */
297 /* x = line number vertical */
298 /* y = column number horizontal */
299 int i;
300 for (i = sx; i < ex; i++) {
301 tb_change_cell(i, y, 0x0000, TB_DEFAULT, bg);
305 static void
306 clear_status(void)
308 clear(1, twidth - 1, theight - 1, cstatus.bg);
311 static void
312 clear_pane(void)
314 int i, ex, y;
315 y = 0, i = 0, ex = 0;
317 if (cpane->pane_id == pane_l.pane_id)
318 ex = (twidth / 2) - 1;
319 else if (cpane->pane_id == pane_r.pane_id)
320 ex = twidth - 1;
322 while (i < scrheight) {
323 clear(cpane->dirx, ex, y, TB_DEFAULT);
324 i++;
325 y++;
328 /* draw top line */
329 for (y = cpane->dirx; y < ex; ++y) {
330 tb_change_cell(y, 0, u_hl, cframe.fg, cframe.bg);
334 static void
335 add_hi(Pane *pane, size_t entpos)
337 Cpair col;
338 get_hicol(&col, pane->direntr[entpos].mode);
339 col.fg |= TB_REVERSE | TB_BOLD;
340 col.bg |= TB_REVERSE;
341 print_row(pane, entpos, col);
344 static void
345 rm_hi(Pane *pane, size_t entpos)
347 Cpair col;
348 get_hicol(&col, pane->direntr[entpos].mode);
349 print_row(pane, entpos, col);
352 static void
353 float_to_string(float f, char *r)
355 int length, length2, i, number, position,
356 tenth; /* length is size of decimal part, length2 is size of tenth part */
357 float number2;
359 f = (float)(int)(f * 10) / 10;
361 number2 = f;
362 number = (int)f;
363 length2 = 0;
364 tenth = 1;
366 /* Calculate length2 tenth part */
367 while ((number2 - (float)number) != 0.0 &&
368 !((number2 - (float)number) < 0.0)) {
369 tenth *= 10.0;
370 number2 = f * (float)tenth;
371 number = (int)number2;
373 length2++;
376 /* Calculate length decimal part */
377 for (length = (f > 1.0) ? 0 : 1; f > 1.0; length++)
378 f /= 10.0;
380 position = length;
381 length = length + 1 + length2;
382 number = (int)number2;
384 if (length2 > 0) {
385 for (i = length; i >= 0; i--) {
386 if (i == (length))
387 r[i] = '\0';
388 else if (i == (position))
389 r[i] = '.';
390 else {
391 r[i] = (char)(number % 10) + '0';
392 number /= 10;
395 } else {
396 length--;
397 for (i = length; i >= 0; i--) {
398 if (i == (length))
399 r[i] = '\0';
400 else {
401 r[i] = (char)(number % 10) + '0';
402 number /= 10;
408 static int
409 check_dir(char *path)
411 DIR *dir;
412 dir = opendir(path);
414 if (dir == NULL) {
415 if (errno == ENOTDIR) {
416 return 1;
417 } else {
418 return -1;
422 if (closedir(dir) < 0)
423 return -1;
425 return 0;
428 static mode_t
429 chech_execf(mode_t mode)
431 if (S_ISREG(mode))
432 return (((S_IXUSR | S_IXGRP | S_IXOTH) & mode));
433 return 0;
436 static int
437 sort_name(const void *const A, const void *const B)
439 int result;
440 mode_t data1 = (*(Entry *)A).mode;
441 mode_t data2 = (*(Entry *)B).mode;
443 if (data1 < data2) {
444 return -1;
445 } else if (data1 == data2) {
446 result = strcmp((*(Entry *)A).name, (*(Entry *)B).name);
447 return result;
448 } else {
449 return 1;
454 static void
455 get_dirp(char *cdir)
457 int counter, len, i;
459 counter = 0;
460 len = strlen(cdir);
461 if (len ==1)
462 return;
464 for (i = len - 1; i > 1; i--) {
465 if (cdir[i] == '/')
466 break;
467 else
468 counter++;
471 cdir[len-counter-1] = '\0';
474 static char *
475 get_ext(char *str)
477 char *ext;
478 char dot;
479 size_t counter, len, i;
481 dot = '.';
482 counter = 0;
483 len = strlen(str);
485 for (i = len - 1; i > 0; i--) {
486 if (str[i] == dot) {
487 break;
488 } else {
489 counter++;
493 ext = ecalloc(counter + 1, sizeof(char));
494 strncpy(ext, &str[len - counter], counter);
495 return ext;
498 static int
499 get_fdt(char *result, size_t reslen, time_t status)
501 struct tm lt;
502 localtime_r(&status, &lt);
503 return strftime(result, reslen, dtfmt, &lt);
506 static char *
507 get_fgrp(gid_t status, size_t len)
509 char *result;
510 struct group *gr;
512 result = ecalloc(len, sizeof(char));
513 gr = getgrgid(status);
514 if (gr == NULL)
515 (void)snprintf(result, len - 1, "%u", status);
516 else
517 strncpy(result, gr->gr_name, len - 1);
519 return result;
522 static char *
523 get_finfo(Entry *cursor)
525 char *sz, *rst, *ur, *gr, *td, *prm;
526 size_t szlen, prmlen, urlen, grlen, tdlen, rstlen;
528 szlen = 9;
529 prmlen = 11;
530 urlen = grlen = tdlen = 32;
531 rstlen = szlen + prmlen + urlen + grlen + tdlen;
532 rst = ecalloc(rstlen, sizeof(char));
534 if (show_perm == 1) {
535 prm = get_fperm(cursor->mode);
536 strncpy(rst, prm, prmlen);
537 strcat(rst, " ");
538 free(prm);
541 if (show_ug == 1) {
542 ur = get_fusr(cursor->user, urlen);
543 gr = get_fgrp(cursor->group, grlen);
544 strncat(rst, ur, urlen);
545 strcat(rst, ":");
546 strncat(rst, gr, grlen);
547 strcat(rst, " ");
548 free(ur);
549 free(gr);
552 if (show_dt == 1) {
553 td = ecalloc(tdlen, sizeof(char));
554 if (get_fdt(td, tdlen, cursor->td) > 0) {
555 strncat(rst, td, tdlen);
556 strcat(rst, " ");
558 free(td);
561 if (show_size == 1 && S_ISREG(cursor->mode)) {
562 sz = get_fsize(cursor->size);
563 strncat(rst, sz, szlen);
564 free(sz);
567 return rst;
570 static char *
571 get_fperm(mode_t mode)
573 char *buf;
574 size_t i;
576 const char chars[] = "rwxrwxrwx";
577 buf = ecalloc(11, sizeof(char));
579 if (S_ISDIR(mode))
580 buf[0] = 'd';
581 else if (S_ISREG(mode))
582 buf[0] = '-';
583 else if (S_ISLNK(mode))
584 buf[0] = 'l';
585 else if (S_ISBLK(mode))
586 buf[0] = 'b';
587 else if (S_ISCHR(mode))
588 buf[0] = 'c';
589 else if (S_ISFIFO(mode))
590 buf[0] = 'p';
591 else if (S_ISSOCK(mode))
592 buf[0] = 's';
593 else
594 buf[0] = '?';
596 for (i = 1; i < 10; i++) {
597 buf[i] = (mode & (1 << (9 - i))) ? chars[i - 1] : '-';
599 buf[10] = '\0';
601 return buf;
604 static char *
605 get_fsize(off_t size)
607 /* need to be freed */
608 char *Rsize;
609 float lsize;
610 int counter;
611 counter = 0;
613 Rsize = ecalloc(10, sizeof(char));
614 lsize = (float)size;
616 while (lsize >= 1000.0) {
617 lsize /= 1024.0;
618 ++counter;
621 float_to_string(lsize, Rsize);
623 switch (counter) {
624 case 0:
625 strcat(Rsize, "B");
626 break;
627 case 1:
628 strcat(Rsize, "K");
629 break;
630 case 2:
631 strcat(Rsize, "M");
632 break;
633 case 3:
634 strcat(Rsize, "G");
635 break;
636 case 4:
637 strcat(Rsize, "T");
638 break;
641 return Rsize;
644 static char *
645 get_fullpath(char *first, char *second)
647 char *full_path;
648 size_t full_path_len;
650 full_path_len = strlen(first) + strlen(second) + 2;
651 full_path = ecalloc(full_path_len, sizeof(char));
653 if (strcmp(first, "/") == 0) {
654 (void)snprintf(full_path, full_path_len, "/%s", second);
656 } else {
657 (void)snprintf(full_path, full_path_len, "%s/%s", first,
658 second);
661 return full_path;
664 static char *
665 get_fusr(uid_t status, size_t len)
667 char *result;
668 struct passwd *pw;
670 result = ecalloc(len, sizeof(char));
671 pw = getpwuid(status);
672 if (pw == NULL)
673 (void)snprintf(result, len - 1, "%u", status);
674 else
675 strncpy(result, pw->pw_name, len - 1);
677 return result;
680 static void
681 get_dirsize(char *fullpath, off_t *fullsize)
683 DIR *dir;
684 char *ent_full;
685 mode_t mode;
686 struct dirent *entry;
687 struct stat status;
689 dir = opendir(fullpath);
690 if (dir == NULL) {
691 return;
694 while ((entry = readdir(dir)) != 0) {
695 if ((strcmp(entry->d_name, ".") == 0 ||
696 strcmp(entry->d_name, "..") == 0))
697 continue;
699 ent_full = get_fullpath(fullpath, entry->d_name);
700 if (lstat(ent_full, &status) == 0) {
701 mode = status.st_mode;
702 if (S_ISDIR(mode)) {
703 get_dirsize(ent_full, fullsize);
704 free(ent_full);
705 } else {
706 *fullsize += status.st_size;
707 free(ent_full);
712 closedir(dir);
713 clear_status();
716 static void
717 get_hicol(Cpair *col, mode_t mode)
719 *col = cfile;
720 if (S_ISDIR(mode))
721 *col = cdir;
722 else if (S_ISLNK(mode))
723 *col = cother;
724 else if (chech_execf(mode) > 0)
725 *col = cexec;
728 static int
729 delent(char *fullpath)
731 char *inp_conf;
732 int conf_len = 4;
733 char conf[] = "yes";
735 inp_conf = ecalloc(conf_len, sizeof(char));
736 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
737 (strncmp(inp_conf, conf, conf_len) != 0)) {
738 free(inp_conf);
739 return 1; /* canceled by user or wrong inp_conf */
741 free(inp_conf);
743 return spawn(rm_cmd, fullpath);
746 static void
747 calcdir(void)
749 off_t *fullsize;
750 char *csize;
751 char *result;
753 if (S_ISDIR(cpane->direntr[cpane->hdir - 1].mode)) {
754 fullsize = ecalloc(50, sizeof(off_t));
755 get_dirsize(CURSOR_NAME, fullsize);
756 csize = get_fsize(*fullsize);
757 result = get_finfo(&cpane->direntr[cpane->hdir - 1]);
759 clear_status();
760 print_status(cstatus, "%d/%d %s%s", cpane->hdir, cpane->dirc,
761 result, csize);
762 free(csize);
763 free(fullsize);
764 free(result);
768 static void
769 crnd(void)
771 char *user_input, *path;
772 size_t pathlen;
774 user_input = ecalloc(MAX_USRI, sizeof(char));
775 if (get_usrinput(user_input, MAX_USRI, "new dir") < 0) {
776 free(user_input);
777 return;
780 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
781 path = ecalloc(pathlen, sizeof(char));
782 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
783 free(user_input);
784 free(path);
785 return;
788 if (mkdir(path, ndir_perm) < 0)
789 print_error(strerror(errno));
791 free(user_input);
792 free(path);
795 static void
796 crnf(void)
798 char *user_input, *path;
799 size_t pathlen;
800 int rf;
802 user_input = ecalloc(MAX_USRI, sizeof(char));
803 if (get_usrinput(user_input, MAX_USRI, "new file") < 0) {
804 free(user_input);
805 return;
808 pathlen = strlen(cpane->dirn) + 1 + MAX_USRI + 1;
809 path = ecalloc(pathlen, sizeof(char));
810 if (snprintf(path, pathlen, "%s/%s", cpane->dirn, user_input) < 0) {
811 free(user_input);
812 free(path);
813 return;
816 rf = open(path, O_CREAT | O_EXCL, nf_perm);
818 if (rf < 0)
819 print_error(strerror(errno));
820 else
821 if (close(rf) < 0)
822 print_error(strerror(errno));
824 free(user_input);
825 free(path);
828 static void
829 delfd(void)
831 switch (delent(CURSOR_NAME)) {
832 case -1:
833 print_error(strerror(errno));
834 break;
835 case 0:
836 if (BETWEEN(cpane->hdir - 1, 1, cpane->dirc)) /* last entry */
837 cpane->hdir--;
838 break;
842 static void
843 mvbk(void)
845 get_dirp(cpane->dirn);
846 if (check_dir(cpane->dirn) < 0) {
847 print_error(strerror(errno));
848 return;
851 rmwatch(cpane);
852 cpane->firstrow = cpane->parent_firstrow;
853 cpane->hdir = cpane->parent_row;
854 if (listdir(AddHi) < 0)
855 print_error(strerror(errno));
856 cpane->parent_firstrow = 0;
857 cpane->parent_row = 1;
860 static void
861 mvbtm(void)
863 if (cpane->dirc < 1)
864 return;
865 if (cpane->dirc > scrheight) {
866 rm_hi(cpane, cpane->hdir - 1);
867 cpane->hdir = cpane->dirc;
868 cpane->firstrow = cpane->dirc - scrheight + 1;
869 refresh_pane();
870 add_hi(cpane, cpane->hdir - 1);
871 } else {
872 rm_hi(cpane, cpane->hdir - 1);
873 cpane->hdir = cpane->dirc;
874 add_hi(cpane, cpane->hdir - 1);
876 print_info();
879 static void
880 mvdwn(void)
882 if (cpane->dirc < 1)
883 return;
884 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
885 rm_hi(cpane, cpane->hdir - 1);
886 cpane->hdir++;
887 add_hi(cpane, cpane->hdir - 1);
888 } else {
889 mvdwns(); /* scroll */
891 print_info();
894 static void
895 mvdwns(void)
897 int real;
898 real = cpane->hdir - 1 - cpane->firstrow;
900 if (real > scrheight - 3 - scrsp && cpane->hdir + scrsp < cpane->dirc) {
901 cpane->firstrow++;
902 rm_hi(cpane, cpane->hdir - 1);
903 cpane->hdir++;
904 refresh_pane();
905 add_hi(cpane, cpane->hdir - 1);
906 } else if (cpane->hdir < cpane->dirc) {
907 rm_hi(cpane, cpane->hdir - 1);
908 cpane->hdir++;
909 add_hi(cpane, cpane->hdir - 1);
913 static void
914 mvfor(void)
916 rmwatch(cpane);
917 if (cpane->dirc < 1)
918 return;
919 int s;
921 switch (check_dir(CURSOR_NAME)) {
922 case 0:
923 strcpy(cpane->dirn, CURSOR_NAME);
924 cpane->parent_row = cpane->hdir;
925 cpane->parent_firstrow = cpane->firstrow;
926 cpane->hdir = 1;
927 cpane->firstrow = 0;
928 if (listdir(AddHi) < 0)
929 print_error(strerror(errno));
930 break;
931 case 1: /* not a directory open file */
932 tb_shutdown();
933 s = opnf(CURSOR_NAME);
934 if (tb_init() != 0)
935 die("tb_init");
936 t_resize();
937 if (s < 0)
938 print_error("process failed non-zero exit");
939 break;
940 case -1: /* failed to open directory */
941 print_error(strerror(errno));
945 static void
946 mvmid(void)
948 if (cpane->dirc < 1)
949 return;
950 rm_hi(cpane, cpane->hdir - 1);
951 if (cpane->dirc < scrheight / 2)
952 cpane->hdir = (cpane->dirc + 1) / 2;
953 else
954 cpane->hdir = (scrheight / 2) + cpane->firstrow;
955 add_hi(cpane, cpane->hdir - 1);
956 print_info();
959 static void
960 mvtop(void)
962 if (cpane->dirc < 1)
963 return;
964 if (cpane->dirc > scrheight) {
965 rm_hi(cpane, cpane->hdir - 1);
966 cpane->hdir = 1;
967 cpane->firstrow = 0;
968 refresh_pane();
969 add_hi(cpane, cpane->hdir - 1);
970 } else {
971 rm_hi(cpane, cpane->hdir - 1);
972 cpane->hdir = 1;
973 add_hi(cpane, cpane->hdir - 1);
974 print_info();
978 static void
979 mvup(void)
981 if (cpane->dirc < 1)
982 return;
983 if (cpane->dirc < scrheight && cpane->hdir > 1) {
984 rm_hi(cpane, cpane->hdir - 1);
985 cpane->hdir--;
986 add_hi(cpane, cpane->hdir - 1);
987 } else {
988 mvups(); /* scroll */
990 print_info();
993 static void
994 mvups(void)
996 size_t real;
997 real = cpane->hdir - 1 - cpane->firstrow;
999 if (cpane->firstrow > 0 && real < 1 + scrsp) {
1000 cpane->firstrow--;
1001 rm_hi(cpane, cpane->hdir - 1);
1002 cpane->hdir--;
1003 refresh_pane();
1004 add_hi(cpane, cpane->hdir - 1);
1005 } else if (cpane->hdir > 1) {
1006 rm_hi(cpane, cpane->hdir - 1);
1007 cpane->hdir--;
1008 add_hi(cpane, cpane->hdir - 1);
1012 static void
1013 scrdwn(void)
1015 if (cpane->dirc < 1)
1016 return;
1017 if (cpane->dirc < scrheight && cpane->hdir < cpane->dirc) {
1018 if (cpane->hdir < cpane->dirc - scrmv) {
1019 rm_hi(cpane, cpane->hdir - 1);
1020 cpane->hdir += scrmv;
1021 add_hi(cpane, cpane->hdir - 1);
1022 } else {
1023 mvbtm();
1025 } else {
1026 scrdwns();
1028 print_info();
1031 static void
1032 scrdwns(void)
1034 int real, dynmv;
1036 real = cpane->hdir - cpane->firstrow;
1037 dynmv = MIN(cpane->dirc - cpane->hdir - cpane->firstrow, scrmv);
1039 if (real + scrmv + 1 > scrheight &&
1040 cpane->hdir + scrsp + scrmv < cpane->dirc) { /* scroll */
1041 cpane->firstrow += dynmv;
1042 rm_hi(cpane, cpane->hdir - 1);
1043 cpane->hdir += scrmv;
1044 refresh_pane();
1045 add_hi(cpane, cpane->hdir - 1);
1046 } else {
1047 if (cpane->hdir < cpane->dirc - scrmv - 1) {
1048 rm_hi(cpane, cpane->hdir - 1);
1049 cpane->hdir += scrmv;
1050 add_hi(cpane, cpane->hdir - 1);
1051 } else {
1052 mvbtm();
1057 static void
1058 scrup(void)
1060 if (cpane->dirc < 1)
1061 return;
1062 if (cpane->dirc < scrheight && cpane->hdir > 1) {
1063 if (cpane->hdir > scrmv) {
1064 rm_hi(cpane, cpane->hdir - 1);
1065 cpane->hdir = cpane->hdir - scrmv;
1066 add_hi(cpane, cpane->hdir - 1);
1067 print_info();
1068 } else {
1069 mvtop();
1071 } else {
1072 scrups();
1076 static void
1077 scrups(void)
1079 int real, dynmv;
1080 real = cpane->hdir - cpane->firstrow;
1081 dynmv = MIN(cpane->firstrow, scrmv);
1083 if (cpane->firstrow > 0 && real < scrmv + scrsp) {
1084 cpane->firstrow -= dynmv;
1085 rm_hi(cpane, cpane->hdir - 1);
1086 cpane->hdir -= scrmv;
1087 refresh_pane();
1088 add_hi(cpane, cpane->hdir - 1);
1089 } else {
1090 if (cpane->hdir > scrmv + 1) {
1091 rm_hi(cpane, cpane->hdir - 1);
1092 cpane->hdir -= scrmv;
1093 add_hi(cpane, cpane->hdir - 1);
1094 } else {
1095 mvtop();
1100 static int
1101 get_usrinput(char *out, size_t sout, const char *fmt, ...)
1103 int height = tb_height();
1104 size_t startat;
1105 struct tb_event fev;
1106 size_t counter = (size_t)1;
1107 char empty = ' ';
1108 int x = 0;
1109 int name_size = 0;
1110 char buf[256];
1112 clear_status();
1114 va_list vl;
1115 Cpair col;
1116 col = cprompt;
1117 va_start(vl, fmt);
1118 name_size = vsnprintf(buf, sizeof(buf), fmt, vl);
1119 va_end(vl);
1120 print_tb(buf, 1, height-1, col.fg, col.bg);
1121 startat = name_size + 1;
1122 tb_set_cursor((int)(startat + 1), height-1);
1123 tb_present();
1125 while (tb_poll_event(&fev) != 0) {
1126 switch (fev.type) {
1127 case TB_EVENT_KEY:
1128 if (fev.key == TB_KEY_ESC) {
1129 tb_set_cursor(-1, -1);
1130 clear_status();
1131 return -1;
1134 if (fev.key == TB_KEY_BACKSPACE ||
1135 fev.key == TB_KEY_BACKSPACE2) {
1136 if (BETWEEN(counter, 2, sout)) {
1137 out[x - 1] = '\0';
1138 counter--;
1139 x--;
1140 print_xstatus(empty, startat + counter);
1141 tb_set_cursor(startat + counter,
1142 theight - 1);
1145 } else if (fev.key == TB_KEY_ENTER) {
1146 tb_set_cursor(-1, -1);
1147 out[counter - 1] = '\0';
1148 return 0;
1150 } else {
1151 if (counter < sout) {
1152 print_xstatus((char)fev.ch,
1153 (startat + counter));
1154 out[x] = (char)fev.ch;
1155 tb_set_cursor((startat + counter + 1),
1156 theight - 1);
1157 counter++;
1158 x++;
1162 tb_present();
1163 break;
1165 default:
1166 return -1;
1170 return -1;
1173 static int
1174 frules(char *ex)
1176 size_t c, d;
1178 for (c = 0; c < LEN(rules); c++)
1179 for (d = 0; d < rules[c].exlen; d++)
1180 if (strncmp(rules[c].ext[d], ex, MAX_EXT) == 0)
1181 return c;
1182 return -1;
1185 static int
1186 spawn(const void *v, char *fn)
1188 int ws, x, argc, fd;
1189 pid_t pid, r;
1191 x = 0;
1192 argc = 0;
1194 /* count args */
1195 while (((char **)v)[x++] != NULL)
1196 argc++;
1198 char *argv[argc + 2];
1199 for ( x = 0; x < argc; x++)
1200 argv[x] = ((char **)v)[x];
1202 argv[argc] = fn;
1203 argv[argc + 1] = NULL;
1205 pid = fork();
1206 switch (pid) {
1207 case -1:
1208 return -1;
1209 case 0:
1210 fd = open("/dev/null",O_WRONLY);
1211 dup2(fd, STDERR_FILENO);
1212 execvp(argv[0], argv);
1213 exit(EXIT_SUCCESS);
1214 close(fd);
1215 default:
1216 while ((r = waitpid(pid, &ws, 0)) == -1 && errno == EINTR)
1217 continue;
1218 if (r == -1)
1219 return -1;
1220 if ((WIFEXITED(ws) != 0) && (WEXITSTATUS(ws) != 0))
1221 return -1;
1223 return 0;
1226 static int
1227 opnf(char *fn)
1229 char *ex;
1230 int c;
1232 ex = get_ext(fn);
1233 c = frules(ex);
1234 free(ex);
1236 if (c < 0) /* extension not found open in editor */
1237 return spawn(editor, fn);
1238 else
1239 return spawn((char **)rules[c].v, fn);
1242 static int
1243 fsev_init(void)
1245 #if defined _SYS_INOTIFY_H
1246 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1247 if (inotify_fd < 0)
1248 return -1;
1249 #elif defined _SYS_EVENT_H_
1250 kq = kqueue();
1251 if (kq < 0)
1252 return -1;
1253 #endif
1254 return 0;
1257 static int
1258 addwatch(void)
1260 #if defined _SYS_INOTIFY_H
1261 return cpane->inotify_wd = inotify_add_watch(inotify_fd, cpane->dirn,
1262 IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
1263 IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF);
1264 #elif defined _SYS_EVENT_H_
1265 cpane->event_fd = open(cpane->dirn, O_RDONLY);
1266 if (cpane->event_fd < 0)
1267 return cpane->event_fd;
1268 EV_SET(&evlist[cpane->pane_id], cpane->event_fd,
1269 EVFILT_VNODE, EV_ADD | EV_CLEAR,
1270 NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
1271 NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE, 0, NULL);
1272 return 0;
1273 #endif
1276 static int
1277 read_events(void)
1279 #if defined _SYS_INOTIFY_H
1280 char *p;
1281 ssize_t r;
1282 struct inotify_event *event;
1283 const size_t events = 32;
1284 const size_t evbuflen =
1285 events * (sizeof(struct inotify_event) + MAX_N + 1);
1286 char buf[evbuflen];
1288 if (cpane->inotify_wd < 0)
1289 return -1;
1290 r = read(inotify_fd, buf, evbuflen);
1291 if (r <= 0)
1292 return r;
1294 for (p = buf; p < buf + r;) {
1295 event = (struct inotify_event *)p;
1296 if (!event->wd)
1297 break;
1298 if (event->mask) {
1299 return r;
1302 p += sizeof(struct inotify_event) + event->len;
1304 #elif defined _SYS_EVENT_H_
1305 return kevent(kq, evlist, 2, chlist, 2, &gtimeout);
1306 #endif
1307 return -1;
1310 static void
1311 rmwatch(Pane *pane)
1313 #if defined _SYS_INOTIFY_H
1314 if (pane->inotify_wd >= 0)
1315 inotify_rm_watch(inotify_fd, pane->inotify_wd);
1316 #elif defined _SYS_EVENT_H_
1317 close(pane->event_fd);
1318 return;
1319 #endif
1322 static void
1323 fsev_shdn(void)
1325 rmwatch(&pane_l);
1326 rmwatch(&pane_r);
1327 #if defined _SYS_INOTIFY_H
1328 close(inotify_fd);
1329 #elif defined _SYS_EVENT_H_
1330 close(kq);
1331 #endif
1334 static ssize_t
1335 findbm(uint32_t event)
1337 ssize_t i;
1339 for (i = 0; i < (ssize_t)LEN(bmarks); i++) {
1340 if (event == bmarks[i].ch) {
1341 if (check_dir(bmarks[i].path) != 0) {
1342 print_error(strerror(errno));
1343 return -1;
1345 return i;
1348 return -1;
1351 static void
1352 filter(void)
1354 if (cpane->dirc < 1)
1355 return;
1356 char *user_input;
1357 user_input = ecalloc(MAX_USRI, sizeof(char));
1358 if (get_usrinput(user_input, MAX_USRI, "filter") < 0) {
1359 free(user_input);
1360 return;
1362 cpane->filter = user_input;
1363 if (listdir(AddHi) < 0)
1364 print_error("no match");
1365 free(user_input);
1368 static void
1369 start_vmode(void)
1371 struct tb_event fev;
1372 if (selection != NULL) {
1373 free(selection);
1374 selection = NULL;
1377 selection = ecalloc(cpane->dirc, sizeof(size_t));
1378 selection[0] = cpane->hdir;
1379 cont_vmode = 0;
1380 print_prompt("-- VISUAL --");
1381 tb_present();
1382 while (tb_poll_event(&fev) != 0) {
1383 switch (fev.type) {
1384 case TB_EVENT_KEY:
1385 grabkeys(&fev, skeys, skeyslen);
1386 if (cont_vmode == -1)
1387 return;
1388 tb_present();
1389 break;
1394 static void
1395 exit_vmode(void)
1397 refresh_pane();
1398 add_hi(cpane, cpane->hdir - 1);
1399 cont_vmode = -1;
1402 static void
1403 selup(void)
1405 mvup();
1406 print_prompt("-- VISUAL --");
1407 int index = abs(cpane->hdir - selection[0]);
1409 if (cpane->hdir < selection[0]) {
1410 selection[index] = cpane->hdir;
1411 add_hi(cpane, selection[index]);
1412 } else if (index < cpane->dirc) {
1413 selection[index + 1] = 0;
1415 if (cpane->dirc >= scrheight || cpane->hdir <= 1) { /* rehighlight all if scrolling */
1416 selref();
1420 static void
1421 seldwn(void)
1423 mvdwn();
1424 print_prompt("-- VISUAL --");
1425 int index = abs(cpane->hdir - selection[0]);
1427 if (cpane->hdir > selection[0]) {
1428 selection[index] = cpane->hdir;
1429 add_hi(cpane, selection[index] - 2);
1430 } else {
1431 selection[index + 1] = 0;
1433 if (cpane->dirc >= scrheight || cpane->hdir >= cpane->dirc) { /* rehighlight all if scrolling */
1434 selref();
1438 static void
1439 selall(void)
1441 int i;
1442 for (i = 0; i < cpane->dirc; i++) {
1443 selection[i] = i + 1;
1445 selref();
1448 static void
1449 selref(void)
1451 int i;
1452 for (i = 0; i < cpane->dirc; i++) {
1453 if (selection[i] < (scrheight + cpane->firstrow) && selection[i] > cpane->firstrow) { /* checks if in the frame of the directories */
1454 add_hi(cpane, selection[i] - 1);
1459 static void
1460 selcalc(void)
1462 int j;
1463 selection_size = 0;
1465 for (j = 0; j < cpane->dirc; j++) { /* calculate used selection size */
1466 if (selection[j] != 0)
1467 selection_size++;
1468 else
1469 break;
1473 static void
1474 free_files(void)
1476 size_t i;
1478 if (selected_files != NULL) {
1479 for (i = 0; i < selection_size; i++) {
1480 free(selected_files[i]);
1481 selected_files[i] = NULL;
1483 free(selected_files);
1484 selected_files = NULL;
1488 static void
1489 init_files(void)
1491 size_t i;
1492 free_files();
1494 selcalc();
1495 selected_files = ecalloc(selection_size, sizeof(char*));
1497 for (i = 0; i < selection_size; i++) {
1498 selected_files[i] = ecalloc(MAX_P, sizeof(char));
1499 // strcpy(selected_files[i], "\"");
1500 strcpy(selected_files[i], cpane->direntr[selection[i] -1].name);
1501 // strcat(selected_files[i], "\"");
1505 static void
1506 selynk(void)
1508 init_files();
1509 refresh_pane();
1510 add_hi(cpane, cpane->hdir -1);
1511 print_status(cprompt, "%zu files are yanked", selection_size);
1512 cont_vmode = -1;
1515 static void
1516 seldel(void)
1518 char *inp_conf;
1519 int conf_len = 4;
1520 char conf[] = "yes";
1521 size_t i;
1523 inp_conf = ecalloc(conf_len, sizeof(char));
1524 if ((get_usrinput(inp_conf, conf_len, "delete file (yes) ?") < 0) ||
1525 (strncmp(inp_conf, conf, conf_len) != 0)) {
1526 free(inp_conf);
1527 return; /* canceled by user or wrong inp_conf */
1529 free(inp_conf);
1531 init_files();
1532 for (i = 0; i < selection_size; i++) {
1533 spawn(rm_cmd, selected_files[i]);
1536 cpane->hdir = cpane->dirc - selection_size;
1537 print_status(cprompt, "%zu files are deleted", selection_size);
1538 free_files();
1539 cont_vmode = -1;
1542 static void
1543 paste(void)
1545 size_t i;
1546 if (strlen(yank_file) != 0) {
1547 print_status(cprompt, "coping");
1548 if (spawn(cp_cmd, cpane->dirn) != 0)
1549 print_error("coping failed");
1550 else
1551 print_status(cprompt, "file copied");
1552 yank_file[0] = '\0'; /* set yank_file len 0 */
1553 return;
1556 print_error("nothing to paste");
1558 if (selected_files == NULL)
1559 return;
1561 for (i = 0; i < selection_size; i++) {
1562 char *selcp_cmd[] = { "cp", "-r", selected_files[i], cpane->dirn, NULL };
1563 spawn(selcp_cmd,NULL);
1565 print_status(cprompt, "%zu files are copied", selection_size);
1566 free_files();
1569 static void
1570 selmv(void)
1572 size_t i;
1574 if (strlen(yank_file) != 0) {
1575 print_status(cprompt, "moving");
1576 if (spawn(mv_cmd, cpane->dirn) != 0)
1577 print_error("moving failed");
1578 else
1579 print_status(cprompt, "file moved");
1580 yank_file[0] = '\0'; /* set yank_file len 0 */
1581 return;
1584 print_error("nothing to move");
1586 if (selected_files == NULL)
1587 return;
1589 for (i = 0; i < selection_size; i++) {
1590 char *selmv_cmd[] = { "mv", selected_files[i], cpane->dirn, NULL };
1591 spawn(selmv_cmd,NULL);
1593 print_status(cprompt, "%zu files are moved", selection_size);
1594 free_files();
1597 static void
1598 rname(void)
1600 char new_name[MAX_P];
1601 char *input_name;
1603 input_name = ecalloc(MAX_N, sizeof(char));
1605 if (get_usrinput(input_name, MAX_N, "rename: %s", basename(CURSOR_NAME)) < 0) {
1606 free(input_name);
1607 return;
1610 strcpy(new_name, cpane->dirn);
1611 strcat(new_name, "/");
1612 strcat(new_name, input_name);
1614 char *rename_cmd[] = { "mv", CURSOR_NAME, new_name, NULL };
1615 if (spawn(rename_cmd, NULL) < 0)
1616 print_error(strerror(errno));
1618 free(input_name);
1621 static void
1622 yank(void)
1624 strncpy(yank_file, CURSOR_NAME, MAX_P);
1625 print_status(cprompt, "1 file is yanked", selection_size);
1629 static void
1630 switch_pane(void)
1632 if (cpane->dirc > 0)
1633 rm_hi(cpane, cpane->hdir - 1);
1634 if (cpane == &pane_l)
1635 cpane = &pane_r;
1636 else if (cpane == &pane_r)
1637 cpane = &pane_l;
1638 if (cpane->dirc > 0) {
1639 add_hi(cpane, cpane->hdir - 1);
1640 print_info();
1641 } else {
1642 clear_status();
1646 static void
1647 quit(void)
1649 if (cont_vmode == -1) { /* check if selection was allocated */
1650 free(selection);
1651 if (selected_files != NULL)
1652 free_files();
1654 free(pane_l.direntr);
1655 free(pane_r.direntr);
1656 fsev_shdn();
1657 tb_shutdown();
1658 exit(EXIT_SUCCESS);
1661 static void
1662 grabkeys(struct tb_event *event, Key *key, size_t max_keys)
1664 size_t i;
1665 ssize_t b;
1667 for (i = 0; i < max_keys; i++) {
1668 if (event->ch != 0) {
1669 if (event->ch == key[i].evkey.ch) {
1670 key[i].func();
1671 return;
1673 } else if (event->key != 0) {
1674 if (event->key == key[i].evkey.key) {
1675 key[i].func();
1676 return;
1681 /* bookmarks */
1682 b = findbm(event->ch);
1683 if (b < 0)
1684 return;
1685 rmwatch(cpane);
1686 strcpy(cpane->dirn, bmarks[b].path);
1687 cpane->firstrow = 0;
1688 cpane->parent_row = 1;
1689 cpane->hdir = 1;
1690 if (listdir(AddHi) < 0)
1691 print_error(strerror(errno));
1694 static void
1695 start_ev(void)
1697 struct tb_event ev;
1699 for (;;) {
1700 int t = tb_peek_event(&ev, 2000);
1701 if (t < 0) {
1702 tb_shutdown();
1703 return;
1706 if (t == 1) /* keyboard event */
1707 grabkeys(&ev, nkeys, nkeyslen);
1708 else if (t == 2) /* resize event */
1709 t_resize();
1710 else if (t == 0) /* filesystem event */
1711 if (read_events() > 0) { /* TODO need refactoring */
1712 if (cpane == &pane_l) {
1713 cpane = &pane_r;
1714 if (listdir(NoHi) < 0)
1715 print_error(strerror(errno));
1716 cpane = &pane_l;
1717 if (listdir(AddHi) < 0)
1718 print_error(strerror(errno));
1719 } else if (cpane == &pane_r) {
1720 cpane = &pane_l;
1721 if (listdir(NoHi) < 0)
1722 print_error(strerror(errno));
1723 cpane = &pane_r;
1724 if (listdir(AddHi) < 0)
1725 print_error(strerror(errno));
1729 tb_present();
1730 continue;
1732 tb_shutdown();
1735 static void
1736 refresh_pane(void)
1738 size_t y, dyn_max, start_from;
1739 int width;
1740 width = (twidth / 2) - 4;
1741 Cpair col;
1743 y = 1;
1744 start_from = cpane->firstrow;
1745 dyn_max = MIN(cpane->dirc, (scrheight - 1) + cpane->firstrow);
1747 /* print each entry in directory */
1748 while (start_from < dyn_max) {
1749 get_hicol(&col, cpane->direntr[start_from].mode);
1750 print_row(cpane, start_from, col);
1751 start_from++;
1752 y++;
1755 if (cpane->dirc > 0)
1756 print_info();
1757 else
1758 clear_status();
1760 /* print current directory title */
1761 cpane->dircol.fg |= TB_BOLD;
1762 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1765 static void
1766 set_direntr(struct dirent *entry, DIR *dir, char *filter)
1768 int i;
1769 char *tmpfull;
1770 struct stat status;
1773 #define ADD_ENTRY \
1774 tmpfull = get_fullpath(cpane->dirn, entry->d_name); \
1775 strcpy(cpane->direntr[i].name, tmpfull); \
1776 if (lstat(tmpfull, &status) == 0) { \
1777 cpane->direntr[i].size = status.st_size; \
1778 cpane->direntr[i].mode = status.st_mode; \
1779 cpane->direntr[i].group = status.st_gid; \
1780 cpane->direntr[i].user = status.st_uid; \
1781 cpane->direntr[i].td = status.st_mtime; \
1782 }i++;free(tmpfull);
1784 i = 0;
1785 cpane->direntr = erealloc(cpane->direntr, cpane->dirc * sizeof(Entry));
1786 while ((entry = readdir(dir)) != 0) {
1787 if ((strcmp(entry->d_name, ".") == 0 ||
1788 strcmp(entry->d_name, "..") == 0))
1789 continue;
1791 if (filter != NULL) {
1792 if (strcasestr(entry->d_name, filter) != NULL) {
1793 ADD_ENTRY
1797 if (filter == NULL) {
1798 ADD_ENTRY
1802 cpane->dirc = i;
1805 static int
1806 listdir(int hi)
1808 DIR *dir;
1809 struct dirent *entry;
1810 int width;
1811 int filtercount = 0;
1812 size_t oldc = cpane->dirc;
1814 width = (twidth / 2) - 4;
1815 cpane->dirc = 0;
1817 dir = opendir(cpane->dirn);
1818 if (dir == NULL)
1819 return -1;
1821 /* get content and filter sum */
1822 while ((entry = readdir(dir)) != 0) {
1823 if (cpane->filter != NULL) {
1824 if (strcasestr(entry->d_name, cpane->filter) != NULL)
1825 filtercount++;
1826 } else { /* no filter */
1827 cpane->dirc++;
1831 if (cpane->filter == NULL) {
1832 clear_pane();
1833 cpane->dirc -= 2;
1836 if (cpane->filter != NULL) {
1837 if (filtercount > 0) {
1838 cpane->dirc -= 2;
1839 cpane->dirc = filtercount;
1840 clear_pane();
1841 cpane->hdir = 1;
1842 } else if (filtercount == 0) {
1843 if (closedir(dir) < 0)
1844 return -1;
1845 cpane->dirc = oldc;
1846 return -1;
1850 /* print current directory title */
1851 cpane->dircol.fg |= TB_BOLD;
1852 printf_tb(cpane->dirx, 0, cpane->dircol, " %.*s ", width, cpane->dirn);
1854 if (cpane->filter == NULL) /* dont't watch when filtering */
1855 if (addwatch() < 0)
1856 print_error("can't add watch");
1858 /* empty directory */
1859 if (cpane->dirc == 0) {
1860 clear_status();
1861 if (closedir(dir) < 0)
1862 return -1;
1863 return 0;
1866 rewinddir(dir); /* reset position */
1867 set_direntr(entry, dir, cpane->filter); /* create array of entries */
1868 qsort(cpane->direntr, cpane->dirc, sizeof(Entry), sort_name);
1869 refresh_pane();
1871 if (hi == AddHi)
1872 add_hi(cpane, cpane->hdir - 1);
1874 if (closedir(dir) < 0)
1875 return -1;
1876 return 0;
1879 static void
1880 t_resize(void)
1882 /* TODO need refactoring */
1883 tb_clear();
1884 draw_frame();
1885 pane_r.dirx = (twidth / 2) + 2;
1887 if (cpane == &pane_l) {
1888 cpane = &pane_r;
1889 refresh_pane();
1890 cpane = &pane_l;
1891 refresh_pane();
1892 if (cpane->dirc > 0)
1893 add_hi(&pane_l, pane_l.hdir - 1);
1894 } else if (cpane == &pane_r) {
1895 cpane = &pane_l;
1896 refresh_pane();
1897 cpane = &pane_r;
1898 refresh_pane();
1899 if (cpane->dirc > 0)
1900 add_hi(&pane_r, pane_r.hdir - 1);
1903 tb_present();
1906 static void
1907 get_editor(void)
1909 editor[0] = getenv("EDITOR");
1910 editor[1] = NULL;
1912 if (editor[0] == NULL)
1913 editor[0] = fed;
1916 static void
1917 set_panes(void)
1919 char *home;
1920 char cwd[MAX_P];
1922 home = getenv("HOME");
1923 if (home == NULL)
1924 home = "/";
1925 if ((getcwd(cwd, sizeof(cwd)) == NULL))
1926 strncpy(cwd, home, MAX_P);
1928 pane_l.pane_id = 0;
1929 pane_l.dirx = 2;
1930 pane_l.dircol = cpanell;
1931 pane_l.firstrow = 0;
1932 pane_l.direntr = ecalloc(0, sizeof(Entry));
1933 strcpy(pane_l.dirn, cwd);
1934 pane_l.hdir = 1;
1935 pane_l.inotify_wd = -1;
1936 pane_l.parent_row = 1;
1938 pane_r.pane_id = 1;
1939 pane_r.dirx = (twidth / 2) + 2;
1940 pane_r.dircol = cpanelr;
1941 pane_r.firstrow = 0;
1942 pane_r.direntr = ecalloc(0, sizeof(Entry));
1943 strcpy(pane_r.dirn, home);
1944 pane_r.hdir = 1;
1945 pane_r.inotify_wd = -1;
1946 pane_r.parent_row = 1;
1949 static void
1950 draw_frame(void)
1952 int i;
1953 theight = tb_height();
1954 twidth = tb_width();
1955 scrheight = theight - 2;
1957 /* 2 horizontal lines */
1958 for (i = 1; i < twidth - 1; ++i) {
1959 tb_change_cell(i, 0, u_hl, cframe.fg, cframe.bg);
1960 tb_change_cell(i, theight - 2, u_hl, cframe.fg, cframe.bg);
1963 /* 4 vertical lines */
1964 for (i = 1; i < theight - 1; ++i) {
1965 tb_change_cell(0, i, u_vl, cframe.fg, cframe.bg);
1966 tb_change_cell((twidth - 1) / 2, i - 1, u_vl, cframe.fg,
1967 cframe.bg);
1968 tb_change_cell(((twidth - 1) / 2) + 1, i - 1, u_vl, cframe.fg,
1969 cframe.bg);
1970 tb_change_cell(twidth - 1, i, u_vl, cframe.fg, cframe.bg);
1973 /* 4 corners */
1974 tb_change_cell(0, 0, u_cnw, cframe.fg, cframe.bg);
1975 tb_change_cell(twidth - 1, 0, u_cne, cframe.fg, cframe.bg);
1976 tb_change_cell(0, theight - 2, u_csw, cframe.fg, cframe.bg);
1977 tb_change_cell(twidth - 1, theight - 2, u_cse, cframe.fg, cframe.bg);
1979 /* 2 middel top and bottom */
1980 tb_change_cell((twidth - 1) / 2, 0, u_mn, cframe.fg, cframe.bg);
1981 tb_change_cell((twidth - 1) / 2, theight - 2, u_ms, cframe.fg, cframe.bg);
1984 static void
1985 start(void)
1987 if (tb_init() != 0)
1988 die("tb_init");
1989 if (tb_select_output_mode(TB_OUTPUT_256) != TB_OUTPUT_256)
1990 if (tb_select_output_mode(TB_OUTPUT_NORMAL) != TB_OUTPUT_NORMAL)
1991 die("output error");
1993 draw_frame();
1994 set_panes();
1995 get_editor();
1996 if (fsev_init() < 0)
1997 print_error(strerror(errno));
1998 cpane = &pane_r;
1999 if (listdir(NoHi) < 0)
2000 print_error(strerror(errno));
2001 cpane = &pane_l;
2002 if (listdir(AddHi) < 0)
2003 print_error(strerror(errno));
2004 tb_present();
2005 start_ev();
2009 main(int argc, char *argv[])
2011 #ifdef __OpenBSD__
2012 if (pledge("cpath exec getpw proc rpath stdio tmppath tty wpath",
2013 NULL) == -1)
2014 die("pledge");
2015 #endif /* __OpenBSD__ */
2016 if (argc == 1)
2017 start();
2018 else if (argc == 2 && strncmp("-v", argv[1], 2) == 0)
2019 die("sfm-" VERSION);
2020 else
2021 die("usage: sfm [-v]");
2022 return 0;