public static HashSet <string> GetRelativePathsOfAllFiles(string rootFolder) { using (Log.MeasureTime("Scanning files in " + rootFolder)) { var files = Directory.GetFiles(rootFolder, "*", SearchOption.AllDirectories); return(GetRelativePaths(rootFolder, files)); } }
private static void Main(string[] args) { string source = null; string destination = null; if (args.Length == 2) { source = args[0]; destination = args[1]; } else { PrintUsage(); return; } if (Directory.Exists(source)) { source = Path.GetFullPath(source); if (Directory.Exists(destination)) { destination = Path.GetFullPath(destination); } using (Log.MeasureTime("Total time")) { Sync.Directories(source, destination); } Log.PrintFinalReport(); return; } if (File.Exists(source) && Directory.Exists(destination)) { Sync.Files(source, Path.Combine(destination, Path.GetFileName(source))); return; } if (File.Exists(source)) { Sync.Files(source, destination); return; } Console.Error.WriteLine($"Cannot sync {source} to {destination}"); }
/// <summary> /// Assumes leftRoot is an existing folder. rightRoot may not exist if operating in speculative mode. /// </summary> public static FolderDiffResults DiffFolders( string leftRoot, string rightRoot, string pattern, bool recursive = true, bool compareContents = true) { HashSet <string> leftRelativePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase); HashSet <string> leftOnlyFolders = new HashSet <string>(StringComparer.OrdinalIgnoreCase); using (Log.MeasureTime("Scanning source directory")) { GetRelativePathsOfAllFiles(leftRoot, pattern, recursive, leftRelativePaths, leftOnlyFolders); } HashSet <string> rightRelativePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase); HashSet <string> rightOnlyFolders = new HashSet <string>(StringComparer.OrdinalIgnoreCase); if (Directory.Exists(rightRoot)) { using (Log.MeasureTime("Scanning destination directory")) { GetRelativePathsOfAllFiles(rightRoot, pattern, recursive, rightRelativePaths, rightOnlyFolders); } } var leftOnlyFiles = new List <string>(); var identicalFiles = new List <string>(); var changedFiles = new List <string>(); var rightOnlyFiles = new HashSet <string>(rightRelativePaths, StringComparer.OrdinalIgnoreCase); var commonFolders = leftOnlyFolders.Intersect(rightOnlyFolders, StringComparer.OrdinalIgnoreCase).ToArray(); leftOnlyFolders.ExceptWith(commonFolders); rightOnlyFolders.ExceptWith(commonFolders); int current = 0; int total = leftRelativePaths.Count; using (Log.MeasureTime("Comparing")) { Parallel.ForEach( leftRelativePaths, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }, left => { var leftFullPath = leftRoot + left; var rightFullPath = rightRoot + left; bool rightContains = rightRelativePaths.Contains(left); if (rightContains) { bool areSame; try { areSame = !compareContents || Files.AreContentsIdentical(leftFullPath, rightFullPath); } catch (Exception ex) { Log.WriteError(ex.ToString()); return; } if (areSame) { lock (identicalFiles) { identicalFiles.Add(left); } } else { lock (changedFiles) { changedFiles.Add(left); } } } else { lock (leftOnlyFiles) { leftOnlyFiles.Add(left); } } lock (rightOnlyFiles) { rightOnlyFiles.Remove(left); } Interlocked.Increment(ref current); }); } using (Log.MeasureTime("Sorting")) { leftOnlyFiles.Sort(); identicalFiles.Sort(); changedFiles.Sort(); return(new FolderDiffResults( leftOnlyFiles, identicalFiles, changedFiles, rightOnlyFiles.OrderBy(s => s).ToArray(), leftOnlyFolders.OrderBy(s => s).ToArray(), rightOnlyFolders.OrderBy(s => s).ToArray())); } }
/// <summary> /// Assumes leftRoot is an existing folder. rightRoot may not exist if operating in speculative mode. /// </summary> public static FolderDiffResults DiffFolders( string leftRoot, string rightRoot, Log log, CancellationToken token, bool compareContents = true, bool respectDate = true) { var leftRelativePaths = new ConcurrentBag <string>(); using (log.MeasureTime("Scanning source directory")) { GetRelativePathsOfAllFiles(leftRoot, leftRelativePaths, token); } var rightRelativePaths = new ConcurrentBag <string>(); if (Directory.Exists(rightRoot)) { using (log.MeasureTime("Scanning destination directory")) { GetRelativePathsOfAllFiles(rightRoot, rightRelativePaths, token); } } var leftOnlyFiles = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var identicalFiles = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var changedFiles = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var rightOnlyFiles = new HashSet <string>(rightRelativePaths, StringComparer.OrdinalIgnoreCase); using (log.MeasureTime("Comparing")) { leftRelativePaths .Distinct(StringComparer.OrdinalIgnoreCase) .ForEach(AnalyzeFile); } using (log.MeasureTime("Sorting")) { var leftOnlyFilesList = leftOnlyFiles.ToList(); leftOnlyFilesList.Sort(); var identicalFilesList = identicalFiles.ToList(); identicalFilesList.Sort(); var changedFilesList = changedFiles.ToList(); changedFilesList.Sort(); return(new FolderDiffResults( leftOnlyFilesList, identicalFilesList, changedFilesList, rightOnlyFiles.OrderBy(s => s).ToArray())); } void AnalyzeFile(string path) { var leftFullPath = leftRoot + path; var rightFullPath = rightRoot + path; var rightContains = rightRelativePaths.Contains(path); if (rightContains) { var areSame = true; try { areSame = !compareContents || Files.AreContentsIdentical(leftFullPath, rightFullPath, token) || respectDate ? File.GetLastWriteTimeUtc(leftFullPath) <= File.GetLastWriteTimeUtc(rightFullPath) : true; } catch (Exception ex) { log.WriteError(ex.ToString()); return; } if (areSame) { identicalFiles.Add(path); } else { changedFiles.Add(path); } } else { leftOnlyFiles.Add(path); } rightOnlyFiles.Remove(path); } }
private static int Main(string[] args) { try { var arguments = new Arguments(args); if (arguments.Help || args.Length == 0) { PrintUsage(); return(0); } if (!string.IsNullOrEmpty(arguments.Error)) { Log.WriteError("Invalid arguments:" + Environment.NewLine + arguments.Error + Environment.NewLine); PrintUsage(); return(1); } string source = arguments.Source; string destination = arguments.Destination; if (Directory.Exists(source)) { source = Path.GetFullPath(source); if (Directory.Exists(destination)) { destination = Path.GetFullPath(destination); } using (Log.MeasureTime("Total time")) { Sync.Directories(source, destination, arguments); } Log.PrintFinalReport(); return(0); } if (File.Exists(source)) { source = Path.GetFullPath(source); if (Directory.Exists(destination)) { destination = Path.GetFullPath(destination); destination = Path.Combine(destination, Path.GetFileName(source)); } if (File.Exists(destination)) { destination = Path.GetFullPath(destination); } if (arguments.Pattern != "*") { Log.WriteError($"The pattern cannot be specified when processing a single file."); return(4); } Sync.Files(source, destination, arguments); return(0); } Log.WriteError($"Cannot find file or directory: {source}"); return(2); } catch (Exception ex) { Log.WriteError($"Unrecoverable error: {ex.Message}"); return(3); } }
/// <summary> /// Assumes source directory exists. destination may or may not exist. /// </summary> public static void Directories(string source, string destination, Arguments arguments) { if (!Directory.Exists(destination)) { FileSystem.CreateDirectory(destination, arguments.WhatIf); } source = Paths.TrimSeparator(source); destination = Paths.TrimSeparator(destination); var diff = Folders.DiffFolders( source, destination, arguments.Pattern, recursive: !arguments.Nonrecursive, compareContents: arguments.UpdateChangedFiles || arguments.DeleteChangedFiles || arguments.DeleteSameFiles); bool changesMade = false; int filesFailedToCopy = 0; int filesFailedToDelete = 0; int foldersFailedToCreate = 0; int foldersFailedToDelete = 0; if (arguments.CopyLeftOnlyFiles) { using (Log.MeasureTime("Copying new files")) { foreach (var leftOnly in diff.LeftOnlyFiles) { var destinationFilePath = destination + leftOnly; if (!FileSystem.CopyFile(source + leftOnly, destinationFilePath, arguments.WhatIf)) { filesFailedToCopy++; } changesMade = true; } } } if (arguments.UpdateChangedFiles) { using (Log.MeasureTime("Updating changed files")) { foreach (var changed in diff.ChangedFiles) { var destinationFilePath = destination + changed; if (!FileSystem.CopyFile(source + changed, destinationFilePath, arguments.WhatIf)) { filesFailedToCopy++; } changesMade = true; } } } else if (arguments.DeleteChangedFiles) { using (Log.MeasureTime("Deleting changed files")) { foreach (var changed in diff.ChangedFiles) { var destinationFilePath = destination + changed; if (!FileSystem.DeleteFile(destinationFilePath, arguments.WhatIf)) { filesFailedToDelete++; } changesMade = true; } } } if (arguments.DeleteSameFiles) { using (Log.MeasureTime("Deleting identical files")) { foreach (var same in diff.IdenticalFiles) { var destinationFilePath = destination + same; if (!FileSystem.DeleteFile(destinationFilePath, arguments.WhatIf)) { filesFailedToDelete++; } changesMade = true; } } } if (arguments.DeleteRightOnlyFiles) { using (Log.MeasureTime("Deleting extra files")) { foreach (var rightOnly in diff.RightOnlyFiles) { var deletedFilePath = destination + rightOnly; if (!FileSystem.DeleteFile(deletedFilePath, arguments.WhatIf)) { filesFailedToDelete++; } changesMade = true; } } } int foldersCreated = 0; if (arguments.CopyEmptyDirectories) { using (Log.MeasureTime("Creating folders")) { foreach (var leftOnlyFolder in diff.LeftOnlyFolders) { var newFolder = destination + leftOnlyFolder; if (!Directory.Exists(newFolder)) { if (!FileSystem.CreateDirectory(newFolder, arguments.WhatIf)) { foldersFailedToCreate++; } else { foldersCreated++; } changesMade = true; } } } } int foldersDeleted = 0; if (arguments.DeleteRightOnlyDirectories) { using (Log.MeasureTime("Deleting folders")) { foreach (var rightOnlyFolder in diff.RightOnlyFolders) { var deletedFolderPath = destination + rightOnlyFolder; if (Directory.Exists(deletedFolderPath)) { if (!FileSystem.DeleteDirectory(deletedFolderPath, arguments.WhatIf)) { foldersFailedToDelete++; } else { foldersDeleted++; } changesMade = true; } } } } if (diff.LeftOnlyFiles.Any() && arguments.CopyLeftOnlyFiles) { var count = diff.LeftOnlyFiles.Count(); var fileOrFiles = Pluralize("file", count); if (arguments.WhatIf) { Log.WriteLine($"Would have copied {count} new {fileOrFiles}", ConsoleColor.Green); } else { Log.WriteLine($"{count} new {fileOrFiles} copied", ConsoleColor.Green); } } if (foldersCreated > 0 && arguments.CopyEmptyDirectories) { var folderOrFolders = Pluralize("folder", foldersCreated); if (arguments.WhatIf) { Log.WriteLine($"Would have created {foldersCreated} {folderOrFolders}", ConsoleColor.Green); } else { Log.WriteLine($"{foldersCreated} {folderOrFolders} created", ConsoleColor.Green); } } if (diff.ChangedFiles.Any() && arguments.UpdateChangedFiles) { var count = diff.ChangedFiles.Count(); var fileOrFiles = Pluralize("file", count); if (arguments.WhatIf) { Log.WriteLine($"Would have updated {count} changed {fileOrFiles}", ConsoleColor.Yellow); } else { Log.WriteLine($"{count} changed {fileOrFiles} updated", ConsoleColor.Yellow); } } if (diff.ChangedFiles.Any() && arguments.DeleteChangedFiles) { var count = diff.ChangedFiles.Count(); var fileOrFiles = Pluralize("file", count); if (arguments.WhatIf) { Log.WriteLine($"Would have deleted {count} changed {fileOrFiles}", ConsoleColor.Yellow); } else { Log.WriteLine($"{count} changed {fileOrFiles} deleted", ConsoleColor.Yellow); } } if (diff.RightOnlyFiles.Any() && arguments.DeleteRightOnlyFiles) { var count = diff.RightOnlyFiles.Count(); var fileOrFiles = Pluralize("file", count); if (arguments.WhatIf) { Log.WriteLine($"Would have deleted {count} right-only {fileOrFiles}", ConsoleColor.Red); } else { Log.WriteLine($"{count} right-only {fileOrFiles} deleted", ConsoleColor.Red); } } if (foldersDeleted > 0 && arguments.DeleteRightOnlyDirectories) { var folderOrFolders = Pluralize("folder", foldersDeleted); if (arguments.WhatIf) { Log.WriteLine($"Would have deleted {foldersDeleted} right-only {folderOrFolders}", ConsoleColor.Red); } else { Log.WriteLine($"{foldersDeleted} right-only {folderOrFolders} deleted", ConsoleColor.Red); } } if (diff.IdenticalFiles.Any()) { var count = diff.IdenticalFiles.Count(); var fileOrFiles = Pluralize("file", count); if (arguments.DeleteSameFiles) { if (arguments.WhatIf) { Log.WriteLine($"Would have deleted {count} identical {fileOrFiles} from destination", ConsoleColor.White); } else { Log.WriteLine($"{count} identical {fileOrFiles} deleted from destination", ConsoleColor.White); } } else { Log.WriteLine($"{count} identical {fileOrFiles}", ConsoleColor.White); } } if (filesFailedToCopy > 0) { Log.WriteLine($"Failed to copy {filesFailedToCopy} {Pluralize("file", filesFailedToCopy)}", ConsoleColor.Red); } if (filesFailedToDelete > 0) { Log.WriteLine($"Failed to delete {filesFailedToDelete} {Pluralize("file", filesFailedToDelete)}.", ConsoleColor.Red); } if (foldersFailedToCreate > 0) { Log.WriteLine($"Failed to create {foldersFailedToCreate} {Pluralize("folder", foldersFailedToCreate)}.", ConsoleColor.Red); } if (foldersFailedToDelete > 0) { Log.WriteLine($"Failed to delete {foldersFailedToDelete} {Pluralize("folder", foldersFailedToDelete)}.", ConsoleColor.Red); } if (!changesMade) { if (arguments.WhatIf) { Log.WriteLine("Would have made no changes.", ConsoleColor.White); } else { Log.WriteLine("Made no changes.", ConsoleColor.White); } } // if there were no errors, delete the cache of the folder contents. Otherwise // chances are they're going to restart the process, so we might needs the cache // next time. if (filesFailedToCopy == 0 && filesFailedToDelete == 0 && foldersFailedToCreate == 0 && foldersFailedToDelete == 0) { DirectoryContentsCache.ClearWrittenFilesFromCache(); } }
/// <summary> /// Assumes source directory exists. destination may or may not exist. /// </summary> public static void Directories(string source, string destination) { if (!Directory.Exists(destination)) { Directory.CreateDirectory(destination); } source = Paths.TrimSeparator(source); destination = Paths.TrimSeparator(destination); var diff = Folders.DiffFolders(source, destination); using (Log.MeasureTime("Copying new files")) { foreach (var leftOnly in diff.LeftOnlyFiles) { var destinationFilePath = destination + leftOnly; var destinationFolder = Path.GetDirectoryName(destinationFilePath); Directory.CreateDirectory(destinationFolder); File.Copy(source + leftOnly, destinationFilePath); Console.WriteLine("Copy " + destinationFilePath); } } using (Log.MeasureTime("Overwriting changed files")) { foreach (var changed in diff.ChangedFiles) { var destinationFilePath = destination + changed; File.Copy(source + changed, destinationFilePath, overwrite: true); Console.WriteLine("Overwrite " + destinationFilePath); } } using (Log.MeasureTime("Deleting extra files")) { foreach (var rightOnly in diff.RightOnlyFiles) { var deletedFilePath = destination + rightOnly; var attributes = File.GetAttributes(deletedFilePath); if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { File.SetAttributes(deletedFilePath, attributes & ~FileAttributes.ReadOnly); } File.Delete(deletedFilePath); Console.WriteLine("Delete " + deletedFilePath); } } int foldersCreated = 0; using (Log.MeasureTime("Creating folders")) { foreach (var leftOnlyFolder in diff.LeftOnlyFolders) { var newFolder = destination + leftOnlyFolder; if (!Directory.Exists(newFolder)) { Directory.CreateDirectory(newFolder); Console.WriteLine("Create " + newFolder); foldersCreated++; } } } int foldersDeleted = 0; using (Log.MeasureTime("Deleting folders")) { foreach (var rightOnlyFolder in diff.RightOnlyFolders) { var deletedFolderPath = destination + rightOnlyFolder; if (Directory.Exists(deletedFolderPath)) { Directory.Delete(deletedFolderPath, recursive: true); Console.WriteLine("Delete " + deletedFolderPath); foldersDeleted++; } } } if (diff.LeftOnlyFiles.Any()) { Log.WriteLine($"{diff.LeftOnlyFiles.Count()} new files", ConsoleColor.Green); } if (foldersCreated > 0) { Log.WriteLine($"{foldersCreated} folders created", ConsoleColor.Green); } if (diff.ChangedFiles.Any()) { Log.WriteLine($"{diff.ChangedFiles.Count()} changed files", ConsoleColor.Yellow); } if (diff.RightOnlyFiles.Any()) { Log.WriteLine($"{diff.RightOnlyFiles.Count()} deleted files", ConsoleColor.Red); } if (foldersDeleted > 0) { Log.WriteLine($"{foldersDeleted} folders deleted", ConsoleColor.Red); } if (diff.IdenticalFiles.Any()) { Log.WriteLine($"{diff.IdenticalFiles.Count()} identical files", ConsoleColor.White); } }
/// <summary> /// Assumes both leftRoot and rightRoot are existing folders. /// </summary> public static FolderDiffResults DiffFolders(string leftRoot, string rightRoot) { var leftRelativePaths = GetRelativePathsOfAllFiles(leftRoot); var leftOnlyFolders = GetRelativePathsOfAllFolders(leftRoot); var rightRelativePaths = GetRelativePathsOfAllFiles(rightRoot); var rightOnlyFolders = GetRelativePathsOfAllFolders(rightRoot); var leftOnlyFiles = new List <string>(); var identicalFiles = new List <string>(); var changedFiles = new List <string>(); var rightOnlyFiles = new HashSet <string>(rightRelativePaths, StringComparer.OrdinalIgnoreCase); var commonFolders = leftOnlyFolders.Intersect(rightOnlyFolders, StringComparer.OrdinalIgnoreCase).ToArray(); leftOnlyFolders.ExceptWith(commonFolders); rightOnlyFolders.ExceptWith(commonFolders); int current = 0; int total = leftRelativePaths.Count; using (Log.MeasureTime("Comparing")) { Parallel.ForEach( leftRelativePaths, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }, left => { var leftFullPath = leftRoot + left; var rightFullPath = rightRoot + left; bool rightContains = rightRelativePaths.Contains(left); if (rightContains) { bool areSame = Files.AreContentsIdentical(leftFullPath, rightFullPath); if (areSame) { lock (identicalFiles) { identicalFiles.Add(left); } } else { lock (changedFiles) { changedFiles.Add(left); } } } else { lock (leftOnlyFiles) { leftOnlyFiles.Add(left); } } lock (rightOnlyFiles) { rightOnlyFiles.Remove(left); } Interlocked.Increment(ref current); }); } using (Log.MeasureTime("Sorting")) { leftOnlyFiles.Sort(); identicalFiles.Sort(); changedFiles.Sort(); return(new FolderDiffResults( leftOnlyFiles, identicalFiles, changedFiles, rightOnlyFiles.OrderBy(s => s).ToArray(), leftOnlyFolders.OrderBy(s => s).ToArray(), rightOnlyFolders.OrderBy(s => s).ToArray())); } }
/// <summary> /// Assumes source directory exists. destination may or may not exist. /// </summary> public static void Directories(string source, string destination, Log log, CancellationToken token, Arguments arguments) { if (!Directory.Exists(destination)) { FileSystem.CreateDirectory(destination, log); } source = Paths.TrimSeparator(source); destination = Paths.TrimSeparator(destination); var diff = Folders.DiffFolders( source, destination, log, token, compareContents: arguments.UpdateChangedFiles || arguments.DeleteChangedFiles || arguments.DeleteSameFiles, arguments.RespectLastAccessDateTime); bool changesMade = false; int filesFailedToCopy = 0; int filesFailedToDelete = 0; if (arguments.CopyLeftOnlyFiles) { using (log.MeasureTime("Copying new files")) { foreach (var leftOnly in diff.LeftOnlyFiles) { if (token.IsCancellationRequested) { break; } var destinationFilePath = destination + leftOnly; if (!FileSystem.CopyFile(source + leftOnly, destinationFilePath, log)) { filesFailedToCopy++; } changesMade = true; } } } if (arguments.UpdateChangedFiles) { using (log.MeasureTime("Updating changed files")) { foreach (var changed in diff.ChangedFiles) { if (token.IsCancellationRequested) { break; } var destinationFilePath = destination + changed; if (!FileSystem.CopyFile(source + changed, destinationFilePath, log)) { filesFailedToCopy++; } changesMade = true; } } } else if (arguments.DeleteChangedFiles) { using (log.MeasureTime("Deleting changed files")) { foreach (var changed in diff.ChangedFiles) { if (token.IsCancellationRequested) { break; } var destinationFilePath = destination + changed; if (!FileSystem.DeleteFile(destinationFilePath, log)) { filesFailedToDelete++; } changesMade = true; } } } if (arguments.DeleteSameFiles) { using (log.MeasureTime("Deleting identical files")) { foreach (var same in diff.IdenticalFiles) { if (token.IsCancellationRequested) { break; } var destinationFilePath = destination + same; if (!FileSystem.DeleteFile(destinationFilePath, log)) { filesFailedToDelete++; } changesMade = true; } } } if (arguments.DeleteRightOnlyFiles) { using (log.MeasureTime("Deleting extra files")) { foreach (var rightOnly in diff.RightOnlyFiles) { if (token.IsCancellationRequested) { break; } var deletedFilePath = destination + rightOnly; if (!FileSystem.DeleteFile(deletedFilePath, log)) { filesFailedToDelete++; } changesMade = true; } } } if (diff.LeftOnlyFiles.Any() && arguments.CopyLeftOnlyFiles) { var count = diff.LeftOnlyFiles.Count(); var fileOrFiles = Pluralize("file", count); log.WriteLine($"{count} new {fileOrFiles} copied", ConsoleColor.Green); } if (diff.ChangedFiles.Any() && arguments.UpdateChangedFiles) { var count = diff.ChangedFiles.Count(); var fileOrFiles = Pluralize("file", count); log.WriteLine($"{count} changed {fileOrFiles} updated", ConsoleColor.Yellow); } if (diff.ChangedFiles.Any() && arguments.DeleteChangedFiles) { var count = diff.ChangedFiles.Count(); var fileOrFiles = Pluralize("file", count); log.WriteLine($"{count} changed {fileOrFiles} deleted", ConsoleColor.Yellow); } if (diff.RightOnlyFiles.Any() && arguments.DeleteRightOnlyFiles) { var count = diff.RightOnlyFiles.Count(); var fileOrFiles = Pluralize("file", count); log.WriteLine($"{count} right-only {fileOrFiles} deleted", ConsoleColor.Red); } if (diff.IdenticalFiles.Any()) { var count = diff.IdenticalFiles.Count(); var fileOrFiles = Pluralize("file", count); if (arguments.DeleteSameFiles) { log.WriteLine($"{count} identical {fileOrFiles} deleted from destination", ConsoleColor.White); } else { log.WriteLine($"{count} identical {fileOrFiles}", ConsoleColor.White); } } if (filesFailedToCopy > 0) { log.WriteLine($"Failed to copy {filesFailedToCopy} {Pluralize("file", filesFailedToCopy)}", ConsoleColor.Red); } if (filesFailedToDelete > 0) { log.WriteLine($"Failed to delete {filesFailedToDelete} {Pluralize("file", filesFailedToDelete)}.", ConsoleColor.Red); } if (!changesMade) { log.WriteLine("Made no changes.", ConsoleColor.White); } log.PrintFinalReport(); }