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<string>/*!*/ _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<string>();
+                _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<string>/*!*/ 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<string>/*!*/ 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<string> result = new List<string>();
-            
-            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<string>/*!*/ DoGlob(CodeContext/*!*/ context, string/*!*/ pattern, int flags) {
+            GlobMatcher matcher = new GlobMatcher(context, pattern, flags);
+            return matcher.DoGlob();
         }
 
-        private static IEnumerable<MutableString>/*!*/ GlobResults(MutableString/*!*/ pattern, int flags) {
+        private static IEnumerable<MutableString>/*!*/ 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;
===================================================================
