My build of nnn with minor changes
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

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