Esempio n. 1
0
        public void TestProcessCommandLineArguments1()
        {
            string commandLine = "-abZ -ab123 and -a=Foo --Apple:No -b plantain --a %TEMP% @notExpanded --banana -Z --empty=";
            var    args        = G.SplitCommandLineArguments(commandLine);

            var shortOpts = new Dictionary <char, string> {
                { 'a', null }, { 'b', "banana" }
            };
            var twoArgOptions = new InvertibleSet <string>(new[] { "banana" });
            var options       = new DList <KeyValuePair <string, string> >();

            UG.ProcessCommandLineArguments(args, options, null, shortOpts, twoArgOptions, expandEnvVars: false);

            ExpectList(args.AsListSource(), "-abZ", "and", "%TEMP%", "@notExpanded", "-Z");
            ExpectList(options, P("a", null), P("banana", "123"), P("a", "Foo"), P("apple", "No"), P("banana", "plantain"), P("a", null), P("banana", null), P("empty", ""));
        }
Esempio n. 2
0
		public void TestProcessCommandLineArguments1()
		{
			string commandLine = "-abZ -ab123 and -a=Foo --Apple:No -b plantain --a %TEMP% @notExpanded --banana -Z --empty=";
			var args = G.SplitCommandLineArguments(commandLine);
			
			var shortOpts = new Dictionary<char, string> { { 'a', null }, { 'b', "banana" } };
			var twoArgOptions = new InvertibleSet<string>(new[] { "banana" });
			var options = new DList<KeyValuePair<string, string>>();
			UG.ProcessCommandLineArguments(args, options, null, shortOpts, twoArgOptions, expandEnvVars: false);

			ExpectList(args.AsListSource(), "-abZ", "and", "%TEMP%", "@notExpanded", "-Z");
			ExpectList(options, P("a", null), P("banana", "123"), P("a", "Foo"), P("apple", "No"), P("banana", "plantain"), P("a", null), P("banana", null), P("empty", ""));
		}
Esempio n. 3
0
		private static void ProcessArgument(IList<string> args, int i, ICollection<KeyValuePair<string, string>> options, string atFolder, IDictionary<char, string> shortOptions, InvertibleSet<string> twoArgOptions, int argLimit, bool expandEnvVars, bool caseSensitiveLongOpts)
		{
			string s = args[i];
			if (s == null)
				return;
			if (expandEnvVars)
				args[i] = s = Environment.ExpandEnvironmentVariables(s);

			if (s.StartsWith("-")) {
				Match m = CmdLineLongOptRegex.Match(s);
				if (m.Success)
				{
					// it's an --option
					string name = m.Groups[1].ToString();
					string value = m.Groups[3].ToString();
					if (value == "" && m.Groups[2].ToString() == "")
						value = null; // no value present (value=="" means value is present but empty)

					if (!caseSensitiveLongOpts)
						name = name.ToLowerInvariant();

					args[i] = null;
					if (twoArgOptions != null && twoArgOptions.Contains(name))
						MaybeRemoveArg(args, i + 1, expandEnvVars, ref value);
					AddPair(options, s, name, value);
				}
				else if (shortOptions != null)
				{
					// Check if short option(s) are valid
					bool reject = false;
					int div;
					char ch = '\0';
					for (div = 1; div < s.Length; div++) {
						ch = s[div];
						if (!shortOptions.ContainsKey(ch)) {
							if (char.IsLetter(ch))
								reject = true;
							break;
						}
					}

					int afterDiv = (ch == ':' || ch == '=' ? div + 1 : div);
					string value = div < s.Length ? s.Substring(afterDiv) : null;

					if (div > 1 && !reject) // is s entirely valid?
					{
						// detect space-separated argument
						ch = s[div - 1];
						if (twoArgOptions != null && twoArgOptions.Contains(shortOptions[ch] ?? ch.ToString()))
							MaybeRemoveArg(args, i + 1, expandEnvVars, ref value);

						args[i] = null;
						for (int c = 1; c < div; c++) {
							ch = s[c];
							string longName = shortOptions[ch] ?? ch.ToString();
							string curValue = c + 1 == div ? value : null;
							AddPair(options, s, longName, curValue);
						}
					}
				}
			}
			else if (atFolder != null && s.StartsWith("@"))
			{
				// e.g. "@list of options.txt"
				string atFile = s.Substring(1);
				string fileContents = null;
				try {
					string fullpath = Path.Combine(atFolder, atFile);
					if (File.Exists(fullpath))
						fileContents = File.OpenText(fullpath).ReadToEnd();
				} catch (Exception e) {
					MessageSink.Default.Error(s, "Unable to use option file \"{0}\": {1}", atFile, e.Message);
				}
				if (fileContents != null) {
					List<string> list = G.SplitCommandLineArguments(fileContents);

					int maxMore = System.Math.Max(0, argLimit - args.Count);
					if (list.Count > maxMore) {
						// oops, command limit exceeded
						MessageSink.Default.Warning(s, "Limit of {0} commands exceeded", argLimit);
						list.RemoveRange(maxMore, list.Count - maxMore);
					}

					args.InsertRange(i + 1, (IList<string>) list);
				}
			}
		}
