escape_utils 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a15665f9653635141dfb582e5748299d6f31359f
4
- data.tar.gz: 8ed831486f3292a3dd31f20e2b460218bf50cfe5
3
+ metadata.gz: a359a0da23b56ac37301e4ae73060dcfc4870d14
4
+ data.tar.gz: 2a3c9ed36204fc638605f012c3ca5a87ccc756b3
5
5
  SHA512:
6
- metadata.gz: 587f6c30123a5478c3d1775b305cca518342e1d7b1ed0e882a6aeb225e1858812e4d0107b5b00ae8bd3f78a27ecf729217a02660ae58a099d044b634a64d05ec
7
- data.tar.gz: 1dc21ebae6acdef6bacea70b303ed8fc251c5ba479b729ad2bb2670396a9a423342fe136fc255d7fe4f8bb808ef4fa4baebb8962db9abd5f5323bd585372bdfa
6
+ metadata.gz: f862189c73f7858184a61a0e2ae78b2d344ac162022bd41ec77eb18eba6ae52a62e188d29b2b7ed2931ae47a4d6a33b2f8d6ecf82ef85e46ce1457e016897403
7
+ data.tar.gz: 8f4a4643def62dfd8fdb27d9d631aa452bca3e63e1672a70bcf95594f98040ccee00276d6c0abd8e90c11126b7932b6e0efcce9e7d0b2881523b6faed62cad1b
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Brian Lopez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,9 +1,8 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
7
  require 'rack'
9
8
  require 'erb'
@@ -16,53 +15,54 @@ module HamlBench
16
15
  extend Haml::Helpers
17
16
  end
18
17
 
19
- times = 100
20
18
  url = "http://en.wikipedia.org/wiki/Line_of_succession_to_the_British_throne"
21
19
  html = `curl -s #{url}`
22
20
  html = html.force_encoding('utf-8') if html.respond_to?(:force_encoding)
23
- puts "Escaping #{html.bytesize} bytes of html #{times} times, from #{url}"
21
+ puts "Escaping #{html.bytesize} bytes of html from #{url}"
24
22
 
25
- Benchmark.bmbm do |x|
26
- x.report "Rack::Utils.escape_html" do
23
+ Benchmark.ips do |x|
24
+ x.report "Rack::Utils.escape_html" do |times|
27
25
  times.times do
28
26
  Rack::Utils.escape_html(html)
29
27
  end
30
28
  end
31
29
 
32
- x.report "Haml::Helpers.html_escape" do
30
+ x.report "Haml::Helpers.html_escape" do |times|
33
31
  times.times do
34
32
  HamlBench.html_escape(html)
35
33
  end
36
34
  end
37
35
 
38
- x.report "ERB::Util.html_escape" do
36
+ x.report "ERB::Util.html_escape" do |times|
39
37
  times.times do
40
38
  ERB::Util.html_escape(html)
41
39
  end
42
40
  end
43
41
 
44
- x.report "CGI.escapeHTML" do
42
+ x.report "CGI.escapeHTML" do |times|
45
43
  times.times do
46
44
  CGI.escapeHTML(html)
47
45
  end
48
46
  end
49
47
 
50
- x.report "String#gsub" do
48
+ x.report "String#gsub" do |times|
51
49
  html_escape = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
52
50
  times.times do
