public long GetNextRecord(out SizeHashEntry record) { record = new SizeHashEntry(); long beginPosition = this.Stream.Position; // Indicate that there are no next record if (this.Stream.Position >= this.Stream.Length) return -1; using (BinaryReader br = new BinaryReader(this.Stream, Encoding.UTF8, true)) { record.Decided = br.ReadBoolean(); // 1 int ptrCount = br.ReadInt32(); // 4 record.Pointers = new List<HashPointers>(ptrCount); for (int i = 0; i < ptrCount; ++i) { HashPointers hp = new HashPointers(); hp.Hash = Encoding.UTF8.GetString(br.ReadBytes(32)); // 32 int entryCount = br.ReadInt32(); // 4 hp.FileEntries = new List<long>(entryCount); for (int j = 0; j < entryCount; ++j) hp.FileEntries.Add(br.ReadInt64()); // 8 record.Pointers.Add(hp); } } return beginPosition; }
static bool SelectFilesToKeep(HashPointers ptr, out List<int> toKeep) { bool selectionSuccess = false; toKeep = new List<int>(Enumerable.Range(1, ptr.FileEntries.Count)); bool decided = false; int choice = 0; bool canAutoSelect = false; List<int> oldestIDs = new List<int>(); List<int> newestIDs = new List<int>(); { // Read and register the timestamp when the files were last accessed // The oldest file (lowest timestamp) will be on the top of the list SortedList<DateTime, List<int>> timeStamps = new SortedList<DateTime, List<int>>(ptr.FileEntries.Count); PathEntry entry = new PathEntry(); int currentID = 1; foreach (long offset in ptr.FileEntries) { PathsFile.GetRecordAt(offset, out entry); FileInfo fi = new FileInfo(entry.Path); IEnumerable<DateTime> tsRegistered = timeStamps.Select(ts => ts.Key) .Where(ts => ts.Date == fi.LastAccessTime.Date && ts.Hour == fi.LastAccessTime.Hour && ts.Minute == fi.LastAccessTime.Minute && ts.Second == fi.LastAccessTime.Second); if (tsRegistered.Count() == 1) timeStamps[tsRegistered.First()].Add(currentID); else { List<int> idList = new List<int>(1); idList.Add(currentID); timeStamps.Add(fi.LastAccessTime, idList); } ++currentID; } // If the oldest and newest files are the same, don't select any of them if (timeStamps.Count == 1 && (AutoOldest || AutoNewest)) Console.WriteLine("The files' age are equal. Unable to select oldest and newest ones."); else { oldestIDs.AddRange(timeStamps.First().Value); newestIDs.AddRange(timeStamps.Last().Value); canAutoSelect = true; } } while (!selectionSuccess) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(new String('-', Console.WindowWidth - 1)); DuplicateFileLog.WriteLine(new String('-', 24)); Console.WriteLine("The following " + ptr.FileEntries.Count + " files are duplicate of each other"); DuplicateFileLog.WriteLine("The following " + ptr.FileEntries.Count + " files are duplicate of each other"); Console.ResetColor(); if (Verbose) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Hash: " + ptr.Hash); DuplicateFileLog.WriteLine("Hash: " + ptr.Hash); Console.ResetColor(); } if (!DryRun) { Console.Write("Files marked "); Console.ForegroundColor = ConsoleColor.White; Console.Write("[ KEEP ]"); Console.ResetColor(); Console.Write(" will be kept. Files marked "); Console.ForegroundColor = ConsoleColor.Red; Console.Write("[DELETE]"); Console.ResetColor(); Console.WriteLine(" will be deleted."); if (!AutoNewest && !AutoOldest) Console.WriteLine("Please select the files you wish to keep or delete."); else if (!canAutoSelect) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Was unable to automatically select " + (AutoOldest ? "oldest" : "newest") + " file to keep"); Console.ResetColor(); } else { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Automatically selecting the " + (AutoOldest ? "OLDEST" : "NEWEST") + " file to keep"); Console.ResetColor(); toKeep.Clear(); if (AutoOldest) toKeep.AddRange(oldestIDs); else if (AutoNewest) toKeep.AddRange(newestIDs); } } // Print the file list with a choice int menuId = 1; PathEntry etr = new PathEntry(); int totalLog10ofEntries = (int)Math.Floor(Math.Log10((double)ptr.FileEntries.Count)) + 1; if (totalLog10ofEntries < 3) // Make sure "-1." can be printed totalLog10ofEntries = 3; foreach (long offset in ptr.FileEntries) { PathsFile.GetRecordAt(offset, out etr); // Create a little menu for the user to give a choice // The length of the choice option printed, how many characters it takes in base 10 int strCurrentLength = (int)Math.Floor(Math.Log10((double)menuId)) + 1; // 0-9: 1 long, 10-99: 2 long, etc. ++strCurrentLength; // The '.' (dot) takes up another character if (!DryRun) { if (toKeep.Contains(menuId)) { Console.ForegroundColor = ConsoleColor.White; Console.Write("[ KEEP ] "); Console.ResetColor(); } else { Console.ForegroundColor = ConsoleColor.Red; Console.Write("[DELETE] "); Console.ResetColor(); } } Console.ForegroundColor = ConsoleColor.Cyan; Console.Write(new String(' ', totalLog10ofEntries - strCurrentLength + 1) + menuId + ". "); bool oldestOrNewest = false; if (oldestIDs.Contains(menuId)) { Console.ForegroundColor = ConsoleColor.White; Console.Write("[OLDEST] "); DuplicateFileLog.Write("[OLDEST] "); oldestOrNewest = true; } else if (newestIDs.Contains(menuId)) { Console.ForegroundColor = ConsoleColor.White; Console.Write("[NEWEST] "); DuplicateFileLog.Write("[NEWEST] "); oldestOrNewest = true; } DuplicateFileLog.Write(new String(' ', (!oldestOrNewest ? 9 : 0) + totalLog10ofEntries - strCurrentLength + 1) + menuId + ". "); Console.ResetColor(); Console.WriteLine(etr.Path); DuplicateFileLog.WriteLine(etr.Path); ++menuId; } if (!AutoNewest && !AutoOldest && !DryRun) { Console.ForegroundColor = ConsoleColor.Magenta; Console.Write(" [DONE] " + new String(' ', totalLog10ofEntries - 2 + 1) + "0. "); Console.ResetColor(); Console.WriteLine("Finalise the choices"); Console.ForegroundColor = ConsoleColor.Green; Console.Write(" [SKIP] " + new String(' ', totalLog10ofEntries - 3 + 1) + "-1. "); Console.ResetColor(); Console.WriteLine("Keep everything for now, decide later"); Console.ForegroundColor = ConsoleColor.DarkRed; Console.Write(" [NUKE] " + new String(' ', totalLog10ofEntries - 3 + 1) + "-2. "); Console.Write("Delete ALL FILES!"); Console.ResetColor(); Console.WriteLine(); } // Read the user's choice if (!AutoNewest && !AutoOldest && !DryRun) { Console.WriteLine("Please select an option from above. If you select a file, its status will be togged between keep and delete."); Console.Write("? "); try { choice = Convert.ToInt32(Console.ReadLine()); selectionSuccess = true; // Attempt to say that the user successfully selected if (choice >= menuId || choice < -2) throw new ArgumentOutOfRangeException("The entered choice is invalid."); } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.Write("Invalid input. "); Console.ResetColor(); Console.WriteLine("The enterd input is not a number or is out of range. Please select from the presented choices!"); selectionSuccess = false; } } else // If the user decided to automatically keep oldest or newest file, the selection was successful. // Either the oldest or the newest file were selected, or if not, all files were selected to be kept. selectionSuccess = true; if (selectionSuccess) { // Change the buffer list of which files to keep or don't if (choice >= 1) { if (toKeep.Contains(choice)) toKeep.Remove(choice); else toKeep.Add(choice); selectionSuccess = false; // Let the user make further changes decided = false; } else if (choice == -2) { toKeep.Clear(); decided = true; } else decided = (choice == 0); // If -1, tell that the user hasn't decided } } toKeep.Sort(); return decided; }