using System; using System.Linq; using System.Text; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Runtime.CompilerServices; using System.IO; namespace RoboSharp { /// /// RoboCopy Switches that determine which folders and files are selected for copying/moving /// /// /// /// public class SelectionOptions : ICloneable { #region Constructors /// /// Create new SelectionOptions with Default Settings /// public SelectionOptions() { } /// /// Create new SelectionOptions using the provided /// public SelectionOptions(SelectionFlags selectionFlags) { ApplySelectionFlags(selectionFlags); } /// /// Clone a SelectionOptions Object /// public SelectionOptions(SelectionOptions options) { OnlyCopyArchiveFiles = options.OnlyCopyArchiveFiles; OnlyCopyArchiveFilesAndResetArchiveFlag = options.OnlyCopyArchiveFilesAndResetArchiveFlag; IncludeAttributes = options.IncludeAttributes; ExcludeAttributes = options.ExcludeAttributes; ExcludedFiles.AddRange(options.ExcludedFiles); ExcludedDirectories.AddRange(options.ExcludedDirectories); ExcludeChanged = options.ExcludeChanged; ExcludeNewer = options.ExcludeNewer; ExcludeOlder = options.ExcludeOlder; ExcludeExtra = options.ExcludeExtra; ExcludeLonely = options.ExcludeLonely; IncludeSame = options.IncludeSame; IncludeTweaked = options.IncludeTweaked; MaxFileSize = options.MaxFileSize; MinFileSize = options.MinFileSize; MaxFileAge = options.MaxFileAge; MinFileAge = options.MinFileAge; MaxLastAccessDate = options.MaxLastAccessDate; MinLastAccessDate = options.MinLastAccessDate; ExcludeJunctionPoints = options.ExcludeJunctionPoints; UseFatFileTimes = options.UseFatFileTimes; CompensateForDstDifference = options.CompensateForDstDifference; ; ExcludeJunctionPointsForFiles = options.ExcludeJunctionPointsForFiles; } /// /// Clone this SelectionOptions Object /// public SelectionOptions Clone() => new SelectionOptions(this); object ICloneable.Clone() => Clone(); #endregion #region Option Constants internal const string ONLY_COPY_ARCHIVE_FILES = "/A "; internal const string ONLY_COPY_ARCHIVE_FILES_AND_RESET_ARCHIVE_FLAG = "/M "; internal const string INCLUDE_ATTRIBUTES = "/IA:{0} "; internal const string EXCLUDE_ATTRIBUTES = "/XA:{0} "; internal const string EXCLUDE_FILES = "/XF {0} "; internal const string EXCLUDE_DIRECTORIES = "/XD {0} "; internal const string EXCLUDE_CHANGED = "/XC "; internal const string EXCLUDE_NEWER = "/XN "; internal const string EXCLUDE_OLDER = "/XO "; internal const string EXCLUDE_EXTRA = "/XX "; internal const string EXCLUDE_LONELY = "/XL "; internal const string INCLUDE_SAME = "/IS "; internal const string INCLUDE_TWEAKED = "/IT "; internal const string MAX_FILE_SIZE = "/MAX:{0} "; internal const string MIN_FILE_SIZE = "/MIN:{0} "; internal const string MAX_FILE_AGE = "/MAXAGE:{0} "; internal const string MIN_FILE_AGE = "/MINAGE:{0} "; internal const string MAX_LAST_ACCESS_DATE = "/MAXLAD:{0} "; internal const string MIN_LAST_ACCESS_DATE = "/MINLAD:{0} "; internal const string EXCLUDE_JUNCTION_POINTS = "/XJ "; internal const string USE_FAT_FILE_TIMES = "/FFT "; internal const string COMPENSATE_FOR_DST_DIFFERENCE = "/DST "; internal const string EXCLUDE_JUNCTION_POINTS_FOR_DIRECTORIES = "/XJD "; internal const string EXCLUDE_JUNCTION_POINTS_FOR_FILES = "/XJF "; #endregion Option Constants #region < ExcludedDirs and ExcludedFiles > private readonly List excludedDirs = new List(); private readonly List excludedFiles = new List(); /// /// This regex is used when the { } and { } properties are set in order to split the input string to a List{string} /// /// /// Regex Tester to use with to get all the matches from a string. /// public static Regex FileFolderNameRegexSplitter = new Regex("(?\".+?\"|[^\\s\\,\"\\|]+)", RegexOptions.Compiled | RegexOptions.ExplicitCapture); /// /// Use { } to split the , then add the matches to the suppplied . /// /// String to perform against /// List to add regex matches to [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ParseAndAddToList(string inputString, List list) { MatchCollection collection = FileFolderNameRegexSplitter.Matches(inputString); if (collection.Count == 0) return; foreach (Match c in collection) { string s = c.Groups["VALUE"].Value; list.Add(s); } } #endregion #region Public Properties /// /// Copies only files for which the Archive attribute is set. /// [/A] /// public virtual bool OnlyCopyArchiveFiles { get; set; } /// /// Copies only files for which the Archive attribute is set, and resets the Archive attribute. /// [/M] /// public virtual bool OnlyCopyArchiveFilesAndResetArchiveFlag { get; set; } /// /// This property should be set to a string consisting of all the attributes to include (eg. AH; RASHCNETO). /// Includes only files for which any of the specified attributes are set. /// [/IA:attributes] /// public virtual string IncludeAttributes { get; set; } /// /// This property should be set to a string consisting of all the attributes to exclude (eg. AH; RASHCNETO). /// Excludes files for which any of the specified attributes are set. /// [/XA:attributes] /// public virtual string ExcludeAttributes { get; set; } /// /// Files should be separated by spaces. /// Excludes files that match the specified names or paths. Note that FileName can include wildcard characters (* and ?). /// [/XF File File ...] /// /// /// This property is now backed by the ExcludedFiles List{String} property.
/// Get -> Ensures all strings in { } are wrapped in quotes if needed, and concats the items into a single string.
/// Set -- Clears ExcludedFiles and splits this list using a regex to populate the list. ///
[Obsolete("This property is now backed by the ExcludedFiles List property. \n Both Get/Set accessors still work similar to previous:\n" + "- 'Get' sanitizies then Joins all strings in the list into a single output string that is passed into RoboCopy.\n" + "- 'Set' clears the ExcludedFiles list, then splits the input string using regex to repopulate the list." )] public string ExcludeFiles { get { string RetString = ""; foreach (string s in excludedFiles) { RetString += s.WrapPath() + " "; } return RetString.Trim(); } set { excludedFiles.Clear(); if (value.IsNullOrWhiteSpace()) return; ParseAndAddToList(value, excludedFiles); } } /// /// Allows you to supply a set of files to copy or use wildcard characters (* or ?).
/// JobOptions file saves these into the /IF (Include Files) section ///
public List ExcludedFiles { get { return excludedFiles; } } /// /// Directories should be separated by spaces. /// Excludes directories that match the specified names or paths. /// [/XD Directory Directory ...] /// /// /// This property is now backed by the ExcludedDirectories List{String} property.
/// Get -> Ensures all strings in { } are wrapped in quotes if needed, and concats the items into a single string.
/// Set -> Clears ExcludedDirs and splits this list using a regex to populate the list. ///
[Obsolete("This property is now backed by the ExcludedDirectories List property. \n Both Get/Set accessors still work similar to previous:\n" + "- 'Get' sanitizies then Joins all strings in the list into a single output string that is passed into RoboCopy.\n" + "- 'Set' clears the ExcludedDirectories list, then splits the input string using regex to repopulate the list." )] public string ExcludeDirectories { get { string RetString = ""; foreach (string s in excludedDirs) { RetString += s.WrapPath() + " "; } return RetString.Trim(); } set { excludedDirs.Clear(); if (value.IsNullOrWhiteSpace()) return; ParseAndAddToList(value, excludedDirs); } } /// /// Allows you to supply a set of files to copy or use wildcard characters (* or ?).
/// JobOptions file saves these into the /IF (Include Files) section ///
public List ExcludedDirectories { get { return excludedDirs; } } /// /// Excludes changed files. /// [/XC] /// public virtual bool ExcludeChanged { get; set; } /// /// Excludes newer files. /// [/XN] /// public virtual bool ExcludeNewer { get; set; } /// /// Excludes older files. /// [/XO] /// public virtual bool ExcludeOlder { get; set; } /// /// Excludes extra files and directories. /// [/XX] /// public virtual bool ExcludeExtra { get; set; } /// /// Excludes lonely files and directories. /// [/XL] /// public virtual bool ExcludeLonely { get; set; } /// /// Includes the same files. /// [/IS] /// public virtual bool IncludeSame { get; set; } /// /// Includes tweaked files. /// [/IT] /// public virtual bool IncludeTweaked { get; set; } /// /// Zero indicates that this feature is turned off. /// Specifies the maximum file size (to exclude files bigger than N bytes). /// [/MAX:N] /// public virtual long MaxFileSize { get; set; } /// /// Zero indicates that this feature is turned off. /// Specifies the minimum file size (to exclude files smaller than N bytes). /// [/MIN:N] /// public virtual long MinFileSize { get; set; } /// /// Specifies the maximum file age (to exclude files older than N days or date). /// [/MAXAGE:N OR YYYYMMDD] /// public virtual string MaxFileAge { get; set; } /// /// Specifies the minimum file age (exclude files newer than N days or date). /// [/MINAGE:N OR YYYYMMDD] /// public virtual string MinFileAge { get; set; } /// /// Specifies the maximum last access date (excludes files unused since Date). /// [/MAXLAD:YYYYMMDD] /// public virtual string MaxLastAccessDate { get; set; } /// /// Specifies the minimum last access date (excludes files used since N) If N is less /// than 1900, N specifies the number of days. Otherwise, N specifies a date /// in the format YYYYMMDD. /// [/MINLAD:N or YYYYMMDD] /// public virtual string MinLastAccessDate { get; set; } /// /// Excludes junction points, which are normally included by default. /// [/XJ] /// public virtual bool ExcludeJunctionPoints { get; set; } /// /// Assumes FAT file times (two-second precision). /// [/FFT] /// public virtual bool UseFatFileTimes { get; set; } /// /// Compensates for one-hour DST time differences. /// [/DST] /// public virtual bool CompensateForDstDifference { get; set; } /// /// Excludes junction points for directories. /// [/XJD] /// public virtual bool ExcludeJunctionPointsForDirectories { get; set; } /// /// Excludes junction points for files. /// [/XJF] /// public virtual bool ExcludeJunctionPointsForFiles { get; set; } #endregion Public Properties /// /// public void SetIncludedAttributes(FileAttributes? AttributesToInclude) => this.IncludeAttributes = ConvertFileAttrToString(AttributesToInclude); /// /// public void SetExcludedAttributes(FileAttributes? AttributesToExclude) => this.ExcludeAttributes = ConvertFileAttrToString(AttributesToExclude); /// /// Converts a enum to its RASHCNETO string. /// /// /// Accepts: ReadOnly, Archive, System, Hidden, Compressed, NotContentIndexed, Encrypted, Temporary, Offline
/// Ignores: All Other Attributes
/// Pass in NULL value to return empty string. /// /// RASHCNETO depending on submitted enum public static string ConvertFileAttrToString(FileAttributes? attributes) { if (attributes is null) return String.Empty; string s = ""; var Attr = (FileAttributes)attributes; if (Attr.HasFlag(FileAttributes.ReadOnly)) s += "R"; if (Attr.HasFlag(FileAttributes.Archive)) s += "A"; if (Attr.HasFlag(FileAttributes.System)) s += "S"; if (Attr.HasFlag(FileAttributes.Hidden)) s += "H"; if (Attr.HasFlag(FileAttributes.Compressed)) s += "C"; if (Attr.HasFlag(FileAttributes.NotContentIndexed)) s += "N"; if (Attr.HasFlag(FileAttributes.Encrypted)) s += "E"; if (Attr.HasFlag(FileAttributes.Temporary)) s += "T"; if (Attr.HasFlag(FileAttributes.Offline)) s += "O"; return s; } internal string Parse() { var options = new StringBuilder(); #region Set Options if (OnlyCopyArchiveFiles) options.Append(ONLY_COPY_ARCHIVE_FILES); if (OnlyCopyArchiveFilesAndResetArchiveFlag) options.Append(ONLY_COPY_ARCHIVE_FILES_AND_RESET_ARCHIVE_FLAG); if (!IncludeAttributes.IsNullOrWhiteSpace()) options.Append(string.Format(INCLUDE_ATTRIBUTES, IncludeAttributes.CleanOptionInput())); if (!ExcludeAttributes.IsNullOrWhiteSpace()) options.Append(string.Format(EXCLUDE_ATTRIBUTES, ExcludeAttributes.CleanOptionInput())); #pragma warning disable CS0618 // Marked as Obsolete for consumers, but it originally functionality is still intact, so this still works properly. if (!ExcludeFiles.IsNullOrWhiteSpace()) options.Append(string.Format(EXCLUDE_FILES, ExcludeFiles)); if (!ExcludeDirectories.IsNullOrWhiteSpace()) options.Append(string.Format(EXCLUDE_DIRECTORIES, ExcludeDirectories)); #pragma warning restore CS0618 if (ExcludeChanged) options.Append(EXCLUDE_CHANGED); if (ExcludeNewer) options.Append(EXCLUDE_NEWER); if (ExcludeOlder) options.Append(EXCLUDE_OLDER); if (ExcludeExtra) options.Append(EXCLUDE_EXTRA); if (ExcludeLonely) options.Append(EXCLUDE_LONELY); if (IncludeSame) options.Append(INCLUDE_SAME); if (IncludeTweaked) options.Append(INCLUDE_TWEAKED); if (MaxFileSize > 0) options.Append(string.Format(MAX_FILE_SIZE, MaxFileSize)); if (MinFileSize > 0) options.Append(string.Format(MIN_FILE_SIZE, MinFileSize)); if (!MaxFileAge.IsNullOrWhiteSpace()) options.Append(string.Format(MAX_FILE_AGE, MaxFileAge.CleanOptionInput())); if (!MinFileAge.IsNullOrWhiteSpace()) options.Append(string.Format(MIN_FILE_AGE, MinFileAge.CleanOptionInput())); if (!MaxLastAccessDate.IsNullOrWhiteSpace()) options.Append(string.Format(MAX_LAST_ACCESS_DATE, MaxLastAccessDate.CleanOptionInput())); if (!MinLastAccessDate.IsNullOrWhiteSpace()) options.Append(string.Format(MIN_LAST_ACCESS_DATE, MinLastAccessDate.CleanOptionInput())); if (ExcludeJunctionPoints) options.Append(EXCLUDE_JUNCTION_POINTS); if (ExcludeJunctionPointsForDirectories) options.Append(EXCLUDE_JUNCTION_POINTS_FOR_DIRECTORIES); if (ExcludeJunctionPointsForFiles) options.Append(EXCLUDE_JUNCTION_POINTS_FOR_FILES); if (UseFatFileTimes) options.Append(USE_FAT_FILE_TIMES); if (CompensateForDstDifference) options.Append(COMPENSATE_FOR_DST_DIFFERENCE); #endregion Set Options return options.ToString(); } /// /// Combine this object with another RetryOptions object.
/// Any properties marked as true take priority. IEnumerable items are combined.
/// String\Long Values will only be replaced if the primary object has a null/empty value for that property. ///
/// public void Merge(SelectionOptions options) { //File Attributes IncludeAttributes = IncludeAttributes.CombineCharArr(options.IncludeAttributes); ExcludeAttributes = ExcludeAttributes.CombineCharArr(options.ExcludeAttributes); //File Age MaxFileAge = MaxFileAge.ReplaceIfEmpty(options.MaxFileAge); MinFileAge = MaxFileAge.ReplaceIfEmpty(options.MinFileAge); MaxLastAccessDate = MaxFileAge.ReplaceIfEmpty(options.MaxLastAccessDate); MinLastAccessDate = MaxFileAge.ReplaceIfEmpty(options.MinLastAccessDate); //Long MaxFileSize |= options.MaxFileSize; MinFileSize |= options.MinFileSize; //Lists ExcludedFiles.AddRange(options.ExcludedFiles); ExcludedDirectories.AddRange(options.ExcludedDirectories); //Bools OnlyCopyArchiveFiles |= options.OnlyCopyArchiveFiles; OnlyCopyArchiveFilesAndResetArchiveFlag |= options.OnlyCopyArchiveFilesAndResetArchiveFlag; ExcludeChanged |= options.ExcludeChanged; ExcludeNewer |= options.ExcludeNewer; ExcludeOlder |= options.ExcludeOlder; ExcludeExtra |= options.ExcludeExtra; ExcludeLonely |= options.ExcludeLonely; IncludeSame |= options.IncludeSame; IncludeTweaked |= options.IncludeTweaked; ExcludeJunctionPoints |= options.ExcludeJunctionPoints; ExcludeJunctionPointsForFiles |= options.ExcludeJunctionPointsForFiles; ExcludeJunctionPointsForDirectories |= options.ExcludeJunctionPointsForDirectories; UseFatFileTimes |= options.UseFatFileTimes; CompensateForDstDifference |= options.CompensateForDstDifference; ; } /// /// Enum to define various selection options that can be toggled for the RoboCopy process. /// [Flags] public enum SelectionFlags { /// /// Set RoboCopy options to their defaults /// Default = 0, /// ExcludeChanged = 1, /// ExcludeExtra = 2, /// ExcludeLonely = 4, /// ExcludeNewer = 8, /// ExcludeOlder = 16, /// ExcludeJunctionPoints = 32, /// ExcludeJunctionPointsForDirectories = 64, /// ExcludeJunctionPointsForFiles = 128, /// IncludeSame = 256, /// IncludeTweaked = 512, /// OnlyCopyArchiveFiles = 1024, /// OnlyCopyArchiveFilesAndResetArchiveFlag = 2048, } /// /// Apply the to this command /// /// Options to apply public virtual void ApplySelectionFlags(SelectionFlags flags) { this.ExcludeChanged = flags.HasFlag(SelectionFlags.ExcludeChanged); this.ExcludeExtra = flags.HasFlag(SelectionFlags.ExcludeExtra); this.ExcludeJunctionPoints = flags.HasFlag(SelectionFlags.ExcludeJunctionPoints); this.ExcludeJunctionPointsForDirectories = flags.HasFlag(SelectionFlags.ExcludeJunctionPointsForDirectories); this.ExcludeJunctionPointsForFiles = flags.HasFlag(SelectionFlags.ExcludeJunctionPointsForFiles); this.ExcludeLonely = flags.HasFlag(SelectionFlags.ExcludeLonely); this.ExcludeNewer = flags.HasFlag(SelectionFlags.ExcludeNewer); this.ExcludeOlder = flags.HasFlag(SelectionFlags.ExcludeOlder); this.IncludeSame = flags.HasFlag(SelectionFlags.IncludeSame); this.IncludeTweaked = flags.HasFlag(SelectionFlags.IncludeTweaked); this.OnlyCopyArchiveFiles = flags.HasFlag(SelectionFlags.OnlyCopyArchiveFiles); this.OnlyCopyArchiveFilesAndResetArchiveFlag = flags.HasFlag(SelectionFlags.OnlyCopyArchiveFilesAndResetArchiveFlag); } /// /// Translate the selection bools of this object to its representation /// /// The representation of this object. public SelectionFlags GetSelectionFlags() { var flags = SelectionFlags.Default; if (this.ExcludeChanged) flags |= SelectionFlags.ExcludeChanged; if (this.ExcludeExtra) flags |= SelectionFlags.ExcludeExtra; if (this.ExcludeJunctionPoints) flags |= SelectionFlags.ExcludeJunctionPoints; if (this.ExcludeJunctionPointsForDirectories) flags |= SelectionFlags.ExcludeJunctionPointsForDirectories; if (this.ExcludeJunctionPointsForFiles) flags |= SelectionFlags.ExcludeJunctionPointsForFiles; if (this.ExcludeLonely) flags |= SelectionFlags.ExcludeLonely; if (this.ExcludeNewer) flags |= SelectionFlags.ExcludeNewer; if (this.ExcludeOlder) flags |= SelectionFlags.ExcludeOlder; if (this.IncludeSame) flags |= SelectionFlags.IncludeSame; if (this.IncludeTweaked) flags |= SelectionFlags.IncludeTweaked; if (this.OnlyCopyArchiveFiles) flags |= SelectionFlags.OnlyCopyArchiveFiles; if (this.OnlyCopyArchiveFilesAndResetArchiveFlag) flags |= SelectionFlags.OnlyCopyArchiveFilesAndResetArchiveFlag; return flags; } } }