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;
347 size_t common;
348
349
350 if (current->prev != NULL
351 && (common = str_common (current->prev->name, current->name)) > 2)
352 {
353 char *encoded;
354
355 encoded = encode (current->name, common);
356 i = fprintf (file, "%d:%zu %s\n", current->scanned ? 1 : 0, common, encoded);
357 g_free (encoded);
358 }
359 else
360 {
361 char *encoded;
362
363 encoded = encode (current->name, 0);
364 i = fprintf (file, "%d:%s\n", current->scanned ? 1 : 0, encoded);
365 g_free (encoded);
366 }
367
368 if (i == EOF)
369 {
370 fprintf (stderr, _ ("Cannot write to the %s file:\n%s\n"), name,
371 unix_error_string (errno));
372 break;
373 }
374 }
375
376 tree_store_dirty (FALSE);
377 fclose (file);
378
379 return 0;
380 }
381
382
383
384 static tree_entry *
385 tree_store_add_entry (const vfs_path_t *name)
386 {
387 int flag = -1;
388 tree_entry *current;
389 tree_entry *old = NULL;
390 tree_entry *new;
391 int submask = 0;
392
393 if (ts.tree_last != NULL && ts.tree_last->next != NULL)
394 abort ();
395
396
397 for (current = ts.tree_first; current != NULL && (flag = pathcmp (current->name, name)) < 0;
398 current = current->next)
399 old = current;
400
401 if (flag == 0)
402 return current;
403
404
405 new = g_new0 (tree_entry, 1);
406 if (current == NULL)
407 {
408
409 if (ts.tree_first == NULL)
410 {
411
412 ts.tree_first = new;
413 new->prev = NULL;
414 }
415 else
416 {
417 if (old != NULL)
418 old->next = new;
419 new->prev = old;
420 }
421 new->next = NULL;
422 ts.tree_last = new;
423 }
424 else
425 {
426
427 new->prev = old;
428 if (old != NULL)
429 {
430
431 new->next = old->next;
432 old->next = new;
433 }
434 else
435 {
436
437 new->next = ts.tree_first;
438 ts.tree_first = new;
439 }
440 new->next->prev = new;
441 }
442
443
444 new->name = vfs_path_clone (name);
445 new->sublevel = vfs_path_tokens_count (new->name);
446
447 {
448 const char *new_name;
449
450 new_name = vfs_path_get_last_path_str (new->name);
451 new->subname = strrchr (new_name, PATH_SEP);
452 if (new->subname == NULL)
453 new->subname = new_name;
454 else
455 new->subname++;
456 }
457
458 if (new->next != NULL)
459 submask = new->next->submask;
460
461 submask |= 1 << new->sublevel;
462 submask &= (2 << new->sublevel) - 1;
463 new->submask = submask;
464 new->mark = FALSE;
465
466
467 for (current = new->prev; current != NULL && current->sublevel > new->sublevel;
468 current = current->prev)
469 current->submask |= 1 << new->sublevel;
470
471 tree_store_dirty (TRUE);
472 return new;
473 }
474
475
476
477 static void
478 tree_store_notify_remove (tree_entry *entry)
479 {
480 hook_t *p;
481
482 for (p = remove_entry_hooks; p != NULL; p = p->next)
483 {
484 tree_store_remove_fn r = (tree_store_remove_fn) p->hook_fn;
485
486 r (entry, p->hook_data);
487 }
488 }
489
490
491
492 static tree_entry *
493 remove_entry (tree_entry *entry)
494 {
495 tree_entry *current = entry->prev;
496 long submask = 0;
497 tree_entry *ret = NULL;
498
499 tree_store_notify_remove (entry);
500
501
502 if (entry->next != NULL)
503 submask = entry->next->submask;
504
505 for (; current != NULL && current->sublevel > entry->sublevel; current = current->prev)
506 {
507 submask |= 1 << current->sublevel;
508 submask &= (2 << current->sublevel) - 1;
509 current->submask = submask;
510 }
511
512
513 if (entry->prev != NULL)
514 entry->prev->next = entry->next;
515 else
516 ts.tree_first = entry->next;
517
518 if (entry->next != NULL)
519 entry->next->prev = entry->prev;
520 else
521 ts.tree_last = entry->prev;
522
523
524 vfs_path_free (entry->name, TRUE);
525 g_free (entry);
526
527 return ret;
528 }
529
530
531
532 static void
533 process_special_dirs (GList **special_dirs, const char *file)
534 {
535 gchar **start_buff;
536 mc_config_t *cfg;
537
538 cfg = mc_config_init (file, TRUE);
539 if (cfg == NULL)
540 return;
541
542 start_buff = mc_config_get_string_list (cfg, "Special dirs", "list", NULL);
543 if (start_buff != NULL)
544 {
545 gchar **buffers;
546
547 for (buffers = start_buff; *buffers != NULL; buffers++)
548 {
549 *special_dirs = g_list_prepend (*special_dirs, *buffers);
550 *buffers = NULL;
551 }
552
553 g_strfreev (start_buff);
554 }
555 mc_config_deinit (cfg);
556 }
557
558
559
560 static gboolean
561 should_skip_directory (const vfs_path_t *vpath)
562 {
563 static GList *special_dirs = NULL;
564 GList *l;
565 static gboolean loaded = FALSE;
566 gboolean ret = FALSE;
567
568 if (!loaded)
569 {
570 const char *profile_name;
571
572 profile_name = setup_init ();
573 process_special_dirs (&special_dirs, profile_name);
574 process_special_dirs (&special_dirs, mc_global.profile_name);
575
576 loaded = TRUE;
577 }
578
579 for (l = special_dirs; l != NULL; l = g_list_next (l))
580 if (strncmp (vfs_path_as_str (vpath), l->data, strlen (l->data)) == 0)
581 {
582 ret = TRUE;
583 break;
584 }
585
586 return ret;
587 }
588
589
590
591 static void
592 queue_vpath_free (gpointer data)
593 {
594 vfs_path_free ((vfs_path_t *) data, TRUE);
595 }
596
597
598
599
600
601
602 tree_entry *
603 tree_store_whereis (const vfs_path_t *name)
604 {
605 tree_entry *current;
606 int flag = -1;
607
608 for (current = ts.tree_first; current != NULL && (flag = pathcmp (current->name, name)) < 0;
609 current = current->next)
610 ;
611
612 return flag == 0 ? current : NULL;
613 }
614
615
616
617 struct TreeStore *
618 tree_store_get (void)
619 {
620 return &ts;
621 }
622
623
624
625
626
627
628
629
630 int
631 tree_store_load (void)
632 {
633 char *name;
634 int retval;
635
636 name = mc_config_get_full_path (MC_TREESTORE_FILE);
637 retval = tree_store_load_from (name);
638 g_free (name);
639
640 return retval;
641 }
642
643
644
645
646
647
648
649
650 int
651 tree_store_save (void)
652 {
653 char *name;
654 int retval;
655
656 name = mc_config_get_full_path (MC_TREESTORE_FILE);
657 mc_util_make_backup_if_possible (name, ".tmp");
658
659 retval = tree_store_save_to (name);
660 if (retval != 0)
661 mc_util_restore_from_backup_if_possible (name, ".tmp");
662 else
663 mc_util_unlink_backup_if_possible (name, ".tmp");
664
665 g_free (name);
666 return retval;
667 }
668
669
670
671 void
672 tree_store_add_entry_remove_hook (tree_store_remove_fn callback, void *data)
673 {
674 add_hook (&remove_entry_hooks, (void (*) (void *)) callback, data);
675 }
676
677
678
679 void
680 tree_store_remove_entry_remove_hook (tree_store_remove_fn callback)
681 {
682 delete_hook (&remove_entry_hooks, (void (*) (void *)) callback);
683 }
684
685
686
687 void
688 tree_store_remove_entry (const vfs_path_t *name_vpath)
689 {
690 tree_entry *current, *base;
691 size_t len;
692
693 g_return_if_fail (name_vpath != NULL);
694
695
696 {
697 gboolean is_root;
698 const char *name_vpath_str;
699
700 name_vpath_str = vfs_path_as_str (name_vpath);
701 is_root = (IS_PATH_SEP (name_vpath_str[0]) && name_vpath_str[1] == '\0');
702 if (is_root)
703 return;
704 }
705
706
707 base = tree_store_whereis (name_vpath);
708 if (base == NULL)
709 return;
710
711 len = vfs_path_len (base->name);
712 current = base->next;
713 while (current != NULL && vfs_path_equal_len (current->name, base->name, len))
714 {
715 gboolean ok;
716 tree_entry *old;
717 const char *cname;
718
719 cname = vfs_path_as_str (current->name);
720 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]));
721 if (!ok)
722 break;
723
724 old = current;
725 current = current->next;
726 remove_entry (old);
727 }
728 remove_entry (base);
729 tree_store_dirty (TRUE);
730 }
731
732
733
734
735 void
736 tree_store_mark_checked (const char *subname)
737 {
738 vfs_path_t *name;
739 tree_entry *current, *base;
740 int flag = 1;
741 const char *cname;
742
743 if (!ts.loaded)
744 return;
745
746 if (ts.check_name == NULL)
747 return;
748
749
750 if (DIR_IS_DOT (subname) || DIR_IS_DOTDOT (subname))
751 return;
752
753 cname = vfs_path_as_str (ts.check_name);
754 if (IS_PATH_SEP (cname[0]) && cname[1] == '\0')
755 name = vfs_path_build_filename (PATH_SEP_STR, subname, (char *) NULL);
756 else
757 name = vfs_path_append_new (ts.check_name, subname, (char *) NULL);
758
759
760 for (current = ts.check_start; current != NULL && (flag = pathcmp (current->name, name)) < 0;
761 current = current->next)
762 ;
763
764 if (flag != 0)
765 {
766
767 current = tree_store_add_entry (name);
768 ts.add_queue_vpath = g_list_prepend (ts.add_queue_vpath, name);
769 }
770 else
771 vfs_path_free (name, TRUE);
772
773
774 base = current;
775 if (base != NULL)
776 {
777 size_t len;
778
779 len = vfs_path_len (base->name);
780 base->mark = FALSE;
781 for (current = base->next;
782 current != NULL && vfs_path_equal_len (current->name, base->name, len);
783 current = current->next)
784 {
785 gboolean ok;
786
787 cname = vfs_path_as_str (current->name);
788 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
789 if (!ok)
790 break;
791
792 current->mark = FALSE;
793 }
794 }
795 }
796
797
798
799
800 tree_entry *
801 tree_store_start_check (const vfs_path_t *vpath)
802 {
803 tree_entry *current, *retval;
804 size_t len;
805
806 if (!ts.loaded)
807 return NULL;
808
809 g_return_val_if_fail (ts.check_name == NULL, NULL);
810 ts.check_start = NULL;
811
812
813 current = tree_store_whereis (vpath);
814 if (current == NULL)
815 {
816 struct stat s;
817
818 if (mc_stat (vpath, &s) == -1 || !S_ISDIR (s.st_mode))
819 return NULL;
820
821 current = tree_store_add_entry (vpath);
822 ts.check_name = vfs_path_clone (vpath);
823
824 return current;
825 }
826
827 ts.check_name = vfs_path_clone (vpath);
828
829 retval = current;
830
831
832 ts.check_start = current->next;
833 len = vfs_path_len (ts.check_name);
834
835 for (current = ts.check_start;
836 current != NULL && vfs_path_equal_len (current->name, ts.check_name, len);
837 current = current->next)
838 {
839 gboolean ok;
840 const char *cname;
841
842 cname = vfs_path_as_str (current->name);
843 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
844 if (!ok)
845 break;
846
847 current->mark = TRUE;
848 }
849
850 return retval;
851 }
852
853
854
855
856 void
857 tree_store_end_check (void)
858 {
859 tree_entry *current;
860 size_t len;
861 GList *the_queue;
862
863 if (!ts.loaded)
864 return;
865
866 g_return_if_fail (ts.check_name != NULL);
867
868
869 len = vfs_path_len (ts.check_name);
870
871 current = ts.check_start;
872 while (current != NULL && vfs_path_equal_len (current->name, ts.check_name, len))
873 {
874 gboolean ok;
875 tree_entry *old;
876 const char *cname;
877
878 cname = vfs_path_as_str (current->name);
879 ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
880 if (!ok)
881 break;
882
883 old = current;
884 current = current->next;
885 if (old->mark)
886 remove_entry (old);
887 }
888
889
890 the_queue = g_list_reverse (ts.add_queue_vpath);
891 ts.add_queue_vpath = NULL;
892 vfs_path_free (ts.check_name, TRUE);
893 ts.check_name = NULL;
894
895 g_list_free_full (the_queue, queue_vpath_free);
896 }
897
898
899
900 tree_entry *
901 tree_store_rescan (const vfs_path_t *vpath)
902 {
903 DIR *dirp;
904 struct stat buf;
905 tree_entry *entry;
906
907 if (should_skip_directory (vpath))
908 {
909 entry = tree_store_add_entry (vpath);
910 entry->scanned = TRUE;
911 return entry;
912 }
913
914 entry = tree_store_start_check (vpath);
915 if (entry == NULL)
916 return NULL;
917
918 dirp = mc_opendir (vpath);
919 if (dirp != NULL)
920 {
921 struct vfs_dirent *dp;
922
923 for (dp = mc_readdir (dirp); dp != NULL; dp = mc_readdir (dirp))
924 if (!DIR_IS_DOT (dp->d_name) && !DIR_IS_DOTDOT (dp->d_name))
925 {
926 vfs_path_t *tmp_vpath;
927
928 tmp_vpath = vfs_path_append_new (vpath, dp->d_name, (char *) NULL);
929 if (mc_lstat (tmp_vpath, &buf) != -1 && S_ISDIR (buf.st_mode))
930 tree_store_mark_checked (dp->d_name);
931 vfs_path_free (tmp_vpath, TRUE);
932 }
933
934 mc_closedir (dirp);
935 }
936 tree_store_end_check ();
937 entry->scanned = TRUE;
938
939 return entry;
940 }
941
942