Skip to content

Commit

Permalink
Handle File.extname edge case (#6234)
Browse files Browse the repository at this point in the history
* Fix #6215. Handle File.extname edge case

* Fix typo

* Use byte_slice

* Remove local variable

* On less return in the world

* Iterate bytes

* Empty case
  • Loading branch information
bcardiff authored and RX14 committed Jun 25, 2018
1 parent 9228889 commit 9d99700
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
4 changes: 4 additions & 0 deletions spec/std/file_spec.cr
Expand Up @@ -297,18 +297,22 @@ describe "File" do
end

it "gets extname" do
File.extname("/foo/bar/a.cr").should eq(".cr")
File.extname("/foo/bar/baz.cr").should eq(".cr")
File.extname("/foo/bar/baz.cr.cz").should eq(".cz")
File.extname("/foo/bar/.profile").should eq("")
File.extname("/foo/bar/.profile.sh").should eq(".sh")
File.extname("/foo/bar/foo.").should eq("")
File.extname("/foo.bar/baz").should eq("")
File.extname("a.cr").should eq(".cr")
File.extname("test.cr").should eq(".cr")
File.extname("test.cr.cz").should eq(".cz")
File.extname(".test").should eq("")
File.extname(".test.cr").should eq(".cr")
File.extname(".test.cr.cz").should eq(".cz")
File.extname("test").should eq("")
File.extname("test.").should eq("")
File.extname("").should eq("")
end

it "constructs a path from parts" do
Expand Down
37 changes: 32 additions & 5 deletions src/file.cr
Expand Up @@ -282,13 +282,40 @@ class File < IO::FileDescriptor
def self.extname(filename) : String
filename.check_no_null_byte

dot_index = filename.rindex('.')
bytes = filename.to_slice

if dot_index && dot_index != filename.size - 1 && dot_index - 1 > (filename.rindex(SEPARATOR) || 0)
filename[dot_index, filename.size - dot_index]
else
""
return "" if bytes.empty?

current = bytes.size - 1

# if the pattern is foo. it has no extension
return "" if bytes[current] == '.'.ord

# position the reader at the last . or SEPARATOR
# that is not the first char
while bytes[current] != SEPARATOR.ord &&
bytes[current] != '.'.ord &&
current > 0
current -= 1
end

# if we are the beginning of the string there is no extension
# /foo or .foo have no extension
return "" unless current > 0

# otherwise we are not at the beginning, and there is a previous char.
# if current is '/', then the pattern is prefix/foo and has no extension
return "" if bytes[current] == SEPARATOR.ord

# otherwise the current_char is '.'
# if previous is '/', then the pattern is prefix/.foo and has no extension
return "" if bytes[current - 1] == SEPARATOR.ord

# So the current char is '.',
# we are not at the beginning,
# the previous char is not a '/',
# and we have an extension
String.new(bytes[current, bytes.size - current])
end

# Converts *path* to an absolute path. Relative paths are
Expand Down

0 comments on commit 9d99700

Please sign in to comment.