My build of nnn with minor changes
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

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