diff --git a/04.ls/ls.rb b/04.ls/ls.rb index 6b1cfd48e4..089cf6517e 100755 --- a/04.ls/ls.rb +++ b/04.ls/ls.rb @@ -1,10 +1,38 @@ #!/usr/bin/env ruby # frozen_string_literal: true +require 'optparse' +require 'etc' + MAX_CHUNK = 3 TAB_SPACE = 8 -file_paths = Dir.entries('.') +FILE_TYPE = { + 'fifo' => 'p', + 'characterSpecial' => 'c', + 'directory' => 'd', + 'blockSpecial' => 'b', + 'file' => '-', + 'link' => 'l', + 'socket' => 's' +}.freeze + +FILE_MODE = { + '0' => '---', + '1' => '--x', + '2' => '-w-', + '3' => '-wx', + '4' => 'r--', + '5' => 'r-x', + '6' => 'rw-', + '7' => 'rwx' +}.freeze + +SPECIAL_MODE = [ + { bit: 4, index: 2, exec: 's', no_exec: 'S' }, + { bit: 2, index: 5, exec: 's', no_exec: 'S' }, + { bit: 1, index: 8, exec: 't', no_exec: 'T' } +].freeze def except_hidden_file(file_paths) file_paths.reject { |element| element[0] == '.' } @@ -21,20 +49,95 @@ def transpose_chunks(sliced_paths, num_rows) padded_slices.transpose.map(&:compact) end -file_paths = file_paths.sort -file_paths = except_hidden_file(file_paths) +def convert_file_mode(file_stat) + file_mode_octal = file_stat.mode.to_s(8).rjust(6, '0') + file_mode = FILE_TYPE[file_stat.ftype].to_s + file_permissions = file_mode_octal.slice(3..-1).chars.map { |char| FILE_MODE[char] }.join + # 特殊権限の確認 特殊権限は8進数に変換されたファイルモードの3文字目に記載されている そのためfile_mode_octal[2]を参照する + file_permissions = apply_special_modes(file_mode_octal[2].to_i, file_permissions) + file_mode + file_permissions +end + +def apply_special_modes(special_modes, permissions) + # 0は特殊権限なし、なので最初に弾く(特殊権限は1か2か4であるため) + return permissions if special_modes.zero? -num_rows = (file_paths.size.to_f / MAX_CHUNK).ceil -sliced_paths = chunk_file_paths(file_paths, num_rows) -shaped_file_paths_array = transpose_chunks(sliced_paths, num_rows) + SPECIAL_MODE.each do |mode| + next unless (special_modes & mode[:bit]).positive? + + permissions[mode[:index]] = if permissions[mode[:index]] == 'x' + mode[:exec] + else + mode[:no_exec] + end + end + permissions +end + +def format_file_mode(file_path) + file_stat = File.lstat(file_path) + { + file_mode: convert_file_mode(file_stat), + nlink: file_stat.nlink.to_s, + uid: Etc.getpwuid(file_stat.uid).name, + gid: Etc.getgrgid(file_stat.gid).name, + size: file_stat.size.to_s, + mtime: file_stat.mtime.strftime('%-m %d %H:%M'), + file_path:, + blocks: file_stat.blocks + } +end + +def calclate_max_string_modes(file_modes) + { + nlink: file_modes.map { |file_mode| file_mode[:nlink].size }.max, + uid: file_modes.map { |file_mode| file_mode[:uid].size }.max, + gid: file_modes.map { |file_mode| file_mode[:gid].size }.max, + size: file_modes.map { |file_mode| file_mode[:size].size }.max, + mtime: file_modes.map { |file_mode| file_mode[:mtime].size }.max + } +end + +file_paths = Dir.entries('.').sort + +opt = OptionParser.new +options = opt.getopts(ARGV, 'arl') + +file_paths = file_paths.reverse if options['r'] +file_paths = except_hidden_file(file_paths) unless options['a'] + +if options['l'] + file_modes = file_paths.map { |file_path| format_file_mode(file_path) } + total_blocks = file_modes.map { |file_mode| file_mode[:blocks] }.sum + + puts "total #{total_blocks}" + + max_string_modes = calclate_max_string_modes(file_modes) + file_modes.each do |file_mode| + file_mode_print = [ + file_mode[:file_mode], + file_mode[:nlink].rjust(max_string_modes[:nlink]), + file_mode[:uid].ljust(max_string_modes[:uid]), + file_mode[:gid].rjust(max_string_modes[:gid] + 1), + file_mode[:size].rjust(max_string_modes[:size] + 1), + file_mode[:mtime].rjust(max_string_modes[:mtime] + 1), + file_mode[:file_path] + ].join(' ') + puts file_mode_print + end +else + num_rows = (file_paths.size.to_f / MAX_CHUNK).ceil + sliced_paths = chunk_file_paths(file_paths, num_rows) + shaped_file_paths_array = transpose_chunks(sliced_paths, num_rows) -max_string_length = file_paths.map(&:length).max + max_string_length = file_paths.map(&:length).max -shaped_file_paths_array.each do |array_element| - array_element.each do |element| - tab_count = ((max_string_length - element.length) / TAB_SPACE.to_f).ceil + 1 # タブの挿入する回数を計算 - print element - print "\t" * tab_count + shaped_file_paths_array.each do |array_element| + array_element.each do |element| + tab_count = ((max_string_length - element.length) / TAB_SPACE.to_f).ceil + 1 # タブの挿入する回数を計算 + print element + print "\t" * tab_count + end + puts end - puts end