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.
 
 
 
 
 
 

471 lignes
7.7 KiB

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