Esempio n. 4
0
		/// <summary>
		/// Expands environment variables (e.g. %TEMP%) and @files in a list of
		/// command-line arguments, and adds any options of the form "--opt" or
		/// "--opt=value" to a dictionary.
		/// </summary>
		/// <param name="args">The original arguments to process.</param>
		/// <param name="options">Any long options (arguments that start with "--") 
		/// will be added to this dictionary, and removed from <c>args</c>. This 
		/// parameter cannot be null. 
		///   By default, long options are not case sensitive. In that case, the 
		/// user's option name is converted to lower case.
		/// <para/>
		/// Long options are expected to have the form --ID or --ID=value, where ID 
		/// matches the regex "[a-zA-Z_0-9-]+". If there is no "=" or ":", that's 
		/// okay too. For example, --Id{foo} is equivalent to --Id={foo}; both yield
		/// in the name-value pair ("id", "{foo}"). If there is no value (no equals
		/// or colon), the value associated with the option is null.</param>
		/// <param name="atFolder">If a parameter has the form @filename, the folder
		/// specified by atFolder will be searched for an options text file with the
		/// user-specified filename, and the contents of the file will be expanded 
		/// into the list of arguments (split using SplitCommandLineArguments). The
		/// expanded list can contain new @filenames, which are also processed. To
		/// search in the current directory, use "". The @filename may use an absolute
		/// path, which overrides this folder. To disable @filename expansion, set 
		/// this parameter to null. Whether the feature is enabled or disabled, 
		/// @filenames are <i>not</i> removed from <c>args</c>, in case you want to 
		/// be aware of the filenames afterward.</param>
		/// <param name="shortOptions">A map from one-letter options that start with 
		/// "-" rather than "--", to the name of the corresponding long option (this
		/// option can be null to ignore all short options.) For example, if this 
		/// contains (<c>'a', "all"</c>), and the input <c>args</c> contains "-a:foo",
		/// the pair ("all", "foo") will be added to <c>options</c>. If a value in 
		/// this map is null, the key itself is used. Short options can be combined; 
		/// for example <c>-abc:foo</c> is equivalent to <c>-a -b -c:foo</c>. Short 
		/// options are always case-sensitive; to define an option that is not case 
		/// sensitive, place two entries in the dictionary e.g. ('a', "all") and 
		/// ('A', "all"). If the user specifies a short option letter that is not 
		/// recognized, the entire command will be ignored and left in args. For 
		/// example, if <c>shortOptions</c> contains only ('a', "all") but <c>args</c>
		/// contains "-ab=foo", the command is ignored and left in <c>args</c>. 
		/// Rationale: -ab=foo might be a filename.
		/// <para/>
		/// On the other hand, if -a is a valid option then <c>-a123</c> is also 
		/// valid even when there is no option called '1'; the number "123" is 
		/// treated as an argument to -a. Now, if '1' is a registered short option 
		/// but '2' is not, then <c>-a123</c> is equivalent to <c>-a -1=23</c>.
		/// </param>
		/// <param name="twoArgOptions">A set of options in which the argument can
		/// be separated by a space from the option. For example, if the input is 
		/// "--out foo.txt" and you want to recognize "foo.txt" as the argument to
		/// "--out", add the string "out" to this set. If you want to treat <i>all</i>
		/// options this way, use <c>InvertibleSet{string}.All</c>. Note: 
		/// If the input is "--out:foo bar", "foo" is recognized as the argument to
		/// "--out" and "bar" is left alone, i.e. it is treated as unrelated.
		///   Short options participate automatically. For example if "-f" means 
		/// "--foo", and twoArgOptions contains "foo", then "-f arg" is interpreted 
		/// like "--foo=arg".
		/// <para/>
		/// The argument will not be treated as an argument if it starts with a 
		/// dash, e.g. in <c>--foo -*</c>, <c>-*</c> will not be treated as an 
		/// argument to <c>--foo</c>, even if <c>-*</c> is not a registered option.
		/// </param>
		/// <param name="argLimit">A limit placed on the number of arguments when
		/// expanding @files. Such a file may refer to itself, and this is the only
		/// protection provided against infinite recursive expansion.</param>
		/// <param name="expandEnvVars">If true, environment variable references
		/// such as <c>%TEMP%</c> are expanded by calling the standard method
		/// <see cref="Environment.ExpandEnvironmentVariables"/>.</param>
		/// <param name="caseSensitiveLongOpts">If true, long options are case-
		/// sensitive. By default, long options are not case sensitive.</param>
		/// <remarks>
		/// Two types of options are recognized, short (-s) and long (--long), and
		/// only one argument is supported per option. The documentation is above.
		/// <para/>
		/// You can choose whether to permit duplicate options or not. If you use
		/// a standard <see cref="Dictionary{K,V}"/> to hold the options, an 
		/// exception will occur when this method calls Add() to add the duplicate. 
		/// The exception is caught, the first ocurrance is kept, and a warning 
		/// message is printed to <see cref="MessageSink.Default"/>.
		/// <para/>
		/// To allow duplicates, store options in a different data structure such as 
		/// <c>List(KeyValuePair(string, string))</c> or <c>BMultiMap(string,string)</c>.
		/// <para/>
		/// DOS-style slash-options like /foo are not supported. Since Windows
		/// recognizes the forward slash as a path separator, forward-slash options
		/// can be recognized as paths. If you want to recognize them as options 
		/// instead, you can preprocess the argument list, replacing every command 
		/// that starts with "/" with a "--" command:
		/// <code>
		///   for (int i = 0; args.Count > i; i++)
		///     if (args[i].StartsWith("/")) 
		///       args[i] = "--" + args[i].Substring(1);
		/// </code>
		/// <para/>
		/// Globs (e.g. *.txt) are not recognized or expanded, but environment 
		/// variables are expanded when <c>expandEnvVars</c> is true.
		/// <para/>
		/// Quote marks are not processed. An argument of <c>"--a"</c>, with quote 
		/// marks, is not recognized as an option (these quote marks should be 
		/// removed before calling this method, e.g. 
		/// <see cref="G.SplitCommandLineArguments"/> handles this.)
		/// </remarks>
		public static void ProcessCommandLineArguments(IList<string> args, ICollection<KeyValuePair<string, string>> options, string atFolder, IDictionary<char, string> shortOptions = null, InvertibleSet<string> twoArgOptions = null, int argLimit = 0xFFFF, bool expandEnvVars = true, bool caseSensitiveLongOpts = false)
		{
			CheckParam.IsNotNull("args", args);
			CheckParam.IsNotNull("options", options);
			for (int i = 0; i < args.Count; i++)
				ProcessArgument(args, i, options, atFolder, shortOptions, twoArgOptions, argLimit, expandEnvVars, caseSensitiveLongOpts);
			args.RemoveAll(s => s == null);
		}
