ios_parser 0.5.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ banner exec ^C
2
+
3
+ / /
4
+ (\/_//`)
5
+ / '/
6
+ 0 0 \
7
+ / \
8
+ / __/ \
9
+ /, _/ \ \_
10
+ `-./ ) | ~^~^~^~^~^~^~^~\~.
11
+ ( / \_}
12
+ | / |
13
+ ; | \ /
14
+ \/ ,/ \ |
15
+ / /~~|~|~~~~~~|~|\ |
16
+ / / | | | | `\ \
17
+ / / | | | | \ \
18
+ / ( | | | | \ \
19
+ jgs /,_) /__) /__) /,_/
20
+ '''''"""""'''""""""'''""""""''"""""'''''
21
+
22
+ Welcome to the Goat Rodeo!!
23
+
24
+ ^C
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
2
+ require 'ios_parser/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'ios_parser'
6
+ s.version = IOSParser.version
7
+ s.summary = 'convert network switch and router config files to '\
8
+ 'structured data'
9
+ s.authors = ['Ben Miller']
10
+ s.email = 'bjmllr@gmail.com'
11
+ s.homepage = 'https://github.com/bjmllr/ios_parser'
12
+ s.license = 'GPL-3.0'
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
15
+
16
+ if RUBY_PLATFORM == 'java'
17
+ s.platform = 'java'
18
+ else
19
+ s.extensions << 'ext/ios_parser/c_lexer/extconf.rb'
20
+ end
21
+
22
+ s.add_development_dependency 'rake-compiler', '~>0.9'
23
+ s.add_development_dependency 'rspec', '~>3.2'
24
+ s.add_development_dependency 'rubocop', '~> 0.54' if RUBY_VERSION > '2.1'
25
+ end
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+ require_relative 'queryable'
3
+
4
+ module IOSParser
5
+ class IOS
6
+ class Command
7
+ include Enumerable
8
+ include Queryable
9
+ attr_accessor :args, :commands, :parent, :pos, :document
10
+
11
+ def initialize(args: [], commands: [],
12
+ parent: nil, pos: nil, document: nil)
13
+ @args = args
14
+ @commands = commands
15
+ @parent = parent
16
+ @pos = pos
17
+ @document = document
18
+ end
19
+
20
+ def name
21
+ args[0]
22
+ end
23
+
24
+ def ==(other)
25
+ args == other.args && commands == other.commands
26
+ end
27
+
28
+ def eql?(other)
29
+ self == other && self.class == other.class
30
+ end
31
+
32
+ def line
33
+ args.join(' ')
34
+ end
35
+
36
+ def path
37
+ parent ? parent.path + [parent.line] : []
38
+ end
39
+
40
+ def indentation(base: 0)
41
+ ' ' * (path.length - base)
42
+ end
43
+
44
+ def each
45
+ yield self
46
+ commands.each { |command| command.each { |cmd| yield cmd } }
47
+ end
48
+
49
+ def inspect
50
+ "<IOSParser::IOS::Command:0x#{object_id.to_s(16)} "\
51
+ "@args=#{args.inspect}, "\
52
+ "@commands=#{commands.inspect}, "\
53
+ "@pos=#{pos.inspect}, "\
54
+ "@document=<IOSParser::IOS::Document:0x#{document.object_id.to_s(16)}>>"
55
+ end
56
+
57
+ def to_s(dedent: false)
58
+ indent_opts = { base: dedent ? path.length : 0 }
59
+ map { |cmd| "#{cmd.indentation(indent_opts)}#{cmd.line}\n" }.join
60
+ end
61
+
62
+ def to_hash
63
+ {
64
+ args: args,
65
+ commands: commands.map(&:to_hash),
66
+ pos: pos
67
+ }
68
+ end
69
+
70
+ def to_json
71
+ JSON.dump(to_hash)
72
+ end
73
+
74
+ class << self
75
+ def from_hash(hash, parent = nil)
76
+ hash[:parent] = parent
77
+ [:args, :commands, :pos].each do |key|
78
+ val = hash.delete(key.to_s)
79
+ hash[key] ||= val
80
+ end
81
+
82
+ hash[:commands] ||= []
83
+ hash[:commands].each_index do |i|
84
+ hash[:commands][i] = from_hash(hash[:commands][i])
85
+ end
86
+ new(hash)
87
+ end
88
+ end
89
+ end # class Command
90
+ end # class IOS
91
+ end # module IOSParser
@@ -0,0 +1,54 @@
1
+ require 'json'
2
+ require_relative 'queryable'
3
+ require_relative 'command'
4
+
5
+ module IOSParser
6
+ class IOS
7
+ class Document
8
+ include Enumerable
9
+ include Queryable
10
+ attr_accessor :commands, :parent, :source
11
+
12
+ def initialize(source)
13
+ @commands = []
14
+ @parent = nil
15
+ @source = source
16
+ end
17
+
18
+ [:[], :push].each do |method|
19
+ define_method(method) { |*args| commands.send(method, *args) }
20
+ end
21
+
22
+ def each
23
+ commands.each { |command| command.each { |cmd| yield cmd } }
24
+ end
25
+
26
+ def to_s(dedent: false)
27
+ base = dedent ? indentation : 0
28
+ map { |cmd| "#{cmd.indentation(base: base)}#{cmd.line}\n" }.join
29
+ end
30
+
31
+ def to_hash
32
+ { commands: commands.map(&:to_hash) }
33
+ end
34
+
35
+ def to_json
36
+ JSON.dump(to_hash)
37
+ end
38
+
39
+ class << self
40
+ def from_hash(hash)
41
+ hash[:parent] = parent
42
+ [:commands, :source].each do |key|
43
+ val = hash.delete(key.to_s)
44
+ hash[key] = val unless hash.key?(key)
45
+ end
46
+
47
+ new(source).tap do |doc|
48
+ doc.push(*(hash[:commands].map { |c| Command.from_hash(c) }))
49
+ end
50
+ end
51
+ end # class << self
52
+ end # class Document
53
+ end # class IOS
54
+ end # class IOSParser
@@ -0,0 +1,219 @@
1
+ module IOSParser
2
+ class IOS
3
+ module Queryable
4
+ def find_all(expr, &blk)
5
+ _find_all(MatcherReader.query_expression(expr), &blk)
6
+ end
7
+
8
+ def find(expr, &blk)
9
+ _find(MatcherReader.query_expression(expr), &blk)
10
+ end
11
+
12
+ def _find_all(expr, &blk)
13
+ [].tap do |ret|
14
+ commands.each do |command|
15
+ if match_expr(expr, command)
16
+ ret << command
17
+ yield(command) if blk
18
+ end
19
+
20
+ ret.push(*command._find_all(expr, &blk))
21
+ end
22
+ end
23
+ end
24
+
25
+ def match_expr(expr, command)
26
+ expr.each_pair.all? { |pred, arg| Matcher.send(pred, arg, command) }
27
+ end
28
+
29
+ def _find(expr, &blk)
30
+ _find_all(expr) do |command|
31
+ yield(command) if blk
32
+ return command
33
+ end
34
+ nil
35
+ end
36
+
37
+ module MatcherReader
38
+ class << self
39
+ def query_expression(raw)
40
+ case raw
41
+ when Hash then query_expression_hash(raw)
42
+ when Proc then { procedure: procedure(raw) }
43
+ when Regexp then { line: line(raw) }
44
+ when String, Array then { starts_with: starts_with(raw) }
45
+ else raise("Invalid query: #{raw.inspect}")
46
+ end
47
+ end
48
+ alias parent query_expression
49
+ alias any_child query_expression
50
+ alias no_child query_expression
51
+
52
+ def query_expression_hash(original)
53
+ raw = original.dup
54
+ raw.each_pair { |pred, arg| raw[pred] &&= send(pred, arg) }
55
+ raw
56
+ end
57
+
58
+ def name(expr)
59
+ expr
60
+ end
61
+
62
+ def starts_with(expr)
63
+ case expr
64
+ when String then expr.split
65
+ when Array then expr
66
+ else raise("Invalid #{__method__} condition in query: #{expr}")
67
+ end
68
+ end
69
+ alias contains starts_with
70
+ alias ends_with starts_with
71
+
72
+ def procedure(expr)
73
+ unless expr.respond_to?(:call)
74
+ raise("Invalid procedure in query: #{expr}")
75
+ end
76
+ expr
77
+ end
78
+
79
+ def line(expr)
80
+ case expr
81
+ when String, Regexp then expr
82
+ when Array then expr.join(' ')
83
+ else raise("Invalid line condition in query: #{expr}")
84
+ end
85
+ end
86
+
87
+ def array_wrap_and_map(expr)
88
+ (expr.respond_to?(:map) && !expr.is_a?(Hash) ? expr : [expr])
89
+ .map { |e| query_expression(e) }
90
+ end
91
+ alias any array_wrap_and_map
92
+ alias all array_wrap_and_map
93
+ alias none array_wrap_and_map
94
+ alias not array_wrap_and_map
95
+ alias not_all array_wrap_and_map
96
+
97
+ def depth(expr)
98
+ unless expr.is_a?(Integer) || (expr.is_a?(Range) &&
99
+ expr.first.is_a?(Integer) &&
100
+ expr.last.is_a?(Integer))
101
+ raise("Invalid depth constraint in query: #{expr}")
102
+ end
103
+ expr
104
+ end
105
+ end # class << self
106
+ end # module MatcherReader
107
+
108
+ module Matcher
109
+ class << self
110
+ def name(expr, command)
111
+ expr === command.name
112
+ end
113
+
114
+ def starts_with(req_ary, command)
115
+ (0..req_ary.length - 1).all? do |i|
116
+ compare_string_or_case(req_ary[i], command.args[i])
117
+ end
118
+ end
119
+
120
+ def contains(req_ary, command)
121
+ (0..command.args.length - req_ary.length).any? do |j|
122
+ (0..req_ary.length - 1).all? do |i|
123
+ compare_string_or_case(req_ary[i], command.args[i + j])
124
+ end
125
+ end
126
+ end
127
+
128
+ def ends_with(req_ary, command)
129
+ (1..req_ary.length).all? do |i|
130
+ compare_string_or_case(req_ary[-i], command.args[-1])
131
+ end
132
+ end
133
+
134
+ def compare_string_or_case(a_object, b_object)
135
+ case a_object
136
+ when String
137
+ a_object == b_object.to_s
138
+ else
139
+ a_object === b_object
140
+ end
141
+ end
142
+
143
+ def procedure(expr, command)
144
+ expr.call(command)
145
+ end
146
+
147
+ def line(expr, command)
148
+ expr === command.line
149
+ end
150
+
151
+ def parent(expr, command)
152
+ expr.each_pair.all? do |pred, arg|
153
+ command.parent && send(pred, arg, command.parent)
154
+ end
155
+ end
156
+
157
+ def any_child(expr, command)
158
+ command.find(expr)
159
+ end
160
+
161
+ def no_child(expr, command)
162
+ !command.find(expr)
163
+ end
164
+
165
+ def any(expressions, command)
166
+ expressions.any? do |expr|
167
+ expr.each_pair.any? do |pred, arg|
168
+ send(pred, arg, command)
169
+ end
170
+ end
171
+ end
172
+
173
+ def all(expressions, command)
174
+ expressions.all? do |expr|
175
+ expr.each_pair.all? do |pred, arg|
176
+ send(pred, arg, command)
177
+ end
178
+ end
179
+ end
180
+
181
+ def not_all(expressions, command)
182
+ !expressions.all? { |expr| all([expr], command) }
183
+ end
184
+ alias not not_all
185
+
186
+ def none(expressions, command)
187
+ expressions.none? { |expr| all([expr], command) }
188
+ end
189
+
190
+ def depth(expr, command)
191
+ case expr
192
+ when Integer then depth_exact(expr, command)
193
+ when Range then depth_range(expr, command)
194
+ end
195
+ end
196
+
197
+ def depth_exact(expr, command)
198
+ _depth(expr, command) == expr
199
+ end
200
+
201
+ def depth_range(expr, command)
202
+ _depth(expr.last, command) > expr.first
203
+ end
204
+
205
+ def _depth(max, command)
206
+ level = 0
207
+ ptr = command
208
+ while ptr.parent
209
+ ptr = ptr.parent
210
+ level += 1
211
+ return Float::MIN if level > max
212
+ end
213
+ level
214
+ end
215
+ end # class << self
216
+ end # module Matcher
217
+ end # module Queryable
218
+ end # class IOS
219
+ end # module IOSParser
@@ -0,0 +1,73 @@
1
+ require_relative 'ios/document'
2
+
3
+ module IOSParser
4
+ class IOS
5
+ attr_accessor :document
6
+ attr_accessor :lexer
7
+ attr_accessor :source
8
+ attr_writer :tokens
9
+
10
+ def initialize(parent: nil, lexer: IOSParser::Lexer.new)
11
+ @document = Document.new(nil)
12
+ @parent = parent
13
+ @lexer = lexer
14
+ end
15
+
16
+ def tokens
17
+ @tokens ||= lexer.call(@source)
18
+ end
19
+
20
+ def call(source)
21
+ unless source.respond_to? :each_char
22
+ raise ArgumentError, 'Provided configuration source is invalid.'
23
+ end
24
+ @source = source
25
+ @document.source = source
26
+ @document.push(*section) until tokens.empty?
27
+ @document
28
+ end
29
+
30
+ def section(parent = nil)
31
+ [].tap do |commands|
32
+ until tokens.empty? || tokens.first.last == :DEDENT
33
+ commands.push(command(parent, @document))
34
+ end
35
+ tokens.shift # discard :DEDENT
36
+ end
37
+ end
38
+
39
+ def command(parent = nil, document = nil)
40
+ pos = tokens.first.first
41
+ opts = { args: arguments, parent: parent, document: document, pos: pos }
42
+
43
+ Command.new(opts).tap do |cmd|
44
+ cmd.commands = subsections(cmd)
45
+ end
46
+ end
47
+
48
+ def arguments_to_discard
49
+ [:INDENT, :DEDENT,
50
+ :CERTIFICATE_BEGIN, :CERTIFICATE_END,
51
+ :BANNER_BEGIN, :BANNER_END]
52
+ end
53
+
54
+ def arguments
55
+ [].tap do |args|
56
+ until tokens.empty? || tokens.first.last == :EOL
57
+ _, arg = tokens.shift
58
+ args << arg unless arguments_to_discard.include?(arg)
59
+ end
60
+ tokens.shift # discard :EOL
61
+ end
62
+ end
63
+
64
+ def subsections(parent = nil)
65
+ if !tokens.empty? && tokens.first.last == :INDENT
66
+ tokens.shift # discard :INDENT
67
+ section(parent)
68
+ else
69
+ []
70
+ end
71
+ end
72
+ end
73
+ end