This source file includes following definitions.
- tree_store_dirty
- str_common
- pathcmp
- decode
- tree_store_load_from
- encode
- tree_store_save_to
- tree_store_add_entry
- tree_store_notify_remove
- remove_entry
- process_special_dirs
- should_skip_directory
- queue_vpath_free
- tree_store_whereis
- tree_store_get
- tree_store_load
- tree_store_save
- tree_store_add_entry_remove_hook
- tree_store_remove_entry_remove_hook
- tree_store_remove_entry
- tree_store_mark_checked
- tree_store_start_check
- tree_store_end_check
- tree_store_rescan
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
32
33
34
35
36
37
38
39
40
41
42
43 #include <config.h>
44
45 #include <errno.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52
53 #include "lib/global.h"
54 #include "lib/mcconfig.h"
55 #include "lib/vfs/vfs.h"
56 #include "lib/fileloc.h"
57 #include "lib/strutil.h"
58 #include "lib/hook.h"
59 #include "lib/util.h"
60
61 #include "src/setup.h"
62
63 #include "treestore.h"
64
65
66
67
68
69 #define TREE_SIGNATURE "Midnight Commander TreeStore v 2.0"
70
71
72
73
74
75 static tree_entry *tree_store_add_entry (const vfs_path_t * name);
76
77
78
79 static struct TreeStore ts;
80
81 static hook_t *remove_entry_hooks;
82
83
84
85
86
87 static inline void
88 tree_store_dirty (gboolean dirty)
89 {
90 ts.dirty = dirty;
91 }
92
93
94
95
96
97
98
99 static size_t
100 str_common (const vfs_path_t *s1_vpath, const vfs_path_t *s2_vpath)
101 {
102 size_t result = 0;
103 const char *s1, *s2;
104
105 s1 = vfs_path_as_str (s1_vpath);
106 s2 = vfs_path_as_str (s2_vpath);
107
108 while (*s1 != '\0' && *s2 != '\0' && *s1++ == *s2++)
109 result++;
110
111 return result;
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 static int
138 pathcmp (const vfs_path_t *p1_vpath, const vfs_path_t *p2_vpath)
139 {
140 int ret_val;
141 const char *p1, *p2;
142
143 p1 = vfs_path_as_str (p1_vpath);
144 p2 = vfs_path_as_str (p2_vpath);
145
146 for (; *p1 == *p2; p1++, p2++)
147 if (*p1 == '\0')
148 return 0;
149
150 if (*p1 == '\0')
151 ret_val = -1;
152 else if (*p2 == '\0')
153 ret_val = 1;
154 else if (IS_PATH_SEP (*p1))
155 ret_val = -1;
156 else if (IS_PATH_SEP (*p2))
157 ret_val = 1;
158 else
159 ret_val = (*p1 - *p2);
160
161 return ret_val;
162 }
163
164
165
166 static char *
167 decode (char *buffer)
168 {
169 char *res, *p, *q;
170
171 res = g_strdup (buffer);
172
173 for (p = q = res; *p != '\0'; p++, q++)
174 {
175 if (*p == '\n')
176 {
177 *q = '\0';
178 return res;
179 }
180
181 if (*p != '\\')
182 {
183 *q = *p;
184 continue;
185 }
186
187 p++;
188
189 switch (*p)
190 {
191 case 'n':
192 *q = '\n';
193 break;
194 case '\\':
195 *q = '\\';
196 break;
197 default:
198 break;
199 }
200 }
201
202 *q = *p;
203
204 return res;
205 }
206
207
208
209
210 static int
211 tree_store_load_from (const char *name)
212 {
213 FILE *file;
214 char buffer[MC_MAXPATHLEN + 20];
215
216 g_return_val_if_fail (name != NULL, 0);
217
218 if (ts.loaded)
219 return 1;
220
221 file = fopen (name, "r");
222
223 if (file != NULL
224 && (fgets (buffer, sizeof (buffer), file) == NULL
225 || strncmp (buffer, TREE_SIGNATURE, strlen (TREE_SIGNATURE)) != 0))
226 {
227 fclose (file);
228 file = NULL;
229 }
230
231 if (file != NULL)
232 {
233 char oldname[MC_MAXPATHLEN] = "\0";
234
235 ts.loaded = TRUE;
236
237
238 while (fgets (buffer, MC_MAXPATHLEN, file))
239 {
240 tree_entry *e;
241 gboolean scanned;
242 char *lc_name;
243
244
245 if (buffer[0] != '0' && buffer[0] != '1')
246 continue;
247
248 if (buffer[1] != ':')
249 continue;
250
251 scanned = buffer[0] == '1';
252
253 lc_name = decode (buffer + 2);
254 if (!IS_PATH_SEP (lc_name[0]))
255 {
256
257 char *s;
258
259 s = strtok (lc_name, " ");
260 if (s != NULL)
261 {
262 char *different;
263 int common;
264
265 common = atoi (s);
266 different = strtok (NULL, "");
267 if (different != NULL)
268 {
269 vfs_path_t *vpath;
270
271 vpath = vfs_path_from_str (oldname);
272 g_strlcpy (oldname + common, different, sizeof (oldname) - (size_t) common);
273 if (vfs_file_is_local (vpath))
274 {
275 vfs_path_t *tmp_vpath;
276
277 tmp_vpath = vfs_path_from_str (oldname);
278 e = tree_store_add_entry (tmp_vpath);
279 vfs_path_free (tmp_vpath, TRUE);
280 e->scanned = scanned;
281 }
282 vfs_path_free (vpath, TRUE);
283 }
284 }
285 }
286 else
287 {
288 vfs_path_t *vpath;
289
290 vpath = vfs_path_from_str (lc_name);
291 if (vfs_file_is_local (vpath))
292 {
293 e = tree_store_add_entry (vpath);
294 e->scanned = scanned;
295 }
296 vfs_path_free (vpath, TRUE);
297 g_strlcpy (oldname, lc_name, sizeof (oldname));
298 }
299 g_free (lc_name);
300 }
301
302 fclose (file);
303 }
304
305
306 if (!ts.tree_first)
307 {
308 vfs_path_t *tmp_vpath;
309
310 tmp_vpath = vfs_path_from_str (PATH_SEP_STR);
311 tree_store_add_entry (tmp_vpath);
312 tree_store_rescan (tmp_vpath);
313 vfs_path_free (tmp_vpath, TRUE);
314 ts.loaded = TRUE;
315 }
316
317 return 1;
318 }
319
320
321
322 static char *
323 encode (const vfs_path_t *vpath, size_t offset)
324 {
325 return str_escape (vfs_path_as_str (vpath) + offset, -1, "\n\\", FALSE);
326 }
327
328
329
330
331 static int
332 tree_store_save_to (char *name)
333 {
334 tree_entry *current;
335 FILE *file;
336
337 file = fopen (name, "w");
338 if (file == NULL)
339 return errno;
340
341 fprintf (file, "%s\n", TREE_SIGNATURE);
342
343 for (current = ts.tree_first; current != NULL; current = current->next)
344 if (vfs_file_is_local (current->name))
345 {
346 int i, common;
347
348
349 if (current->prev != NULL
350 && (common = str_common (current->prev->name, current->name)) > 2)
351 {
352 char *encoded;
353
354 encoded = encode (current->name, common);
355 i = fprintf (file, "%d:%d %s\n", current->scanned ? 1 : 0, common, encoded);
356 g_free (encoded);
357 }
358 else
359 {
360 char *encoded;
361
362 encoded = encode (current->name, 0);
363 i = fprintf (file, "%d:%s\n", current->scanned ? 1 : 0, encoded);
364 g_free (encoded);
365 }
366
367 if (i == EOF)
368 {
369 fprintf (stderr, _("Cannot write to the %s file:\n%s\n"),
370 name, unix_error_string (errno));
371 break;
372 }
373 }
374
375 tree_store_dirty (FALSE);
376 fclose (file);
377
378 return 0;
379 }
380
381
382
383 static tree_entry *
384 tree_store_add_entry (const vfs_path_t *name)
385 {
386 int flag = -1;
387 tree_entry *current;
388 tree_entry *old = NULL;
389 tree_entry *new;
390 int submask = 0;
391
392 if (ts.tree_last != NULL && ts.tree_last->next != NULL)
393 abort ();
394
395
396 for (current = ts.tree_first;
397 current != NULL && (flag = pathcmp (current->name, name)) < 0; current = current->next)
398 old = current;
399
400 if (flag == 0)
401 return current;
402
403
404 new = g_new0 (tree_entry, 1);
405 if (current == NULL)
406 {
407
408 if (ts.tree_first == NULL)
409 {
410
411 ts.tree_first = new;
412 new->prev = NULL;
413 }
414 else
415 {
416 if (old != NULL)
417 old->next = new;
418 new->prev = old;
419 }
420 new->next = NULL;
421 ts.tree_last = new;
422 }
423 else
424 {
425
426 new->prev = old;
427 if (old != NULL)
428 {
429
430 new->next = old->next;
431 old->next = new;
432 }
433 else
434 {
435
436 new->next = ts.tree_first;
437 ts.tree_first = new;
438 }
439 new->next->prev = new;
440 }
441
442
443 new->name = vfs_path_clone (name);
444 new->sublevel = vfs_path_tokens_count (new->name);
445
446 {
447 const char *new_name;
448
449 new_name = vfs_path_get_last_path_str (new->name);
450 new->subname = strrchr (new_name, PATH_SEP);
451 if (new->subname == NULL)
452 new->subname = new_name;
453 else
454 new->subname++;
455 }
456
457 if (new->next != NULL)
458 submask = new->next->submask;
459
460 submask |= 1 << new->sublevel;
461 submask &= (2 << new->sublevel) - 1;
462 new->submask = submask;
463 new->mark = FALSE;
464
465
466 for (current = new->prev;
467 current != NULL && current->sublevel > new->sublevel; current = current->prev)
468 current->submask |= 1 << new->sublevel;
469
470 tree_store_dirty (TRUE);
471 return new;
472 }
473
474
475
476 static void
477 tree_store_notify_remove (tree_entry *entry)
478 {
479 hook_t *p;
480
481 for (p = remove_entry_hooks; p != NULL; p = p->next)
482 {
483 tree_store_remove_fn r = (tree_store_remove_fn) p->hook_fn;
484
485 r (entry, p->hook_data);
486 }
487 }
488
489
490
491 static tree_entry *
492 remove_entry (tree_entry *entry)
493 {
494 tree_entry *current = entry->prev;
495 long submask = 0;
496 tree_entry *ret = NULL;
497
498 tree_store_notify_remove (entry);
499
500
501 if (entry->next != NULL)
502 submask = entry->next->submask;
503
504 for (; current != NULL && current->sublevel > entry->sublevel; current = current->prev)
505 {
506 submask |= 1 << current->sublevel;
507 submask &= (2 << current->sublevel) - 1;
508 current->submask = submask;
509 }
510
511
512 if (entry->prev != NULL)
513 entry->prev->next = entry->next;
514 else
515 ts.tree_first = entry->next;
516
517 if (entry->next != NULL)
518 entry->next->prev = entry->prev;
519 else
520 ts.tree_last = entry->prev;
521
522
523 vfs_path_free (entry->name, TRUE);
524 g_free (entry);
525
526 return ret;
527 }
528
529
530
531 static void
532 process_special_dirs (GList **special_dirs, const char *file)
533 {
534 gchar **start_buff;
535 mc_config_t *cfg;
536
537 cfg = mc_config_init (file, TRUE);
538 if (cfg == NULL)
539 return;
540
541 start_buff = mc_config_get_string_list (cfg, "Special dirs", "list", NULL);
542 if (start_buff != NULL)
543 {
544 gchar **buffers;
545
546 for (buffers = start_buff; *buffers != NULL; buffers++)
547 {
548 *special_dirs = g_list_prepend (*special_dirs, *buffers);
549 *buffers = NULL;
550 }
551
552 g_strfreev (start_buff);
553 }
554 mc_config_deinit (cfg);
555 }
556
557
558
559 static gboolean
560 should_skip_directory (const vfs_path_t *vpath)
561 {
562 static GList *special_dirs = NULL;
563 GList *l;
564 static gboolean loaded = FALSE;
565 gboolean ret = FALSE;
566
567 if (!loaded)
568 {
569 const char *profile_name;
570
571 profile_name = setup_init ();
572 process_special_dirs (&special_dirs, profile_name);
573 process_special_dirs (&special_dirs, mc_global.profile_name);
574
575 loaded = TRUE;
576 }
577
578 for (l = special_dirs; l != NULL; l = g_list_next (l))
579 if (strncmp (vfs_path_as_str (vpath), l->data, strlen (l->data)) == 0)
580 {
581 ret = TRUE;
582 break;
583 }
584
585 return ret;
586 }
587
588
589
590 static void
591 queue_vpath_free (gpointer data)
592 {
593 vfs_path_free ((vfs_path_t *) data, TRUE);
594 }
595
596
597
598
599
600
601 tree_entry *
602 tree_store_whereis (const vfs_path_t *name)
603 {
604 tree_entry *current;
605 int flag = -1;
606
607 for (current = ts.tree_first;
608 current != NULL && (flag = pathcmp (current->name, name)) < 0; current = current->next)
609 ;
610
611 return flag == 0 ? current : NULL;
612 }
613
614
615
616 struct TreeStore *
617 tree_store_get (void)
618 {
619 return &ts;
620 }
621
622
623
624
625
626
627
628
629 int
630 tree_store_load (void)
631 {
632 char *name;
633 int retval;
634
635 name = mc_config_get_full_path (MC_TREESTORE_FILE);
636 retval = tree_store_load_from (name);
637 g_free (name);
638
639 return retval;
640 }
641
642
643
644
645
646
647
648
649 int
650 tree_store_save (void)
651 {
652 char *name;
653 int retval;
654
655 name = mc_config_get_full_path (MC_TREESTORE_FILE);
656 mc_util_make_backup_if_possible (name, ".tmp");
657
658 retval = tree_store_save_to (name);
659 if (retval != 0)
660 mc_util_restore_from_backup_if_possible (name, ".tmp");
661 else
662 mc_util_unlink_backup_if_possible (name, ".tmp");
663
664 g_free (name);
665 return retval;
666 }
667
668
669
670 void
671 tree_store_add_entry_remove_hook (tree_store_remove_fn callback, void *data)
672 {
673 add_hook (&remove_entry_hooks, (void (*)(void *)) callback, data);
674 }
675
676
677
678 void
679 tree_store_remove_entry_remove_hook (tree_store_remove_fn callback)
680 {
681 delete_hook (&remove_entry_hooks, (void (*)(void *)) callback);
682 }
683
684
685
686 void
687 tree_store_remove_entry (const vfs_path_t *name_vpath)
688 {
689 tree_entry *current, *base;
690 size_t len;
691
692 g_return_if_fail (name_vpath != NULL);
693
694
695 {
696 gboolean is_root;
697 const char *name_vpath_str;
698
699 name_vpath_str = vfs_path_as_str (name_vpath);
700 is_root = (IS_PATH_SEP (name_vpath_str[0]) && name_vpath_str[1] == '\0');
701 if (is_root)
702 return;
703 }
704
705
706 base = tree_store_whereis (name_vpath);
707 if (base == NULL)
708 return;
709
710 len = vfs_path_len (base->name);
711 current = base->next;
712 while (current != NULL && vfs_path_equal_len (current->name, base->name, len))
713 {
714 gboolean ok;
715 tree_entry *old;
716 const char *cname;
717
718 cname = vfs_path_as_str (current->name);
719 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]));
720 if (!ok)
721 break;
722
723 old = current;
724 current = current->next;
725 remove_entry (old);
726 }
727 remove_entry (base);
728 tree_store_dirty (TRUE);
729 }
730
731
732
733
734 void
735 tree_store_mark_checked (const char *subname)
736 {
737 vfs_path_t *name;
738 tree_entry *current, *base;
739 int flag = 1;
740 const char *cname;
741
742 if (!ts.loaded)
743 return;
744
745 if (ts.check_name == NULL)
746 return;
747
748
749 if (DIR_IS_DOT (subname) || DIR_IS_DOTDOT (subname))
750 return;
751
752 cname = vfs_path_as_str (ts.check_name);
753 if (IS_PATH_SEP (cname[0]) && cname[1] == '\0')
754 name = vfs_path_build_filename (PATH_SEP_STR, subname, (char *) NULL);
755 else
756 name = vfs_path_append_new (ts.check_name, subname, (char *) NULL);
757
758
759 for (current = ts.check_start;
760 current != NULL && (flag = pathcmp (current->name, name)) < 0; current = current->next)
761 ;
762
763 if (flag != 0)
764 {
765
766 current = tree_store_add_entry (name);
767 ts.add_queue_vpath = g_list_prepend (ts.add_queue_vpath, name);
768 }
769 else
770 vfs_path_free (name, TRUE);
771
772
773 base = current;
774 if (base != NULL)
775 {
776 size_t len;
777
778 len = vfs_path_len (base->name);
779 base->mark = FALSE;
780 for (current = base->next;
781 current != NULL && vfs_path_equal_len (current->name, base->name, len);
782 current = current->next)
783 {
784 gboolean ok;
785
786 cname = vfs_path_as_str (current->name);
787 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
788 if (!ok)
789 break;
790
791 current->mark = FALSE;
792 }
793 }
794 }
795
796
797
798
799 tree_entry *
800 tree_store_start_check (const vfs_path_t *vpath)
801 {
802 tree_entry *current, *retval;
803 size_t len;
804
805 if (!ts.loaded)
806 return NULL;
807
808 g_return_val_if_fail (ts.check_name == NULL, NULL);
809 ts.check_start = NULL;
810
811
812 current = tree_store_whereis (vpath);
813 if (current == NULL)
814 {
815 struct stat s;
816
817 if (mc_stat (vpath, &s) == -1 || !S_ISDIR (s.st_mode))
818 return NULL;
819
820 current = tree_store_add_entry (vpath);
821 ts.check_name = vfs_path_clone (vpath);
822
823 return current;
824 }
825
826 ts.check_name = vfs_path_clone (vpath);
827
828 retval = current;
829
830
831 ts.check_start = current->next;
832 len = vfs_path_len (ts.check_name);
833
834 for (current = ts.check_start;
835 current != NULL && vfs_path_equal_len (current->name, ts.check_name, len);
836 current = current->next)
837 {
838 gboolean ok;
839 const char *cname;
840
841 cname = vfs_path_as_str (current->name);
842 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
843 if (!ok)
844 break;
845
846 current->mark = TRUE;
847 }
848
849 return retval;
850 }
851
852
853
854
855 void
856 tree_store_end_check (void)
857 {
858 tree_entry *current;
859 size_t len;
860 GList *the_queue;
861
862 if (!ts.loaded)
863 return;
864
865 g_return_if_fail (ts.check_name != NULL);
866
867
868 len = vfs_path_len (ts.check_name);
869
870 current = ts.check_start;
871 while (current != NULL && vfs_path_equal_len (current->name, ts.check_name, len))
872 {
873 gboolean ok;
874 tree_entry *old;
875 const char *cname;
876
877 cname = vfs_path_as_str (current->name);
878 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
879 if (!ok)
880 break;
881
882 old = current;
883 current = current->next;
884 if (old->mark)
885 remove_entry (old);
886 }
887
888
889 the_queue = g_list_reverse (ts.add_queue_vpath);
890 ts.add_queue_vpath = NULL;
891 vfs_path_free (ts.check_name, TRUE);
892 ts.check_name = NULL;
893
894 g_list_free_full (the_queue, queue_vpath_free);
895 }
896
897
898
899 tree_entry *
900 tree_store_rescan (const vfs_path_t *vpath)
901 {
902 DIR *dirp;
903 struct stat buf;
904 tree_entry *entry;
905
906 if (should_skip_directory (vpath))
907 {
908 entry = tree_store_add_entry (vpath);
909 entry->scanned = TRUE;
910 return entry;
911 }
912
913 entry = tree_store_start_check (vpath);
914 if (entry == NULL)
915 return NULL;
916
917 dirp = mc_opendir (vpath);
918 if (dirp != NULL)
919 {
920 struct vfs_dirent *dp;
921
922 for (dp = mc_readdir (dirp); dp != NULL; dp = mc_readdir (dirp))
923 if (!DIR_IS_DOT (dp->d_name) && !DIR_IS_DOTDOT (dp->d_name))
924 {
925 vfs_path_t *tmp_vpath;
926
927 tmp_vpath = vfs_path_append_new (vpath, dp->d_name, (char *) NULL);
928 if (mc_lstat (tmp_vpath, &buf) != -1 && S_ISDIR (buf.st_mode))
929 tree_store_mark_checked (dp->d_name);
930 vfs_path_free (tmp_vpath, TRUE);
931 }
932
933 mc_closedir (dirp);
934 }
935 tree_store_end_check ();
936 entry->scanned = TRUE;
937
938 return entry;
939 }
940
941