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.
 
 
 
 
 
 

355 lines
5.5 KiB

  1. #include <sys/types.h>
  2. #include <errno.h>
  3. #include <fcntl.h>
  4. #include <dirent.h>
  5. #include <curses.h>
  6. #include <libgen.h>
  7. #include <locale.h>
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <signal.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #ifdef DEBUG
  14. #define DPRINTF_D(x) printw(#x "=%d\n", x)
  15. #define DPRINTF_S(x) printw(#x "=%s\n", x)
  16. #define DPRINTF_P(x) printw(#x "=0x%p\n", x)
  17. #else
  18. #define DPRINTF_D(x)
  19. #define DPRINTF_S(x)
  20. #define DPRINTF_P(x)
  21. #endif /* DEBUG */
  22. #define LEN(x) (sizeof(x) / sizeof(*(x)))
  23. /*
  24. * Layout:
  25. * .---------
  26. * | cwd: /mnt/path
  27. * |
  28. * | > file0
  29. * | file1
  30. * ...
  31. * | filen
  32. * |
  33. * | msg: invalid extension
  34. * '------
  35. */
  36. int die = 0;
  37. struct assoc {
  38. char *ext;
  39. char *bin;
  40. } assocs[] = {
  41. { "avi", "mplayer" },
  42. { "mp4", "mplayer" },
  43. { "mkv", "mplayer" },
  44. { "mp3", "mplayer" },
  45. { "ogg", "mplayer" },
  46. { "srt", "less" },
  47. { "txt", "less" },
  48. };
  49. char *
  50. extension(char *file)
  51. {
  52. char *dot;
  53. dot = strrchr(file, '.');
  54. if (dot == NULL || dot == file)
  55. return NULL;
  56. else
  57. return dot + 1;
  58. }
  59. char *
  60. openwith(char *ext)
  61. {
  62. int i;
  63. for (i = 0; i < LEN(assocs); i++)
  64. if (strncmp(assocs[i].ext, ext, strlen(ext)) == 0)
  65. return assocs[i].bin;
  66. return NULL;
  67. }
  68. int
  69. dentcmp(const void *va, const void *vb)
  70. {
  71. const struct dirent *a, *b;
  72. a = *(struct dirent **)va;
  73. b = *(struct dirent **)vb;
  74. return strcmp(a->d_name, b->d_name);
  75. }
  76. /* Warning shows up at the bottom */
  77. void
  78. printwarn(char *prefix)
  79. {
  80. move(LINES - 1, 0);
  81. printw("%s: %s\n", prefix, strerror(errno));
  82. }
  83. /* Kill curses and display error before exiting */
  84. void
  85. printerr(int ret, char *prefix)
  86. {
  87. endwin();
  88. printf("%s: %s\n", prefix, strerror(errno));
  89. exit(ret);
  90. }
  91. /*
  92. * Returns 0 normally
  93. * On movement it updates *cur
  94. * Returns 1 on quit
  95. * Returns 2 on go in
  96. * Returns 3 on go up
  97. */
  98. int
  99. nextsel(int *cur, int max)
  100. {
  101. int c;
  102. c = getch();
  103. switch (c) {
  104. case 'q':
  105. return 1;
  106. /* go up */
  107. case KEY_BACKSPACE:
  108. case KEY_LEFT:
  109. case 'h':
  110. return 2;
  111. /* go in */
  112. case KEY_ENTER:
  113. case '\r':
  114. case KEY_RIGHT:
  115. case 'l':
  116. return 3;
  117. /* next */
  118. case 'j':
  119. case KEY_DOWN:
  120. if (*cur < max - 1)
  121. (*cur)++;
  122. break;
  123. /* prev */
  124. case 'k':
  125. case KEY_UP:
  126. if (*cur > 0)
  127. (*cur)--;
  128. break;
  129. }
  130. return 0;
  131. }
  132. int
  133. testopen(char *path)
  134. {
  135. int fd;
  136. fd = open(path, O_RDONLY);
  137. if (fd == -1) {
  138. return 0;
  139. } else {
  140. close(fd);
  141. return 1;
  142. }
  143. }
  144. int
  145. testopendir(char *path)
  146. {
  147. DIR *dirp;
  148. dirp = opendir(path);
  149. if (dirp == NULL) {
  150. return 0;
  151. } else {
  152. closedir(dirp);
  153. return 1;
  154. }
  155. }
  156. void
  157. browse(const char *ipath)
  158. {
  159. DIR *dirp;
  160. struct dirent *dp;
  161. struct dirent **dents;
  162. int i, n, cur;
  163. int r, ret;
  164. char *path = strdup(ipath);
  165. begin:
  166. /* Path is a malloc(3)-ed string */
  167. n = 0;
  168. cur = 0;
  169. dents = NULL;
  170. dirp = opendir(path);
  171. if (dirp == NULL) {
  172. printwarn("opendir");
  173. goto nochange;
  174. }
  175. while ((dp = readdir(dirp)) != NULL) {
  176. /* Skip self and parent */
  177. if (strncmp(dp->d_name, ".", 2) == 0
  178. || strncmp(dp->d_name, "..", 3) == 0)
  179. continue;
  180. dents = realloc(dents, (n + 1) * sizeof(*dents));
  181. if (dents == NULL)
  182. printerr(1, "realloc");
  183. dents[n] = dp;
  184. n++;
  185. }
  186. qsort(dents, n, sizeof(*dents), dentcmp);
  187. for (;;) {
  188. redraw:
  189. /* Clean screen */
  190. erase();
  191. /* Strip slashes */
  192. for (i = strlen(path) - 1; i > -1; i--)
  193. if (path[i] == '/')
  194. path[i] = '\0';
  195. else
  196. break;
  197. DPRINTF_D(cur);
  198. DPRINTF_S(path);
  199. /* Print cwd */
  200. printw("cwd: %s%s\n\n",
  201. strncmp(path, "", 1) == 0 ? "/" : "",
  202. path);
  203. /* Print listing */
  204. for (i = 0; i < n; i++)
  205. printw(" %s %s\n",
  206. i == cur ? ">" : " ",
  207. dents[i]->d_name);
  208. nochange:
  209. ret = nextsel(&cur, n);
  210. if (ret == 1) {
  211. free(path);
  212. return;
  213. }
  214. if (ret == 2) {
  215. /* Handle root case */
  216. if (strncmp(path, "", 1) == 0) {
  217. goto nochange;
  218. } else {
  219. path = dirname(path);
  220. goto out;
  221. }
  222. }
  223. if (ret == 3) {
  224. char *name, *file = NULL;
  225. char *newpath;
  226. char *ext, *bin;
  227. pid_t pid;
  228. name = dents[cur]->d_name;
  229. switch (dents[cur]->d_type) {
  230. case DT_DIR:
  231. newpath = malloc(strlen(path) + 1
  232. + strlen(name) + 1);
  233. sprintf(newpath, "%s/%s", path, name);
  234. if (testopen(newpath)) {
  235. free(path);
  236. path = newpath;
  237. goto out;
  238. } else {
  239. printwarn(newpath);
  240. free(newpath);
  241. goto nochange;
  242. }
  243. case DT_REG:
  244. file = malloc(strlen(path) + 1
  245. + strlen(name) + 1);
  246. sprintf(file, "%s/%s", path, name);
  247. DPRINTF_S(file);
  248. /* Open with */
  249. ext = extension(name);
  250. if (ext == NULL) {
  251. printwarn("invalid extension\n");
  252. goto nochange;
  253. }
  254. bin = openwith(ext);
  255. if (bin == NULL) {
  256. printwarn("no association\n");
  257. goto nochange;
  258. }
  259. DPRINTF_S(ext);
  260. DPRINTF_S(bin);
  261. /* Run program */
  262. pid = fork();
  263. if (pid == 0)
  264. execlp(bin, bin, file, NULL);
  265. else
  266. waitpid(pid, NULL, 0);
  267. free(file);
  268. /* Screen may be messed up */
  269. clear();
  270. /* Some programs reset this */
  271. keypad(stdscr, TRUE);
  272. goto redraw;
  273. default:
  274. DPRINTF_D(dents[cur]->d_type);
  275. }
  276. }
  277. }
  278. out:
  279. free(dents);
  280. r = closedir(dirp);
  281. if (r == -1)
  282. printerr(1, "closedir");
  283. goto begin;
  284. }
  285. int
  286. main(int argc, char *argv[])
  287. {
  288. char *ipath = argv[1] != NULL ? argv[1] : "/";
  289. /* Test initial path */
  290. if (!testopendir(ipath))
  291. printerr(1, ipath);
  292. /* Set locale before curses setup */
  293. setlocale(LC_ALL, "");
  294. /* Init curses */
  295. initscr();
  296. cbreak();
  297. noecho();
  298. nonl();
  299. intrflush(stdscr, FALSE);
  300. keypad(stdscr, TRUE);
  301. curs_set(FALSE); /* Hide cursor */
  302. browse(ipath);
  303. endwin(); /* Restore terminal */
  304. return 0;
  305. }