第33回 Ruby/Rails 勉強会 - 演習 2
演習 2
- cat コマンドを実装してください。
- see also cat(1)
Mac は BSD cat なので、オプションはこれだけ。
でも usage の表示だけ GNU cat からパクってきた。
#! /usr/local/bin/ruby19 require 'optparse' Encoding.default_internal = Encoding.find('ASCII-8BIT') Encoding.default_external = Encoding.find('ASCII-8BIT') $NUMBER_WIDTH = 6 $C_OFFSET = "\C-@".ord - '@'.ord $M_OFFSET = "\M-@".ord - '@'.ord $M_C_OFFSET = "\M-\C-@".ord - '@'.ord opt = { show: { ends: false, tabs: false, nonprinting: false, }, number: false, squeeze: false, help: false, } parser = OptionParser.new parser.on('-b', '--number-nonblank', 'number nonempty output lines') do opt[:number] = :nonblank end parser.on('-e', '--show-ends', 'display $ at end of each line') do opt[:show][:ends] = true opt[:show][:nonprinting] = true end parser.on('-n', '--number', 'number all output lines') do opt[:number] = :all end parser.on('-s', '--squeeze-blank', 'suppress repeated empty output lines') do opt[:squeeze] = true end parser.on('-t', '--show-tabs', 'display TAB characters as ^I') do opt[:show][:tabs] = true opt[:show][:nonprinting] = true end parser.on('-v', '--show-nonprinting', 'use ^ and M- notation, except for LFD and TAB') do opt[:show][:nonprinting] = true end parser.on('-h', '--help', 'display this help and exit') do opt[:help] = true STDERR.puts parser.help exit 0 end parser.banner = <<USAGE usage: #{File.basename $0} [options] [file ...] options: USAGE def squeeze_blank!(buf) buf.gsub!(/(\r?\n)\1+/, '\1\1') end def count_lines_and_print(opt, n, line) return n unless opt if opt == :all || opt == :nonblank && /^\r?$/ !~ line n += 1 printf("%*d ", $NUMBER_WIDTH, n) end n end def show_ends_hash {"\n" => '$'} end def show_tabs_hash {"\t" => '^I'} end def to_printable_array(codes, offset) codes.map{|c| [(c.ord+offset).chr, yield(c)]} end def show_nonprinting_hash c = to_printable_array('@'..'_', $C_OFFSET){|c| "^#{c}"} m = to_printable_array('@'..'_', $M_OFFSET){|c| "M-#{c}"} mc = to_printable_array('@'..'_', $M_C_OFFSET){|c| "M-^#{c}"} nltab = to_printable_array(%W[I J], $C_OFFSET){|c| "^#{c}"} Hash[*(c + m + mc - nltab).flatten] end def to_printable!(convertor, line) convertor.each do |src, dst| line.gsub!(src, dst) end line end def cat(opt, buf) lineno = 0 squeeze_blank!(buf) if opt[:squeeze] conv = [] conv = conv | show_ends_hash.to_a if opt[:show][:ends] conv = conv | show_tabs_hash.to_a if opt[:show][:tabs] conv = conv | show_nonprinting_hash.to_a if opt[:show][:nonprinting] buf.each_line do |line| lineno = count_lines_and_print(opt[:number], lineno, line) if opt[:number] puts to_printable!(Hash[*conv.flatten], line) end end begin parser.parse! ARGV rescue OptionParser::ParseError => e STDERR.puts "#{File.basename $0}: #{e.to_s}" STDERR.puts parser.help exit 1 end cat(opt, ARGF.binmode.read)