My build of nnn with minor changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

noice.c 16 KiB

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