Blame


1 71ee1627 2014-11-07 marcelgmr #include <stdlib.h>
2 bae1431c 2014-11-07 marcelgmr #include <stdint.h>
3 6beff8a2 2014-11-08 marcelgmr #include <ctype.h>
4 6beff8a2 2014-11-08 marcelgmr #include <string.h>
5 5f52c474 2014-11-08 marcelgmr #include <sys/types.h> /* pid_t, ... */
6 c04b0d89 2015-03-30 marcelgmr #include <stdio.h>
7 c04b0d89 2015-03-30 marcelgmr #include <limits.h> /* PATH_MAX */
8 71ee1627 2014-11-07 marcelgmr #include <locale.h> /* setlocale(), LC_ALL */
9 b7ee4ee2 2014-11-14 marcelgmr #include <unistd.h> /* chdir(), getcwd(), read(), close(), ... */
10 71ee1627 2014-11-07 marcelgmr #include <dirent.h> /* DIR, struct dirent, opendir(), ... */
11 71ee1627 2014-11-07 marcelgmr #include <sys/stat.h>
12 b7ee4ee2 2014-11-14 marcelgmr #include <fcntl.h> /* open() */
13 71ee1627 2014-11-07 marcelgmr #include <sys/wait.h> /* waitpid() */
14 b529416b 2014-11-11 marcelgmr #include <signal.h> /* struct sigaction, sigaction() */
15 8a757b0f 2015-03-07 marcelgmr #include <errno.h>
16 71ee1627 2014-11-07 marcelgmr #include <curses.h>
17 71ee1627 2014-11-07 marcelgmr
18 71ee1627 2014-11-07 marcelgmr #include "config.h"
19 71ee1627 2014-11-07 marcelgmr
20 3cd6bb98 2014-11-11 marcelgmr /* String buffers. */
21 576e768d 2014-11-08 marcelgmr #define ROWSZ 256
22 3cd6bb98 2014-11-11 marcelgmr static char ROW[ROWSZ];
23 a17e36e3 2014-11-07 marcelgmr #define STATUSSZ 256
24 3cd6bb98 2014-11-11 marcelgmr static char STATUS[STATUSSZ];
25 1447784d 2014-11-14 marcelgmr #define INPUTSZ 256
26 1447784d 2014-11-14 marcelgmr static char INPUT[INPUTSZ];
27 3cd6bb98 2014-11-11 marcelgmr
28 3cd6bb98 2014-11-11 marcelgmr /* Argument buffers for execvp(). */
29 71ee1627 2014-11-07 marcelgmr #define MAXARGS 256
30 3cd6bb98 2014-11-11 marcelgmr static char *ARGS[MAXARGS];
31 71ee1627 2014-11-07 marcelgmr
32 60128fcd 2014-11-15 marcelgmr /* Listing view parameters. */
33 60128fcd 2014-11-15 marcelgmr #define HEIGHT (LINES-4)
34 60128fcd 2014-11-15 marcelgmr #define STATUSPOS (COLS-16)
35 f05efd9e 2014-11-07 marcelgmr
36 3cd6bb98 2014-11-11 marcelgmr /* Listing view flags. */
37 bae1431c 2014-11-07 marcelgmr #define SHOW_FILES 0x01u
38 bae1431c 2014-11-07 marcelgmr #define SHOW_DIRS 0x02u
39 bae1431c 2014-11-07 marcelgmr #define SHOW_HIDDEN 0x04u
40 bae1431c 2014-11-07 marcelgmr
41 50c63039 2014-11-14 marcelgmr /* Marks parameters. */
42 18016333 2014-11-14 marcelgmr #define BULK_INIT 5
43 18016333 2014-11-14 marcelgmr #define BULK_THRESH 256
44 18016333 2014-11-14 marcelgmr
45 3cd6bb98 2014-11-11 marcelgmr /* Information associated to each entry in listing. */
46 8f853fb5 2015-03-25 marcelgmr typedef struct Row {
47 576e768d 2014-11-08 marcelgmr char *name;
48 576e768d 2014-11-08 marcelgmr off_t size;
49 18016333 2014-11-14 marcelgmr int marked;
50 eef51ac5 2015-03-24 marcelgmr } Row;
51 576e768d 2014-11-08 marcelgmr
52 5c4a6b62 2014-11-14 marcelgmr /* Dynamic array of marked entries. */
53 8f853fb5 2015-03-25 marcelgmr typedef struct Marks {
54 c04b0d89 2015-03-30 marcelgmr char dirpath[PATH_MAX];
55 18016333 2014-11-14 marcelgmr int bulk;
56 18016333 2014-11-14 marcelgmr int nentries;
57 18016333 2014-11-14 marcelgmr char **entries;
58 eef51ac5 2015-03-24 marcelgmr } Marks;
59 18016333 2014-11-14 marcelgmr
60 a022230d 2015-04-26 marcelgmr /* Line editing state. */
61 a022230d 2015-04-26 marcelgmr typedef struct Edit {
62 6110e5a1 2015-04-26 marcelgmr char buffer[INPUTSZ-1];
63 a022230d 2015-04-26 marcelgmr int left, right;
64 a022230d 2015-04-26 marcelgmr } Edit;
65 a022230d 2015-04-26 marcelgmr
66 3cd6bb98 2014-11-11 marcelgmr /* Global state. Some basic info is allocated for ten tabs. */
67 eef51ac5 2015-03-24 marcelgmr static struct Rover {
68 ada8fba9 2014-11-09 marcelgmr int tab;
69 71ee1627 2014-11-07 marcelgmr int nfiles;
70 ada8fba9 2014-11-09 marcelgmr int scroll[10];
71 7c74df90 2014-11-15 marcelgmr int esel[10];
72 47e9f26d 2014-11-09 marcelgmr uint8_t flags[10];
73 eef51ac5 2015-03-24 marcelgmr Row *rows;
74 71ee1627 2014-11-07 marcelgmr WINDOW *window;
75 c04b0d89 2015-03-30 marcelgmr char cwd[10][PATH_MAX];
76 eef51ac5 2015-03-24 marcelgmr Marks marks;
77 a022230d 2015-04-26 marcelgmr Edit edit;
78 f05efd9e 2014-11-07 marcelgmr } rover;
79 71ee1627 2014-11-07 marcelgmr
80 3cd6bb98 2014-11-11 marcelgmr /* Macros for accessing global state. */
81 7c74df90 2014-11-15 marcelgmr #define ENAME(I) rover.rows[I].name
82 7c74df90 2014-11-15 marcelgmr #define ESIZE(I) rover.rows[I].size
83 18016333 2014-11-14 marcelgmr #define MARKED(I) rover.rows[I].marked
84 3cd6bb98 2014-11-11 marcelgmr #define SCROLL rover.scroll[rover.tab]
85 7c74df90 2014-11-15 marcelgmr #define ESEL rover.esel[rover.tab]
86 3cd6bb98 2014-11-11 marcelgmr #define FLAGS rover.flags[rover.tab]
87 3cd6bb98 2014-11-11 marcelgmr #define CWD rover.cwd[rover.tab]
88 845f2b87 2014-11-14 marcelgmr
89 60128fcd 2014-11-15 marcelgmr /* Helpers. */
90 60128fcd 2014-11-15 marcelgmr #define MIN(A, B) ((A) < (B) ? (A) : (B))
91 60128fcd 2014-11-15 marcelgmr #define MAX(A, B) ((A) > (B) ? (A) : (B))
92 7c74df90 2014-11-15 marcelgmr #define ISDIR(E) (strchr((E), '/') != NULL)
93 a022230d 2015-04-26 marcelgmr
94 a022230d 2015-04-26 marcelgmr /* Line Editing Macros. */
95 6110e5a1 2015-04-26 marcelgmr #define EDIT_FULL(E) ((E).left == (E).right)
96 a022230d 2015-04-26 marcelgmr #define EDIT_CAN_LEFT(E) ((E).left)
97 6110e5a1 2015-04-26 marcelgmr #define EDIT_CAN_RIGHT(E) ((E).right < INPUTSZ-1)
98 a022230d 2015-04-26 marcelgmr #define EDIT_LEFT(E) (E).buffer[(E).right--] = (E).buffer[--(E).left]
99 a022230d 2015-04-26 marcelgmr #define EDIT_RIGHT(E) (E).buffer[(E).left++] = (E).buffer[++(E).right]
100 a022230d 2015-04-26 marcelgmr #define EDIT_INSERT(E, C) (E).buffer[(E).left++] = (C)
101 a022230d 2015-04-26 marcelgmr #define EDIT_BACKSPACE(E) (E).left--
102 a022230d 2015-04-26 marcelgmr #define EDIT_DELETE(E) (E).right++
103 6110e5a1 2015-04-26 marcelgmr #define EDIT_CLEAR(E) do { (E).left = 0; (E).right = INPUTSZ-1; } while(0)
104 7c74df90 2014-11-15 marcelgmr
105 a022230d 2015-04-26 marcelgmr typedef enum EditStat {CONTINUE, CONFIRM, CANCEL} EditStat;
106 8f853fb5 2015-03-25 marcelgmr typedef enum Color {DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, MAGENTA, WHITE} Color;
107 845f2b87 2014-11-14 marcelgmr typedef int (*PROCESS)(const char *path);
108 576e768d 2014-11-08 marcelgmr
109 d06e977d 2014-11-14 marcelgmr static void
110 eef51ac5 2015-03-24 marcelgmr init_marks(Marks *marks)
111 18016333 2014-11-14 marcelgmr {
112 18016333 2014-11-14 marcelgmr strcpy(marks->dirpath, "");
113 18016333 2014-11-14 marcelgmr marks->bulk = BULK_INIT;
114 18016333 2014-11-14 marcelgmr marks->nentries = 0;
115 f41c2ea1 2014-12-23 marcelgmr marks->entries = calloc(marks->bulk, sizeof *marks->entries);
116 18016333 2014-11-14 marcelgmr }
117 18016333 2014-11-14 marcelgmr
118 5c4a6b62 2014-11-14 marcelgmr /* Unmark all entries. */
119 d06e977d 2014-11-14 marcelgmr static void
120 eef51ac5 2015-03-24 marcelgmr mark_none(Marks *marks)
121 18016333 2014-11-14 marcelgmr {
122 18016333 2014-11-14 marcelgmr int i;
123 18016333 2014-11-14 marcelgmr
124 8ba07de5 2014-11-14 marcelgmr strcpy(marks->dirpath, "");
125 18016333 2014-11-14 marcelgmr for (i = 0; i < marks->bulk && marks->nentries; i++)
126 18016333 2014-11-14 marcelgmr if (marks->entries[i]) {
127 18016333 2014-11-14 marcelgmr free(marks->entries[i]);
128 9a852838 2014-11-14 marcelgmr marks->entries[i] = NULL;
129 18016333 2014-11-14 marcelgmr marks->nentries--;
130 18016333 2014-11-14 marcelgmr }
131 18016333 2014-11-14 marcelgmr if (marks->bulk > BULK_THRESH) {
132 18016333 2014-11-14 marcelgmr /* Reset bulk to free some memory. */
133 18016333 2014-11-14 marcelgmr free(marks->entries);
134 18016333 2014-11-14 marcelgmr marks->bulk = BULK_INIT;
135 f41c2ea1 2014-12-23 marcelgmr marks->entries = calloc(marks->bulk, sizeof *marks->entries);
136 18016333 2014-11-14 marcelgmr }
137 18016333 2014-11-14 marcelgmr }
138 18016333 2014-11-14 marcelgmr
139 d06e977d 2014-11-14 marcelgmr static void
140 eef51ac5 2015-03-24 marcelgmr add_mark(Marks *marks, char *dirpath, char *entry)
141 18016333 2014-11-14 marcelgmr {
142 18016333 2014-11-14 marcelgmr int i;
143 18016333 2014-11-14 marcelgmr
144 18016333 2014-11-14 marcelgmr if (!strcmp(marks->dirpath, dirpath)) {
145 18016333 2014-11-14 marcelgmr /* Append mark to directory. */
146 18016333 2014-11-14 marcelgmr if (marks->nentries == marks->bulk) {
147 18016333 2014-11-14 marcelgmr /* Expand bulk to accomodate new entry. */
148 18016333 2014-11-14 marcelgmr int extra = marks->bulk >> 1;
149 18016333 2014-11-14 marcelgmr marks->bulk += extra; /* bulk *= 1.5; */
150 f41c2ea1 2014-12-23 marcelgmr marks->entries = realloc(marks->entries,
151 f41c2ea1 2014-12-23 marcelgmr marks->bulk * sizeof *marks->entries);
152 f41c2ea1 2014-12-23 marcelgmr memset(&marks->entries[marks->nentries], 0,
153 f41c2ea1 2014-12-23 marcelgmr extra * sizeof *marks->entries);
154 18016333 2014-11-14 marcelgmr i = marks->nentries;
155 590d39af 2014-12-02 marcelgmr } else {
156 18016333 2014-11-14 marcelgmr /* Search for empty slot (there must be one). */
157 18016333 2014-11-14 marcelgmr for (i = 0; i < marks->bulk; i++)
158 18016333 2014-11-14 marcelgmr if (!marks->entries[i])
159 18016333 2014-11-14 marcelgmr break;
160 18016333 2014-11-14 marcelgmr }
161 590d39af 2014-12-02 marcelgmr } else {
162 18016333 2014-11-14 marcelgmr /* Directory changed. Discard old marks. */
163 18016333 2014-11-14 marcelgmr mark_none(marks);
164 18016333 2014-11-14 marcelgmr strcpy(marks->dirpath, dirpath);
165 18016333 2014-11-14 marcelgmr i = 0;
166 18016333 2014-11-14 marcelgmr }
167 f41c2ea1 2014-12-23 marcelgmr marks->entries[i] = malloc(strlen(entry) + 1);
168 18016333 2014-11-14 marcelgmr strcpy(marks->entries[i], entry);
169 18016333 2014-11-14 marcelgmr marks->nentries++;
170 18016333 2014-11-14 marcelgmr }
171 18016333 2014-11-14 marcelgmr
172 d06e977d 2014-11-14 marcelgmr static void
173 eef51ac5 2015-03-24 marcelgmr del_mark(Marks *marks, char *entry)
174 18016333 2014-11-14 marcelgmr {
175 18016333 2014-11-14 marcelgmr int i;
176 18016333 2014-11-14 marcelgmr
177 18016333 2014-11-14 marcelgmr if (marks->nentries > 1) {
178 18016333 2014-11-14 marcelgmr for (i = 0; i < marks->bulk; i++)
179 18016333 2014-11-14 marcelgmr if (marks->entries[i] && !strcmp(marks->entries[i], entry))
180 18016333 2014-11-14 marcelgmr break;
181 18016333 2014-11-14 marcelgmr free(marks->entries[i]);
182 18016333 2014-11-14 marcelgmr marks->entries[i] = NULL;
183 18016333 2014-11-14 marcelgmr marks->nentries--;
184 590d39af 2014-12-02 marcelgmr } else
185 590d39af 2014-12-02 marcelgmr mark_none(marks);
186 18016333 2014-11-14 marcelgmr }
187 18016333 2014-11-14 marcelgmr
188 d06e977d 2014-11-14 marcelgmr static void
189 eef51ac5 2015-03-24 marcelgmr free_marks(Marks *marks)
190 18016333 2014-11-14 marcelgmr {
191 18016333 2014-11-14 marcelgmr int i;
192 18016333 2014-11-14 marcelgmr
193 18016333 2014-11-14 marcelgmr for (i = 0; i < marks->bulk && marks->nentries; i++)
194 18016333 2014-11-14 marcelgmr if (marks->entries[i]) {
195 18016333 2014-11-14 marcelgmr free(marks->entries[i]);
196 18016333 2014-11-14 marcelgmr marks->nentries--;
197 18016333 2014-11-14 marcelgmr }
198 18016333 2014-11-14 marcelgmr free(marks->entries);
199 18016333 2014-11-14 marcelgmr }
200 b25c9834 2014-11-14 marcelgmr
201 7896cfae 2015-04-17 marcelgmr static void update_view();
202 18016333 2014-11-14 marcelgmr
203 7896cfae 2015-04-17 marcelgmr /* SIGSEGV handler: clean up curses before exiting. */
204 7896cfae 2015-04-17 marcelgmr static void
205 7896cfae 2015-04-17 marcelgmr handle_segv(int sig)
206 7896cfae 2015-04-17 marcelgmr {
207 7896cfae 2015-04-17 marcelgmr (void) sig;
208 7896cfae 2015-04-17 marcelgmr endwin();
209 7896cfae 2015-04-17 marcelgmr fprintf(stderr, "Received SIGSEGV (segmentation fault).\n");
210 7896cfae 2015-04-17 marcelgmr exit(1);
211 7896cfae 2015-04-17 marcelgmr }
212 e1c42cfe 2014-11-11 marcelgmr
213 7896cfae 2015-04-17 marcelgmr /* SIGWINCH handler: resize application according to new terminal settings. */
214 71ee1627 2014-11-07 marcelgmr static void
215 7896cfae 2015-04-17 marcelgmr handle_winch(int sig)
216 71ee1627 2014-11-07 marcelgmr {
217 7896cfae 2015-04-17 marcelgmr (void) sig;
218 7896cfae 2015-04-17 marcelgmr delwin(rover.window);
219 7896cfae 2015-04-17 marcelgmr endwin();
220 7896cfae 2015-04-17 marcelgmr refresh();
221 7896cfae 2015-04-17 marcelgmr clear();
222 7896cfae 2015-04-17 marcelgmr rover.window = subwin(stdscr, LINES - 2, COLS, 1, 0);
223 7896cfae 2015-04-17 marcelgmr update_view();
224 7896cfae 2015-04-17 marcelgmr }
225 7896cfae 2015-04-17 marcelgmr
226 7896cfae 2015-04-17 marcelgmr static void
227 7896cfae 2015-04-17 marcelgmr enable_handlers()
228 7896cfae 2015-04-17 marcelgmr {
229 e1c42cfe 2014-11-11 marcelgmr struct sigaction sa;
230 e1c42cfe 2014-11-11 marcelgmr
231 7896cfae 2015-04-17 marcelgmr memset(&sa, 0, sizeof (struct sigaction));
232 7896cfae 2015-04-17 marcelgmr sa.sa_handler = handle_segv;
233 7896cfae 2015-04-17 marcelgmr sigaction(SIGSEGV, &sa, NULL);
234 7896cfae 2015-04-17 marcelgmr sa.sa_handler = handle_winch;
235 7896cfae 2015-04-17 marcelgmr sigaction(SIGWINCH, &sa, NULL);
236 7896cfae 2015-04-17 marcelgmr }
237 7896cfae 2015-04-17 marcelgmr
238 7896cfae 2015-04-17 marcelgmr static void
239 7896cfae 2015-04-17 marcelgmr disable_handlers()
240 7896cfae 2015-04-17 marcelgmr {
241 7896cfae 2015-04-17 marcelgmr struct sigaction sa;
242 7896cfae 2015-04-17 marcelgmr
243 7896cfae 2015-04-17 marcelgmr memset(&sa, 0, sizeof (struct sigaction));
244 7896cfae 2015-04-17 marcelgmr sa.sa_handler = SIG_DFL;
245 7896cfae 2015-04-17 marcelgmr sigaction(SIGSEGV, &sa, NULL);
246 7896cfae 2015-04-17 marcelgmr sigaction(SIGWINCH, &sa, NULL);
247 7896cfae 2015-04-17 marcelgmr }
248 7896cfae 2015-04-17 marcelgmr
249 7896cfae 2015-04-17 marcelgmr /* Do a fork-exec to external program (e.g. $EDITOR). */
250 7896cfae 2015-04-17 marcelgmr static void
251 7896cfae 2015-04-17 marcelgmr spawn()
252 7896cfae 2015-04-17 marcelgmr {
253 7896cfae 2015-04-17 marcelgmr pid_t pid;
254 7896cfae 2015-04-17 marcelgmr int status;
255 7896cfae 2015-04-17 marcelgmr
256 7896cfae 2015-04-17 marcelgmr pid = fork();
257 7896cfae 2015-04-17 marcelgmr if (pid > 0) {
258 7896cfae 2015-04-17 marcelgmr /* fork() succeeded. */
259 7896cfae 2015-04-17 marcelgmr disable_handlers();
260 7896cfae 2015-04-17 marcelgmr endwin();
261 7896cfae 2015-04-17 marcelgmr waitpid(pid, &status, 0);
262 7896cfae 2015-04-17 marcelgmr enable_handlers();
263 7896cfae 2015-04-17 marcelgmr kill(getpid(), SIGWINCH);
264 7896cfae 2015-04-17 marcelgmr } else if (pid == 0) {
265 7896cfae 2015-04-17 marcelgmr /* Child process. */
266 7896cfae 2015-04-17 marcelgmr execvp(ARGS[0], ARGS);
267 7896cfae 2015-04-17 marcelgmr }
268 7896cfae 2015-04-17 marcelgmr }
269 7896cfae 2015-04-17 marcelgmr
270 7896cfae 2015-04-17 marcelgmr /* Curses setup. */
271 7896cfae 2015-04-17 marcelgmr static void
272 7896cfae 2015-04-17 marcelgmr init_term()
273 7896cfae 2015-04-17 marcelgmr {
274 71ee1627 2014-11-07 marcelgmr setlocale(LC_ALL, "");
275 71ee1627 2014-11-07 marcelgmr initscr();
276 71ee1627 2014-11-07 marcelgmr cbreak(); /* Get one character at a time. */
277 71ee1627 2014-11-07 marcelgmr noecho();
278 71ee1627 2014-11-07 marcelgmr nonl(); /* No NL->CR/NL on output. */
279 71ee1627 2014-11-07 marcelgmr intrflush(stdscr, FALSE);
280 71ee1627 2014-11-07 marcelgmr keypad(stdscr, TRUE);
281 71ee1627 2014-11-07 marcelgmr curs_set(FALSE); /* Hide blinking cursor. */
282 71ee1627 2014-11-07 marcelgmr if (has_colors()) {
283 eb265708 2015-03-24 marcelgmr short bg;
284 71ee1627 2014-11-07 marcelgmr start_color();
285 eb265708 2015-03-24 marcelgmr #ifdef NCURSES_EXT_FUNCS
286 eb265708 2015-03-24 marcelgmr use_default_colors();
287 eb265708 2015-03-24 marcelgmr bg = -1;
288 eb265708 2015-03-24 marcelgmr #else
289 eb265708 2015-03-24 marcelgmr bg = COLOR_BLACK;
290 eb265708 2015-03-24 marcelgmr #endif
291 eb265708 2015-03-24 marcelgmr init_pair(RED, COLOR_RED, bg);
292 eb265708 2015-03-24 marcelgmr init_pair(GREEN, COLOR_GREEN, bg);
293 eb265708 2015-03-24 marcelgmr init_pair(YELLOW, COLOR_YELLOW, bg);
294 eb265708 2015-03-24 marcelgmr init_pair(BLUE, COLOR_BLUE, bg);
295 eb265708 2015-03-24 marcelgmr init_pair(CYAN, COLOR_CYAN, bg);
296 eb265708 2015-03-24 marcelgmr init_pair(MAGENTA, COLOR_MAGENTA, bg);
297 eb265708 2015-03-24 marcelgmr init_pair(WHITE, COLOR_WHITE, bg);
298 71ee1627 2014-11-07 marcelgmr }
299 7c74df90 2014-11-15 marcelgmr atexit((void (*)(void)) endwin);
300 7896cfae 2015-04-17 marcelgmr enable_handlers();
301 71ee1627 2014-11-07 marcelgmr }
302 71ee1627 2014-11-07 marcelgmr
303 3cd6bb98 2014-11-11 marcelgmr /* Update the listing view. */
304 71ee1627 2014-11-07 marcelgmr static void
305 743e70b8 2014-11-14 marcelgmr update_view()
306 71ee1627 2014-11-07 marcelgmr {
307 222db952 2014-11-08 marcelgmr int i, j;
308 38347b2a 2015-03-30 marcelgmr int numsize;
309 576e768d 2014-11-08 marcelgmr int ishidden, isdir;
310 18016333 2014-11-14 marcelgmr int marking;
311 71ee1627 2014-11-07 marcelgmr
312 41aae0b0 2014-11-11 marcelgmr mvhline(0, 0, ' ', COLS);
313 41aae0b0 2014-11-11 marcelgmr attr_on(A_BOLD, NULL);
314 41aae0b0 2014-11-11 marcelgmr color_set(RVC_TABNUM, NULL);
315 38347b2a 2015-03-30 marcelgmr mvaddch(0, COLS - 2, rover.tab + '0');
316 41aae0b0 2014-11-11 marcelgmr color_set(DEFAULT, NULL);
317 41aae0b0 2014-11-11 marcelgmr attr_off(A_BOLD, NULL);
318 38347b2a 2015-03-30 marcelgmr if (rover.marks.nentries) {
319 e5fe9820 2015-04-17 marcelgmr numsize = snprintf(STATUS, STATUSSZ, "%d", rover.marks.nentries);
320 38347b2a 2015-03-30 marcelgmr color_set(RVC_NMARKS, NULL);
321 38347b2a 2015-03-30 marcelgmr mvaddstr(0, COLS - 3 - numsize, STATUS);
322 38347b2a 2015-03-30 marcelgmr color_set(DEFAULT, NULL);
323 38347b2a 2015-03-30 marcelgmr } else
324 38347b2a 2015-03-30 marcelgmr numsize = -1;
325 38347b2a 2015-03-30 marcelgmr color_set(RVC_CWD, NULL);
326 38347b2a 2015-03-30 marcelgmr mvaddnstr(0, 0, CWD, COLS - 4 - numsize);
327 38347b2a 2015-03-30 marcelgmr color_set(DEFAULT, NULL);
328 41aae0b0 2014-11-11 marcelgmr wcolor_set(rover.window, RVC_BORDER, NULL);
329 41aae0b0 2014-11-11 marcelgmr wborder(rover.window, 0, 0, 0, 0, 0, 0, 0, 0);
330 41aae0b0 2014-11-11 marcelgmr wcolor_set(rover.window, DEFAULT, NULL);
331 667d4907 2014-11-14 marcelgmr /* Selection might not be visible, due to cursor wrapping or window
332 667d4907 2014-11-14 marcelgmr shrinking. In that case, the scroll must be moved to make it visible. */
333 60128fcd 2014-11-15 marcelgmr SCROLL = MAX(MIN(SCROLL, ESEL), ESEL - HEIGHT + 1);
334 18016333 2014-11-14 marcelgmr marking = !strcmp(CWD, rover.marks.dirpath);
335 ada8fba9 2014-11-09 marcelgmr for (i = 0, j = SCROLL; i < HEIGHT && j < rover.nfiles; i++, j++) {
336 7c74df90 2014-11-15 marcelgmr ishidden = ENAME(j)[0] == '.';
337 7c74df90 2014-11-15 marcelgmr isdir = ISDIR(ENAME(j));
338 7c74df90 2014-11-15 marcelgmr if (j == ESEL)
339 f05efd9e 2014-11-07 marcelgmr wattr_on(rover.window, A_REVERSE, NULL);
340 576e768d 2014-11-08 marcelgmr if (ishidden)
341 37233869 2014-11-07 marcelgmr wcolor_set(rover.window, RVC_HIDDEN, NULL);
342 576e768d 2014-11-08 marcelgmr else if (isdir)
343 37233869 2014-11-07 marcelgmr wcolor_set(rover.window, RVC_DIR, NULL);
344 37233869 2014-11-07 marcelgmr else
345 37233869 2014-11-07 marcelgmr wcolor_set(rover.window, RVC_FILE, NULL);
346 165b8d9e 2015-04-23 marcelgmr if (!isdir) {
347 ddca2129 2015-04-23 marcelgmr char *suffix, *suffixes = "BKMGTPEZY";
348 ddca2129 2015-04-23 marcelgmr off_t human_size = ESIZE(j) * 10;
349 ddca2129 2015-04-23 marcelgmr for (suffix = suffixes; human_size >= 10240; suffix++)
350 165b8d9e 2015-04-23 marcelgmr human_size = (human_size + 512) / 1024;
351 ddca2129 2015-04-23 marcelgmr if (*suffix == 'B')
352 ddca2129 2015-04-23 marcelgmr snprintf(ROW, ROWSZ, "%s%*d %c", ENAME(j),
353 ddca2129 2015-04-23 marcelgmr (int) (COLS - strlen(ENAME(j)) - 6),
354 ddca2129 2015-04-23 marcelgmr (int) human_size / 10, *suffix);
355 ddca2129 2015-04-23 marcelgmr else
356 ddca2129 2015-04-23 marcelgmr snprintf(ROW, ROWSZ, "%s%*d.%d %c", ENAME(j),
357 ddca2129 2015-04-23 marcelgmr (int) (COLS - strlen(ENAME(j)) - 8),
358 ddca2129 2015-04-23 marcelgmr (int) human_size / 10, (int) human_size % 10, *suffix);
359 165b8d9e 2015-04-23 marcelgmr } else
360 7c74df90 2014-11-15 marcelgmr strcpy(ROW, ENAME(j));
361 a81852bf 2014-11-08 marcelgmr mvwhline(rover.window, i + 1, 1, ' ', COLS - 2);
362 a651a76d 2014-11-14 marcelgmr if (marking && MARKED(j))
363 e6a7541a 2014-11-14 marcelgmr mvwaddch(rover.window, i + 1, 1, RVS_MARK);
364 18016333 2014-11-14 marcelgmr else
365 18016333 2014-11-14 marcelgmr mvwaddch(rover.window, i + 1, 1, ' ');
366 b9837001 2014-11-24 marcelgmr mvwaddnstr(rover.window, i + 1, 2, ROW, COLS - 4);
367 37233869 2014-11-07 marcelgmr wcolor_set(rover.window, DEFAULT, NULL);
368 7c74df90 2014-11-15 marcelgmr if (j == ESEL)
369 f05efd9e 2014-11-07 marcelgmr wattr_off(rover.window, A_REVERSE, NULL);
370 a81852bf 2014-11-08 marcelgmr }
371 37087140 2015-03-30 marcelgmr for (;i < HEIGHT; i++)
372 37087140 2015-03-30 marcelgmr mvwhline(rover.window, i + 1, 1, ' ', COLS - 2);
373 a81852bf 2014-11-08 marcelgmr if (rover.nfiles > HEIGHT) {
374 a81852bf 2014-11-08 marcelgmr int center, height;
375 ada8fba9 2014-11-09 marcelgmr center = (SCROLL + (HEIGHT >> 1)) * HEIGHT / rover.nfiles;
376 a81852bf 2014-11-08 marcelgmr height = (HEIGHT-1) * HEIGHT / rover.nfiles;
377 a81852bf 2014-11-08 marcelgmr if (!height) height = 1;
378 a81852bf 2014-11-08 marcelgmr wcolor_set(rover.window, RVC_BORDER, NULL);
379 a81852bf 2014-11-08 marcelgmr wborder(rover.window, 0, 0, 0, 0, 0, 0, 0, 0);
380 2a2b72b6 2014-11-08 marcelgmr wcolor_set(rover.window, RVC_SCROLLBAR, NULL);
381 4c8a3f34 2014-11-14 marcelgmr mvwvline(rover.window, center-(height>>1)+1, COLS-1, RVS_SCROLLBAR, height);
382 a81852bf 2014-11-08 marcelgmr wcolor_set(rover.window, DEFAULT, NULL);
383 71ee1627 2014-11-07 marcelgmr }
384 47e9f26d 2014-11-09 marcelgmr STATUS[0] = FLAGS & SHOW_FILES ? 'F' : ' ';
385 47e9f26d 2014-11-09 marcelgmr STATUS[1] = FLAGS & SHOW_DIRS ? 'D' : ' ';
386 47e9f26d 2014-11-09 marcelgmr STATUS[2] = FLAGS & SHOW_HIDDEN ? 'H' : ' ';
387 bb90b2d2 2014-11-08 marcelgmr if (!rover.nfiles)
388 222db952 2014-11-08 marcelgmr strcpy(ROW, "0/0");
389 bb90b2d2 2014-11-08 marcelgmr else
390 e5fe9820 2015-04-17 marcelgmr snprintf(ROW, ROWSZ, "%d/%d", ESEL + 1, rover.nfiles);
391 e5fe9820 2015-04-17 marcelgmr snprintf(STATUS+3, STATUSSZ-3, "%12s", ROW);
392 e1f31679 2014-11-07 marcelgmr color_set(RVC_STATUS, NULL);
393 50c63039 2014-11-14 marcelgmr mvaddstr(LINES - 1, STATUSPOS, STATUS);
394 e1f31679 2014-11-07 marcelgmr color_set(DEFAULT, NULL);
395 37087140 2015-03-30 marcelgmr wrefresh(rover.window);
396 71ee1627 2014-11-07 marcelgmr }
397 71ee1627 2014-11-07 marcelgmr
398 7896cfae 2015-04-17 marcelgmr /* Show a message on the status bar. */
399 7896cfae 2015-04-17 marcelgmr static void
400 7896cfae 2015-04-17 marcelgmr message(const char *msg, Color color)
401 41aae0b0 2014-11-11 marcelgmr {
402 7896cfae 2015-04-17 marcelgmr int len, pos;
403 7896cfae 2015-04-17 marcelgmr
404 7896cfae 2015-04-17 marcelgmr len = strlen(msg);
405 7896cfae 2015-04-17 marcelgmr pos = (STATUSPOS - len) >> 1;
406 7896cfae 2015-04-17 marcelgmr attr_on(A_BOLD, NULL);
407 7896cfae 2015-04-17 marcelgmr color_set(color, NULL);
408 7896cfae 2015-04-17 marcelgmr mvaddstr(LINES - 1, pos, msg);
409 7896cfae 2015-04-17 marcelgmr color_set(DEFAULT, NULL);
410 7896cfae 2015-04-17 marcelgmr attr_off(A_BOLD, NULL);
411 41aae0b0 2014-11-11 marcelgmr }
412 41aae0b0 2014-11-11 marcelgmr
413 7896cfae 2015-04-17 marcelgmr /* Clear message area, leaving only status info. */
414 41aae0b0 2014-11-11 marcelgmr static void
415 7896cfae 2015-04-17 marcelgmr clear_message()
416 41aae0b0 2014-11-11 marcelgmr {
417 7896cfae 2015-04-17 marcelgmr mvhline(LINES - 1, 0, ' ', STATUSPOS);
418 41aae0b0 2014-11-11 marcelgmr }
419 41aae0b0 2014-11-11 marcelgmr
420 3cd6bb98 2014-11-11 marcelgmr /* Comparison used to sort listing entries. */
421 3cd6bb98 2014-11-11 marcelgmr static int
422 3cd6bb98 2014-11-11 marcelgmr rowcmp(const void *a, const void *b)
423 3cd6bb98 2014-11-11 marcelgmr {
424 3cd6bb98 2014-11-11 marcelgmr int isdir1, isdir2, cmpdir;
425 eef51ac5 2015-03-24 marcelgmr const Row *r1 = a;
426 eef51ac5 2015-03-24 marcelgmr const Row *r2 = b;
427 7c74df90 2014-11-15 marcelgmr isdir1 = ISDIR(r1->name);
428 7c74df90 2014-11-15 marcelgmr isdir2 = ISDIR(r2->name);
429 3cd6bb98 2014-11-11 marcelgmr cmpdir = isdir2 - isdir1;
430 3cd6bb98 2014-11-11 marcelgmr return cmpdir ? cmpdir : strcoll(r1->name, r2->name);
431 3cd6bb98 2014-11-11 marcelgmr }
432 3cd6bb98 2014-11-11 marcelgmr
433 496dd2b0 2015-03-24 marcelgmr /* Get all entries in current working directory. */
434 3cd6bb98 2014-11-11 marcelgmr static int
435 496dd2b0 2015-03-24 marcelgmr ls(Row **rowsp, uint8_t flags)
436 3cd6bb98 2014-11-11 marcelgmr {
437 3cd6bb98 2014-11-11 marcelgmr DIR *dp;
438 3cd6bb98 2014-11-11 marcelgmr struct dirent *ep;
439 3cd6bb98 2014-11-11 marcelgmr struct stat statbuf;
440 eef51ac5 2015-03-24 marcelgmr Row *rows;
441 3cd6bb98 2014-11-11 marcelgmr int i, n;
442 3cd6bb98 2014-11-11 marcelgmr
443 496dd2b0 2015-03-24 marcelgmr if(!(dp = opendir("."))) return -1;
444 3cd6bb98 2014-11-11 marcelgmr n = -2; /* We don't want the entries "." and "..". */
445 3cd6bb98 2014-11-11 marcelgmr while (readdir(dp)) n++;
446 3cd6bb98 2014-11-11 marcelgmr rewinddir(dp);
447 f41c2ea1 2014-12-23 marcelgmr rows = malloc(n * sizeof *rows);
448 3cd6bb98 2014-11-11 marcelgmr i = 0;
449 3cd6bb98 2014-11-11 marcelgmr while ((ep = readdir(dp))) {
450 3cd6bb98 2014-11-11 marcelgmr if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, ".."))
451 3cd6bb98 2014-11-11 marcelgmr continue;
452 3cd6bb98 2014-11-11 marcelgmr if (!(flags & SHOW_HIDDEN) && ep->d_name[0] == '.')
453 3cd6bb98 2014-11-11 marcelgmr continue;
454 f1e9b90e 2015-03-30 marcelgmr lstat(ep->d_name, &statbuf);
455 3cd6bb98 2014-11-11 marcelgmr if (S_ISDIR(statbuf.st_mode)) {
456 3cd6bb98 2014-11-11 marcelgmr if (flags & SHOW_DIRS) {
457 f41c2ea1 2014-12-23 marcelgmr rows[i].name = malloc(strlen(ep->d_name) + 2);
458 3cd6bb98 2014-11-11 marcelgmr strcpy(rows[i].name, ep->d_name);
459 3cd6bb98 2014-11-11 marcelgmr strcat(rows[i].name, "/");
460 3cd6bb98 2014-11-11 marcelgmr i++;
461 3cd6bb98 2014-11-11 marcelgmr }
462 590d39af 2014-12-02 marcelgmr } else if (flags & SHOW_FILES) {
463 f41c2ea1 2014-12-23 marcelgmr rows[i].name = malloc(strlen(ep->d_name) + 1);
464 3cd6bb98 2014-11-11 marcelgmr strcpy(rows[i].name, ep->d_name);
465 3cd6bb98 2014-11-11 marcelgmr rows[i].size = statbuf.st_size;
466 3cd6bb98 2014-11-11 marcelgmr i++;
467 3cd6bb98 2014-11-11 marcelgmr }
468 3cd6bb98 2014-11-11 marcelgmr }
469 3cd6bb98 2014-11-11 marcelgmr n = i; /* Ignore unused space in array caused by filters. */
470 f41c2ea1 2014-12-23 marcelgmr qsort(rows, n, sizeof (*rows), rowcmp);
471 3cd6bb98 2014-11-11 marcelgmr closedir(dp);
472 3cd6bb98 2014-11-11 marcelgmr *rowsp = rows;
473 3cd6bb98 2014-11-11 marcelgmr return n;
474 3cd6bb98 2014-11-11 marcelgmr }
475 3cd6bb98 2014-11-11 marcelgmr
476 3cd6bb98 2014-11-11 marcelgmr static void
477 eef51ac5 2015-03-24 marcelgmr free_rows(Row **rowsp, int nfiles)
478 3cd6bb98 2014-11-11 marcelgmr {
479 3cd6bb98 2014-11-11 marcelgmr int i;
480 3cd6bb98 2014-11-11 marcelgmr
481 3cd6bb98 2014-11-11 marcelgmr for (i = 0; i < nfiles; i++)
482 3cd6bb98 2014-11-11 marcelgmr free((*rowsp)[i].name);
483 3cd6bb98 2014-11-11 marcelgmr free(*rowsp);
484 3cd6bb98 2014-11-11 marcelgmr *rowsp = NULL;
485 3cd6bb98 2014-11-11 marcelgmr }
486 3cd6bb98 2014-11-11 marcelgmr
487 8f853fb5 2015-03-25 marcelgmr /* Change working directory to the path in CWD. */
488 71ee1627 2014-11-07 marcelgmr static void
489 ada8fba9 2014-11-09 marcelgmr cd(int reset)
490 71ee1627 2014-11-07 marcelgmr {
491 18016333 2014-11-14 marcelgmr int i, j;
492 18016333 2014-11-14 marcelgmr
493 d0cdbd3c 2014-11-15 marcelgmr message("Loading...", CYAN);
494 d0cdbd3c 2014-11-15 marcelgmr refresh();
495 60128fcd 2014-11-15 marcelgmr if (reset) ESEL = SCROLL = 0;
496 ea0214c3 2014-11-09 marcelgmr chdir(CWD);
497 f05efd9e 2014-11-07 marcelgmr if (rover.nfiles)
498 576e768d 2014-11-08 marcelgmr free_rows(&rover.rows, rover.nfiles);
499 496dd2b0 2015-03-24 marcelgmr rover.nfiles = ls(&rover.rows, FLAGS);
500 18016333 2014-11-14 marcelgmr if (!strcmp(CWD, rover.marks.dirpath)) {
501 18016333 2014-11-14 marcelgmr for (i = 0; i < rover.nfiles; i++) {
502 18016333 2014-11-14 marcelgmr for (j = 0; j < rover.marks.bulk; j++)
503 18016333 2014-11-14 marcelgmr if (
504 18016333 2014-11-14 marcelgmr rover.marks.entries[j] &&
505 7c74df90 2014-11-15 marcelgmr !strcmp(rover.marks.entries[j], ENAME(i))
506 18016333 2014-11-14 marcelgmr )
507 18016333 2014-11-14 marcelgmr break;
508 18016333 2014-11-14 marcelgmr MARKED(i) = j < rover.marks.bulk;
509 18016333 2014-11-14 marcelgmr }
510 590d39af 2014-12-02 marcelgmr } else
511 590d39af 2014-12-02 marcelgmr for (i = 0; i < rover.nfiles; i++)
512 590d39af 2014-12-02 marcelgmr MARKED(i) = 0;
513 d0cdbd3c 2014-11-15 marcelgmr clear_message();
514 743e70b8 2014-11-14 marcelgmr update_view();
515 548a3163 2014-11-14 marcelgmr }
516 548a3163 2014-11-14 marcelgmr
517 430cd7d8 2014-11-19 marcelgmr /* Select a target entry, if it is present. */
518 430cd7d8 2014-11-19 marcelgmr static void
519 430cd7d8 2014-11-19 marcelgmr try_to_sel(const char *target)
520 430cd7d8 2014-11-19 marcelgmr {
521 430cd7d8 2014-11-19 marcelgmr ESEL = 0;
522 f499f7c7 2014-12-03 marcelgmr if (!ISDIR(target))
523 f499f7c7 2014-12-03 marcelgmr while ((ESEL+1) < rover.nfiles && ISDIR(ENAME(ESEL)))
524 f499f7c7 2014-12-03 marcelgmr ESEL++;
525 00ecdc1e 2014-11-19 marcelgmr while ((ESEL+1) < rover.nfiles && strcoll(ENAME(ESEL), target) < 0)
526 430cd7d8 2014-11-19 marcelgmr ESEL++;
527 430cd7d8 2014-11-19 marcelgmr if (rover.nfiles > HEIGHT) {
528 430cd7d8 2014-11-19 marcelgmr SCROLL = ESEL - (HEIGHT >> 1);
529 430cd7d8 2014-11-19 marcelgmr SCROLL = MIN(MAX(SCROLL, 0), rover.nfiles - HEIGHT);
530 430cd7d8 2014-11-19 marcelgmr }
531 430cd7d8 2014-11-19 marcelgmr }
532 430cd7d8 2014-11-19 marcelgmr
533 00ecdc1e 2014-11-19 marcelgmr /* Reload CWD, but try to keep selection. */
534 00ecdc1e 2014-11-19 marcelgmr static void
535 00ecdc1e 2014-11-19 marcelgmr reload()
536 00ecdc1e 2014-11-19 marcelgmr {
537 00ecdc1e 2014-11-19 marcelgmr if (rover.nfiles) {
538 00ecdc1e 2014-11-19 marcelgmr strcpy(INPUT, ENAME(ESEL));
539 00ecdc1e 2014-11-19 marcelgmr cd(1);
540 00ecdc1e 2014-11-19 marcelgmr try_to_sel(INPUT);
541 00ecdc1e 2014-11-19 marcelgmr update_view();
542 590d39af 2014-12-02 marcelgmr } else
543 590d39af 2014-12-02 marcelgmr cd(1);
544 00ecdc1e 2014-11-19 marcelgmr }
545 00ecdc1e 2014-11-19 marcelgmr
546 5c4a6b62 2014-11-14 marcelgmr /* Recursively process a source directory using CWD as destination root.
547 c8891c90 2014-12-23 marcelgmr For each node (i.e. directory), do the following:
548 c8891c90 2014-12-23 marcelgmr 1. call pre(destination);
549 c8891c90 2014-12-23 marcelgmr 2. call proc() on every child leaf (i.e. files);
550 c8891c90 2014-12-23 marcelgmr 3. recurse into every child node;
551 c8891c90 2014-12-23 marcelgmr 4. call pos(source).
552 c8891c90 2014-12-23 marcelgmr E.g. to move directory /src/ (and all its contents) inside /dst/:
553 c8891c90 2014-12-23 marcelgmr strcpy(CWD, "/dst/");
554 c8891c90 2014-12-23 marcelgmr process_dir(adddir, movfile, deldir, "/src/"); */
555 2a117cd2 2014-11-15 marcelgmr static int
556 845f2b87 2014-11-14 marcelgmr process_dir(PROCESS pre, PROCESS proc, PROCESS pos, const char *path)
557 548a3163 2014-11-14 marcelgmr {
558 2a117cd2 2014-11-15 marcelgmr int ret;
559 548a3163 2014-11-14 marcelgmr DIR *dp;
560 548a3163 2014-11-14 marcelgmr struct dirent *ep;
561 548a3163 2014-11-14 marcelgmr struct stat statbuf;
562 c04b0d89 2015-03-30 marcelgmr char subpath[PATH_MAX];
563 548a3163 2014-11-14 marcelgmr
564 2a117cd2 2014-11-15 marcelgmr ret = 0;
565 68ff0591 2014-11-14 marcelgmr if (pre) {
566 c04b0d89 2015-03-30 marcelgmr char dstpath[PATH_MAX];
567 68ff0591 2014-11-14 marcelgmr strcpy(dstpath, CWD);
568 68ff0591 2014-11-14 marcelgmr strcat(dstpath, path + strlen(rover.marks.dirpath));
569 2a117cd2 2014-11-15 marcelgmr ret |= pre(dstpath);
570 68ff0591 2014-11-14 marcelgmr }
571 2a117cd2 2014-11-15 marcelgmr if(!(dp = opendir(path))) return -1;
572 548a3163 2014-11-14 marcelgmr while ((ep = readdir(dp))) {
573 548a3163 2014-11-14 marcelgmr if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, ".."))
574 548a3163 2014-11-14 marcelgmr continue;
575 e5fe9820 2015-04-17 marcelgmr snprintf(subpath, PATH_MAX, "%s%s", path, ep->d_name);
576 548a3163 2014-11-14 marcelgmr stat(subpath, &statbuf);
577 548a3163 2014-11-14 marcelgmr if (S_ISDIR(statbuf.st_mode)) {
578 548a3163 2014-11-14 marcelgmr strcat(subpath, "/");
579 2a117cd2 2014-11-15 marcelgmr ret |= process_dir(pre, proc, pos, subpath);
580 590d39af 2014-12-02 marcelgmr } else
581 590d39af 2014-12-02 marcelgmr ret |= proc(subpath);
582 548a3163 2014-11-14 marcelgmr }
583 548a3163 2014-11-14 marcelgmr closedir(dp);
584 2a117cd2 2014-11-15 marcelgmr if (pos) ret |= pos(path);
585 2a117cd2 2014-11-15 marcelgmr return ret;
586 548a3163 2014-11-14 marcelgmr }
587 548a3163 2014-11-14 marcelgmr
588 5c4a6b62 2014-11-14 marcelgmr /* Process all marked entries using CWD as destination root.
589 c8891c90 2014-12-23 marcelgmr All marked entries that are directories will be recursively processed.
590 c8891c90 2014-12-23 marcelgmr See process_dir() for details on the parameters. */
591 548a3163 2014-11-14 marcelgmr static void
592 845f2b87 2014-11-14 marcelgmr process_marked(PROCESS pre, PROCESS proc, PROCESS pos)
593 548a3163 2014-11-14 marcelgmr {
594 2a117cd2 2014-11-15 marcelgmr int i, ret;
595 c04b0d89 2015-03-30 marcelgmr char path[PATH_MAX];
596 548a3163 2014-11-14 marcelgmr
597 d0cdbd3c 2014-11-15 marcelgmr clear_message();
598 d0cdbd3c 2014-11-15 marcelgmr message("Processing...", CYAN);
599 d0cdbd3c 2014-11-15 marcelgmr refresh();
600 548a3163 2014-11-14 marcelgmr for (i = 0; i < rover.marks.bulk; i++)
601 548a3163 2014-11-14 marcelgmr if (rover.marks.entries[i]) {
602 2a117cd2 2014-11-15 marcelgmr ret = 0;
603 e5fe9820 2015-04-17 marcelgmr snprintf(path, PATH_MAX, "%s%s", rover.marks.dirpath, rover.marks.entries[i]);
604 7c74df90 2014-11-15 marcelgmr if (ISDIR(rover.marks.entries[i])) {
605 b25c9834 2014-11-14 marcelgmr if (!strncmp(path, CWD, strlen(path)))
606 2a117cd2 2014-11-15 marcelgmr ret = -1;
607 b25c9834 2014-11-14 marcelgmr else
608 2a117cd2 2014-11-15 marcelgmr ret = process_dir(pre, proc, pos, path);
609 590d39af 2014-12-02 marcelgmr } else
610 590d39af 2014-12-02 marcelgmr ret = proc(path);
611 2a117cd2 2014-11-15 marcelgmr if (!ret) del_mark(&rover.marks, rover.marks.entries[i]);
612 2a117cd2 2014-11-15 marcelgmr }
613 00ecdc1e 2014-11-19 marcelgmr reload();
614 2a117cd2 2014-11-15 marcelgmr if (!rover.marks.nentries)
615 bad36707 2014-11-19 marcelgmr message("Done.", GREEN);
616 2a117cd2 2014-11-15 marcelgmr else
617 bad36707 2014-11-19 marcelgmr message("Some errors occured.", RED);
618 3cd6bb98 2014-11-11 marcelgmr }
619 3cd6bb98 2014-11-11 marcelgmr
620 b7ee4ee2 2014-11-14 marcelgmr /* Wrappers for file operations. */
621 ca8ebdce 2014-11-14 marcelgmr static PROCESS delfile = unlink;
622 b7ee4ee2 2014-11-14 marcelgmr static PROCESS deldir = rmdir;
623 1447784d 2014-11-14 marcelgmr static int addfile(const char *path) {
624 1447784d 2014-11-14 marcelgmr /* Using creat(2) because mknod(2) doesn't seem to be portable. */
625 1447784d 2014-11-14 marcelgmr int ret;
626 1447784d 2014-11-14 marcelgmr
627 1447784d 2014-11-14 marcelgmr ret = creat(path, 0644);
628 1447784d 2014-11-14 marcelgmr if (ret < 0) return ret;
629 1447784d 2014-11-14 marcelgmr return close(ret);
630 1447784d 2014-11-14 marcelgmr }
631 ca8ebdce 2014-11-14 marcelgmr static int cpyfile(const char *srcpath) {
632 b7ee4ee2 2014-11-14 marcelgmr int src, dst, ret;
633 b7ee4ee2 2014-11-14 marcelgmr size_t size;
634 b7ee4ee2 2014-11-14 marcelgmr struct stat st;
635 b7ee4ee2 2014-11-14 marcelgmr char buf[BUFSIZ];
636 c04b0d89 2015-03-30 marcelgmr char dstpath[PATH_MAX];
637 b7ee4ee2 2014-11-14 marcelgmr
638 b7ee4ee2 2014-11-14 marcelgmr ret = src = open(srcpath, O_RDONLY);
639 b7ee4ee2 2014-11-14 marcelgmr if (ret < 0) return ret;
640 b7ee4ee2 2014-11-14 marcelgmr ret = fstat(src, &st);
641 b7ee4ee2 2014-11-14 marcelgmr if (ret < 0) return ret;
642 b7ee4ee2 2014-11-14 marcelgmr strcpy(dstpath, CWD);
643 b7ee4ee2 2014-11-14 marcelgmr strcat(dstpath, srcpath + strlen(rover.marks.dirpath));
644 b7ee4ee2 2014-11-14 marcelgmr ret = dst = creat(dstpath, st.st_mode);
645 b7ee4ee2 2014-11-14 marcelgmr if (ret < 0) return ret;
646 b7ee4ee2 2014-11-14 marcelgmr while ((size = read(src, buf, BUFSIZ)) > 0)
647 b7ee4ee2 2014-11-14 marcelgmr write(dst, buf, size);
648 b7ee4ee2 2014-11-14 marcelgmr close(src);
649 b7ee4ee2 2014-11-14 marcelgmr close(dst);
650 b7ee4ee2 2014-11-14 marcelgmr return 0;
651 b7ee4ee2 2014-11-14 marcelgmr }
652 b7ee4ee2 2014-11-14 marcelgmr static int adddir(const char *path) {
653 b7ee4ee2 2014-11-14 marcelgmr int ret;
654 b7ee4ee2 2014-11-14 marcelgmr struct stat st;
655 b7ee4ee2 2014-11-14 marcelgmr
656 b7ee4ee2 2014-11-14 marcelgmr ret = stat(CWD, &st);
657 b7ee4ee2 2014-11-14 marcelgmr if (ret < 0) return ret;
658 b7ee4ee2 2014-11-14 marcelgmr return mkdir(path, st.st_mode);
659 ca8ebdce 2014-11-14 marcelgmr }
660 0ea64589 2014-11-14 marcelgmr static int movfile(const char *srcpath) {
661 8a757b0f 2015-03-07 marcelgmr int ret;
662 c04b0d89 2015-03-30 marcelgmr char dstpath[PATH_MAX];
663 ca8ebdce 2014-11-14 marcelgmr
664 0ea64589 2014-11-14 marcelgmr strcpy(dstpath, CWD);
665 0ea64589 2014-11-14 marcelgmr strcat(dstpath, srcpath + strlen(rover.marks.dirpath));
666 8a757b0f 2015-03-07 marcelgmr ret = rename(srcpath, dstpath);
667 8a757b0f 2015-03-07 marcelgmr if (ret < 0 && errno == EXDEV) {
668 8a757b0f 2015-03-07 marcelgmr ret = cpyfile(srcpath);
669 8a757b0f 2015-03-07 marcelgmr if (ret < 0) return ret;
670 8a757b0f 2015-03-07 marcelgmr ret = delfile(srcpath);
671 8a757b0f 2015-03-07 marcelgmr }
672 8a757b0f 2015-03-07 marcelgmr return ret;
673 b7ee4ee2 2014-11-14 marcelgmr }
674 b7ee4ee2 2014-11-14 marcelgmr
675 a022230d 2015-04-26 marcelgmr static void
676 a022230d 2015-04-26 marcelgmr start_line_edit(const char *init_input)
677 411ec846 2014-11-08 marcelgmr {
678 d5fe31b0 2014-11-14 marcelgmr curs_set(TRUE);
679 a022230d 2015-04-26 marcelgmr strncpy(INPUT, init_input, INPUTSZ);
680 6110e5a1 2015-04-26 marcelgmr strncpy(rover.edit.buffer, init_input, INPUTSZ);
681 a022230d 2015-04-26 marcelgmr rover.edit.left = strlen(init_input);
682 6110e5a1 2015-04-26 marcelgmr rover.edit.right = INPUTSZ - 1;
683 a022230d 2015-04-26 marcelgmr }
684 a022230d 2015-04-26 marcelgmr
685 a022230d 2015-04-26 marcelgmr /* Read input and change editing state accordingly. */
686 a022230d 2015-04-26 marcelgmr static EditStat
687 a022230d 2015-04-26 marcelgmr get_line_edit()
688 a022230d 2015-04-26 marcelgmr {
689 a022230d 2015-04-26 marcelgmr int ch = getch();
690 a022230d 2015-04-26 marcelgmr if (ch == '\r' || ch == '\n' || ch == KEY_ENTER) {
691 d5fe31b0 2014-11-14 marcelgmr curs_set(FALSE);
692 a022230d 2015-04-26 marcelgmr return CONFIRM;
693 a022230d 2015-04-26 marcelgmr } else if (ch == '\t') {
694 a022230d 2015-04-26 marcelgmr curs_set(FALSE);
695 a022230d 2015-04-26 marcelgmr return CANCEL;
696 a022230d 2015-04-26 marcelgmr } else if (EDIT_CAN_LEFT(rover.edit) && ch == KEY_LEFT) {
697 a022230d 2015-04-26 marcelgmr EDIT_LEFT(rover.edit);
698 a022230d 2015-04-26 marcelgmr } else if (EDIT_CAN_RIGHT(rover.edit) && ch == KEY_RIGHT) {
699 a022230d 2015-04-26 marcelgmr EDIT_RIGHT(rover.edit);
700 a022230d 2015-04-26 marcelgmr } else if (ch == KEY_UP) {
701 a022230d 2015-04-26 marcelgmr while (EDIT_CAN_LEFT(rover.edit)) EDIT_LEFT(rover.edit);
702 a022230d 2015-04-26 marcelgmr } else if (ch == KEY_DOWN) {
703 a022230d 2015-04-26 marcelgmr while (EDIT_CAN_RIGHT(rover.edit)) EDIT_RIGHT(rover.edit);
704 a022230d 2015-04-26 marcelgmr } else if (ch == erasechar() || ch == KEY_BACKSPACE) {
705 a022230d 2015-04-26 marcelgmr if (EDIT_CAN_LEFT(rover.edit)) EDIT_BACKSPACE(rover.edit);
706 a022230d 2015-04-26 marcelgmr } else if (ch == KEY_DC) {
707 a022230d 2015-04-26 marcelgmr if (EDIT_CAN_RIGHT(rover.edit)) EDIT_DELETE(rover.edit);
708 a022230d 2015-04-26 marcelgmr } else if (ch == killchar()) {
709 a022230d 2015-04-26 marcelgmr EDIT_CLEAR(rover.edit);
710 a022230d 2015-04-26 marcelgmr clear_message();
711 a022230d 2015-04-26 marcelgmr } else if (!EDIT_FULL(rover.edit) && isprint(ch)) {
712 a022230d 2015-04-26 marcelgmr EDIT_INSERT(rover.edit, ch);
713 411ec846 2014-11-08 marcelgmr }
714 a022230d 2015-04-26 marcelgmr /* Copy edit contents to INPUT and append null character. */
715 6110e5a1 2015-04-26 marcelgmr strncpy(INPUT, rover.edit.buffer, rover.edit.left);
716 a022230d 2015-04-26 marcelgmr strncpy(&INPUT[rover.edit.left], &rover.edit.buffer[rover.edit.right+1],
717 6110e5a1 2015-04-26 marcelgmr INPUTSZ-rover.edit.left-1);
718 6110e5a1 2015-04-26 marcelgmr INPUT[rover.edit.left+INPUTSZ-rover.edit.right-1] = '\0';
719 a022230d 2015-04-26 marcelgmr return CONTINUE;
720 743e70b8 2014-11-14 marcelgmr }
721 743e70b8 2014-11-14 marcelgmr
722 5c4a6b62 2014-11-14 marcelgmr /* Update line input on the screen. */
723 743e70b8 2014-11-14 marcelgmr static void
724 eef51ac5 2015-03-24 marcelgmr update_input(char *prompt, Color color)
725 743e70b8 2014-11-14 marcelgmr {
726 743e70b8 2014-11-14 marcelgmr int plen, ilen;
727 743e70b8 2014-11-14 marcelgmr
728 743e70b8 2014-11-14 marcelgmr plen = strlen(prompt);
729 743e70b8 2014-11-14 marcelgmr ilen = strlen(INPUT);
730 743e70b8 2014-11-14 marcelgmr color_set(RVC_PROMPT, NULL);
731 743e70b8 2014-11-14 marcelgmr mvaddstr(LINES - 1, 0, prompt);
732 743e70b8 2014-11-14 marcelgmr color_set(color, NULL);
733 743e70b8 2014-11-14 marcelgmr mvaddstr(LINES - 1, plen, INPUT);
734 a022230d 2015-04-26 marcelgmr mvaddch(LINES - 1, plen + ilen, ' ');
735 a022230d 2015-04-26 marcelgmr move(LINES - 1, plen + rover.edit.left);
736 743e70b8 2014-11-14 marcelgmr color_set(DEFAULT, NULL);
737 e1c42cfe 2014-11-11 marcelgmr }
738 e1c42cfe 2014-11-11 marcelgmr
739 71ee1627 2014-11-07 marcelgmr int
740 38a357b6 2014-11-09 marcelgmr main(int argc, char *argv[])
741 71ee1627 2014-11-07 marcelgmr {
742 ada8fba9 2014-11-09 marcelgmr int i, ch;
743 14b5cd28 2015-01-06 marcelgmr char *program;
744 14b5cd28 2015-01-06 marcelgmr const char *key;
745 38a357b6 2014-11-09 marcelgmr DIR *d;
746 a022230d 2015-04-26 marcelgmr EditStat edit_stat;
747 71ee1627 2014-11-07 marcelgmr
748 ff684771 2014-12-02 marcelgmr if (argc == 2) {
749 ff684771 2014-12-02 marcelgmr if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) {
750 ff684771 2014-12-02 marcelgmr printf("rover %s\n", RV_VERSION);
751 ff684771 2014-12-02 marcelgmr return 0;
752 ff684771 2014-12-02 marcelgmr } else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
753 ff684771 2014-12-02 marcelgmr printf(
754 ff684771 2014-12-02 marcelgmr "Usage: rover [DIRECTORY [DIRECTORY [DIRECTORY [...]]]]\n"
755 ff684771 2014-12-02 marcelgmr " or: rover [OPTION]\n"
756 ff684771 2014-12-02 marcelgmr "Browse current working directory or the ones specified.\n\n"
757 ff684771 2014-12-02 marcelgmr "Options:\n"
758 ff684771 2014-12-02 marcelgmr " -h, --help print this help message and exit\n"
759 ff684771 2014-12-02 marcelgmr " -v, --version print program version and exit\n\n"
760 ff684771 2014-12-02 marcelgmr "See rover(1) for more information.\n\n"
761 ff684771 2014-12-02 marcelgmr "Rover homepage: <https://github.com/lecram/rover>.\n"
762 ff684771 2014-12-02 marcelgmr );
763 ff684771 2014-12-02 marcelgmr return 0;
764 ff684771 2014-12-02 marcelgmr }
765 ff684771 2014-12-02 marcelgmr }
766 f05efd9e 2014-11-07 marcelgmr init_term();
767 f05efd9e 2014-11-07 marcelgmr rover.nfiles = 0;
768 47e9f26d 2014-11-09 marcelgmr for (i = 0; i < 10; i++) {
769 7c74df90 2014-11-15 marcelgmr rover.esel[i] = rover.scroll[i] = 0;
770 47e9f26d 2014-11-09 marcelgmr rover.flags[i] = SHOW_FILES | SHOW_DIRS;
771 47e9f26d 2014-11-09 marcelgmr }
772 ada8fba9 2014-11-09 marcelgmr strcpy(rover.cwd[0], getenv("HOME"));
773 38a357b6 2014-11-09 marcelgmr for (i = 1; i < argc && i < 10; i++) {
774 7c74df90 2014-11-15 marcelgmr if ((d = opendir(argv[i]))) {
775 c04b0d89 2015-03-30 marcelgmr realpath(argv[i], rover.cwd[i]);
776 38a357b6 2014-11-09 marcelgmr closedir(d);
777 590d39af 2014-12-02 marcelgmr } else
778 590d39af 2014-12-02 marcelgmr strcpy(rover.cwd[i], rover.cwd[0]);
779 38a357b6 2014-11-09 marcelgmr }
780 c04b0d89 2015-03-30 marcelgmr getcwd(rover.cwd[i], PATH_MAX);
781 38a357b6 2014-11-09 marcelgmr for (i++; i < 10; i++)
782 38a357b6 2014-11-09 marcelgmr strcpy(rover.cwd[i], rover.cwd[i-1]);
783 38a357b6 2014-11-09 marcelgmr for (i = 0; i < 10; i++)
784 38a357b6 2014-11-09 marcelgmr if (rover.cwd[i][strlen(rover.cwd[i]) - 1] != '/')
785 38a357b6 2014-11-09 marcelgmr strcat(rover.cwd[i], "/");
786 ada8fba9 2014-11-09 marcelgmr rover.tab = 1;
787 ada8fba9 2014-11-09 marcelgmr rover.window = subwin(stdscr, LINES - 2, COLS, 1, 0);
788 18016333 2014-11-14 marcelgmr init_marks(&rover.marks);
789 ada8fba9 2014-11-09 marcelgmr cd(1);
790 0b301167 2014-11-07 marcelgmr while (1) {
791 ada8fba9 2014-11-09 marcelgmr ch = getch();
792 ada8fba9 2014-11-09 marcelgmr key = keyname(ch);
793 865c9839 2014-11-15 marcelgmr clear_message();
794 a31a7b29 2014-11-15 marcelgmr if (!strcmp(key, RVK_QUIT)) break;
795 ada8fba9 2014-11-09 marcelgmr else if (ch >= '0' && ch <= '9') {
796 ada8fba9 2014-11-09 marcelgmr rover.tab = ch - '0';
797 ada8fba9 2014-11-09 marcelgmr cd(0);
798 ff684771 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_HELP)) {
799 ff684771 2014-12-02 marcelgmr ARGS[0] = "man";
800 ff684771 2014-12-02 marcelgmr ARGS[1] = "rover";
801 ff684771 2014-12-02 marcelgmr ARGS[2] = NULL;
802 ff684771 2014-12-02 marcelgmr spawn();
803 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_DOWN)) {
804 bb90b2d2 2014-11-08 marcelgmr if (!rover.nfiles) continue;
805 7c74df90 2014-11-15 marcelgmr ESEL = (ESEL + 1) % rover.nfiles;
806 743e70b8 2014-11-14 marcelgmr update_view();
807 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_UP)) {
808 bb90b2d2 2014-11-08 marcelgmr if (!rover.nfiles) continue;
809 7c74df90 2014-11-15 marcelgmr ESEL = ESEL ? ESEL - 1 : rover.nfiles - 1;
810 743e70b8 2014-11-14 marcelgmr update_view();
811 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_JUMP_DOWN)) {
812 bb90b2d2 2014-11-08 marcelgmr if (!rover.nfiles) continue;
813 60128fcd 2014-11-15 marcelgmr ESEL = MIN(ESEL + RV_JUMP, rover.nfiles - 1);
814 60128fcd 2014-11-15 marcelgmr if (rover.nfiles > HEIGHT)
815 60128fcd 2014-11-15 marcelgmr SCROLL = MIN(SCROLL + RV_JUMP, rover.nfiles - HEIGHT);
816 743e70b8 2014-11-14 marcelgmr update_view();
817 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_JUMP_UP)) {
818 bb90b2d2 2014-11-08 marcelgmr if (!rover.nfiles) continue;
819 60128fcd 2014-11-15 marcelgmr ESEL = MAX(ESEL - RV_JUMP, 0);
820 60128fcd 2014-11-15 marcelgmr SCROLL = MAX(SCROLL - RV_JUMP, 0);
821 743e70b8 2014-11-14 marcelgmr update_view();
822 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_CD_DOWN)) {
823 7c74df90 2014-11-15 marcelgmr if (!rover.nfiles || !ISDIR(ENAME(ESEL))) continue;
824 7c74df90 2014-11-15 marcelgmr strcat(CWD, ENAME(ESEL));
825 ada8fba9 2014-11-09 marcelgmr cd(1);
826 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_CD_UP)) {
827 45020106 2014-11-08 marcelgmr char *dirname, first;
828 58fbefc5 2014-11-19 marcelgmr if (!strcmp(CWD, "/")) continue;
829 ada8fba9 2014-11-09 marcelgmr CWD[strlen(CWD) - 1] = '\0';
830 ada8fba9 2014-11-09 marcelgmr dirname = strrchr(CWD, '/') + 1;
831 45020106 2014-11-08 marcelgmr first = dirname[0];
832 45020106 2014-11-08 marcelgmr dirname[0] = '\0';
833 ada8fba9 2014-11-09 marcelgmr cd(1);
834 430cd7d8 2014-11-19 marcelgmr dirname[0] = first;
835 430cd7d8 2014-11-19 marcelgmr dirname[strlen(dirname)] = '/';
836 430cd7d8 2014-11-19 marcelgmr try_to_sel(dirname);
837 430cd7d8 2014-11-19 marcelgmr dirname[0] = '\0';
838 430cd7d8 2014-11-19 marcelgmr update_view();
839 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_HOME)) {
840 ada8fba9 2014-11-09 marcelgmr strcpy(CWD, getenv("HOME"));
841 ada8fba9 2014-11-09 marcelgmr if (CWD[strlen(CWD) - 1] != '/')
842 ada8fba9 2014-11-09 marcelgmr strcat(CWD, "/");
843 ada8fba9 2014-11-09 marcelgmr cd(1);
844 97217d57 2015-05-26 marcelgmr } else if (!strcmp(key, RVK_REFRESH)) {
845 97217d57 2015-05-26 marcelgmr reload();
846 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_SHELL)) {
847 71ee1627 2014-11-07 marcelgmr program = getenv("SHELL");
848 71ee1627 2014-11-07 marcelgmr if (program) {
849 3cd6bb98 2014-11-11 marcelgmr ARGS[0] = program;
850 3cd6bb98 2014-11-11 marcelgmr ARGS[1] = NULL;
851 71ee1627 2014-11-07 marcelgmr spawn();
852 00ecdc1e 2014-11-19 marcelgmr reload();
853 71ee1627 2014-11-07 marcelgmr }
854 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_VIEW)) {
855 7c74df90 2014-11-15 marcelgmr if (!rover.nfiles || ISDIR(ENAME(ESEL))) continue;
856 4b66a2c4 2014-11-07 marcelgmr program = getenv("PAGER");
857 4b66a2c4 2014-11-07 marcelgmr if (program) {
858 3cd6bb98 2014-11-11 marcelgmr ARGS[0] = program;
859 7c74df90 2014-11-15 marcelgmr ARGS[1] = ENAME(ESEL);
860 3cd6bb98 2014-11-11 marcelgmr ARGS[2] = NULL;
861 4b66a2c4 2014-11-07 marcelgmr spawn();
862 4b66a2c4 2014-11-07 marcelgmr }
863 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_EDIT)) {
864 7c74df90 2014-11-15 marcelgmr if (!rover.nfiles || ISDIR(ENAME(ESEL))) continue;
865 71ee1627 2014-11-07 marcelgmr program = getenv("EDITOR");
866 71ee1627 2014-11-07 marcelgmr if (program) {
867 3cd6bb98 2014-11-11 marcelgmr ARGS[0] = program;
868 7c74df90 2014-11-15 marcelgmr ARGS[1] = ENAME(ESEL);
869 3cd6bb98 2014-11-11 marcelgmr ARGS[2] = NULL;
870 71ee1627 2014-11-07 marcelgmr spawn();
871 39ee8f33 2014-11-14 marcelgmr cd(0);
872 71ee1627 2014-11-07 marcelgmr }
873 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_SEARCH)) {
874 1447784d 2014-11-14 marcelgmr int oldsel, oldscroll, length;
875 743e70b8 2014-11-14 marcelgmr char *prompt = "search: ";
876 bb90b2d2 2014-11-08 marcelgmr if (!rover.nfiles) continue;
877 7c74df90 2014-11-15 marcelgmr oldsel = ESEL;
878 ada8fba9 2014-11-09 marcelgmr oldscroll = SCROLL;
879 a022230d 2015-04-26 marcelgmr start_line_edit("");
880 743e70b8 2014-11-14 marcelgmr update_input(prompt, DEFAULT);
881 a022230d 2015-04-26 marcelgmr while ((edit_stat = get_line_edit()) == CONTINUE) {
882 1447784d 2014-11-14 marcelgmr int sel;
883 eef51ac5 2015-03-24 marcelgmr Color color = RED;
884 1447784d 2014-11-14 marcelgmr length = strlen(INPUT);
885 a17e36e3 2014-11-07 marcelgmr if (length) {
886 f05efd9e 2014-11-07 marcelgmr for (sel = 0; sel < rover.nfiles; sel++)
887 7c74df90 2014-11-15 marcelgmr if (!strncmp(ENAME(sel), INPUT, length))
888 a17e36e3 2014-11-07 marcelgmr break;
889 f05efd9e 2014-11-07 marcelgmr if (sel < rover.nfiles) {
890 a17e36e3 2014-11-07 marcelgmr color = GREEN;
891 7c74df90 2014-11-15 marcelgmr ESEL = sel;
892 f05efd9e 2014-11-07 marcelgmr if (rover.nfiles > HEIGHT) {
893 89cfd83b 2014-11-08 marcelgmr if (sel < 3)
894 ada8fba9 2014-11-09 marcelgmr SCROLL = 0;
895 89cfd83b 2014-11-08 marcelgmr else if (sel - 3 > rover.nfiles - HEIGHT)
896 ada8fba9 2014-11-09 marcelgmr SCROLL = rover.nfiles - HEIGHT;
897 a17e36e3 2014-11-07 marcelgmr else
898 ada8fba9 2014-11-09 marcelgmr SCROLL = sel - 3;
899 a17e36e3 2014-11-07 marcelgmr }
900 a17e36e3 2014-11-07 marcelgmr }
901 590d39af 2014-12-02 marcelgmr } else {
902 7c74df90 2014-11-15 marcelgmr ESEL = oldsel;
903 ada8fba9 2014-11-09 marcelgmr SCROLL = oldscroll;
904 411ec846 2014-11-08 marcelgmr }
905 743e70b8 2014-11-14 marcelgmr update_view();
906 743e70b8 2014-11-14 marcelgmr update_input(prompt, color);
907 a17e36e3 2014-11-07 marcelgmr }
908 a022230d 2015-04-26 marcelgmr if (edit_stat == CANCEL) {
909 a022230d 2015-04-26 marcelgmr ESEL = oldsel;
910 a022230d 2015-04-26 marcelgmr SCROLL = oldscroll;
911 a022230d 2015-04-26 marcelgmr }
912 865c9839 2014-11-15 marcelgmr clear_message();
913 743e70b8 2014-11-14 marcelgmr update_view();
914 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_TG_FILES)) {
915 47e9f26d 2014-11-09 marcelgmr FLAGS ^= SHOW_FILES;
916 00ecdc1e 2014-11-19 marcelgmr reload();
917 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_TG_DIRS)) {
918 47e9f26d 2014-11-09 marcelgmr FLAGS ^= SHOW_DIRS;
919 00ecdc1e 2014-11-19 marcelgmr reload();
920 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_TG_HIDDEN)) {
921 47e9f26d 2014-11-09 marcelgmr FLAGS ^= SHOW_HIDDEN;
922 00ecdc1e 2014-11-19 marcelgmr reload();
923 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_NEW_FILE)) {
924 743e70b8 2014-11-14 marcelgmr int ok = 0;
925 743e70b8 2014-11-14 marcelgmr char *prompt = "new file: ";
926 a022230d 2015-04-26 marcelgmr start_line_edit("");
927 743e70b8 2014-11-14 marcelgmr update_input(prompt, DEFAULT);
928 a022230d 2015-04-26 marcelgmr while ((edit_stat = get_line_edit()) == CONTINUE) {
929 ddbe9d62 2014-11-14 marcelgmr int length = strlen(INPUT);
930 1447784d 2014-11-14 marcelgmr ok = 1;
931 e444d3b1 2014-11-14 marcelgmr for (i = 0; i < rover.nfiles; i++) {
932 ddbe9d62 2014-11-14 marcelgmr if (
933 7c74df90 2014-11-15 marcelgmr !strncmp(ENAME(i), INPUT, length) &&
934 7c74df90 2014-11-15 marcelgmr (!strcmp(ENAME(i) + length, "") ||
935 7c74df90 2014-11-15 marcelgmr !strcmp(ENAME(i) + length, "/"))
936 ddbe9d62 2014-11-14 marcelgmr ) {
937 1447784d 2014-11-14 marcelgmr ok = 0;
938 1447784d 2014-11-14 marcelgmr break;
939 1447784d 2014-11-14 marcelgmr }
940 e444d3b1 2014-11-14 marcelgmr }
941 743e70b8 2014-11-14 marcelgmr update_input(prompt, ok ? GREEN : RED);
942 1447784d 2014-11-14 marcelgmr }
943 865c9839 2014-11-15 marcelgmr clear_message();
944 a022230d 2015-04-26 marcelgmr if (edit_stat == CONFIRM && strlen(INPUT)) {
945 430cd7d8 2014-11-19 marcelgmr if (ok) {
946 430cd7d8 2014-11-19 marcelgmr addfile(INPUT);
947 430cd7d8 2014-11-19 marcelgmr cd(1);
948 430cd7d8 2014-11-19 marcelgmr try_to_sel(INPUT);
949 430cd7d8 2014-11-19 marcelgmr update_view();
950 590d39af 2014-12-02 marcelgmr } else
951 590d39af 2014-12-02 marcelgmr message("File already exists.", RED);
952 1447784d 2014-11-14 marcelgmr }
953 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_NEW_DIR)) {
954 743e70b8 2014-11-14 marcelgmr int ok = 0;
955 743e70b8 2014-11-14 marcelgmr char *prompt = "new directory: ";
956 a022230d 2015-04-26 marcelgmr start_line_edit("");
957 743e70b8 2014-11-14 marcelgmr update_input(prompt, DEFAULT);
958 a022230d 2015-04-26 marcelgmr while ((edit_stat = get_line_edit()) == CONTINUE) {
959 ddbe9d62 2014-11-14 marcelgmr int length = strlen(INPUT);
960 1447784d 2014-11-14 marcelgmr ok = 1;
961 e444d3b1 2014-11-14 marcelgmr for (i = 0; i < rover.nfiles; i++) {
962 ddbe9d62 2014-11-14 marcelgmr if (
963 7c74df90 2014-11-15 marcelgmr !strncmp(ENAME(i), INPUT, length) &&
964 7c74df90 2014-11-15 marcelgmr (!strcmp(ENAME(i) + length, "") ||
965 7c74df90 2014-11-15 marcelgmr !strcmp(ENAME(i) + length, "/"))
966 ddbe9d62 2014-11-14 marcelgmr ) {
967 1447784d 2014-11-14 marcelgmr ok = 0;
968 1447784d 2014-11-14 marcelgmr break;
969 1447784d 2014-11-14 marcelgmr }
970 e444d3b1 2014-11-14 marcelgmr }
971 743e70b8 2014-11-14 marcelgmr update_input(prompt, ok ? GREEN : RED);
972 1447784d 2014-11-14 marcelgmr }
973 865c9839 2014-11-15 marcelgmr clear_message();
974 a022230d 2015-04-26 marcelgmr if (edit_stat == CONFIRM && strlen(INPUT)) {
975 430cd7d8 2014-11-19 marcelgmr if (ok) {
976 430cd7d8 2014-11-19 marcelgmr adddir(INPUT);
977 430cd7d8 2014-11-19 marcelgmr cd(1);
978 430cd7d8 2014-11-19 marcelgmr try_to_sel(INPUT);
979 430cd7d8 2014-11-19 marcelgmr update_view();
980 590d39af 2014-12-02 marcelgmr } else
981 590d39af 2014-12-02 marcelgmr message("File already exists.", RED);
982 1447784d 2014-11-14 marcelgmr }
983 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_RENAME)) {
984 bdb195d0 2014-11-14 marcelgmr int ok = 0;
985 bdb195d0 2014-11-14 marcelgmr char *prompt = "rename: ";
986 f499f7c7 2014-12-03 marcelgmr char *last;
987 f499f7c7 2014-12-03 marcelgmr int isdir;
988 7c74df90 2014-11-15 marcelgmr strcpy(INPUT, ENAME(ESEL));
989 f499f7c7 2014-12-03 marcelgmr last = INPUT + strlen(INPUT) - 1;
990 f499f7c7 2014-12-03 marcelgmr if ((isdir = *last == '/'))
991 f499f7c7 2014-12-03 marcelgmr *last = '\0';
992 a022230d 2015-04-26 marcelgmr start_line_edit(INPUT);
993 ddbe9d62 2014-11-14 marcelgmr update_input(prompt, RED);
994 a022230d 2015-04-26 marcelgmr while ((edit_stat = get_line_edit()) == CONTINUE) {
995 ddbe9d62 2014-11-14 marcelgmr int length = strlen(INPUT);
996 bdb195d0 2014-11-14 marcelgmr ok = 1;
997 5f047452 2014-12-03 marcelgmr for (i = 0; i < rover.nfiles; i++)
998 ddbe9d62 2014-11-14 marcelgmr if (
999 7c74df90 2014-11-15 marcelgmr !strncmp(ENAME(i), INPUT, length) &&
1000 7c74df90 2014-11-15 marcelgmr (!strcmp(ENAME(i) + length, "") ||
1001 7c74df90 2014-11-15 marcelgmr !strcmp(ENAME(i) + length, "/"))
1002 ddbe9d62 2014-11-14 marcelgmr ) {
1003 bdb195d0 2014-11-14 marcelgmr ok = 0;
1004 bdb195d0 2014-11-14 marcelgmr break;
1005 bdb195d0 2014-11-14 marcelgmr }
1006 bdb195d0 2014-11-14 marcelgmr update_input(prompt, ok ? GREEN : RED);
1007 bdb195d0 2014-11-14 marcelgmr }
1008 865c9839 2014-11-15 marcelgmr clear_message();
1009 a022230d 2015-04-26 marcelgmr if (edit_stat == CONFIRM && strlen(INPUT)) {
1010 f499f7c7 2014-12-03 marcelgmr if (isdir)
1011 f499f7c7 2014-12-03 marcelgmr strcat(INPUT, "/");
1012 00ecdc1e 2014-11-19 marcelgmr if (ok) {
1013 dbb53e68 2014-12-03 marcelgmr if (!rename(ENAME(ESEL), INPUT) && MARKED(ESEL)) {
1014 dbb53e68 2014-12-03 marcelgmr del_mark(&rover.marks, ENAME(ESEL));
1015 dbb53e68 2014-12-03 marcelgmr add_mark(&rover.marks, CWD, INPUT);
1016 dbb53e68 2014-12-03 marcelgmr }
1017 00ecdc1e 2014-11-19 marcelgmr cd(1);
1018 00ecdc1e 2014-11-19 marcelgmr try_to_sel(INPUT);
1019 00ecdc1e 2014-11-19 marcelgmr update_view();
1020 590d39af 2014-12-02 marcelgmr } else
1021 590d39af 2014-12-02 marcelgmr message("File already exists.", RED);
1022 bdb195d0 2014-11-14 marcelgmr }
1023 e0d6077a 2015-05-27 marcelgmr } else if (!strcmp(key, RVK_DELETE)) {
1024 e0d6077a 2015-05-27 marcelgmr if (rover.nfiles) {
1025 e0d6077a 2015-05-27 marcelgmr message("Delete selected entry? (Y to confirm)", YELLOW);
1026 e0d6077a 2015-05-27 marcelgmr if (getch() == 'Y') {
1027 e0d6077a 2015-05-27 marcelgmr const char *name = ENAME(ESEL);
1028 e0d6077a 2015-05-27 marcelgmr int ret = ISDIR(name) ? deldir(name) : delfile(name);
1029 e0d6077a 2015-05-27 marcelgmr reload();
1030 e0d6077a 2015-05-27 marcelgmr if (ret)
1031 e0d6077a 2015-05-27 marcelgmr message("Could not delete entry.", RED);
1032 e0d6077a 2015-05-27 marcelgmr } else
1033 e0d6077a 2015-05-27 marcelgmr clear_message();
1034 e0d6077a 2015-05-27 marcelgmr } else
1035 e0d6077a 2015-05-27 marcelgmr message("No entry selected for deletion.", RED);
1036 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_TG_MARK)) {
1037 7c74df90 2014-11-15 marcelgmr if (MARKED(ESEL))
1038 7c74df90 2014-11-15 marcelgmr del_mark(&rover.marks, ENAME(ESEL));
1039 18016333 2014-11-14 marcelgmr else
1040 7c74df90 2014-11-15 marcelgmr add_mark(&rover.marks, CWD, ENAME(ESEL));
1041 7c74df90 2014-11-15 marcelgmr MARKED(ESEL) = !MARKED(ESEL);
1042 7c74df90 2014-11-15 marcelgmr ESEL = (ESEL + 1) % rover.nfiles;
1043 743e70b8 2014-11-14 marcelgmr update_view();
1044 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_INVMARK)) {
1045 5fc8b927 2014-11-14 marcelgmr for (i = 0; i < rover.nfiles; i++) {
1046 5fc8b927 2014-11-14 marcelgmr if (MARKED(i))
1047 7c74df90 2014-11-15 marcelgmr del_mark(&rover.marks, ENAME(i));
1048 5fc8b927 2014-11-14 marcelgmr else
1049 7c74df90 2014-11-15 marcelgmr add_mark(&rover.marks, CWD, ENAME(i));
1050 5fc8b927 2014-11-14 marcelgmr MARKED(i) = !MARKED(i);
1051 5fc8b927 2014-11-14 marcelgmr }
1052 743e70b8 2014-11-14 marcelgmr update_view();
1053 590d39af 2014-12-02 marcelgmr } else if (!strcmp(key, RVK_MARKALL)) {
1054 5fc8b927 2014-11-14 marcelgmr for (i = 0; i < rover.nfiles; i++)
1055 5fc8b927 2014-11-14 marcelgmr if (!MARKED(i)) {
1056 7c74df90 2014-11-15 marcelgmr add_mark(&rover.marks, CWD, ENAME(i));
1057 5fc8b927 2014-11-14 marcelgmr MARKED(i) = 1;
1058 5fc8b927 2014-11-14 marcelgmr }
1059 743e70b8 2014-11-14 marcelgmr update_view();
1060 e0d6077a 2015-05-27 marcelgmr } else if (!strcmp(key, RVK_MARK_DELETE)) {
1061 a31a7b29 2014-11-15 marcelgmr if (rover.marks.nentries) {
1062 a31a7b29 2014-11-15 marcelgmr message("Delete marked entries? (Y to confirm)", YELLOW);
1063 a31a7b29 2014-11-15 marcelgmr if (getch() == 'Y')
1064 a31a7b29 2014-11-15 marcelgmr process_marked(NULL, delfile, deldir);
1065 590d39af 2014-12-02 marcelgmr else
1066 590d39af 2014-12-02 marcelgmr clear_message();
1067 590d39af 2014-12-02 marcelgmr } else
1068 590d39af 2014-12-02 marcelgmr message("No entries marked for deletion.", RED);
1069 e0d6077a 2015-05-27 marcelgmr } else if (!strcmp(key, RVK_MARK_COPY)) {
1070 a31a7b29 2014-11-15 marcelgmr if (rover.marks.nentries)
1071 a31a7b29 2014-11-15 marcelgmr process_marked(adddir, cpyfile, NULL);
1072 590d39af 2014-12-02 marcelgmr else
1073 590d39af 2014-12-02 marcelgmr message("No entries marked for copying.", RED);
1074 e0d6077a 2015-05-27 marcelgmr } else if (!strcmp(key, RVK_MARK_MOVE)) {
1075 a31a7b29 2014-11-15 marcelgmr if (rover.marks.nentries)
1076 a31a7b29 2014-11-15 marcelgmr process_marked(adddir, movfile, deldir);
1077 590d39af 2014-12-02 marcelgmr else
1078 590d39af 2014-12-02 marcelgmr message("No entries marked for moving.", RED);
1079 a31a7b29 2014-11-15 marcelgmr }
1080 576e768d 2014-11-08 marcelgmr }
1081 18016333 2014-11-14 marcelgmr if (rover.nfiles)
1082 18016333 2014-11-14 marcelgmr free_rows(&rover.rows, rover.nfiles);
1083 c3758004 2014-11-15 marcelgmr free_marks(&rover.marks);
1084 f05efd9e 2014-11-07 marcelgmr delwin(rover.window);
1085 71ee1627 2014-11-07 marcelgmr return 0;
1086 71ee1627 2014-11-07 marcelgmr }