Skip to content

Commit e02a803

Browse files
committed
Handle escaped characters in glob patterns.
1 parent 4c21f35 commit e02a803

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

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

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131

3232
class Dir
3333
module Glob
34-
no_meta_chars = '[^*?\\[\\]{}\\\\]'
34+
no_meta_chars = /[^*?\[\]{}\\]/
35+
no_meta_chars_unescaped = /(?:#{no_meta_chars}|\\\\|\\\*|\\\?|\\\[|\\\]|\\\{|\\\})/
3536
NO_GLOB_META_CHARS = /\A#{no_meta_chars}+\z/
36-
TRAILING_BRACES = /\A(#{no_meta_chars}+)(?:\{(#{no_meta_chars}*)\})?\z/
37+
NO_GLOB_META_CHARS_UNESCAPED = /\A#{no_meta_chars_unescaped}+\z/
38+
TRAILING_BRACES = /\A(#{no_meta_chars_unescaped}+)(?:\{(#{no_meta_chars_unescaped}*)\})?\z/
3739

3840
class Node
3941
def initialize(nxt, flags)
40-
@flags = flags
42+
@flags = flags | File::FNM_EXTGLOB
4143
@next = nxt
4244
@separator = nil
4345
end
@@ -349,6 +351,10 @@ def process_entry(entry, entry_type, matches, parent, glob_base_dir)
349351
end
350352
end
351353

354+
def self.unescape(pattern)
355+
pattern.gsub(/\\(.)/, '\\1')
356+
end
357+
352358
def self.path_split(str)
353359
start = 0
354360
ret = []
@@ -383,7 +389,10 @@ def self.path_split(str)
383389
end
384390

385391
def self.single_compile(glob, flags=0)
386-
if glob.getbyte(-1) != 47 && NO_GLOB_META_CHARS.match?(glob) # byte value 47 = ?/
392+
escape = (flags & File::FNM_NOESCAPE) == 0
393+
if escape && glob.getbyte(-1) != 47 && NO_GLOB_META_CHARS_UNESCAPED.match?(glob) # byte value 47 = ?/
394+
return ConstantEntry.new nil, flags, unescape(glob)
395+
elsif !escape && glob.getbyte(-1) != 47 && NO_GLOB_META_CHARS.match?(glob) # byte value 47 = ?/
387396
return ConstantEntry.new nil, flags, glob
388397
end
389398

@@ -393,8 +402,10 @@ def self.single_compile(glob, flags=0)
393402
last = DirectoriesOnly.new nil, flags
394403
else
395404
file = parts.pop
396-
if NO_GLOB_META_CHARS.match?(file)
405+
if !escape && NO_GLOB_META_CHARS.match?(file)
397406
last = ConstantEntry.new nil, flags, file
407+
elsif escape && NO_GLOB_META_CHARS_UNESCAPED.match?(file)
408+
last = ConstantEntry.new nil, flags, unescape(file)
398409
elsif file == '*'
399410
last = AllNameEntryMatch.new nil, flags, file
400411
elsif file && file[0] == '*' && NO_GLOB_META_CHARS.match?(file[1..])
@@ -414,14 +425,25 @@ def self.single_compile(glob, flags=0)
414425
else
415426
last = RecursiveDirectories.new last, flags
416427
end
417-
elsif NO_GLOB_META_CHARS.match?(dir)
428+
elsif !escape && NO_GLOB_META_CHARS.match?(dir)
418429
while NO_GLOB_META_CHARS.match?(parts[-2])
419430
next_sep = parts.pop
420431
next_sect = parts.pop
421432

422433
dir = next_sect << next_sep << dir
423434
end
424435

436+
last = ConstantDirectory.new last, flags, dir
437+
elsif escape && NO_GLOB_META_CHARS_UNESCAPED.match?(dir)
438+
439+
dir = unescape(dir)
440+
while NO_GLOB_META_CHARS_UNESCAPED.match?(parts[-2])
441+
next_sep = parts.pop
442+
next_sect = unescape(parts.pop)
443+
444+
dir = next_sect << next_sep << dir
445+
end
446+
425447
last = ConstantDirectory.new last, flags, dir
426448
elsif !dir.empty?
427449
last = DirectoryMatch.new last, flags, dir
@@ -466,9 +488,10 @@ def self.glob(base_dir, pattern, flags, matches)
466488
# only as a suffix.
467489

468490
if braces = m[2]
469-
stem = m[1]
491+
stem = unescape(m[1])
470492

471493
braces.split(',').each do |s|
494+
s = unescape(s)
472495
path = "#{stem}#{s}"
473496
if Truffle::FileOperations.exist? path_join(base_dir, path)
474497
matches << path
@@ -480,6 +503,7 @@ def self.glob(base_dir, pattern, flags, matches)
480503
matches << stem if Truffle::FileOperations.exist? path_join(base_dir, stem)
481504
end
482505
else
506+
pattern = unescape(pattern)
483507
matches << pattern if Truffle::FileOperations.exist?(path_join(base_dir, pattern))
484508
end
485509

0 commit comments

Comments
 (0)