53
51
  html.gsub(/[&"'><]/, html_escape)
54
52
  end
55
53
  end
56
54
 
57
- x.report "fast_xs_extra#fast_xs_html" do
55
+ x.report "fast_xs_extra#fast_xs_html" do |times|
58
56
  times.times do
59
57
  html.fast_xs_html
60
58
  end
61
59
  end
62
60
 
63
- x.report "EscapeUtils.escape_html" do
61
+ x.report "EscapeUtils.escape_html" do |times|
64
62
  times.times do
65
63
  EscapeUtils.escape_html(html)
66
64
  end
67
65
  end
66
+
67
+ x.compare!
68
68
  end
@@ -1,9 +1,8 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
7
  require 'cgi'
9
8
  require 'haml'
@@ -13,23 +12,24 @@ module HamlBench
13
12
  extend Haml::Helpers
14
13
  end
15
14
 
16
- times = 100
17
15
  url = "http://en.wikipedia.org/wiki/Line_of_succession_to_the_British_throne"
18
16
  html = `curl -s #{url}`
19
17
  html = html.force_encoding('binary') if html.respond_to?(:force_encoding)
20
18
  escaped_html = EscapeUtils.escape_html(html)
21
- puts "Unescaping #{escaped_html.bytesize} bytes of escaped html #{times} times, from #{url}"
19
+ puts "Unescaping #{escaped_html.bytesize} bytes of escaped html, from #{url}"
22
20
 
23
- Benchmark.bmbm do |x|
24
- x.report "CGI.unescapeHTML" do
21
+ Benchmark.ips do |x|
22
+ x.report "CGI.unescapeHTML" do |times|
25
23
  times.times do
26
24
  CGI.unescapeHTML(escaped_html)
27
25
  end
28
26
  end
29
27
 
30
- x.report "EscapeUtils.unescape_html" do
28
+ x.report "EscapeUtils.unescape_html" do |times|
31
29
  times.times do
32
30
  EscapeUtils.unescape_html(escaped_html)
33
31
  end
34
32
  end
35
- end
33
+
34
+ x.compare!
35
+ end
@@ -1,9 +1,8 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
7
  require 'action_view'
9
8
  require 'escape_utils'
@@ -12,22 +11,23 @@ class ActionPackBench
12
11
  extend ActionView::Helpers::JavaScriptHelper
13
12
  end
14
13
 
15
- times = 100
16
14
  url = "http://ajax.googleapis.com/ajax/libs/dojo/1.4.3/dojo/dojo.xd.js.uncompressed.js"
17
15
  javascript = `curl -s #{url}`
18
16
  javascript = javascript.force_encoding('utf-8') if javascript.respond_to?(:force_encoding)
19
- puts "Escaping #{javascript.bytesize} bytes of javascript #{times} times, from #{url}"
17
+ puts "Escaping #{javascript.bytesize} bytes of javascript, from #{url}"
20
18
 
21
- Benchmark.bmbm do |x|
22
- x.report "ActionView::Helpers::JavaScriptHelper#escape_javascript" do
19
+ Benchmark.ips do |x|
20
+ x.report "ActionView::Helpers::JavaScriptHelper#escape_javascript" do |times|
23
21
  times.times do
24
22
  ActionPackBench.escape_javascript(javascript)
25
23
  end
26
24
  end
27
25
 
28
- x.report "EscapeUtils.escape_javascript" do
26
+ x.report "EscapeUtils.escape_javascript" do |times|
29
27
  times.times do
30
28
  EscapeUtils.escape_javascript(javascript)
31
29
  end
32
30
  end
33
- end
31
+
32
+ x.compare!
33
+ end
@@ -1,23 +1,21 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
7
  require 'escape_utils'
9
8
 
10
- times = 100
11
9
  url = "http://ajax.googleapis.com/ajax/libs/dojo/1.4.3/dojo/dojo.xd.js.uncompressed.js"
12
10
  javascript = `curl -s #{url}`
13
11
  javascript = javascript.force_encoding('utf-8') if javascript.respond_to?(:force_encoding)
14
12
  escaped_javascript = EscapeUtils.escape_javascript(javascript)
15
- puts "Escaping #{escaped_javascript.bytesize} bytes of javascript #{times} times, from #{url}"
13
+ puts "Escaping #{escaped_javascript.bytesize} bytes of javascript, from #{url}"
16
14
 
17
- Benchmark.bmbm do |x|
18
- x.report "EscapeUtils.escape_javascript" do
15
+ Benchmark.ips do |x|
16
+ x.report "EscapeUtils.escape_javascript" do |times|
19
17
  times.times do
20
18
  EscapeUtils.unescape_javascript(escaped_javascript)
21
19
  end
22
20
  end
23
- end
21
+ end
@@ -1,9 +1,8 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
7
  require 'rack'
9
8
  require 'erb'
@@ -12,45 +11,46 @@ require 'url_escape'
12
11
  require 'fast_xs_extra'
13
12
  require 'escape_utils'
14
13
 
15
- times = 10_000
16
14
  url = "https://www.yourmom.com/cgi-bin/session.cgi?sess_args=mYHcEA dh435dqUs0moGHeeAJTSLLbdbcbd9ef----,574b95600e9ab7d27eb0bf524ac68c27----"
17
15
  url = url.force_encoding('us-ascii') if url.respond_to?(:force_encoding)
18
- puts "Escaping a #{url.bytesize} byte URL #{times} times"
16
+ puts "Escaping a #{url.bytesize} byte URL times"
19
17
 
20
- Benchmark.bmbm do |x|
21
- x.report "ERB::Util.url_encode" do
18
+ Benchmark.ips do |x|
19
+ x.report "ERB::Util.url_encode" do |times|
22
20
  times.times do
23
21
  ERB::Util.url_encode(url)
24
22
  end
25
23
  end
26
24
 
27
- x.report "Rack::Utils.escape" do
25
+ x.report "Rack::Utils.escape" do |times|
28
26
  times.times do
29
27
  Rack::Utils.escape(url)
30
28
  end
31
29
  end
32
30
 
33
- x.report "CGI.escape" do
31
+ x.report "CGI.escape" do |times|
34
32
  times.times do
35
33
  CGI.escape(url)
36
34
  end
37
35
  end
38
36
 
39
- x.report "URLEscape#escape" do
37
+ x.report "URLEscape#escape" do |times|
40
38
  times.times do
41
39
  URLEscape.escape(url)
42
40
  end
43
41
  end
44
42
 
45
- x.report "fast_xs_extra#fast_xs_url" do
43
+ x.report "fast_xs_extra#fast_xs_url" do |times|
46
44
  times.times do
47
45
  url.fast_xs_url
48
46
  end
49
47
  end
50
48
 
51
- x.report "EscapeUtils.escape_url" do
49
+ x.report "EscapeUtils.escape_url" do |times|
52
50
  times.times do
53
51
  EscapeUtils.escape_url(url)
54
52
  end
55
53
  end
56
- end
54
+
55
+ x.compare!
56
+ end
@@ -1,9 +1,8 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
7
  require 'rack'
9
8
  require 'cgi'
@@ -11,40 +10,41 @@ require 'url_escape'
11
10
  require 'fast_xs_extra'
12
11
  require 'escape_utils'
13
12
 
14
- times = 10_000
15
13
  url = "https://www.yourmom.com/cgi-bin/session.cgi?sess_args=mYHcEA dh435dqUs0moGHeeAJTSLLbdbcbd9ef----,574b95600e9ab7d27eb0bf524ac68c27----"
16
14
  url = url.force_encoding('us-ascii') if url.respond_to?(:force_encoding)
17
15
  escaped_url = EscapeUtils.escape_url(url)
18
- puts "Escaping a #{url.bytesize} byte URL #{times} times"
16
+ puts "Escaping a #{url.bytesize} byte URL"
19
17
 
20
- Benchmark.bmbm do |x|
21
- x.report "Rack::Utils.unescape" do
18
+ Benchmark.ips do |x|
19
+ x.report "Rack::Utils.unescape" do |times|
22
20
  times.times do
23
21
  Rack::Utils.unescape(escaped_url)
24
22
  end
25
23
  end
26
-
27
- x.report "CGI.unescape" do
24
+
25
+ x.report "CGI.unescape" do |times|
28
26
  times.times do
29
27
  CGI.unescape(escaped_url)
30
28
  end
31
29
  end
32
-
33
- x.report "URLEscape#unescape" do
30
+
31
+ x.report "URLEscape#unescape" do |times|
34
32
  times.times do
35
33
  URLEscape.unescape(escaped_url)
36
34
  end
37
35
  end
38
36
 
39
- x.report "fast_xs_extra#fast_uxs_cgi" do
37
+ x.report "fast_xs_extra#fast_uxs_cgi" do |times|
40
38
  times.times do
41
39
  url.fast_uxs_cgi
42
40
  end
43
41
  end
44
42
 
45
- x.report "EscapeUtils.unescape_url" do
43
+ x.report "EscapeUtils.unescape_url" do |times|
46
44
  times.times do
47
45
  EscapeUtils.unescape_url(escaped_url)
48
46
  end
49
47
  end
50
- end
48
+
49
+ x.compare!
50
+ end
@@ -1,29 +1,29 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  require 'rubygems'
6
- require 'benchmark'
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
7
6
 
8
- require 'builder'
7
+ require 'fast_xs'
9
8
  require 'escape_utils'
10
9
 
11
- times = 100
12
10
  url = "http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml"
13
11
  xml = `curl -s #{url}`
14
12
  xml = xml.force_encoding('binary') if xml.respond_to?(:force_encoding)
15
- puts "Escaping #{xml.bytesize} bytes of xml #{times} times, from #{url}"
13
+ puts "Escaping #{xml.bytesize} bytes of xml, from #{url}"
16
14
 
17
- Benchmark.bmbm do |x|
18
- x.report "Builder::String.to_xs" do
15
+ Benchmark.ips do |x|
16
+ x.report "fast_xs" do |times|
19
17
  times.times do
20
- xml.to_xs
18
+ xml.fast_xs
21
19
  end
22
20
  end
23
21
 
24
- x.report "EscapeUtils.escape_xml" do
22
+ x.report "EscapeUtils.escape_xml" do |times|
25
23
  times.times do
26
24
  EscapeUtils.escape_xml(xml)
27
25
  end
28
26
  end
27
+
28
+ x.compare!
29
29
  end
@@ -22,10 +22,10 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency 'rake-compiler', ">= 0.7.5"
23
23
  s.add_development_dependency 'minitest', ">= 5.0.0"
24
24
  # benchmarks
25
+ s.add_development_dependency 'benchmark-ips'
25
26
  s.add_development_dependency 'rack'
26
27
  s.add_development_dependency 'haml'
27
28
  s.add_development_dependency 'fast_xs'
28
29
  s.add_development_dependency 'actionpack'
29
30
  s.add_development_dependency 'url_escape'
30
31
  end
31
-
@@ -242,7 +242,7 @@ void gh_buf_attach(gh_buf *buf, char *ptr, size_t asize)
242
242
 
243
243
  int gh_buf_cmp(const gh_buf *a, const gh_buf *b)
244
244
  {
245
- int result = memcmp(a->ptr, b->ptr, (a->size < b->size) ? a->size : b->size);
245
+ int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
246
246
  return (result != 0) ? result :
247
247
  (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
248
248
  }
@@ -202,6 +202,19 @@ static VALUE rb_eu_unescape_uri(VALUE self, VALUE str)
202
202
  return rb_eu__generic(str, &houdini_unescape_uri);
203
203
  }
204
204
 
205
+ /**
206
+ * URI component methods
207
+ */
208
+ static VALUE rb_eu_escape_uri_component(VALUE self, VALUE str)
209
+ {
210
+ return rb_eu__generic(str, &houdini_escape_uri_component);
211
+ }
212
+
213
+ static VALUE rb_eu_unescape_uri_component(VALUE self, VALUE str)
214
+ {
215
+ return rb_eu__generic(str, &houdini_unescape_uri_component);
216
+ }
217
+
205
218
 
206
219
  /**
207
220
  * Ruby Extension initializer
@@ -227,6 +240,8 @@ void Init_escape_utils()
227
240
  rb_define_method(rb_mEscapeUtils, "unescape_url", rb_eu_unescape_url, 1);
228
241
  rb_define_method(rb_mEscapeUtils, "escape_uri", rb_eu_escape_uri, 1);
229
242
  rb_define_method(rb_mEscapeUtils, "unescape_uri", rb_eu_unescape_uri, 1);
243
+ rb_define_method(rb_mEscapeUtils, "escape_uri_component", rb_eu_escape_uri_component, 1);
244
+ rb_define_method(rb_mEscapeUtils, "unescape_uri_component", rb_eu_unescape_uri_component, 1);
230
245
 
231
246
  rb_define_singleton_method(rb_mEscapeUtils, "html_secure=", rb_eu_set_html_secure, 1);
232
247
  rb_define_singleton_method(rb_mEscapeUtils, "html_safe_string_class=", rb_eu_set_html_safe_string_class, 1);
@@ -30,9 +30,11 @@ extern int houdini_escape_html0(gh_buf *ob, const uint8_t *src, size_t size, int
30
30
  extern int houdini_unescape_html(gh_buf *ob, const uint8_t *src, size_t size);
31
31
  extern int houdini_escape_xml(gh_buf *ob, const uint8_t *src, size_t size);
32
32
  extern int houdini_escape_uri(gh_buf *ob, const uint8_t *src, size_t size);
33
+ extern int houdini_escape_uri_component(gh_buf *ob, const uint8_t *src, size_t size);
33
34
  extern int houdini_escape_url(gh_buf *ob, const uint8_t *src, size_t size);
34
35
  extern int houdini_escape_href(gh_buf *ob, const uint8_t *src, size_t size);
35
36
  extern int houdini_unescape_uri(gh_buf *ob, const uint8_t *src, size_t size);
37
+ extern int houdini_unescape_uri_component(gh_buf *ob, const uint8_t *src, size_t size);
36
38
  extern int houdini_unescape_url(gh_buf *ob, const uint8_t *src, size_t size);
37
39
  extern int houdini_escape_js(gh_buf *ob, const uint8_t *src, size_t size);
38
40
  extern int houdini_unescape_js(gh_buf *ob, const uint8_t *src, size_t size);
@@ -57,7 +57,7 @@ unescape_ent(gh_buf *ob, const uint8_t *src, size_t size)
57
57
  codepoint = (codepoint * 16) + ((src[i] | 32) % 39 - 9);
58
58
  }
59
59
 
60
- if (i < size && src[i] == ';') {
60
+ if (i < size && src[i] == ';' && codepoint) {
61
61
  gh_buf_put_utf8(ob, codepoint);
62
62
  return i + 1;
63
63
  }
@@ -43,10 +43,10 @@ static const char URI_SAFE[] = {
43
43
  };
44
44
 
45
45
  static int
46
- escape(gh_buf *ob, const uint8_t *src, size_t size, int is_url)
46
+ escape(gh_buf *ob, const uint8_t *src, size_t size,
47
+ const char *safe_table, bool escape_plus)
47
48
  {
48
49
  static const uint8_t hex_chars[] = "0123456789ABCDEF";
49
- const char *safe_table = is_url ? URL_SAFE : URI_SAFE;
50
50
 
51
51
  size_t i = 0, org;
52
52
  uint8_t hex_str[3];
@@ -73,7 +73,7 @@ escape(gh_buf *ob, const uint8_t *src, size_t size, int is_url)
73
73
  if (i >= size)
74
74
  break;
75
75
 
76
- if (src[i] == ' ' && is_url) {
76
+ if (src[i] == ' ' && escape_plus) {
77
77
  gh_buf_putc(ob, '+');
78
78
  } else {
79
79
  hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
@@ -90,12 +90,18 @@ escape(gh_buf *ob, const uint8_t *src, size_t size, int is_url)
90
90
  int
91
91
  houdini_escape_uri(gh_buf *ob, const uint8_t *src, size_t size)
92
92
  {
93
- return escape(ob, src, size, 0);
93
+ return escape(ob, src, size, URI_SAFE, false);
94
+ }
95
+
96
+ int
97
+ houdini_escape_uri_component(gh_buf *ob, const uint8_t *src, size_t size)
98
+ {
99
+ return escape(ob, src, size, URL_SAFE, false);
94
100
  }
95
101
 
96
102
  int
97
103
  houdini_escape_url(gh_buf *ob, const uint8_t *src, size_t size)
98
104
  {
99
- return escape(ob, src, size, 1);
105
+ return escape(ob, src, size, URL_SAFE, true);
100
106
  }
101
107
 
@@ -7,18 +7,18 @@
7
7
  #define hex2c(c) ((c | 32) % 39 - 9)
8
8
 
9
9
  static int
10
- unescape(gh_buf *ob, const uint8_t *src, size_t size, int is_url)
10
+ unescape(gh_buf *ob, const uint8_t *src, size_t size, bool unescape_plus)
11
11
  {
12
12
  size_t i = 0, org;
13
13
 
14
14
  while (i < size) {
15
15
  org = i;
16
- while (i < size && src[i] != '%')
16
+ while (i < size && src[i] != '%' && src[i] != '+')
17
17
  i++;
18
18
 
19
19
  if (likely(i > org)) {
20
20
  if (unlikely(org == 0)) {
21
- if (i >= size && !is_url)
21
+ if (i >= size)
22
22
  return 0;
23
23
 
24
24
  gh_buf_grow(ob, HOUDINI_UNESCAPED_SIZE(size));
@@ -31,7 +31,10 @@ unescape(gh_buf *ob, const uint8_t *src, size_t size, int is_url)
31
31
  if (i >= size)
32
32
  break;
33
33
 
34
- i++;
34
+ if (src[i++] == '+') {
35
+ gh_buf_putc(ob, unescape_plus ? ' ' : '+');
36
+ continue;
37
+ }
35
38
 
36
39
  if (i + 1 < size && _isxdigit(src[i]) && _isxdigit(src[i + 1])) {
37
40
  unsigned char new_char = (hex2c(src[i]) << 4) + hex2c(src[i + 1]);
@@ -42,24 +45,24 @@ unescape(gh_buf *ob, const uint8_t *src, size_t size, int is_url)
42
45
  }
43
46
  }
44
47
 
45
- if (is_url) {
46
- char *find = (char *)gh_buf_cstr(ob);
47
- while ((find = strchr(find, '+')) != NULL)
48
- *find = ' ';
49
- }
50
-
51
48
  return 1;
52
49
  }
53
50
 
54
51
  int
55
52
  houdini_unescape_uri(gh_buf *ob, const uint8_t *src, size_t size)
56
53
  {
57
- return unescape(ob, src, size, 0);
54
+ return unescape(ob, src, size, false);
55
+ }
56
+
57
+ int
58
+ houdini_unescape_uri_component(gh_buf *ob, const uint8_t *src, size_t size)
59
+ {
60
+ return unescape(ob, src, size, false);
58
61
  }
59
62
 
60
63
  int
61
64
  houdini_unescape_url(gh_buf *ob, const uint8_t *src, size_t size)
62
65
  {
63
- return unescape(ob, src, size, 1);
66
+ return unescape(ob, src, size, true);
64
67
  }
65
68
 
@@ -1,3 +1,3 @@
1
1
  module EscapeUtils
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -25,6 +25,14 @@ class UriEscapeTest < Minitest::Test
25
25
  assert_equal '%E3%81%BE%E3%81%A4%20%E3%82%82%E3%81%A8', EscapeUtils.escape_uri(matz_name_sep)
26
26
  end
27
27
 
28
+ def test_uri_containing_pluses
29
+ assert_equal "a+plus", EscapeUtils.escape_uri("a+plus")
30
+ end
31
+
32
+ def test_uri_containing_slashes
33
+ assert_equal "a/slash", EscapeUtils.escape_uri("a/slash")
34
+ end
35
+
28
36
  if RUBY_VERSION =~ /^1.9/
29
37
  def test_input_must_be_utf8_or_ascii
30
38
  str = "fo<o>bar"
@@ -30,18 +30,29 @@ class UriUnescapeTest < Minitest::Test
30
30
  assert_equal matz_name_sep, EscapeUtils.unescape_uri('%E3%81%BE%E3%81%A4%20%E3%82%82%E3%81%A8')
31
31
  end
32
32
 
33
+ def test_uri_containing_pluses
34
+ assert_equal "a+plus", EscapeUtils.unescape_uri("a%2Bplus")
35
+ end
36
+
37
+ def test_escape_unescape_roundtrip
38
+ (0..127).each do |index|
39
+ char = index.chr
40
+ assert_equal char, EscapeUtils.unescape_uri(EscapeUtils.escape_uri(char))
41
+ end
42
+ end
43
+
33
44
  if RUBY_VERSION =~ /^1.9/
34
45
  def test_input_must_be_valid_utf8_or_ascii
35
46
  escaped = EscapeUtils.escape_uri("fo<o>bar")
36
47
 
37
48
  escaped.force_encoding 'ISO-8859-1'
38
49
  assert_raises Encoding::CompatibilityError do
39
- EscapeUtils.unescape_url(escaped)
50
+ EscapeUtils.unescape_uri(escaped)
40
51
  end
41
52
 
42
53
  escaped.force_encoding 'UTF-8'
43
54
  begin
44
- EscapeUtils.unescape_url(escaped)
55
+ EscapeUtils.unescape_uri(escaped)
45
56
  rescue Encoding::CompatibilityError => e
46
57
  assert_nil e, "#{e.class.name} raised, expected not to"
47
58
  end
@@ -49,7 +60,7 @@ class UriUnescapeTest < Minitest::Test
49
60
 
50
61
  def test_return_value_is_tagged_as_utf8
51
62
  escaped = EscapeUtils.escape_uri("a space")
52
- assert_equal Encoding.find('UTF-8'), EscapeUtils.unescape_url(escaped).encoding
63
+ assert_equal Encoding.find('UTF-8'), EscapeUtils.unescape_uri(escaped).encoding
53
64
  end
54
65
  end
55
66
  end
@@ -0,0 +1,68 @@
1
+ require File.expand_path("../../helper", __FILE__)
2
+ require 'cgi'
3
+
4
+ class UriComponentEscapeTest < Minitest::Test
5
+ def test_basic_url
6
+ assert_equal "http%3A%2F%2Fwww.homerun.com%2F", EscapeUtils.escape_uri_component("http://www.homerun.com/")
7
+ end
8
+
9
+ def test_cgi_equivalence
10
+ (0..127).each do |i|
11
+ c = i.chr
12
+ # Escaping URI path components should match CGI parameter escaping, except
13
+ # spaces should be escaped as "%20" instead of "+"
14
+ assert_equal CGI.escape(c).sub("+", "%20"), EscapeUtils.escape_uri_component(c)
15
+ end
16
+ end
17
+
18
+ def test_uri_component_containing_tags
19
+ assert_equal "fo%3Co%3Ebar", EscapeUtils.escape_uri_component("fo<o>bar")
20
+ end
21
+
22
+ def test_uri_component_containing_tags_containing_spaces
23
+ assert_equal "a%20space", EscapeUtils.escape_uri_component("a space")
24
+ assert_equal "a%20%20%20sp%20ace%20", EscapeUtils.escape_uri_component("a sp ace ")
25
+ end
26
+
27
+ def test_uri_component_containing_mixed_characters
28
+ assert_equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C", EscapeUtils.escape_uri_component("q1!2\"'w$5&7/z8)?\\")
29
+ end
30
+
31
+ def test_multibyte_characters
32
+ matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8" # Matsumoto
33
+ assert_equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8', EscapeUtils.escape_uri_component(matz_name)
34
+ matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8" # Matsu moto
35
+ assert_equal '%E3%81%BE%E3%81%A4%20%E3%82%82%E3%81%A8', EscapeUtils.escape_uri_component(matz_name_sep)
36
+ end
37
+
38
+ def test_uri_component_containing_pluses
39
+ assert_equal "a%2Bplus", EscapeUtils.escape_uri_component("a+plus")
40
+ end
41
+
42
+ def test_uri_component_containing_slashes
43
+ assert_equal "a%2Fslash", EscapeUtils.escape_uri_component("a/slash")
44
+ end
45
+
46
+ if RUBY_VERSION =~ /^1.9/
47
+ def test_input_must_be_utf8_or_ascii
48
+ str = "fo<o>bar"
49
+
50
+ str.force_encoding 'ISO-8859-1'
51
+ assert_raises Encoding::CompatibilityError do
52
+ EscapeUtils.escape_uri_component(str)
53
+ end
54
+
55
+ str.force_encoding 'UTF-8'
56
+ begin
57
+ EscapeUtils.escape_uri_component(str)
58
+ rescue Encoding::CompatibilityError => e
59
+ assert_nil e, "#{e.class.name} raised, expected not to"
60
+ end
61
+ end
62
+
63
+ def test_return_value_is_tagged_as_utf8
64
+ str = "fo<o>bar"
65
+ assert_equal Encoding.find('UTF-8'), EscapeUtils.escape_uri_component(str).encoding
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,71 @@
1
+ require File.expand_path("../../helper", __FILE__)
2
+
3
+ class UriComponentUnescapeTest < Minitest::Test
4
+ def test_basic_url
5
+ assert_equal "http://www.homerun.com/", EscapeUtils.unescape_uri_component("http%3A%2F%2Fwww.homerun.com%2F")
6
+ assert_equal "http://www.homerun.com/", EscapeUtils.unescape_uri_component("http://www.homerun.com/")
7
+ end
8
+
9
+ def test_doesnt_unescape_an_incomplete_escape
10
+ assert_equal "%", EscapeUtils.unescape_uri_component("%")
11
+ assert_equal "http%", EscapeUtils.unescape_uri_component("http%")
12
+ end
13
+
14
+ def test_uri_component_containing_tags
15
+ assert_equal "fo<o>bar", EscapeUtils.unescape_uri_component("fo%3Co%3Ebar")
16
+ end
17
+
18
+ def test_uri_component_containing_spaces
19
+ assert_equal "a space", EscapeUtils.unescape_uri_component("a%20space")
20
+ assert_equal "a sp ace ", EscapeUtils.unescape_uri_component("a%20%20%20sp%20ace%20")
21
+ end
22
+
23
+ def test_uri_component_containing_pluses
24
+ assert_equal "a+plus", EscapeUtils.unescape_uri_component("a%2Bplus")
25
+ assert_equal "a+plus", EscapeUtils.unescape_uri_component("a+plus")
26
+ end
27
+
28
+ def test_escape_unescape_roundtrip
29
+ (0..127).each do |index|
30
+ char = index.chr
31
+ assert_equal char, EscapeUtils.unescape_uri_component(EscapeUtils.escape_uri_component(char))
32
+ end
33
+ end
34
+
35
+ def test_uri_component_containing_mixed_characters
36
+ assert_equal "q1!2\"'w$5&7/z8)?\\", EscapeUtils.unescape_uri_component("q1%212%22%27w%245%267%2Fz8%29%3F%5C")
37
+ assert_equal "q1!2\"'w$5&7/z8)?\\", EscapeUtils.unescape_uri_component("q1!2%22'w$5&7/z8)?%5C")
38
+ end
39
+
40
+ def test_multibyte_characters
41
+ matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8" # Matsumoto
42
+ matz_name.force_encoding('UTF-8') if matz_name.respond_to?(:force_encoding)
43
+ assert_equal matz_name, EscapeUtils.unescape_uri_component('%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8')
44
+ matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8" # Matsu moto
45
+ matz_name_sep.force_encoding('UTF-8') if matz_name_sep.respond_to?(:force_encoding)
46
+ assert_equal matz_name_sep, EscapeUtils.unescape_uri_component('%E3%81%BE%E3%81%A4%20%E3%82%82%E3%81%A8')
47
+ end
48
+
49
+ if RUBY_VERSION =~ /^1.9/
50
+ def test_input_must_be_valid_utf8_or_ascii
51
+ escaped = EscapeUtils.escape_uri_component("fo<o>bar")
52
+
53
+ escaped.force_encoding 'ISO-8859-1'
54
+ assert_raises Encoding::CompatibilityError do
55
+ EscapeUtils.unescape_uri_component(escaped)
56
+ end
57
+
58
+ escaped.force_encoding 'UTF-8'
59
+ begin
60
+ EscapeUtils.unescape_uri_component(escaped)
61
+ rescue Encoding::CompatibilityError => e
62
+ assert_nil e, "#{e.class.name} raised, expected not to"
63
+ end
64
+ end
65
+
66
+ def test_return_value_is_tagged_as_utf8
67
+ escaped = EscapeUtils.escape_uri_component("fo<o>bar")
68
+ assert_equal Encoding.find('UTF-8'), EscapeUtils.unescape_uri_component(escaped).encoding
69
+ end
70
+ end
71
+ end
@@ -1,7 +1,7 @@
1
1
  require File.expand_path("../../helper", __FILE__)
2
2
  require 'cgi'
3
3
 
4
- class UriEscapeTest < Minitest::Test
4
+ class UrlEscapeTest < Minitest::Test
5
5
  def test_basic_url
6
6
  assert_equal "http%3A%2F%2Fwww.homerun.com%2F", EscapeUtils.escape_url("http://www.homerun.com/")
7
7
  end
@@ -33,6 +33,14 @@ class UriEscapeTest < Minitest::Test
33
33
  assert_equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8', EscapeUtils.escape_url(matz_name_sep)
34
34
  end
35
35
 
36
+ def test_url_containing_pluses
37
+ assert_equal "a%2Bplus", EscapeUtils.escape_url("a+plus")
38
+ end
39
+
40
+ def test_url_containing_slashes
41
+ assert_equal "a%2Fslash", EscapeUtils.escape_url("a/slash")
42
+ end
43
+
36
44
  if RUBY_VERSION =~ /^1.9/
37
45
  def test_input_must_be_utf8_or_ascii
38
46
  str = "fo<o>bar"
@@ -1,6 +1,6 @@
1
1
  require File.expand_path("../../helper", __FILE__)
2
2
 
3
- class UriUnescapeTest < Minitest::Test
3
+ class UrlUnescapeTest < Minitest::Test
4
4
  def test_basic_url
5
5
  assert_equal "http://www.homerun.com/", EscapeUtils.unescape_url("http%3A%2F%2Fwww.homerun.com%2F")
6
6
  assert_equal "http://www.homerun.com/", EscapeUtils.unescape_url("http://www.homerun.com/")
@@ -35,9 +35,20 @@ class UriUnescapeTest < Minitest::Test
35
35
  assert_equal matz_name_sep, EscapeUtils.unescape_url('%E3%81%BE%E3%81%A4%20%E3%82%82%E3%81%A8')
36
36
  end
37
37
 
38
+ def test_url_containing_pluses
39
+ assert_equal "a+plus", EscapeUtils.unescape_url("a%2Bplus")
40
+ end
41
+
42
+ def test_escape_unescape_roundtrip
43
+ (0..127).each do |index|
44
+ char = index.chr
45
+ assert_equal char, EscapeUtils.unescape_url(EscapeUtils.escape_url(char))
46
+ end
47
+ end
48
+
38
49
  if RUBY_VERSION =~ /^1.9/
39
50
  def test_input_must_be_valid_utf8_or_ascii
40
- escaped = EscapeUtils.escape_uri("fo<o>bar")
51
+ escaped = EscapeUtils.escape_url("fo<o>bar")
41
52
 
42
53
  escaped.force_encoding 'ISO-8859-1'
43
54
  assert_raises Encoding::CompatibilityError do
@@ -53,7 +64,7 @@ class UriUnescapeTest < Minitest::Test
53
64
  end
54
65
 
55
66
  def test_return_value_is_tagged_as_utf8
56
- escaped = EscapeUtils.escape_uri("fo<o>bar")
67
+ escaped = EscapeUtils.escape_url("fo<o>bar")
57
68
  assert_equal Encoding.find('UTF-8'), EscapeUtils.unescape_url(escaped).encoding
58
69
  end
59
70
  end
metadata CHANGED
@@ -1,111 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: escape_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Lopez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-10 00:00:00.000000000 Z
11
+ date: 2015-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.7.5
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.7.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 5.0.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 5.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: benchmark-ips
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rack
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - '>='
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - '>='
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: haml
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '>='
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '>='
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: fast_xs
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - '>='
87
+ - - ">="
74
88
  - !ruby/object:Gem::Version
75
89
  version: '0'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - '>='
94
+ - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: actionpack
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
- - - '>='
101
+ - - ">="
88
102
  - !ruby/object:Gem::Version
89
103
  version: '0'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
- - - '>='
108
+ - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: url_escape
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - '>='
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - '>='
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  description: Quickly perform HTML, URL, URI and Javascript escaping/unescaping
@@ -115,10 +129,10 @@ extensions:
115
129
  - ext/escape_utils/extconf.rb
116
130
  extra_rdoc_files: []
117
131
  files:
118
- - .gitignore
119
- - .travis.yml
132
+ - ".gitignore"
133
+ - ".travis.yml"
120
134
  - Gemfile
121
- - MIT-LICENSE
135
+ - LICENSE
122
136
  - README.md
123
137
  - Rakefile
124
138
  - benchmark/html_escape.rb
@@ -167,6 +181,8 @@ files:
167
181
  - test/query/unescape_test.rb
168
182
  - test/uri/escape_test.rb
169
183
  - test/uri/unescape_test.rb
184
+ - test/uri_component/escape_test.rb
185
+ - test/uri_component/unescape_test.rb
170
186
  - test/url/escape_test.rb
171
187
  - test/url/unescape_test.rb
172
188
  - test/xml/escape_test.rb
@@ -176,22 +192,22 @@ licenses:
176
192
  metadata: {}
177
193
  post_install_message:
178
194
  rdoc_options:
179
- - --charset=UTF-8
195
+ - "--charset=UTF-8"
180
196
  require_paths:
181
197
  - lib
182
198
  required_ruby_version: !ruby/object:Gem::Requirement
183
199
  requirements:
184
- - - '>='
200
+ - - ">="
185
201
  - !ruby/object:Gem::Version
186
202
  version: 1.9.3
187
203
  required_rubygems_version: !ruby/object:Gem::Requirement
188
204
  requirements:
189
- - - '>='
205
+ - - ">="
190
206
  - !ruby/object:Gem::Version
191
207
  version: '0'
192
208
  requirements: []
193
209
  rubyforge_project:
194
- rubygems_version: 2.0.3
210
+ rubygems_version: 2.2.2
195
211
  signing_key:
196
212
  specification_version: 4
197
213
  summary: Faster string escaping routines for your web apps
@@ -206,6 +222,8 @@ test_files:
206
222
  - test/query/unescape_test.rb
207
223
  - test/uri/escape_test.rb
208
224
  - test/uri/unescape_test.rb
225
+ - test/uri_component/escape_test.rb
226
+ - test/uri_component/unescape_test.rb
209
227
  - test/url/escape_test.rb
210
228
  - test/url/unescape_test.rb
211
229
  - test/xml/escape_test.rb
@@ -1,20 +0,0 @@
1
- Copyright (c) 2010-2013 Brian Lopez - http://github.com/brianmario
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.