From 3edee32bb9d89ff4cf51faba029012558be4452e Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Mon, 4 Jun 2018 18:45:53 +0200 Subject: [PATCH] Add File.same?(path1, path2) with follow_symlinks option (#6161) --- spec/std/file_spec.cr | 23 +++++++++++++++++++++-- src/crystal/system/unix/file_info.cr | 2 +- src/file.cr | 6 ++++++ src/file/info.cr | 4 ++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/spec/std/file_spec.cr b/spec/std/file_spec.cr index 65de5d0c3624..650f3f44807f 100644 --- a/spec/std/file_spec.cr +++ b/spec/std/file_spec.cr @@ -230,13 +230,32 @@ describe "File" do File.link(in_path, out_path) File.exists?(out_path).should be_true File.symlink?(out_path).should be_false - File.info(in_path).should eq(File.info(out_path)) + File.same?(in_path, out_path).should be_true ensure File.delete(out_path) if File.exists?(out_path) end end end + describe "same?" do + it "compares following symlinks only if requested" do + file = "#{__DIR__}/data/test_file.txt" + symlink = "#{__DIR__}/data/test_file_symlink.txt" + other = "#{__DIR__}/data/test_file.ini" + + begin + File.symlink(file, symlink) + + File.same?(file, symlink).should be_false + File.same?(file, symlink, follow_symlinks: true).should be_true + File.same?(file, symlink, follow_symlinks: false).should be_false + File.same?(file, other).should be_false + ensure + File.delete(symlink) if File.exists?(symlink) + end + end + end + describe "symlink" do it "creates a symbolic link" do in_path = "#{__DIR__}/data/test_file.txt" @@ -244,7 +263,7 @@ describe "File" do begin File.symlink(in_path, out_path) File.symlink?(out_path).should be_true - File.info(in_path).should eq(File.info(out_path)) + File.same?(in_path, out_path, follow_symlinks: true).should be_true ensure File.delete(out_path) if File.exists?(out_path) end diff --git a/src/crystal/system/unix/file_info.cr b/src/crystal/system/unix/file_info.cr index 94052f15c99e..3b42496b96fd 100644 --- a/src/crystal/system/unix/file_info.cr +++ b/src/crystal/system/unix/file_info.cr @@ -54,7 +54,7 @@ struct Crystal::System::FileInfo < ::File::Info @stat.st_gid.to_u32 end - def ==(other : ::File::Info) : Bool + def same_file?(other : ::File::Info) : Bool @stat.st_dev == other.@stat.st_dev && @stat.st_ino == other.@stat.st_ino end end diff --git a/src/file.cr b/src/file.cr index 82f503881e84..8064a2cf9251 100644 --- a/src/file.cr +++ b/src/file.cr @@ -82,6 +82,12 @@ class File < IO::FileDescriptor Crystal::System::File.exists?(path) end + # Returns `true` if *path1* and *path2* represents the same file. + # The comparison take symlinks in consideration if *follow_symlinks* is `true`. + def self.same?(path1 : String, path2 : String, follow_symlinks = false) : Bool + info(path1, follow_symlinks).same_file? info(path2, follow_symlinks) + end + # Returns the size of *filename* bytes. Raises `Errno` if the file at *path* # does not exist. # diff --git a/src/file/info.cr b/src/file/info.cr index 0f323f8292af..0df61ab3fd05 100644 --- a/src/file/info.cr +++ b/src/file/info.cr @@ -99,11 +99,11 @@ class File # The group ID that the file belongs to. abstract def group : UInt32 - # Two `File::Info`s are equal if and only if they are of the same file. + # Returns true if this `Info` and *other* are of the same file. # # On unix, this compares device and inode fields, and will compare equal for # hard linked files. - abstract def ==(other : File::Info) + abstract def same_file?(other : File::Info) : Bool # Returns true if this `Info` represents a standard file. Shortcut for # `type.file?`.