public string GetAnyArgName_OrDefault(string defaultRet, params string[] args)
    {
        string ret = GetAnyArgName(args);

        if (StringSupportMethods.IsStringNullOrWhitespace(ret))
        {
            return(defaultRet);
        }
        else
        {
            return(ret);
        }
    }
    /// <summary>
    /// For argument aliases (like -n or -num)
    /// </summary>
    public string GetArgValue(params string[] argAliases)
    {
        foreach (string s in argAliases)
        {
            CmdArg argInfo;
            string value = GetArgValue(s, out argInfo);

            if (!StringSupportMethods.IsStringNullOrWhitespace(value))
            {
                return(value);
            }
        }

        return("");
    }
        public static bool EndsWithAny_NoCase(string input, string[] startsWithStrings)
        {
            input = input.Trim();

            foreach (string s in startsWithStrings)
            {
                if (!StringSupportMethods.IsStringNullOrWhitespace(s))           // EndsWith returns true for ending with "" blank strings when the string has data.
                {
                    if (input.EndsWith(s, StringComparison.CurrentCultureIgnoreCase))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Returns the specific string that the supplied string starts with
        /// </summary>
        public static string FindStartingString(string input, string[] startingPossibilites)
        {
            List <string> strings         = new List <string>(startingPossibilites);
            List <string> stringsByLength = strings.OrderBy(x => x.Length).Reverse().ToList();

            foreach (string s in stringsByLength)
            {
                if (!StringSupportMethods.IsStringNullOrWhitespace(s))           // EndsWith returns true for ending with "" blank strings when the string has data. Doing this check just in case StartsWith does too
                {
                    if (input.StartsWith(s, StringComparison.CurrentCultureIgnoreCase))
                    {
                        return(s);
                    }
                }
            }

            return("");
        }
        /// <summary>
        /// Splits out the combined File Name + Command Line into their own string
        /// </summary>
        public static void SplitCommandLineInto(string cmdLineAndFile, out string fileName, out string commandLine, bool expandEnvVar = true)
        {
            // Output params
            fileName    = "";
            commandLine = "";

            // 1. To be nice, if the user didn't quote the path to the file with spaces in it, then
            // Systematically search for a file in existence with spaces, until either 1 is found, or none is found and the best case scenario is selected
            //
            int startingArrayIndexForCmdLine = 1;

            string fileNameChecking = "";

            string[] splitAppArgs = CmdLineHelper.SupportMethods.CommandLineToArgs(cmdLineAndFile, true, false);
            if (splitAppArgs.Length == 0)
            {
                // Only a file Name was supplied if there are no spaces
                fileName = cmdLineAndFile;
            }
            else
            {
                for (int i = 0; i < splitAppArgs.Length; i++)
                {
                    fileNameChecking += ExpandEnvVar(splitAppArgs[i].Replace("\"", ""), expandEnvVar);
                    if (File.Exists(fileNameChecking))
                    {
                        startingArrayIndexForCmdLine = i + 1;
                        fileName = fileNameChecking;
                        break;
                    }

                    fileNameChecking += " ";
                }
            }

            // Set the output parameter
            if (File.Exists(fileNameChecking))
            {
                fileName = fileNameChecking;
            }


            //
            // Otherwise, if the file name wasn't obtained, go select the first non-blank filename:
            //

            if (StringSupportMethods.IsStringNullOrWhitespace(fileName))
            {
                // Build up all the rest of the items in the array specified within the -a "cmd.exe /s" switch
                for (int i = 0; i < splitAppArgs.Length; i++)
                {
                    if (StringSupportMethods.IsStringNullOrWhitespace(fileName))
                    {
                        // skip to the first token and treat it as the file name
                        if (StringSupportMethods.IsStringNullOrWhitespace(splitAppArgs[i]))
                        {
                            continue;
                        }
                        else
                        {
                            startingArrayIndexForCmdLine = i + 1;
                            fileName = splitAppArgs[i];
                        }
                    }
                }
            }


            //
            // Build the command line manually
            //
            for (int i = startingArrayIndexForCmdLine; i < splitAppArgs.Length; i++)
            {
                // Added support for environment variables (such as for 7z which wont support them)
                commandLine += ExpandEnvVar(splitAppArgs[i], expandEnvVar) + " ";
            }


            fileName = fileName.Trim();

            // Necessary or else a space will be at the end of the CmdLine when specifying [a] [a] for example. Can cause issues with some applications like Tweaking Registry Backup
            commandLine = commandLine.Trim();
        }
    /// <summary>
    /// Core method - Initalizes an argument list (which will be returned) given a string array of command line arguments
    /// </summary>
    private List <CmdArg> ParseArgs(string[] argArray, bool includeSpecialCommands = false)
    {
        // Note:
        // 'argArray' needs to be a linear list of Arg Name (ex: "-Say") followed next by its Arg Value (ex: "Hello"), Name, Value, ...

        List <CmdArg> retList = new List <CmdArg>();

        CmdArg currArg         = null;
        bool   currentArgAdded = true;              // (true: start out like a previous argument was successfully added)



        // Flag used to prevent checking for "-" or "/" in parameters that are nested and meant to be passed on to the next application's command line,
        // which will also use the same special characters.
        bool passEverything           = false;
        bool startingPassAllIndicator = false;
        bool startedPassAllRightNow   = false;


        // Now adding support to [A] nesting like [A1] [A1], [A2] [A2] (possibly add support for any number later)...
        // See ParseArgs_NestingCheck


        // Arguments here within the argArray will be separated out by spaces (like a split on " ")
        // So the logic in this loop determines which arguments to combine, and which ones are separate based on special argument characters encountered:
        // Those are "-" or "/".
        bool startOfNewArgument = true;

        bool turnOffSlashSwitch = false;
        bool turnOffDashSwitch  = false;

        for (int i = 0; i < argArray.Length; i++)
        {
            var argStr = argArray[i].Trim();


            // 8-9-17
            // Always reset -- only set on first encountered [a]
            if (startingPassAllIndicator)
            {
                // Reset this flag
                startingPassAllIndicator = false;

                string startingString = StringSupportMethods.FindStartingString(argStr, PASS_ALL_INDICATORS);
                if (!StringSupportMethods.IsStringNullOrWhitespace(startingString) &&
                    startingString.ToLower().Trim() == argStr.ToLower().Trim())
                {
                    // In this case, this will be the ending [a], so just stop passEverything, and this loop execution can be completly skipped
                    passEverything = false;
                    continue;
                }
            }


            // Always reset
            startedPassAllRightNow = false;


            // Always recheck to see if the current argument begins with the "include-everything", which is a single double-quote (but the argName / caller has to escape it via 3 double quotes: """)
            // This is re-evaluated below
            if (passEverything == false)
            {
                // Special args:
                //      -disableSlash
                //      -disableDash
                // If found, then "-" or "/" will not be considered the start of a new argument
                // This is an alternative to specifying [a] or [all] around a string such as:  -a [a] cmd.exe /k ... [a]   -->  -noSlash -a cmd.exe /k ...
                if (!includeSpecialCommands && (DisableSlash || argStr.ToLower() == "-disableslash"))
                {
                    turnOffSlashSwitch     = true;
                    WasDashOrSlashDisabled = true;
                    continue;
                }
                else if (!includeSpecialCommands && (DisableDash || argStr.ToLower() == "-disabledash"))
                {
                    turnOffDashSwitch      = true;
                    WasDashOrSlashDisabled = true;
                    continue;
                }

                // If a quote is encountered, it means that the user escaped it in the command line by passing it as """
                // When this occurs it means to pass everything through in this current parameter (meaning I can nest in other single "-" or "/" as special chars to a sub-application that also uses these, like cmd.exe, or another application that I created which uses this parser)
                passEverything = StringSupportMethods.StartsWithAny_NoCase(argStr, PASS_ALL_INDICATORS);

                if (passEverything && currArg != null)
                {
                    currArg.FullArgSupplied = true;

                    startingPassAllIndicator = true;        // Prevention of setting disablePassEverything to true below when checking to see if the string is the ending [A]
                    startedPassAllRightNow   = true;
                }


                //startOfNewArgument = true;
            }
            else
            {
                // Always disable startingPassAllIndicator unless it was just enabled for the first [A]
                if (!startedPassAllRightNow)
                {
                    startingPassAllIndicator = false;
                }
            }


            //
            // New arg or not?
            //

            // Start a new arg if the current string begins with a "-" or "/"
            if (passEverything == false &&
                ((!turnOffDashSwitch && argStr.StartsWith("-")) ||
                 (!turnOffSlashSwitch && argStr.StartsWith("/"))))
            {
                //
                // New argument to add
                //

                startOfNewArgument = true;

                // If a new arg was encountered before the previous argument's value, then simply add in the previous argument
                if (currentArgAdded == false)
                {
                    if (currArg != null)
                    {
                        retList.Add(currArg);

                        currentArgAdded = true;
                    }
                }

                // Start the new arg
                currArg = new CmdArg();
                if (argStr.Length > 1)
                {
                    // Store the argument Name
                    currArg.ArgDelimiterUsed = argStr.Substring(0, 1);      //  the "-" or "/" char
                    currArg.Name             = argStr.Substring(1);         // Remove the "-" or "/" start of the argument Name
                    currentArgAdded          = false;
                }
            }
            else
            {
                //
                // Not a new argument (so append this onto the current one in progress)
                //

                // If this is not a new arg Name, then this string is added to the current argument's value
                if (currArg != null)
                {
                    // Note:
                    // If the current argument has data in it from the previous array value, then that means there was a space in the cmdline parsed from CommandLineToArgvW
                    //    and hence can be preserved by adding 1 space
                    //    HOPEFULLY only is one space in the Name, if 2 spaces back-to-back, then this could be problematic, as we wouldn't know, unless implementing our own CommandLineToArgvW parser

                    bool disablePassEverything = false;


                    if (
                        !startOfNewArgument &&          // Prevent the starting [a] from being looked at as the ending [a]      (not sufficient, need the next check too: !startedPassAllRightNow)
                        !startedPassAllRightNow &&      // Set to true if this is the starting [a]                              Added - 8-9-17
                        passEverything &&
                        StringSupportMethods.EndsWithAny_NoCase(argStr, PASS_ALL_INDICATORS)
                        )
                    {
                        // Stop the PassEverything sequence if this argument ends with the quote
                        disablePassEverything = true;


                        argStr = StringSupportMethods.ReplaceAny_NoCase(argStr, PASS_ALL_INDICATORS, "");
                    }
                    else if (currArg.FullArgSupplied)
                    {
                        // 8-9-17
                        // Passing something like this:     -u0 -s [a]-1[a] -a [a] C:\Program Files (x86)\Tweaking.com\Registry Backup\TweakingRegistryBackup.exe /silent [a]
                        //     Causes the pass-all to occur all the way to the end after the first set of [a] [a], since there is no space in that value (between the [a]s).
                        //     Hence its all grouped under -s, and no -a is seen as another argument
                        //     Therefore a check must be done to see if the string ends in [a] (excluding if its the starting [a] in the event that the user did pass in spaces)

                        string startingString = StringSupportMethods.FindStartingString(argStr, PASS_ALL_INDICATORS);
                        if (!StringSupportMethods.IsStringNullOrWhitespace(startingString) &&                           // MUST check for IsNullOrWhitespace, otherwise EndsWith will return true for ending with "" (a blank string)... probably the same for starts with
                            argStr.Length > startingString.Length)
                        {
                            // This shows that this scenario is possibly like this: [a]-1[a]        That is, [a]-1[a] length is greater than [a]
                            // Must check the length for it being grater, otherwise having the argStr just be [a] (that is a space was added between this and the actual full string), would incorrectly satisfy StartsWith and EndsWith
                            // However its possible that a space was added, so therefore we now must check to see if the string ends with this indicator
                            if (argStr.EndsWith(startingString, StringComparison.CurrentCultureIgnoreCase))
                            {
                                // Go ahead and just disable Pass Everything now (we could just set startingPassAllIndicator = false to satisfy the condition below, but we truly just want disablePassEverything to disable it at the end of this method)
                                disablePassEverything = true;
                            }
                        }


                        // Fix / Check - 11-13-16
                        // Before replacing [A] or [ALL], need to check if the string already ends with [A] or [ALL] in the event that no space was provided in the trailing [a] or [all]
                        // Ex: -s [a]-2[a] -d hello     -s would incorrectly include everything else
                        //     -s [a]-a [a] -d hello    This would work, but we don't want to rely on the user having to remember that a space in the trailing [a] is required
                        if (
                            passEverything &&
                            !startingPassAllIndicator &&                    // Ensures that this is not turned off on the first encountered [A]

                            // Using EndsWith in the event that no space was provided at the end and the [A] is right next to the last char and is included in this token
                            StringSupportMethods.EndsWithAny_NoCase(argStr, PASS_ALL_INDICATORS)
                            )
                        {
                            disablePassEverything = true;
                        }

                        argStr = StringSupportMethods.ReplaceAny_NoCase(argStr, PASS_ALL_INDICATORS, "");

                        startOfNewArgument = false;             // Toggle off now, since this is going to be a string of arguments until an ending [a] is found
                    }



                    //
                    // Update the arg value
                    //

                    // Always add a space between the args, which is possible until a new switch is encountered - 11-1-16
                    if (!StringSupportMethods.IsStringNullOrWhitespace(currArg.Value))        // Will be blank if this was a [a]
                    {
                        argStr = " " + argStr;
                    }


                    // Need to trim off any quotes, which may have been supplied in order to pass in a negative value, like "-1", which will be undesirably returned when called GetArgValue (since it cannot be parsed into an int)
                    if (!passEverything)
                    {
                        argStr = StringSupportMethods.TrimQuotes(argStr);
                    }

                    // Store the Argument Value
                    currArg.Value += argStr;

                    // Optionally add it in the list -- Only add in this argument if its a brand new one
                    if (startOfNewArgument && !StringSupportMethods.IsStringNullOrWhitespace(currArg.Value.Trim()))
                    {
                        retList.Add(currArg);

                        // [Re]set flags:
                        currentArgAdded    = true;
                        startOfNewArgument = false;
                    }

                    // Lastly, toggle off the special case flag once complete
                    if (disablePassEverything)
                    {
                        passEverything = false;
                    }
                }
            }
        }       // foreach arg


        // If this loop finished (no more arguments), and the last argument wasn't added (it didn't have a value) then add it in
        if (currentArgAdded == false)
        {
            if (currArg != null)
            {
                retList.Add(currArg);
            }
        }

        // Ensure everything is tidy
        for (int i = 0; i < retList.Count; i++)
        {
            retList[i].Value = retList[i].Value.Trim();
        }

        return(retList);
    }
    /// <summary>
    /// Initializes an argument list (which will be returned) given a single linear string as the command line
    /// </summary>
    private List <CmdArg> ParseArgs_NestingCheck(string commandLine, bool includeSpecialCommands = false)
    {
        // TO TEST FURTHER

        bool isAll           = StringSupportMethods.ContainsNoCase(commandLine, "[ALL1]");
        bool nestingSupplied = isAll || StringSupportMethods.ContainsNoCase(commandLine, "[A1]");

        //bool all2 = ContainsNoCase(commandLine, "[ALL2]") || ContainsNoCase(commandLine, "[A2]");

        if (nestingSupplied)
        {
            string toFind = "";
            if (isAll)
            {
                toFind = "[ALL1]";
            }
            else
            {
                toFind = "[A1]";
            }

            int startPos = commandLine.IndexOf(toFind, StringComparison.CurrentCultureIgnoreCase);
            if (startPos >= 0)
            {
                startPos += toFind.Length;          // Skip over this

                int endPos = commandLine.LastIndexOf(toFind, StringComparison.CurrentCultureIgnoreCase);
                if (endPos > startPos)
                {
                    int posOfSwitchStart = commandLine.LastIndexOf("-", startPos, StringComparison.CurrentCultureIgnoreCase);
                    int posOfWhiteSpace  = commandLine.IndexOf(" ", posOfSwitchStart);
                    if (posOfSwitchStart >= 0 && posOfWhiteSpace >= 0)
                    {
                        string[] switchExtracts = StringSupportMethods.ExtractAround(commandLine, posOfSwitchStart, posOfWhiteSpace, 1);
                        if (switchExtracts.Length == 3)
                        {
                            string[] extracts          = StringSupportMethods.ExtractAround(commandLine, startPos, endPos, toFind.Length);
                            string   before            = StringSupportMethods.ReplaceNoCase(extracts[0], toFind, "");
                            string   nestedCommandLine = extracts[1];
                            string   after             = StringSupportMethods.ReplaceNoCase(extracts[2], toFind, "");

                            // Remove the switch, which will be stored manually below (and should be kept out of what will be parsed next)
                            before = StringSupportMethods.SubstringIndex(before, 0, posOfSwitchStart - 1);

                            // Remove this process as part of the command line
                            before = SupportMethods.SplitOutCmdLine(before);

                            List <CmdArg> list = ParseArgs(SupportMethods.CommandLineToArgs(before.Trim() + " " + after.Trim()), includeSpecialCommands);

                            string argName = switchExtracts[1].Replace("-", "");

                            list.Add(new CmdArg
                            {
                                FullArgSupplied = true,               // Set indicator
                                Name            = argName.Substring(1).Trim(),
                                Value           = nestedCommandLine
                            });

                            return(list);
                        }
                    }
                }
            }
        }

        string[] args = SupportMethods.CommandLineToArgs(commandLine);
        WasAnyArgSupplied = (args.Length >= 1);

        return(ParseArgs(args, includeSpecialCommands));
    }