Friday, March 26, 2010

Commandline HelperClass in C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Text.RegularExpressions;

namespace Kurapaty.Solutions.Utils
{
	/// 
	/// Commandline Arguments Parser class
	/// 
	/// 
	/// Source: 
	/// 
	public class Arguments
	{
		/// 
		/// Splits the command line. When main(string[] args) is used escaped quotes (ie a path “c:\folder\”)
		/// Will consume all the following command line arguments as the one argument. 
		/// This function ignores escaped quotes making handling paths much easier.
		/// 
		/// The command line.		/// 
		public static string[] SplitCommandLine(string commandLine)
		{
			var translatedArguments = new StringBuilder(commandLine);
			var escaped = false;
			for (var i = 0; i < translatedArguments.Length; i++)
			{
				if (translatedArguments[i] == '"')
				{
					escaped = !escaped;
				}
				if (translatedArguments[i] == ' ' && !escaped)
				{
					translatedArguments[i] = '\n';
				}
			}

			var toReturn = translatedArguments.ToString().Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
			for (var i = 0; i < toReturn.Length; i++)
			{
				toReturn[i] = RemoveMatchingQuotes(toReturn[i]);
			}
			return toReturn;
		}

		public static string RemoveMatchingQuotes(string stringToTrim)
		{
			var firstQuoteIndex = stringToTrim.IndexOf('"');
			var lastQuoteIndex = stringToTrim.LastIndexOf('"');
			while (firstQuoteIndex != lastQuoteIndex)
			{
				stringToTrim = stringToTrim.Remove(firstQuoteIndex, 1);
				stringToTrim = stringToTrim.Remove(lastQuoteIndex - 1, 1); //-1 because we’ve shifted the indicies left by one
				firstQuoteIndex = stringToTrim.IndexOf('"');
				lastQuoteIndex = stringToTrim.LastIndexOf('"');
			}

			return stringToTrim;
		}

		private readonly Dictionary> _parameters;
		private string _waitingParameter;

		public Arguments(IEnumerable arguments)
		{
			_parameters = new Dictionary>();

			string[] parts;

			//Splits on beginning of arguments ( – and — and / )
			//And on assignment operators ( = and : )
			var argumentSplitter = new Regex(@"^-{1,2}|^/|=|:", RegexOptions.IgnoreCase | RegexOptions.Compiled);

			foreach (var argument in arguments)
			{
				parts = argumentSplitter.Split(argument, 3);
				switch (parts.Length)
				{
					case 1:
						AddValueToWaitingArgument(parts[0]);
						break;
					case 2:
						AddWaitingArgumentAsFlag();

						//Because of the split index 0 will be a empty string
						_waitingParameter = parts[1];
						break;
					case 3:
						AddWaitingArgumentAsFlag();

						//Because of the split index 0 will be a empty string
						string valuesWithoutQuotes = RemoveMatchingQuotes(parts[2]);

						AddListValues(parts[1], valuesWithoutQuotes.Split(','));
						break;
				}
			}

			AddWaitingArgumentAsFlag();
		}

		private void AddListValues(string argument, IEnumerable values)
		{
			foreach (var listValue in values)
			{
				Add(argument, listValue);
			}
		}

		private void AddWaitingArgumentAsFlag()
		{
			if (_waitingParameter == null) return;

			AddSingle(_waitingParameter, "true");
			_waitingParameter = null;
		}

		private void AddValueToWaitingArgument(string value)
		{
			if (_waitingParameter == null) return;

			value = RemoveMatchingQuotes(value);

			Add(_waitingParameter, value);
			_waitingParameter = null;
		}

		/// 
		/// Gets the count.
		/// 
		/// The count.
		public int Count
		{
			get
			{
				return _parameters.Count;
			}
		}

		/// 
		/// Adds the specified argument.
		/// 
		/// The argument.		/// The value.		public void Add(string argument, string value)
		{
			if (!_parameters.ContainsKey(argument))
				_parameters.Add(argument, new Collection());

			_parameters[argument].Add(value);
		}

