#
# Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of Ronin.
#
# Ronin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ronin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ronin. If not, see .
#
require 'ronin/ui/output'
require 'ronin/ui/output/terminal'
require 'ronin/support/inflector'
require 'thor'
require 'thor/group'
module Ronin
module UI
module CLI
#
# The {Command} class inherits `Thor::Group` to provide a base-class
# for defining commands for the {CLI}.
#
# # Extending
#
# To create a new command one can inherit the {Command} class.
# The new command can define multiple `class_options` and `arguments`
# which `Thor::Group` will use to parse command-line arguments.
#
# require 'ronin/ui/cli/command'
#
# module Ronin
# module UI
# module CLI
# module Commands
# class MyCommand < Command
#
# desc 'My command'
#
# # command options
# class_option :stuff, :type => :boolean
# class_option :syntax, :type => :string
# class_option :includes, :type => :array
#
# # command arguments
# argument :path
#
# #
# # Executes the command.
# #
# def execute
# print_info "Stuff enabled" if options.stuff?
#
# if options[:syntax]
# print_info "Using syntax #{options[:syntax]}"
# end
#
# if options[:includes]
# print_info "Including:"
# print_array options[:includes]
# end
# end
#
# end
# end
# end
# end
# end
#
# # Running
#
# To run the command from Ruby, one can call the {run} class method
# with the options and arguments to run the command with:
#
# MyCommand.run(
# {:stuff => true, :syntax => 'bla', :includes => ['other']},
# ['some/file.txt']
# )
#
# To run the command from Ruby, with raw command-line options, one
# can call the `start` class method:
#
# MyCommand.start([
# '--stuff', 'true', '--syntax', 'bla', '--includes', 'other',
# 'some/file.txt'
# ])
#
# Note: If `MyCommand.start` is not given any arguments, it will use
# `ARGV` instead.
#
# To ensure that your command is accessible to the `ronin` command,
# make sure that the ruby file the command is defined within is in
# the `ronin/ui/cli/commands` directory of a Ronin library.
# If the command class is named 'MyCommand' it's ruby file must also
# be named 'my_command.rb'.
#
# To run the command using the `ronin` command, simply specify it's
# underscored name:
#
# ronin my_command some/file.txt --stuff --syntax bla \
# --includes one two
#
class Command < Thor::Group
include Thor::Actions
include Output::Helpers
class_option :verbose, :type => :boolean,
:default => false,
:aliases => '-v'
class_option :quiet, :type => :boolean,
:default => false,
:aliases => '-q'
class_option :silent, :type => :boolean,
:default => false,
:aliases => '-Q'
class_option :color, :type => :boolean, :default => true
#
# Sets the namespace of a new {Command} class.
#
# @param [Class] super_class
# The new {Command} class.
#
# @api private
#
def self.inherited(super_class)
super_class.namespace(super_class.command_name)
end
#
# Returns the name of the command.
#
# @api semipublic
#
def self.command_name
Support::Inflector.underscore(self.name.split('::').last)
end
#
# Runs the command.
#
# @param [Hash{String,Symbol => Object}] options
# Option values for the command.
#
# @param [Array] arguments
# Additional arguments for the command.
#
# @return [Command]
# The executed command.
#
# @since 1.0.0
#
# @api public
#
def self.run(options={},arguments=[])
command = self.new(arguments,options)
command.invoke_all()
return command
end
#
# Creates a new Command object.
#
# @param [Array] arguments
# Command-line arguments.
#
# @param [Array] opts
# Additional command-line options.
#
# @param [Hash] config
# Additional configuration.
#
# @api semipublic
#
def initialize(arguments=[],opts={},config={})
@indent = 0
super(arguments,opts,config)
setup
end
#
# Default method to call after the options have been parsed.
#
# @api semipublic
#
def execute
end
protected
#
# The banner for the command.
#
# @return [String]
# The banner string.
#
# @since 1.0.0
#
# @api private
#
def self.banner
"ronin #{self_task.formatted_usage(self,false,true)}"
end
#
# Default method to call before {#execute}.
#
# @since 1.1.0
#
# @api semipublic
#
def setup
Output.verbose! if self.options.verbose?
Output.quiet! if self.options.quiet?
Output.silent! if self.options.silent?
Output.handler = if self.options.color?
Output::Terminal::Color
else
Output::Terminal::Raw
end
end
#
# Increases the indentation out output temporarily.
#
# @param [Integer] n
# The number of spaces to increase the indentation by.
#
# @yield []
# The block will be called after the indentation has been
# increased. After the block has returned, the indentation will
# be returned to normal.
#
# @return [nil]
#
# @api semipublic
#
def indent(n=2)
@indent += n
yield
@indent -= n
return nil
end
#
# Print the given messages with indentation.
#
# @param [Array] messages
# The messages to print, one per-line.
#
# @api semipublic
#
def puts(*messages)
super(*(messages.map { |mesg| (' ' * @indent) + mesg.to_s }))
end
#
# Prints a given title.
#
# @param [String] title
# The title to print.
#
# @api semipublic
#
def print_title(title)
puts "[ #{title} ]\n"
end
#
# Prints a section with a title.
#
# @yield []
# The block will be called after the title has been printed
# and indentation increased.
#
# @since 1.0.0
#
# @api semipublic
#
def print_section(title,&block)
print_title(title)
indent(&block)
end
#
# Prints a given Array.
#
# @param [Array] array
# The Array to print.
#
# @param [Hash] options
# Additional options.
#
# @option options [String] :title
# The optional title to print before the contents of the Array.
#
# @return [nil]
#
# @api semipublic
#
def print_array(array,options={})
print_title(options[:title]) if options[:title]
indent do
array.each { |value| puts value }
end
puts if options[:title]
return nil
end
#
# Prints a given Hash.
#
# @param [Hash] hash
# The Hash to print.
#
# @param [Hash] options
# Additional options.
#
# @option options [String] :title
# The optional title to print before the contents of the Hash.
#
# @return [nil]
#
# @api semipublic
#
def print_hash(hash,options={})
align = hash.keys.map { |name|
name.to_s.length
}.max
print_title(options[:title]) if options[:title]
indent do
hash.each do |name,value|
name = "#{name}:".ljust(align)
puts "#{name}\t#{value}"
end
end
puts if options[:title]
return nil
end
#
# Prints an exception and a shortened backtrace.
#
# @param [Exception] exception
# The exception to print.
#
# @since 1.0.0
#
# @api semipublic
#
def print_exception(exception)
print_error exception.message
(0..5).each do |i|
print_error ' ' + exception.backtrace[i]
end
end
end
end
end
end