fixed several missing #include's
[free-mc.git] / src / dir.c
blob977d0b6e765e098972462e67752346af9228f529
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 /** \file dir.c
20 * \brief Source: directory routines
23 #include <config.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
31 #include "global.h"
32 #include "tty.h"
33 #include "dir.h"
34 #include "wtools.h"
35 #include "treestore.h"
36 #include "strutil.h"
37 #include "fs.h"
38 #include "../src/search/search.h"
40 /* If true show files starting with a dot */
41 int show_dot_files = 1;
43 /* If true show files ending in ~ */
44 int show_backups = 1;
46 /* If false then directories are shown separately from files */
47 int mix_all_files = 0;
49 /* Reverse flag */
50 static int reverse = 1;
52 /* Are the files sorted case sensitively? */
53 static int case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
55 /* Are the exec_bit files top in list*/
56 static int exec_first = 1;
58 #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) )
60 sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
61 { N_("&Unsorted"), unsorted },
62 { N_("&Name"), sort_name },
63 { N_("&Extension"), sort_ext },
64 { N_("&Modify time"), sort_time },
65 { N_("&Access time"), sort_atime },
66 { N_("C&Hange time"), sort_ctime },
67 { N_("&Size"), sort_size },
68 { N_("&Inode"), sort_inode },
72 int
73 unsorted (file_entry *a, file_entry *b)
75 (void) a;
76 (void) b;
77 return 0;
80 int
81 sort_name (file_entry *a, file_entry *b)
83 int ad = MY_ISDIR (a);
84 int bd = MY_ISDIR (b);
86 if (ad == bd || mix_all_files) {
87 /* create key if does not exist, key will be freed after sorting */
88 if (a->sort_key == NULL)
89 a->sort_key = str_create_key_for_filename (a->fname, case_sensitive);
90 if (b->sort_key == NULL)
91 b->sort_key = str_create_key_for_filename (b->fname, case_sensitive);
93 return str_key_collate (a->sort_key, b->sort_key, case_sensitive)
94 * reverse;
96 return bd - ad;
99 int
100 sort_ext (file_entry *a, file_entry *b)
102 int r;
103 int ad = MY_ISDIR (a);
104 int bd = MY_ISDIR (b);
106 if (ad == bd || mix_all_files){
107 if (a->second_sort_key == NULL)
108 a->second_sort_key = str_create_key (extension (a->fname), case_sensitive);
109 if (b->second_sort_key == NULL)
110 b->second_sort_key = str_create_key (extension (b->fname), case_sensitive);
112 r = str_key_collate (a->second_sort_key, b->second_sort_key, case_sensitive);
113 if (r)
114 return r * reverse;
115 else
116 return sort_name (a, b);
117 } else
118 return bd-ad;
122 sort_time (file_entry *a, file_entry *b)
124 int ad = MY_ISDIR (a);
125 int bd = MY_ISDIR (b);
127 if (ad == bd || mix_all_files) {
128 int result = a->st.st_mtime < b->st.st_mtime ? -1 :
129 a->st.st_mtime > b->st.st_mtime;
130 if (result != 0)
131 return result * reverse;
132 else
133 return sort_name (a, b);
135 else
136 return bd-ad;
140 sort_ctime (file_entry *a, file_entry *b)
142 int ad = MY_ISDIR (a);
143 int bd = MY_ISDIR (b);
145 if (ad == bd || mix_all_files) {
146 int result = a->st.st_ctime < b->st.st_ctime ? -1 :
147 a->st.st_ctime > b->st.st_ctime;
148 if (result != 0)
149 return result * reverse;
150 else
151 return sort_name (a, b);
153 else
154 return bd-ad;
158 sort_atime (file_entry *a, file_entry *b)
160 int ad = MY_ISDIR (a);
161 int bd = MY_ISDIR (b);
163 if (ad == bd || mix_all_files) {
164 int result = a->st.st_atime < b->st.st_atime ? -1 :
165 a->st.st_atime > b->st.st_atime;
166 if (result != 0)
167 return result * reverse;
168 else
169 return sort_name (a, b);
171 else
172 return bd-ad;
176 sort_inode (file_entry *a, file_entry *b)
178 int ad = MY_ISDIR (a);
179 int bd = MY_ISDIR (b);
181 if (ad == bd || mix_all_files)
182 return (a->st.st_ino - b->st.st_ino) * reverse;
183 else
184 return bd-ad;
188 sort_size (file_entry *a, file_entry *b)
190 int ad = MY_ISDIR (a);
191 int bd = MY_ISDIR (b);
192 int result = 0;
194 if (ad != bd && !mix_all_files)
195 return bd - ad;
197 result = a->st.st_size < b->st.st_size ? -1 :
198 a->st.st_size > b->st.st_size;
199 if (result != 0)
200 return result * reverse;
201 else
202 return sort_name (a, b);
205 /* clear keys, should be call after sorting is finished */
206 static void
207 clean_sort_keys (dir_list *list, int start, int count)
209 int i;
211 for (i = 0; i < count; i++){
212 str_release_key (list->list [i + start].sort_key, case_sensitive);
213 list->list [i + start].sort_key = NULL;
214 str_release_key (list->list [i + start].second_sort_key, case_sensitive);
215 list->list [i + start].second_sort_key = NULL;
220 void
221 do_sort (dir_list *list, sortfn *sort, int top, int reverse_f, int case_sensitive_f, int exec_first_f)
223 int dot_dot_found = 0;
225 if (top == 0)
226 return;
228 /* If there is an ".." entry the caller must take care to
229 ensure that it occupies the first list element. */
230 if (!strcmp (list->list [0].fname, ".."))
231 dot_dot_found = 1;
233 reverse = reverse_f ? -1 : 1;
234 case_sensitive = case_sensitive_f;
235 exec_first = exec_first_f;
236 qsort (&(list->list) [dot_dot_found],
237 top + 1 - dot_dot_found, sizeof (file_entry), sort);
239 clean_sort_keys (list, dot_dot_found, top + 1 - dot_dot_found);
242 void
243 clean_dir (dir_list *list, int count)
245 int i;
247 for (i = 0; i < count; i++){
248 g_free (list->list [i].fname);
249 list->list [i].fname = NULL;
253 static int
254 add_dotdot_to_list (dir_list *list, int index)
256 /* Need to grow the *list? */
257 if (index == list->size) {
258 list->list = g_realloc (list->list, sizeof (file_entry) *
259 (list->size + RESIZE_STEPS));
260 if (!list->list)
261 return 0;
262 list->size += RESIZE_STEPS;
265 memset (&(list->list) [index], 0, sizeof(file_entry));
266 (list->list) [index].fnamelen = 2;
267 (list->list) [index].fname = g_strdup ("..");
268 (list->list) [index].f.link_to_dir = 0;
269 (list->list) [index].f.stale_link = 0;
270 (list->list) [index].f.dir_size_computed = 0;
271 (list->list) [index].f.marked = 0;
272 (list->list) [index].st.st_mode = 040755;
273 return 1;
276 /* Used to set up a directory list when there is no access to a directory */
278 set_zero_dir (dir_list *list)
280 return (add_dotdot_to_list (list, 0));
283 /* If you change handle_dirent then check also handle_path. */
284 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
285 static int
286 handle_dirent (dir_list *list, const char *filter, struct dirent *dp,
287 struct stat *buf1, int next_free, int *link_to_dir,
288 int *stale_link)
290 if (dp->d_name[0] == '.' && dp->d_name[1] == 0)
291 return 0;
292 if (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == 0)
293 return 0;
294 if (!show_dot_files && (dp->d_name[0] == '.'))
295 return 0;
296 if (!show_backups && dp->d_name[NLENGTH (dp) - 1] == '~')
297 return 0;
298 if (mc_lstat (dp->d_name, buf1) == -1) {
300 * lstat() fails - such entries should be identified by
301 * buf1->st_mode being 0.
302 * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
304 memset (buf1, 0, sizeof (*buf1));
307 if (S_ISDIR (buf1->st_mode))
308 tree_store_mark_checked (dp->d_name);
310 /* A link to a file or a directory? */
311 *link_to_dir = 0;
312 *stale_link = 0;
313 if (S_ISLNK (buf1->st_mode)) {
314 struct stat buf2;
315 if (!mc_stat (dp->d_name, &buf2))
316 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
317 else
318 *stale_link = 1;
320 if (!(S_ISDIR (buf1->st_mode) || *link_to_dir) && filter
321 && !mc_search(filter, dp->d_name, MC_SEARCH_T_GLOB) )
322 return 0;
324 /* Need to grow the *list? */
325 if (next_free == list->size) {
326 list->list =
327 g_realloc (list->list,
328 sizeof (file_entry) * (list->size + RESIZE_STEPS));
329 if (!list->list)
330 return -1;
331 list->size += RESIZE_STEPS;
333 return 1;
336 /* handle_path is a simplified handle_dirent. The difference is that
337 handle_path doesn't pay attention to show_dot_files and show_backups.
338 Moreover handle_path can't be used with a filemask.
339 If you change handle_path then check also handle_dirent. */
340 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
342 handle_path (dir_list *list, const char *path,
343 struct stat *buf1, int next_free, int *link_to_dir,
344 int *stale_link)
346 if (path [0] == '.' && path [1] == 0)
347 return 0;
348 if (path [0] == '.' && path [1] == '.' && path [2] == 0)
349 return 0;
350 if (mc_lstat (path, buf1) == -1)
351 return 0;
353 if (S_ISDIR (buf1->st_mode))
354 tree_store_mark_checked (path);
356 /* A link to a file or a directory? */
357 *link_to_dir = 0;
358 *stale_link = 0;
359 if (S_ISLNK(buf1->st_mode)){
360 struct stat buf2;
361 if (!mc_stat (path, &buf2))
362 *link_to_dir = S_ISDIR(buf2.st_mode) != 0;
363 else
364 *stale_link = 1;
367 /* Need to grow the *list? */
368 if (next_free == list->size){
369 list->list = g_realloc (list->list, sizeof (file_entry) *
370 (list->size + RESIZE_STEPS));
371 if (!list->list)
372 return -1;
373 list->size += RESIZE_STEPS;
375 return 1;
379 do_load_dir (const char *path, dir_list *list, sortfn *sort, int reverse,
380 int case_sensitive, int exec_ff, const char *filter)
382 DIR *dirp;
383 struct dirent *dp;
384 int status, link_to_dir, stale_link;
385 int next_free = 0;
386 struct stat st;
388 /* ".." (if any) must be the first entry in the list */
389 if (set_zero_dir (list) == 0)
390 return next_free;
391 next_free++;
393 dirp = mc_opendir (path);
394 if (!dirp) {
395 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
396 return next_free;
398 tree_store_start_check (path);
399 /* Do not add a ".." entry to the root directory */
400 if (!strcmp (path, "/"))
401 next_free--;
402 while ((dp = mc_readdir (dirp))) {
403 status =
404 handle_dirent (list, filter, dp, &st, next_free, &link_to_dir,
405 &stale_link);
406 if (status == 0)
407 continue;
408 if (status == -1) {
409 tree_store_end_check ();
410 mc_closedir (dirp);
411 return next_free;
413 list->list[next_free].fnamelen = NLENGTH (dp);
414 list->list[next_free].fname = g_strdup (dp->d_name);
415 list->list[next_free].f.marked = 0;
416 list->list[next_free].f.link_to_dir = link_to_dir;
417 list->list[next_free].f.stale_link = stale_link;
418 list->list[next_free].f.dir_size_computed = 0;
419 list->list[next_free].st = st;
420 list->list[next_free].sort_key = NULL;
421 list->list[next_free].second_sort_key = NULL;
422 next_free++;
423 if (!(next_free % 32))
424 rotate_dash ();
427 if (next_free) {
428 do_sort (list, sort, next_free - 1, reverse, case_sensitive, exec_ff);
431 mc_closedir (dirp);
432 tree_store_end_check ();
433 return next_free;
437 link_isdir (const file_entry *file)
439 if (file->f.link_to_dir)
440 return 1;
441 else
442 return 0;
446 if_link_is_exe (const char *full_name, const file_entry *file)
448 struct stat b;
450 if (S_ISLNK (file->st.st_mode) && !mc_stat (full_name, &b)) {
451 return is_exe (b.st_mode);
452 } else
453 return 1;
456 static dir_list dir_copy = { 0, 0 };
458 static void
459 alloc_dir_copy (int size)
461 int i;
463 if (dir_copy.size < size){
464 if (dir_copy.list){
466 for (i = 0; i < dir_copy.size; i++) {
467 g_free (dir_copy.list [i].fname);
469 g_free (dir_copy.list);
470 dir_copy.list = 0;
473 dir_copy.list = g_new (file_entry, size);
474 for (i = 0; i < size; i++) {
475 dir_copy.list [i].fname = NULL;
476 dir_copy.list [i].sort_key = NULL;
477 dir_copy.list [i].second_sort_key = NULL;
480 dir_copy.size = size;
484 /* If filter is null, then it is a match */
486 do_reload_dir (const char *path, dir_list *list, sortfn *sort, int count,
487 int rev, int case_sensitive, int exec_ff, const char *filter)
489 DIR *dirp;
490 struct dirent *dp;
491 int next_free = 0;
492 int i, status, link_to_dir, stale_link;
493 struct stat st;
494 int marked_cnt;
495 GHashTable *marked_files;
497 dirp = mc_opendir (path);
498 if (!dirp) {
499 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
500 clean_dir (list, count);
501 return set_zero_dir (list);
504 tree_store_start_check (path);
505 marked_files = g_hash_table_new (g_str_hash, g_str_equal);
506 alloc_dir_copy (list->size);
507 for (marked_cnt = i = 0; i < count; i++) {
508 dir_copy.list[i].fnamelen = list->list[i].fnamelen;
509 dir_copy.list[i].fname = list->list[i].fname;
510 dir_copy.list[i].f.marked = list->list[i].f.marked;
511 dir_copy.list[i].f.dir_size_computed =
512 list->list[i].f.dir_size_computed;
513 dir_copy.list[i].f.link_to_dir = list->list[i].f.link_to_dir;
514 dir_copy.list[i].f.stale_link = list->list[i].f.stale_link;
515 dir_copy.list[i].sort_key = NULL;
516 dir_copy.list[i].second_sort_key = NULL;
517 if (list->list[i].f.marked) {
518 g_hash_table_insert (marked_files, dir_copy.list[i].fname,
519 &dir_copy.list[i]);
520 marked_cnt++;
524 /* Add ".." except to the root directory. The ".." entry
525 (if any) must be the first in the list. */
526 if (strcmp (path, "/") != 0) {
527 if (set_zero_dir (list) == 0) {
528 clean_dir (list, count);
529 clean_dir (&dir_copy, count);
530 return next_free;
532 next_free++;
535 while ((dp = mc_readdir (dirp))) {
536 status =
537 handle_dirent (list, filter, dp, &st, next_free, &link_to_dir,
538 &stale_link);
539 if (status == 0)
540 continue;
541 if (status == -1) {
542 mc_closedir (dirp);
543 /* Norbert (Feb 12, 1997):
544 Just in case someone finds this memory leak:
545 -1 means big trouble (at the moment no memory left),
546 I don't bother with further cleanup because if one gets to
547 this point he will have more problems than a few memory
548 leaks and because one 'clean_dir' would not be enough (and
549 because I don't want to spent the time to make it working,
550 IMHO it's not worthwhile).
551 clean_dir (&dir_copy, count);
553 tree_store_end_check ();
554 g_hash_table_destroy (marked_files);
555 return next_free;
558 list->list[next_free].f.marked = 0;
561 * If we have marked files in the copy, scan through the copy
562 * to find matching file. Decrease number of remaining marks if
563 * we copied one.
565 if (marked_cnt > 0) {
566 if ((g_hash_table_lookup (marked_files, dp->d_name))) {
567 list->list[next_free].f.marked = 1;
568 marked_cnt--;
572 list->list[next_free].fnamelen = NLENGTH (dp);
573 list->list[next_free].fname = g_strdup (dp->d_name);
574 list->list[next_free].f.link_to_dir = link_to_dir;
575 list->list[next_free].f.stale_link = stale_link;
576 list->list[next_free].f.dir_size_computed = 0;
577 list->list[next_free].st = st;
578 list->list[next_free].sort_key = NULL;
579 list->list[next_free].second_sort_key = NULL;
580 next_free++;
581 if (!(next_free % 16))
582 rotate_dash ();
584 mc_closedir (dirp);
585 tree_store_end_check ();
586 g_hash_table_destroy (marked_files);
587 if (next_free) {
588 do_sort (list, sort, next_free - 1, rev, case_sensitive, exec_ff);
590 clean_dir (&dir_copy, count);
591 return next_free;