dir.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /* Directory routines
  2. Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  3. 2006, 2007 Free Software Foundation, Inc.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
  15. /** \file dir.c
  16. * \brief Source: directory routines
  17. */
  18. #include <config.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <sys/stat.h>
  23. #include "global.h"
  24. #include "../src/tty/tty.h"
  25. #include "dir.h"
  26. #include "wtools.h"
  27. #include "treestore.h"
  28. #include "strutil.h"
  29. #include "fs.h"
  30. #include "../src/search/search.h"
  31. /* If true show files starting with a dot */
  32. int show_dot_files = 1;
  33. /* If true show files ending in ~ */
  34. int show_backups = 1;
  35. /* If false then directories are shown separately from files */
  36. int mix_all_files = 0;
  37. /*
  38. * If true, SI units (1000 based) will be used for
  39. * larger units (kilobyte, megabyte, ...).
  40. * If false binary units (1024 based) will be used.
  41. */
  42. int kilobyte_si = 0;
  43. /* Reverse flag */
  44. static int reverse = 1;
  45. /* Are the files sorted case sensitively? */
  46. static int case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
  47. /* Are the exec_bit files top in list*/
  48. static int exec_first = 1;
  49. #define MY_ISDIR(x) ( (is_exe (x->st.st_mode) && !(S_ISDIR (x->st.st_mode) || x->f.link_to_dir) && (exec_first == 1)) ? 1 : ( (S_ISDIR (x->st.st_mode) || x->f.link_to_dir) ? 2 : 0) )
  50. sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
  51. { N_("&Unsorted"), unsorted },
  52. { N_("&Name"), sort_name },
  53. { N_("&Extension"), sort_ext },
  54. { N_("&Modify time"), sort_time },
  55. { N_("&Access time"), sort_atime },
  56. { N_("C&Hange time"), sort_ctime },
  57. { N_("&Size"), sort_size },
  58. { N_("&Inode"), sort_inode },
  59. };
  60. int
  61. unsorted (file_entry *a, file_entry *b)
  62. {
  63. (void) a;
  64. (void) b;
  65. return 0;
  66. }
  67. int
  68. sort_name (file_entry *a, file_entry *b)
  69. {
  70. int ad = MY_ISDIR (a);
  71. int bd = MY_ISDIR (b);
  72. if (ad == bd || mix_all_files) {
  73. /* create key if does not exist, key will be freed after sorting */
  74. if (a->sort_key == NULL)
  75. a->sort_key = str_create_key_for_filename (a->fname, case_sensitive);
  76. if (b->sort_key == NULL)
  77. b->sort_key = str_create_key_for_filename (b->fname, case_sensitive);
  78. return str_key_collate (a->sort_key, b->sort_key, case_sensitive)
  79. * reverse;
  80. }
  81. return bd - ad;
  82. }
  83. int
  84. sort_ext (file_entry *a, file_entry *b)
  85. {
  86. int r;
  87. int ad = MY_ISDIR (a);
  88. int bd = MY_ISDIR (b);
  89. if (ad == bd || mix_all_files){
  90. if (a->second_sort_key == NULL)
  91. a->second_sort_key = str_create_key (extension (a->fname), case_sensitive);
  92. if (b->second_sort_key == NULL)
  93. b->second_sort_key = str_create_key (extension (b->fname), case_sensitive);
  94. r = str_key_collate (a->second_sort_key, b->second_sort_key, case_sensitive);
  95. if (r)
  96. return r * reverse;
  97. else
  98. return sort_name (a, b);
  99. } else
  100. return bd-ad;
  101. }
  102. int
  103. sort_time (file_entry *a, file_entry *b)
  104. {
  105. int ad = MY_ISDIR (a);
  106. int bd = MY_ISDIR (b);
  107. if (ad == bd || mix_all_files) {
  108. int result = a->st.st_mtime < b->st.st_mtime ? -1 :
  109. a->st.st_mtime > b->st.st_mtime;
  110. if (result != 0)
  111. return result * reverse;
  112. else
  113. return sort_name (a, b);
  114. }
  115. else
  116. return bd-ad;
  117. }
  118. int
  119. sort_ctime (file_entry *a, file_entry *b)
  120. {
  121. int ad = MY_ISDIR (a);
  122. int bd = MY_ISDIR (b);
  123. if (ad == bd || mix_all_files) {
  124. int result = a->st.st_ctime < b->st.st_ctime ? -1 :
  125. a->st.st_ctime > b->st.st_ctime;
  126. if (result != 0)
  127. return result * reverse;
  128. else
  129. return sort_name (a, b);
  130. }
  131. else
  132. return bd-ad;
  133. }
  134. int
  135. sort_atime (file_entry *a, file_entry *b)
  136. {
  137. int ad = MY_ISDIR (a);
  138. int bd = MY_ISDIR (b);
  139. if (ad == bd || mix_all_files) {
  140. int result = a->st.st_atime < b->st.st_atime ? -1 :
  141. a->st.st_atime > b->st.st_atime;
  142. if (result != 0)
  143. return result * reverse;
  144. else
  145. return sort_name (a, b);
  146. }
  147. else
  148. return bd-ad;
  149. }
  150. int
  151. sort_inode (file_entry *a, file_entry *b)
  152. {
  153. int ad = MY_ISDIR (a);
  154. int bd = MY_ISDIR (b);
  155. if (ad == bd || mix_all_files)
  156. return (a->st.st_ino - b->st.st_ino) * reverse;
  157. else
  158. return bd-ad;
  159. }
  160. int
  161. sort_size (file_entry *a, file_entry *b)
  162. {
  163. int ad = MY_ISDIR (a);
  164. int bd = MY_ISDIR (b);
  165. int result = 0;
  166. if (ad != bd && !mix_all_files)
  167. return bd - ad;
  168. result = a->st.st_size < b->st.st_size ? -1 :
  169. a->st.st_size > b->st.st_size;
  170. if (result != 0)
  171. return result * reverse;
  172. else
  173. return sort_name (a, b);
  174. }
  175. /* clear keys, should be call after sorting is finished */
  176. static void
  177. clean_sort_keys (dir_list *list, int start, int count)
  178. {
  179. int i;
  180. for (i = 0; i < count; i++){
  181. str_release_key (list->list [i + start].sort_key, case_sensitive);
  182. list->list [i + start].sort_key = NULL;
  183. str_release_key (list->list [i + start].second_sort_key, case_sensitive);
  184. list->list [i + start].second_sort_key = NULL;
  185. }
  186. }
  187. void
  188. do_sort (dir_list *list, sortfn *sort, int top, int reverse_f, int case_sensitive_f, int exec_first_f)
  189. {
  190. int dot_dot_found = 0;
  191. if (top == 0)
  192. return;
  193. /* If there is an ".." entry the caller must take care to
  194. ensure that it occupies the first list element. */
  195. if (!strcmp (list->list [0].fname, ".."))
  196. dot_dot_found = 1;
  197. reverse = reverse_f ? -1 : 1;
  198. case_sensitive = case_sensitive_f;
  199. exec_first = exec_first_f;
  200. qsort (&(list->list) [dot_dot_found],
  201. top + 1 - dot_dot_found, sizeof (file_entry), sort);
  202. clean_sort_keys (list, dot_dot_found, top + 1 - dot_dot_found);
  203. }
  204. void
  205. clean_dir (dir_list *list, int count)
  206. {
  207. int i;
  208. for (i = 0; i < count; i++){
  209. g_free (list->list [i].fname);
  210. list->list [i].fname = NULL;
  211. }
  212. }
  213. static int
  214. add_dotdot_to_list (dir_list *list, int index)
  215. {
  216. /* Need to grow the *list? */
  217. if (index == list->size) {
  218. list->list = g_realloc (list->list, sizeof (file_entry) *
  219. (list->size + RESIZE_STEPS));
  220. if (!list->list)
  221. return 0;
  222. list->size += RESIZE_STEPS;
  223. }
  224. memset (&(list->list) [index], 0, sizeof(file_entry));
  225. (list->list) [index].fnamelen = 2;
  226. (list->list) [index].fname = g_strdup ("..");
  227. (list->list) [index].f.link_to_dir = 0;
  228. (list->list) [index].f.stale_link = 0;
  229. (list->list) [index].f.dir_size_computed = 0;
  230. (list->list) [index].f.marked = 0;
  231. (list->list) [index].st.st_mode = 040755;
  232. return 1;
  233. }
  234. /* Used to set up a directory list when there is no access to a directory */
  235. int
  236. set_zero_dir (dir_list *list)
  237. {
  238. return (add_dotdot_to_list (list, 0));
  239. }
  240. /* If you change handle_dirent then check also handle_path. */
  241. /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
  242. static int
  243. handle_dirent (dir_list *list, const char *filter, struct dirent *dp,
  244. struct stat *buf1, int next_free, int *link_to_dir,
  245. int *stale_link)
  246. {
  247. if (dp->d_name[0] == '.' && dp->d_name[1] == 0)
  248. return 0;
  249. if (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == 0)
  250. return 0;
  251. if (!show_dot_files && (dp->d_name[0] == '.'))
  252. return 0;
  253. if (!show_backups && dp->d_name[NLENGTH (dp) - 1] == '~')
  254. return 0;
  255. if (mc_lstat (dp->d_name, buf1) == -1) {
  256. /*
  257. * lstat() fails - such entries should be identified by
  258. * buf1->st_mode being 0.
  259. * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
  260. */
  261. memset (buf1, 0, sizeof (*buf1));
  262. }
  263. if (S_ISDIR (buf1->st_mode))
  264. tree_store_mark_checked (dp->d_name);
  265. /* A link to a file or a directory? */
  266. *link_to_dir = 0;
  267. *stale_link = 0;
  268. if (S_ISLNK (buf1->st_mode)) {
  269. struct stat buf2;
  270. if (!mc_stat (dp->d_name, &buf2))
  271. *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
  272. else
  273. *stale_link = 1;
  274. }
  275. if (!(S_ISDIR (buf1->st_mode) || *link_to_dir) && filter
  276. && !mc_search(filter, dp->d_name, MC_SEARCH_T_GLOB) )
  277. return 0;
  278. /* Need to grow the *list? */
  279. if (next_free == list->size) {
  280. list->list =
  281. g_realloc (list->list,
  282. sizeof (file_entry) * (list->size + RESIZE_STEPS));
  283. if (!list->list)
  284. return -1;
  285. list->size += RESIZE_STEPS;
  286. }
  287. return 1;
  288. }
  289. /* handle_path is a simplified handle_dirent. The difference is that
  290. handle_path doesn't pay attention to show_dot_files and show_backups.
  291. Moreover handle_path can't be used with a filemask.
  292. If you change handle_path then check also handle_dirent. */
  293. /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
  294. int
  295. handle_path (dir_list *list, const char *path,
  296. struct stat *buf1, int next_free, int *link_to_dir,
  297. int *stale_link)
  298. {
  299. if (path [0] == '.' && path [1] == 0)
  300. return 0;
  301. if (path [0] == '.' && path [1] == '.' && path [2] == 0)
  302. return 0;
  303. if (mc_lstat (path, buf1) == -1)
  304. return 0;
  305. if (S_ISDIR (buf1->st_mode))
  306. tree_store_mark_checked (path);
  307. /* A link to a file or a directory? */
  308. *link_to_dir = 0;
  309. *stale_link = 0;
  310. if (S_ISLNK(buf1->st_mode)){
  311. struct stat buf2;
  312. if (!mc_stat (path, &buf2))
  313. *link_to_dir = S_ISDIR(buf2.st_mode) != 0;
  314. else
  315. *stale_link = 1;
  316. }
  317. /* Need to grow the *list? */
  318. if (next_free == list->size){
  319. list->list = g_realloc (list->list, sizeof (file_entry) *
  320. (list->size + RESIZE_STEPS));
  321. if (!list->list)
  322. return -1;
  323. list->size += RESIZE_STEPS;
  324. }
  325. return 1;
  326. }
  327. int
  328. do_load_dir (const char *path, dir_list *list, sortfn *sort, int reverse,
  329. int case_sensitive, int exec_ff, const char *filter)
  330. {
  331. DIR *dirp;
  332. struct dirent *dp;
  333. int status, link_to_dir, stale_link;
  334. int next_free = 0;
  335. struct stat st;
  336. /* ".." (if any) must be the first entry in the list */
  337. if (set_zero_dir (list) == 0)
  338. return next_free;
  339. next_free++;
  340. dirp = mc_opendir (path);
  341. if (!dirp) {
  342. message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
  343. return next_free;
  344. }
  345. tree_store_start_check (path);
  346. /* Do not add a ".." entry to the root directory */
  347. if (!strcmp (path, "/"))
  348. next_free--;
  349. while ((dp = mc_readdir (dirp))) {
  350. status =
  351. handle_dirent (list, filter, dp, &st, next_free, &link_to_dir,
  352. &stale_link);
  353. if (status == 0)
  354. continue;
  355. if (status == -1) {
  356. tree_store_end_check ();
  357. mc_closedir (dirp);
  358. return next_free;
  359. }
  360. list->list[next_free].fnamelen = NLENGTH (dp);
  361. list->list[next_free].fname = g_strdup (dp->d_name);
  362. list->list[next_free].f.marked = 0;
  363. list->list[next_free].f.link_to_dir = link_to_dir;
  364. list->list[next_free].f.stale_link = stale_link;
  365. list->list[next_free].f.dir_size_computed = 0;
  366. list->list[next_free].st = st;
  367. list->list[next_free].sort_key = NULL;
  368. list->list[next_free].second_sort_key = NULL;
  369. next_free++;
  370. if (!(next_free % 32))
  371. rotate_dash ();
  372. }
  373. if (next_free) {
  374. do_sort (list, sort, next_free - 1, reverse, case_sensitive, exec_ff);
  375. }
  376. mc_closedir (dirp);
  377. tree_store_end_check ();
  378. return next_free;
  379. }
  380. int
  381. link_isdir (const file_entry *file)
  382. {
  383. if (file->f.link_to_dir)
  384. return 1;
  385. else
  386. return 0;
  387. }
  388. int
  389. if_link_is_exe (const char *full_name, const file_entry *file)
  390. {
  391. struct stat b;
  392. if (S_ISLNK (file->st.st_mode) && !mc_stat (full_name, &b)) {
  393. return is_exe (b.st_mode);
  394. } else
  395. return 1;
  396. }
  397. static dir_list dir_copy = { 0, 0 };
  398. static void
  399. alloc_dir_copy (int size)
  400. {
  401. int i;
  402. if (dir_copy.size < size){
  403. if (dir_copy.list){
  404. for (i = 0; i < dir_copy.size; i++) {
  405. g_free (dir_copy.list [i].fname);
  406. }
  407. g_free (dir_copy.list);
  408. dir_copy.list = 0;
  409. }
  410. dir_copy.list = g_new (file_entry, size);
  411. for (i = 0; i < size; i++) {
  412. dir_copy.list [i].fname = NULL;
  413. dir_copy.list [i].sort_key = NULL;
  414. dir_copy.list [i].second_sort_key = NULL;
  415. }
  416. dir_copy.size = size;
  417. }
  418. }
  419. /* If filter is null, then it is a match */
  420. int
  421. do_reload_dir (const char *path, dir_list *list, sortfn *sort, int count,
  422. int rev, int case_sensitive, int exec_ff, const char *filter)
  423. {
  424. DIR *dirp;
  425. struct dirent *dp;
  426. int next_free = 0;
  427. int i, status, link_to_dir, stale_link;
  428. struct stat st;
  429. int marked_cnt;
  430. GHashTable *marked_files;
  431. dirp = mc_opendir (path);
  432. if (!dirp) {
  433. message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
  434. clean_dir (list, count);
  435. return set_zero_dir (list);
  436. }
  437. tree_store_start_check (path);
  438. marked_files = g_hash_table_new (g_str_hash, g_str_equal);
  439. alloc_dir_copy (list->size);
  440. for (marked_cnt = i = 0; i < count; i++) {
  441. dir_copy.list[i].fnamelen = list->list[i].fnamelen;
  442. dir_copy.list[i].fname = list->list[i].fname;
  443. dir_copy.list[i].f.marked = list->list[i].f.marked;
  444. dir_copy.list[i].f.dir_size_computed =
  445. list->list[i].f.dir_size_computed;
  446. dir_copy.list[i].f.link_to_dir = list->list[i].f.link_to_dir;
  447. dir_copy.list[i].f.stale_link = list->list[i].f.stale_link;
  448. dir_copy.list[i].sort_key = NULL;
  449. dir_copy.list[i].second_sort_key = NULL;
  450. if (list->list[i].f.marked) {
  451. g_hash_table_insert (marked_files, dir_copy.list[i].fname,
  452. &dir_copy.list[i]);
  453. marked_cnt++;
  454. }
  455. }
  456. /* Add ".." except to the root directory. The ".." entry
  457. (if any) must be the first in the list. */
  458. if (strcmp (path, "/") != 0) {
  459. if (set_zero_dir (list) == 0) {
  460. clean_dir (list, count);
  461. clean_dir (&dir_copy, count);
  462. return next_free;
  463. }
  464. next_free++;
  465. }
  466. while ((dp = mc_readdir (dirp))) {
  467. status =
  468. handle_dirent (list, filter, dp, &st, next_free, &link_to_dir,
  469. &stale_link);
  470. if (status == 0)
  471. continue;
  472. if (status == -1) {
  473. mc_closedir (dirp);
  474. /* Norbert (Feb 12, 1997):
  475. Just in case someone finds this memory leak:
  476. -1 means big trouble (at the moment no memory left),
  477. I don't bother with further cleanup because if one gets to
  478. this point he will have more problems than a few memory
  479. leaks and because one 'clean_dir' would not be enough (and
  480. because I don't want to spent the time to make it working,
  481. IMHO it's not worthwhile).
  482. clean_dir (&dir_copy, count);
  483. */
  484. tree_store_end_check ();
  485. g_hash_table_destroy (marked_files);
  486. return next_free;
  487. }
  488. list->list[next_free].f.marked = 0;
  489. /*
  490. * If we have marked files in the copy, scan through the copy
  491. * to find matching file. Decrease number of remaining marks if
  492. * we copied one.
  493. */
  494. if (marked_cnt > 0) {
  495. if ((g_hash_table_lookup (marked_files, dp->d_name))) {
  496. list->list[next_free].f.marked = 1;
  497. marked_cnt--;
  498. }
  499. }
  500. list->list[next_free].fnamelen = NLENGTH (dp);
  501. list->list[next_free].fname = g_strdup (dp->d_name);
  502. list->list[next_free].f.link_to_dir = link_to_dir;
  503. list->list[next_free].f.stale_link = stale_link;
  504. list->list[next_free].f.dir_size_computed = 0;
  505. list->list[next_free].st = st;
  506. list->list[next_free].sort_key = NULL;
  507. list->list[next_free].second_sort_key = NULL;
  508. next_free++;
  509. if (!(next_free % 16))
  510. rotate_dash ();
  511. }
  512. mc_closedir (dirp);
  513. tree_store_end_check ();
  514. g_hash_table_destroy (marked_files);
  515. if (next_free) {
  516. do_sort (list, sort, next_free - 1, rev, case_sensitive, exec_ff);
  517. }
  518. clean_dir (&dir_copy, count);
  519. return next_free;
  520. }