/// \brief Parse a single argument; returning the new argument and /// updating Index. /// /// \param [in,out] Index - The current parsing position in the argument /// string list; on return this will be the index of the next argument /// string to parse. /// \param [in] FlagsToInclude - Only parse options with any of these flags. /// Zero is the default which includes all flags. /// \param [in] FlagsToExclude - Don't parse options with this flag. Zero /// is the default and means exclude nothing. /// /// <returns>The parsed argument, or 0 if the argument is missing values (in which case Index still points at the conceptual next argument string to parse).</returns> public Argument ParseOneArg(ArgumentList args, ref int index, int flagsToInclude = 0, int flagsToExclude = 0) { int prev = index; string str = args.GetArgString(index); // Anything that doesn't start with PrefixesUnion is an input, as is '-' // itself. if (IsInput(_prefixesUnion, str)) { return(new Argument(GetOption(_theInputOptionId), str, index++, str)); } IEnumerable <OptionInfo> startEnd = _optionInfos.Skip(_firstSearchableIndex); string name = string.Concat(str.SkipWhile(item => _prefixChars.Any(chars => chars == item))); // Search for the first next option which could be a prefix. startEnd = startEnd.SkipWhile(item => !name.StartsWith(item.Name.ToString(), StringComparison.InvariantCultureIgnoreCase)); // Options are stored in sorted order, with '\0' at the end of the // alphabet. Since the only options which can accept a string must // prefix it, we iteratively search for the next option which could // be a prefix. // // FIXME: This is searching much more than necessary, but I am // blanking on the simplest way to make it fast. We can solve this // problem when we move to TableGen. // Scan for first option which is a proper prefix. int i = 0; var startList = startEnd.ToList(); for (; i < startList.Count; i++) { int argSize = 0; // Scan for first option which is a proper prefix. for (; i < startList.Count; i++) { argSize = MatchOption(startList[i], str, _ignoreCase); if (argSize > 0) { break; } } if (i == startList.Count) { break; } Option opt = new Option(startList[i], this); if (flagsToInclude != 0 && !opt.HasFlag(flagsToInclude)) { continue; } if (opt.HasFlag(flagsToExclude)) { continue; } // See if this option matches. Argument a = opt.Accept(args, ref index, argSize); if (a != null) { return(a); } // Otherwise, see if this argument was missing values. if (prev != index) { return(null); } } // If we failed to find an option and this arg started with /, then it's // probably an input path. if (str[0] == '/') { return(new Argument(GetOption(_theInputOptionId), str, index++, str)); } return(new Argument(GetOption(_theUnknownOptionId), str, index++, str)); }
/// accept - Potentially accept the current argument, returning a /// new Argument instance, or 0 if the option does not accept this /// argument (or the argument is missing values). /// /// If the option accepts the current argument, accept() sets /// Index to the position where argument parsing should resume /// (even if the argument is missing values). /// /// \param ArgSize The number of bytes taken up by the matched Option prefix /// and name. This is used to determine where joined values /// start. public Argument Accept(ArgumentList args, ref int index, int argSize) { var unaliasedOption = GetUnaliasedOption(); string spelling; // If the option was an alias, get the spelling from the unaliased one. if (GetId() == unaliasedOption.GetId()) { spelling = args.GetArgString(index).Substring(0, argSize); } else { spelling = args.MakeArgString(unaliasedOption.GetPrefix() + unaliasedOption.GetName()); } var kind = GetKind(); if (kind == OptionKind.FlagClass) { if (argSize != args.GetArgString(index).Length) { return(null); } var a = new Argument(unaliasedOption, spelling, index++); if (!string.IsNullOrEmpty(GetAliasArgs())) { var vals = GetAliasArgs().Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); a.GetValues().AddRange(vals.Select(item => item)); //while (*Val != '\0') //{ // A.getValues().AddRange(Val); // // Move past the '\0' to the next argument. // Val += Val.Length + 1; //} } if (unaliasedOption.GetKind() == OptionKind.JoinedClass && string.IsNullOrEmpty(GetAliasArgs())) { a.GetValues().Add(string.Empty); } return(a); } else if (kind == OptionKind.JoinedClass) { string value = args.GetArgString(index).Substring(argSize); return(new Argument(unaliasedOption, spelling, index++, value)); } else if (kind == OptionKind.CommaJoinedClass) { // Always matches. var str = args.GetArgString(index).Substring(argSize). Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // + ArgSize; var a = new Argument(unaliasedOption, spelling, index++); a.GetValues().AddRange(str.Select(item => item)); a.SetOwnsValues(true); return(a); } else if (kind == OptionKind.SeparateClass) { // Matches iff this is an exact match. // FIXME: Avoid strlen. if (argSize != args.GetArgString(index).Length) { return(null); } index += 2; if (index > args.GetNumInputArgStrings() || args.GetArgString(index - 1) == null) { return(null); } return(new Argument(unaliasedOption, spelling, index - 2, args.GetArgString(index - 1))); } else if (kind == OptionKind.MultiArgClass) { // Matches iff this is an exact match. // FIXME: Avoid strlen. if (argSize != args.GetArgString(index).Length) { return(null); } index += 1 + GetNumArgs(); if (index > args.GetNumInputArgStrings()) { return(null); } var a = new Argument(unaliasedOption, spelling, index - 1 - GetNumArgs(), args.GetArgString(index - GetNumArgs())); for (var i = 1; i != GetNumArgs(); ++i) { a.GetValues().Add(args.GetArgString(index - GetNumArgs() + i)); } return(a); } else if (kind == OptionKind.JoinedOrSeparateClass) { // If this is not an exact match, it is a joined arg. // FIXME: Avoid strlen. if (argSize != args.GetArgString(index).Length) { var value = args.GetArgString(index).Substring(argSize); return(new Argument(this, spelling, index++, value)); } // Otherwise it must be separate. index += 2; if (index > args.GetNumInputArgStrings() || args.GetArgString(index - 1) == null) { return(null); } return(new Argument(unaliasedOption, spelling, index - 2, args.GetArgString(index - 1))); } else if (kind == OptionKind.JoinedAndSeparateClass) { // Always matches. index += 2; if (index > args.GetNumInputArgStrings() || args.GetArgString(index - 1) == null) { return(null); } return(new Argument(unaliasedOption, spelling, index - 2, args.GetArgString(index - 2).Substring(argSize), args.GetArgString(index - 1))); } else if (kind == OptionKind.RemainingArgsClass) { // Matches iff this is an exact match. // FIXME: Avoid strlen. if (argSize != args.GetArgString(index).Length) { return(null); } var a = new Argument(unaliasedOption, spelling, index++); while (index < args.GetNumInputArgStrings() && args.GetArgString(index) != null) { a.GetValues().Add(args.GetArgString(index++)); } return(a); } else if (kind == OptionKind.RemainingArgsJoinedClass) { var a = new Argument(unaliasedOption, spelling, index); if (argSize != args.GetArgString(index).Length) { a.GetValues().Add(args.GetArgString(index).Substring(argSize)); } index++; while (index < args.GetNumInputArgStrings() && args.GetArgString(index) != null) { a.GetValues().Add(args.GetArgString(index++)); } return(a); } throw new Exception("Invalid option kind!"); }