Esempio n. 5
0
 public PGNodeSet(InvertibleSet <LNode> set) : base(set.BaseSet, set.IsInverted)
 {
 }
Esempio n. 6
0
        private static void ProcessArgument(IList <string> args, int i, ICollection <KeyValuePair <string, string> > options, string atFolder, IDictionary <char, string> shortOptions, InvertibleSet <string> twoArgOptions, int argLimit, bool expandEnvVars, bool caseSensitiveLongOpts)
        {
            string s = args[i];

            if (s == null)
            {
                return;
            }
            if (expandEnvVars)
            {
                args[i] = s = Environment.ExpandEnvironmentVariables(s);
            }

            if (s.StartsWith("-"))
            {
                Match m = CmdLineLongOptRegex.Match(s);
                if (m.Success)
                {
                    // it's an --option
                    string name  = m.Groups[1].ToString();
                    string value = m.Groups[3].ToString();
                    if (value == "" && m.Groups[2].ToString() == "")
                    {
                        value = null;                         // no value present (value=="" means value is present but empty)
                    }
                    if (!caseSensitiveLongOpts)
                    {
                        name = name.ToLowerInvariant();
                    }

                    args[i] = null;
                    if (twoArgOptions != null && twoArgOptions.Contains(name))
                    {
                        MaybeRemoveArg(args, i + 1, expandEnvVars, ref value);
                    }
                    AddPair(options, s, name, value);
                }
                else if (shortOptions != null)
                {
                    // Check if short option(s) are valid
                    bool reject = false;
                    int  div;
                    char ch = '\0';
                    for (div = 1; div < s.Length; div++)
                    {
                        ch = s[div];
                        if (!shortOptions.ContainsKey(ch))
                        {
                            if (char.IsLetter(ch))
                            {
                                reject = true;
                            }
                            break;
                        }
                    }

                    int    afterDiv = (ch == ':' || ch == '=' ? div + 1 : div);
                    string value    = div < s.Length ? s.Substring(afterDiv) : null;

                    if (div > 1 && !reject)                     // is s entirely valid?
                    {
                        // detect space-separated argument
                        ch = s[div - 1];
                        if (twoArgOptions != null && twoArgOptions.Contains(shortOptions[ch] ?? ch.ToString()))
                        {
                            MaybeRemoveArg(args, i + 1, expandEnvVars, ref value);
                        }

                        args[i] = null;
                        for (int c = 1; c < div; c++)
                        {
                            ch = s[c];
                            string longName = shortOptions[ch] ?? ch.ToString();
                            string curValue = c + 1 == div ? value : null;
                            AddPair(options, s, longName, curValue);
                        }
                    }
                }
            }
            else if (atFolder != null && s.StartsWith("@"))
            {
                // e.g. "@list of options.txt"
                string atFile       = s.Substring(1);
                string fileContents = null;
                try {
                    string fullpath = Path.Combine(atFolder, atFile);
                    if (File.Exists(fullpath))
                    {
                        fileContents = File.OpenText(fullpath).ReadToEnd();
                    }
                } catch (Exception e) {
                    MessageSink.Default.Error(s, "Unable to use option file \"{0}\": {1}", atFile, e.Message);
                }
                if (fileContents != null)
                {
                    List <string> list = G.SplitCommandLineArguments(fileContents);

                    int maxMore = System.Math.Max(0, argLimit - args.Count);
                    if (list.Count > maxMore)
                    {
                        // oops, command limit exceeded
                        MessageSink.Default.Warning(s, "Limit of {0} commands exceeded", argLimit);
                        list.RemoveRange(maxMore, list.Count - maxMore);
                    }

                    args.InsertRange(i + 1, (IList <string>)list);
                }
            }
        }
