private static int QuickfindMultipleCopiesOnSameVolume(QuickfindMultipleOptions a) { ShouldPrint shouldPrint = (x) => x.Count >= 2 && (a.Subdir == null || x.Any((y) => y.Path.StartsWith(a.Subdir, true, System.Globalization.CultureInfo.InvariantCulture))); if (a.InteractiveDeleteMode) { return(RunInteractiveFileDeleteMode(a.DatabasePath, a.Volume, shouldPrint)); } else { return(ListFiles(a.LogPath, a.DatabasePath, a.Volume, true, shouldPrint)); } }
private static IEnumerable <List <StorageFile> > CollectFiles(SQLiteConnection connection, List <StorageFile> files, ShouldPrint shouldPrint, long?onlyOnVolume = null) { ISet <long> seenIds = new HashSet <long>(); foreach (var file in files) { if (seenIds.Contains(file.FileId)) { continue; } seenIds.Add(file.FileId); var sameFiles = GetStorageFilesForFileId(connection, file.FileId, onlyOnVolume); if (shouldPrint(sameFiles)) { yield return(sameFiles); } } }
private static int ListFiles(string logPath, string databasePath, int?volume, bool selectedVolumeOnly, ShouldPrint shouldPrint) { using (TextWriterWrapper textWriterWrapper = new TextWriterWrapper(logPath)) using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + databasePath)) { connection.Open(); List <StorageFile> files = GetKnownFilesOnVolume(connection, volume); List <Volume> volumes = VolumeOperations.GetKnownVolumes(connection); foreach (var sameFiles in CollectFiles(connection, files, shouldPrint, selectedVolumeOnly ? volume : null)) { PrintSameFileInformation(textWriterWrapper.Writer, sameFiles, volumes); } connection.Close(); } return(0); }
private static int RunInteractiveFileDeleteMode(string databasePath, long volumeId, ShouldPrint shouldPrint) { using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + databasePath)) { connection.Open(); Volume volume; { var volumes = VolumeOperations.FindAndInsertAttachedVolumes(connection); volume = volumes.FirstOrDefault((x) => x.ID == volumeId); } if (volume == null) { Console.WriteLine("Volume {0} is not attached.", volumeId); return(-1); } List <StorageFile> files = GetKnownFilesOnVolume(connection, volumeId); List <List <StorageFile> > allfiles = CollectFiles(connection, files, shouldPrint, volumeId).ToList(); for (int allfilesindex = 0; allfilesindex < allfiles.Count; allfilesindex++) { allfiles[allfilesindex] = allfiles[allfilesindex].OrderBy(x => (x.Path + "/" + x.Filename)).ToList(); } int more = 5; for (int allfilesindex = 0; allfilesindex < allfiles.Count; allfilesindex++) { List <StorageFile> sameFiles = allfiles[allfilesindex]; List <string> existingFilenames = new List <string>(); foreach (StorageFile ssf in sameFiles) { if (!existingFilenames.Contains(ssf.Filename)) { existingFilenames.Add(ssf.Filename); } } existingFilenames.Sort(); int?selectedFilename = null; while (true) { int prevfilesindex = allfilesindex - 1; int nextfilesindex = allfilesindex + more; if (prevfilesindex >= 0) { PrintFileInfoForInteractiveDelete(allfiles[prevfilesindex], " "); Console.WriteLine(); } PrintFileInfoForInteractiveDelete(sameFiles, " >>> "); if (existingFilenames.Count > 1) { Console.WriteLine(" >>> Available filenames:"); for (int i = 0; i < existingFilenames.Count; ++i) { Console.WriteLine(" >>> {2}Filename {0}: {1}", AsLetter(i), existingFilenames[i], selectedFilename == i ? "!" : " "); } } for (int iiii = allfilesindex + 1; iiii <= nextfilesindex; ++iiii) { if (iiii < allfiles.Count) { Console.WriteLine(); PrintFileInfoForInteractiveDelete(allfiles[iiii], " "); } } Console.WriteLine(); Console.WriteLine(" [file {0}/{1}, {2} to go]", allfilesindex + 1, allfiles.Count, allfiles.Count - allfilesindex); Console.WriteLine(); Console.WriteLine("Enter number of file to keep, lowercase letter for target filename, nothing to skip, Q to quit, +/- to show more/less files."); Console.Write(" > "); string input = Console.ReadLine(); if (input == "") { Console.Clear(); break; } if (input == "+") { ++more; Console.Clear(); continue; } if (input == "-") { if (more > 0) { --more; } Console.Clear(); continue; } if (input == "Q") { return(-2); } int number; if (int.TryParse(input, out number)) { if (number >= 0 && number < sameFiles.Count) { List <(FileInfo fi, bool shouldDelete)> data = new List <(FileInfo fi, bool shouldDelete)>(); for (int i = 0; i < sameFiles.Count; ++i) { var sf = sameFiles[i]; string path; if (sf.Path == "" || sf.Path == "/" || sf.Path == "\\") { path = Path.Combine(volume.DeviceID, sf.Filename); } else if (sf.Path[0] == '/' || sf.Path[0] == '\\') { path = Path.Combine(volume.DeviceID, sf.Path.Substring(1).Replace('/', '\\'), sf.Filename); } else { Console.WriteLine("Unexpected path in database: " + sf.Path); break; } data.Add((new FileInfo(path), i != number)); } Console.Clear(); bool inconsistent = false; foreach (var d in data) { if (!d.fi.Exists) { Console.WriteLine("Inconsistent file state: File {0} does not actually exist on disk.", d.fi.FullName); inconsistent = true; } } if (!inconsistent) { foreach (var d in data) { if (d.shouldDelete) { Console.WriteLine("Deleting {0}", d.fi.FullName); d.fi.Delete(); } else { if (selectedFilename.HasValue) { string newpath = Path.Combine(Path.GetDirectoryName(d.fi.FullName), existingFilenames[selectedFilename.Value]); if (File.Exists(newpath)) { Console.WriteLine("Keeping {0}; renaming to {1} not possible, skipping it", d.fi.FullName, newpath); } else { Console.WriteLine("Renaming {0} to {1}", d.fi.FullName, newpath); File.Move(d.fi.FullName, newpath); } } else { Console.WriteLine("Keeping {0}", d.fi.FullName); } } } } break; } } for (int i = 0; i < existingFilenames.Count; ++i) { if (AsLetter(i) == input) { selectedFilename = i; break; } } } } connection.Close(); } return(0); }