noice.c - noice - small file browser (mirror / fork from 2f30.org) | |
git clone git://git.codemadness.org/noice | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
noice.c (15447B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <sys/stat.h> | |
3 #include <sys/types.h> | |
4 | |
5 #include <curses.h> | |
6 #include <dirent.h> | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <libgen.h> | |
10 #include <limits.h> | |
11 #include <locale.h> | |
12 #include <regex.h> | |
13 #include <signal.h> | |
14 #include <stdarg.h> | |
15 #include <stdio.h> | |
16 #include <stdlib.h> | |
17 #include <string.h> | |
18 #include <unistd.h> | |
19 | |
20 #include "arg.h" | |
21 #include "util.h" | |
22 | |
23 #define ISODD(x) ((x) & 1) | |
24 #define CONTROL(c) ((c) ^ 0x40) | |
25 #define META(c) ((c) ^ 0x80) | |
26 | |
27 struct cpair { | |
28 int fg; | |
29 int bg; | |
30 }; | |
31 | |
32 /* Supported actions */ | |
33 enum action { | |
34 SEL_QUIT = 1, | |
35 SEL_BACK, | |
36 SEL_GOIN, | |
37 SEL_FLTR, | |
38 SEL_NEXT, | |
39 SEL_PREV, | |
40 SEL_PGDN, | |
41 SEL_PGUP, | |
42 SEL_HOME, | |
43 SEL_END, | |
44 SEL_CD, | |
45 SEL_CDHOME, | |
46 SEL_TOGGLEDOT, | |
47 SEL_DSORT, | |
48 SEL_MTIME, | |
49 SEL_ICASE, | |
50 SEL_VERS, | |
51 SEL_REDRAW, | |
52 SEL_RUN, | |
53 SEL_RUNARG, | |
54 }; | |
55 | |
56 struct key { | |
57 int sym; /* Key pressed */ | |
58 enum action act; /* Action */ | |
59 char *run; /* Program to run */ | |
60 char *env; /* Environment variable override */ | |
61 }; | |
62 | |
63 #include "noiceconf.h" | |
64 | |
65 struct entry { | |
66 char name[PATH_MAX]; | |
67 mode_t mode; | |
68 time_t t; | |
69 }; | |
70 | |
71 /* Global context */ | |
72 struct entry *dents; | |
73 char *argv0; | |
74 int ndents, cur; | |
75 int idle; | |
76 | |
77 /* | |
78 * Layout: | |
79 * .--------- | |
80 * | /mnt/path | |
81 * | | |
82 * | file0 | |
83 * | file1 | |
84 * | > file2 | |
85 * | file3 | |
86 * | file4 | |
87 * ... | |
88 * | filen | |
89 * | | |
90 * | Permission denied | |
91 * '------ | |
92 */ | |
93 | |
94 void info(char *, ...); | |
95 void warn(char *, ...); | |
96 void fatal(char *, ...); | |
97 | |
98 void * | |
99 xrealloc(void *p, size_t size) | |
100 { | |
101 p = realloc(p, size); | |
102 if (p == NULL) | |
103 fatal("realloc"); | |
104 return p; | |
105 } | |
106 | |
107 /* Some implementations of dirname(3) may modify `path' and some | |
108 * return a pointer inside `path'. */ | |
109 char * | |
110 xdirname(const char *path) | |
111 { | |
112 static char out[PATH_MAX]; | |
113 char tmp[PATH_MAX], *p; | |
114 | |
115 strlcpy(tmp, path, sizeof(tmp)); | |
116 p = dirname(tmp); | |
117 if (p == NULL) | |
118 fatal("dirname"); | |
119 strlcpy(out, p, sizeof(out)); | |
120 return out; | |
121 } | |
122 | |
123 char * | |
124 xgetenv(char *name, char *fallback) | |
125 { | |
126 char *value; | |
127 | |
128 if (name == NULL) | |
129 return fallback; | |
130 value = getenv(name); | |
131 return value && value[0] ? value : fallback; | |
132 } | |
133 | |
134 int | |
135 setfilter(regex_t *regex, char *filter) | |
136 { | |
137 char errbuf[LINE_MAX]; | |
138 size_t len; | |
139 int r; | |
140 | |
141 r = regcomp(regex, filter, REG_NOSUB | REG_EXTENDED | REG_ICASE); | |
142 if (r != 0) { | |
143 len = COLS; | |
144 if (len > sizeof(errbuf)) | |
145 len = sizeof(errbuf); | |
146 regerror(r, regex, errbuf, len); | |
147 info("%s", errbuf); | |
148 } | |
149 return r; | |
150 } | |
151 | |
152 void | |
153 freefilter(regex_t *regex) | |
154 { | |
155 regfree(regex); | |
156 } | |
157 | |
158 void | |
159 initfilter(int dot, char **ifilter) | |
160 { | |
161 *ifilter = dot ? "." : "^[^.]"; | |
162 } | |
163 | |
164 int | |
165 visible(regex_t *regex, char *file) | |
166 { | |
167 return regexec(regex, file, 0, NULL, 0) == 0; | |
168 } | |
169 | |
170 int | |
171 dircmp(mode_t a, mode_t b) | |
172 { | |
173 if (S_ISDIR(a) && S_ISDIR(b)) | |
174 return 0; | |
175 if (!S_ISDIR(a) && !S_ISDIR(b)) | |
176 return 0; | |
177 if (S_ISDIR(a)) | |
178 return -1; | |
179 else | |
180 return 1; | |
181 } | |
182 | |
183 int | |
184 entrycmp(const void *va, const void *vb) | |
185 { | |
186 const struct entry *a = va, *b = vb; | |
187 | |
188 if (dirorder) { | |
189 if (dircmp(a->mode, b->mode) != 0) | |
190 return dircmp(a->mode, b->mode); | |
191 } | |
192 | |
193 if (mtimeorder) | |
194 return b->t - a->t; | |
195 if (icaseorder) | |
196 return strcasecmp(a->name, b->name); | |
197 if (versorder) | |
198 return strverscmp(a->name, b->name); | |
199 return strcmp(a->name, b->name); | |
200 } | |
201 | |
202 void | |
203 initcolor(void) | |
204 { | |
205 int i; | |
206 | |
207 start_color(); | |
208 use_default_colors(); | |
209 for (i = 1; i < LEN(pairs); i++) | |
210 init_pair(i, pairs[i].fg, pairs[i].bg); | |
211 } | |
212 | |
213 void | |
214 initcurses(void) | |
215 { | |
216 char *term; | |
217 | |
218 if (initscr() == NULL) { | |
219 term = getenv("TERM"); | |
220 if (term != NULL) | |
221 fprintf(stderr, "error opening terminal: %s\n", … | |
222 else | |
223 fprintf(stderr, "failed to initialize curses\n"); | |
224 exit(1); | |
225 } | |
226 if (usecolor && has_colors()) | |
227 initcolor(); | |
228 cbreak(); | |
229 noecho(); | |
230 nonl(); | |
231 intrflush(stdscr, FALSE); | |
232 keypad(stdscr, TRUE); | |
233 curs_set(FALSE); /* Hide cursor */ | |
234 timeout(1000); /* One second */ | |
235 } | |
236 | |
237 void | |
238 exitcurses(void) | |
239 { | |
240 endwin(); /* Restore terminal */ | |
241 } | |
242 | |
243 /* Messages show up at the bottom */ | |
244 void | |
245 info(char *fmt, ...) | |
246 { | |
247 char buf[LINE_MAX]; | |
248 va_list ap; | |
249 | |
250 va_start(ap, fmt); | |
251 vsnprintf(buf, sizeof(buf), fmt, ap); | |
252 va_end(ap); | |
253 move(LINES - 1, 0); | |
254 printw("%s\n", buf); | |
255 } | |
256 | |
257 /* Display warning as a message */ | |
258 void | |
259 warn(char *fmt, ...) | |
260 { | |
261 char buf[LINE_MAX]; | |
262 va_list ap; | |
263 | |
264 va_start(ap, fmt); | |
265 vsnprintf(buf, sizeof(buf), fmt, ap); | |
266 va_end(ap); | |
267 move(LINES - 1, 0); | |
268 printw("%s: %s\n", buf, strerror(errno)); | |
269 } | |
270 | |
271 /* Kill curses and display error before exiting */ | |
272 void | |
273 fatal(char *fmt, ...) | |
274 { | |
275 va_list ap; | |
276 | |
277 exitcurses(); | |
278 va_start(ap, fmt); | |
279 vfprintf(stderr, fmt, ap); | |
280 fprintf(stderr, ": %s\n", strerror(errno)); | |
281 va_end(ap); | |
282 exit(1); | |
283 } | |
284 | |
285 /* Clear the last line */ | |
286 void | |
287 clearprompt(void) | |
288 { | |
289 info(""); | |
290 } | |
291 | |
292 /* Print prompt on the last line */ | |
293 void | |
294 printprompt(char *str) | |
295 { | |
296 clearprompt(); | |
297 info("%s", str); | |
298 } | |
299 | |
300 int | |
301 xgetch(void) | |
302 { | |
303 int c; | |
304 | |
305 c = getch(); | |
306 if (c == -1) | |
307 idle++; | |
308 else | |
309 idle = 0; | |
310 return c; | |
311 } | |
312 | |
313 /* Returns SEL_* if key is bound and 0 otherwise. | |
314 * Also modifies the run and env pointers (used on SEL_{RUN,RUNARG}) */ | |
315 int | |
316 nextsel(char **run, char **env) | |
317 { | |
318 int c, i; | |
319 | |
320 c = xgetch(); | |
321 if (c == 033) | |
322 c = META(xgetch()); | |
323 | |
324 for (i = 0; i < LEN(bindings); i++) | |
325 if (c == bindings[i].sym) { | |
326 *run = bindings[i].run; | |
327 *env = bindings[i].env; | |
328 return bindings[i].act; | |
329 } | |
330 return 0; | |
331 } | |
332 | |
333 char * | |
334 readln(void) | |
335 { | |
336 static char ln[LINE_MAX]; | |
337 | |
338 timeout(-1); | |
339 echo(); | |
340 curs_set(TRUE); | |
341 memset(ln, 0, sizeof(ln)); | |
342 wgetnstr(stdscr, ln, sizeof(ln) - 1); | |
343 noecho(); | |
344 curs_set(FALSE); | |
345 timeout(1000); | |
346 return ln[0] ? ln : NULL; | |
347 } | |
348 | |
349 int | |
350 canopendir(char *path) | |
351 { | |
352 DIR *dirp; | |
353 | |
354 dirp = opendir(path); | |
355 if (dirp == NULL) | |
356 return 0; | |
357 closedir(dirp); | |
358 return 1; | |
359 } | |
360 | |
361 char * | |
362 mkpath(char *dir, char *name, char *out, size_t n) | |
363 { | |
364 /* Handle absolute path */ | |
365 if (name[0] == '/') { | |
366 strlcpy(out, name, n); | |
367 } else { | |
368 /* Handle root case */ | |
369 if (strcmp(dir, "/") == 0) { | |
370 strlcpy(out, "/", n); | |
371 strlcat(out, name, n); | |
372 } else { | |
373 strlcpy(out, dir, n); | |
374 strlcat(out, "/", n); | |
375 strlcat(out, name, n); | |
376 } | |
377 } | |
378 return out; | |
379 } | |
380 | |
381 void | |
382 printent(struct entry *ent, int active) | |
383 { | |
384 char name[PATH_MAX]; | |
385 unsigned int len = COLS - strlen(CURSR) - 1; | |
386 char cm = 0; | |
387 int attr = 0; | |
388 | |
389 /* Copy name locally */ | |
390 strlcpy(name, ent->name, sizeof(name)); | |
391 | |
392 /* No text wrapping in entries */ | |
393 if (strlen(name) < len) | |
394 len = strlen(name) + 1; | |
395 | |
396 if (S_ISDIR(ent->mode)) { | |
397 cm = '/'; | |
398 attr |= DIR_ATTR; | |
399 } else if (S_ISLNK(ent->mode)) { | |
400 cm = '@'; | |
401 attr |= LINK_ATTR; | |
402 } else if (S_ISSOCK(ent->mode)) { | |
403 cm = '='; | |
404 attr |= SOCK_ATTR; | |
405 } else if (S_ISFIFO(ent->mode)) { | |
406 cm = '|'; | |
407 attr |= FIFO_ATTR; | |
408 } else if (ent->mode & S_IXUSR) { | |
409 cm = '*'; | |
410 attr |= EXEC_ATTR; | |
411 } | |
412 | |
413 if (active) | |
414 attr |= CURSR_ATTR; | |
415 | |
416 if (cm) { | |
417 name[len - 1] = cm; | |
418 name[len] = '\0'; | |
419 } | |
420 | |
421 attron(attr); | |
422 printw("%s%s\n", active ? CURSR : EMPTY, name); | |
423 attroff(attr); | |
424 } | |
425 | |
426 int | |
427 dentfill(char *path, struct entry **dents, | |
428 int (*filter)(regex_t *, char *), regex_t *re) | |
429 { | |
430 char newpath[PATH_MAX]; | |
431 DIR *dirp; | |
432 struct dirent *dp; | |
433 struct stat sb; | |
434 int r, n = 0; | |
435 | |
436 dirp = opendir(path); | |
437 if (dirp == NULL) | |
438 return 0; | |
439 | |
440 while ((dp = readdir(dirp)) != NULL) { | |
441 /* Skip self and parent */ | |
442 if (strcmp(dp->d_name, ".") == 0 || | |
443 strcmp(dp->d_name, "..") == 0) | |
444 continue; | |
445 if (filter(re, dp->d_name) == 0) | |
446 continue; | |
447 *dents = xrealloc(*dents, (n + 1) * sizeof(**dents)); | |
448 strlcpy((*dents)[n].name, dp->d_name, sizeof((*dents)[n]… | |
449 /* Get mode flags */ | |
450 mkpath(path, dp->d_name, newpath, sizeof(newpath)); | |
451 r = lstat(newpath, &sb); | |
452 if (r == -1) | |
453 fatal("lstat"); | |
454 (*dents)[n].mode = sb.st_mode; | |
455 (*dents)[n].t = sb.st_mtime; | |
456 n++; | |
457 } | |
458 | |
459 /* Should never be null */ | |
460 r = closedir(dirp); | |
461 if (r == -1) | |
462 fatal("closedir"); | |
463 return n; | |
464 } | |
465 | |
466 void | |
467 dentfree(struct entry *dents) | |
468 { | |
469 free(dents); | |
470 } | |
471 | |
472 /* Return the position of the matching entry or 0 otherwise */ | |
473 int | |
474 dentfind(struct entry *dents, int n, char *cwd, char *path) | |
475 { | |
476 char tmp[PATH_MAX]; | |
477 int i; | |
478 | |
479 if (path == NULL) | |
480 return 0; | |
481 for (i = 0; i < n; i++) { | |
482 mkpath(cwd, dents[i].name, tmp, sizeof(tmp)); | |
483 DPRINTF_S(path); | |
484 DPRINTF_S(tmp); | |
485 if (strcmp(tmp, path) == 0) | |
486 return i; | |
487 } | |
488 return 0; | |
489 } | |
490 | |
491 int | |
492 populate(char *path, char *oldpath, char *fltr) | |
493 { | |
494 regex_t re; | |
495 int r; | |
496 | |
497 /* Can fail when permissions change while browsing */ | |
498 if (canopendir(path) == 0) | |
499 return -1; | |
500 | |
501 /* Search filter */ | |
502 r = setfilter(&re, fltr); | |
503 if (r != 0) | |
504 return -1; | |
505 | |
506 dentfree(dents); | |
507 | |
508 ndents = 0; | |
509 dents = NULL; | |
510 | |
511 ndents = dentfill(path, &dents, visible, &re); | |
512 freefilter(&re); | |
513 if (ndents == 0) | |
514 return 0; /* Empty result */ | |
515 | |
516 qsort(dents, ndents, sizeof(*dents), entrycmp); | |
517 | |
518 /* Find cur from history */ | |
519 cur = dentfind(dents, ndents, path, oldpath); | |
520 return 0; | |
521 } | |
522 | |
523 void | |
524 redraw(char *path) | |
525 { | |
526 char cwd[PATH_MAX], cwdresolved[PATH_MAX]; | |
527 size_t ncols; | |
528 int nlines, odd; | |
529 int i; | |
530 | |
531 nlines = MIN(LINES - 4, ndents); | |
532 | |
533 /* Clean screen */ | |
534 erase(); | |
535 | |
536 /* Strip trailing slashes */ | |
537 for (i = strlen(path) - 1; i > 0; i--) | |
538 if (path[i] == '/') | |
539 path[i] = '\0'; | |
540 else | |
541 break; | |
542 | |
543 DPRINTF_D(cur); | |
544 DPRINTF_S(path); | |
545 | |
546 /* No text wrapping in cwd line */ | |
547 ncols = COLS; | |
548 if (ncols > PATH_MAX) | |
549 ncols = PATH_MAX; | |
550 strlcpy(cwd, path, ncols); | |
551 cwd[ncols - strlen(CWD) - 1] = '\0'; | |
552 realpath(cwd, cwdresolved); | |
553 | |
554 printw(CWD "%s\n\n", cwdresolved); | |
555 | |
556 /* Print listing */ | |
557 odd = ISODD(nlines); | |
558 if (cur < nlines / 2) { | |
559 for (i = 0; i < nlines; i++) | |
560 printent(&dents[i], i == cur); | |
561 } else if (cur >= ndents - nlines / 2) { | |
562 for (i = ndents - nlines; i < ndents; i++) | |
563 printent(&dents[i], i == cur); | |
564 } else { | |
565 for (i = cur - nlines / 2; | |
566 i < cur + nlines / 2 + odd; i++) | |
567 printent(&dents[i], i == cur); | |
568 } | |
569 } | |
570 | |
571 void | |
572 browse(char *ipath, char *ifilter) | |
573 { | |
574 char path[PATH_MAX], oldpath[PATH_MAX], newpath[PATH_MAX]; | |
575 char fltr[LINE_MAX]; | |
576 char *dir, *tmp, *run, *env; | |
577 struct stat sb; | |
578 regex_t re; | |
579 int r, fd; | |
580 | |
581 strlcpy(path, ipath, sizeof(path)); | |
582 strlcpy(fltr, ifilter, sizeof(fltr)); | |
583 oldpath[0] = '\0'; | |
584 begin: | |
585 r = populate(path, oldpath, fltr); | |
586 if (r == -1) { | |
587 warn("populate"); | |
588 goto nochange; | |
589 } | |
590 | |
591 for (;;) { | |
592 redraw(path); | |
593 nochange: | |
594 switch (nextsel(&run, &env)) { | |
595 case SEL_QUIT: | |
596 dentfree(dents); | |
597 return; | |
598 case SEL_BACK: | |
599 /* There is no going back */ | |
600 if (strcmp(path, "/") == 0 || | |
601 strcmp(path, ".") == 0 || | |
602 strchr(path, '/') == NULL) | |
603 goto nochange; | |
604 dir = xdirname(path); | |
605 if (canopendir(dir) == 0) { | |
606 warn("canopendir"); | |
607 goto nochange; | |
608 } | |
609 /* Save history */ | |
610 strlcpy(oldpath, path, sizeof(oldpath)); | |
611 strlcpy(path, dir, sizeof(path)); | |
612 /* Reset filter */ | |
613 strlcpy(fltr, ifilter, sizeof(fltr)); | |
614 goto begin; | |
615 case SEL_GOIN: | |
616 /* Cannot descend in empty directories */ | |
617 if (ndents == 0) | |
618 goto nochange; | |
619 | |
620 mkpath(path, dents[cur].name, newpath, sizeof(ne… | |
621 DPRINTF_S(newpath); | |
622 | |
623 /* Get path info */ | |
624 fd = open(newpath, O_RDONLY | O_NONBLOCK); | |
625 if (fd == -1) { | |
626 warn("open"); | |
627 goto nochange; | |
628 } | |
629 r = fstat(fd, &sb); | |
630 if (r == -1) { | |
631 warn("fstat"); | |
632 close(fd); | |
633 goto nochange; | |
634 } | |
635 close(fd); | |
636 DPRINTF_U(sb.st_mode); | |
637 | |
638 switch (sb.st_mode & S_IFMT) { | |
639 case S_IFDIR: | |
640 if (canopendir(newpath) == 0) { | |
641 warn("canopendir"); | |
642 goto nochange; | |
643 } | |
644 strlcpy(path, newpath, sizeof(path)); | |
645 /* Reset filter */ | |
646 strlcpy(fltr, ifilter, sizeof(fltr)); | |
647 goto begin; | |
648 case S_IFREG: | |
649 exitcurses(); | |
650 run = xgetenv("NOPEN", NOPEN); | |
651 r = spawnlp(path, run, run, newpath, (vo… | |
652 initcurses(); | |
653 if (r == -1) { | |
654 info("Failed to execute plumber"… | |
655 goto nochange; | |
656 } | |
657 continue; | |
658 default: | |
659 info("Unsupported file"); | |
660 goto nochange; | |
661 } | |
662 case SEL_FLTR: | |
663 /* Read filter */ | |
664 printprompt("/"); | |
665 tmp = readln(); | |
666 if (tmp == NULL) | |
667 tmp = ifilter; | |
668 /* Check and report regex errors */ | |
669 r = setfilter(&re, tmp); | |
670 if (r != 0) | |
671 goto nochange; | |
672 freefilter(&re); | |
673 strlcpy(fltr, tmp, sizeof(fltr)); | |
674 DPRINTF_S(fltr); | |
675 /* Save current */ | |
676 if (ndents > 0) | |
677 mkpath(path, dents[cur].name, oldpath, s… | |
678 goto begin; | |
679 case SEL_NEXT: | |
680 if (cur < ndents - 1) | |
681 cur++; | |
682 break; | |
683 case SEL_PREV: | |
684 if (cur > 0) | |
685 cur--; | |
686 break; | |
687 case SEL_PGDN: | |
688 if (cur < ndents - 1) | |
689 cur += MIN((LINES - 4) / 2, ndents - 1 -… | |
690 break; | |
691 case SEL_PGUP: | |
692 if (cur > 0) | |
693 cur -= MIN((LINES - 4) / 2, cur); | |
694 break; | |
695 case SEL_HOME: | |
696 cur = 0; | |
697 break; | |
698 case SEL_END: | |
699 cur = ndents - 1; | |
700 break; | |
701 case SEL_CD: | |
702 /* Read target dir */ | |
703 printprompt("chdir: "); | |
704 tmp = readln(); | |
705 if (tmp == NULL) { | |
706 clearprompt(); | |
707 goto nochange; | |
708 } | |
709 mkpath(path, tmp, newpath, sizeof(newpath)); | |
710 if (canopendir(newpath) == 0) { | |
711 warn("canopendir"); | |
712 goto nochange; | |
713 } | |
714 strlcpy(path, newpath, sizeof(path)); | |
715 /* Reset filter */ | |
716 strlcpy(fltr, ifilter, sizeof(fltr)); | |
717 DPRINTF_S(path); | |
718 goto begin; | |
719 case SEL_CDHOME: | |
720 tmp = getenv("HOME"); | |
721 if (tmp == NULL) { | |
722 clearprompt(); | |
723 goto nochange; | |
724 } | |
725 if (canopendir(tmp) == 0) { | |
726 warn("canopendir"); | |
727 goto nochange; | |
728 } | |
729 strlcpy(path, tmp, sizeof(path)); | |
730 /* Reset filter */ | |
731 strlcpy(fltr, ifilter, sizeof(fltr)); | |
732 DPRINTF_S(path); | |
733 goto begin; | |
734 case SEL_TOGGLEDOT: | |
735 showhidden ^= 1; | |
736 initfilter(showhidden, &ifilter); | |
737 strlcpy(fltr, ifilter, sizeof(fltr)); | |
738 goto begin; | |
739 case SEL_MTIME: | |
740 mtimeorder = !mtimeorder; | |
741 /* Save current */ | |
742 if (ndents > 0) | |
743 mkpath(path, dents[cur].name, oldpath, s… | |
744 goto begin; | |
745 case SEL_DSORT: | |
746 dirorder = !dirorder; | |
747 /* Save current */ | |
748 if (ndents > 0) | |
749 mkpath(path, dents[cur].name, oldpath, s… | |
750 goto begin; | |
751 case SEL_ICASE: | |
752 icaseorder = !icaseorder; | |
753 /* Save current */ | |
754 if (ndents > 0) | |
755 mkpath(path, dents[cur].name, oldpath, s… | |
756 goto begin; | |
757 case SEL_VERS: | |
758 versorder = !versorder; | |
759 /* Save current */ | |
760 if (ndents > 0) | |
761 mkpath(path, dents[cur].name, oldpath, s… | |
762 goto begin; | |
763 case SEL_REDRAW: | |
764 /* Save current */ | |
765 if (ndents > 0) | |
766 mkpath(path, dents[cur].name, oldpath, s… | |
767 goto begin; | |
768 case SEL_RUN: | |
769 /* Save current */ | |
770 if (ndents > 0) | |
771 mkpath(path, dents[cur].name, oldpath, s… | |
772 run = xgetenv(env, run); | |
773 exitcurses(); | |
774 spawnlp(path, run, run, (void *)0); | |
775 initcurses(); | |
776 goto begin; | |
777 case SEL_RUNARG: | |
778 /* Save current */ | |
779 if (ndents > 0) | |
780 mkpath(path, dents[cur].name, oldpath, s… | |
781 run = xgetenv(env, run); | |
782 exitcurses(); | |
783 spawnlp(path, run, run, dents[cur].name, (void *… | |
784 initcurses(); | |
785 goto begin; | |
786 } | |
787 /* Screensaver */ | |
788 if (idletimeout != 0 && idle == idletimeout) { | |
789 idle = 0; | |
790 exitcurses(); | |
791 spawnlp(NULL, idlecmd, idlecmd, (void *)0); | |
792 initcurses(); | |
793 } | |
794 } | |
795 } | |
796 | |
797 void | |
798 usage(void) | |
799 { | |
800 fprintf(stderr, "usage: %s [-c] [dir]\n", argv0); | |
801 exit(1); | |
802 } | |
803 | |
804 int | |
805 main(int argc, char *argv[]) | |
806 { | |
807 char cwd[PATH_MAX], *ipath; | |
808 char *ifilter; | |
809 | |
810 ARGBEGIN { | |
811 case 'c': | |
812 usecolor = 1; | |
813 break; | |
814 default: | |
815 usage(); | |
816 } ARGEND | |
817 | |
818 if (argc > 1) | |
819 usage(); | |
820 | |
821 /* Confirm we are in a terminal */ | |
822 if (!isatty(0) || !isatty(1)) { | |
823 fprintf(stderr, "stdin or stdout is not a tty\n"); | |
824 exit(1); | |
825 } | |
826 | |
827 if (getuid() == 0) | |
828 showhidden = 1; | |
829 initfilter(showhidden, &ifilter); | |
830 | |
831 if (argv[0] != NULL) { | |
832 ipath = argv[0]; | |
833 } else { | |
834 ipath = getcwd(cwd, sizeof(cwd)); | |
835 if (ipath == NULL) | |
836 ipath = "/"; | |
837 } | |
838 | |
839 signal(SIGINT, SIG_IGN); | |
840 | |
841 /* Test initial path */ | |
842 if (canopendir(ipath) == 0) { | |
843 fprintf(stderr, "%s: %s\n", ipath, strerror(errno)); | |
844 exit(1); | |
845 } | |
846 | |
847 /* Set locale before curses setup */ | |
848 setlocale(LC_ALL, ""); | |
849 initcurses(); | |
850 browse(ipath, ifilter); | |
851 exitcurses(); | |
852 exit(0); | |
853 } |