edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Dir.cs;C448811 File: Dir.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Dir.cs;C448811 (server) 5/7/2008 3:32 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Dir.cs;RubyGlob @@ -394,88 +394,151 @@ return ungrouper.Flatten(); } - public static int FixedPathIndex(string/*!*/ path) { - int lastSlash = 0; - bool inEscape = false; - for (int i = 0; i < path.Length; i++) { - if (inEscape) { - inEscape = false; - continue; + class GlobMatcher { + PlatformAdaptationLayer/*!*/ _pal; + string/*!*/ _pattern; + int _flags; + bool _dirOnly; + bool _stripTwo; + List/*!*/ _result; + + internal GlobMatcher(CodeContext/*!*/ context, string/*!*/ pattern, int flags) { + _pal = context.LanguageContext.DomainManager.Platform; + _pattern = (pattern == "**") ? "*" : pattern; + _flags = flags | RubyFileOps.Constants.FNM_CASEFOLD; + _result = new List(); + _dirOnly = (_pattern.Length > 0) && (_pattern[_pattern.Length - 1] == '/'); + _stripTwo = false; + } + + internal int FindNextSeparator(int position, bool allowWildcard, out bool containsWildcard) { + int lastSlash = 0; + bool inEscape = false; + containsWildcard = false; + for (int i = position; i < _pattern.Length; i++) { + if (inEscape) { + inEscape = false; + continue; + } + char c = _pattern[i]; + if (c == '\\') { + inEscape = true; + continue; + } else if (c == '*' || c == '?' || c == '[') { + if (!allowWildcard) { + return lastSlash + 1; + } + containsWildcard = true; + } else if (c == '/' || c == ':') { + if (containsWildcard) { + return i; + } + lastSlash = i; + } } - if (path[i] == '\\') { - inEscape = true; - continue; - } else if (path[i] == '*' || path[i] == '?' || path[i] == '[') { - return lastSlash; - } else if (path[i] == '/' || path[i] == ':') { - lastSlash = i; - } + return _pattern.Length; } - return -1; - } - public static IList/*!*/ DoGlob(string/*!*/ pattern, int flags) { - bool dirOnly = false; - if (pattern.EndsWith("/")) { - dirOnly = true; - pattern = pattern.Substring(0, pattern.Length - 1); + private void TestPath(string path, int patternEnd, bool isLastPathSegment) { + string pathName = path.Replace('\\', '/'); + if (_stripTwo) { + pathName = pathName.Substring(2); + } + if (!isLastPathSegment) { + DoGlob(pathName, patternEnd); + } else if (_pal.DirectoryExists(pathName)) { + _result.Add(pathName); + } else if (!_dirOnly && _pal.FileExists(pathName)) { + _result.Add(pathName); + } } - int pos = FixedPathIndex(pattern); - if (pos < 0) { - if (PlatformAdaptationLayer.Default.FileExists(pattern) || PlatformAdaptationLayer.Default.DirectoryExists(pattern)) { - return new string[1] { pattern }; - } else { + internal IList/*!*/ DoGlob() { + if (_pattern.Length == 0) { return ArrayUtils.EmptyStrings; } + + int pos = 0; + string baseDirectory = "."; + if (_pattern[0] == '/' || _pattern.IndexOf(':') >= 0) { + bool containsWildcard; + pos = FindNextSeparator(0, false, out containsWildcard); + if (pos == _pattern.Length) { + TestPath(_pattern, pos, true); + return _result; + } + if (pos > 0 || _pattern[0] == '/') { + baseDirectory = _pattern.Substring(0, pos); + } + } + + _stripTwo = (baseDirectory == "."); + + DoGlob(baseDirectory, pos); + return _result; } - string baseDirectory; - MutableString mPattern = MutableString.Create(pattern); - bool stripLeadingChars; - if (pos > 0 || pattern[0] == '/') { - baseDirectory = pattern.Substring(0, pos + 1); - stripLeadingChars = false; - } else { - baseDirectory = "."; - stripLeadingChars = true; - } + internal void DoGlob(string/*!*/ baseDirectory, int position) { + if (!_pal.DirectoryExists(baseDirectory)) { + return; + } - if (!PlatformAdaptationLayer.Default.DirectoryExists(baseDirectory)) { - return ArrayUtils.EmptyStrings; - } + bool containsWildcard; + int patternEnd = FindNextSeparator(position, true, out containsWildcard); + bool isLastPathSegment = (patternEnd == _pattern.Length); + string dirSegment = _pattern.Substring(position, patternEnd - position); - int matchFlags = flags | RubyFileOps.Constants.FNM_PATHNAME | RubyFileOps.Constants.FNM_CASEFOLD; - List result = new List(); - - string[] files = Directory.GetFileSystemEntries(baseDirectory); - foreach (string file in files) { - string pathName = file.Replace('\\', '/'); - if (stripLeadingChars) { - pathName = file.Substring(2); + if (!isLastPathSegment) { + patternEnd++; } - if (RubyFileOps.FnMatch(null, mPattern, MutableString.Create(pathName), matchFlags)) { - if (dirOnly) { - if (PlatformAdaptationLayer.Default.DirectoryExists(file)) - result.Add(pathName); - } else { - result.Add(pathName); + + if (!containsWildcard) { + string path = baseDirectory + "/" + dirSegment; + TestPath(path, patternEnd, isLastPathSegment); + return; + } + + MutableString mPattern = MutableString.Create(dirSegment); + bool doubleStar = dirSegment.Equals("**"); + if (doubleStar) { + DoGlob(baseDirectory, patternEnd); + } + + string[] files = Directory.GetFileSystemEntries(baseDirectory); + foreach (string file in files) { + string objectName = Path.GetFileName(file); + if (RubyFileOps.FnMatch(null, mPattern, MutableString.Create(objectName), _flags)) { + TestPath(file, patternEnd, isLastPathSegment); + if (doubleStar) { + DoGlob(file, position); + } } } - } - if ((flags & RubyFileOps.Constants.FNM_DOTMATCH) != 0 || mPattern.GetChar(0) == '.') { - if (RubyFileOps.FnMatch(null, mPattern, MutableString.Create("."), matchFlags)) { - result.Add("."); + if (isLastPathSegment && (_flags & RubyFileOps.Constants.FNM_DOTMATCH) != 0 || mPattern.GetChar(0) == '.') { + if (RubyFileOps.FnMatch(null, mPattern, MutableString.Create("."), _flags)) { + string directory = baseDirectory + "/."; + if (_dirOnly) { + directory += '/'; + } + TestPath(directory, patternEnd, true); + } + if (RubyFileOps.FnMatch(null, mPattern, MutableString.Create(".."), _flags)) { + string directory = baseDirectory + "/.."; + if (_dirOnly) { + directory += '/'; + } + TestPath(directory, patternEnd, true); + } } - if (RubyFileOps.FnMatch(null, mPattern, MutableString.Create(".."), matchFlags)) { - result.Add(".."); - } } + } - return result; + private static IList/*!*/ DoGlob(CodeContext/*!*/ context, string/*!*/ pattern, int flags) { + GlobMatcher matcher = new GlobMatcher(context, pattern, flags); + return matcher.DoGlob(); } - private static IEnumerable/*!*/ GlobResults(MutableString/*!*/ pattern, int flags) { + private static IEnumerable/*!*/ GlobResults(CodeContext/*!*/ context, MutableString/*!*/ pattern, int flags) { if (pattern.Length == 0) { yield break; } @@ -487,7 +550,7 @@ } foreach (string group in groups) { - foreach (string filename in DoGlob(group, flags)) { + foreach (string filename in DoGlob(context, group, flags)) { yield return MutableString.Create(filename); } } @@ -495,7 +558,7 @@ [RubyMethod("glob", RubyMethodAttributes.PublicSingleton)] public static object Glob(CodeContext/*!*/ context, object self, BlockParam block, [NotNull]MutableString/*!*/ pattern, [Optional]int flags) { - foreach (MutableString fileName in GlobResults(pattern, flags)) { + foreach (MutableString fileName in GlobResults(context, pattern, flags)) { object result = _GlobSite.Invoke(context, block, fileName); if (block.BlockJumped(result)) { return result; @@ -518,7 +581,7 @@ [RubyMethod("[]", RubyMethodAttributes.PublicSingleton)] public static RubyArray/*!*/ Glob(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ pattern, [Optional]int flags) { RubyArray ret = new RubyArray(); - foreach (MutableString fileName in GlobResults(pattern, flags)) { + foreach (MutableString fileName in GlobResults(context, pattern, flags)) { ret.Add(fileName); } return ret; ===================================================================