Reverted the use of bool in favour of gboolean
[midnight-commander.git] / src / dir.c
blobbf732bab577fb7dba98c41b29fa39e0ff334a097
1 /* Directory routines
2 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
27 #include <mhl/memory.h>
28 #include <mhl/string.h>
30 #include "global.h"
31 #include "tty.h"
32 #include "dir.h"
33 #include "wtools.h"
34 #include "treestore.h"
36 /* If true show files starting with a dot */
37 int show_dot_files = 1;
39 /* If true show files ending in ~ */
40 int show_backups = 1;
42 /* If false then directories are shown separately from files */
43 int mix_all_files = 0;
45 /* Reverse flag */
46 static int reverse = 1;
48 /* Are the files sorted case sensitively? */
49 static int case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
51 /* Are the exec_bit files top in list*/
52 static int exec_first = 1;
54 #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) )
56 sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
57 { N_("&Unsorted"), unsorted },
58 { N_("&Name"), sort_name },
59 { N_("&Extension"), sort_ext },
60 { N_("&Modify time"), sort_time },
61 { N_("&Access time"), sort_atime },
62 { N_("C&Hange time"), sort_ctime },
63 { N_("&Size"), sort_size },
64 { N_("&Inode"), sort_inode },
67 #ifdef HAVE_STRCOLL
69 * g_strcasecmp() doesn't work well in some locales because it relies on
70 * the locale-specific toupper(). On the other hand, strcoll() is case
71 * sensitive in the "C" and "POSIX" locales, unlike other locales.
72 * Solution: always use strcmp() for case sensitive sort. For case
73 * insensitive sort use strcoll() if it's case insensitive for ASCII and
74 * g_strcasecmp() otherwise.
76 typedef enum {
77 STRCOLL_NO,
78 STRCOLL_YES,
79 STRCOLL_TEST
80 } strcoll_status;
82 static int string_sortcomp (const char *str1, const char *str2)
84 static strcoll_status use_strcoll = STRCOLL_TEST;
86 if (case_sensitive) {
87 return strcmp (str1, str2);
90 /* Initialize use_strcoll once. */
91 if (use_strcoll == STRCOLL_TEST) {
92 /* Only use strcoll() if it considers "B" between "a" and "c". */
93 if (strcoll ("a", "B") * strcoll ("B", "c") > 0) {
94 use_strcoll = STRCOLL_YES;
95 } else {
96 use_strcoll = STRCOLL_NO;
100 if (use_strcoll == STRCOLL_NO)
101 return g_strcasecmp (str1, str2);
102 else
103 return strcoll (str1, str2);
105 #else
106 #define string_sortcomp(a,b) (case_sensitive ? strcmp (a,b) : g_strcasecmp (a,b))
107 #endif
110 unsorted (const file_entry *a, const file_entry *b)
112 (void) a;
113 (void) b;
114 return 0;
118 sort_name (const file_entry *a, const file_entry *b)
120 int ad = MY_ISDIR (a);
121 int bd = MY_ISDIR (b);
123 if (ad == bd || mix_all_files)
124 return string_sortcomp (a->fname, b->fname) * reverse;
125 return bd-ad;
129 sort_ext (const file_entry *a, const file_entry *b)
131 const char *exta, *extb;
132 int r;
133 int ad = MY_ISDIR (a);
134 int bd = MY_ISDIR (b);
136 if (ad == bd || mix_all_files){
137 exta = extension (a->fname);
138 extb = extension (b->fname);
139 r = string_sortcomp (exta, extb);
140 if (r)
141 return r * reverse;
142 else
143 return sort_name (a, b);
144 } else
145 return bd-ad;
149 sort_time (const file_entry *a, const file_entry *b)
151 int ad = MY_ISDIR (a);
152 int bd = MY_ISDIR (b);
154 if (ad == bd || mix_all_files) {
155 int result = a->st.st_mtime < b->st.st_mtime ? -1 :
156 a->st.st_mtime > b->st.st_mtime;
157 if (result != 0)
158 return result * reverse;
159 else
160 return sort_name (a, b);
162 else
163 return bd-ad;
167 sort_ctime (const file_entry *a, const file_entry *b)
169 int ad = MY_ISDIR (a);
170 int bd = MY_ISDIR (b);
172 if (ad == bd || mix_all_files) {
173 int result = a->st.st_ctime < b->st.st_ctime ? -1 :
174 a->st.st_ctime > b->st.st_ctime;
175 if (result != 0)
176 return result * reverse;
177 else
178 return sort_name (a, b);
180 else
181 return bd-ad;
185 sort_atime (const file_entry *a, const file_entry *b)
187 int ad = MY_ISDIR (a);
188 int bd = MY_ISDIR (b);
190 if (ad == bd || mix_all_files) {
191 int result = a->st.st_atime < b->st.st_atime ? -1 :
192 a->st.st_atime > b->st.st_atime;
193 if (result != 0)
194 return result * reverse;
195 else
196 return sort_name (a, b);
198 else
199 return bd-ad;
203 sort_inode (const file_entry *a, const file_entry *b)
205 int ad = MY_ISDIR (a);
206 int bd = MY_ISDIR (b);
208 if (ad == bd || mix_all_files)
209 return (a->st.st_ino - b->st.st_ino) * reverse;
210 else
211 return bd-ad;
215 sort_size (const file_entry *a, const file_entry *b)
217 int ad = MY_ISDIR (a);
218 int bd = MY_ISDIR (b);
219 int result = 0;
221 if (ad != bd && !mix_all_files)
222 return bd - ad;
224 result = a->st.st_size < b->st.st_size ? -1 :
225 a->st.st_size > b->st.st_size;
226 if (result != 0)
227 return result * reverse;
228 else
229 return sort_name (a, b);
233 void
234 do_sort (dir_list *list, sortfn *sort, int top, int reverse_f, int case_sensitive_f, int exec_first_f)
236 int dot_dot_found = 0;
238 if (top == 0)
239 return;
241 /* If there is an ".." entry the caller must take care to
242 ensure that it occupies the first list element. */
243 if (!strcmp (list->list [0].fname, ".."))
244 dot_dot_found = 1;
246 reverse = reverse_f ? -1 : 1;
247 case_sensitive = case_sensitive_f;
248 exec_first = exec_first_f;
249 qsort (&(list->list) [dot_dot_found],
250 top + 1 - dot_dot_found, sizeof (file_entry), sort);
253 void
254 clean_dir (dir_list *list, int count)
256 int i;
258 for (i = 0; i < count; i++){
259 g_free (list->list [i].fname);
260 list->list [i].fname = 0;
264 static int
265 add_dotdot_to_list (dir_list *list, int index)
267 /* Need to grow the *list? */
268 if (index == list->size) {
269 list->list = g_realloc (list->list, sizeof (file_entry) *
270 (list->size + RESIZE_STEPS));
271 if (!list->list)
272 return 0;
273 list->size += RESIZE_STEPS;
276 memset (&(list->list) [index], 0, sizeof(file_entry));
277 (list->list) [index].fnamelen = 2;
278 (list->list) [index].fname = g_strdup ("..");
279 (list->list) [index].f.link_to_dir = 0;
280 (list->list) [index].f.stale_link = 0;
281 (list->list) [index].f.dir_size_computed = 0;
282 (list->list) [index].f.marked = 0;
283 (list->list) [index].st.st_mode = 040755;
284 return 1;
287 /* Used to set up a directory list when there is no access to a directory */
289 set_zero_dir (dir_list *list)
291 return (add_dotdot_to_list (list, 0));
294 /* If you change handle_dirent then check also handle_path. */
295 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
296 static int
297 handle_dirent (dir_list *list, const char *filter, struct dirent *dp,
298 struct stat *buf1, int next_free, int *link_to_dir,
299 int *stale_link)
301 if (dp->d_name[0] == '.' && dp->d_name[1] == 0)
302 return 0;
303 if (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == 0)
304 return 0;
305 if (!show_dot_files && (dp->d_name[0] == '.'))
306 return 0;
307 if (!show_backups && dp->d_name[NLENGTH (dp) - 1] == '~')
308 return 0;
309 if (mc_lstat (dp->d_name, buf1) == -1) {
311 * lstat() fails - such entries should be identified by
312 * buf1->st_mode being 0.
313 * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
315 memset (buf1, 0, sizeof (*buf1));
318 if (S_ISDIR (buf1->st_mode))
319 tree_store_mark_checked (dp->d_name);
321 /* A link to a file or a directory? */
322 *link_to_dir = 0;
323 *stale_link = 0;
324 if (S_ISLNK (buf1->st_mode)) {
325 struct stat buf2;
326 if (!mc_stat (dp->d_name, &buf2))
327 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
328 else
329 *stale_link = 1;
331 if (!(S_ISDIR (buf1->st_mode) || *link_to_dir) && filter
332 && !regexp_match (filter, dp->d_name, match_file))
333 return 0;
335 /* Need to grow the *list? */
336 if (next_free == list->size) {
337 list->list =
338 g_realloc (list->list,
339 sizeof (file_entry) * (list->size + RESIZE_STEPS));
340 if (!list->list)
341 return -1;
342 list->size += RESIZE_STEPS;
344 return 1;
347 /* handle_path is a simplified handle_dirent. The difference is that
348 handle_path doesn't pay attention to show_dot_files and show_backups.
349 Moreover handle_path can't be used with a filemask.
350 If you change handle_path then check also handle_dirent. */
351 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
353 handle_path (dir_list *list, const char *path,
354 struct stat *buf1, int next_free, int *link_to_dir,
355 int *stale_link)
357 if (path [0] == '.' && path [1] == 0)
358 return 0;
359 if (path [0] == '.' && path [1] == '.' && path [2] == 0)
360 return 0;
361 if (mc_lstat (path, buf1) == -1)
362 return 0;
364 if (S_ISDIR (buf1->st_mode))
365 tree_store_mark_checked (path);
367 /* A link to a file or a directory? */
368 *link_to_dir = 0;
369 *stale_link = 0;
370 if (S_ISLNK(buf1->st_mode)){
371 struct stat buf2;
372 if (!mc_stat (path, &buf2))
373 *link_to_dir = S_ISDIR(buf2.st_mode) != 0;
374 else
375 *stale_link = 1;
378 /* Need to grow the *list? */
379 if (next_free == list->size){
380 list->list = g_realloc (list->list, sizeof (file_entry) *
381 (list->size + RESIZE_STEPS));
382 if (!list->list)
383 return -1;
384 list->size += RESIZE_STEPS;
386 return 1;
390 do_load_dir (const char *path, dir_list *list, sortfn *sort, int reverse,
391 int case_sensitive, int exec_ff, const char *filter)
393 DIR *dirp;
394 struct dirent *dp;
395 int status, link_to_dir, stale_link;
396 int next_free = 0;
397 struct stat st;
399 /* ".." (if any) must be the first entry in the list */
400 if (set_zero_dir (list) == 0)
401 return next_free;
402 next_free++;
404 dirp = mc_opendir (path);
405 if (!dirp) {
406 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
407 return next_free;
409 tree_store_start_check (path);
410 /* Do not add a ".." entry to the root directory */
411 if (!strcmp (path, "/"))
412 next_free--;
413 while ((dp = mc_readdir (dirp))) {
414 status =
415 handle_dirent (list, filter, dp, &st, next_free, &link_to_dir,
416 &stale_link);
417 if (status == 0)
418 continue;
419 if (status == -1) {
420 tree_store_end_check ();
421 mc_closedir (dirp);
422 return next_free;
424 list->list[next_free].fnamelen = NLENGTH (dp);
425 list->list[next_free].fname = g_strdup (dp->d_name);
426 list->list[next_free].f.marked = 0;
427 list->list[next_free].f.link_to_dir = link_to_dir;
428 list->list[next_free].f.stale_link = stale_link;
429 list->list[next_free].f.dir_size_computed = 0;
430 list->list[next_free].st = st;
431 next_free++;
432 if (!(next_free % 32))
433 rotate_dash ();
436 if (next_free) {
437 do_sort (list, sort, next_free - 1, reverse, case_sensitive, exec_ff);
440 mc_closedir (dirp);
441 tree_store_end_check ();
442 return next_free;
446 link_isdir (const file_entry *file)
448 if (file->f.link_to_dir)
449 return 1;
450 else
451 return 0;
455 if_link_is_exe (const char *full_name, const file_entry *file)
457 struct stat b;
459 if (S_ISLNK (file->st.st_mode) && !mc_stat (full_name, &b)) {
460 return is_exe (b.st_mode);
461 } else
462 return 1;
465 static dir_list dir_copy = { 0, 0 };
467 static void
468 alloc_dir_copy (int size)
470 int i;
472 if (dir_copy.size < size){
473 if (dir_copy.list){
475 for (i = 0; i < dir_copy.size; i++) {
476 g_free (dir_copy.list [i].fname);
478 g_free (dir_copy.list);
479 dir_copy.list = 0;
482 dir_copy.list = g_new (file_entry, size);
483 for (i = 0; i < size; i++)
484 dir_copy.list [i].fname = 0;
486 dir_copy.size = size;
490 /* If filter is null, then it is a match */
492 do_reload_dir (const char *path, dir_list *list, sortfn *sort, int count,
493 int rev, int case_sensitive, int exec_ff, const char *filter)
495 DIR *dirp;
496 struct dirent *dp;
497 int next_free = 0;
498 int i, status, link_to_dir, stale_link;
499 struct stat st;
500 int marked_cnt;
501 GHashTable *marked_files;
503 dirp = mc_opendir (path);
504 if (!dirp) {
505 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
506 clean_dir (list, count);
507 return set_zero_dir (list);
510 tree_store_start_check (path);
511 marked_files = g_hash_table_new (g_str_hash, g_str_equal);
512 alloc_dir_copy (list->size);
513 for (marked_cnt = i = 0; i < count; i++) {
514 dir_copy.list[i].fnamelen = list->list[i].fnamelen;
515 dir_copy.list[i].fname = list->list[i].fname;
516 dir_copy.list[i].f.marked = list->list[i].f.marked;
517 dir_copy.list[i].f.dir_size_computed =
518 list->list[i].f.dir_size_computed;
519 dir_copy.list[i].f.link_to_dir = list->list[i].f.link_to_dir;
520 dir_copy.list[i].f.stale_link = list->list[i].f.stale_link;
521 if (list->list[i].f.marked) {
522 g_hash_table_insert (marked_files, dir_copy.list[i].fname,
523 &dir_copy.list[i]);
524 marked_cnt++;
528 /* Add ".." except to the root directory. The ".." entry
529 (if any) must be the first in the list. */
530 if (strcmp (path, "/") != 0) {
531 if (set_zero_dir (list) == 0) {
532 clean_dir (list, count);
533 clean_dir (&dir_copy, count);
534 return next_free;
536 next_free++;
539 while ((dp = mc_readdir (dirp))) {
540 status =
541 handle_dirent (list, filter, dp, &st, next_free, &link_to_dir,
542 &stale_link);
543 if (status == 0)
544 continue;
545 if (status == -1) {
546 mc_closedir (dirp);
547 /* Norbert (Feb 12, 1997):
548 Just in case someone finds this memory leak:
549 -1 means big trouble (at the moment no memory left),
550 I don't bother with further cleanup because if one gets to
551 this point he will have more problems than a few memory
552 leaks and because one 'clean_dir' would not be enough (and
553 because I don't want to spent the time to make it working,
554 IMHO it's not worthwhile).
555 clean_dir (&dir_copy, count);
557 tree_store_end_check ();
558 g_hash_table_destroy (marked_files);
559 return next_free;
562 list->list[next_free].f.marked = 0;
565 * If we have marked files in the copy, scan through the copy
566 * to find matching file. Decrease number of remaining marks if
567 * we copied one.
569 if (marked_cnt > 0) {
570 if ((g_hash_table_lookup (marked_files, dp->d_name))) {
571 list->list[next_free].f.marked = 1;
572 marked_cnt--;
576 list->list[next_free].fnamelen = NLENGTH (dp);
577 list->list[next_free].fname = g_strdup (dp->d_name);
578 list->list[next_free].f.link_to_dir = link_to_dir;
579 list->list[next_free].f.stale_link = stale_link;
580 list->list[next_free].f.dir_size_computed = 0;
581 list->list[next_free].st = st;
582 next_free++;
583 if (!(next_free % 16))
584 rotate_dash ();
586 mc_closedir (dirp);
587 tree_store_end_check ();
588 g_hash_table_destroy (marked_files);
589 if (next_free) {
590 do_sort (list, sort, next_free - 1, rev, case_sensitive, exec_ff);
592 clean_dir (&dir_copy, count);
593 return next_free;