Class: Rex::Parser::Arguments

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/parser/arguments.rb

Overview

This class parses arguments in a getopt style format, kind of. Unfortunately, the default ruby getopt implementation will only work on ARGV, so we can't use it.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fmt) ⇒ Arguments

Initializes the format list with an array of formats like:

Arguments.new(

'-b'                => [ false, "some text"                 ],
['-b']              => [ false, "some text"                 ],
['-x', '--execute'] => [ true, "mixing long and short args" ],
['-t', '--test']    => [ true, "testing custom <opt> value", "<arg_to_test>" ],
['--long-flag']     => [ false, "sample long flag" ]

)



33
34
35
36
37
# File 'lib/rex/parser/arguments.rb', line 33

def initialize(fmt)
  normalised_fmt = fmt.map { |key, | [Array(key), ] }.to_h
  self.fmt = normalised_fmt
  self.longest = normalised_fmt.each_pair.map { |key, value| key.flatten.join(', ') + (value[0] ? ' ' + value[2].to_s : '') }.max_by(&:length)
end

Class Method Details

.from_s(str) ⇒ Object

Takes a string and converts it into an array of arguments.



42
43
44
# File 'lib/rex/parser/arguments.rb', line 42

def self.from_s(str)
  Shellwords.shellwords(str)
end

Instance Method Details

#arg_required?(opt) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
# File 'lib/rex/parser/arguments.rb', line 122

def arg_required?(opt)
  value = select_value_from_fmt_option(opt)
  return false if value.nil?

  value.first
end

#include?(search) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/rex/parser/arguments.rb', line 118

def include?(search)
  fmt.keys.flatten.include?(search)
end

#merge(to_merge) ⇒ Object

Return new Parser object featuring options from the base object and including the options hash that was passed in



134
135
136
137
138
# File 'lib/rex/parser/arguments.rb', line 134

def merge(to_merge)
  return fmt unless to_merge.is_a?(Hash)

  Rex::Parser::Arguments.new(fmt.clone.merge(to_merge))
end

#option_keysObject



129
130
131
# File 'lib/rex/parser/arguments.rb', line 129

def option_keys
  fmt.keys.flatten
end

#parse(args, &_block) ⇒ Object

Parses the supplied arguments into a set of options.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rex/parser/arguments.rb', line 49

def parse(args, &_block)
  skip_next = 0

  args.each_with_index do |arg, idx|
    if skip_next > 0
      skip_next -= 1
      next
    end

    param = nil
    if arg =~ SHORT_FLAG
      # parsing needs to take into account a couple requirements
      #  1. longest `short` flag found in 'arg' should be extracted first
      #    * consider passing the short flag to a tokenizer that returns a list of tokens in order with any invalid tokens
      #  2. any short flag arguments that need an option will consume the next option from the list
      short_args_from_token(arg).each do |letter|
        next unless include?("-#{letter}")

        if arg_required?("-#{letter}")
          skip_next += 1
          param = args[idx + skip_next]
        end

        yield "-#{letter}", idx, param
      end
    elsif arg =~ LONG_FLAG && include?(arg)
      if arg_required?(arg)
        skip_next = 1
        param = args[idx + skip_next]
      end

      # Try to yield the short hand version of our argument if possible
      # This will result in less areas of code that would need to be changed
      to_return = short_arg_from_long_arg(arg)
      if to_return.nil?
        yield arg, idx, param
      else
        yield to_return, idx, param
      end
    else
      # else treat the passed in flag as argument
      yield nil, idx, arg
    end
  end
end

#usageObject

Returns usage information for this parsing context.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/rex/parser/arguments.rb', line 98

def usage
  txt = ["\nOPTIONS:\n"]

  fmt.sort_by { |key, | key.to_s.downcase }.each do |key, val|
    # if the arg takes in a parameter, get parameter string
    opt = val[0] ? " #{val[2]}" : ''

    # Get all arguments for a command
    output = key.join(', ')
    output += opt

    # Left align the fmt options and <opt> string
    aligned_option = "    #{output.ljust(longest.length)}"
    txt << "#{aligned_option}  #{val[1]}"
  end

  txt << ""
  txt.join("\n")
end