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