diff --git a/Files.cs b/Files.cs index 88fd7de..1392ffd 100644 --- a/Files.cs +++ b/Files.cs @@ -25,8 +25,11 @@ public bool ToCopy(ref Dictionary destinationDictionary, reason = Reason.CopyNotThere; return true; } + // Remove from destination dictionary to speed up the search for files to remove + destinationDictionary.Remove(relativePath); // Skip if directory - if((e.fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) return false; + if((e.fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) + return false; // Check size if(fileInfo.Length != e.fileInfo.Length) { reason = Reason.CopyDifferentSize; @@ -47,7 +50,9 @@ public bool ToCopy(ref Dictionary destinationDictionary, stream1.Close(); stream2.Close(); bool toCopy = (byte1 != byte2); - if(toCopy) reason = Reason.CopyDifferentContent; + if(toCopy) { + reason = Reason.CopyDifferentContent; + } return toCopy; } catch(Exception exc) { diff --git a/Logger.cs b/Logger.cs index a45da66..85c0bd6 100644 --- a/Logger.cs +++ b/Logger.cs @@ -4,8 +4,6 @@ public class Logger { private static string barFull = "█", barEmpty = " "; private static string? logfilename; private static StreamWriter? logstream; - private static List logTasks = new List(); - private static int ticket = 0; /// /// Function to initialize the file logging /// () @@ -29,11 +27,9 @@ public static void ReinitializeLogging() { /// /// Function to close the log stream /// - public static async void TerminateLogging() { + public static void TerminateLogging() { if(logstream != null) { - if(logTasks.Count != 0) await logTasks[logTasks.Count - 1]; logstream.Close(); - logTasks = new List(); } } /// @@ -41,79 +37,63 @@ public static async void TerminateLogging() { /// () /// /// The message to output - public static async void Success(string message) { - int t = ticket++; + public static void Success(string message) { string time = TimeString(); - await logTasks.ElementAt(t); - logTasks.Append(Task.Run(() => { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.Write(time); - Console.ForegroundColor = ConsoleColor.Green; - Console.Write("(Success) "); - Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine(message); - Console.ResetColor(); - if(logstream != null) logstream.WriteLineAsync(time + "(Success) " + message); - })); + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.Write(time); + Console.ForegroundColor = ConsoleColor.Green; + Console.Write("(Success) "); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(message); + Console.ResetColor(); + if(logstream != null) logstream.WriteLineAsync(time + "(Success) " + message); } /// /// Function to output an info message /// () /// /// The message to output - public static async void Info(string message) { - int t = ticket++; + public static void Info(string message) { string time = TimeString(); - await logTasks.ElementAt(t); - logTasks.Append(Task.Run(() => { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.Write(time); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write("(Info) "); - Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine(message); - Console.ResetColor(); - })); + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.Write(time); + Console.ForegroundColor = ConsoleColor.Cyan; + Console.Write("(Info) "); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(message); + Console.ResetColor(); } /// /// Function to output a warning message /// () /// /// The message to output - public static async void Warning(string message) { - int t = ticket++; + public static void Warning(string message) { string time = TimeString(); - await logTasks.ElementAt(t); - logTasks.Append(Task.Run(() => { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.Write(time); - Console.ForegroundColor = ConsoleColor.Yellow; - Console.Write("(Warning) "); - Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine(message); - Console.ResetColor(); - if(logstream != null) logstream.WriteLineAsync(time + "(Warning) " + message); - })); + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.Write(time); + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write("(Warning) "); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(message); + Console.ResetColor(); + if(logstream != null) logstream.WriteLineAsync(time + "(Warning) " + message); } /// /// Function to output an error message /// () /// /// The message to output - public static async void Error(string message) { - int t = ticket++; + public static void Error(string message) { string time = TimeString(); - await logTasks.ElementAt(t); - logTasks.Append(Task.Run(() => { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.Write(time); - Console.ForegroundColor = ConsoleColor.Red; - Console.Write("(Error) "); - Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine(message); - Console.ResetColor(); - if(logstream != null) logstream.WriteLineAsync(time + "(Error) " + message); - })); + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.Write(time); + Console.ForegroundColor = ConsoleColor.Red; + Console.Write("(Error) "); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(message); + Console.ResetColor(); + if(logstream != null) logstream.WriteLineAsync(time + "(Error) " + message); } /// /// Function to clear the last console line @@ -123,17 +103,11 @@ public static async void Error(string message) { public static void RemoveLine(Int16 line = 1) { Int32 currentLineCursor = Console.CursorTop; Console.SetCursorPosition(0, currentLineCursor - line); - for (Int32 i = 0; i < Console.WindowWidth; i++) + for(Int32 i = 0; i < Console.WindowWidth; i++) Console.Write(" "); Console.SetCursorPosition(0, currentLineCursor - line); } /// - /// Function to output the hour - /// - public static void WriteHour() { - Console.Write(TimeString()); - } - /// /// Function to get the string time /// /// The string time, hh:mm:ss.msmsms @@ -159,13 +133,15 @@ public static string LongTimeString() { } /// /// Function to print a progress bar string - /// (, ) + /// (, , , ) /// - /// The current stage - /// The total - public static void ProgressBar(UInt64 current, UInt64 total) { + /// The current size + /// The total size + /// The current number of elements + /// The total number of elements + public static void ProgressBar(UInt64 currentSize, UInt64 totalSize, Int32 currentElements, Int32 totalElements) { string bar = "["; - Int16 percent = (Int16)((float)current / total * 100); + Int16 percent = (Int16)((float)currentSize / totalSize * 100); for(Int16 i = 1; i <= percent; i++) { bar += barFull; } @@ -177,7 +153,8 @@ public static void ProgressBar(UInt64 current, UInt64 total) { } Console.BackgroundColor = ConsoleColor.DarkGray; Console.Write(bar); - bar = "] " + percent.ToString() + "% (" + HumanReadableSize(current) + "/" + HumanReadableSize(total) + ")"; + bar = "] " + percent.ToString() + "% (" + HumanReadableSize(currentSize) + "/" + HumanReadableSize(totalSize) + ") (" + + currentElements + "/" + totalElements + ")"; Console.ResetColor(); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine(bar); @@ -189,7 +166,7 @@ public static void ProgressBar(UInt64 current, UInt64 total) { /// /// The reason /// The name of the file - public static void InfoReason(Reason reason, string file) { + public static void InfoReason(Reason reason, string file, UInt64? size = null) { string line = ""; switch(reason) { case Reason.CopyNotThere: @@ -205,7 +182,7 @@ public static void InfoReason(Reason reason, string file) { line = "Removing: "; break; } - Info(line + file); + Info(line + file + " (" + ((size != null) ? HumanReadableSize((UInt64)size) : "folder") + ")"); } /// /// Function to print a message of the file that has been copied @@ -213,7 +190,7 @@ public static void InfoReason(Reason reason, string file) { /// /// The reason /// The name of the file - public static void SuccessReason(Reason reason, string file) { + public static void SuccessReason(Reason reason, string file, UInt64? size = null) { string line = ""; switch(reason) { case Reason.CopyNotThere: @@ -229,7 +206,7 @@ public static void SuccessReason(Reason reason, string file) { line = "Removed: "; break; } - Success(line + file); + Success(line + file + " (" + ((size != null) ? HumanReadableSize((UInt64)size) : "folder") + ")"); } /// /// Function to print a progress bar string diff --git a/Makefile b/Makefile index 4c30448..d2da1cb 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .RECIPEPREFIX=> -VERSION=1.5.6 +VERSION=1.6.0 default: > clear @@ -24,5 +24,4 @@ run-dotnet-debug: > dotnet ./transfer/docker/debug/backup-utility.dll -- -s test/source -d test/destination -r test/removed -t 100 -e extensions.txt -l -b run-dotnet-release: -> dotnet publish -c debug -> ./transfer/docker/release/linux-x64/backup-utility -s test/source -d test/destination -r test/removed -l \ No newline at end of file +> ./transfer/docker/release/linux-x64/backup-utility -s test/source -d test/destination -r test/removed -l -e extensions.txt \ No newline at end of file diff --git a/Program.cs b/Program.cs index 223d9e8..09ec98b 100644 --- a/Program.cs +++ b/Program.cs @@ -6,8 +6,8 @@ public class Program { private static EnumerationOptions enumOptions = new EnumerationOptions(); static async Task Main(string[] args) { // Version - string version = "1.5.6"; - // Lists + string version = "1.6.0"; + // Lists and dictionaries string[] sourceList = new string[0], destinationList = new string[0], extensionList = new string[0]; Dictionary sourceInfoDictionary = new Dictionary(); Dictionary destinationInfoDictionary = new Dictionary(); @@ -51,7 +51,7 @@ static async Task Main(string[] args) { Logger.Warning("Destination folder does not exist, attempting creation"); try { Directory.CreateDirectory(arguments.destination); - Logger.Success("Destination directory creation succeeded"); + Logger.Success("Destination directory created"); } catch(Exception e) { Logger.Error("Destination directory creation failed, error: " + e); @@ -68,7 +68,7 @@ static async Task Main(string[] args) { Logger.Warning("Folder for removed files does not exist, attempting creation"); try { Directory.CreateDirectory(arguments.removed); - Logger.Success("Directory for removed files creation succeeded"); + Logger.Success("Directory for removed files created"); } catch(Exception e) { Logger.Error("Directory for removed files creation failed, error: " + e); @@ -97,7 +97,7 @@ static async Task Main(string[] args) { } try { extensionList = File.ReadAllLines(arguments.extensions); - Logger.Success("Extension list succesfully retrieved from file"); + Logger.Success("Extension list retrieved from file"); } catch(Exception e) { Logger.Error("Could not retrieve extension list from file, error: " + e); @@ -116,7 +116,7 @@ static async Task Main(string[] args) { Logger.Warning("Folder for backups " + backupFolder + " does not exist, attempting creation"); try { Directory.CreateDirectory(backupFolder); - Logger.Success("Succesfully created folder for backups"); + Logger.Success("Created folder for backups"); } catch(Exception e) { Logger.Error("Could not create folder for backups, error: " + e); @@ -130,15 +130,19 @@ static async Task Main(string[] args) { // Timestamp timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds() + arguments.time; // Scan folders + Logger.Info("Starting source and destination folders scan..."); Task sourceTask = scanFolder(arguments.source, true); Task destinationTask = scanFolder(arguments.destination, false); sourceList = await sourceTask; destinationList = await destinationTask; + Logger.Success("Source and destination folders scanned: " + sourceList.Length + " and " + destinationList.Length + " items found"); // Build file info + Logger.Info("Building source and destination file info dictionaries..."); Task> sourceDictionaryTask = buildInfoDictionary(sourceList, arguments.source, true); - Task> destinationDictionaryTask = buildInfoDictionary(sourceList, arguments.destination, false); + Task> destinationDictionaryTask = buildInfoDictionary(destinationList, arguments.destination, false); sourceInfoDictionary = await sourceDictionaryTask; - destinationInfoDictionary = await sourceDictionaryTask; + destinationInfoDictionary = await destinationDictionaryTask; + Logger.Success("Source and destination file info dictionaries built"); sourceList = new string[0]; destinationList = new string[0]; // Items to copy @@ -188,10 +192,12 @@ static async Task Main(string[] args) { filesCopied = 0; foldersCopied = 0; sizeCopied = 0; for(Int32 i = 0; i < length; i++) { DirectoryEntry e = toCopyList[i]; - Logger.InfoReason(e.reason, e.relativePath); - Logger.ProgressBar(sizeCopied, sizeToCopy); + bool isDirectory = (e.fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory; + UInt64 fileSize = (isDirectory ? 0 : (UInt64)e.fileInfo.Length); + Logger.InfoReason(e.reason, e.relativePath, (isDirectory ? null : fileSize)); + Logger.ProgressBar(sizeCopied, sizeToCopy, foldersCopied + filesCopied, foldersToCopy + filesToCopy); string destinationPath = arguments.destination + Path.DirectorySeparatorChar + e.relativePath; - if((e.fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { // Copy folder + if(isDirectory) { // Copy folder try { Directory.CreateDirectory(destinationPath); foldersCopied++; @@ -199,6 +205,7 @@ static async Task Main(string[] args) { Logger.SuccessReason(e.reason, e.relativePath); } catch(Exception exc) { + foldersToCopy--; Logger.RemoveLine(); Logger.RemoveLine(); Logger.Error("Could not copy: " + e.relativePath + ", error: " + exc); } @@ -207,11 +214,14 @@ static async Task Main(string[] args) { try { File.Copy(e.fileInfo.FullName, destinationPath, true); filesCopied++; - sizeCopied += (UInt64)e.fileInfo.Length; + sizeCopied += fileSize; Logger.RemoveLine(); Logger.RemoveLine(); - Logger.SuccessReason(e.reason, e.relativePath); + Logger.SuccessReason(e.reason, e.relativePath, fileSize); } catch(Exception exc) { + filesToCopy--; + try {sizeToCopy -= fileSize;} + catch(Exception) {} Logger.RemoveLine(); Logger.RemoveLine(); Logger.Error("Could not copy: " + e.relativePath + ", error: " + exc); } @@ -220,13 +230,13 @@ static async Task Main(string[] args) { toCopyList = new DirectoryEntry[0]; // Remove files length = toRemoveFileList.Length; - filesRemoved = 0; sizeRemoved = 0; + filesRemoved = 0; sizeRemoved = 0; foldersRemoved = 0; for(Int32 i = 0; i < length; i++) { DirectoryEntry e = toRemoveFileList[i]; UInt64 fileSize = (UInt64)e.fileInfo.Length; bool err; - Logger.InfoReason(e.reason, e.relativePath); - Logger.ProgressBar(sizeRemoved, sizeToRemove); + Logger.InfoReason(e.reason, e.relativePath, fileSize); + Logger.ProgressBar(sizeRemoved, sizeToRemove, foldersRemoved + filesRemoved, foldersToRemove + filesToRemove); if(arguments.removed != null) { // Move string newPath = arguments.removed + Path.DirectorySeparatorChar + e.relativePath; try { @@ -261,13 +271,17 @@ static async Task Main(string[] args) { sizeRemoved += fileSize; filesRemoved++; Logger.RemoveLine(); Logger.RemoveLine(); - Logger.SuccessReason(e.reason, e.relativePath); + Logger.SuccessReason(e.reason, e.relativePath, fileSize); + } + else { + filesToRemove--; + try {sizeToRemove -= fileSize;} + catch(Exception) {} } } toRemoveFileList = new DirectoryEntry[0]; // Remove folders length = toRemoveFolderList.Length; - foldersRemoved = 0; for(Int32 i = length - 1; i >= 0; i--) { DirectoryEntry e = toRemoveFolderList[i]; bool err; @@ -301,6 +315,7 @@ static async Task Main(string[] args) { Logger.RemoveLine(); Logger.SuccessReason(e.reason, e.relativePath); } + else foldersToRemove--; } toRemoveFolderList = new DirectoryEntry[0]; // Log copied and removed items @@ -316,7 +331,7 @@ static async Task Main(string[] args) { backupFileName = backupFolder + Path.DirectorySeparatorChar + backupFileName; try { ZipFile.CreateFromDirectory(arguments.destination, backupFileName); - Logger.Success("Succesfully created compressed backup (" + Logger.HumanReadableSize((UInt64)new FileInfo(backupFileName).Length) + ")"); + Logger.Success("Created compressed backup (" + Logger.HumanReadableSize((UInt64)new FileInfo(backupFileName).Length) + ")"); } catch(Exception e) { Logger.Error("Could not create compressed backup, error: " + e); @@ -345,10 +360,8 @@ static async Task Main(string[] args) { public static async Task scanFolder(string path, bool type) { return await Task.Run(() => { string[] array; - Logger.Info("Starting " + (type ? "source" : "destination") + " folder scan..."); try { - array = Directory.EnumerateFileSystemEntries(arguments.source, "*", enumOptions).ToArray(); - Logger.Success((type ? "Source" : "Destination") + " folder scanned: " + array.Length + " items found"); + array = Directory.EnumerateFileSystemEntries(path, "*", enumOptions).ToArray(); } catch(Exception e) { Logger.Error("Error while scanning " + (type ? "source" : "destination") + " folder: " + e); @@ -369,16 +382,14 @@ public static async Task scanFolder(string path, bool type) { public static async Task> buildInfoDictionary(string[] list, string path, bool type) { return await Task.Run>(() => { Dictionary dictionary = new Dictionary(); - Logger.Info("Building " + (type ? "source" : "destination") + " file info list..."); try { foreach(string item in list) { string relativePath = item.Substring(path.Length + 1); dictionary[relativePath] = new DirectoryEntry(item, relativePath); } - Logger.Success((type ? "Source" : "Destination") + " file info list built"); } catch(Exception e) { - Logger.Error("Error while building " + (type ? "source" : "destination") + " file info list: " + e); + Logger.Error("Error while building " + (type ? "source" : "destination") + " file info dictionary: " + e); Environment.Exit(2); } return dictionary; diff --git a/github-updater.backup-utility-cs.json b/github-updater.backup-utility-cs.json index bbd1373..dcd9b6c 100644 --- a/github-updater.backup-utility-cs.json +++ b/github-updater.backup-utility-cs.json @@ -1,7 +1,12 @@ { - "latest": "1.5.6", + "latest": "1.6.0", "keep": [], "releases": [ + { + "tag": "1.6.0", + "linux": "backup-utility-1.6.0-linux-x64.tar.gz", + "win": "backup-utility-1.6.0-win-x64.zip" + }, { "tag": "1.5.6", "linux": "backup-utility-1.5.6-linux-x64.tar.gz", diff --git a/transfer/local/options.sh b/transfer/local/options.sh index e3ebfdd..86f7157 100644 --- a/transfer/local/options.sh +++ b/transfer/local/options.sh @@ -1,4 +1,4 @@ #!/bin/bash -VERSION=1.5.6 +VERSION=1.6.0 DEBUG=0 RELEASE=1 \ No newline at end of file