ronin-support 0.2.0 → 0.3.0
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.
- data/.gitignore +11 -0
- data/ChangeLog.md +42 -1
- data/README.md +4 -1
- data/gemspec.yml +2 -1
- data/lib/ronin/extensions.rb +2 -0
- data/lib/ronin/extensions/enumerable.rb +54 -0
- data/lib/ronin/extensions/file.rb +70 -2
- data/lib/ronin/extensions/ip_addr.rb +45 -45
- data/lib/ronin/extensions/regexp.rb +45 -0
- data/lib/ronin/extensions/resolv.rb +80 -0
- data/lib/ronin/extensions/string.rb +35 -32
- data/lib/ronin/formatting/extensions/binary/integer.rb +12 -5
- data/lib/ronin/formatting/extensions/binary/string.rb +44 -16
- data/lib/ronin/formatting/extensions/html/integer.rb +51 -31
- data/lib/ronin/formatting/extensions/html/string.rb +50 -31
- data/lib/ronin/formatting/extensions/http/integer.rb +10 -2
- data/lib/ronin/formatting/extensions/sql.rb +20 -0
- data/lib/ronin/formatting/extensions/sql/string.rb +98 -0
- data/lib/ronin/formatting/extensions/text/array.rb +11 -9
- data/lib/ronin/formatting/extensions/text/string.rb +213 -29
- data/lib/ronin/formatting/sql.rb +20 -0
- data/lib/ronin/network/extensions/http.rb +1 -0
- data/lib/ronin/network/extensions/http/net.rb +2 -2
- data/lib/ronin/network/extensions/http/uri/http.rb +226 -0
- data/lib/ronin/network/extensions/imap/net.rb +1 -1
- data/lib/ronin/network/extensions/ssl/net.rb +7 -1
- data/lib/ronin/network/http/proxy.rb +20 -21
- data/lib/ronin/network/mixins.rb +27 -0
- data/lib/ronin/network/mixins/esmtp.rb +165 -0
- data/lib/ronin/network/mixins/http.rb +723 -0
- data/lib/ronin/network/mixins/imap.rb +151 -0
- data/lib/ronin/network/mixins/pop3.rb +141 -0
- data/lib/ronin/network/mixins/smtp.rb +159 -0
- data/lib/ronin/network/mixins/tcp.rb +331 -0
- data/lib/ronin/network/mixins/telnet.rb +199 -0
- data/lib/ronin/network/mixins/udp.rb +227 -0
- data/lib/ronin/network/ssl.rb +17 -11
- data/lib/ronin/path.rb +3 -3
- data/lib/ronin/spec/ui/output.rb +28 -0
- data/lib/ronin/support.rb +3 -0
- data/lib/ronin/support/version.rb +1 -1
- data/lib/ronin/ui/output.rb +21 -0
- data/lib/ronin/ui/output/helpers.rb +248 -0
- data/lib/ronin/ui/output/output.rb +146 -0
- data/lib/ronin/ui/output/terminal.rb +21 -0
- data/lib/ronin/ui/output/terminal/color.rb +118 -0
- data/lib/ronin/ui/output/terminal/raw.rb +103 -0
- data/lib/ronin/ui/shell.rb +219 -0
- data/ronin-support.gemspec +1 -1
- data/spec/extensions/enumerable_spec.rb +24 -0
- data/spec/extensions/file_spec.rb +39 -0
- data/spec/extensions/ip_addr_spec.rb +6 -0
- data/spec/extensions/resolv_spec.rb +18 -0
- data/spec/formatting/html/integer_spec.rb +2 -2
- data/spec/formatting/html/string_spec.rb +1 -1
- data/spec/formatting/sql/string_spec.rb +55 -0
- data/spec/formatting/text/string_spec.rb +110 -0
- data/spec/network/ssl_spec.rb +10 -4
- data/spec/ui/classes/test_shell.rb +22 -0
- data/spec/ui/output_spec.rb +32 -0
- data/spec/ui/shell_spec.rb +79 -0
- metadata +132 -90
@@ -41,6 +41,10 @@ class String
|
|
41
41
|
# @return [String]
|
42
42
|
# The HTML escaped String.
|
43
43
|
#
|
44
|
+
# @example
|
45
|
+
# "one & two".html_escape
|
46
|
+
# # => "one & two"
|
47
|
+
#
|
44
48
|
# @see http://rubydoc.info/stdlib/cgi/1.9.2/CGI.escapeHTML
|
45
49
|
#
|
46
50
|
# @since 0.2.0
|
@@ -57,6 +61,10 @@ class String
|
|
57
61
|
# @return [String]
|
58
62
|
# The unescaped String.
|
59
63
|
#
|
64
|
+
# @example
|
65
|
+
# "<p>one <span>two</span></p>".html_unescape
|
66
|
+
# # => "<p>one <span>two</span></p>"
|
67
|
+
#
|
60
68
|
# @see http://rubydoc.info/stdlib/cgi/1.9.2/CGI.unescapeHTML
|
61
69
|
#
|
62
70
|
# @since 0.2.0
|
@@ -76,6 +84,10 @@ class String
|
|
76
84
|
# @return [String]
|
77
85
|
# The formatted HTML String.
|
78
86
|
#
|
87
|
+
# @example
|
88
|
+
# "abc".format_html
|
89
|
+
# # => "abc"
|
90
|
+
#
|
79
91
|
# @see Integer#format_html
|
80
92
|
#
|
81
93
|
# @since 0.2.0
|
@@ -83,12 +95,14 @@ class String
|
|
83
95
|
# @api public
|
84
96
|
#
|
85
97
|
def format_html(options={})
|
86
|
-
if RUBY_VERSION < '1.9.'
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
98
|
+
formatter = if RUBY_VERSION < '1.9.'
|
99
|
+
# String#ord was not backported to Ruby 1.8.7
|
100
|
+
lambda { |c| c[0].format_html }
|
101
|
+
else
|
102
|
+
lambda { |c| c.ord.format_html }
|
103
|
+
end
|
104
|
+
|
105
|
+
format_chars(options,&formatter)
|
92
106
|
end
|
93
107
|
|
94
108
|
#
|
@@ -101,8 +115,8 @@ class String
|
|
101
115
|
# The JavaScript escaped String.
|
102
116
|
#
|
103
117
|
# @example
|
104
|
-
# "hello".js_escape
|
105
|
-
# # => "
|
118
|
+
# "hello\nworld\n".js_escape
|
119
|
+
# # => "hello\\nworld\\n"
|
106
120
|
#
|
107
121
|
# @see Integer#js_escape
|
108
122
|
#
|
@@ -111,12 +125,14 @@ class String
|
|
111
125
|
# @api public
|
112
126
|
#
|
113
127
|
def js_escape(options={})
|
114
|
-
if RUBY_VERSION < '1.9.'
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
128
|
+
formatter = if RUBY_VERSION < '1.9.'
|
129
|
+
# String#ord was not backported to Rub 1.8.7
|
130
|
+
lambda { |c| c[0].js_escape }
|
131
|
+
else
|
132
|
+
lambda { |c| c.ord.js_escape }
|
133
|
+
end
|
134
|
+
|
135
|
+
format_chars(options,&formatter)
|
120
136
|
end
|
121
137
|
|
122
138
|
#
|
@@ -126,7 +142,7 @@ class String
|
|
126
142
|
# The unescaped JavaScript String.
|
127
143
|
#
|
128
144
|
# @example
|
129
|
-
# "
|
145
|
+
# "\\u0068\\u0065\\u006C\\u006C\\u006F world".js_unescape
|
130
146
|
# # => "hello world"
|
131
147
|
#
|
132
148
|
# @since 0.2.0
|
@@ -139,15 +155,16 @@ class String
|
|
139
155
|
scan(/([\\%]u[0-9a-fA-F]{4}|[\\%][0-9a-fA-F]{2}|\\[btnfr"\\]|.)/).each do |match|
|
140
156
|
c = match[0]
|
141
157
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
158
|
+
unescaped << case c.length
|
159
|
+
when 6
|
160
|
+
c[2,4].to_i(16)
|
161
|
+
when 3
|
162
|
+
c[1,2].to_i(16)
|
163
|
+
when 2
|
164
|
+
JS_BACKSLASHED_CHARS[c]
|
165
|
+
else
|
166
|
+
c
|
167
|
+
end
|
151
168
|
end
|
152
169
|
|
153
170
|
return unescaped
|
@@ -164,7 +181,7 @@ class String
|
|
164
181
|
#
|
165
182
|
# @example
|
166
183
|
# "hello".js_escape
|
167
|
-
# # => "
|
184
|
+
# # => "\\u0068\\u0065\\u006C\\u006C\\u006F"
|
168
185
|
#
|
169
186
|
# @see Integer#js_escape
|
170
187
|
#
|
@@ -173,12 +190,14 @@ class String
|
|
173
190
|
# @api public
|
174
191
|
#
|
175
192
|
def format_js(options={})
|
176
|
-
if RUBY_VERSION < '1.9.'
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
193
|
+
formatter = if RUBY_VERSION < '1.9.'
|
194
|
+
# String#ord was not backported to Rub 1.8.7
|
195
|
+
lambda { |c| c[0].format_js }
|
196
|
+
else
|
197
|
+
lambda { |c| c.ord.format_js }
|
198
|
+
end
|
199
|
+
|
200
|
+
format_chars(options,&formatter)
|
182
201
|
end
|
183
202
|
|
184
203
|
end
|
@@ -31,7 +31,7 @@ class Integer
|
|
31
31
|
# @api public
|
32
32
|
#
|
33
33
|
def uri_encode
|
34
|
-
URI.encode(
|
34
|
+
URI.encode(chr)
|
35
35
|
end
|
36
36
|
|
37
37
|
#
|
@@ -40,10 +40,14 @@ class Integer
|
|
40
40
|
# @return [String]
|
41
41
|
# The URI escaped byte.
|
42
42
|
#
|
43
|
+
# @example
|
44
|
+
# 0x3d.uri_escape
|
45
|
+
# # => "%3D"
|
46
|
+
#
|
43
47
|
# @api public
|
44
48
|
#
|
45
49
|
def uri_escape
|
46
|
-
CGI.escape(
|
50
|
+
CGI.escape(chr)
|
47
51
|
end
|
48
52
|
|
49
53
|
#
|
@@ -52,6 +56,10 @@ class Integer
|
|
52
56
|
# @return [String]
|
53
57
|
# The formatted byte.
|
54
58
|
#
|
59
|
+
# @example
|
60
|
+
# 0x41.format_http
|
61
|
+
# # => "%41"
|
62
|
+
#
|
55
63
|
# @api public
|
56
64
|
#
|
57
65
|
def format_http
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
|
3
|
+
#
|
4
|
+
# This file is part of Ronin Support.
|
5
|
+
#
|
6
|
+
# Ronin Support is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Ronin Support is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'ronin/formatting/extensions/sql/string'
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
|
3
|
+
#
|
4
|
+
# This file is part of Ronin Support.
|
5
|
+
#
|
6
|
+
# Ronin Support is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Ronin Support is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
|
20
|
+
class String
|
21
|
+
|
22
|
+
#
|
23
|
+
# Escapes an String for SQL.
|
24
|
+
#
|
25
|
+
# @param [Symbol] quotes (:single)
|
26
|
+
# Specifies whether to create a single or double quoted string.
|
27
|
+
# May be either `:single` or `:double`.
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
# The escaped String.
|
31
|
+
#
|
32
|
+
# @raise [ArgumentError]
|
33
|
+
# The quotes argument was neither `:single` nor `:double`.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# "O'Brian".sql_escape
|
37
|
+
# # => "'O''Brian'"
|
38
|
+
#
|
39
|
+
# @since 0.3.0
|
40
|
+
#
|
41
|
+
def sql_escape(quotes=:single)
|
42
|
+
case quotes
|
43
|
+
when :single
|
44
|
+
"'#{gsub(/'/,"''")}'"
|
45
|
+
when :double
|
46
|
+
"\"#{gsub(/"/,'""')}\""
|
47
|
+
else
|
48
|
+
raise(ArgumentError,"invalid quoting style #{quotes.inspect}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Returns the SQL hex-string encoded form of the String.
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# "/etc/passwd".sql_encode
|
57
|
+
# # => "0x2f6574632f706173737764"
|
58
|
+
#
|
59
|
+
def sql_encode
|
60
|
+
return '' if empty?
|
61
|
+
|
62
|
+
hex_string = '0x'
|
63
|
+
|
64
|
+
each_byte do |b|
|
65
|
+
hex_string << ('%.2x' % b)
|
66
|
+
end
|
67
|
+
|
68
|
+
return hex_string
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Returns the SQL decoded form of the String.
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# "'Conan O''Brian'".sql_decode
|
76
|
+
# # => "Conan O'Brian"
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# "0x2f6574632f706173737764".sql_decode
|
80
|
+
# # => "/etc/passwd"
|
81
|
+
#
|
82
|
+
def sql_decode
|
83
|
+
if ((self[0...2] == '0x') && (length % 2 == 0))
|
84
|
+
raw = ''
|
85
|
+
|
86
|
+
self[2..-1].scan(/[0-9a-fA-F]{2}/).each do |hex_char|
|
87
|
+
raw << hex_char.hex.chr
|
88
|
+
end
|
89
|
+
|
90
|
+
return raw
|
91
|
+
elsif (self[0..0] == "'" && self[-1..-1] == "'")
|
92
|
+
self[1..-2].gsub("\\'","'").gsub("''","'")
|
93
|
+
else
|
94
|
+
return self
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -37,15 +37,17 @@ class Array
|
|
37
37
|
# @api public
|
38
38
|
#
|
39
39
|
def bytes
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
bytes = []
|
41
|
+
|
42
|
+
each do |element|
|
43
|
+
if element.kind_of?(Integer)
|
44
|
+
bytes << element
|
43
45
|
else
|
44
|
-
|
46
|
+
element.to_s.each_byte { |b| bytes << b }
|
45
47
|
end
|
46
|
-
|
47
|
-
accum
|
48
48
|
end
|
49
|
+
|
50
|
+
return bytes
|
49
51
|
end
|
50
52
|
|
51
53
|
#
|
@@ -62,7 +64,7 @@ class Array
|
|
62
64
|
# @api public
|
63
65
|
#
|
64
66
|
def chars
|
65
|
-
array_bytes =
|
67
|
+
array_bytes = bytes
|
66
68
|
|
67
69
|
array_bytes.map! { |b| b.chr }
|
68
70
|
return array_bytes
|
@@ -100,7 +102,7 @@ class Array
|
|
100
102
|
# @api public
|
101
103
|
#
|
102
104
|
def hex_chars
|
103
|
-
array_bytes =
|
105
|
+
array_bytes = bytes
|
104
106
|
|
105
107
|
array_bytes.map! { |b| '\x%x' % b }
|
106
108
|
return array_bytes
|
@@ -124,7 +126,7 @@ class Array
|
|
124
126
|
# @api public
|
125
127
|
#
|
126
128
|
def hex_integers
|
127
|
-
array_bytes =
|
129
|
+
array_bytes = bytes
|
128
130
|
|
129
131
|
array_bytes.map! { |b| '0x%x' % b }
|
130
132
|
return array_bytes
|
@@ -17,10 +17,190 @@
|
|
17
17
|
# along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
#
|
19
19
|
|
20
|
+
require 'combinatorics/list_comprehension'
|
21
|
+
require 'combinatorics/generator'
|
22
|
+
require 'chars'
|
20
23
|
require 'set'
|
21
24
|
|
22
25
|
class String
|
23
26
|
|
27
|
+
#
|
28
|
+
# Generate permutations of Strings from a format template.
|
29
|
+
#
|
30
|
+
# @param [Array(<String, Symbol, Enumerable>, <Integer, Enumerable>)] template
|
31
|
+
# The template which defines the string or character sets which will
|
32
|
+
# make up parts of the String.
|
33
|
+
#
|
34
|
+
# @yield [string]
|
35
|
+
# The given block will be passed each unique String.
|
36
|
+
#
|
37
|
+
# @yieldparam [String] string
|
38
|
+
# A newly generated String.
|
39
|
+
#
|
40
|
+
# @return [Enumerator]
|
41
|
+
# If no block is given, an Enumerator will be returned.
|
42
|
+
#
|
43
|
+
# @raise [ArgumentError]
|
44
|
+
# A given character set name was unknown.
|
45
|
+
#
|
46
|
+
# @raise [TypeError]
|
47
|
+
# A given string set was not a String, Symbol or Enumerable.
|
48
|
+
# A given string set length was not an Integer or Enumerable.
|
49
|
+
#
|
50
|
+
# @example Generate Strings with ranges of repeating sub-strings.
|
51
|
+
#
|
52
|
+
# @example Generate Strings with three alpha chars and one numeric chars.
|
53
|
+
# String.generate([:alpha, 3], :numeric) do |password|
|
54
|
+
# puts password
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# @example Generate Strings with two to four alpha chars.
|
58
|
+
# String.generate([:alpha, 2..4]) do |password|
|
59
|
+
# puts password
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# @example Generate Strings using alpha and punctuation chars.
|
63
|
+
# String.generate([Chars.alpha + Chars.punctuation, 4]) do |password|
|
64
|
+
# puts password
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# @example Generate Strings from a custom char set.
|
68
|
+
# String.generate([['a', 'b', 'c'], 3], [['1', '2', '3'], 3]) do |password|
|
69
|
+
# puts password
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @example Generate Strings containing known Strings.
|
73
|
+
# String.generate("rock", [:numeric, 4]) do |password|
|
74
|
+
# puts password
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# @example Generate Strings with ranges of repeating sub-strings.
|
78
|
+
# String.generate(['/AA', (1..100).step(5)]) do |path|
|
79
|
+
# puts path
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# @since 0.3.0
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
#
|
86
|
+
def self.generate(*template)
|
87
|
+
return enum_for(:generate,*template) unless block_given?
|
88
|
+
|
89
|
+
sets = []
|
90
|
+
|
91
|
+
template.each do |pattern|
|
92
|
+
set, length = pattern
|
93
|
+
set = case set
|
94
|
+
when String
|
95
|
+
[set].each
|
96
|
+
when Symbol
|
97
|
+
name = set.to_s.upcase
|
98
|
+
|
99
|
+
unless Chars.const_defined?(name)
|
100
|
+
raise(ArgumentError,"unknown charset #{set.inspect}")
|
101
|
+
end
|
102
|
+
|
103
|
+
Chars.const_get(name).each_char
|
104
|
+
when Enumerable
|
105
|
+
Chars::CharSet.new(set).each_char
|
106
|
+
else
|
107
|
+
raise(TypeError,"set must be a String, Symbol or Enumerable")
|
108
|
+
end
|
109
|
+
|
110
|
+
case length
|
111
|
+
when Integer
|
112
|
+
length.times { sets << set.dup }
|
113
|
+
when Enumerable
|
114
|
+
sets << Combinatorics::Generator.new do |g|
|
115
|
+
length.each do |sublength|
|
116
|
+
superset = Array.new(sublength) { set.dup }
|
117
|
+
|
118
|
+
superset.comprehension { |strings| g.yield strings.join }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
when nil
|
122
|
+
sets << set
|
123
|
+
else
|
124
|
+
raise(TypeError,"length must be an Integer or Enumerable")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
sets.comprehension { |strings| yield strings.join }
|
129
|
+
return nil
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Incrementally fuzzes the String.
|
134
|
+
#
|
135
|
+
# @param [Hash{Regexp,String,Integer,Enumerable => #each}] mutations
|
136
|
+
# Patterns and their substitutions.
|
137
|
+
#
|
138
|
+
# @yield [fuzz]
|
139
|
+
# The given block will be passed every fuzzed String.
|
140
|
+
#
|
141
|
+
# @yieldparam [String] fuzz
|
142
|
+
# A fuzzed String.
|
143
|
+
#
|
144
|
+
# @return [Enumerator]
|
145
|
+
# If no block is given, an Enumerator will be returned.
|
146
|
+
#
|
147
|
+
# @example Replace every `e`, `i`, `o`, `u` with `(`, 100 `A`s and a `\0`:
|
148
|
+
# "the quick brown fox".fuzz(/[eiou]/ => ['(', ('A' * 100), "\0"]) do |str|
|
149
|
+
# p str
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# @example {String.generate} with {String.fuzz}:
|
153
|
+
# "GET /".fuzz('/' => String.generate(['A', 1..100])) do |str|
|
154
|
+
# p str
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# @since 0.3.0
|
158
|
+
#
|
159
|
+
# @api public
|
160
|
+
#
|
161
|
+
def fuzz(mutations={})
|
162
|
+
return enum_for(:fuzz,mutations) unless block_given?
|
163
|
+
|
164
|
+
mutations.each do |pattern,substitution|
|
165
|
+
pattern = case pattern
|
166
|
+
when Regexp
|
167
|
+
pattern
|
168
|
+
when String
|
169
|
+
Regexp.new(Regexp.escape(pattern))
|
170
|
+
when Integer
|
171
|
+
Regexp.new(pattern.chr)
|
172
|
+
when Enumerable
|
173
|
+
Regexp.union(pattern.map { |s| Regexp.escape(s.to_s) })
|
174
|
+
else
|
175
|
+
raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
|
176
|
+
end
|
177
|
+
|
178
|
+
scanner = StringScanner.new(self)
|
179
|
+
indices = []
|
180
|
+
|
181
|
+
while scanner.scan_until(pattern)
|
182
|
+
indices << [scanner.pos - scanner.matched_size, scanner.matched_size]
|
183
|
+
end
|
184
|
+
|
185
|
+
indices.each do |index,length|
|
186
|
+
substitution.each do |substitute|
|
187
|
+
substitute = case substitute
|
188
|
+
when Proc
|
189
|
+
substitute.call(self[index,length])
|
190
|
+
when Integer
|
191
|
+
substitute.chr
|
192
|
+
else
|
193
|
+
substitute.to_s
|
194
|
+
end
|
195
|
+
|
196
|
+
fuzzed = dup
|
197
|
+
fuzzed[index,length] = substitute
|
198
|
+
yield fuzzed
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
24
204
|
#
|
25
205
|
# Creates a new String by formatting each byte.
|
26
206
|
#
|
@@ -43,20 +223,23 @@ class String
|
|
43
223
|
# @return [String]
|
44
224
|
# The formatted version of the String.
|
45
225
|
#
|
226
|
+
# @example
|
227
|
+
# "hello".format_bytes { |b| "%x" % b }
|
228
|
+
# # => "68656c6c6f"
|
229
|
+
#
|
46
230
|
# @api public
|
47
231
|
#
|
48
232
|
def format_bytes(options={})
|
49
|
-
included
|
50
|
-
excluded
|
51
|
-
|
233
|
+
included = (options[:include] || (0x00..0xff))
|
234
|
+
excluded = (options[:exclude] || Set[])
|
52
235
|
formatted = ''
|
53
236
|
|
54
|
-
|
55
|
-
if (included.include?(b) && !excluded.include?(b))
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
237
|
+
each_byte do |b|
|
238
|
+
formatted << if (included.include?(b) && !excluded.include?(b))
|
239
|
+
yield(b)
|
240
|
+
else
|
241
|
+
b
|
242
|
+
end
|
60
243
|
end
|
61
244
|
|
62
245
|
return formatted
|
@@ -84,12 +267,15 @@ class String
|
|
84
267
|
# @return [String]
|
85
268
|
# The formatted version of the String.
|
86
269
|
#
|
270
|
+
# @example
|
271
|
+
# "hello".format_chars { |c| c * 3 }
|
272
|
+
# # => "hhheeellllllooo"
|
273
|
+
#
|
87
274
|
# @api public
|
88
275
|
#
|
89
276
|
def format_chars(options={})
|
90
|
-
included
|
91
|
-
excluded
|
92
|
-
|
277
|
+
included = (options[:include] || /./m)
|
278
|
+
excluded = (options[:exclude] || Set[])
|
93
279
|
formatted = ''
|
94
280
|
|
95
281
|
matches = lambda { |filter,c|
|
@@ -97,17 +283,15 @@ class String
|
|
97
283
|
filter.include?(c)
|
98
284
|
elsif filter.kind_of?(Regexp)
|
99
285
|
c =~ filter
|
100
|
-
else
|
101
|
-
false
|
102
286
|
end
|
103
287
|
}
|
104
288
|
|
105
|
-
|
106
|
-
if (matches[included,c] && !matches[excluded,c])
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
289
|
+
each_char do |c|
|
290
|
+
formatted << if (matches[included,c] && !matches[excluded,c])
|
291
|
+
yield(c)
|
292
|
+
else
|
293
|
+
c
|
294
|
+
end
|
111
295
|
end
|
112
296
|
|
113
297
|
return formatted
|
@@ -162,8 +346,8 @@ class String
|
|
162
346
|
# @api public
|
163
347
|
#
|
164
348
|
def insert_before(pattern,data)
|
165
|
-
string =
|
166
|
-
index
|
349
|
+
string = dup
|
350
|
+
index = string.index(pattern)
|
167
351
|
|
168
352
|
string.insert(index,data) if index
|
169
353
|
return string
|
@@ -184,8 +368,8 @@ class String
|
|
184
368
|
# @api public
|
185
369
|
#
|
186
370
|
def insert_after(pattern,data)
|
187
|
-
string =
|
188
|
-
match
|
371
|
+
string = dup
|
372
|
+
match = string.match(pattern)
|
189
373
|
|
190
374
|
if match
|
191
375
|
index = match.end(match.length - 1)
|
@@ -215,19 +399,19 @@ class String
|
|
215
399
|
#
|
216
400
|
# @api public
|
217
401
|
#
|
218
|
-
def pad(padding,max_length=
|
402
|
+
def pad(padding,max_length=length)
|
219
403
|
padding = padding.to_s
|
220
404
|
|
221
|
-
if max_length
|
222
|
-
max_length -=
|
405
|
+
if max_length > length
|
406
|
+
max_length -= length
|
223
407
|
else
|
224
408
|
max_length = 0
|
225
409
|
end
|
226
410
|
|
227
411
|
padded = self + (padding * (max_length / padding.length))
|
228
412
|
|
229
|
-
unless (remaining = max_length % padding.length) == 0
|
230
|
-
padded << padding[0
|
413
|
+
unless (remaining = (max_length % padding.length)) == 0
|
414
|
+
padded << padding[0,remaining]
|
231
415
|
end
|
232
416
|
|
233
417
|
return padded
|