diff --git a/Library/Homebrew/extend/os/linux/keg_relocate.rb b/Library/Homebrew/extend/os/linux/keg_relocate.rb index 0dccc1e804..e36e28d310 100644 --- a/Library/Homebrew/extend/os/linux/keg_relocate.rb +++ b/Library/Homebrew/extend/os/linux/keg_relocate.rb @@ -10,6 +10,15 @@ module OS requires_ancestor { ::Keg } + module ClassMethods + # GNU `strings` splits runs at non-ASCII bytes, such as those in a + # prefix like `/Users/José`, unless asked to keep 8-bit bytes. + sig { returns(T::Array[String]) } + def strings_encoding_args + ["-e", "S"] + end + end + sig { params(relocation: ::Keg::Relocation, with_placeholders: T::Boolean, files: T.nilable(T::Array[::Pathname])).returns(T::Array[::Pathname]) @@ -126,4 +135,5 @@ module OS end end +Keg.singleton_class.prepend(OS::Linux::Keg::ClassMethods) Keg.prepend(OS::Linux::Keg) diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb index 40c672176d..34fee7fe70 100644 --- a/Library/Homebrew/keg_relocate.rb +++ b/Library/Homebrew/keg_relocate.rb @@ -614,7 +614,8 @@ class Keg # Some binaries contain strings with lists of files # e.g. `/usr/local/lib/foo:/usr/local/share/foo:/usr/lib/foo` # Each item in the list should be checked separately - match.split(":").each do |sub_match| + # A high byte just before a path can be reported as part of the same run. + match.split(":").map { it.b.sub(%r{\A[\x80-\xFF]+(?=/)}n, "") }.each do |sub_match| # Not all items in the list may be matches next unless sub_match.match? path_regex next if linked_libraries.include? sub_match # Don't bother reporting a string if it was found by otool @@ -648,7 +649,7 @@ class Keg return end - Utils.popen_read("strings", "-t", "x", "-", file.to_s) do |io| + Utils.popen_read("strings", *strings_encoding_args, "-t", "x", "-", file.to_s) do |io| until io.eof? line = io.readline.chomp offset, match = line.split(" ", 2) @@ -823,6 +824,11 @@ class Keg [] end + sig { returns(T::Array[String]) } + def self.strings_encoding_args + [] + end + private sig { returns(T::Boolean) } diff --git a/Library/Homebrew/test/keg_relocate/text_spec.rb b/Library/Homebrew/test/keg_relocate/text_spec.rb index 3c26ee62a4..f1d850f59f 100644 --- a/Library/Homebrew/test/keg_relocate/text_spec.rb +++ b/Library/Homebrew/test/keg_relocate/text_spec.rb @@ -48,6 +48,22 @@ RSpec.describe Keg do expect(result.count).to eq 2 end + specify "::text_matches_in_file finds a path preceded by a non-ASCII byte" do + ENV["LC_ALL"] = "en_US.UTF-8" + file.binwrite("\0\xD6#{dir}/file.txt\0".b) + + expect(described_class.text_matches_in_file(file, dir.to_s, [], [], nil).count).to eq 1 + end + + specify "::text_matches_in_file finds a path containing non-ASCII characters", + unless: ENV.fetch("HOMEBREW_TEST_GENERIC_OS", nil) do + ENV["LC_ALL"] = "en_US.UTF-8" + (dir/"José").mkpath + file.binwrite("\0#{dir}/José\0".b) + + expect(described_class.text_matches_in_file(file, dir.to_s, [], [], nil).count).to eq 1 + end + specify "::each_candidate_string still yields runs that are not valid UTF-8" do setup_file