#!/usr/bin/env ruby require 'tins' include Tins include Tins::GO class CompoundRange def initialize @ranges = [] end def concat(range) @ranges << range self end def each(&block) @ranges.each { |r| r.each(&block) } end end class Step def initialize(a, b, step = 1, exclusive = false) @a = a @b = b @step = step if exclusive if step < 0 @b += 1 else @b -= 1 end end end def each(&block) @a.step(@b, @step, &block) end end class StringRange def initialize(a, b, exclusive = false) @range = if a > b if exclusive (b.succ..a).to_a.reverse else (b..a).to_a.reverse end else Range.new(a, b, exclusive) end end def each(&block) @range.each(&block) end end class IntegerRange < Step def initialize(a, b, exclusive = false) @a = a @b = b super(a, b, a > b ? -1 : 1, exclusive) end end def parse_range(range) case range when /,/ range.split(/,/).inject(CompoundRange.new) do |a,x| a.concat(parse_range(x)) end when /\A(-?\d+)\Z/ ($1.to_i)..($1.to_i) when /\A(\w+)\Z/ $1..$1 when /\A(-?\d+)..(-?\d+)\Z/ IntegerRange.new($1.to_i, $2.to_i) when /\A(-?\d+)...(-?\d+)\Z/ IntegerRange.new($1.to_i, $2.to_i, true) when /\A(-?\d+)..(-?\d+)([+-]\d+)\Z/ Step.new($1.to_i, $2.to_i, $3.to_i) when /\A(-?\d+)...(-?\d+)([+-]\d+)\Z/ Step.new($1.to_i, $2.to_i, $3.to_i, true) when /\A(\w+)..(\w+)\Z/ StringRange.new($1, $2) when /\A(\w+)...(\w+)\Z/ StringRange.new($1, $2, true) else fail "parsing range failed due to '#{range}'" end end def parse_ranges(pattern) pattern.split(/:/).map { |range| parse_range(range) } end def execute_command(command, format, tuple) formatted = (format % tuple).split(/:/) if $limited $limited.execute do system sprintf(command, *formatted) end else system sprintf(command, *formatted) end end def usage puts <