Just a little correction at the it.po file.
[midnight-commander.git] / src / dir.c
blob0b8f2c7a018229c8d8acfa9547b5fc595b5d3244
1 /* Directory routines
2 Copyright (C) 1994 Miguel de Icaza.
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.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 #include <config.h>
19 #define DIR_H_INCLUDE_HANDLE_DIRENT
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <sys/stat.h>
26 #include "global.h"
27 #include "tty.h"
28 #include "dir.h"
29 #include "wtools.h"
30 #include "tree.h"
32 /* If true show files starting with a dot */
33 int show_dot_files = 1;
35 /* If true show files ending in ~ */
36 int show_backups = 1;
38 /* If false then directories are shown separately from files */
39 int mix_all_files = 0;
41 /* Reverse flag */
42 static int reverse = 1;
44 /* Are the files sorted case sensitively? */
45 static int case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
47 #define MY_ISDIR(x) ( (S_ISDIR (x->buf.st_mode) || x->f.link_to_dir) ? 1 : 0)
49 sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
50 { N_("&Unsorted"), unsorted },
51 { N_("&Name"), sort_name },
52 { N_("&Extension"), sort_ext },
53 { N_("&Modify time"), sort_time },
54 { N_("&Access time"), sort_atime },
55 { N_("&Change time"), sort_ctime },
56 { N_("&Size"), sort_size },
57 { N_("&Inode"), sort_inode },
59 /* New sort orders */
60 { N_("&Type"), sort_type },
61 { N_("&Links"), sort_links },
62 { N_("N&GID"), sort_ngid },
63 { N_("N&UID"), sort_nuid },
64 { N_("&Owner"), sort_owner },
65 { N_("&Group"), sort_group }
68 #ifdef HAVE_STRCOLL
70 * g_strcasecmp() doesn't work well in some locales because it relies on
71 * the locale-specific toupper(). On the other hand, strcoll() is case
72 * sensitive in the "C" and "POSIX" locales, unlike other locales.
73 * Solution: always use strcmp() for case sensitive sort. For case
74 * insensitive sort use strcoll() if it's case insensitive for ASCII and
75 * g_strcasecmp() otherwise.
77 typedef enum {
78 STRCOLL_NO,
79 STRCOLL_YES,
80 STRCOLL_TEST
81 } strcoll_status;
83 static int string_sortcomp (char *str1, char *str2)
85 static strcoll_status use_strcoll = STRCOLL_TEST;
87 if (case_sensitive) {
88 return strcmp (str1, str2);
91 /* Initialize use_strcoll once. */
92 if (use_strcoll == STRCOLL_TEST) {
93 /* Only use strcoll() if it considers "B" between "a" and "c". */
94 if (strcoll ("a", "B") * strcoll ("B", "c") > 0) {
95 use_strcoll = STRCOLL_YES;
96 } else {
97 use_strcoll = STRCOLL_NO;
101 if (use_strcoll == STRCOLL_NO)
102 return g_strcasecmp (str1, str2);
103 else
104 return strcoll (str1, str2);
106 #else
107 #define string_sortcomp(a,b) (case_sensitive ? strcmp (a,b) : g_strcasecmp (a,b))
108 #endif
111 unsorted (const file_entry *a, const file_entry *b)
113 return 0;
117 sort_name (const file_entry *a, const file_entry *b)
119 int ad = MY_ISDIR (a);
120 int bd = MY_ISDIR (b);
122 if (ad == bd || mix_all_files)
123 return string_sortcomp (a->fname, b->fname) * reverse;
124 return bd-ad;
128 sort_ext (const file_entry *a, const file_entry *b)
130 char *exta, *extb;
131 int r;
132 int ad = MY_ISDIR (a);
133 int bd = MY_ISDIR (b);
135 if (ad == bd || mix_all_files){
136 exta = extension (a->fname);
137 extb = extension (b->fname);
138 r = string_sortcomp (exta, extb);
139 if (r)
140 return r * reverse;
141 else
142 return sort_name (a, b);
143 } else
144 return bd-ad;
148 sort_owner (const file_entry *a, const file_entry *b)
150 int ad = MY_ISDIR (a);
151 int bd = MY_ISDIR (b);
153 if (ad == bd || mix_all_files)
154 return string_sortcomp (get_owner (a->buf.st_uid), get_owner (a->buf.st_uid)) * reverse;
155 return bd-ad;
159 sort_group (const file_entry *a, const file_entry *b)
161 int ad = MY_ISDIR (a);
162 int bd = MY_ISDIR (b);
164 if (ad == bd || mix_all_files)
165 return string_sortcomp (get_group (a->buf.st_gid), get_group (a->buf.st_gid)) * reverse;
166 return bd-ad;
170 sort_time (const file_entry *a, const file_entry *b)
172 int ad = MY_ISDIR (a);
173 int bd = MY_ISDIR (b);
175 if (ad == bd || mix_all_files)
176 return (a->buf.st_mtime - b->buf.st_mtime) * reverse;
177 else
178 return bd-ad;
182 sort_ctime (const file_entry *a, const file_entry *b)
184 int ad = MY_ISDIR (a);
185 int bd = MY_ISDIR (b);
187 if (ad == bd || mix_all_files)
188 return (a->buf.st_ctime - b->buf.st_ctime) * reverse;
189 else
190 return bd-ad;
194 sort_atime (const file_entry *a, const file_entry *b)
196 int ad = MY_ISDIR (a);
197 int bd = MY_ISDIR (b);
199 if (ad == bd || mix_all_files)
200 return (a->buf.st_atime - b->buf.st_atime) * reverse;
201 else
202 return bd-ad;
206 sort_inode (const file_entry *a, const file_entry *b)
208 int ad = MY_ISDIR (a);
209 int bd = MY_ISDIR (b);
211 if (ad == bd || mix_all_files)
212 return (a->buf.st_ino - b->buf.st_ino) * reverse;
213 else
214 return bd-ad;
218 sort_size (const file_entry *a, const file_entry *b)
220 int ad = MY_ISDIR (a);
221 int bd = MY_ISDIR (b);
223 if (ad != bd && !mix_all_files)
224 return bd - ad;
226 return (2 * (b->buf.st_size > a->buf.st_size) - 1) * reverse;
230 sort_links (const file_entry *a, const file_entry *b)
232 int ad = MY_ISDIR (a);
233 int bd = MY_ISDIR (b);
235 if (ad == bd || mix_all_files)
236 return (b->buf.st_nlink - a->buf.st_nlink) * reverse;
237 else
238 return bd-ad;
242 sort_ngid (const file_entry *a, const file_entry *b)
244 int ad = MY_ISDIR (a);
245 int bd = MY_ISDIR (b);
247 if (ad == bd || mix_all_files)
248 return (b->buf.st_gid - a->buf.st_gid) * reverse;
249 else
250 return bd-ad;
254 sort_nuid (const file_entry *a, const file_entry *b)
256 int ad = MY_ISDIR (a);
257 int bd = MY_ISDIR (b);
259 if (ad == bd || mix_all_files)
260 return (b->buf.st_uid - a->buf.st_uid) * reverse;
261 else
262 return bd-ad;
265 inline static int
266 file_type_to_num (const file_entry *fe)
268 const struct stat *s = &fe->buf;
270 if (S_ISDIR (s->st_mode))
271 return 0;
272 if (S_ISLNK (s->st_mode)){
273 if (fe->f.link_to_dir)
274 return 1;
275 if (fe->f.stale_link)
276 return 2;
277 else
278 return 3;
280 if (S_ISSOCK (s->st_mode))
281 return 4;
282 if (S_ISCHR (s->st_mode))
283 return 5;
284 if (S_ISBLK (s->st_mode))
285 return 6;
286 if (S_ISFIFO (s->st_mode))
287 return 7;
288 if (is_exe (s->st_mode))
289 return 8;
290 return 9;
294 sort_type (const file_entry *a, const file_entry *b)
296 int aa = file_type_to_num (a);
297 int bb = file_type_to_num (b);
299 return bb-aa;
303 void
304 do_sort (dir_list *list, sortfn *sort, int top, int reverse_f, int case_sensitive_f)
306 int i;
307 int dot_dot_found = 0;
308 file_entry tmp_fe;
310 for (i = 0; i < top + 1; i++) { /* put ".." first in list */
311 if (!strcmp (list->list [i].fname, "..")) {
312 dot_dot_found = 1;
313 if (i > 0) { /* swap [i] and [0] */
314 memcpy (&tmp_fe, &(list->list [0]), sizeof (file_entry));
315 memcpy (&(list->list [0]), &(list->list [i]), sizeof (file_entry));
316 memcpy (&(list->list [i]), &tmp_fe, sizeof (file_entry));
318 break;
322 reverse = reverse_f ? -1 : 1;
323 case_sensitive = case_sensitive_f;
324 qsort (&(list->list) [dot_dot_found],
325 top + 1 - dot_dot_found, sizeof (file_entry), sort);
328 void
329 clean_dir (dir_list *list, int count)
331 int i;
333 for (i = 0; i < count; i++){
334 g_free (list->list [i].fname);
335 list->list [i].fname = 0;
339 static int
340 add_dotdot_to_list (dir_list *list, int index)
342 /* Need to grow the *list? */
343 if (index == list->size) {
344 list->list = g_realloc (list->list, sizeof (file_entry) *
345 (list->size + RESIZE_STEPS));
346 if (!list->list)
347 return 0;
348 list->size += RESIZE_STEPS;
351 memset (&(list->list) [index], 0, sizeof(file_entry));
352 (list->list) [index].fnamelen = 2;
353 (list->list) [index].fname = g_strdup ("..");
354 (list->list) [index].f.link_to_dir = 0;
355 (list->list) [index].f.stale_link = 0;
356 (list->list) [index].f.dir_size_computed = 0;
357 (list->list) [index].f.marked = 0;
358 (list->list) [index].buf.st_mode = 040755;
359 return 1;
362 /* Used to set up a directory list when there is no access to a directory */
364 set_zero_dir (dir_list *list)
366 return (add_dotdot_to_list (list, 0));
369 /* If you change handle_dirent then check also handle_path. */
370 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
372 handle_dirent (dir_list *list, char *filter, struct dirent *dp,
373 struct stat *buf1, int next_free, int *link_to_dir,
374 int *stale_link)
376 if (dp->d_name[0] == '.' && dp->d_name[1] == 0)
377 return 0;
378 if (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == 0)
379 return 0;
380 if (!show_dot_files && (dp->d_name[0] == '.'))
381 return 0;
382 if (!show_backups && dp->d_name[NLENGTH (dp) - 1] == '~')
383 return 0;
384 if (mc_lstat (dp->d_name, buf1) == -1) {
386 * lstat() fails - such entries should be identified by
387 * buf1->st_mode being 0.
388 * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
390 memset (buf1, 0, sizeof (*buf1));
393 if (S_ISDIR (buf1->st_mode))
394 tree_store_mark_checked (dp->d_name);
396 /* A link to a file or a directory? */
397 *link_to_dir = 0;
398 *stale_link = 0;
399 if (S_ISLNK (buf1->st_mode)) {
400 struct stat buf2;
401 if (!mc_stat (dp->d_name, &buf2))
402 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
403 else
404 *stale_link = 1;
406 if (!(S_ISDIR (buf1->st_mode) || *link_to_dir) && filter
407 && !regexp_match (filter, dp->d_name, match_file))
408 return 0;
410 /* Need to grow the *list? */
411 if (next_free == list->size) {
412 list->list =
413 g_realloc (list->list,
414 sizeof (file_entry) * (list->size + RESIZE_STEPS));
415 if (!list->list)
416 return -1;
417 list->size += RESIZE_STEPS;
419 return 1;
422 /* handle_path is a simplified handle_dirent. The difference is that
423 handle_path doesn't pay attention to show_dot_files and show_backups.
424 Moreover handle_path can't be used with a filemask.
425 If you change handle_path then check also handle_dirent. */
426 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
428 handle_path (dir_list *list, char *path,
429 struct stat *buf1, int next_free, int *link_to_dir,
430 int *stale_link)
432 if (path [0] == '.' && path [1] == 0)
433 return 0;
434 if (path [0] == '.' && path [1] == '.' && path [2] == 0)
435 return 0;
436 if (mc_lstat (path, buf1) == -1)
437 return 0;
439 if (S_ISDIR (buf1->st_mode))
440 tree_store_mark_checked (path);
442 /* A link to a file or a directory? */
443 *link_to_dir = 0;
444 *stale_link = 0;
445 if (S_ISLNK(buf1->st_mode)){
446 struct stat buf2;
447 if (!mc_stat (path, &buf2))
448 *link_to_dir = S_ISDIR(buf2.st_mode) != 0;
449 else
450 *stale_link = 1;
453 /* Need to grow the *list? */
454 if (next_free == list->size){
455 list->list = g_realloc (list->list, sizeof (file_entry) *
456 (list->size + RESIZE_STEPS));
457 if (!list->list)
458 return -1;
459 list->size += RESIZE_STEPS;
461 return 1;
465 do_load_dir (dir_list *list, sortfn *sort, int reverse, int case_sensitive,
466 char *filter)
468 DIR *dirp;
469 struct dirent *dp;
470 int status, link_to_dir, stale_link;
471 int next_free = 0;
472 struct stat buf;
474 tree_store_start_check_cwd ();
476 dirp = mc_opendir (".");
477 if (!dirp) {
478 message (1, MSG_ERROR, _("Cannot read directory contents"));
479 tree_store_end_check ();
480 return set_zero_dir (list);
482 for (dp = mc_readdir (dirp); dp; dp = mc_readdir (dirp)) {
483 status =
484 handle_dirent (list, filter, dp, &buf, next_free, &link_to_dir,
485 &stale_link);
486 if (status == 0)
487 continue;
488 if (status == -1) {
489 tree_store_end_check ();
490 mc_closedir (dirp);
491 return next_free;
493 list->list[next_free].fnamelen = NLENGTH (dp);
494 list->list[next_free].fname = g_strdup (dp->d_name);
495 list->list[next_free].f.marked = 0;
496 list->list[next_free].f.link_to_dir = link_to_dir;
497 list->list[next_free].f.stale_link = stale_link;
498 list->list[next_free].f.dir_size_computed = 0;
499 list->list[next_free].buf = buf;
500 next_free++;
501 if (!(next_free % 32))
502 rotate_dash ();
505 if (next_free) {
506 /* Add ".." except the root directory */
507 if (strcmp (vfs_canon ("."), "/") != 0)
508 add_dotdot_to_list (list, next_free++);
509 do_sort (list, sort, next_free - 1, reverse, case_sensitive);
510 } else {
511 tree_store_end_check ();
512 mc_closedir (dirp);
513 return set_zero_dir (list);
516 mc_closedir (dirp);
517 tree_store_end_check ();
518 return next_free;
522 link_isdir (file_entry *file)
524 if (file->f.link_to_dir)
525 return 1;
526 else
527 return 0;
531 if_link_is_exe (char *full_name, file_entry *file)
533 struct stat b;
535 if (S_ISLNK (file->buf.st_mode)) {
536 mc_stat (full_name, &b);
537 return is_exe (b.st_mode);
538 } else
539 return 1;
542 static dir_list dir_copy = { 0, 0 };
544 static void
545 alloc_dir_copy (int size)
547 int i;
549 if (dir_copy.size < size){
550 if (dir_copy.list){
552 for (i = 0; i < dir_copy.size; i++) {
553 if (dir_copy.list [i].fname)
554 g_free (dir_copy.list [i].fname);
556 g_free (dir_copy.list);
557 dir_copy.list = 0;
560 dir_copy.list = g_new (file_entry, size);
561 for (i = 0; i < size; i++)
562 dir_copy.list [i].fname = 0;
564 dir_copy.size = size;
568 /* If filter is null, then it is a match */
570 do_reload_dir (dir_list * list, sortfn * sort, int count, int rev,
571 int case_sensitive, char *filter)
573 DIR *dirp;
574 struct dirent *dp;
575 int next_free = 0;
576 int i, status, link_to_dir, stale_link;
577 struct stat buf;
578 int marked_cnt;
579 GHashTable *marked_files = g_hash_table_new (g_str_hash, g_str_equal);
581 tree_store_start_check_cwd ();
582 dirp = mc_opendir (".");
583 if (!dirp) {
584 message (1, MSG_ERROR, _("Cannot read directory contents"));
585 clean_dir (list, count);
586 tree_store_end_check ();
587 return set_zero_dir (list);
590 alloc_dir_copy (list->size);
591 for (marked_cnt = i = 0; i < count; i++) {
592 dir_copy.list[i].fnamelen = list->list[i].fnamelen;
593 dir_copy.list[i].fname = list->list[i].fname;
594 dir_copy.list[i].f.marked = list->list[i].f.marked;
595 dir_copy.list[i].f.dir_size_computed =
596 list->list[i].f.dir_size_computed;
597 dir_copy.list[i].f.link_to_dir = list->list[i].f.link_to_dir;
598 dir_copy.list[i].f.stale_link = list->list[i].f.stale_link;
599 if (list->list[i].f.marked) {
600 g_hash_table_insert (marked_files, dir_copy.list[i].fname,
601 &dir_copy.list[i]);
602 marked_cnt++;
606 for (dp = mc_readdir (dirp); dp; dp = mc_readdir (dirp)) {
607 status =
608 handle_dirent (list, filter, dp, &buf, next_free, &link_to_dir,
609 &stale_link);
610 if (status == 0)
611 continue;
612 if (status == -1) {
613 mc_closedir (dirp);
614 /* Norbert (Feb 12, 1997):
615 Just in case someone finds this memory leak:
616 -1 means big trouble (at the moment no memory left),
617 I don't bother with further cleanup because if one gets to
618 this point he will have more problems than a few memory
619 leaks and because one 'clean_dir' would not be enough (and
620 because I don't want to spent the time to make it working,
621 IMHO it's not worthwhile).
622 clean_dir (&dir_copy, count);
624 tree_store_end_check ();
625 return next_free;
628 list->list[next_free].f.marked = 0;
631 * If we have marked files in the copy, scan through the copy
632 * to find matching file. Decrease number of remaining marks if
633 * we copied one.
635 if (marked_cnt > 0) {
636 file_entry *p;
637 if (NULL !=
638 (p = g_hash_table_lookup (marked_files, dp->d_name))) {
639 list->list[next_free].f.marked = 1;
640 marked_cnt--;
644 list->list[next_free].fnamelen = NLENGTH (dp);
645 list->list[next_free].fname = g_strdup (dp->d_name);
646 list->list[next_free].f.link_to_dir = link_to_dir;
647 list->list[next_free].f.stale_link = stale_link;
648 list->list[next_free].f.dir_size_computed = 0;
649 list->list[next_free].buf = buf;
650 next_free++;
651 if (!(next_free % 16))
652 rotate_dash ();
654 mc_closedir (dirp);
655 tree_store_end_check ();
656 g_hash_table_destroy (marked_files);
657 if (next_free) {
658 /* Add ".." except the root directory */
659 if (strcmp (vfs_canon ("."), "/") != 0)
660 add_dotdot_to_list (list, next_free++);
661 do_sort (list, sort, next_free - 1, rev, case_sensitive);
662 } else
663 next_free = set_zero_dir (list);
664 clean_dir (&dir_copy, count);
665 return next_free;