class Sixword::CLI
The Sixword::CLI
class implements all of the complex processing needed for the sixword Command Line Interface.
Attributes
@return [String] Input filename
@return [:encode, :decode]
@return [Hash] Options hash
@return [File, IO] Stream opened from filename
Public Class Methods
Create a Sixword
CLI
to operate on filename with options
@param filename [String] Input file name (or '-' for stdin) @param options [Hash]
@option options [:encode, :decode] :mode (:encode) @option options [Boolean] :pad (false) @option options [String] :hex_style @option options [Integer] :line_width (1) In encode mode, the number of
sentences to output per line.
# File lib/sixword/cli.rb, line 33 def initialize(filename, options) @filename = filename @options = {mode: :encode, pad: false}.merge(options) if filename == '-' # dup stdin and put it into binary mode @stream = $stdin.dup @stream.binmode else # read in binary mode even on unix so ruby yields binary encoding @stream = File.open(filename, 'rb') end @mode = @options.fetch(:mode) unless [:encode, :decode].include?(mode) raise ArgumentError.new("Invalid mode: #{mode.inspect}") end end
Public Instance Methods
Return true if we are in encoding mode, false otherwise (decoding). @return [Boolean]
# File lib/sixword/cli.rb, line 61 def encoding? mode == :encode end
Return the value of the :hex_style option. @return [String, nil]
# File lib/sixword/cli.rb, line 67 def hex_style options[:hex_style] end
Return the value of the :pad option. @return [Boolean]
# File lib/sixword/cli.rb, line 55 def pad? options.fetch(:pad) end
Format data as hex in various styles.
# File lib/sixword/cli.rb, line 72 def print_hex(data, chunk_index, cols=80) case hex_style when 'lower', 'lowercase' # encode to lowercase hex with no newlines print Sixword::Hex.encode(data) when 'finger', 'fingerprint' # encode to GPG fingerprint like hex with newlines newlines_every = cols / 5 if chunk_index != 0 if chunk_index % newlines_every == 0 print "\n" else print ' ' end end print Sixword::Hex.encode_fingerprint(data) when 'colon', 'colons' # encode to SSL/SSH fingerprint like hex with colons print ':' unless chunk_index == 0 print Sixword::Hex.encode_colons(data) end end
Run the encoding/decoding operation, printing the result to stdout.
# File lib/sixword/cli.rb, line 96 def run! if encoding? do_encode! do |encoded| puts encoded end else chunk_index = 0 do_decode! do |decoded| if hex_style print_hex(decoded, chunk_index) chunk_index += 1 else print decoded end end # add trailing newline for hex output puts if hex_style end end
Private Instance Methods
# File lib/sixword/cli.rb, line 213 def accumulate_hex_input unless block_given? raise ArgumentError.new("must pass block") end # these are actually treated the same at the moment case hex_style when 'lower', 'lowercase' when 'finger', 'fingerprint' else raise CLIError.new("unknown hex style: #{hex_style.inspect}") end while true buf = '' # try to accumulate 8 bytes (16 chars) before yielding the hex while buf.length < 16 char = stream.getc if char.nil? # EOF, so yield whatever we have if it's non-empty yield buf unless buf.empty? return end if Sixword::Hex.valid_hex?(char) buf << char next elsif Sixword::Hex.strip_char?(char) next end raise CLIError.new("invalid hex character: #{char.inspect}") end yield buf end end
# File lib/sixword/cli.rb, line 119 def do_decode! unless block_given? raise ArgumentError.new("block is required") end read_input_by_6_words do |arr| yield Sixword.decode(arr, padding_ok: pad?) end end
# File lib/sixword/cli.rb, line 129 def do_encode! sentences_per_line = options.fetch(:line_width, 1) if sentences_per_line <= 0 sentences_per_line = 1 << 32 end sentences = [] process_encode_input do |chunk| Sixword.encode_iter(chunk, words_per_slice:6, pad:pad?) do |encoded| sentences << encoded # yield sentences once we reach sentences_per_line of them if sentences.length >= sentences_per_line yield sentences.join(' ') sentences.clear end end end # yield any leftover sentences unless sentences.empty? yield sentences.join(' ') end end
# File lib/sixword/cli.rb, line 155 def process_encode_input unless block_given? raise ArgumentError.new("block is required") end if hex_style # yield data in chunks from accumulate_hex_input until EOF accumulate_hex_input do |hex| begin data = Sixword::Hex.decode(hex) rescue ArgumentError => err # expose hex decoding failures to user raise CLIError.new(err.message) else yield data end end else # yield data 8 bytes at a time until EOF while true buf = stream.read(8) if buf yield buf else # EOF break end end end end
Yield data 6 words at a time until EOF
# File lib/sixword/cli.rb, line 187 def read_input_by_6_words word_arr = [] while true line = stream.gets if line.nil? break # EOF end line.scan(/\S+/) do |word| word_arr << word # return the array if we have accumulated 6 words if word_arr.length == 6 yield word_arr word_arr.clear end end end # yield whatever we have left, if anything if !word_arr.empty? yield word_arr end end