Skip to content

Commit 2fc989c

Browse files
committed
[GR-19220] Fix Dir.glob returning a blank string entry in some cases
PullRequest: truffleruby/3813
2 parents bfbc036 + 7e1cdca commit 2fc989c

File tree

3 files changed

+146
-15
lines changed

3 files changed

+146
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ New features:
55

66
Bug fixes:
77

8+
* Fix `Dir.glob` returning blank string entry with leading `**/` in glob and `base:` argument (@rwstauner).
89

910
Compatibility:
1011

spec/ruby/core/dir/glob_spec.rb

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,134 @@
182182
Dir.glob('**/**/**').should_not.empty?
183183
end
184184

185+
it "handles **/** with base keyword argument" do
186+
Dir.glob('**/**', base: "dir").should == ["filename_ordering"]
187+
188+
expected = %w[
189+
nested
190+
nested/directory
191+
nested/directory/structure
192+
nested/directory/structure/bar
193+
nested/directory/structure/baz
194+
nested/directory/structure/file_one
195+
nested/directory/structure/file_one.ext
196+
nested/directory/structure/foo
197+
nondotfile
198+
].sort
199+
200+
Dir.glob('**/**', base: "deeply").sort.should == expected
201+
end
202+
203+
it "handles **/ with base keyword argument" do
204+
expected = %w[
205+
/
206+
directory/
207+
directory/structure/
208+
]
209+
Dir.glob('**/', base: "deeply/nested").sort.should == expected
210+
end
211+
212+
it "handles **/nondotfile with base keyword argument" do
213+
expected = %w[
214+
deeply/nondotfile
215+
nondotfile
216+
subdir_one/nondotfile
217+
subdir_two/nondotfile
218+
]
219+
Dir.glob('**/nondotfile', base: ".").sort.should == expected
220+
end
221+
222+
it "handles **/nondotfile with base keyword argument and FNM_DOTMATCH" do
223+
expected = %w[
224+
.dotsubdir/nondotfile
225+
deeply/nondotfile
226+
nested/.dotsubir/nondotfile
227+
nondotfile
228+
subdir_one/nondotfile
229+
subdir_two/nondotfile
230+
]
231+
Dir.glob('**/nondotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected
232+
end
233+
234+
it "handles **/.dotfile with base keyword argument" do
235+
expected = %w[
236+
.dotfile
237+
deeply/.dotfile
238+
subdir_one/.dotfile
239+
]
240+
Dir.glob('**/.dotfile', base: ".").sort.should == expected
241+
end
242+
243+
it "handles **/.dotfile with base keyword argument and FNM_DOTMATCH" do
244+
expected = %w[
245+
.dotfile
246+
.dotsubdir/.dotfile
247+
deeply/.dotfile
248+
nested/.dotsubir/.dotfile
249+
subdir_one/.dotfile
250+
]
251+
Dir.glob('**/.dotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected
252+
end
253+
254+
it "handles **/.* with base keyword argument" do
255+
expected = %w[
256+
.dotfile.ext
257+
directory/structure/.ext
258+
].sort
259+
260+
Dir.glob('**/.*', base: "deeply/nested").sort.should == expected
261+
end
262+
263+
# 2.7 and 3.0 include a "." entry for every dir: ["directory/.", "directory/structure/.", ...]
264+
ruby_version_is '3.1' do
265+
it "handles **/.* with base keyword argument and FNM_DOTMATCH" do
266+
expected = %w[
267+
.
268+
.dotfile.ext
269+
directory/structure/.ext
270+
].sort
271+
272+
Dir.glob('**/.*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected
273+
end
274+
275+
it "handles **/** with base keyword argument and FNM_DOTMATCH" do
276+
expected = %w[
277+
.
278+
.dotfile.ext
279+
directory
280+
directory/structure
281+
directory/structure/.ext
282+
directory/structure/bar
283+
directory/structure/baz
284+
directory/structure/file_one
285+
directory/structure/file_one.ext
286+
directory/structure/foo
287+
].sort
288+
289+
Dir.glob('**/**', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected
290+
end
291+
end
292+
293+
it "handles **/*pattern* with base keyword argument and FNM_DOTMATCH" do
294+
expected = %w[
295+
.dotfile.ext
296+
directory/structure/file_one
297+
directory/structure/file_one.ext
298+
]
299+
300+
Dir.glob('**/*file*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected
301+
end
302+
303+
it "handles **/glob with base keyword argument and FNM_EXTGLOB" do
304+
expected = %w[
305+
directory/structure/bar
306+
directory/structure/file_one
307+
directory/structure/file_one.ext
308+
]
309+
310+
Dir.glob('**/*{file,bar}*', File::FNM_EXTGLOB, base: "deeply/nested").sort.should == expected
311+
end
312+
185313
it "handles simple filename patterns" do
186314
Dir.glob('.dotfile').should == ['.dotfile']
187315
end

src/main/ruby/truffleruby/core/dir_glob.rb

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -225,18 +225,18 @@ class StartRecursiveDirectories < Node
225225
def process_directory(matches, parent, entry, glob_base_dir)
226226
raise 'invalid usage' if parent || entry
227227

228-
# Even though the recursive entry is zero width
229-
# in this case, its left separator is still the
230-
# dominant one, so we fix things up to use it.
231-
if @separator
232-
else
233-
@next.process_entry '', Truffle::DirOperations::DT_DIR, matches, parent, glob_base_dir if glob_base_dir
234-
end
235-
236228
stack = [[nil, subdir_entries(glob_base_dir, nil)]]
237229

238230
allow_dots = ((@flags & File::FNM_DOTMATCH) != 0)
239231

232+
if glob_base_dir
233+
if Primitive.is_a?(@next, DirectoriesOnly)
234+
@next.process_entry '', Truffle::DirOperations::DT_DIR, matches, parent, glob_base_dir
235+
else
236+
@next.process_entry '.', Truffle::DirOperations::DT_DIR, matches, parent, glob_base_dir if allow_dots
237+
end
238+
end
239+
240240
until stack.empty?
241241
path, dir_entries = *stack.pop
242242

@@ -245,13 +245,15 @@ def process_directory(matches, parent, entry, glob_base_dir)
245245
type = entry.type
246246

247247
full = path_join(path, ent)
248-
if (type == Truffle::DirOperations::DT_DIR) and (allow_dots or ent.getbyte(0) != 46) # ?.
249-
@next.process_entry ent, type, matches, path, glob_base_dir
250-
251-
stack << [path, dir_entries]
252-
path = full
253-
dir_entries = subdir_entries(glob_base_dir, full)
254-
elsif (allow_dots or ent.getbyte(0) != 46) # ?.
248+
if (type == Truffle::DirOperations::DT_DIR)
249+
if (allow_dots or ent.getbyte(0) != 46) # ?.
250+
@next.process_entry ent, type, matches, path, glob_base_dir
251+
252+
stack << [path, dir_entries]
253+
path = full
254+
dir_entries = subdir_entries(glob_base_dir, full)
255+
end
256+
else
255257
@next.process_entry ent, type, matches, path, glob_base_dir
256258
end
257259
end

0 commit comments

Comments
 (0)