diff --git a/Duplicati/CommandLine/Commands.cs b/Duplicati/CommandLine/Commands.cs new file mode 100644 index 000000000..3f56a52dc --- /dev/null +++ b/Duplicati/CommandLine/Commands.cs @@ -0,0 +1,375 @@ +// Copyright (C) 2011, Kenneth Skovhede + +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Collections.Generic; + +namespace Duplicati.CommandLine +{ + public static class Commands + { + public static int PurgeSignatureCache(List args, Dictionary options) + { + Library.Main.Interface.PurgeSignatureCache(options); + return 0; + } + + public static int Help(List args, Dictionary options) + { + if (args.Count < 1) + Duplicati.CommandLine.Help.PrintUsage("help", options); + else + Duplicati.CommandLine.Help.PrintUsage(args[0], options); + + return 0; + } + + public static int List(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(string.Join(Environment.NewLine, Duplicati.Library.Main.Interface.List(args[0], options))); + + return 0; + } + + public static int ListCurrentFiles(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(string.Join(Environment.NewLine, new List(Duplicati.Library.Main.Interface.ListCurrentFiles(args[0], options)).ToArray())); + + return 0; + } + + public static int ListSourceFolders(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(string.Join(Environment.NewLine, Duplicati.Library.Main.Interface.ListSourceFolders(args[0], options) ?? new string[0])); + + return 0; + } + + public static int ListActualSignatureFiles(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + List> files = Duplicati.Library.Main.Interface.ListActualSignatureFiles(args[0], options); + + Console.WriteLine("* " + Strings.Program.DeletedFoldersHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.DeletedFolder) + Console.WriteLine(x.Value); + + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.AddedFoldersHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedFolder) + Console.WriteLine(x.Value); + + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.DeletedFilesHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.DeletedFile) + Console.WriteLine(x.Value); + + bool hasCombinedSignatures = false; + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedOrUpdatedFile) + { + hasCombinedSignatures = true; + break; + } + + if (hasCombinedSignatures) + { + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.NewOrModifiedFilesHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedOrUpdatedFile) + Console.WriteLine(x.Value); + } + else + { + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.NewFilesHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedFile) + Console.WriteLine(x.Value); + + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.ModifiedFilesHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.UpdatedFile) + Console.WriteLine(x.Value); + } + + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.ControlFilesHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.ControlFile) + Console.WriteLine(x.Value); + + Console.WriteLine(); + Console.WriteLine("* " + Strings.Program.SymlinksHeader + ":"); + foreach (KeyValuePair x in files) + if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.Symlink) + Console.WriteLine(x.Value); + + return 0; + } + + public static int CollectionStatus(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + List entries = Duplicati.Library.Main.Interface.ParseFileList(args[0], options); + + Console.WriteLine(Strings.Program.CollectionStatusHeader.Replace("\\t", "\t"), entries.Count); + + foreach (Duplicati.Library.Main.ManifestEntry m in entries) + { + Console.WriteLine(); + + long size = Math.Max(m.Fileentry.Size ,0); + foreach (KeyValuePair x in m.Volumes) + size += Math.Max(x.Key.Fileentry.Size, 0) + Math.Max(x.Value.Fileentry.Size, 0); + + Console.WriteLine(Strings.Program.CollectionStatusLineFull.Replace("\\t", "\t"), m.Time.ToString(), m.Volumes.Count, Library.Utility.Utility.FormatSizeString(size)); + + foreach (Duplicati.Library.Main.ManifestEntry mi in m.Incrementals) + { + size = Math.Max(mi.Fileentry.Size, 0); + foreach (KeyValuePair x in mi.Volumes) + size += Math.Max(x.Key.Fileentry.Size, 0) + Math.Max(x.Value.Fileentry.Size, 0); + + Console.WriteLine(Strings.Program.CollectionStatusLineInc.Replace("\\t", "\t"), mi.Time.ToString(), mi.Volumes.Count, Library.Utility.Utility.FormatSizeString(size)); + } + } + + return 0; + } + + public static int DeleteAllButN(List args, Dictionary options) + { + int n = 0; + if (!int.TryParse(args[0], out n) || n < 0) + { + Console.WriteLine(string.Format(Strings.Program.IntegerParseError, args[0])); + return 200; + } + + options["delete-all-but-n-full"] = n.ToString(); + + args.RemoveAt(0); + + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(Duplicati.Library.Main.Interface.DeleteAllButN(args[0], options)); + + return 0; + } + + public static int DeleteAllButNFull(List args, Dictionary options) + { + int n = 0; + if (!int.TryParse(args[0], out n) || n < 0) + { + Console.WriteLine(string.Format(Strings.Program.IntegerParseError, args[0])); + return 200; + } + + options["delete-all-but-n-full"] = n.ToString(); + + args.RemoveAt(0); + + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(Duplicati.Library.Main.Interface.DeleteAllButNFull(args[0], options)); + + return 0; + } + + public static int DeleteOlderThan(List args, Dictionary options) + { + try + { + Duplicati.Library.Utility.Timeparser.ParseTimeSpan(args[0]); + } + catch (Exception ex) + { + Console.WriteLine(string.Format(Strings.Program.TimeParseError, args[0], ex.Message)); + return 200; + } + + options["delete-older-than"] = args[0]; + + args.RemoveAt(0); + + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(Duplicati.Library.Main.Interface.DeleteOlderThan(args[0], options)); + + return 0; + } + + public static int Cleanup(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Console.WriteLine(Duplicati.Library.Main.Interface.Cleanup(args[0], options)); + + return 0; + } + + public static int CreateFolder(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + Duplicati.Library.Main.Interface.CreateFolder(args[0], options); + Console.WriteLine(string.Format(Strings.Program.FolderCreatedMessage, args[0])); + + return 0; + } + + public static int FindLastVersion(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + List> results = Duplicati.Library.Main.Interface.FindLastFileVersion(args[0], options); + Console.WriteLine(Strings.Program.FindLastVersionHeader.Replace("\\t", "\t")); + foreach(KeyValuePair k in results) + Console.WriteLine(string.Format(Strings.Program.FindLastVersionEntry.Replace("\\t", "\t"), k.Value.Ticks == 0 ? Strings.Program.FileEntryNotFound : k.Value.ToLocalTime().ToString("yyyyMMdd hhmmss"), k.Key)); + + return 0; + } + + public static int Verify(List args, Dictionary options) + { + if (args.Count != 1) + return PrintWrongNumberOfArguments(args, 1); + + List> results = Duplicati.Library.Main.Interface.VerifyBackup(args[0], options); + + int manifests = 0; + int signatures = 0; + int contentfiles = 0; + int errors = 0; + + foreach (KeyValuePair x in results) + { + if (x.Key is Duplicati.Library.Main.ManifestEntry) + manifests++; + else if (x.Key is Duplicati.Library.Main.SignatureEntry) + signatures++; + else if (x.Key is Duplicati.Library.Main.ContentEntry) + contentfiles++; + + if (x.Value != null) + errors++; + } + + Console.WriteLine(string.Format(Strings.Program.VerificationCompleted, manifests, signatures, contentfiles, errors)); + if (errors > 0) + { + Console.WriteLine(); + Console.WriteLine(Strings.Program.VerificationErrorHeader); + Console.WriteLine(); + + foreach (KeyValuePair x in results) + if (x.Value != null) + Console.WriteLine(string.Format("{0}: {1}", x.Key.Filename, x.Value.Message)); + } + + return 0; + } + + public static int Restore(List args, Dictionary options) + { + if (args.Count != 2) + return PrintWrongNumberOfArguments(args, 2); + + Console.WriteLine(Duplicati.Library.Main.Interface.Restore(args[0], args[1].Split(System.IO.Path.PathSeparator), options)); + + return 0; + } + + public static int Backup(List args, Dictionary options) + { + if (args.Count != 2) + return PrintWrongNumberOfArguments(args, 2); + + string result = Duplicati.Library.Main.Interface.Backup(args[0].Split(System.IO.Path.PathSeparator), args[1], options); + Console.WriteLine(result); + + Dictionary tmp = ParseDuplicatiOutput(result); + + //Interrupted = 50 + if (tmp.ContainsKey("PartialBackup")) + return 50; + + //Completed with warnings = 2 + if (tmp.ContainsKey("NumberOfWarnings")) + return 2; + + //Success, but no upload = 1 + if (tmp.ContainsKey("BytesUploaded")) + { + long s; + if (long.TryParse(tmp["BytesUploaded"], out s) && s == 0) + return 1; + } + + return 0; + } + + private static Dictionary ParseDuplicatiOutput(string output) + { + System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("(?[^\\:]+)\\:(?[^\\n]*)", System.Text.RegularExpressions.RegexOptions.Singleline); + Dictionary res = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + foreach (System.Text.RegularExpressions.Match m in re.Matches(output)) + res[m.Groups["key"].Value.Trim()] = m.Groups["value"].Value.Trim(); + + return res; + } + + private static int PrintWrongNumberOfArguments(List args, int expected) + { + Console.WriteLine(Strings.Program.WrongNumberOfCommandsError_v2, args.Count, expected, args.Count == 0 ? "" : "\"" + string.Join("\", \"", args.ToArray()) + "\""); + return 200; + } + + public static int PrintInvalidCommand(List args) + { + Console.WriteLine(Strings.Program.InvalidCommandError, args.Count == 0 ? "" : args[0]); + return 200; + } + } +} + diff --git a/Duplicati/CommandLine/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/Duplicati.CommandLine.csproj index 0d2529941..b01f53c5f 100644 --- a/Duplicati/CommandLine/Duplicati.CommandLine.csproj +++ b/Duplicati/CommandLine/Duplicati.CommandLine.csproj @@ -70,6 +70,7 @@ Program.resx + diff --git a/Duplicati/CommandLine/Program.cs b/Duplicati/CommandLine/Program.cs index f8dcb9b8d..b1a63c520 100644 --- a/Duplicati/CommandLine/Program.cs +++ b/Duplicati/CommandLine/Program.cs @@ -116,53 +116,8 @@ namespace Duplicati.CommandLine if (!string.IsNullOrEmpty(filter)) options["filter"] = filter; - if (cargs.Count == 1) - { - switch (cargs[0].Trim().ToLower()) - { - case "purge-signature-cache": - Library.Main.Interface.PurgeSignatureCache(options); - return 0; - } - } - - if (cargs.Count < 2 || cargs[0].Trim().Equals("help", StringComparison.InvariantCultureIgnoreCase)) - { - if (cargs.Count == 1 && !cargs[0].Trim().Equals("help", StringComparison.InvariantCultureIgnoreCase) ) - { - PrintWrongNumberOfArguments(cargs, 2); - return 200; - } - - if (cargs.Count < 2) - Help.PrintUsage("help", options); - else - Help.PrintUsage(cargs[1], options); - - - return 0; - } - - - string source = cargs[0]; - string target = cargs[1]; - bool operationSpecified = false; - - if (source.Trim().ToLower() == "restore" && cargs.Count == 3) - { - source = target; - target = cargs[2]; - options["restore"] = null; - cargs.RemoveAt(0); - operationSpecified = true; - } - else if (source.Trim().ToLower() == "backup" && cargs.Count == 3) - { - source = target; - target = cargs[2]; - cargs.RemoveAt(0); - operationSpecified = true; - } + string command = cargs[0]; + cargs.RemoveAt(0); if (!options.ContainsKey("passphrase")) if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("PASSPHRASE"))) @@ -176,319 +131,34 @@ namespace Duplicati.CommandLine if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("FTP_USERNAME"))) options["ftp-username"] = System.Environment.GetEnvironmentVariable("FTP_USERNAME"); - if (source.Trim().ToLower() == "list") - Console.WriteLine(string.Join(Environment.NewLine, Duplicati.Library.Main.Interface.List(target, options))); - else if (source.Trim().ToLower() == "list-current-files") + + var knownCommands = new Dictionary, Dictionary, int>>(StringComparer.InvariantCultureIgnoreCase); + knownCommands["purge-signature-cache"] = Commands.PurgeSignatureCache; + knownCommands["help"] = Commands.Help; + knownCommands["list"] = Commands.List; + knownCommands["list-current-files"] = Commands.ListCurrentFiles; + knownCommands["list-source-folders"] = Commands.ListSourceFolders; + knownCommands["list-actual-signature-files"] = Commands.ListActualSignatureFiles; + knownCommands["collection-status"] = Commands.CollectionStatus; + knownCommands["delete-all-but-n-full"] = Commands.DeleteAllButNFull; + knownCommands["delete-all-but-n"] = Commands.DeleteAllButN; + knownCommands["delete-older-than"] = Commands.DeleteOlderThan; + knownCommands["cleanup"] = Commands.Cleanup; + knownCommands["create-folder"] = Commands.CreateFolder; + knownCommands["find-last-version"] = Commands.FindLastVersion; + knownCommands["verify"] = Commands.Verify; + knownCommands["restore"] = Commands.Restore; + knownCommands["backup"] = Commands.Backup; + + if (knownCommands.ContainsKey(command)) { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - Console.WriteLine(string.Join(Environment.NewLine, new List(Duplicati.Library.Main.Interface.ListCurrentFiles(target, options)).ToArray())); - } - else if (source.Trim().ToLower() == "list-source-folders") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - Console.WriteLine(string.Join(Environment.NewLine, Duplicati.Library.Main.Interface.ListSourceFolders(target, options) ?? new string[0])); - } - else if (source.Trim().ToLower() == "list-actual-signature-files") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - List> files = Duplicati.Library.Main.Interface.ListActualSignatureFiles(cargs[0], options); - - Console.WriteLine("* " + Strings.Program.DeletedFoldersHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.DeletedFolder) - Console.WriteLine(x.Value); - - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.AddedFoldersHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedFolder) - Console.WriteLine(x.Value); - - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.DeletedFilesHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.DeletedFile) - Console.WriteLine(x.Value); - - bool hasCombinedSignatures = false; - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedOrUpdatedFile) - { - hasCombinedSignatures = true; - break; - } - - if (hasCombinedSignatures) - { - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.NewOrModifiedFilesHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedOrUpdatedFile) - Console.WriteLine(x.Value); - } - else - { - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.NewFilesHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedFile) - Console.WriteLine(x.Value); - - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.ModifiedFilesHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.UpdatedFile) - Console.WriteLine(x.Value); - } - - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.ControlFilesHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.ControlFile) - Console.WriteLine(x.Value); - - Console.WriteLine(); - Console.WriteLine("* " + Strings.Program.SymlinksHeader + ":"); - foreach (KeyValuePair x in files) - if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.Symlink) - Console.WriteLine(x.Value); - } - else if (source.Trim().ToLower() == "collection-status") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - List entries = Duplicati.Library.Main.Interface.ParseFileList(cargs[0], options); - - Console.WriteLine(Strings.Program.CollectionStatusHeader.Replace("\\t", "\t"), entries.Count); - - foreach (Duplicati.Library.Main.ManifestEntry m in entries) - { - Console.WriteLine(); - - long size = Math.Max(m.Fileentry.Size ,0); - foreach (KeyValuePair x in m.Volumes) - size += Math.Max(x.Key.Fileentry.Size, 0) + Math.Max(x.Value.Fileentry.Size, 0); - - Console.WriteLine(Strings.Program.CollectionStatusLineFull.Replace("\\t", "\t"), m.Time.ToString(), m.Volumes.Count, Library.Utility.Utility.FormatSizeString(size)); - - foreach (Duplicati.Library.Main.ManifestEntry mi in m.Incrementals) - { - size = Math.Max(mi.Fileentry.Size, 0); - foreach (KeyValuePair x in mi.Volumes) - size += Math.Max(x.Key.Fileentry.Size, 0) + Math.Max(x.Value.Fileentry.Size, 0); - - Console.WriteLine(Strings.Program.CollectionStatusLineInc.Replace("\\t", "\t"), mi.Time.ToString(), mi.Volumes.Count, Library.Utility.Utility.FormatSizeString(size)); - } - } - } - else if (source.Trim().ToLower() == "delete-all-but-n-full" || source.Trim().ToLower() == "delete-all-but-n") - { - int n = 0; - if (!int.TryParse(target, out n) || n < 0) - { - Console.WriteLine(string.Format(Strings.Program.IntegerParseError, target)); - return 200; - } - - options["delete-all-but-n-full"] = n.ToString(); - - cargs.RemoveAt(0); - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - if (source.Trim().ToLower() == "delete-all-but-n") - Console.WriteLine(Duplicati.Library.Main.Interface.DeleteAllButN(cargs[0], options)); - else - Console.WriteLine(Duplicati.Library.Main.Interface.DeleteAllButNFull(cargs[0], options)); - } - else if (source.Trim().ToLower() == "delete-older-than") - { - try - { - Duplicati.Library.Utility.Timeparser.ParseTimeSpan(target); - } - catch (Exception ex) - { - Console.WriteLine(string.Format(Strings.Program.TimeParseError, target, ex.Message)); - return 200; - } - - options["delete-older-than"] = target; - - cargs.RemoveAt(0); - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - Console.WriteLine(Duplicati.Library.Main.Interface.DeleteOlderThan(cargs[0], options)); - } - else if (source.Trim().ToLower() == "cleanup") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - Console.WriteLine(Duplicati.Library.Main.Interface.Cleanup(cargs[0], options)); - } - else if (source.Trim().ToLower() == "create-folder") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - Duplicati.Library.Main.Interface.CreateFolder(cargs[0], options); - Console.WriteLine(string.Format(Strings.Program.FolderCreatedMessage, cargs[0])); - } - else if (source.Trim().ToLower() == "find-last-version") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - List> results = Duplicati.Library.Main.Interface.FindLastFileVersion(cargs[0], options); - Console.WriteLine(Strings.Program.FindLastVersionHeader.Replace("\\t", "\t")); - foreach(KeyValuePair k in results) - Console.WriteLine(string.Format(Strings.Program.FindLastVersionEntry.Replace("\\t", "\t"), k.Value.Ticks == 0 ? Strings.Program.FileEntryNotFound : k.Value.ToLocalTime().ToString("yyyyMMdd hhmmss"), k.Key)); - } - else if (source.Trim().ToLower() == "verify") - { - cargs.RemoveAt(0); - - if (cargs.Count != 1) - { - PrintWrongNumberOfArguments(cargs, 1); - return 200; - } - - List> results = Duplicati.Library.Main.Interface.VerifyBackup(cargs[0], options); - - int manifests = 0; - int signatures = 0; - int contentfiles = 0; - int errors = 0; - - foreach (KeyValuePair x in results) - { - if (x.Key is Duplicati.Library.Main.ManifestEntry) - manifests++; - else if (x.Key is Duplicati.Library.Main.SignatureEntry) - signatures++; - else if (x.Key is Duplicati.Library.Main.ContentEntry) - contentfiles++; - - if (x.Value != null) - errors++; - } - - Console.WriteLine(string.Format(Strings.Program.VerificationCompleted, manifests, signatures, contentfiles, errors)); - if (errors > 0) - { - Console.WriteLine(); - Console.WriteLine(Strings.Program.VerificationErrorHeader); - Console.WriteLine(); - - foreach (KeyValuePair x in results) - if (x.Value != null) - Console.WriteLine(string.Format("{0}: {1}", x.Key.Filename, x.Value.Message)); - } - } - else if (source.IndexOf("://") > 0 || options.ContainsKey("restore")) - { - if (cargs.Count != 2) - { - PrintWrongNumberOfArguments(cargs, 2); - return 200; - } - - Console.WriteLine(Duplicati.Library.Main.Interface.Restore(source, target.Split(System.IO.Path.PathSeparator), options)); + return knownCommands[command](cargs, options); } else { - if (cargs.Count != 2) - { - PrintWrongNumberOfArguments(cargs, 2); - return 200; - } - - //Assume file:// if no url fragment is found, but only if "backup" is specified - if (!target.Contains("://") && !operationSpecified) - { - Console.WriteLine(Strings.Program.MissingURISchemeError, "file://", target); - return 200; - } - - string result = Duplicati.Library.Main.Interface.Backup(source.Split(System.IO.Path.PathSeparator), target, options); - Console.WriteLine(result); - - Dictionary tmp = ParseDuplicatiOutput(result); - - //Interrupted = 50 - if (tmp.ContainsKey("PartialBackup")) - return 50; - - //Completed with warnings = 2 - if (tmp.ContainsKey("NumberOfWarnings")) - return 2; - - //Success, but no upload = 1 - if (tmp.ContainsKey("BytesUploaded")) - { - long s; - if (long.TryParse(tmp["BytesUploaded"], out s) && s == 0) - return 1; - } + Commands.PrintInvalidCommand(cargs); + return 200; } - - //Normal operation = 0 - return 0; } catch (Exception ex) { @@ -520,20 +190,6 @@ namespace Duplicati.CommandLine } } - public static Dictionary ParseDuplicatiOutput(string output) - { - System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("(?[^\\:]+)\\:(?[^\\n]*)", System.Text.RegularExpressions.RegexOptions.Singleline); - Dictionary res = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (System.Text.RegularExpressions.Match m in re.Matches(output)) - res[m.Groups["key"].Value.Trim()] = m.Groups["value"].Value.Trim(); - - return res; - } - - private static void PrintWrongNumberOfArguments(List args, int expected) - { - Console.WriteLine(Strings.Program.WrongNumberOfCommandsError_v2, args.Count, expected, args.Count == 0 ? "" : "\"" + string.Join("\", \"", args.ToArray()) + "\""); - } public static IList SupportedCommands { @@ -561,7 +217,7 @@ namespace Duplicati.CommandLine filter = newfilter; Dictionary opt = Library.Utility.CommandLineParser.ExtractOptions(fargs); - String newsource = null, newtarget = null; + string newsource = null, newtarget = null; foreach (KeyValuePair keyvalue in opt) { switch (keyvalue.Key.ToLower()) @@ -584,13 +240,11 @@ namespace Duplicati.CommandLine // Note: this block is faily complex due to the way parameters are handled by the rest of the // procedure. It could likely be much simpler and versatile (allow --source and --target on // restore or other actions) with some refactoring of the parameters decision tree - if (!String.IsNullOrEmpty(newsource) || !String.IsNullOrEmpty(newtarget)) + if (!string.IsNullOrEmpty(newsource) || !string.IsNullOrEmpty(newtarget)) { - int offset = cargs.Count > 0 && (cargs[0] == "backup" || cargs[0] == "restore") ? 1 : 0; - bool isrestore = cargs.Count > 0 && cargs[0] == "restore" - || cargs.Count >= 2 && cargs[0].Contains("://"); + bool isrestore = cargs.Count > 0 && cargs[0] == "restore"; - if (cargs.Count == 0 + offset) + if (cargs.Count == 1 || cargs.Count == 0) { // if either is empty loading will fail later, so we don't really care. if (!String.IsNullOrEmpty(newsource)) cargs.Add(newsource); @@ -598,28 +252,15 @@ namespace Duplicati.CommandLine } else { - bool isurl = cargs[offset].Contains("://"); - bool isdir = !isurl && cargs[offset].IndexOfAny(new char[] { '/', '\\', ':' }) >= 0; - if (offset > 0 || isdir || isurl) + if (isrestore) { - if (cargs.Count == 1 + offset) - { - if (isrestore ^ isurl) - { - if (!String.IsNullOrEmpty(newtarget)) cargs[offset] = newtarget; - if (!String.IsNullOrEmpty(newsource)) cargs.Insert(offset, newsource); - } - else - { - if (!String.IsNullOrEmpty(newtarget)) cargs[offset] = newsource; - if (!String.IsNullOrEmpty(newsource)) cargs.Add(newtarget); - } - } - else - { - if (!String.IsNullOrEmpty(newsource)) cargs[offset] = newsource; - if (!String.IsNullOrEmpty(newtarget)) cargs[offset + 1] = newtarget; - } + if (!String.IsNullOrEmpty(newtarget)) cargs[1] = newtarget; + if (!String.IsNullOrEmpty(newsource)) cargs.Insert(1, newsource); + } + else + { + if (!String.IsNullOrEmpty(newtarget)) cargs[1] = newsource; + if (!String.IsNullOrEmpty(newsource)) cargs.Add(newtarget); } } } diff --git a/Duplicati/CommandLine/Strings/Program.Designer.cs b/Duplicati/CommandLine/Strings/Program.Designer.cs index 3d535c5e6..53c34fe2d 100644 --- a/Duplicati/CommandLine/Strings/Program.Designer.cs +++ b/Duplicati/CommandLine/Strings/Program.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.296 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -90,7 +90,7 @@ namespace Duplicati.CommandLine.Strings { } /// - /// Looks up a localized string similar to Inc\t{0}\t{1}\t{2}. + /// Looks up a localized string similar to Inc\t{0}\t{1}\t{2}. /// internal static string CollectionStatusLineInc { get { @@ -215,6 +215,16 @@ namespace Duplicati.CommandLine.Strings { } } + /// + /// Looks up a localized string similar to Command not supported: {0} + ///{1}. + /// + internal static string InvalidCommandError { + get { + return ResourceManager.GetString("InvalidCommandError", resourceCulture); + } + } + /// /// Looks up a localized string similar to The source or target parameter must be a URL, try using "{0}{1}". /// @@ -292,7 +302,7 @@ namespace Duplicati.CommandLine.Strings { /// Using this system, Duplicati can detect if you want to backup or restore. /// The cleanup and delete commands do not delete files, unless the --force option is specified, so you may examine what files are affected, before actually deleting the files. /// The cleanup command should not be used unless a backup was interrupted and has left partial files. Duplicati will inform you if this happens. - /// The delete command can be used to remove bac [rest of string was truncated]";. + /// The delete command can be used to remove backup sets when newer backups are present.. /// internal static string ProgramUsageBackend { get { @@ -347,7 +357,10 @@ namespace Duplicati.CommandLine.Strings { /// Duplicati uses a "first-touch" filter where the first rule that matches a file determines if the file is included or excluded. Internally Duplicati uses regular expression filters, but supports filters in the form of filename globbing. The order of the commandline arguments also determine what order they are applied in. An example: /// --include=*.txt --exclude=*\Thumbs.db --include=* /// - /// Even though the last filter includes everything, no [rest of string was truncated]";. + /// Even though the last filter includes everything, no files named "Thumbs.db" are included because they match the exclude rule before the include rule. Paths are evaluated in full, and as paths that are relative to folder being backed up, but including a leading slash. An example: + /// + ///{0} + /// If a folder is excluded, files in that folder are always excluded, even if there are filters that include files in that folder. If a folder is included with a wildcard at the end, all files are included, if the folder is included without a wildcard, files may be excluded or included with extra rules.. /// internal static string ProgramUsageFilters { get { diff --git a/Duplicati/CommandLine/Strings/Program.resx b/Duplicati/CommandLine/Strings/Program.resx index 91ea18993..3e781bec1 100644 --- a/Duplicati/CommandLine/Strings/Program.resx +++ b/Duplicati/CommandLine/Strings/Program.resx @@ -337,4 +337,8 @@ Errors: {3} Found {0} commands but expected {1}, commands: {2} + + Command not supported: {0} +{1} + \ No newline at end of file