685 lines
31 KiB
C#
685 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace RoboSharp
|
|
{
|
|
/// <summary>
|
|
/// Source, Destination, and options for how to move or copy files.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <see href="https://github.com/tjscience/RoboSharp/wiki/CopyOptions"/>
|
|
/// </remarks>
|
|
public class CopyOptions : ICloneable
|
|
{
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Create new CopyOptions with Default Settings
|
|
/// </summary>
|
|
public CopyOptions() { }
|
|
|
|
/// <summary>
|
|
/// Create a new CopyOptions object with the provided settings
|
|
/// </summary>
|
|
/// <param name="source"><inheritdoc cref="Source" path="*"/></param>
|
|
/// <param name="destination"><inheritdoc cref="Destination" path="*"/></param>
|
|
/// <param name="flags"><inheritdoc cref="CopyActionFlags" path="*"/></param>
|
|
public CopyOptions(string source, string destination, CopyActionFlags flags)
|
|
{
|
|
this.Source = source ?? string.Empty;
|
|
this.Destination = destination ?? string.Empty;
|
|
this.ApplyActionFlags(flags);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clone a CopyOptions Object
|
|
/// </summary>
|
|
/// <param name="copyOptions">CopyOptions object to clone</param>
|
|
/// <param name="NewSource">Specify a new source if desired. If left as null, will use Source from <paramref name="copyOptions"/></param>
|
|
/// <param name="NewDestination">Specify a new source if desired. If left as null, will use Destination from <paramref name="copyOptions"/></param>
|
|
public CopyOptions(CopyOptions copyOptions, string NewSource = null, string NewDestination = null)
|
|
{
|
|
Source = NewSource ?? copyOptions.Source;
|
|
Destination = NewDestination ?? copyOptions.Destination;
|
|
|
|
AddAttributes = copyOptions.AddAttributes;
|
|
CheckPerFile = copyOptions.CheckPerFile;
|
|
CopyAll = copyOptions.CopyAll;
|
|
CopyFilesWithSecurity = copyOptions.CopyFilesWithSecurity;
|
|
CopyFlags = copyOptions.CopyFlags;
|
|
CopySubdirectories = copyOptions.CopySubdirectories;
|
|
CopySubdirectoriesIncludingEmpty = copyOptions.CopySubdirectoriesIncludingEmpty;
|
|
CopySymbolicLink = copyOptions.CopySymbolicLink;
|
|
CreateDirectoryAndFileTree = copyOptions.CreateDirectoryAndFileTree;
|
|
Depth = copyOptions.Depth;
|
|
DirectoryCopyFlags = copyOptions.DirectoryCopyFlags;
|
|
DoNotCopyDirectoryInfo = copyOptions.DoNotCopyDirectoryInfo;
|
|
DoNotUseWindowsCopyOffload = copyOptions.DoNotUseWindowsCopyOffload;
|
|
EnableBackupMode = copyOptions.EnableBackupMode;
|
|
EnableEfsRawMode = copyOptions.EnableEfsRawMode;
|
|
EnableRestartMode = copyOptions.EnableRestartMode;
|
|
EnableRestartModeWithBackupFallback = copyOptions.EnableRestartModeWithBackupFallback;
|
|
FatFiles = copyOptions.FatFiles;
|
|
FileFilter = copyOptions.FileFilter;
|
|
FixFileSecurityOnAllFiles = copyOptions.FixFileSecurityOnAllFiles;
|
|
FixFileTimesOnAllFiles = copyOptions.FixFileTimesOnAllFiles;
|
|
InterPacketGap = copyOptions.InterPacketGap;
|
|
Mirror = copyOptions.Mirror;
|
|
MonitorSourceChangesLimit = copyOptions.MonitorSourceChangesLimit;
|
|
MonitorSourceTimeLimit = copyOptions.MonitorSourceTimeLimit;
|
|
MoveFiles = copyOptions.MoveFiles;
|
|
MoveFilesAndDirectories = copyOptions.MoveFilesAndDirectories;
|
|
MultiThreadedCopiesCount = copyOptions.MultiThreadedCopiesCount;
|
|
Purge = copyOptions.Purge;
|
|
RemoveAttributes = copyOptions.RemoveAttributes;
|
|
RemoveFileInformation = copyOptions.RemoveFileInformation;
|
|
RunHours = copyOptions.RunHours;
|
|
TurnLongPathSupportOff = copyOptions.TurnLongPathSupportOff;
|
|
UseUnbufferedIo = copyOptions.UseUnbufferedIo;
|
|
}
|
|
|
|
/// <inheritdoc cref="CopyOptions.CopyOptions(CopyOptions, string, string)"/>
|
|
public CopyOptions Clone(string NewSource = null, string NewDestination = null) => new CopyOptions(this, NewSource, NewDestination);
|
|
object ICloneable.Clone() => Clone();
|
|
|
|
#endregion
|
|
|
|
#region Option Constants
|
|
|
|
internal const string COPY_SUBDIRECTORIES = "/S ";
|
|
internal const string COPY_SUBDIRECTORIES_INCLUDING_EMPTY = "/E ";
|
|
internal const string DEPTH = "/LEV:{0} ";
|
|
internal const string ENABLE_RESTART_MODE = "/Z ";
|
|
internal const string ENABLE_BACKUP_MODE = "/B ";
|
|
internal const string ENABLE_RESTART_MODE_WITH_BACKUP_FALLBACK = "/ZB ";
|
|
internal const string USE_UNBUFFERED_IO = "/J ";
|
|
internal const string ENABLE_EFSRAW_MODE = "/EFSRAW ";
|
|
internal const string COPY_FLAGS = "/COPY:{0} ";
|
|
internal const string COPY_FILES_WITH_SECURITY = "/SEC ";
|
|
internal const string COPY_ALL = "/COPYALL ";
|
|
internal const string REMOVE_FILE_INFORMATION = "/NOCOPY ";
|
|
internal const string FIX_FILE_SECURITY_ON_ALL_FILES = "/SECFIX ";
|
|
internal const string FIX_FILE_TIMES_ON_ALL_FILES = "/TIMFIX ";
|
|
internal const string PURGE = "/PURGE ";
|
|
internal const string MIRROR = "/MIR ";
|
|
internal const string MOVE_FILES = "/MOV ";
|
|
internal const string MOVE_FILES_AND_DIRECTORIES = "/MOVE ";
|
|
internal const string ADD_ATTRIBUTES = "/A+:{0} ";
|
|
internal const string REMOVE_ATTRIBUTES = "/A-:{0} ";
|
|
internal const string CREATE_DIRECTORY_AND_FILE_TREE = "/CREATE ";
|
|
internal const string FAT_FILES = "/FAT ";
|
|
internal const string TURN_LONG_PATH_SUPPORT_OFF = "/256 ";
|
|
internal const string MONITOR_SOURCE_CHANGES_LIMIT = "/MON:{0} ";
|
|
internal const string MONITOR_SOURCE_TIME_LIMIT = "/MOT:{0} ";
|
|
internal const string RUN_HOURS = "/RH:{0} ";
|
|
internal const string CHECK_PER_FILE = "/PF ";
|
|
internal const string INTER_PACKET_GAP = "/IPG:{0} ";
|
|
internal const string COPY_SYMBOLIC_LINK = "/SL ";
|
|
internal const string MULTITHREADED_COPIES_COUNT = "/MT:{0} ";
|
|
internal const string DIRECTORY_COPY_FLAGS = "/DCOPY:{0} ";
|
|
internal const string DO_NOT_COPY_DIRECTORY_INFO = "/NODCOPY ";
|
|
internal const string DO_NOT_USE_WINDOWS_COPY_OFFLOAD = "/NOOFFLOAD ";
|
|
|
|
#endregion Option Constants
|
|
|
|
#region Option Defaults
|
|
|
|
/// <summary>
|
|
/// The Default File Filter used that will allow copying of all files
|
|
/// </summary>
|
|
public const string DefaultFileFilter = "*.*" ;
|
|
|
|
private IEnumerable<string> fileFilter = new[] { DefaultFileFilter };
|
|
private string copyFlags = "DAT";
|
|
private string directoryCopyFlags = VersionManager.Version >= 6.2 ? "DA" : "T";
|
|
|
|
#endregion Option Defaults
|
|
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// The source file path where the RoboCommand is copying files from.
|
|
/// </summary>
|
|
public virtual string Source { get { return _source; } set { _source = value.CleanDirectoryPath(); } }
|
|
private string _source;
|
|
|
|
/// <summary>
|
|
/// The destination file path where the RoboCommand is copying files to.
|
|
/// </summary>
|
|
public virtual string Destination { get { return _destination; } set { _destination = value.CleanDirectoryPath(); } }
|
|
private string _destination;
|
|
|
|
/// <summary>
|
|
/// Allows you to supply a set of files to copy or use wildcard characters (* or ?). <br/>
|
|
/// JobOptions file saves these into the /IF (Include Files) section
|
|
/// </summary>
|
|
public IEnumerable<string> FileFilter
|
|
{
|
|
get
|
|
{
|
|
return fileFilter;
|
|
}
|
|
set
|
|
{
|
|
fileFilter = value;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Copies subdirectories. Note that this option excludes empty directories.
|
|
/// [/S]
|
|
/// </summary>
|
|
public virtual bool CopySubdirectories { get; set; }
|
|
/// <summary>
|
|
/// Copies subdirectories. Note that this option includes empty directories.
|
|
/// [/E]
|
|
/// </summary>
|
|
public virtual bool CopySubdirectoriesIncludingEmpty { get; set; }
|
|
/// <summary>
|
|
/// Copies only the top N levels of the source directory tree. The default is
|
|
/// zero which does not limit the depth.
|
|
/// [/LEV:N]
|
|
/// </summary>
|
|
public virtual int Depth { get; set; }
|
|
/// <summary>
|
|
/// Copies files in Restart mode.
|
|
/// [/Z]
|
|
/// </summary>
|
|
public virtual bool EnableRestartMode { get; set; }
|
|
/// <summary>
|
|
/// Copies files in Backup mode.
|
|
/// [/B]
|
|
/// </summary>
|
|
public virtual bool EnableBackupMode { get; set; }
|
|
/// <summary>
|
|
/// Uses Restart mode. If access is denied, this option uses Backup mode.
|
|
/// [/ZB]
|
|
/// </summary>
|
|
public virtual bool EnableRestartModeWithBackupFallback { get; set; }
|
|
/// <summary>
|
|
/// Copy using unbuffered I/O (recommended for large files).
|
|
/// [/J]
|
|
/// </summary>
|
|
public virtual bool UseUnbufferedIo { get; set; }
|
|
/// <summary>
|
|
/// Copies all encrypted files in EFS RAW mode.
|
|
/// [/EFSRAW]
|
|
/// </summary>
|
|
public virtual bool EnableEfsRawMode { get; set; }
|
|
/// <summary>
|
|
/// This property should be set to a string consisting of all the flags to include (eg. DAT; DATSOU)
|
|
/// Specifies the file properties to be copied. The following are the valid values for this option:
|
|
///D Data
|
|
///A Attributes
|
|
///T Time stamps
|
|
///S NTFS access control list (ACL)
|
|
///O Owner information
|
|
///U Auditing information
|
|
///The default value for copyflags is DAT (data, attributes, and time stamps).
|
|
///[/COPY:copyflags]
|
|
/// </summary>
|
|
public string CopyFlags
|
|
{
|
|
get
|
|
{
|
|
return copyFlags;
|
|
}
|
|
set => copyFlags = value;
|
|
}
|
|
/// <summary>
|
|
/// Copies files with security (equivalent to /copy:DAT).
|
|
/// [/SEC]
|
|
/// </summary>
|
|
public virtual bool CopyFilesWithSecurity { get; set; }
|
|
/// <summary>
|
|
/// Copies all file information (equivalent to /copy:DATSOU).
|
|
/// [/COPYALL]
|
|
/// </summary>
|
|
public virtual bool CopyAll { get; set; }
|
|
/// <summary>
|
|
/// Copies no file information (useful with Purge option).
|
|
/// [/NOCOPY]
|
|
/// </summary>
|
|
public virtual bool RemoveFileInformation { get; set; }
|
|
/// <summary>
|
|
/// Fixes file security on all files, even skipped ones.
|
|
/// [/SECFIX]
|
|
/// </summary>
|
|
public virtual bool FixFileSecurityOnAllFiles { get; set; }
|
|
/// <summary>
|
|
/// Fixes file times on all files, even skipped ones.
|
|
/// [/TIMFIX]
|
|
/// </summary>
|
|
public virtual bool FixFileTimesOnAllFiles { get; set; }
|
|
/// <summary>
|
|
/// Deletes destination files and directories that no longer exist in the source.
|
|
/// [/PURGE]
|
|
/// </summary>
|
|
public virtual bool Purge { get; set; }
|
|
/// <summary>
|
|
/// Mirrors a directory tree (equivalent to CopySubdirectoriesIncludingEmpty plus Purge).
|
|
/// [/MIR]
|
|
/// </summary>
|
|
public virtual bool Mirror { get; set; }
|
|
/// <summary>
|
|
/// Moves files, and deletes them from the source after they are copied.
|
|
/// [/MOV]
|
|
/// </summary>
|
|
public virtual bool MoveFiles { get; set; }
|
|
/// <summary>
|
|
/// Moves files and directories, and deletes them from the source after they are copied.
|
|
/// [/MOVE]
|
|
/// </summary>
|
|
public virtual bool MoveFilesAndDirectories { get; set; }
|
|
/// <summary>
|
|
/// This property should be set to a string consisting of all the attributes to add (eg. AH; RASHCNET).
|
|
/// Adds the specified attributes to copied files.
|
|
/// [/A+:attributes]
|
|
/// </summary>
|
|
public string AddAttributes { get; set; }
|
|
/// <summary>
|
|
/// This property should be set to a string consisting of all the attributes to remove (eg. AH; RASHCNET).
|
|
/// Removes the specified attributes from copied files.
|
|
/// [/A-:attributes]
|
|
/// </summary>
|
|
public string RemoveAttributes { get; set; }
|
|
/// <summary>
|
|
/// Creates a directory tree and zero-length files only.
|
|
/// [/CREATE]
|
|
/// </summary>
|
|
public virtual bool CreateDirectoryAndFileTree { get; set; }
|
|
/// <summary>
|
|
/// Creates destination files by using 8.3 character-length FAT file names only.
|
|
/// [/FAT]
|
|
/// </summary>
|
|
public virtual bool FatFiles { get; set; }
|
|
/// <summary>
|
|
/// Turns off support for very long paths (longer than 256 characters).
|
|
/// [/256]
|
|
/// </summary>
|
|
public virtual bool TurnLongPathSupportOff { get; set; }
|
|
/// <summary>
|
|
/// The default value of zero indicates that you do not wish to monitor for changes.
|
|
/// Monitors the source, and runs again when more than N changes are detected.
|
|
/// [/MON:N]
|
|
/// </summary>
|
|
public virtual int MonitorSourceChangesLimit { get; set; }
|
|
/// <summary>
|
|
/// The default value of zero indicates that you do not wish to monitor for changes.
|
|
/// Monitors source, and runs again in M minutes if changes are detected.
|
|
/// [/MOT:M]
|
|
/// </summary>
|
|
public virtual int MonitorSourceTimeLimit { get; set; }
|
|
/// <summary>
|
|
/// Specifies run times when new copies may be started. ( Copy Operation is scheduled to only operate within specified timeframe )
|
|
/// [/rh:hhmm-hhmm] <br/>
|
|
/// If copy operation is unfinished, robocopy will remain active in idle state until the specified time, at which it will resume copying.<br/>
|
|
/// Must be in correct format. Incorrectly formatted strings will be ignored. <para/>
|
|
/// Examples:<br/>
|
|
/// 1500-1800 -> Robocopy will only copy between 3 PM and 5 PM <br/>
|
|
/// 0015-0530 -> Robocopy will only copy between 12:15 AM and 5:30 AM <br/>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// If this is set up, then the robocopy process will remain active after the program exits if the calling asemmbly does not call <see cref="RoboCommand.Stop()"/> prior to exiting the application.
|
|
/// </remarks>
|
|
public string RunHours
|
|
{
|
|
get => runHours;
|
|
set
|
|
{
|
|
if (String.IsNullOrWhiteSpace(value))
|
|
runHours = value?.Trim() ?? string.Empty;
|
|
else if (CheckRunHoursString(value))
|
|
runHours = value.Trim();
|
|
}
|
|
}
|
|
private string runHours;
|
|
|
|
/// <summary>
|
|
/// Checks the scheduled /RH (run hours) per file instead of per pass.
|
|
/// [/PF]
|
|
/// </summary>
|
|
public virtual bool CheckPerFile { get; set; }
|
|
|
|
/// <summary>
|
|
/// The default value of zero indicates that this feature is turned off.
|
|
/// Specifies the inter-packet gap to free bandwidth on slow lines.
|
|
/// [/IPG:N]
|
|
/// </summary>
|
|
public virtual int InterPacketGap { get; set; }
|
|
/// <summary>
|
|
/// Copies the symbolic link instead of the target.
|
|
/// [/SL]
|
|
/// </summary>
|
|
public virtual bool CopySymbolicLink { get; set; }
|
|
|
|
/// <summary>
|
|
/// The default value of zero indicates that this feature is turned off.
|
|
/// Creates multi-threaded copies with N threads. Must be an integer between 1 and 128.
|
|
/// The MultiThreadedCopiesCount parameter cannot be used with the /IPG and EnableEfsRawMode parameters.
|
|
/// [/MT:N]
|
|
/// </summary>
|
|
public virtual int MultiThreadedCopiesCount { get; set; }
|
|
/// <summary>
|
|
/// What to copy for directories (default is DA).
|
|
/// (copyflags: D=Data, A=Attributes, T=Timestamps).
|
|
/// [/DCOPY:copyflags]
|
|
/// </summary>
|
|
public string DirectoryCopyFlags
|
|
{
|
|
get { return directoryCopyFlags; }
|
|
set { directoryCopyFlags = value; }
|
|
}
|
|
/// <summary>
|
|
/// Do not copy any directory info.
|
|
/// [/NODCOPY]
|
|
/// </summary>
|
|
public virtual bool DoNotCopyDirectoryInfo { get; set; }
|
|
/// <summary>
|
|
/// Copy files without using the Windows Copy Offload mechanism.
|
|
/// [/NOOFFLOAD]
|
|
/// </summary>
|
|
public virtual bool DoNotUseWindowsCopyOffload { get; set; }
|
|
|
|
#endregion Public Properties
|
|
|
|
#region < Parse (INTERNAL) >
|
|
|
|
/// <summary>
|
|
/// Used by the Parse method to sanitize path for the command options.<br/>
|
|
/// Evaluate the path. If needed, wrap it in quotes. <br/>
|
|
/// If the path ends in a DirectorySeperatorChar, santize it to work as expected. <br/>
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
/// <returns>Each return string includes a space at the end of the string to seperate it from the next option variable.</returns>
|
|
private string WrapPath(string path)
|
|
{
|
|
if (!path.Contains(" ")) return $"{path} "; //No spaces, just return the path
|
|
//Below this line, the path contains a space, so it must be wrapped in quotes.
|
|
if (path.EndsWithDirectorySeperator()) return $"\"{path}.\" "; // Ends with a directory seperator - Requires a '.' to denote using that directory. ex: "F:\."
|
|
return $"\"{path}\" ";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse the class properties and generate the command arguments
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal string Parse()
|
|
{
|
|
Debugger.Instance.DebugMessage("Parsing CopyOptions...");
|
|
var version = VersionManager.Version;
|
|
var options = new StringBuilder();
|
|
|
|
// Set Source and Destination
|
|
options.Append(WrapPath(Source));
|
|
options.Append(WrapPath(Destination));
|
|
|
|
// Set FileFilter
|
|
// Quote each FileFilter item. The quotes are trimmed first to ensure that they are applied only once.
|
|
var fileFilterQuotedItems = FileFilter.Select(word => "\"" + word.Trim('"') + "\"");
|
|
string fileFilter = String.Join(" ", fileFilterQuotedItems);
|
|
options.Append($"{fileFilter} ");
|
|
|
|
Debugger.Instance.DebugMessage(string.Format("Parsing CopyOptions progress ({0}).", options.ToString()));
|
|
|
|
#region Set Options
|
|
var cleanedCopyFlags = CopyFlags.CleanOptionInput();
|
|
var cleanedDirectoryCopyFlags = DirectoryCopyFlags.CleanOptionInput();
|
|
|
|
if (!cleanedCopyFlags.IsNullOrWhiteSpace())
|
|
{
|
|
options.Append(string.Format(COPY_FLAGS, cleanedCopyFlags));
|
|
Debugger.Instance.DebugMessage(string.Format("Parsing CopyOptions progress ({0}).", options.ToString()));
|
|
}
|
|
if (!cleanedDirectoryCopyFlags.IsNullOrWhiteSpace() && version >= 5.1260026)
|
|
{
|
|
options.Append(string.Format(DIRECTORY_COPY_FLAGS, cleanedDirectoryCopyFlags));
|
|
Debugger.Instance.DebugMessage(string.Format("Parsing CopyOptions progress ({0}).", options.ToString()));
|
|
}
|
|
if (CopySubdirectories)
|
|
{
|
|
options.Append(COPY_SUBDIRECTORIES);
|
|
Debugger.Instance.DebugMessage(string.Format("Parsing CopyOptions progress ({0}).", options.ToString()));
|
|
}
|
|
if (CopySubdirectoriesIncludingEmpty)
|
|
options.Append(COPY_SUBDIRECTORIES_INCLUDING_EMPTY);
|
|
if (Depth > 0)
|
|
options.Append(string.Format(DEPTH, Depth));
|
|
if (EnableRestartMode)
|
|
options.Append(ENABLE_RESTART_MODE);
|
|
if (EnableBackupMode)
|
|
options.Append(ENABLE_BACKUP_MODE);
|
|
if (EnableRestartModeWithBackupFallback)
|
|
options.Append(ENABLE_RESTART_MODE_WITH_BACKUP_FALLBACK);
|
|
if (UseUnbufferedIo && version >= 6.2)
|
|
options.Append(USE_UNBUFFERED_IO);
|
|
if (EnableEfsRawMode)
|
|
options.Append(ENABLE_EFSRAW_MODE);
|
|
if (CopyFilesWithSecurity)
|
|
options.Append(COPY_FILES_WITH_SECURITY);
|
|
if (CopyAll)
|
|
options.Append(COPY_ALL);
|
|
if (RemoveFileInformation)
|
|
options.Append(REMOVE_FILE_INFORMATION);
|
|
if (FixFileSecurityOnAllFiles)
|
|
options.Append(FIX_FILE_SECURITY_ON_ALL_FILES);
|
|
if (FixFileTimesOnAllFiles)
|
|
options.Append(FIX_FILE_TIMES_ON_ALL_FILES);
|
|
if (Purge)
|
|
options.Append(PURGE);
|
|
if (Mirror)
|
|
options.Append(MIRROR);
|
|
if (MoveFiles)
|
|
options.Append(MOVE_FILES);
|
|
if (MoveFilesAndDirectories)
|
|
options.Append(MOVE_FILES_AND_DIRECTORIES);
|
|
if (!AddAttributes.IsNullOrWhiteSpace())
|
|
options.Append(string.Format(ADD_ATTRIBUTES, AddAttributes.CleanOptionInput()));
|
|
if (!RemoveAttributes.IsNullOrWhiteSpace())
|
|
options.Append(string.Format(REMOVE_ATTRIBUTES, RemoveAttributes.CleanOptionInput()));
|
|
if (CreateDirectoryAndFileTree)
|
|
options.Append(CREATE_DIRECTORY_AND_FILE_TREE);
|
|
if (FatFiles)
|
|
options.Append(FAT_FILES);
|
|
if (TurnLongPathSupportOff)
|
|
options.Append(TURN_LONG_PATH_SUPPORT_OFF);
|
|
if (MonitorSourceChangesLimit > 0)
|
|
options.Append(string.Format(MONITOR_SOURCE_CHANGES_LIMIT, MonitorSourceChangesLimit));
|
|
if (MonitorSourceTimeLimit > 0)
|
|
options.Append(string.Format(MONITOR_SOURCE_TIME_LIMIT, MonitorSourceTimeLimit));
|
|
if (!RunHours.IsNullOrWhiteSpace())
|
|
options.Append(string.Format(RUN_HOURS, RunHours.CleanOptionInput()));
|
|
if (CheckPerFile)
|
|
options.Append(CHECK_PER_FILE);
|
|
if (InterPacketGap > 0)
|
|
options.Append(string.Format(INTER_PACKET_GAP, InterPacketGap));
|
|
if (CopySymbolicLink)
|
|
options.Append(COPY_SYMBOLIC_LINK);
|
|
if (MultiThreadedCopiesCount > 0)
|
|
options.Append(string.Format(MULTITHREADED_COPIES_COUNT, MultiThreadedCopiesCount));
|
|
if (DoNotCopyDirectoryInfo && version >= 6.2)
|
|
options.Append(DO_NOT_COPY_DIRECTORY_INFO);
|
|
if (DoNotUseWindowsCopyOffload && version >= 6.2)
|
|
options.Append(DO_NOT_USE_WINDOWS_COPY_OFFLOAD);
|
|
#endregion Set Options
|
|
|
|
var parsedOptions = options.ToString();
|
|
Debugger.Instance.DebugMessage(string.Format("CopyOptions parsed ({0}).", parsedOptions));
|
|
return parsedOptions;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region < RunHours (Public) >
|
|
|
|
private static Regex RunHours_OverallRegex = new Regex("^(?<StartTime>[0-2][0-9][0-5][0-9])-(?<EndTime>[0-2][0-9][0-5][0-9])$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
|
private static Regex RunHours_Check1 = new Regex("^[0-1][0-9][0-5][0-9]$", RegexOptions.Compiled); // Checks 0000 - 1959
|
|
private static Regex RunHours_Check2 = new Regex("^[2][0-3][0-5][0-9]$", RegexOptions.Compiled); // Checks 2000 - 2359
|
|
private GroupCollection RunHoursGroups => RunHours_OverallRegex.Match(RunHours).Groups;
|
|
|
|
/// <summary>
|
|
/// Get the StartTime portion of <see cref="RunHours"/>
|
|
/// </summary>
|
|
/// <returns>hhmm or String.Empty</returns>
|
|
public string GetRunHours_StartTime()
|
|
{
|
|
if (RunHours.IsNullOrWhiteSpace()) return string.Empty;
|
|
return RunHoursGroups["StartTime"]?.Value ?? String.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the EndTime portion of <see cref="RunHours"/>
|
|
/// </summary>
|
|
/// <returns>hhmm or String.Empty</returns>
|
|
public string GetRunHours_EndTime()
|
|
{
|
|
if (RunHours.IsNullOrWhiteSpace()) return string.Empty;
|
|
return RunHoursGroups["EndTime"]?.Value ?? String.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method to check if some string is valid for use as with the <see cref="RunHours"/> property.
|
|
/// </summary>
|
|
/// <param name="runHours"></param>
|
|
/// <returns>True if correct format, otherwise false</returns>
|
|
public bool CheckRunHoursString(string runHours)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(runHours)) return true;
|
|
if (!RunHours_OverallRegex.IsMatch(runHours.Trim())) return false;
|
|
var times = RunHours_OverallRegex.Match(runHours.Trim());
|
|
bool StartMatch = RunHours_Check1.IsMatch(times.Groups["StartTime"].Value) || RunHours_Check2.IsMatch(times.Groups["StartTime"].Value);
|
|
bool EndMatch = RunHours_Check1.IsMatch(times.Groups["EndTime"].Value) || RunHours_Check2.IsMatch(times.Groups["EndTime"].Value);
|
|
return StartMatch && EndMatch;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region < Flags >
|
|
|
|
/// <summary>
|
|
/// Enum to define the high-level copy action to be taken by RoboCopy process.
|
|
/// </summary>
|
|
[Flags]
|
|
public enum CopyActionFlags
|
|
{
|
|
/// <summary>
|
|
/// Default Functionality is to only copy the files within the source directory - does not copy any files within the subfolders.
|
|
/// </summary>
|
|
Default = 0,
|
|
/// <inheritdoc cref="CopyOptions.CopySubdirectories"/>
|
|
CopySubdirectories = 1,
|
|
/// <inheritdoc cref="CopyOptions.CopySubdirectoriesIncludingEmpty"/>
|
|
CopySubdirectoriesIncludingEmpty = 2,
|
|
/// <inheritdoc cref="CopyOptions.Purge"/>
|
|
Purge = 4,
|
|
/// <inheritdoc cref="CopyOptions.CreateDirectoryAndFileTree"/>
|
|
CreateDirectoryAndFileTree = 8,
|
|
/// <inheritdoc cref="CopyOptions.MoveFiles"/>
|
|
MoveFiles = 16,
|
|
/// <inheritdoc cref="CopyOptions.MoveFilesAndDirectories"/>
|
|
MoveFilesAndDirectories = 32,
|
|
/// <inheritdoc cref="CopyOptions.Mirror"/>
|
|
Mirror = CopySubdirectoriesIncludingEmpty | Purge, //6
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply the <see cref="CopyActionFlags"/> to the command
|
|
/// </summary>
|
|
/// <param name="flags">Options to apply</param>
|
|
public virtual void ApplyActionFlags(CopyActionFlags flags)
|
|
{
|
|
this.CopySubdirectories = flags.HasFlag(CopyActionFlags.CopySubdirectories);
|
|
this.CopySubdirectoriesIncludingEmpty = flags.HasFlag(CopyActionFlags.CopySubdirectoriesIncludingEmpty);
|
|
this.Purge = flags.HasFlag(CopyActionFlags.Purge);
|
|
this.Mirror = flags.HasFlag(CopyActionFlags.Mirror);
|
|
this.MoveFiles = flags.HasFlag(CopyActionFlags.MoveFiles);
|
|
this.MoveFilesAndDirectories = flags.HasFlag(CopyActionFlags.MoveFilesAndDirectories);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="CopyActionFlags"/> representation of this object
|
|
/// </summary>
|
|
public virtual CopyActionFlags GetCopyActionFlags()
|
|
{
|
|
var flags = CopyActionFlags.Default;
|
|
if (this.CopySubdirectories) flags |=CopyActionFlags.CopySubdirectories;
|
|
if (this.CopySubdirectoriesIncludingEmpty) flags |=CopyActionFlags.CopySubdirectoriesIncludingEmpty;
|
|
if (this.Purge) flags |=CopyActionFlags.Purge;
|
|
if (this.Mirror) flags |=CopyActionFlags.Mirror;
|
|
if (this.MoveFiles) flags |=CopyActionFlags.MoveFiles;
|
|
if (this.MoveFilesAndDirectories) flags |=CopyActionFlags.MoveFilesAndDirectories;
|
|
return flags;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region < Other Public Methods >
|
|
|
|
/// <summary>
|
|
/// Combine this object with another CopyOptions object. <br/>
|
|
/// Any properties marked as true take priority. IEnumerable items are combined.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Source and Destination are only taken from the merged item if this object's Source/Destination values are null/empty. <br/>
|
|
/// RunHours follows the same rules.
|
|
///
|
|
/// </remarks>
|
|
/// <param name="copyOptions"></param>
|
|
public void Merge(CopyOptions copyOptions)
|
|
{
|
|
Source = Source.ReplaceIfEmpty(copyOptions.Source);
|
|
Destination = Destination.ReplaceIfEmpty(copyOptions.Destination);
|
|
RunHours = RunHours.ReplaceIfEmpty(copyOptions.RunHours);
|
|
|
|
//int -> Take Greater Value
|
|
Depth = Depth.GetGreaterVal(copyOptions.Depth);
|
|
InterPacketGap = InterPacketGap.GetGreaterVal(copyOptions.InterPacketGap);
|
|
MonitorSourceChangesLimit = MonitorSourceChangesLimit.GetGreaterVal(copyOptions.MonitorSourceChangesLimit);
|
|
MonitorSourceTimeLimit = MonitorSourceTimeLimit.GetGreaterVal(copyOptions.MonitorSourceTimeLimit);
|
|
MultiThreadedCopiesCount = MultiThreadedCopiesCount.GetGreaterVal(copyOptions.MultiThreadedCopiesCount);
|
|
|
|
//Flags
|
|
AddAttributes = AddAttributes.CombineCharArr(copyOptions.AddAttributes);
|
|
CopyFlags = CopyFlags.CombineCharArr(copyOptions.CopyFlags);
|
|
DirectoryCopyFlags = DirectoryCopyFlags.CombineCharArr(copyOptions.DirectoryCopyFlags);
|
|
RemoveAttributes = RemoveAttributes.CombineCharArr(copyOptions.RemoveAttributes);
|
|
|
|
//IEnumerable
|
|
var list = new List<String>(FileFilter);
|
|
list.AddRange(copyOptions.FileFilter);
|
|
FileFilter = list;
|
|
|
|
//Bool
|
|
CheckPerFile |= copyOptions.CheckPerFile;
|
|
CopyAll |= copyOptions.CopyAll;
|
|
CopyFilesWithSecurity |= copyOptions.CopyFilesWithSecurity;
|
|
CopySubdirectories |= copyOptions.CopySubdirectories;
|
|
CopySubdirectoriesIncludingEmpty |= copyOptions.CopySubdirectoriesIncludingEmpty;
|
|
CopySymbolicLink |= copyOptions.CopySymbolicLink;
|
|
CreateDirectoryAndFileTree |= copyOptions.CreateDirectoryAndFileTree;
|
|
DoNotCopyDirectoryInfo |= copyOptions.DoNotCopyDirectoryInfo;
|
|
DoNotUseWindowsCopyOffload |= copyOptions.DoNotUseWindowsCopyOffload;
|
|
EnableBackupMode |= copyOptions.EnableBackupMode;
|
|
EnableEfsRawMode |= copyOptions.EnableEfsRawMode;
|
|
EnableRestartMode |= copyOptions.EnableRestartMode;
|
|
EnableRestartModeWithBackupFallback |= copyOptions.EnableRestartModeWithBackupFallback;
|
|
FatFiles |= copyOptions.FatFiles;
|
|
FixFileSecurityOnAllFiles |= copyOptions.FixFileSecurityOnAllFiles;
|
|
FixFileTimesOnAllFiles |= copyOptions.FixFileTimesOnAllFiles;
|
|
Mirror |= copyOptions.Mirror;
|
|
MoveFiles |= copyOptions.MoveFiles;
|
|
MoveFilesAndDirectories |= copyOptions.MoveFilesAndDirectories;
|
|
Purge |= copyOptions.Purge;
|
|
RemoveFileInformation |= copyOptions.RemoveFileInformation;
|
|
TurnLongPathSupportOff |= copyOptions.TurnLongPathSupportOff;
|
|
UseUnbufferedIo |= copyOptions.UseUnbufferedIo;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|