ronin-support 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.gitignore +11 -0
  2. data/ChangeLog.md +42 -1
  3. data/README.md +4 -1
  4. data/gemspec.yml +2 -1
  5. data/lib/ronin/extensions.rb +2 -0
  6. data/lib/ronin/extensions/enumerable.rb +54 -0
  7. data/lib/ronin/extensions/file.rb +70 -2
  8. data/lib/ronin/extensions/ip_addr.rb +45 -45
  9. data/lib/ronin/extensions/regexp.rb +45 -0
  10. data/lib/ronin/extensions/resolv.rb +80 -0
  11. data/lib/ronin/extensions/string.rb +35 -32
  12. data/lib/ronin/formatting/extensions/binary/integer.rb +12 -5
  13. data/lib/ronin/formatting/extensions/binary/string.rb +44 -16
  14. data/lib/ronin/formatting/extensions/html/integer.rb +51 -31
  15. data/lib/ronin/formatting/extensions/html/string.rb +50 -31
  16. data/lib/ronin/formatting/extensions/http/integer.rb +10 -2
  17. data/lib/ronin/formatting/extensions/sql.rb +20 -0
  18. data/lib/ronin/formatting/extensions/sql/string.rb +98 -0
  19. data/lib/ronin/formatting/extensions/text/array.rb +11 -9
  20. data/lib/ronin/formatting/extensions/text/string.rb +213 -29
  21. data/lib/ronin/formatting/sql.rb +20 -0
  22. data/lib/ronin/network/extensions/http.rb +1 -0
  23. data/lib/ronin/network/extensions/http/net.rb +2 -2
  24. data/lib/ronin/network/extensions/http/uri/http.rb +226 -0
  25. data/lib/ronin/network/extensions/imap/net.rb +1 -1
  26. data/lib/ronin/network/extensions/ssl/net.rb +7 -1
  27. data/lib/ronin/network/http/proxy.rb +20 -21
  28. data/lib/ronin/network/mixins.rb +27 -0
  29. data/lib/ronin/network/mixins/esmtp.rb +165 -0
  30. data/lib/ronin/network/mixins/http.rb +723 -0
  31. data/lib/ronin/network/mixins/imap.rb +151 -0
  32. data/lib/ronin/network/mixins/pop3.rb +141 -0
  33. data/lib/ronin/network/mixins/smtp.rb +159 -0
  34. data/lib/ronin/network/mixins/tcp.rb +331 -0
  35. data/lib/ronin/network/mixins/telnet.rb +199 -0
  36. data/lib/ronin/network/mixins/udp.rb +227 -0
  37. data/lib/ronin/network/ssl.rb +17 -11
  38. data/lib/ronin/path.rb +3 -3
  39. data/lib/ronin/spec/ui/output.rb +28 -0
  40. data/lib/ronin/support.rb +3 -0
  41. data/lib/ronin/support/version.rb +1 -1
  42. data/lib/ronin/ui/output.rb +21 -0
  43. data/lib/ronin/ui/output/helpers.rb +248 -0
  44. data/lib/ronin/ui/output/output.rb +146 -0
  45. data/lib/ronin/ui/output/terminal.rb +21 -0
  46. data/lib/ronin/ui/output/terminal/color.rb +118 -0
  47. data/lib/ronin/ui/output/terminal/raw.rb +103 -0
  48. data/lib/ronin/ui/shell.rb +219 -0
  49. data/ronin-support.gemspec +1 -1
  50. data/spec/extensions/enumerable_spec.rb +24 -0
  51. data/spec/extensions/file_spec.rb +39 -0
  52. data/spec/extensions/ip_addr_spec.rb +6 -0
  53. data/spec/extensions/resolv_spec.rb +18 -0
  54. data/spec/formatting/html/integer_spec.rb +2 -2
  55. data/spec/formatting/html/string_spec.rb +1 -1
  56. data/spec/formatting/sql/string_spec.rb +55 -0
  57. data/spec/formatting/text/string_spec.rb +110 -0
  58. data/spec/network/ssl_spec.rb +10 -4
  59. data/spec/ui/classes/test_shell.rb +22 -0
  60. data/spec/ui/output_spec.rb +32 -0
  61. data/spec/ui/shell_spec.rb +79 -0
  62. 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
+ # # => "&#97;&#98;&#99;"
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
- # String#ord was not backported to Rub 1.8.7
88
- format_chars(options) { |c| c[0].format_html }
89
- else
90
- format_chars(options) { |c| c.ord.format_html }
91
- end
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
- # # => "%u0068%u0065%u006C%u006C%u006F"
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
- # String#ord was not backported to Rub 1.8.7
116
- format_chars(options) { |c| c[0].js_escape }
117
- else
118
- format_chars(options) { |c| c.ord.js_escape }
119
- end
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
- # "%u0068%u0065%u006C%u006C%u006F world".js_unescape
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
- if c.length == 6
143
- unescaped << c[2,4].to_i(16)
144
- elsif c.length == 3
145
- unescaped << c[1,2].to_i(16)
146
- elsif c.length == 2
147
- unescaped << JS_BACKSLASHED_CHARS[c]
148
- else
149
- unescaped << c
150
- end
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
- # # => "%u0068%u0065%u006C%u006C%u006F"
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
- # String#ord was not backported to Rub 1.8.7
178
- format_chars(options) { |c| c[0].format_js }
179
- else
180
- format_chars(options) { |c| c.ord.format_js }
181
- end
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(self.chr)
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(self.chr)
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
- self.inject([]) do |accum,elem|
41
- if elem.kind_of?(Integer)
42
- accum << elem
40
+ bytes = []
41
+
42
+ each do |element|
43
+ if element.kind_of?(Integer)
44
+ bytes << element
43
45
  else
44
- elem.to_s.each_byte { |b| accum << b }
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 = self.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 = self.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 = self.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 = options.fetch(:include,(0x00..0xff))
50
- excluded = options.fetch(:exclude,Set[])
51
-
233
+ included = (options[:include] || (0x00..0xff))
234
+ excluded = (options[:exclude] || Set[])
52
235
  formatted = ''
53
236
 
54
- self.each_byte do |b|
55
- if (included.include?(b) && !excluded.include?(b))
56
- formatted << yield(b)
57
- else
58
- formatted << b
59
- end
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 = options.fetch(:include,/./m)
91
- excluded = options.fetch(:exclude,Set[])
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
- self.each_char do |c|
106
- if (matches[included,c] && !matches[excluded,c])
107
- formatted << yield(c)
108
- else
109
- formatted << c
110
- end
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 = self.dup
166
- index = string.index(pattern)
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 = self.dup
188
- match = string.match(pattern)
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=self.length)
402
+ def pad(padding,max_length=length)
219
403
  padding = padding.to_s
220
404
 
221
- if max_length >= self.length
222
- max_length -= self.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...remaining]
413
+ unless (remaining = (max_length % padding.length)) == 0
414
+ padded << padding[0,remaining]
231
415
  end
232
416
 
233
417
  return padded