This file is indexed.

/usr/lib/ruby/vendor_ruby/octocatalog-diff/cli/options.rb is in octocatalog-diff 1.5.3-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 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
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# frozen_string_literal: true

require_relative '../cli'
require_relative '../facts'
require_relative '../version'

require 'optparse'

module OctocatalogDiff
  class Cli
    # This class contains the option parser. 'parse_options' is the external entry point.
    class Options
      # The usage banner.
      BANNER = 'Usage: catalog-diff -n <hostname> [-f <from environment>] [-t <to environment>]'.freeze

      # An error class specifically for passing information to the document build task.
      class DocBuildError < RuntimeError; end

      # List of classes
      def self.classes
        @classes ||= []
      end

      # Define the Option class and newoption() method for use by cli/options/*.rb files
      class Option
        def self.has_weight(w) # rubocop:disable Style/PredicateName
          @weight = w
        end

        def self.order_within_weight(w) # rubocop:disable Style/TrivialAccessors
          @order_within_weight = w
        end

        def self.weight
          if @weight && @order_within_weight
            @weight + (@order_within_weight / 100.0)
          elsif @weight
            @weight
          else
            # :nocov:
            raise ArgumentError, "Option #{name} does not have a weight specified. Add 'has_weight NNN' to control ordering."
            # :nocov:
          end
        end

        def self.name
          self::NAME
        end

        def self.newoption(name, &block)
          klass = Class.new(OctocatalogDiff::Cli::Options::Option)
          klass.const_set('NAME', name)
          klass.class_exec(&block)
          Options.classes.push(klass)
        end
      end

      # Method to call all of the other methods in this class. Except in very specific circumstances,
      # this should be the method called from outside of this class.
      # @param argv [Array] Array of command line arguments
      # @param defaults [Hash] Default values
      # @return [Hash] Parsed options
      def self.parse_options(argv, defaults = {})
        options = defaults.dup
        Options.classes.clear
        ::OptionParser.new do |parser|
          parser.banner = "#{BANNER}\n\n"
          option_classes.each do |klass|
            obj = klass.new
            obj.parse(parser, options)
          end
          parser.on_tail('-v', '--version', 'Show version information about this program and quit.') do
            puts "octocatalog-diff #{OctocatalogDiff::Version::VERSION}"
            exit
          end
        end.parse! argv
        options
      end

      # Read in *.rb files in the 'options' directory and create classes from them.
      # Sort the classes according to weight and name and return the list of sorted classes.
      # @return [Array<Class>] Sorted classes
      def self.option_classes
        files = Dir.glob(File.join(File.dirname(__FILE__), 'options', '*.rb'))
        files.each { |file| load file } # Populates self.classes
        classes.sort do |a, b|
          [
            a.weight <=> b.weight,
            a.name.downcase <=> b.name.downcase,
            a.object_id <=> b.object_id
          ].find(&:nonzero?)
        end
      end

      # Sets up options that can be defined globally or for just one branch. For example, with a
      # CLI name of 'puppet-binary' this will acknowledge 3 options: --puppet-binary (global),
      # --from-puppet-binary (for the from branch only), and --to-puppet-binary (for the to branch
      # only). The only options that will be created are the 'to' and 'from' variants, but the global
      # option will populate any of the 'to' and 'from' variants that are missing.
      # @param :datatype [?] Expected data type
      def self.option_globally_or_per_branch(opts = {})
        opts[:filename] = caller[0].split(':').first
        datatype = opts.fetch(:datatype, '')
        return option_globally_or_per_branch_string(opts) if datatype.is_a?(String)
        return option_globally_or_per_branch_array(opts) if datatype.is_a?(Array)
        raise ArgumentError, "option_globally_or_per_branch not equipped to handle #{datatype.class}"
      end

      # See description of `option_globally_or_per_branch`. This implements the logic for a string value.
      # @param :parser [OptionParser object] The OptionParser argument
      # @param :options [Hash] Options hash being constructed; this is modified in this method.
      # @param :cli_name [String] Name of option on command line (e.g. puppet-binary)
      # @param :option_name [Symbol] Name of option in the options hash (e.g. :puppet_binary)
      # @param :desc [String] Description of option on the command line; will have "for the XX branch" appended
      def self.option_globally_or_per_branch_string(opts)
        parser = opts.fetch(:parser)
        options = opts.fetch(:options)
        cli_name = opts.fetch(:cli_name)
        option_name = opts.fetch(:option_name)
        desc = opts.fetch(:desc)

        flag = "#{cli_name} STRING"
        from_option = "from_#{option_name}".to_sym
        to_option = "to_#{option_name}".to_sym
        parser.on("--#{flag}", "#{desc} globally") do |x|
          validate_option(opts, x)
          translated = translate_option(opts[:translator], x)
          options[to_option] ||= translated
          options[from_option] ||= translated
          post_process(opts[:post_process], options)
        end
        parser.on("--to-#{flag}", "#{desc} for the to branch") do |x|
          validate_option(opts, x)
          options[to_option] = translate_option(opts[:translator], x)
          post_process(opts[:post_process], options)
        end
        parser.on("--from-#{flag}", "#{desc} for the from branch") do |x|
          validate_option(opts, x)
          options[from_option] = translate_option(opts[:translator], x)
          post_process(opts[:post_process], options)
        end
      end

      # See description of `option_globally_or_per_branch`. This implements the logic for an array.
      # @param :parser [OptionParser object] The OptionParser argument
      # @param :options [Hash] Options hash being constructed; this is modified in this method.
      # @param :cli_name [String] Name of option on command line (e.g. puppet-binary)
      # @param :option_name [Symbol] Name of option in the options hash (e.g. :puppet_binary)
      # @param :desc [String] Description of option on the command line; will have "for the XX branch" appended
      def self.option_globally_or_per_branch_array(opts = {})
        parser = opts.fetch(:parser)
        options = opts.fetch(:options)
        cli_name = opts.fetch(:cli_name)
        option_name = opts.fetch(:option_name)
        desc = opts.fetch(:desc)

        flag = "#{cli_name} STRING1[,STRING2[,...]]"
        from_option = "from_#{option_name}".to_sym
        to_option = "to_#{option_name}".to_sym
        parser.on("--#{flag}", Array, "#{desc} globally") do |x|
          validate_option(opts, x)
          translated = translate_option(opts[:translator], x)
          options[to_option] ||= []
          options[to_option].concat translated
          options[from_option] ||= []
          options[from_option].concat translated
        end
        parser.on("--to-#{flag}", Array, "#{desc} for the to branch") do |x|
          validate_option(opts, x)
          options[to_option] ||= []
          options[to_option].concat translate_option(opts[:translator], x)
        end
        parser.on("--from-#{flag}", Array, "#{desc} for the from branch") do |x|
          validate_option(opts, x)
          options[from_option] ||= []
          options[from_option].concat translate_option(opts[:translator], x)
        end
      end

      # If a validator was provided, run the validator on the supplied value. The validator is expected to
      # throw an error if there is a problem. Note that the validator runs *before* the translator if both
      # a validator and translator are supplied.
      # @param opts [Hash] Options hash
      # @param value [?] Value to validate (typically a String but can really be anything)
      def self.validate_option(opts, value)
        # Special value to help build documentation automatically, since the source file location
        # for `option_globally_or_per_branch` is always this file.
        raise DocBuildError, opts[:filename] if value == :DOC_BUILD_FILENAME

        validator = opts[:validator]
        return true unless validator
        validator.call(value)
      end

      # If a translator was provided, run the translator on the supplied value. The translator is expected
      # to return the data type needed for the option (typically a String but can really be anything). Note
      # that the translator runs *after* the validator if both a validator and translator are supplied.
      # @param translator [Code] Translator function
      # @param value [?] Original input value
      # @return [?] Translated value
      def self.translate_option(translator, value)
        return value if translator.nil?
        translator.call(value)
      end

      # Code that can run after a translation and operate upon all options. This returns nothing but may
      # modify options that were input.
      # @param processor [Code] Processor function
      # @param options [Hash] Options hash
      def self.post_process(processor, options)
        return if processor.nil?
        processor.call(options)
      end
    end
  end
end