This source file includes following definitions.
- key_collate
- compare_by_names
- clean_sort_keys
- handle_dirent
- dir_get_dotdot_stat
- alloc_dir_copy
- dir_list_grow
- dir_list_append
- unsorted
- sort_name
- sort_vers
- sort_ext
- sort_time
- sort_ctime
- sort_atime
- sort_inode
- sort_size
- dir_list_sort
- dir_list_clean
- dir_list_free_list
- dir_list_init
- handle_path
- dir_list_load
- if_link_is_exe
- dir_list_reload
- file_filter_clear
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 #include <config.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37
38 #include "lib/global.h"
39 #include "lib/tty/tty.h"
40 #include "lib/search.h"
41 #include "lib/vfs/vfs.h"
42 #include "lib/fs.h"
43 #include "lib/strutil.h"
44 #include "lib/util.h"
45
46 #include "src/setup.h"
47
48 #include "treestore.h"
49 #include "file.h"
50 #include "dir.h"
51
52
53
54
55
56 #define MY_ISDIR(x) (\
57 (is_exe (x->st.st_mode) && !(S_ISDIR (x->st.st_mode) || link_isdir (x)) && exec_first) \
58 ? 1 \
59 : ( (S_ISDIR (x->st.st_mode) || link_isdir (x)) ? 2 : 0) )
60
61
62
63
64
65
66
67
68 static int reverse = 1;
69
70
71 static gboolean case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
72
73
74 static gboolean exec_first = TRUE;
75
76 static dir_list dir_copy = { NULL, 0, 0, NULL };
77
78
79
80
81
82 static inline int
83 key_collate (const char *t1, const char *t2)
84 {
85 int dotdot = 0;
86 int ret;
87
88 dotdot = (t1[0] == '.' ? 1 : 0) | ((t2[0] == '.' ? 1 : 0) << 1);
89
90 switch (dotdot)
91 {
92 case 0:
93 case 3:
94 ret = str_key_collate (t1, t2, case_sensitive) * reverse;
95 break;
96 case 1:
97 ret = -1;
98 break;
99 case 2:
100 ret = 1;
101 break;
102 default:
103 ret = 0;
104 }
105
106 return ret;
107 }
108
109
110
111 static inline int
112 compare_by_names (file_entry_t *a, file_entry_t *b)
113 {
114
115 if (a->name_sort_key == NULL)
116 a->name_sort_key = str_create_key_for_filename (a->fname->str, case_sensitive);
117 if (b->name_sort_key == NULL)
118 b->name_sort_key = str_create_key_for_filename (b->fname->str, case_sensitive);
119
120 return key_collate (a->name_sort_key, b->name_sort_key);
121 }
122
123
124
125
126
127
128 static void
129 clean_sort_keys (dir_list *list, int start, int count)
130 {
131 int i;
132
133 for (i = 0; i < count; i++)
134 {
135 file_entry_t *fentry;
136
137 fentry = &list->list[i + start];
138 str_release_key (fentry->name_sort_key, case_sensitive);
139 fentry->name_sort_key = NULL;
140 str_release_key (fentry->extension_sort_key, case_sensitive);
141 fentry->extension_sort_key = NULL;
142 }
143 }
144
145
146
147
148
149
150
151 static gboolean
152 handle_dirent (struct vfs_dirent *dp, const file_filter_t *filter, struct stat *buf1,
153 gboolean *link_to_dir, gboolean *stale_link)
154 {
155 vfs_path_t *vpath;
156 gboolean ok = TRUE;
157
158 if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
159 return FALSE;
160 if (!panels_options.show_dot_files && (dp->d_name[0] == '.'))
161 return FALSE;
162 if (!panels_options.show_backups && dp->d_name[dp->d_len - 1] == '~')
163 return FALSE;
164
165 vpath = vfs_path_from_str (dp->d_name);
166 if (mc_lstat (vpath, buf1) == -1)
167 {
168
169
170
171
172
173 memset (buf1, 0, sizeof (*buf1));
174 }
175
176 if (S_ISDIR (buf1->st_mode))
177 tree_store_mark_checked (dp->d_name);
178
179
180 *link_to_dir = file_is_symlink_to_dir (vpath, buf1, stale_link);
181
182 vfs_path_free (vpath, TRUE);
183
184 if (filter != NULL && filter->handler != NULL)
185 {
186 gboolean files_only = (filter->flags & SELECT_FILES_ONLY) != 0;
187
188 ok = ((S_ISDIR (buf1->st_mode) || *link_to_dir) && files_only)
189 || mc_search_run (filter->handler, dp->d_name, 0, dp->d_len, NULL);
190 }
191
192 return ok;
193 }
194
195
196
197
198 static gboolean
199 dir_get_dotdot_stat (const vfs_path_t *vpath, struct stat *st)
200 {
201 gboolean ret = FALSE;
202
203 if ((vpath != NULL) && (st != NULL))
204 {
205 const char *path;
206
207 path = vfs_path_get_by_index (vpath, 0)->path;
208 if (path != NULL && *path != '\0')
209 {
210 vfs_path_t *tmp_vpath;
211
212 tmp_vpath = vfs_path_append_new (vpath, "..", (char *) NULL);
213 ret = mc_stat (tmp_vpath, st) == 0;
214 vfs_path_free (tmp_vpath, TRUE);
215 }
216 }
217
218 return ret;
219 }
220
221
222
223 static void
224 alloc_dir_copy (int size)
225 {
226 if (dir_copy.size < size)
227 {
228 if (dir_copy.list != NULL)
229 dir_list_free_list (&dir_copy);
230
231 dir_copy.list = g_new0 (file_entry_t, size);
232 dir_copy.size = size;
233 dir_copy.len = 0;
234 }
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249 gboolean
250 dir_list_grow (dir_list *list, int delta)
251 {
252 int size;
253 gboolean clear_flag = FALSE;
254
255 if (list == NULL)
256 return FALSE;
257
258 if (delta == 0)
259 return TRUE;
260
261 size = list->size + delta;
262 if (size <= 0)
263 {
264 size = DIR_LIST_MIN_SIZE;
265 clear_flag = TRUE;
266 }
267
268 if (size != list->size)
269 {
270 file_entry_t *fe;
271
272 fe = g_try_renew (file_entry_t, list->list, size);
273 if (fe == NULL)
274 return FALSE;
275
276 list->list = fe;
277 list->size = size;
278 }
279
280 list->len = clear_flag ? 0 : MIN (list->len, size);
281
282 return TRUE;
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298 gboolean
299 dir_list_append (dir_list *list, const char *fname, const struct stat *st,
300 gboolean link_to_dir, gboolean stale_link)
301 {
302 file_entry_t *fentry;
303
304
305 if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
306 return FALSE;
307
308 fentry = &list->list[list->len];
309 fentry->fname = g_string_new (fname);
310 fentry->f.marked = 0;
311 fentry->f.link_to_dir = link_to_dir ? 1 : 0;
312 fentry->f.stale_link = stale_link ? 1 : 0;
313 fentry->f.dir_size_computed = 0;
314 fentry->st = *st;
315 fentry->name_sort_key = NULL;
316 fentry->extension_sort_key = NULL;
317
318 list->len++;
319
320 return TRUE;
321 }
322
323
324
325 int
326 unsorted (file_entry_t *a, file_entry_t *b)
327 {
328 (void) a;
329 (void) b;
330
331 return 0;
332 }
333
334
335
336 int
337 sort_name (file_entry_t *a, file_entry_t *b)
338 {
339 int ad = MY_ISDIR (a);
340 int bd = MY_ISDIR (b);
341
342 if (ad == bd || panels_options.mix_all_files)
343 return compare_by_names (a, b);
344
345 return bd - ad;
346 }
347
348
349
350 int
351 sort_vers (file_entry_t *a, file_entry_t *b)
352 {
353 int ad = MY_ISDIR (a);
354 int bd = MY_ISDIR (b);
355
356 if (ad == bd || panels_options.mix_all_files)
357 {
358 int result;
359
360 result = filevercmp (a->fname->str, b->fname->str);
361 if (result != 0)
362 return result * reverse;
363
364 return compare_by_names (a, b);
365 }
366
367 return bd - ad;
368 }
369
370
371
372 int
373 sort_ext (file_entry_t *a, file_entry_t *b)
374 {
375 int ad = MY_ISDIR (a);
376 int bd = MY_ISDIR (b);
377
378 if (ad == bd || panels_options.mix_all_files)
379 {
380 int r;
381
382 if (a->extension_sort_key == NULL)
383 a->extension_sort_key = str_create_key (extension (a->fname->str), case_sensitive);
384 if (b->extension_sort_key == NULL)
385 b->extension_sort_key = str_create_key (extension (b->fname->str), case_sensitive);
386
387 r = str_key_collate (a->extension_sort_key, b->extension_sort_key, case_sensitive);
388 if (r != 0)
389 return r * reverse;
390
391 return compare_by_names (a, b);
392 }
393
394 return bd - ad;
395 }
396
397
398
399 int
400 sort_time (file_entry_t *a, file_entry_t *b)
401 {
402 int ad = MY_ISDIR (a);
403 int bd = MY_ISDIR (b);
404
405 if (ad == bd || panels_options.mix_all_files)
406 {
407 int result = _GL_CMP (a->st.st_mtime, b->st.st_mtime);
408
409 if (result != 0)
410 return result * reverse;
411
412 return compare_by_names (a, b);
413 }
414
415 return bd - ad;
416 }
417
418
419
420 int
421 sort_ctime (file_entry_t *a, file_entry_t *b)
422 {
423 int ad = MY_ISDIR (a);
424 int bd = MY_ISDIR (b);
425
426 if (ad == bd || panels_options.mix_all_files)
427 {
428 int result = _GL_CMP (a->st.st_ctime, b->st.st_ctime);
429
430 if (result != 0)
431 return result * reverse;
432
433 return compare_by_names (a, b);
434 }
435
436 return bd - ad;
437 }
438
439
440
441 int
442 sort_atime (file_entry_t *a, file_entry_t *b)
443 {
444 int ad = MY_ISDIR (a);
445 int bd = MY_ISDIR (b);
446
447 if (ad == bd || panels_options.mix_all_files)
448 {
449 int result = _GL_CMP (a->st.st_atime, b->st.st_atime);
450
451 if (result != 0)
452 return result * reverse;
453
454 return compare_by_names (a, b);
455 }
456
457 return bd - ad;
458 }
459
460
461
462 int
463 sort_inode (file_entry_t *a, file_entry_t *b)
464 {
465 int ad = MY_ISDIR (a);
466 int bd = MY_ISDIR (b);
467
468 if (ad == bd || panels_options.mix_all_files)
469 return (a->st.st_ino - b->st.st_ino) * reverse;
470
471 return bd - ad;
472 }
473
474
475
476 int
477 sort_size (file_entry_t *a, file_entry_t *b)
478 {
479 int ad = MY_ISDIR (a);
480 int bd = MY_ISDIR (b);
481
482 if (ad == bd || panels_options.mix_all_files)
483 {
484 int result = _GL_CMP (a->st.st_size, b->st.st_size);
485
486 if (result != 0)
487 return result * reverse;
488
489 return compare_by_names (a, b);
490 }
491
492 return bd - ad;
493 }
494
495
496
497 void
498 dir_list_sort (dir_list *list, GCompareFunc sort, const dir_sort_options_t *sort_op)
499 {
500 if (list->len > 1 && sort != (GCompareFunc) unsorted)
501 {
502 file_entry_t *fentry = &list->list[0];
503 int dot_dot_found;
504
505
506
507 dot_dot_found = DIR_IS_DOTDOT (fentry->fname->str) ? 1 : 0;
508 reverse = sort_op->reverse ? -1 : 1;
509 case_sensitive = sort_op->case_sensitive ? 1 : 0;
510 exec_first = sort_op->exec_first;
511 qsort (&(list->list)[dot_dot_found], list->len - dot_dot_found, sizeof (file_entry_t),
512 sort);
513
514 clean_sort_keys (list, dot_dot_found, list->len - dot_dot_found);
515 }
516 }
517
518
519
520 void
521 dir_list_clean (dir_list *list)
522 {
523 int i;
524
525 for (i = 0; i < list->len; i++)
526 {
527 file_entry_t *fentry;
528
529 fentry = &list->list[i];
530 g_string_free (fentry->fname, TRUE);
531 fentry->fname = NULL;
532 }
533
534 list->len = 0;
535
536 dir_list_grow (list, DIR_LIST_MIN_SIZE - list->size);
537 }
538
539
540
541 void
542 dir_list_free_list (dir_list *list)
543 {
544 int i;
545
546 for (i = 0; i < list->len; i++)
547 {
548 file_entry_t *fentry;
549
550 fentry = &list->list[i];
551 g_string_free (fentry->fname, TRUE);
552 }
553
554 MC_PTR_FREE (list->list);
555 list->len = 0;
556 list->size = 0;
557 }
558
559
560
561
562 gboolean
563 dir_list_init (dir_list *list)
564 {
565 file_entry_t *fentry;
566
567
568 if (list->size == 0 && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
569 {
570 list->len = 0;
571 return FALSE;
572 }
573
574 fentry = &list->list[0];
575 memset (fentry, 0, sizeof (*fentry));
576 fentry->fname = g_string_new ("..");
577 fentry->f.link_to_dir = 0;
578 fentry->f.stale_link = 0;
579 fentry->f.dir_size_computed = 0;
580 fentry->f.marked = 0;
581 fentry->st.st_mode = 040755;
582 list->len = 1;
583 return TRUE;
584 }
585
586
587
588
589
590
591
592
593
594
595 gboolean
596 handle_path (const char *path, struct stat *buf1, gboolean *link_to_dir, gboolean *stale_link)
597 {
598 vfs_path_t *vpath;
599
600 if (DIR_IS_DOT (path) || DIR_IS_DOTDOT (path))
601 return FALSE;
602
603 vpath = vfs_path_from_str (path);
604 if (mc_lstat (vpath, buf1) == -1)
605 {
606 vfs_path_free (vpath, TRUE);
607 return FALSE;
608 }
609
610 if (S_ISDIR (buf1->st_mode))
611 tree_store_mark_checked (path);
612
613
614 *link_to_dir = FALSE;
615 *stale_link = FALSE;
616 if (S_ISLNK (buf1->st_mode))
617 {
618 struct stat buf2;
619
620 if (mc_stat (vpath, &buf2) == 0)
621 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
622 else
623 *stale_link = TRUE;
624 }
625
626 vfs_path_free (vpath, TRUE);
627
628 return TRUE;
629 }
630
631
632
633 gboolean
634 dir_list_load (dir_list *list, const vfs_path_t *vpath, GCompareFunc sort,
635 const dir_sort_options_t *sort_op, const file_filter_t *filter)
636 {
637 DIR *dirp;
638 struct vfs_dirent *dp;
639 struct stat st;
640 file_entry_t *fentry;
641 const char *vpath_str;
642 gboolean ret = TRUE;
643
644
645 if (!dir_list_init (list))
646 return FALSE;
647
648 fentry = &list->list[0];
649 if (dir_get_dotdot_stat (vpath, &st))
650 fentry->st = st;
651
652 if (list->callback != NULL)
653 list->callback (DIR_OPEN, (void *) vpath);
654 dirp = mc_opendir (vpath);
655 if (dirp == NULL)
656 return FALSE;
657
658 tree_store_start_check (vpath);
659
660 vpath_str = vfs_path_as_str (vpath);
661
662 if (IS_PATH_SEP (vpath_str[0]) && vpath_str[1] == '\0')
663 dir_list_clean (list);
664
665 while (ret && (dp = mc_readdir (dirp)) != NULL)
666 {
667 gboolean link_to_dir, stale_link;
668
669 if (list->callback != NULL)
670 list->callback (DIR_READ, dp);
671
672 if (!handle_dirent (dp, filter, &st, &link_to_dir, &stale_link))
673 continue;
674
675 if (!dir_list_append (list, dp->d_name, &st, link_to_dir, stale_link))
676 ret = FALSE;
677 }
678
679 if (ret)
680 dir_list_sort (list, sort, sort_op);
681
682 if (list->callback != NULL)
683 list->callback (DIR_CLOSE, NULL);
684 mc_closedir (dirp);
685 tree_store_end_check ();
686
687 return ret;
688 }
689
690
691
692 gboolean
693 if_link_is_exe (const vfs_path_t *full_name_vpath, const file_entry_t *file)
694 {
695 struct stat b;
696
697 if (S_ISLNK (file->st.st_mode) && mc_stat (full_name_vpath, &b) == 0)
698 return is_exe (b.st_mode);
699
700 return TRUE;
701 }
702
703
704
705
706 gboolean
707 dir_list_reload (dir_list *list, const vfs_path_t *vpath, GCompareFunc sort,
708 const dir_sort_options_t *sort_op, const file_filter_t *filter)
709 {
710 DIR *dirp;
711 struct vfs_dirent *dp;
712 int i;
713 struct stat st;
714 int marked_cnt;
715 GHashTable *marked_files;
716 const char *tmp_path;
717 gboolean ret = TRUE;
718
719 if (list->callback != NULL)
720 list->callback (DIR_OPEN, (void *) vpath);
721 dirp = mc_opendir (vpath);
722 if (dirp == NULL)
723 {
724 dir_list_clean (list);
725 dir_list_init (list);
726 return FALSE;
727 }
728
729 tree_store_start_check (vpath);
730
731 marked_files = g_hash_table_new (g_str_hash, g_str_equal);
732 alloc_dir_copy (list->len);
733 for (marked_cnt = i = 0; i < list->len; i++)
734 {
735 file_entry_t *fentry, *dfentry;
736
737 fentry = &list->list[i];
738 dfentry = &dir_copy.list[i];
739
740 dfentry->fname = mc_g_string_dup (fentry->fname);
741 dfentry->f.marked = fentry->f.marked;
742 dfentry->f.dir_size_computed = fentry->f.dir_size_computed;
743 dfentry->f.link_to_dir = fentry->f.link_to_dir;
744 dfentry->f.stale_link = fentry->f.stale_link;
745 dfentry->name_sort_key = NULL;
746 dfentry->extension_sort_key = NULL;
747 if (fentry->f.marked != 0)
748 {
749 g_hash_table_insert (marked_files, dfentry->fname->str, dfentry);
750 marked_cnt++;
751 }
752 }
753
754
755 dir_copy.len = list->len;
756
757
758
759 tmp_path = vfs_path_get_by_index (vpath, 0)->path;
760 if (vfs_path_elements_count (vpath) == 1 && IS_PATH_SEP (tmp_path[0]) && tmp_path[1] == '\0')
761 {
762
763 dir_list_clean (list);
764 }
765 else
766 {
767 dir_list_clean (list);
768 if (!dir_list_init (list))
769 {
770 dir_list_free_list (&dir_copy);
771 mc_closedir (dirp);
772 return FALSE;
773 }
774
775 if (dir_get_dotdot_stat (vpath, &st))
776 {
777 file_entry_t *fentry;
778
779 fentry = &list->list[0];
780 fentry->st = st;
781 }
782 }
783
784 while (ret && (dp = mc_readdir (dirp)) != NULL)
785 {
786 gboolean link_to_dir, stale_link;
787
788 if (list->callback != NULL)
789 list->callback (DIR_READ, dp);
790
791 if (!handle_dirent (dp, filter, &st, &link_to_dir, &stale_link))
792 continue;
793
794 if (!dir_list_append (list, dp->d_name, &st, link_to_dir, stale_link))
795 ret = FALSE;
796 else
797 {
798 file_entry_t *fentry;
799
800 fentry = &list->list[list->len - 1];
801
802
803
804
805
806
807 fentry->f.marked = (marked_cnt > 0
808 && g_hash_table_lookup (marked_files, dp->d_name) != NULL) ? 1 : 0;
809 if (fentry->f.marked != 0)
810 marked_cnt--;
811 }
812 }
813
814 if (ret)
815 dir_list_sort (list, sort, sort_op);
816
817 if (list->callback != NULL)
818 list->callback (DIR_CLOSE, NULL);
819 mc_closedir (dirp);
820 tree_store_end_check ();
821
822 g_hash_table_destroy (marked_files);
823 dir_list_free_list (&dir_copy);
824
825 return ret;
826 }
827
828
829
830 void
831 file_filter_clear (file_filter_t *filter)
832 {
833 MC_PTR_FREE (filter->value);
834 mc_search_free (filter->handler);
835 filter->handler = NULL;
836
837 }
838
839