5 #include <sys/types.h> /* pid_t, ... */
6 #include <stdio.h> /* FILENAME_MAX */
7 #include <locale.h> /* setlocale(), LC_ALL */
8 #include <unistd.h> /* chdir(), getcwd() */
9 #include <dirent.h> /* DIR, struct dirent, opendir(), ... */
11 #include <sys/wait.h> /* waitpid() */
19 char STATUS[STATUSSZ];
21 char SEARCH[SEARCHSZ];
25 typedef enum {DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, MAGENTA, WHITE} color_t;
27 #define HEIGHT (LINES-4)
29 #define SHOW_FILES 0x01u
30 #define SHOW_DIRS 0x02u
31 #define SHOW_HIDDEN 0x04u
46 char cwd[10][FILENAME_MAX];
49 #define FNAME(I) rover.rows[I].name
50 #define FSIZE(I) rover.rows[I].size
51 #define SCROLL rover.scroll[rover.tab]
52 #define FSEL rover.fsel[rover.tab]
53 #define FLAGS rover.flags[rover.tab]
54 #define CWD rover.cwd[rover.tab]
57 rowcmp(const void *a, const void *b)
59 int isdir1, isdir2, cmpdir;
62 isdir1 = strchr(r1->name, '/') != NULL;
63 isdir2 = strchr(r2->name, '/') != NULL;
64 cmpdir = isdir2 - isdir1;
65 return cmpdir ? cmpdir : strcoll(r1->name, r2->name);
69 free_rows(row_t **rowsp, int nfiles)
73 for (i = 0; i < nfiles; i++)
74 free((*rowsp)[i].name);
80 ls(char *path, row_t **rowsp, uint8_t flags)
88 if((dp = opendir(path)) == NULL)
90 n = -2; /* We don't want the entries "." and "..". */
91 while (readdir(dp)) n++;
93 rows = (row_t *) malloc(n * sizeof(row_t));
95 while ((ep = readdir(dp))) {
96 if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, ".."))
98 if (!(flags & SHOW_HIDDEN) && ep->d_name[0] == '.')
100 /* FIXME: ANSI C doesn't have lstat(). How do we handle symlinks? */
101 stat(ep->d_name, &statbuf);
102 if (S_ISDIR(statbuf.st_mode)) {
103 if (flags & SHOW_DIRS) {
104 rows[i].name = (char *) malloc(strlen(ep->d_name) + 2);
105 strcpy(rows[i].name, ep->d_name);
106 strcat(rows[i].name, "/");
110 else if (flags & SHOW_FILES) {
111 rows[i].name = (char *) malloc(strlen(ep->d_name) + 1);
112 strcpy(rows[i].name, ep->d_name);
113 rows[i].size = statbuf.st_size;
117 n = i; /* Ignore unused space in array caused by filters. */
118 qsort(rows, n, sizeof(row_t), rowcmp);
133 setlocale(LC_ALL, "");
135 cbreak(); /* Get one character at a time. */
137 nonl(); /* No NL->CR/NL on output. */
138 intrflush(stdscr, FALSE);
139 keypad(stdscr, TRUE);
140 curs_set(FALSE); /* Hide blinking cursor. */
143 init_pair(RED, COLOR_RED, COLOR_BLACK);
144 init_pair(GREEN, COLOR_GREEN, COLOR_BLACK);
145 init_pair(YELLOW, COLOR_YELLOW,COLOR_BLACK);
146 init_pair(BLUE, COLOR_BLUE, COLOR_BLACK);
147 init_pair(CYAN, COLOR_CYAN, COLOR_BLACK);
148 init_pair(MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
149 init_pair(WHITE, COLOR_WHITE, COLOR_BLACK);
160 for (i = 0, j = SCROLL; i < HEIGHT && j < rover.nfiles; i++, j++) {
161 ishidden = FNAME(j)[0] == '.';
162 isdir = strchr(FNAME(j), '/') != NULL;
164 wattr_on(rover.window, A_REVERSE, NULL);
166 wcolor_set(rover.window, RVC_HIDDEN, NULL);
168 wcolor_set(rover.window, RVC_DIR, NULL);
170 wcolor_set(rover.window, RVC_FILE, NULL);
172 sprintf(ROW, "%s%*d", FNAME(j),
173 COLS - strlen(FNAME(j)) - 2, (int) FSIZE(j));
175 strcpy(ROW, FNAME(j));
176 mvwhline(rover.window, i + 1, 1, ' ', COLS - 2);
177 mvwaddnstr(rover.window, i + 1, 1, ROW, COLS - 2);
178 wcolor_set(rover.window, DEFAULT, NULL);
180 wattr_off(rover.window, A_REVERSE, NULL);
182 if (rover.nfiles > HEIGHT) {
184 center = (SCROLL + (HEIGHT >> 1)) * HEIGHT / rover.nfiles;
185 height = (HEIGHT-1) * HEIGHT / rover.nfiles;
186 if (!height) height = 1;
187 wcolor_set(rover.window, RVC_BORDER, NULL);
188 wborder(rover.window, 0, 0, 0, 0, 0, 0, 0, 0);
189 wcolor_set(rover.window, RVC_SCROLLBAR, NULL);
190 mvwvline(rover.window, center-(height>>1)+1, COLS-1, ACS_CKBOARD, height);
191 wcolor_set(rover.window, DEFAULT, NULL);
193 wrefresh(rover.window);
194 STATUS[0] = FLAGS & SHOW_FILES ? 'F' : ' ';
195 STATUS[1] = FLAGS & SHOW_DIRS ? 'D' : ' ';
196 STATUS[2] = FLAGS & SHOW_HIDDEN ? 'H' : ' ';
200 sprintf(ROW, "%d/%d", FSEL + 1, rover.nfiles);
201 sprintf(STATUS+3, "%*s", 12, ROW);
202 color_set(RVC_STATUS, NULL);
203 mvaddstr(LINES - 1, COLS - 15, STATUS);
204 color_set(DEFAULT, NULL);
208 /* NOTE: The caller needs to write the new path to CWD
209 * *before* calling this function. */
216 mvhline(0, 0, ' ', COLS);
217 color_set(RVC_CWD, NULL);
218 mvaddnstr(0, 0, CWD, COLS);
219 color_set(DEFAULT, NULL);
221 attr_on(A_BOLD, NULL);
222 color_set(RVC_TABNUM, NULL);
223 echochar(rover.tab + '0');
224 color_set(DEFAULT, NULL);
225 attr_off(A_BOLD, NULL);
227 free_rows(&rover.rows, rover.nfiles);
228 rover.nfiles = ls(CWD, &rover.rows, FLAGS);
229 wclear(rover.window);
230 wcolor_set(rover.window, RVC_BORDER, NULL);
231 wborder(rover.window, 0, 0, 0, 0, 0, 0, 0, 0);
232 wcolor_set(rover.window, DEFAULT, NULL);
244 /* fork() succeeded. */
246 waitpid(pid, &status, 0);
252 execvp(args[0], args);
256 /* Interactive getstr(). */
258 igetstr(char *buffer, int maxlen)
262 length = strlen(buffer);
264 if (ch == '\r' || ch == '\n' || ch == KEY_DOWN || ch == KEY_ENTER)
266 else if (ch == erasechar() || ch == KEY_LEFT || ch == KEY_BACKSPACE) {
268 buffer[--length] = '\0';
270 else if (ch == killchar()) {
274 else if (length < maxlen - 1 && isprint(ch)) {
275 buffer[length++] = ch;
276 buffer[length] = '\0';
282 main(int argc, char *argv[])
289 /* Avoid invalid free() calls in cd() by zeroing the tally. */
291 for (i = 0; i < 10; i++) {
292 rover.fsel[i] = rover.scroll[i] = 0;
293 rover.flags[i] = SHOW_FILES | SHOW_DIRS;
295 strcpy(rover.cwd[0], getenv("HOME"));
296 for (i = 1; i < argc && i < 10; i++) {
297 d = opendir(argv[i]);
299 strcpy(rover.cwd[i], argv[i]);
302 else strcpy(rover.cwd[i], rover.cwd[0]);
304 getcwd(rover.cwd[i], FILENAME_MAX);
305 for (i++; i < 10; i++)
306 strcpy(rover.cwd[i], rover.cwd[i-1]);
307 for (i = 0; i < 10; i++)
308 if (rover.cwd[i][strlen(rover.cwd[i]) - 1] != '/')
309 strcat(rover.cwd[i], "/");
311 rover.window = subwin(stdscr, LINES - 2, COLS, 1, 0);
316 if (!strcmp(key, RVK_QUIT))
318 else if (ch >= '0' && ch <= '9') {
319 rover.tab = ch - '0';
322 else if (!strcmp(key, RVK_DOWN)) {
323 if (!rover.nfiles) continue;
324 if (FSEL == rover.nfiles - 1)
328 if ((FSEL - SCROLL) == HEIGHT)
333 else if (!strcmp(key, RVK_UP)) {
334 if (!rover.nfiles) continue;
336 FSEL = rover.nfiles - 1;
337 SCROLL = rover.nfiles - HEIGHT;
348 else if (!strcmp(key, RVK_JUMP_DOWN)) {
349 if (!rover.nfiles) continue;
351 if (FSEL >= rover.nfiles)
352 FSEL = rover.nfiles - 1;
353 if (rover.nfiles > HEIGHT) {
355 if (SCROLL > rover.nfiles - HEIGHT)
356 SCROLL = rover.nfiles - HEIGHT;
360 else if (!strcmp(key, RVK_JUMP_UP)) {
361 if (!rover.nfiles) continue;
370 else if (!strcmp(key, RVK_CD_DOWN)) {
371 if (!rover.nfiles) continue;
372 if (strchr(FNAME(FSEL), '/') == NULL)
374 strcat(CWD, FNAME(FSEL));
377 else if (!strcmp(key, RVK_CD_UP)) {
378 char *dirname, first;
379 if (strlen(CWD) == 1)
381 CWD[strlen(CWD) - 1] = '\0';
382 dirname = strrchr(CWD, '/') + 1;
386 if ((FLAGS & SHOW_DIRS) &&
387 ((FLAGS & SHOW_HIDDEN) || (first != '.'))
390 dirname[strlen(dirname)] = '/';
391 while (strcmp(FNAME(FSEL), dirname))
393 if (rover.nfiles > HEIGHT) {
394 SCROLL = FSEL - (HEIGHT >> 1);
397 if (SCROLL > rover.nfiles - HEIGHT)
398 SCROLL = rover.nfiles - HEIGHT;
404 else if (!strcmp(key, RVK_HOME)) {
405 strcpy(CWD, getenv("HOME"));
406 if (CWD[strlen(CWD) - 1] != '/')
410 else if (!strcmp(key, RVK_SHELL)) {
411 program = getenv("SHELL");
418 else if (!strcmp(key, RVK_VIEW)) {
419 if (!rover.nfiles) continue;
420 if (strchr(FNAME(FSEL), '/') != NULL)
422 program = getenv("PAGER");
425 args[1] = FNAME(FSEL);
430 else if (!strcmp(key, RVK_EDIT)) {
431 if (!rover.nfiles) continue;
432 if (strchr(FNAME(FSEL), '/') != NULL)
434 program = getenv("EDITOR");
437 args[1] = FNAME(FSEL);
442 else if (!strcmp(key, RVK_SEARCH)) {
443 int oldsel, oldscroll;
444 if (!rover.nfiles) continue;
448 color_set(RVC_PROMPT, NULL);
449 mvaddstr(LINES - 1, 0, "search: ");
450 color_set(DEFAULT, NULL);
451 while (igetstr(SEARCH, SEARCHSZ)) {
454 length = strlen(SEARCH);
456 for (sel = 0; sel < rover.nfiles; sel++)
457 if (!strncmp(FNAME(sel), SEARCH, length))
459 if (sel < rover.nfiles) {
462 if (rover.nfiles > HEIGHT) {
465 else if (sel - 3 > rover.nfiles - HEIGHT)
466 SCROLL = rover.nfiles - HEIGHT;
480 color_set(color, NULL);
481 mvaddstr(LINES - 1, 8, SEARCH);
482 color_set(DEFAULT, NULL);
483 SEARCH[length] = '\0';
489 else if (!strcmp(key, RVK_TG_FILES)) {
493 else if (!strcmp(key, RVK_TG_DIRS)) {
497 else if (!strcmp(key, RVK_TG_HIDDEN)) {
498 FLAGS ^= SHOW_HIDDEN;
503 free_rows(&rover.rows, rover.nfiles);
505 delwin(rover.window);