/// <summary> /// Gets the sequence of arguments to pass to the command line client. This might write out a temporary file on disk, /// so be sure to call <see cref="Cleanup"/> when the command has completed execution. /// </summary> /// <returns> /// A collection of arguments to pass to the command line client. /// </returns> public string[] GetArguments(bool useInPersistentClient) { var arguments = (from argument in _Collection where !StringEx.IsNullOrWhiteSpace(argument) select argument.Trim()).ToArray(); if (arguments.Length == 0) { return(arguments); } if (ClientExecutable.CurrentVersion < new Version(1, 8) || useInPersistentClient) { return(arguments); } _ListFileName = Path.GetTempFileName(); var listFileEncoding = ClientExecutable.GetMainEncoding(); File.WriteAllText(_ListFileName, string.Join(Environment.NewLine, arguments), listFileEncoding); return(new[] { string.Format(CultureInfo.InvariantCulture, "\"listfile:{0}\"", _ListFileName) }); }
/// <summary> /// Executes the given <see cref="IMercurialCommand"/> command against /// the Mercurial repository. /// </summary> /// <param name="repositoryPath"> /// The root path of the repository to execute the command in. /// </param> /// <param name="command"> /// The <see cref="IMercurialCommand"/> command to execute. /// </param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="repositoryPath"/> is <c>null</c>.</para> /// <para>- or -</para> /// <para><paramref name="command"/> is <c>null</c>.</para> /// </exception> /// <exception cref="MercurialException"> /// HG did not complete within the allotted time. /// </exception> public static void Execute(string repositoryPath, IMercurialCommand command) { if (StringEx.IsNullOrWhiteSpace(repositoryPath)) { throw new ArgumentNullException("repositoryPath"); } if (command == null) { throw new ArgumentNullException("command"); } ClientExecutable.LazyInitialize(); var specialArguments = (IEnumerable <string>) new[] { "--noninteractive", }; var environmentVariables = new[] { new KeyValuePair <string, string>("LANGUAGE", "EN"), }; CommandProcessor .Execute(repositoryPath, ClientExecutable.ClientPath, command, environmentVariables, specialArguments); MercurialVersionBase.Current.WaitForLocksToDissipate(repositoryPath); }
/// <summary> /// Decodes and returns a single string from the <see cref="System.IO.StreamReader"/>. /// </summary> /// <param name="reader"> /// The <see cref="System.IO.StreamReader"/> to decode the string from. /// </param> /// <returns> /// The decoded string or <c>null</c> if the data could not be decoded. /// </returns> /// <remarks> /// Note that this method will not check that <paramref name="reader"/> is non-null. /// </remarks> private static string DecodeString(StreamReader reader) { var length = DecodeInt32(reader); if (length < 0) { return(null); } // max: 1MB, to avoid memory problems with corrupt data if (length > 1 * 1024 * 1024) { return(null); } var buffer = new byte[length]; for (int index = 0; index < length; index++) { int unencodedByte = reader.Read(); if (unencodedByte == -1) { return(null); } buffer[index] = (byte)unencodedByte; } var encoding = ClientExecutable.GetMainEncoding(); return(encoding.GetString(buffer)); }
/// <summary> /// This spins up a Mercurial client in command server mode for the /// repository. /// </summary> private void StartPersistentMercurialClient() { var psi = new ProcessStartInfo { FileName = ClientExecutable.ClientPath, WorkingDirectory = _RepositoryPath, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = false, ErrorDialog = false, Arguments = "serve --cmdserver pipe --noninteractive" //--encoding cp1251", }; psi.EnvironmentVariables.Add("LANGUAGE", "EN"); psi.EnvironmentVariables.Remove("HGENCODING"); psi.EnvironmentVariables.Add("HGENCODING", ClientExecutable.GetMainEncoding().WebName); psi.StandardOutputEncoding = ClientExecutable.GetMainEncoding(); psi.StandardErrorEncoding = ClientExecutable.GetMainEncoding(); _Process = Process.Start(psi); DecodeInitialBlock(); }
/// <summary> /// Executes the given <see cref="IMercurialCommand"/> command without /// a repository. /// </summary> /// <param name="command"> /// The <see cref="IMercurialCommand"/> command to execute. /// </param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="command"/> is <c>null</c>.</para> /// </exception> public static void Execute(IMercurialCommand command) { if (command == null) { throw new ArgumentNullException("command"); } ClientExecutable.LazyInitialize(); Execute(Path.GetTempPath(), command); }
private int RunCommand(IList <string> command, IDictionary <CommandChannel, Stream> outputs, IDictionary <CommandChannel, Func <uint, byte[]> > inputs) { if (null == command || 0 == command.Count) { throw new ArgumentException("Command must not be empty", "command"); } var _codec = ClientExecutable.GetMainEncoding(); byte[] commandBuffer = _codec.GetBytes("runcommand\n"); byte[] argumentBuffer; argumentBuffer = command.Aggregate(new List <byte>(), (bytes, arg) => { bytes.AddRange(_codec.GetBytes(arg)); bytes.Add(0); return(bytes); }, bytes => { bytes.RemoveAt(bytes.Count - 1); return(bytes.ToArray()); } ).ToArray(); byte[] lengthBuffer = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(argumentBuffer.Length)); lock (_Process) { _Process.StandardInput.BaseStream.Write(commandBuffer, 0, commandBuffer.Length); _Process.StandardInput.BaseStream.Write(lengthBuffer, 0, lengthBuffer.Length); _Process.StandardInput.BaseStream.Write(argumentBuffer, 0, argumentBuffer.Length); _Process.StandardInput.BaseStream.Flush(); return(ReadCommandOutputs(command, outputs, inputs)); }// lock _Process }
/// <summary> /// Override this method to implement code that will execute before command /// line execution. /// </summary> protected override void Prepare() { _MessageFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Replace("-", string.Empty).ToLowerInvariant() + ".txt"); File.WriteAllText(_MessageFilePath, Message, ClientExecutable.GetMainEncoding()); }
/// <summary> /// Executes the given executable to process the given command. /// </summary> /// <param name="workingDirectory"> /// The working directory while executing the command. /// </param> /// <param name="executable"> /// The full path to and name of the executable to execute. /// </param> /// <param name="command"> /// The options to the executable. /// </param> /// <param name="environmentVariables"> /// An array of <see cref="System.Collections.Generic.KeyValuePair{TKey,TValue}"/> objects, containing environment variable /// overrides to use while executing the executable. /// </param> /// <param name="specialArguments"> /// Any special arguments to pass to the executable, not defined by the <paramref name="command"/> /// object, typically common arguments that should always be passed to the executable. /// </param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="workingDirectory"/> is <c>null</c> or empty.</para> /// <para>- or -</para> /// <para><paramref name="executable"/> is <c>null</c> or empty.</para> /// <para>- or -</para> /// <para><paramref name="command"/> is <c>null</c>.</para> /// <para>- or -</para> /// <para><paramref name="environmentVariables"/> is <c>null</c>.</para> /// <para>- or -</para> /// <para><paramref name="specialArguments"/> is <c>null</c>.</para> /// </exception> /// <exception cref="MercurialException"> /// <para>The executable did not finish in the allotted time.</para> /// </exception> public static void Execute( string workingDirectory, string executable, ICommand command, KeyValuePair <string, string>[] environmentVariables, IEnumerable <string> specialArguments) { if (StringEx.IsNullOrWhiteSpace(workingDirectory)) { throw new ArgumentNullException(nameof(workingDirectory)); } if (StringEx.IsNullOrWhiteSpace(executable)) { throw new ArgumentNullException(nameof(executable)); } if (command == null) { throw new ArgumentNullException(nameof(command)); } if (environmentVariables == null) { throw new ArgumentNullException(nameof(environmentVariables)); } if (specialArguments == null) { throw new ArgumentNullException(nameof(specialArguments)); } command.Validate(); command.Before(); IEnumerable <string> arguments = specialArguments; arguments = arguments.Concat(command.Arguments.Where(a => !StringEx.IsNullOrWhiteSpace(a))); arguments = arguments.Concat(command.AdditionalArguments.Where(a => !StringEx.IsNullOrWhiteSpace(a))); string argumentsString = string.Join(" ", arguments.ToArray()); var startInfo = new ProcessStartInfo { FileName = executable, WorkingDirectory = workingDirectory, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = false, ErrorDialog = false, Arguments = command.Command + " " + argumentsString, }; foreach (var kvp in environmentVariables) { startInfo.EnvironmentVariables[kvp.Key] = kvp.Value; } ClientExecutable.LazyInitialize(); startInfo.StandardErrorEncoding = ClientExecutable.GetMainEncoding(); startInfo.StandardOutputEncoding = ClientExecutable.GetMainEncoding(); command.Observer?.Executing(command.Command, argumentsString); using Process process = Process.Start(startInfo); Func <StreamReader, Action <string>, string> reader; if (command.Observer != null) { reader = delegate(StreamReader streamReader, Action <string> logToObserver) { var output = new StringBuilder(); string line; while ((line = streamReader.ReadLine()) != null) { logToObserver(line); if (output.Length > 0) { output.Append(Environment.NewLine); } output.Append(line); } return(output.ToString()); }; } else { reader = (streamReader, logToObserver) => streamReader.ReadToEnd(); } string standardOutput = reader(process.StandardOutput, line => command.Observer.Output(line)); string errorOutput = reader(process.StandardError, line => command.Observer.ErrorOutput(line)); int timeout = Timeout.Infinite; if (command.Timeout > 0) { timeout = 1000 * command.Timeout; } process.WaitForExit(timeout); if (command.Observer != null) { command.Observer.Executed(command.Command, argumentsString, process.ExitCode, standardOutput, errorOutput); } command.After(process.ExitCode, standardOutput, errorOutput); }
/// <summary> /// Executes the given <see cref="IMercurialCommand"/> command against /// the Mercurial repository. /// </summary> /// <param name="command"> /// The <see cref="IMercurialCommand"/> command to execute. /// </param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="command"/> is <c>null</c>.</para> /// </exception> /// <exception cref="MercurialException"> /// HG did not complete within the allotted time. /// </exception> public void Execute(IMercurialCommand command) { if (command == null) { throw new ArgumentNullException("command"); } if (_Process == null) { StartPersistentMercurialClient(); } command.Validate(); command.Before(); IEnumerable <string> arguments = new[] { command.Command, "--noninteractive", }; arguments = arguments.Concat(command.Arguments.Where(a => !StringEx.IsNullOrWhiteSpace(a))); arguments = arguments.Concat(command.AdditionalArguments.Where(a => !StringEx.IsNullOrWhiteSpace(a))); var commandParts = arguments.ToArray(); string commandEncoded = string.Join("\0", commandParts.Select(p => p.Trim(new[] { '"' })).ToArray()); int length = commandEncoded.Length; var commandBuffer = new StringBuilder(); commandBuffer.Append("runcommand\n"); commandBuffer.Append((char)((length >> 24) & 0xff)); commandBuffer.Append((char)((length >> 16) & 0xff)); commandBuffer.Append((char)((length >> 8) & 0xff)); commandBuffer.Append((char)(length & 0xff)); commandBuffer.Append(commandEncoded); string commandArguments = null; if (command.Observer != null) { commandArguments = string.Join(" ", commandParts.Skip(1).ToArray()); command.Observer.Executing(command.Command, commandArguments); } MemoryStream output = new MemoryStream(); MemoryStream error = new MemoryStream(); var outputs = new Dictionary <CommandChannel, Stream>() { { CommandChannel.Output, output }, { CommandChannel.Error, error }, }; var _codec = ClientExecutable.GetMainEncoding(); int resultCode = RunCommand(commandParts, outputs, null); var result = new CommandResult(_codec.GetString(output.GetBuffer(), 0, (int)output.Length), _codec.GetString(error.GetBuffer(), 0, (int)error.Length), resultCode); if (resultCode == 0 || !string.IsNullOrEmpty(result.Output)) { if (command.Observer != null) { command.Observer.Output(result.Output); command.Observer.ErrorOutput(result.Error); command.Observer.Executed(command.Command, commandArguments, resultCode, result.Output, result.Error); } command.After(resultCode, result.Output, result.Error); return; } StopPersistentMercurialClient(); throw new MercurialExecutionException( string.IsNullOrEmpty(result.Error) ? "Unable to decode output from executing command, spinning down persistent client" : result.Error); }