Use ASCII_8BIT for paths and directory entries

On Unix systems the encoding of file names is specified by the user via the
locale settings.

In order to avoid encoding problems simply read file names in ASCII 8 bit
encoding and try to convert to the external encoding while replacing undefined
characters.

Also, does not show an icon if the external encoding does not support it and
shows a `=` instead of a check mark for git status.

Fixes #352.
This commit is contained in:
Claudio Bley 2020-04-15 23:04:00 +02:00
parent 7e84eded0c
commit 73e4de9378
9 changed files with 43 additions and 11 deletions

View file

@ -56,6 +56,8 @@ script:
- colorls --color=always - colorls --color=always
- colorls --tree - colorls --tree
- colorls --tree=1 - colorls --tree=1
- LC_ALL=C colorls spec/fixtures/
- LC_ALL=C colorls --git spec/fixtures/
deploy: deploy:
provider: rubygems provider: rubygems

View file

@ -47,7 +47,9 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2.4.0' spec.required_ruby_version = '>= 2.4.0'
spec.files = `git ls-files -z`.split("\x0").reject do |f| spec.files = IO.popen(
%w[git ls-files -z], external_encoding: Encoding::ASCII_8BIT
).read.split("\x0").reject do |f|
f.match(%r{^(test|spec|features)/}) f.match(%r{^(test|spec|features)/})
end end

View file

@ -72,14 +72,14 @@ module ColorLS
CHARS_PER_ITEM = 12 CHARS_PER_ITEM = 12
def item_widths def item_widths
@contents.map { |item| item.name.size + CHARS_PER_ITEM } @contents.map { |item| item.show.size + CHARS_PER_ITEM }
end end
def init_contents(path) def init_contents(path)
info = FileInfo.new(path, link_info = @long) info = FileInfo.new(path, link_info = @long)
if info.directory? if info.directory?
@contents = Dir.entries(path) @contents = Dir.entries(path, encoding: Encoding::ASCII_8BIT)
filter_hidden_contents filter_hidden_contents
@ -239,7 +239,11 @@ module ColorLS
end end
def git_file_info(path) def git_file_info(path)
return ' ✓ '.colorize(@colors[:unchanged]) unless @git_status[path] unless @git_status[path]
return ' ✓ '
.encode(Encoding.default_external, undef: :replace, replace: '=')
.colorize(@colors[:unchanged])
end
Git.colored_status_symbols(@git_status[path], @colors) Git.colored_status_symbols(@git_status[path], @colors)
end end
@ -283,10 +287,10 @@ module ColorLS
@count[increment] += 1 @count[increment] += 1
value = increment == :folders ? @folders[key] : @files[key] value = increment == :folders ? @folders[key] : @files[key]
logo = value.gsub(/\\u[\da-f]{4}/i) { |m| [m[-4..-1].to_i(16)].pack('U') } logo = value.gsub(/\\u[\da-f]{4}/i) { |m| [m[-4..-1].to_i(16)].pack('U') }
name = content.name name = content.show
name = make_link(path, name) if @hyperlink name = make_link(path, name) if @hyperlink
name += content.directory? ? '/' : ' ' name += content.directory? ? '/' : ' '
entry = logo + ' ' + name entry = logo.encode(Encoding.default_external, undef: :replace, replace: '') + ' ' + name
"#{long_info(content)} #{git_info(content)} #{entry.colorize(color)}#{symlink_info(content)}" "#{long_info(content)} #{git_info(content)} #{entry.colorize(color)}#{symlink_info(content)}"
end end
@ -295,9 +299,10 @@ module ColorLS
padding = 0 padding = 0
line = +'' line = +''
chunk.each_with_index do |content, i| chunk.each_with_index do |content, i|
entry = fetch_string(@input, content, *options(content))
line << ' ' * padding line << ' ' * padding
line << ' ' << fetch_string(@input, content, *options(content)) line << ' ' << entry.encode(Encoding.default_external, undef: :replace)
padding = widths[i] - content.name.length - CHARS_PER_ITEM padding = widths[i] - content.show.length - CHARS_PER_ITEM
end end
print line << "\n" print line << "\n"
end end

View file

@ -16,6 +16,8 @@ module ColorLS
@name = File.basename(path) @name = File.basename(path)
@stats = File.lstat(path) @stats = File.lstat(path)
@show_name = nil
handle_symlink(path) if link_info && @stats.symlink? handle_symlink(path) if link_info && @stats.symlink?
end end
@ -23,6 +25,13 @@ module ColorLS
FileInfo.new(path) FileInfo.new(path)
end end
def show
return @show_name unless @show_name.nil?
@show_name = @name.encode(Encoding.find('filesystem'), Encoding.default_external,
invalid: :replace, undef: :replace)
end
def dead? def dead?
@dead @dead
end end

View file

@ -31,7 +31,11 @@ module ColorLS
end end
def self.colored_status_symbols(modes, colors) def self.colored_status_symbols(modes, colors)
return ' ✓ '.colorize(colors[:unchanged]) if modes.empty? if modes.empty?
return ' ✓ '
.encode(Encoding.default_external, undef: :replace, replace: '=')
.colorize(colors[:unchanged])
end
modes = modes.to_a.join.uniq.rjust(3).ljust(4) modes = modes.to_a.join.uniq.rjust(3).ljust(4)
@ -51,7 +55,10 @@ module ColorLS
end end
def git_subdir_status(repo_path) def git_subdir_status(repo_path)
yield IO.popen(['git', '-C', repo_path, 'status', '--porcelain', '-z', '-unormal', '--ignored', '.']) yield IO.popen(
['git', '-C', repo_path, 'status', '--porcelain', '-z', '-unormal', '--ignored', '.'],
external_encoding: Encoding::ASCII_8BIT
)
end end
end end
end end

View file

@ -20,7 +20,7 @@ module ColorLS
end end
def read_file(filepath) def read_file(filepath)
::YAML.safe_load(File.read(filepath)).symbolize_keys ::YAML.safe_load(File.read(filepath, encoding: Encoding::UTF_8)).symbolize_keys
end end
end end
end end

View file

@ -68,6 +68,7 @@ RSpec.describe ColorLS::Flags do
:directory? => false, :directory? => false,
:owner => "user", :owner => "user",
:name => "a.txt", :name => "a.txt",
:show => "a.txt",
:nlink => 1, :nlink => 1,
:size => 128, :size => 128,
:blockdev? => false, :blockdev? => false,
@ -95,6 +96,7 @@ RSpec.describe ColorLS::Flags do
:directory? => false, :directory? => false,
:owner => "user", :owner => "user",
:name => "a.txt", :name => "a.txt",
:show => "a.txt",
:nlink => 5, # number of hardlinks :nlink => 5, # number of hardlinks
:size => 128, :size => 128,
:blockdev? => false, :blockdev? => false,

View file

@ -1,6 +1,11 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ColorLS::Git do RSpec.describe ColorLS::Git do
before(:all) do
`echo` # initialize $CHILD_STATUS
expect($CHILD_STATUS).to be_success
end
def git_status(*entries) def git_status(*entries)
StringIO.new entries.map { |line| line + "\x0" }.join StringIO.new entries.map { |line| line + "\x0" }.join
end end

0
spec/fixtures/Glück vendored Normal file
View file