		public void AddSingle(string argument, string value)
		{
			if (!_parameters.ContainsKey(argument))
				_parameters.Add(argument, new Collection());
			else
				throw new ArgumentException(string.Format("Argument {0} has already been defined", argument));

			_parameters[argument].Add(value);
		}

		public void Remove(string argument)
		{
			if (_parameters.ContainsKey(argument))
				_parameters.Remove(argument);
		}

		/// 
		/// Determines whether the specified argument is true.
		/// 
		/// The argument.		/// 
		///     true if the specified argument is true; otherwise, false.
		/// 
		public bool IsTrue(string argument)
		{
			AssertSingle(argument);

			var arg = this[argument];

			return arg != null && arg[0].Equals("true", StringComparison.OrdinalIgnoreCase);
		}

		public bool IsExists(string argument)
		{
			return IsTrue(argument);
		}

		private void AssertSingle(string argument)
		{
			if (this[argument] != null && this[argument].Count > 1)
				throw new ArgumentException(string.Format("{0} has been specified more than once, expecting single value", argument));
		}

		public string Single(string argument)
		{
			AssertSingle(argument);

			//only return value if its NOT true, there is only a single item for that argument
			//and the argument is defined
			if (this[argument] != null && !IsTrue(argument))
				return this[argument][0];

			return null;
		}

		public bool Exists(string argument)
		{
			return (this[argument] != null && this[argument].Count > 0);
		}

		/// 
		/// Gets the  with the specified parameter.
		/// 
		/// 
		public Collection this[string parameter]
		{
			get
			{
				return _parameters.ContainsKey(parameter) ? _parameters[parameter] : null;
			}
		}
	}
}

