My build of nnn with minor changes
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

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