My build of nnn with minor changes
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

949 строки
18 KiB

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