File: Microsoft\Tools\ServiceModel\SvcUtil\CommandLineParser.cs
Web Access
Project: src\src\svcutilcore\src\dotnet-svcutil.xmlserializer.csproj (dotnet-svcutil.xmlserializer)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace Microsoft.Tools.ServiceModel.SvcUtil.XmlSerializer
{
    using System;
    using System.Globalization;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using System.IO;
 
    internal enum SwitchType
    {
        Flag,
        SingletonValue,
        ValueList
    }
 
    internal class CommandSwitch
    {
        private readonly string _name;
        private readonly string _abbreviation;
        private readonly SwitchType _switchType;
 
        internal CommandSwitch(string name, string abbreviation, SwitchType switchType)
        {
            //ensure name doesn't start with '--' and abbreviation doesn't start with '-'
            //also convert to lower-case
            if (name.StartsWith("--"))
                name = name.Substring(2);
          
            _name = name.ToLower(CultureInfo.InvariantCulture);
 
            if (abbreviation.StartsWith("-"))
                abbreviation = abbreviation.Substring(1);
 
            _abbreviation = abbreviation.ToLower(CultureInfo.InvariantCulture);
 
            _switchType = switchType;
        }
 
        internal string Name
        {
            get { return _name; }
        }
 
#if NotUsed
        internal string Abbreviation
        {
            get { return abbreviation; }
        }
#endif
 
        internal SwitchType SwitchType
        {
            get { return _switchType; }
        }
 
        internal bool Equals(string other)
        { 
            //ensure that compare doesn't start with '--' or '-'
            //also convert to lower-case
            if (other.StartsWith("--"))
                other = other.Substring(2);
            else if (other.StartsWith("-"))
                other = other.Substring(1);
            
            //if equal to name, then return the OK
            if (_name.Equals(other, StringComparison.InvariantCultureIgnoreCase))
                return true;
            //now check abbreviation
            return _abbreviation.Equals(other, StringComparison.InvariantCultureIgnoreCase);
        }
 
        internal static CommandSwitch FindSwitch(string name, CommandSwitch[] switches)
        {
            foreach (CommandSwitch cs in switches)
                if (cs.Equals(name))
                    return cs;
            //if no match found, then return null
            return null;
        }
    }
 
    internal class ArgumentDictionary
    {
        private Dictionary<string, IList<string>> _contents;
 
        internal ArgumentDictionary(int capacity)
        {
            _contents = new Dictionary<string, IList<string>>(capacity);
        }
 
        internal void Add(string key, string value)
        {
            IList<string> values;
            if (!ContainsArgument(key))
            {
                values = new List<string>();
                Add(key, values);
            }
            else
                values = GetArguments(key);
 
            values.Add(value);
        }
 
        internal string GetArgument(string key)
        {
            IList<string> values;
            if (_contents.TryGetValue(key.ToLower(CultureInfo.InvariantCulture), out values))
            {
#if SM_TOOL
                Tool.Assert((values.Count == 1), "contains more than one argument please call GetArguments");
#endif
                return values[0];
            }
#if SM_TOOL
            Tool.Assert(false, "argument was not specified please call ContainsArgument to check this");
#endif
 
            return null; // unreachable code but the compiler doesn't know this.
        }
 
        internal IList<string> GetArguments(string key)
        {
            IList<string> result;
            if (!_contents.TryGetValue(key.ToLower(CultureInfo.InvariantCulture), out result))
                result = new List<string>();
            return result;
        }
 
        internal bool ContainsArgument(string key)
        {
            return _contents.ContainsKey(key.ToLower(CultureInfo.InvariantCulture));
        }
 
        internal void Add(string key, IList<string> values)
        {
            _contents.Add(key.ToLower(CultureInfo.InvariantCulture), values);
        }
 
        internal int Count
        {
            get { return _contents.Count; }
        }
    }
 
    internal static class CommandParser
    {
        internal static ArgumentDictionary ParseCommand(string[] cmd, CommandSwitch[] switches)
        {
            ArgumentDictionary arguments;   //switches/values from cmd line
 
            string arg;                     //argument to test next
            CommandSwitch argSwitch;        //switch corresponding to that argument
            string argValue;                //value corresponding to that argument
            int delim;                      //location of value delimiter (':' or '=')
 
            arguments = new ArgumentDictionary(cmd.Length);
            foreach (string s in cmd)
            {
                arg = s;
                bool argIsFlag = true;
 
                //if argument does not start with switch indicator, place into "default" arguments
                if (arg[0] != '-')
                {
                    arguments.Add(String.Empty, arg);
                    continue;
                }
 
                //if we have something which begins with '--' or '-', throw if nothing after it
                if (arg == "-" || arg == "--")
                    throw new ArgumentException(SR.Format(SR.ErrSwitchMissing, arg));
 
                //yank switch indicator ('--' or '-') off of command argument
                if (arg[1] != '-')
                    arg = arg.Substring(1);
                else
                    arg = arg.Substring(2);
 
                //check to make sure delimiter does not start off switch
                delim = arg.IndexOfAny(new char[] { ':', '=' });
                if (delim == 0)
                    throw new ArgumentException(SR.Format(SR.ErrUnexpectedDelimiter));
 
                //if there is no value, than create a null string
                if (delim == (-1))
                    argValue = String.Empty;
                else
                {
                    //assume valid argument now; must remove value attached to it
                    //must avoid copying delimeter into either arguments
                    argValue = arg.Substring(delim + 1);
                    arg = arg.Substring(0, delim);
                    argIsFlag = false;
                }
 
                //check if this switch exists in the list of possible switches
                //if no match found, then throw an exception
                argSwitch = CommandSwitch.FindSwitch(arg.ToLower(CultureInfo.InvariantCulture), switches);
                if (argSwitch == null)
                {
                    // Paths start with "/" on Unix, so the arg could potentially be a path.
                    // If we didn't find any matched option, check and see if it's a path.
                    string potentialPath = "/" + arg;
                    if (File.Exists(potentialPath))
                    {
                        arguments.Add(string.Empty, potentialPath);
                        continue;
                    }
 
                    throw new ArgumentException(SR.Format(SR.ErrUnknownSwitch, arg.ToLower(CultureInfo.InvariantCulture)));
                }
 
                //check if switch is allowed to have a value
                // if not and a value has been specified, then thrown an exception
                if (argSwitch.SwitchType == SwitchType.Flag)
                {
                    if (!argIsFlag)
                        throw new ArgumentException(SR.Format(SR.ErrUnexpectedValue, arg.ToLower(CultureInfo.InvariantCulture)));
                }
                else
                {
                    if (argIsFlag)
                        throw new ArgumentException(SR.Format(SR.ErrExpectedValue, arg.ToLower(CultureInfo.InvariantCulture)));
                }
 
                //check if switch is allowed to be specified multiple times
                // if not and it has already been specified and a new value has been paresd, throw an exception
                if (argSwitch.SwitchType != SwitchType.ValueList && arguments.ContainsArgument(argSwitch.Name))
                {
                    throw new ArgumentException(SR.Format(SR.ErrSingleUseSwitch, arg.ToLower(CultureInfo.InvariantCulture)));
                }
                else
                {
                    arguments.Add(argSwitch.Name, argValue);
                }
            }
 
            return arguments;
        }
    }
}