Usage:
		private bool ProcessCommandLineParameters()
		{
			// Usage: -ec -source:"C:\Temp\formDefinition.txt" -target:"C:\Temp\formDefinition.enc"
			try
			{
				Arguments parser = new Arguments(Arguments.SplitCommandLine(Environment.CommandLine));

				if (parser.IsExists("help") || parser.IsExists("?"))
					ShowUsageHelpMessage();

				if (parser.Exists("source") && 1 == parser["source"].Count) tbSource.Text = parser.Single("source");
				if (parser.Exists("target") && 1 == parser["target"].Count) tbTarget.Text = parser.Single("target");

				if (parser.Exists("ec") && parser.IsTrue("ec"))
				{
					button1_Click(null, null);
					return true;
				}
				if (parser.Exists("dc") && parser.IsTrue("dc"))
				{
					button2_Click(null, null);
					return true;
				}
			}
			catch (Exception)
			{
				MessageBox.Show("Invalid commandline arguments passed. Please use GUI or type \"help\" at commandline.",
				                "Commandline processor", MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
			return false;
		}

Delphi 6 Commandline Helper Class

unit uCmdLineHelper;

interface

uses Windows, Classes, SysUtils;

  type
  TCommandLineHelper = class(TObject)
  private
    function GetNextParam(var CmdLine: PChar; Buffer: PChar; Len: PInteger): Boolean;
    function GetParamCount: Integer;
    function GetCommandLine: string;
    function GetParamStr(Index: Integer): String;
    function GetHasParam(Value: String): Boolean;
  public
    property ParamStr[Index: Integer]: String read GetParamStr;
    property ParamCount: Integer read GetParamCount;
    property CommandLine: String read GetCommandLine;
    property HasParam[Value: String]: Boolean read GetHasParam;
  end;

implementation


function TCommandLineHelper.GetCommandLine : string;
begin
  result := Windows.GetCommandLine;
end;

function TCommandLineHelper.GetHasParam(Value: String): Boolean;
var
 I: Integer;
 param: String;
begin
  result := False;
  for I:= 1 to ParamCount do
  begin
    param := UpperCase(ParamStr[I]);
    Value := UpperCase(Value);
    result := ((param = Value) or
               (param = '/'+Value) or ('/'+param = Value) or
               (param = '\'+Value) or ('\'+param = Value) or
               (param = '-'+Value) or ('-'+param = Value) or
               (param = ':'+Value) or (':'+param = Value));
    if result then Exit;
  end;
end;

function TCommandLineHelper.GetNextParam(var CmdLine: PChar; Buffer: PChar; Len: PInteger): Boolean;
var
  InQuotedStr, IsOdd: Boolean;
  NumSlashes, NewLen, cnt: Integer;
begin
  Result := False;
  if Len <> nil then Len^ := 0;
  if CmdLine = nil then Exit;

  while (CmdLine^ <= ' ') and (CmdLine^ <> #0) do CmdLine := CharNext(CmdLine) ;
  if CmdLine^ = #0 then Exit;

  InQuotedStr := False;
  NewLen := 0;
  repeat
    if CmdLine^ = '\' then
    begin
      NumSlashes := 0;
      repeat
        Inc(NumSlashes) ;
        CmdLine := CharNext(CmdLine) ;
      until CmdLine^ <> '\';

      if CmdLine^ = '"' then
      begin
        IsOdd := (NumSlashes mod 2) <> 0;
        NumSlashes := NumSlashes div 2;
        Inc(NewLen, NumSlashes) ;

        if IsOdd then Inc(NewLen);

        if Buffer <> nil then
        begin
          for cnt := 0 to NumSlashes-1 do
          begin
            Buffer^ := '\';
            Inc(Buffer) ;
          end;

          if IsOdd then
          begin
            Buffer^ := '"';
            Inc(Buffer) ;
          end;
        end;
        if IsOdd then CmdLine := CharNext(CmdLine) ;
      end else
      begin
        Inc(NewLen, NumSlashes);

        if Buffer <> nil then
        begin
          for cnt := 0 to NumSlashes-1 do
          begin
            Buffer^ := '\';
            Inc(Buffer) ;
          end;
        end;
      end;
      Continue;
    end;
    if CmdLine^ <> '"' then
    begin
      if (CmdLine^ <= ' ') and (not InQuotedStr) then Break;
      Inc(NewLen) ;
      if Buffer <> nil then
      begin
        Buffer^ := CmdLine^;
        Inc(Buffer) ;
      end;
    end
    else
      InQuotedStr := not InQuotedStr;
    CmdLine := CharNext(CmdLine) ;
  until CmdLine^ = #0;
  if Len <> nil then Len^ := NewLen;
  Result := True;
end;

function TCommandLineHelper.GetParamCount: Integer;
var
  CmdLine: PChar;
begin
  Result := 0;
  CmdLine := Windows.GetCommandLine;
  GetNextParam(CmdLine, nil, nil) ;
  while GetNextParam(CmdLine, nil, nil) do Inc(Result) ;
end;

function TCommandLineHelper.GetParamStr(Index: Integer): String;
var
  Buffer: array[0..MAX_PATH] of Char;
  CmdLine, P: PChar;
  Len: Integer;
begin
  Result := '';
  if Index <= 0 then
  begin
    Len := GetModuleFileName(0, Buffer, MAX_PATH+1) ;
    SetString(Result, Buffer, Len) ;
  end else
  begin
    CmdLine := windows.GetCommandLine;
    GetNextParam(CmdLine, nil, nil) ;
    repeat
      Dec(Index) ;
      if Index = 0 then Break;
      if not GetNextParam(CmdLine, nil, nil) then Exit;
    until False;
    P := CmdLine;
    if GetNextParam(P, nil, @Len) then
    begin
      SetLength(Result, Len) ;
      GetNextParam(CmdLine, PChar(Result), nil) ;
    end;
  end;
end;

end.


Usage:
var
  idx : integer;
  helper: TCommandLineHelper;
begin
  helper:= TCommandLineHelper.Create;
  with Memo1.Lines do
  begin
    Clear;

    Add('CMD Line: ' + helper.CommandLine + #13#10) ;
    Add('Number of params: ' + IntToStr(helper.ParamCount) + #13#10) ;

    for idx := 1 to helper.ParamCount do
    begin
      Memo1.Lines.Add(helper.ParamStr[idx]) ;
    end;
      Memo1.Lines.Add('Has Param - and ? '+ BoolToStr(helper.HasParam['third'], true));
  end;
  helper.Free;
end;