From 26ea4f90b131fdb8f193541f8e8d32c45d1e573d Mon Sep 17 00:00:00 2001
From: chylex <info@chylex.com>
Date: Sun, 12 Apr 2015 23:04:47 +0200
Subject: [PATCH] Added run info class, multiple sources and debug report

---
 BackupEssentials/App.xaml.cs             |  5 +-
 BackupEssentials/Backup/BackupRunInfo.cs | 11 +++
 BackupEssentials/Backup/BackupRunner.cs  | 93 ++++++++++++++++--------
 BackupEssentials/BackupEssentials.csproj |  1 +
 4 files changed, 78 insertions(+), 32 deletions(-)
 create mode 100644 BackupEssentials/Backup/BackupRunInfo.cs

diff --git a/BackupEssentials/App.xaml.cs b/BackupEssentials/App.xaml.cs
index 44f7c1b..31a6fc7 100644
--- a/BackupEssentials/App.xaml.cs
+++ b/BackupEssentials/App.xaml.cs
@@ -37,7 +37,10 @@ namespace BackupEssentials{
                     }
                 }
 
-                if (dest.Length > 0)new BackupWindow(new BackupRunner(parser.GetValue("src",null),dest)).Show();
+                if (dest.Length > 0){
+                    BackupRunInfo info = new BackupRunInfo(parser.GetMultiValue("src"),dest);
+                    new BackupWindow(new BackupRunner(info)).Show();
+                }
                 else Application.Current.Shutdown();
             }
             else{
diff --git a/BackupEssentials/Backup/BackupRunInfo.cs b/BackupEssentials/Backup/BackupRunInfo.cs
new file mode 100644
index 0000000..e164af6
--- /dev/null
+++ b/BackupEssentials/Backup/BackupRunInfo.cs
@@ -0,0 +1,11 @@
+namespace BackupEssentials.Backup{
+    public class BackupRunInfo{
+        public readonly string[] source;
+        public readonly string destination;
+
+        public BackupRunInfo(string[] source, string destination){
+            this.source = source;
+            this.destination = destination;
+        }
+    }
+}
diff --git a/BackupEssentials/Backup/BackupRunner.cs b/BackupEssentials/Backup/BackupRunner.cs
index da8ee63..6ca805b 100644
--- a/BackupEssentials/Backup/BackupRunner.cs
+++ b/BackupEssentials/Backup/BackupRunner.cs
@@ -12,14 +12,16 @@ using System.Threading;
 
 namespace BackupEssentials.Backup{
     public class BackupRunner{
+        private static readonly bool DEBUG = false;
+
         private BackgroundWorker Worker;
-        private Tuple<string,string> WorkerData;
+        private BackupRunInfo WorkerData;
 
         public Action<object,ProgressChangedEventArgs> EventProgressUpdate;
         public Action<object,RunWorkerCompletedEventArgs> EventCompleted;
 
-        public BackupRunner(string source, string destFolder){
-            WorkerData = new Tuple<string,string>(source,destFolder);
+        public BackupRunner(BackupRunInfo info){
+            WorkerData = info;
         }
 
         public void Start(){
@@ -42,37 +44,41 @@ namespace BackupEssentials.Backup{
 
         private void WorkerDoWork(object sender, DoWorkEventArgs e){
             BackgroundWorker worker = (BackgroundWorker)sender;
-            Tuple<string,string> data = (Tuple<string,string>)e.Argument;
+            BackupRunInfo data = (BackupRunInfo)e.Argument;
 
-            string src = data.Item1, fullSrc = src;
-            string destFolder = data.Item2;
+            string[] src = data.source;
+            string srcParent = Directory.GetParent(src[0]).FullName, fullSrc = string.Join(", ",src);
+            string destFolder = data.destination;
 
             // Figure out the file and directory lists
             Dictionary<string,IOEntry> srcEntries = new Dictionary<string,IOEntry>(), dstEntries = new Dictionary<string,IOEntry>();
+            string[] updatedSrc = new string[src.Length];
 
-            if (File.GetAttributes(src).HasFlag(FileAttributes.Directory)){
-                int srcLen = src.Length;
-                foreach(string dir in Directory.GetDirectories(src,"*",SearchOption.AllDirectories))srcEntries.Add(dir.Remove(0,srcLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = dir });
-                foreach(string file in Directory.GetFiles(src,"*.*",SearchOption.AllDirectories))srcEntries.Add(file.Remove(0,srcLen),new IOEntry(){ Type = IOType.File, AbsolutePath = file });
+            int destFolderLen = destFolder.Length+1;
+            foreach(string dir in Directory.GetDirectories(destFolder,"*",SearchOption.AllDirectories))dstEntries.Add(dir.Remove(0,destFolderLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = dir });
+            foreach(string file in Directory.GetFiles(destFolder,"*.*",SearchOption.AllDirectories))dstEntries.Add(file.Remove(0,destFolderLen),new IOEntry(){ Type = IOType.File, AbsolutePath = file });
+
+            for(int a = 0; a < src.Length; a++){
+                string srcEntry = src[a];
+
+                if (File.GetAttributes(srcEntry).HasFlag(FileAttributes.Directory)){
+                    int srcLen = srcParent.Length+1;
+                    srcEntries.Add(srcEntry.Remove(0,srcLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = srcEntry });
+
+                    foreach(string dir in Directory.GetDirectories(srcEntry,"*",SearchOption.AllDirectories))srcEntries.Add(dir.Remove(0,srcLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = dir });
+                    foreach(string file in Directory.GetFiles(srcEntry,"*.*",SearchOption.AllDirectories))srcEntries.Add(file.Remove(0,srcLen),new IOEntry(){ Type = IOType.File, AbsolutePath = file });
+                }
+                else{
+                    string fname = Path.GetFileName(srcEntry);
+                    srcEntries.Add(fname,new IOEntry(){ Type = IOType.File, AbsolutePath = srcEntry });
                 
-                destFolder = Path.Combine(destFolder,Path.GetFileName(src.TrimEnd(Path.DirectorySeparatorChar)));
-                if (!Directory.Exists(destFolder))Directory.CreateDirectory(destFolder);
+                    updatedSrc[a] = Directory.GetParent(srcEntry).FullName;
 
-                int destFolderLen = destFolder.Length;
-                foreach(string dir in Directory.GetDirectories(destFolder,"*",SearchOption.AllDirectories))dstEntries.Add(dir.Remove(0,destFolderLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = dir });
-                foreach(string file in Directory.GetFiles(destFolder,"*.*",SearchOption.AllDirectories))dstEntries.Add(file.Remove(0,destFolderLen),new IOEntry(){ Type = IOType.File, AbsolutePath = file });
+                    if (!Directory.Exists(destFolder))Directory.CreateDirectory(destFolder);
+                }
             }
-            else{
-                string fname = Path.GetFileName(src);
-                srcEntries.Add(fname,new IOEntry(){ Type = IOType.File, AbsolutePath = src });
-                
-                src = Directory.GetParent(src).FullName;
 
-                string dst = Path.Combine(destFolder,fname);
-                if (File.Exists(dst))dstEntries.Add(fname,new IOEntry{ Type = IOType.File, AbsolutePath = dst });
-
-                if (!Directory.Exists(destFolder))Directory.CreateDirectory(destFolder);
-            }
+            src = updatedSrc;
 
             // Generate the IO actions
             List<IOActionEntry> actions = new List<IOActionEntry>();
@@ -83,6 +89,7 @@ namespace BackupEssentials.Backup{
             IEnumerable<string> ioIntersecting = srcEntries.Keys.Intersect(dstEntries.Keys);
             
             foreach(KeyValuePair<string,IOEntry> deleted in ioDeleted){
+                if (deleted.Key.IndexOf(Path.DirectorySeparatorChar) == -1)continue; // ignore everything in root folder
                 actions.Add(new IOActionEntry(){ Type = deleted.Value.Type, Action = IOAction.Delete, RelativePath = deleted.Key });
             }
 
@@ -117,6 +124,13 @@ namespace BackupEssentials.Backup{
             string path;
 
             BackupReport.Builder reportBuilder = new BackupReport.Builder();
+
+            if (DEBUG){
+                reportBuilder.Add("= Caution =");
+                reportBuilder.Add("Backup Essentials is running in debug mode, all actions will be logged into the report but will not actually modify any files or folders!");
+                reportBuilder.Add("");
+            }
+
             reportBuilder.Add("= Preparing backup =");
             reportBuilder.Add("Source: "+fullSrc);
             reportBuilder.Add("Destination: "+destFolder);
@@ -137,30 +151,47 @@ namespace BackupEssentials.Backup{
                     IOActionEntry entry = actions[index];
 
                     try{
+                        bool ignoreEntry = false;
+
                         if (entry.Action == IOAction.Delete){
                             path = Path.Combine(destFolder,entry.RelativePath);
 
                             if (entry.Type == IOType.File){
-                                if (File.Exists(path))File.Delete(path);
+                                if (File.Exists(path)){
+                                    if (DEBUG)reportBuilder.Add("[D] Deleting file: "+path);
+                                    else File.Delete(path);
+                                }
+                                else ignoreEntry = true;
                             }
                             else if (entry.Type == IOType.Directory){
-                                if (Directory.Exists(path))Directory.Delete(path,true);
+                                if (Directory.Exists(path)){
+                                    if (DEBUG)reportBuilder.Add("[D] Deleting directory: "+path);
+                                    else Directory.Delete(path,true);
+                                }
+                                else ignoreEntry = true;
                             }
                         }
                         else if (entry.Action == IOAction.Create){
                             path = Path.Combine(destFolder,entry.RelativePath);
 
-                            if (entry.Type == IOType.File)File.Copy(Path.Combine(src,entry.RelativePath),path,false);
+                            if (entry.Type == IOType.File){
+                                if (DEBUG)reportBuilder.Add("[D] Copying file: "+Path.Combine(srcParent,entry.RelativePath)+" --> "+path);
+                                else File.Copy(Path.Combine(srcParent,entry.RelativePath),path,false);
+                            }
                             else if (entry.Type == IOType.Directory){
-                                if (!Directory.Exists(path))Directory.CreateDirectory(path);
+                                if (!Directory.Exists(path)){
+                                    if (DEBUG)reportBuilder.Add("[D] Creating directory: "+path);
+                                    else Directory.CreateDirectory(path);
+                                }
                             }
                         }
                         else if (entry.Action == IOAction.Replace){
-                            File.Copy(Path.Combine(src,entry.RelativePath),Path.Combine(destFolder,entry.RelativePath),true);
+                            if (DEBUG)reportBuilder.Add("[D] Replacing file: "+Path.Combine(srcParent,entry.RelativePath)+" --> "+Path.Combine(destFolder,entry.RelativePath));
+                            else File.Copy(Path.Combine(srcParent,entry.RelativePath),Path.Combine(destFolder,entry.RelativePath),true);
                         }
                         
                         indexesToRemove.Add(index-indexesToRemove.Count); // goes from 0 to actions.Count, removing each index will move the structure
-                        reportBuilder.Add(entry.Action,entry.Type,entry.RelativePath);
+                        if (!ignoreEntry)reportBuilder.Add(entry.Action,entry.Type,entry.RelativePath);
 
                         worker.ReportProgress((int)Math.Ceiling(((totalActions-actions.Count+indexesToRemove.Count)*100D)/totalActions));
                         if (worker.CancellationPending)break;
diff --git a/BackupEssentials/BackupEssentials.csproj b/BackupEssentials/BackupEssentials.csproj
index b1cd2d6..3c27936 100644
--- a/BackupEssentials/BackupEssentials.csproj
+++ b/BackupEssentials/BackupEssentials.csproj
@@ -78,6 +78,7 @@
     </Compile>
     <Compile Include="Backup\BackupLocation.cs" />
     <Compile Include="Backup\BackupReport.cs" />
+    <Compile Include="Backup\BackupRunInfo.cs" />
     <Compile Include="Backup\BackupRunner.cs" />
     <Compile Include="Backup\DataStorage.cs" />
     <Compile Include="Backup\ExplorerIntegration.cs" />