コード例 #1
0
        public long AddAfter(PathEntry node, PathEntry entry, long nodePosition = -1)
        {
            // If we don't know where the record is, search for it
            if (nodePosition == -1)
                GetRecord(node.Path, out node, out nodePosition);

            entry.PrevRecord = nodePosition;
            long insertedPosition = WriteRecord(entry);

            if (node.NextRecord != -1)
            {
                // If there is a record after 'node', we need to insert 'entry' before it
                // So A -> B -> C, insert D after B results in: A -> B -> D -> C (and of course the backwards links.)
                PathEntry nextRecord = new PathEntry();
                GetRecordAt(node.NextRecord, out nextRecord);

                nextRecord.PrevRecord = insertedPosition;
                WriteRecordAt(nextRecord, node.NextRecord);

                entry.NextRecord = node.NextRecord;
                WriteRecordAt(entry, insertedPosition);
            }

            node.NextRecord = insertedPosition;
            WriteRecordAt(node, nodePosition);

            return insertedPosition;
        }
コード例 #2
0
        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;
        }
コード例 #3
0
        static void ReadFileSizes(string directory, ref List<string> subfolderList)
        {
            if (Verbose)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Reading contents of " + directory);
                Console.ResetColor();
            }

            try
            {
                int insertIndex = 0;
                foreach (string path in Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly))
                {
                    string relativePath = Path.GetFullPath(path).Replace(Directory.GetCurrentDirectory(), String.Empty).TrimStart('\\');

                    // Skip some files which should not be access by the program
                    if (Path.GetFullPath(path) == SizesFile.Stream.Name || Path.GetFullPath(path) == PathsFile.Stream.Name
                        || Path.GetFullPath(path) == HashesFile.Stream.Name || Path.GetFullPath(path) == FilesToRemove.Name
                        || Path.GetFullPath(path) == ((FileStream)DuplicateFileLog.BaseStream).Name)
                        continue;

                    // Skip files if they are in a Subversion structure
                    // SVN saves a "pristine" copy of every file, and this makes every SVNd file to be marked as duplicate.
                    if (relativePath.Contains(".svn\\pristine") || relativePath.Contains(".svn\\entries")
                        || relativePath.Contains(".svn\\format"))
                        continue;

                    try
                    {
                        if (Directory.Exists(relativePath))
                        {
                            // If it is a directory, add it to the list of subfolders to check later on
                            if (Verbose)
                                Console.WriteLine(relativePath + " is a subfolder.");

                            // Add the found subfolders to the beginning of the list, but keep their natural order
                            subfolderList.Insert(++insertIndex, relativePath);
                        }
                        else if (File.Exists(relativePath))
                        {
                            if (Verbose)
                                Console.Write("Measuring " + relativePath + "...");

                            // If it is a file, register its size and the count for its size
                            FileInfo fi = new FileInfo(relativePath);

                            try
                            {
                                SizeEntry entry = new SizeEntry();
                                long position = 0;
                                bool known = SizesFile.GetRecord((ulong)fi.Length, out entry, out position);
                                entry.Size = (ulong)fi.Length;
                                if (!known)
                                {
                                    // Need to reset the entry's count because GetRecord gives
                                    // undefined value if the entry is not found.
                                    entry.Count = 0;
                                    ++SizeCount;

                                    // The new size record currently has no associated PathEntry records in the path file.
                                    entry.FirstPath = -1;
                                    entry.LastPath = -1;
                                    entry.HashEntry = -1;
                                }
                                entry.Count++;

                                // Also register its path
                                PathEntry pathRec = new PathEntry(relativePath);
                                long pathWrittenPosition;
                                if (entry.LastPath != -1)
                                {
                                    PathEntry previousLastEntry = new PathEntry();
                                    PathsFile.GetRecordAt(entry.LastPath, out previousLastEntry);
                                    pathWrittenPosition = PathsFile.AddAfter(previousLastEntry, pathRec, entry.LastPath);
                                }
                                else
                                {
                                    pathWrittenPosition = PathsFile.WriteRecord(pathRec);

                                    entry.FirstPath = pathWrittenPosition;
                                }

                                entry.LastPath = pathWrittenPosition;
                                SizesFile.WriteRecord(entry);

                                if (Verbose)
                                    Console.WriteLine(" Size: " + fi.Length + " bytes.");

                                ++FileCount;
                                VisualGlyph(FileCount);
                            }
                            catch (Exception ex)
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("There was an error registering " + relativePath + " in the databank.");
                                Console.ResetColor();
                                Console.WriteLine(ex.Message);

                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("This indicates an error with the databank. Execution cannot continue.");
                                Console.ResetColor();
                                Console.ReadLine();
                                Environment.Exit(1);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine("The path " + relativePath + " could not be accessed, because:");
                        Console.ResetColor();
                        Console.WriteLine(ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("The directory " + directory + " could not be accessed, because:");
                Console.ResetColor();
                Console.WriteLine(ex.Message);
            }

            subfolderList.Remove(directory);
        }
コード例 #4
0
        static void Main(string[] args)
        {
            Console.WriteLine("Duplicate Destroyer");
            Console.WriteLine("'Devastating Desert'");
            Console.WriteLine("Licenced under Tiny Driplet Licence (can be found at cloudchiller.net)");
            Console.WriteLine("Copyright, Copydrunk, Copypone (c) 2012-2014, Cloud Chiller");
            Console.WriteLine();

            if (args.Contains("-h"))
            {
                Console.WriteLine("HELP:");
                Console.WriteLine("-h       Show this help text");
                Console.WriteLine("-v       Verbose mode");
                Console.WriteLine("-d       Dry run/discovery - Only check for duplicates, but don't actually remove them");
                Console.WriteLine("-o       Automatically keep the OLDEST of the files");
                Console.WriteLine("-n       Automatically keep the NEWEST of the files");
                Console.WriteLine();
                Console.WriteLine("Omitting both -o and -n results in the user being queried about which file to keep.");
                Console.WriteLine("Using both -o and -n throws an error.");
                Console.WriteLine();

                Environment.Exit(0);
            }

            Verbose = args.Contains("-v");
            DryRun = args.Contains("-d");
            AutoOldest = args.Contains("-o");
            AutoNewest = args.Contains("-n");
            SizeCount = 0;
            FileCount = 0;

            if (AutoOldest == true && AutoNewest == true)
            {
                Console.WriteLine("ERROR: Conflicting arguments.");
                Console.WriteLine("Please use either -o or -n, not both.");
                Console.WriteLine();

                Environment.Exit(3);
            }

            FileStream SizesFileStream = null;
            FileStream PathsFileStream = null;
            FileStream HashesFileStream = null;
            FileStream DuplicateLogFileStream = null;
            try
            {
                SizesFileStream = new FileStream(".dd_sizes", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
                SizesFileStream.SetLength(0);

                PathsFileStream = new FileStream(".dd_files", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
                PathsFileStream.SetLength(0);

                HashesFileStream = new FileStream(".dd_hashes", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
                HashesFileStream.SetLength(0);

                FilesToRemove = new FileStream(".dd_remove", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
                FilesToRemove.SetLength(0);

                DuplicateLogFileStream = new FileStream("duplicates_" + DateTime.Now.ToString().Replace(":", "_") + ".log", FileMode.OpenOrCreate,
                    FileAccess.Write, FileShare.None);
                DuplicateLogFileStream.SetLength(0);
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Was unable to create the program's datafiles.");
                Console.ResetColor();
                Console.WriteLine("Please make sure the folder " + Directory.GetCurrentDirectory() + " is writable.");
                Console.WriteLine("The following error happened: " + ex.Message);

                Environment.Exit(1);
            }

            SizesFile = new SizeFile(SizesFileStream);
            PathsFile = new PathFile(PathsFileStream);
            HashesFile = new HashFile(HashesFileStream);
            DuplicateFileLog = new StreamWriter(DuplicateLogFileStream);

            FileRemoveException = false;
            TargetDirectory = Directory.GetCurrentDirectory();

            {
                Console.Write("Counting files and measuring sizes... " + (Verbose ? "\n" : String.Empty));
                List<string> Subfolders = new List<string>();
                Subfolders.Add(TargetDirectory);
                while (Subfolders.Count != 0)
                {
                    // Read the files in the subfolders.
                    ReadFileSizes(Subfolders[0], ref Subfolders);
                    // The on-the-fly detected subfolders are added to the list while reading.
                }
                SizesFile.Stream.Flush(true);
                PathsFile.Stream.Flush(true);
                Console.WriteLine((!Verbose ? "\n" : String.Empty) + FileCount + " files found.");
                Console.WriteLine();
            }

            {
                Console.Write("Analysing sizes... " + (Verbose ? "\n" : String.Empty));
                AnalyseSizes();
                SizesFile.DeleteRecord(0); // 0-byte files are ALWAYS duplicates of each other...
                SizesFile.Stream.Flush(true);
                PathsFile.Stream.Flush(true);
                Console.WriteLine((!Verbose ? "\n" : String.Empty) + SizeCount + " unique file size found for " + FileCount + " files.");
                Console.WriteLine();
            }

            //{
            //    // Remove entries from the PathsFile physically which were logically removed (marked deleted) in the previous step
            //    if (Verbose)
            //    {
            //        Console.WriteLine("Removing knowledge about files I don't need to check.");
            //        Console.WriteLine("(This is an internal maintenance run to speed up further operations.)");
            //    }
            //    PathsFile.Consolidate(new SizeFileAligner(Program.AlignSizeFilePointers));
            //    PathsFile.Stream.Flush(true);
            //    if (Verbose)
            //        Console.WriteLine();
            //}

            {
                Console.Write("Reading file contents... " + (Verbose ? "\n" : String.Empty));
                MD5CryptoServiceProvider mcsp = new MD5CryptoServiceProvider();
                ulong _hashesReadCount = 0;
                foreach (SizeEntry duplicated_size in SizesFile.GetRecords())
                {
                    if (Verbose)
                    {
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        Console.WriteLine("Reading files of " + duplicated_size.Size + " size");
                        Console.ResetColor();
                    }
                    // For each size entry, iterate the path list
                    PathEntry entry;
                    long position = duplicated_size.FirstPath;

                    while (position != -1)
                    {
                        if (PathsFile.GetRecordAt(position, out entry))
                        {
                            string hash = String.Empty;
                            try
                            {
                                hash = CalculateHash(ref mcsp, entry.Path);
                                ++_hashesReadCount;
                            }
                            catch (Exception ex)
                            {
                                Console.ForegroundColor = ConsoleColor.Yellow;
                                Console.WriteLine("The file " + entry.Path + " could not be checked, because:");
                                Console.ResetColor();
                                Console.WriteLine(ex.Message);
                            }

                            if (!String.IsNullOrEmpty(hash))
                                entry.Hash = hash;
                            else
                                // Mark this record "deleted" so it won't be checked for hash duplication
                                entry.Deleted = true;

                            PathsFile.WriteRecordAt(entry, position);
                            VisualGlyph(_hashesReadCount);
                            position = entry.NextRecord; // Jump to the next record in the chain
                        }
                    }
                }
                PathsFile.Stream.Flush(true);
                Console.WriteLine((!Verbose ? "\n" : String.Empty) + _hashesReadCount + " files read.");
            }

            {
                Console.Write("Searching for true duplication... " + (Verbose ? "\n" : String.Empty));
                long UniqueHashCount, DuplicatedFileCount;
                AnalyseFilelist(out UniqueHashCount, out DuplicatedFileCount);
                HashesFile.Stream.Flush(true);
                Console.WriteLine((!Verbose ? "\n" : String.Empty) + UniqueHashCount + " unique content duplicated across " + DuplicatedFileCount + " files.");
                Console.WriteLine();

                Console.WriteLine();
                Console.WriteLine("Please select which files you wish to remove.");
                long dealtWithCount = 0;
                while (dealtWithCount < UniqueHashCount)
                {
                    // We go through every hash entry and prompt the user to decide which file to remove
                    HashesFile.Stream.Seek(0, SeekOrigin.Begin);
                    SizeHashEntry she = new SizeHashEntry();
                    PathEntry etr = new PathEntry();
                    long pos = 0;

                    while (pos != -1)
                    {
                        // Get the next duplicated hash
                        pos = HashesFile.GetNextRecord(out she);
                        if (pos != -1)
                        {
                            // Iterate the hash pointers...
                            foreach (HashPointers ptr in she.Pointers)
                            {
                                if (ptr.FileEntries.Count == 0)
                                    continue;

                                // Select which file the user wants to keep
                                List<int> fileIDsToKeep;
                                bool userDecided = SelectFilesToKeep(ptr, out fileIDsToKeep);

                                if (!DryRun)
                                {
                                    if (!userDecided)
                                        Console.WriteLine("Didn't make a decision. You will be asked later on.");
                                    else
                                    {
                                        ++dealtWithCount;

                                        if (fileIDsToKeep.Count == ptr.FileEntries.Count)
                                            Console.WriteLine("Selected to keep all files.");
                                        else if (fileIDsToKeep.Count > 0)
                                        {
                                            if (!AutoOldest && !AutoNewest)
                                            {
                                                foreach (int id in fileIDsToKeep)
                                                {
                                                    Console.Write("Selected to  ");
                                                    Console.ForegroundColor = ConsoleColor.White;
                                                    Console.Write("KEEP");
                                                    Console.ResetColor();
                                                    Console.Write("  ");

                                                    PathsFile.GetRecordAt(ptr.FileEntries[id - 1], out etr);
                                                    Console.WriteLine(etr.Path);
                                                }

                                                foreach (int id in Enumerable.Range(1, ptr.FileEntries.Count).Except(fileIDsToKeep))
                                                {
                                                    Console.Write("Selected to ");
                                                    Console.ForegroundColor = ConsoleColor.Red;
                                                    Console.Write("DELETE");
                                                    Console.ResetColor();
                                                    Console.Write(" ");

                                                    PathsFile.GetRecordAt(ptr.FileEntries[id - 1], out etr);
                                                    Console.WriteLine(etr.Path);

                                                    byte[] pathLine = Encoding.UTF8.GetBytes(etr.Path + StreamWriter.Null.NewLine);
                                                    FilesToRemove.Write(pathLine, 0, pathLine.Length);
                                                }
                                            }
                                        }
                                        else if (fileIDsToKeep.Count == 0)
                                        {
                                            Console.WriteLine("All files will be deleted:");

                                            foreach (long offset in ptr.FileEntries)
                                            {
                                                PathsFile.GetRecordAt(offset, out etr);
                                                Console.WriteLine(etr.Path);

                                                byte[] pathLine = Encoding.UTF8.GetBytes(etr.Path + StreamWriter.Null.NewLine);
                                                FilesToRemove.Write(pathLine, 0, pathLine.Length);
                                            }
                                        }

                                        FilesToRemove.Flush();
                                    }
                                }
                                else
                                    ++dealtWithCount;
                            }
                        }
                    }
                }
                Console.WriteLine();
            }

            {
                Console.Write("Removing all scheduled files... " + (Verbose ? "\n" : String.Empty));
                uint _filesRemoved = 0;
                if (DryRun)
                    Console.WriteLine("Won't remove files in dry-run/discovery mode.");
                else
                {
                    FilesToRemove.Seek(0, SeekOrigin.Begin);
                    string path;

                    if (FilesToRemove.Length > 0) // Only if there are files to be removed
                    {
                        using (StreamReader sr = new StreamReader(FilesToRemove))
                        {
                            path = sr.ReadLine();
                            if (RemoveFile(path))
                                ++_filesRemoved;
                        }
                    }
                }
                Console.WriteLine((!Verbose ? "\n" : String.Empty) + _filesRemoved + " files deleted successfully.");
            }

            SizesFileStream.Dispose();
            PathsFileStream.Dispose();
            HashesFileStream.Dispose();
            //FilesToRemove.Dispose();
            DuplicateFileLog.Dispose();

            // Cleanup
            //File.Delete(".dd_sizes");
            //File.Delete(".dd_files");
            //File.Delete(".dd_hashes");
            //File.Delete(".dd_remove");

            if (FileRemoveException)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("One or more files could not be deleted.");
                Console.ResetColor();
            }

            Console.WriteLine("Press ENTER to exit...");
            Console.ReadLine();

            if (FileRemoveException)
                Environment.Exit(2);
            else
                Environment.Exit(0);
        }
コード例 #5
0
        internal IEnumerable<PathEntry> GetRecords(bool skipDeleted = false)
        {
            this.Stream.Seek(0, SeekOrigin.Begin);
            long position = 0;

            using (BinaryReader br = new BinaryReader(this.Stream, Encoding.UTF8, true))
                while (position < this.Stream.Length)
                {
                    this.Stream.Seek(position, SeekOrigin.Begin);
                    PathEntry record = new PathEntry();

                    record.Deleted = br.ReadBoolean(); // 1
                    record.PrevRecord = br.ReadInt64(); // 8
                    record.NextRecord = br.ReadInt64(); // 8
                    record.PathLength = br.ReadUInt16(); // Path length (2)
                    byte[] path_bytes = br.ReadBytes(record.PathLength); // n
                    record.Path = Encoding.UTF8.GetString(path_bytes);
                    record.Hash = Encoding.UTF8.GetString(br.ReadBytes(32)); // 32

                    position = this.Stream.Position;

                    if (!skipDeleted || !record.Deleted)
                        yield return record;
                }

            yield break;
        }
コード例 #6
0
        public long WriteRecord(PathEntry rec)
        {
            long pos = -1;
            this.Stream.Seek(0, SeekOrigin.End);
            using (BinaryWriter bw = new BinaryWriter(this.Stream, Encoding.UTF8, true))
            {
                pos = bw.BaseStream.Position;
                bw.Write(rec.Deleted); // 1
                bw.Write(rec.PrevRecord); // 8
                bw.Write(rec.NextRecord); // 8
                bw.Write(rec.PathLength); // 2
                bw.Write(Encoding.UTF8.GetBytes(rec.Path)); // n
                bw.Write(Encoding.UTF8.GetBytes(rec.Hash)); // 32
            }
            this.Stream.Flush();

            return pos;
        }
コード例 #7
0
 public void WriteRecordAt(PathEntry rec, long pos)
 {
     this.Stream.Seek(pos, SeekOrigin.Begin);
     using (BinaryWriter bw = new BinaryWriter(this.Stream, Encoding.UTF8, true))
     {
         bw.Write(rec.Deleted); // 1
         bw.Write(rec.PrevRecord); // 8
         bw.Write(rec.NextRecord); // 8
         bw.Write(rec.PathLength); // 2
         bw.Write(Encoding.UTF8.GetBytes(rec.Path)); // n
         bw.Write(Encoding.UTF8.GetBytes(rec.Hash)); // 32
     }
     this.Stream.Flush();
 }
コード例 #8
0
        public IEnumerable<PathEntry> GetRecords(string path, long pos = -1, bool traverseBackwards = false)
        {
            PathEntry entry = new PathEntry();

            // Try to find the record given for traversal
            bool recordFound;
            if (pos == -1)
                recordFound = GetRecord(path, out entry, out pos);
            else
            {
                recordFound = GetRecordAt(pos, out entry);

                // GetRecordAt doesn't check just gives the record as result.
                // Bail out if the given record is not the right one we searched for...
                if (entry.Path != path)
                    recordFound = false;
            }

            // Get the rest of the records and given them in the enumerable
            if (recordFound)
            {
                yield return entry;

                long nextPositionToRead;
                if (traverseBackwards)
                    nextPositionToRead = entry.PrevRecord;
                else
                    nextPositionToRead = entry.NextRecord;

                while (nextPositionToRead != -1)
                {
                    if (GetRecordAt(nextPositionToRead, out entry))
                    {
                        yield return entry;

                        if (traverseBackwards)
                            nextPositionToRead = entry.PrevRecord;
                        else
                            nextPositionToRead = entry.NextRecord;
                    }
                    else
                        break;
                }
            }

            yield break;
        }
コード例 #9
0
        public bool GetRecordAt(long position, out PathEntry record)
        {
            record = new PathEntry();

            if (position > this.Stream.Length)
                throw new ArgumentOutOfRangeException("Position out of stream bounds.");

            using (BinaryReader br = new BinaryReader(this.Stream, Encoding.UTF8, true))
            {
                try
                {
                    this.Stream.Seek(position, SeekOrigin.Begin);

                    record.Deleted = br.ReadBoolean(); // 1
                    record.PrevRecord = br.ReadInt64(); // 8
                    record.NextRecord = br.ReadInt64(); // 8
                    record.PathLength = br.ReadUInt16(); // Path length (2)
                    byte[] path_bytes = br.ReadBytes(record.PathLength); // n
                    record.Path = Encoding.UTF8.GetString(path_bytes);
                    record.Hash = Encoding.UTF8.GetString(br.ReadBytes(32)); // 32
                }
                catch (Exception e)
                {
                    record.Deleted = true;
                    //record.PrevRecord = -1;
                    //record.NextRecord = -1;
                    //record.PathLength = 0;
                    record.Path = e.Message;
                    //record.Hash = String.Empty;

                    return false;
                }
            }

            return true;
        }
コード例 #10
0
        public bool GetRecord(string path, out PathEntry record, out long position)
        {
            record = new PathEntry();
            position = 0;

            if (this.Stream.Length == 0)
                return false;

            // Search for the record linearly
            this.Stream.Seek(0, SeekOrigin.Begin);
            bool found = false;
            using (BinaryReader br = new BinaryReader(this.Stream, Encoding.UTF8, true))
            {
                while (this.Stream.Position < this.Stream.Length && !found)
                {
                    // Read every record and try to make out if it is the searched one.
                    position = this.Stream.Position;
                    record.Deleted = br.ReadBoolean(); // 1
                    record.PrevRecord = br.ReadInt64(); // 8
                    record.NextRecord = br.ReadInt64(); // 8
                    record.PathLength = br.ReadUInt16(); // Path length.
                    byte[] path_bytes = br.ReadBytes(record.PathLength); // n
                    record.Path = Encoding.UTF8.GetString(path_bytes);
                    record.Hash = Encoding.UTF8.GetString(br.ReadBytes(32)); // 32

                    if (record.Path == path && !record.Deleted)
                        found = true;
                }
            }

            return found;
        }
コード例 #11
0
        public void DeleteRecord(PathEntry rec, long position)
        {
            // Check if the given record is the one we want to delete.
            PathEntry already = new PathEntry();
            if (!GetRecordAt(position, out already))
                throw new ArgumentOutOfRangeException("There is no record at the given position.");

            if (rec.Path != already.Path)
                throw new ArgumentException("The record at the given position does not match the given record.");

            // When a record is deleted, the broken chain that was going through them has to be reconnected
            // A -> B -> C with B's deletion becomes A -> C
            if (already.PrevRecord != -1)
            {
                PathEntry prevRec = new PathEntry();
                GetRecordAt(already.PrevRecord, out prevRec);

                // If there is something after the deleted one, it comes after the previous one
                if (already.NextRecord != -1)
                    prevRec.NextRecord = already.NextRecord;
                else
                    prevRec.NextRecord = -1;

                WriteRecordAt(prevRec, already.PrevRecord);
            }

            if (already.NextRecord != -1)
            {
                PathEntry nextRec = new PathEntry();
                GetRecordAt(already.NextRecord, out nextRec);

                // If there is something before the deleted one, it comes before the next one
                if (already.PrevRecord != -1)
                    nextRec.PrevRecord = already.PrevRecord;
                else
                    nextRec.PrevRecord = -1;

                WriteRecordAt(nextRec, already.NextRecord);
            }

            // Mark the current record deleted and break its chains
            already.PrevRecord = -1;
            already.NextRecord = -1;
            already.Deleted = true;

            WriteRecordAt(already, position);
        }
コード例 #12
0
        public void DeleteRecord(string path)
        {
            PathEntry rec = new PathEntry();
            long pos = 0;

            if (GetRecord(path, out rec, out pos))
                DeleteRecord(rec, pos);
        }