Esempio n. 7
0
 /// <summary>
 /// Expands environment variables (e.g. %TEMP%) and @files in a list of
 /// command-line arguments, and adds any options of the form "--opt" or
 /// "--opt=value" to a dictionary.
 /// </summary>
 /// <param name="args">The original arguments to process.</param>
 /// <param name="options">Any long options (arguments that start with "--")
 /// will be added to this dictionary, and removed from <c>args</c>. This
 /// parameter cannot be null.
 ///   By default, long options are not case sensitive. In that case, the
 /// user's option name is converted to lower case.
 /// <para/>
 /// Long options are expected to have the form --ID or --ID=value, where ID
 /// matches the regex "[a-zA-Z_0-9-]+". If there is no "=" or ":", that's
 /// okay too. For example, --Id{foo} is equivalent to --Id={foo}; both yield
 /// in the name-value pair ("id", "{foo}"). If there is no value (no equals
 /// or colon), the value associated with the option is null.</param>
 /// <param name="atFolder">If a parameter has the form @filename, the folder
 /// specified by atFolder will be searched for an options text file with the
 /// user-specified filename, and the contents of the file will be expanded
 /// into the list of arguments (split using SplitCommandLineArguments). The
 /// expanded list can contain new @filenames, which are also processed. To
 /// search in the current directory, use "". The @filename may use an absolute
 /// path, which overrides this folder. To disable @filename expansion, set
 /// this parameter to null. Whether the feature is enabled or disabled,
 /// @filenames are <i>not</i> removed from <c>args</c>, in case you want to
 /// be aware of the filenames afterward.</param>
 /// <param name="shortOptions">A map from one-letter options that start with
 /// "-" rather than "--", to the name of the corresponding long option (this
 /// option can be null to ignore all short options.) For example, if this
 /// contains (<c>'a', "all"</c>), and the input <c>args</c> contains "-a:foo",
 /// the pair ("all", "foo") will be added to <c>options</c>. If a value in
 /// this map is null, the key itself is used. Short options can be combined;
 /// for example <c>-abc:foo</c> is equivalent to <c>-a -b -c:foo</c>. Short
 /// options are always case-sensitive; to define an option that is not case
 /// sensitive, place two entries in the dictionary e.g. ('a', "all") and
 /// ('A', "all"). If the user specifies a short option letter that is not
 /// recognized, the entire command will be ignored and left in args. For
 /// example, if <c>shortOptions</c> contains only ('a', "all") but <c>args</c>
 /// contains "-ab=foo", the command is ignored and left in <c>args</c>.
 /// Rationale: -ab=foo might be a filename.
 /// <para/>
 /// On the other hand, if -a is a valid option then <c>-a123</c> is also
 /// valid even when there is no option called '1'; the number "123" is
 /// treated as an argument to -a. Now, if '1' is a registered short option
 /// but '2' is not, then <c>-a123</c> is equivalent to <c>-a -1=23</c>.
 /// </param>
 /// <param name="twoArgOptions">A set of options in which the argument can
 /// be separated by a space from the option. For example, if the input is
 /// "--out foo.txt" and you want to recognize "foo.txt" as the argument to
 /// "--out", add the string "out" to this set. If you want to treat <i>all</i>
 /// options this way, use <c>InvertibleSet{string}.All</c>. Note:
 /// If the input is "--out:foo bar", "foo" is recognized as the argument to
 /// "--out" and "bar" is left alone, i.e. it is treated as unrelated.
 ///   Short options participate automatically. For example if "-f" means
 /// "--foo", and twoArgOptions contains "foo", then "-f arg" is interpreted
 /// like "--foo=arg".
 /// <para/>
 /// The argument will not be treated as an argument if it starts with a
 /// dash, e.g. in <c>--foo -*</c>, <c>-*</c> will not be treated as an
 /// argument to <c>--foo</c>, even if <c>-*</c> is not a registered option.
 /// </param>
 /// <param name="argLimit">A limit placed on the number of arguments when
 /// expanding @files. Such a file may refer to itself, and this is the only
 /// protection provided against infinite recursive expansion.</param>
 /// <param name="expandEnvVars">If true, environment variable references
 /// such as <c>%TEMP%</c> are expanded by calling the standard method
 /// <see cref="Environment.ExpandEnvironmentVariables"/>.</param>
 /// <param name="caseSensitiveLongOpts">If true, long options are case-
 /// sensitive. By default, long options are not case sensitive.</param>
 /// <remarks>
 /// Two types of options are recognized, short (-s) and long (--long), and
 /// only one argument is supported per option. The documentation is above.
 /// <para/>
 /// You can choose whether to permit duplicate options or not. If you use
 /// a standard <see cref="Dictionary{K,V}"/> to hold the options, an
 /// exception will occur when this method calls Add() to add the duplicate.
 /// The exception is caught, the first ocurrance is kept, and a warning
 /// message is printed to <see cref="MessageSink.Default"/>.
 /// <para/>
 /// To allow duplicates, store options in a different data structure such as
 /// <c>List(KeyValuePair(string, string))</c> or <c>BMultiMap(string,string)</c>.
 /// <para/>
 /// DOS-style slash-options like /foo are not supported. Since Windows
 /// recognizes the forward slash as a path separator, forward-slash options
 /// can be recognized as paths. If you want to recognize them as options
 /// instead, you can preprocess the argument list, replacing every command
 /// that starts with "/" with a "--" command:
 /// <code>
 ///   for (int i = 0; args.Count > i; i++)
 ///     if (args[i].StartsWith("/"))
 ///       args[i] = "--" + args[i].Substring(1);
 /// </code>
 /// <para/>
 /// Globs (e.g. *.txt) are not recognized or expanded, but environment
 /// variables are expanded when <c>expandEnvVars</c> is true.
 /// <para/>
 /// Quote marks are not processed. An argument of <c>"--a"</c>, with quote
 /// marks, is not recognized as an option (these quote marks should be
 /// removed before calling this method, e.g.
 /// <see cref="G.SplitCommandLineArguments"/> handles this.)
 /// </remarks>
 public static void ProcessCommandLineArguments(IList <string> args, ICollection <KeyValuePair <string, string> > options, string atFolder, IDictionary <char, string> shortOptions = null, InvertibleSet <string> twoArgOptions = null, int argLimit = 0xFFFF, bool expandEnvVars = true, bool caseSensitiveLongOpts = false)
 {
     CheckParam.IsNotNull("args", args);
     CheckParam.IsNotNull("options", options);
     for (int i = 0; i < args.Count; i++)
     {
         ProcessArgument(args, i, options, atFolder, shortOptions, twoArgOptions, argLimit, expandEnvVars, caseSensitiveLongOpts);
     }
     args.RemoveAll(s => s == null);
 }