public override ReturnCode Execute( Interpreter interpreter, IClientData clientData, ArgumentList arguments, ref Result result ) { ReturnCode code = ReturnCode.Ok; if (interpreter != null) { if (arguments != null) { if (arguments.Count >= 2) { OptionDictionary options = new OptionDictionary( new IOption[] { new Option(null, OptionFlags.None, 1, Index.Invalid, "-ascii", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-dictionary", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-integer", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-random", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-real", null), new Option(null, OptionFlags.None, 2, Index.Invalid, "-increasing", null), new Option(null, OptionFlags.None, 2, Index.Invalid, "-decreasing", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-nocase", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-unique", null), new Option(null, OptionFlags.MustHaveValue, Index.Invalid, Index.Invalid, "-command", null), new Option(null, OptionFlags.MustHaveValue, Index.Invalid, Index.Invalid, "-index", null) // NOTE: Of sub-lists, not list. }); int argumentIndex = Index.Invalid; code = interpreter.GetOptions(options, arguments, 0, 1, Index.Invalid, false, ref argumentIndex, ref result); if (code == ReturnCode.Ok) { if ((argumentIndex != Index.Invalid) && ((argumentIndex + 1) == arguments.Count)) { StringList list = null; // // WARNING: Cannot cache list representation here, the list // is modified below. // code = Parser.SplitList( interpreter, arguments[argumentIndex], 0, Length.Invalid, false, ref list, ref result); if (code == ReturnCode.Ok) { Variant value = null; string indexText = null; if (options.IsPresent("-index", ref value)) { indexText = value.ToString(); } bool ascending = true; // FIXME: PRI 5: Default handling. if (options.IsPresent("-decreasing")) { ascending = false; } else if (options.IsPresent("-increasing")) { ascending = true; } bool noCase = false; if (options.IsPresent("-nocase")) { noCase = true; } bool unique = false; if (options.IsPresent("-unique")) { unique = true; } IntDictionary duplicates = null; IComparer <string> comparer = null; if (options.IsPresent("-command", ref value)) { StringList callbackArguments = null; if (value.IsList()) { callbackArguments = (StringList)value.Value; } else { string temporary = value.ToString(); code = Parser.SplitList( interpreter, temporary, 0, Length.Invalid, true, ref callbackArguments); } if (code == ReturnCode.Ok) { ICallback callback = CommandCallback.Create( MarshalFlags.Default, CallbackFlags.Default, ObjectFlags.Callback, ByRefArgumentFlags.None, interpreter, _Public.ClientData.Empty, null, callbackArguments, ref result); if (callback != null) { comparer = new _Comparers.StringCommandComparer( interpreter, callback, ascending, indexText, false, unique, interpreter.CultureInfo, ref duplicates); } else { code = ReturnCode.Error; } } } else if (options.IsPresent("-dictionary")) { comparer = new _Comparers.StringDictionaryComparer( interpreter, ascending, indexText, false, unique, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-integer")) { comparer = new _Comparers.StringIntegerComparer( interpreter, ascending, indexText, false, unique, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-random")) { comparer = new _Comparers.StringRandomComparer( interpreter, ascending, indexText, false, unique, interpreter.CultureInfo, interpreter.RandomNumberGenerator, ref duplicates); } else if (options.IsPresent("-real")) { comparer = new _Comparers.StringRealComparer( interpreter, ascending, indexText, false, unique, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-ascii") || true) // FIXME: PRI 5: Default handling. { comparer = new _Comparers.StringAsciiComparer( interpreter, ascending, indexText, false, noCase, unique, interpreter.CultureInfo, ref duplicates); } if (code == ReturnCode.Ok) { try { lock (interpreter.SyncRoot) /* TRANSACTIONAL */ { if (comparer != null) { list.Sort(comparer); } else { // // FIXME: This will never be hit because we always default // to using the StringAsciiComparer (above). // list.Sort(); // use .NET Framework defaults } } // // NOTE: If we are in unique mode, remove any duplicates from // the final resulting list now. // if (unique) { StringList uniqueList = new StringList(); // // NOTE: Process each element in the list to see if it has // been counted as a duplicate value by the comparer. // // If the value has not been added to the final resulting // list yet, add it now and mark the value so that it will // never be added again (i.e. we only want the first value // from every group of duplicates and we want all the other // values as well). // // HACK: In the worst possible case, this loop can have a runtime // of O(N^2), including called functions, primarily due to // the inability of .NET to provide proper context to // IComparer callbacks. This code could be avoided entirely // if there was an interface for sorting comparison callbacks // that provided the indexes of the elements being compared // in addition to their values. // foreach (string element in list) /* O(N) */ { // // NOTE: Has this value been marked as having been previously // added to the final resulting list? // int count = ListOps.GetDuplicateCount(comparer, duplicates, element); if (count != Count.Invalid) { // // NOTE: Add this element into the final resulting list. // Either it has no duplicates or we have not yet // added it to the final resulting list. // uniqueList.Add(element); // // NOTE: If this value had any duplicates, mark the value // as having been added to the final resulting list. // if (!ListOps.SetDuplicateCount(comparer, duplicates, element, Count.Invalid)) { result = String.Format( "failed to update duplicate count for element \"{0}\"", element); code = ReturnCode.Error; break; } } } // // NOTE: The list of unique elements is now the result. // if (code == ReturnCode.Ok) { list = uniqueList; } } if (code == ReturnCode.Ok) { result = list; } } catch (Exception e) { Engine.SetExceptionErrorCode(interpreter, e); if (e.InnerException != null) { result = e.InnerException.Message; } else if (e is ScriptException) { result = e.Message; } else { result = e; } code = ReturnCode.Error; } } } } else { if ((argumentIndex != Index.Invalid) && Option.LooksLikeOption(arguments[argumentIndex])) { result = OptionDictionary.BadOption(options, arguments[argumentIndex]); } else { result = "wrong # args: should be \"lsort ?options? list\""; } code = ReturnCode.Error; } } } else { result = "wrong # args: should be \"lsort ?options? list\""; code = ReturnCode.Error; } } else { result = "invalid argument list"; code = ReturnCode.Error; } } else { result = "invalid interpreter"; code = ReturnCode.Error; } return(code); }
public override ReturnCode Execute( Interpreter interpreter, IClientData clientData, ArgumentList arguments, ref Result result ) { ReturnCode code = ReturnCode.Ok; if (interpreter != null) { if (arguments != null) { if (arguments.Count >= 2) { OptionDictionary options = new OptionDictionary( new IOption[] { new Option(null, OptionFlags.None, 1, Index.Invalid, "-ascii", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-dictionary", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-integer", null), new Option(null, OptionFlags.None, 1, Index.Invalid, "-real", null), new Option(null, OptionFlags.None, 2, Index.Invalid, "-decreasing", null), new Option(null, OptionFlags.None, 2, Index.Invalid, "-increasing", null), new Option(null, OptionFlags.None, 3, Index.Invalid, "-exact", null), new Option(null, OptionFlags.None, 3, Index.Invalid, "-substring", null), new Option(null, OptionFlags.None, 3, Index.Invalid, "-glob", null), new Option(null, OptionFlags.None, 3, Index.Invalid, "-regexp", null), new Option(null, OptionFlags.None, 3, Index.Invalid, "-sorted", null), // NOTE: Implies "-exact" new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-variable", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-inverse", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-subindices", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-all", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-inline", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-nocase", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, "-not", null), new Option(null, OptionFlags.MustHaveValue, Index.Invalid, Index.Invalid, "-start", null), new Option(null, OptionFlags.MustHaveValue, Index.Invalid, Index.Invalid, "-index", null), new Option(null, OptionFlags.None, Index.Invalid, Index.Invalid, Option.EndOfOptions, null) }); int argumentIndex = Index.Invalid; code = interpreter.GetOptions(options, arguments, 0, 1, Index.Invalid, false, ref argumentIndex, ref result); if (code == ReturnCode.Ok) { if ((argumentIndex != Index.Invalid) && ((argumentIndex + 2) == arguments.Count)) { StringList list = null; /////////////////////////////////////////////////////////////////////// // // HACK: *PERF* This option enables an optimization that allows us to // use the cached list representation for a particular script // variable, if any, instead of re-parsing the string. If this // option is enabled, the first non-option argument is NOT the // list to search; rather, it is the variable name containing // the list to search. // bool isVariable = false; if (options.IsPresent("-variable")) { isVariable = true; } if (isVariable) { /* IGNORED */ interpreter.GetListVariableValue( VariableFlags.None, arguments[argumentIndex], false, true, true, ref list); } /////////////////////////////////////////////////////////////////////// // // NOTE: If no list representation is available, then parse the first // non-option argument string into a list. // if (list == null) { code = Parser.SplitList( interpreter, arguments[argumentIndex], 0, Length.Invalid, true, ref list, ref result); } if (code == ReturnCode.Ok) { Variant value = null; string indexText = null; if (options.IsPresent("-index", ref value)) { indexText = value.ToString(); } bool inverse = false; if (options.IsPresent("-inverse")) { inverse = true; } bool subIndexes = false; if (options.IsPresent("-subindices")) { subIndexes = true; } if ((indexText != null) || !subIndexes) { string start = null; if (options.IsPresent("-start", ref value)) { start = value.ToString(); } int startIndex = Index.Invalid; if (start != null) { code = Value.GetIndex( start, list.Count, ValueFlags.AnyIndex, interpreter.CultureInfo, ref startIndex, ref result); } if (code == ReturnCode.Ok) { bool all = false; if (options.IsPresent("-all")) { all = true; } bool inline = false; if (options.IsPresent("-inline")) { inline = true; } if (startIndex < list.Count) { if (startIndex < 0) { startIndex = 0; } bool ascending = true; // FIXME: PRI 5: Default handling. if (options.IsPresent("-decreasing")) { ascending = false; } else if (options.IsPresent("-increasing")) { ascending = true; } MatchMode mode = StringOps.DefaultMatchMode; bool sorted = false; if (options.IsPresent("-sorted")) { mode = MatchMode.Exact; sorted = true; } else if (options.IsPresent("-exact")) { mode = MatchMode.Exact; } else if (options.IsPresent("-substring")) { mode = MatchMode.SubString; } else if (options.IsPresent("-regexp")) { mode = MatchMode.RegExp; } else if (options.IsPresent("-glob")) { mode = MatchMode.Glob; } bool noCase = false; if (options.IsPresent("-nocase")) { noCase = true; } bool not = false; if (options.IsPresent("-not")) { not = true; } IntDictionary duplicates = null; IComparer <string> comparer = null; if (options.IsPresent("-exact") || options.IsPresent("-sorted")) { if (options.IsPresent("-dictionary")) { comparer = new _Comparers.StringDictionaryComparer( interpreter, ascending, indexText, true, false, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-integer")) { comparer = new _Comparers.StringIntegerComparer( interpreter, ascending, indexText, true, false, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-real")) { comparer = new _Comparers.StringRealComparer( interpreter, ascending, indexText, true, false, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-ascii") || true) // FIXME: PRI 5: Default handling. { // // NOTE: Check for things that the .NET Framework will not do by // default (via String.Compare). // if (!ascending || (indexText != null) || noCase) { comparer = new _Comparers.StringAsciiComparer( interpreter, ascending, indexText, true, noCase, false, interpreter.CultureInfo, ref duplicates); } } } else if (options.IsPresent("-regexp")) { comparer = new _Comparers.StringRegexpComparer( interpreter, ascending, indexText, true, noCase, false, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-substring")) { comparer = new _Comparers.StringSubStringComparer( interpreter, ascending, indexText, true, noCase, false, interpreter.CultureInfo, ref duplicates); } else if (options.IsPresent("-glob") || true) // FIXME: PRI 5: Default handling. { comparer = new _Comparers.StringGlobComparer( interpreter, ascending, indexText, true, noCase, false, interpreter.CultureInfo, ref duplicates); } try { string pattern = arguments[argumentIndex + 1]; int listIndex = Index.Invalid; StringList matches = all ? new StringList() : null; if (sorted && ((indexText == null) || (comparer != null)) && !all && !not && !inverse) { // // NOTE: Use the built-in binary search with the selected comparer. // listIndex = list.BinarySearch(startIndex, list.Count - startIndex, pattern, comparer); if (listIndex < 0) { listIndex = Index.Invalid; } } else if ((comparer != null) || all || not || inverse) { // // NOTE: Some custom handling is required, use the selected comparer // and options. // for (int searchIndex = startIndex; searchIndex < list.Count; searchIndex++) { // // NOTE: If we have a comparer object, use it; otherwise, use our // fallback matching routine. // bool match; if (inverse) { if (comparer != null) { match = (comparer.Compare(pattern, list[searchIndex]) == 0); } else { match = StringOps.Match(interpreter, mode, pattern, list[searchIndex], noCase); } } else { if (comparer != null) { match = (comparer.Compare(list[searchIndex], pattern) == 0); } else { match = StringOps.Match(interpreter, mode, list[searchIndex], pattern, noCase); } } // // NOTE: Do we want to consider this to be a match? // if ((match && !not) || (!match && not)) { if (all) { if (inline) { if (subIndexes) { string subValue = null; code = ListOps.SelectFromSubList( interpreter, list[searchIndex], indexText, false, interpreter.CultureInfo, ref subValue, ref result); if (code != ReturnCode.Ok) { break; } matches.Add(subValue); } else { matches.Add(list[searchIndex]); } } else { if (subIndexes) { IntList indexList = new IntList(new int[] { searchIndex }); code = ListOps.SelectFromSubList( interpreter, list[searchIndex], indexText, false, interpreter.CultureInfo, ref indexList, ref result); if (code != ReturnCode.Ok) { break; } matches.Add(indexList.ToString()); } else { matches.Add(searchIndex.ToString()); } } } else { listIndex = searchIndex; break; } } } } else { // // NOTE: No special handling required, use built-in find routine. // listIndex = list.IndexOf(pattern, startIndex); } // // NOTE: Make sure nothing in the search loop failed. // if (code == ReturnCode.Ok) { // // NOTE: Handle the result(s) of the search and build the result. // if (all) { // // NOTE: This may be an empty list. // result = matches; } else { if (listIndex != Index.Invalid) { // // NOTE: Match found, returning index or value, based on // "-inline" option. // if (inline) { result = list[listIndex]; } else { if (subIndexes) { IntList indexList = new IntList(new int[] { listIndex }); code = ListOps.SelectFromSubList( interpreter, list[listIndex], indexText, false, interpreter.CultureInfo, ref indexList, ref result); if (code == ReturnCode.Ok) { result = indexList.ToString(); } } else { result = listIndex; } } } else { // // NOTE: Match not found, returning invalid index or empty // value, based on "-inline" option. // if (inline) { result = String.Empty; } else { result = Index.Invalid; } } } } } catch (Exception e) { Engine.SetExceptionErrorCode(interpreter, e); if (e.InnerException != null) { result = e.InnerException.Message; } else if (e is ScriptException) { result = e.Message; } else { result = e; } code = ReturnCode.Error; } } else { if (all || inline) { result = String.Empty; } else { result = Index.Invalid; } } } } else { result = "-subindices cannot be used without -index option"; code = ReturnCode.Error; } } } else { if ((argumentIndex != Index.Invalid) && Option.LooksLikeOption(arguments[argumentIndex])) { result = OptionDictionary.BadOption(options, arguments[argumentIndex]); } else { result = "wrong # args: should be \"lsearch ?options? list pattern\""; } code = ReturnCode.Error; } } } else { result = "wrong # args: should be \"lsearch ?options? list pattern\""; code = ReturnCode.Error; } } else { result = "invalid argument list"; code = ReturnCode.Error; } } else { result = "invalid interpreter"; code = ReturnCode.Error; } return(code); }