rsutphin-cf_case_check 0.0.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,25 @@
1
+ 0.1.2 / 2009-09-23
2
+ ==================
3
+ * Ignore createObject("java", ...) calls (instead of warning about them)
4
+
5
+ 0.1.1 / 2009-09-23
6
+ ==================
7
+ * Support unquoted attributes in tags
8
+ * Do not abort on an analysis exception -- print the exception and continue
9
+ with the next file
10
+
11
+ 0.1.0 / 2008-12-15
12
+ ==================
13
+ * Add support for configurable substitutions for, e.g., application variable
14
+ use in references
15
+ * Include 'tem' (along with 'cfm' and 'cfc') in the list of extensions to use
16
+ when looking for CF source
17
+ * Ensure that source filenames are globbed case-insensitively
18
+
19
+ 0.0.2 / 2008-12-15
20
+ ==================
21
+ * Resolve CFCs in the same directory as the source
22
+
1
23
  0.0.1 / 2008-12-14
2
24
  ==================
3
25
  * Fix recursion bug in CaseCheck::exit
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ PROJ.url = 'http://github.com/rsutphin/cf_case_check'
22
22
  PROJ.version = CaseCheck::VERSION
23
23
  # PROJ.rubyforge.name = 'cf_case_check'
24
24
  PROJ.description = "A utility which walks a ColdFusion application's source and determines which includes, custom tags, etc, will not work with a case-sensitive filesystem"
25
- PROJ.exclude << "gem$" << "gemspec$"
25
+ PROJ.exclude << "gem$" << "gemspec$" << ".DS*" << '.git*'
26
26
 
27
27
  PROJ.ruby_opts = [] # There are a bunch of warnings in rspec, so setting -w isn't useful
28
28
  PROJ.spec.opts << '--color'
@@ -33,6 +33,7 @@ PROJ.gem.dependencies << 'activesupport'
33
33
  desc 'Regenerate the gemspec for github'
34
34
  task :'gem:spec' => 'gem:prereqs' do
35
35
  PROJ.gem._spec.files = PROJ.gem._spec.files.reject { |f| f =~ /^tasks/ }
36
+ PROJ.gem._spec.rubyforge_project = nil
36
37
  File.open("#{PROJ.name}.gemspec", 'w') do |gemspec|
37
38
  gemspec.puts PROJ.gem._spec.to_ruby
38
39
  end
data/lib/case_check.rb CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  module CaseCheck
3
3
  # :stopdoc:
4
- VERSION = '0.0.1'.freeze
4
+ VERSION = '0.1.2'.freeze
5
5
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
6
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
7
  # :startdoc:
@@ -16,8 +16,13 @@ class ColdfusionSource
16
16
  end
17
17
 
18
18
  def analyze
19
- [CustomTag, Cfmodule, Cfinclude, Cfc].each do |reftype|
20
- internal_references.concat reftype.search(self)
19
+ begin
20
+ [CustomTag, Cfmodule, Cfinclude, Cfc].each do |reftype|
21
+ internal_references.concat reftype.search(self)
22
+ end
23
+ rescue => e
24
+ $stderr.puts "Analyzing #{filename} failed: #{e}"
25
+ e.backtrace.each { |l| $stderr.puts " #{l}" }
21
26
  end
22
27
  end
23
28
 
@@ -72,16 +77,24 @@ class ColdfusionSource
72
77
  # attributes, and the line number back to the provided block.
73
78
  def scan_for_tag(tag, &block)
74
79
  scan(/<#{tag}(.*?)>/mi) do |md, l|
75
- attributes = %w(' ").collect do |q|
76
- /(\w+)\s*=\s*#{q}([^#{q}]*?)#{q}/.scan(md[1].gsub(%r(/$), ''))
77
- end.flatten.inject({}) do |attrs, amd|
80
+ attributes = ['"', "'", nil].collect { |q|
81
+ attribute_re(q).scan(md[1].gsub(%r(/$), ''))
82
+ }.flatten.inject({}) { |attrs, amd|
78
83
  attrs[normalize_attribute_key(amd[1])] = amd[2]
79
84
  attrs
80
- end
85
+ }
81
86
  yield md[0], attributes, l
82
87
  end
83
88
  end
84
89
 
90
+ def attribute_re(q)
91
+ if q
92
+ /(\w+)\s*=\s*#{q}([^#{q}]*?)#{q}/
93
+ else
94
+ /(\w+)\s*=\s*([^'"\s]+)/
95
+ end
96
+ end
97
+
85
98
  private
86
99
 
87
100
  def normalize_attribute_key(key)
@@ -64,10 +64,15 @@ module CaseCheck
64
64
  def initialize(params)
65
65
  @params = params
66
66
  CaseCheck.status_stream.print "Reading source files "
67
- @sources = Dir["#{params.directory}/**/*.cf[mc]"].collect do |f|
68
- CaseCheck.status_stream.print '.'
69
- ColdfusionSource.create(f)
70
- end
67
+ @sources = extensions.collect {
68
+ |ext| [ext, Dir.glob("#{params.directory}/**/*.#{ext}", File::FNM_CASEFOLD)]
69
+ }.collect do |ext, files|
70
+ CaseCheck.status_stream.print "#{ext}: " unless files.empty?
71
+ files.collect do |f|
72
+ CaseCheck.status_stream.print '.'
73
+ ColdfusionSource.create(f)
74
+ end
75
+ end.flatten
71
76
  CaseCheck.status_stream.puts
72
77
  CaseCheck.status_stream.print "Analyzing "
73
78
  @sources.each do |s|
@@ -85,6 +90,10 @@ module CaseCheck
85
90
  def reference_count
86
91
  sources.inject(0) { |c, s| c + s.internal_references.size }
87
92
  end
93
+
94
+ def extensions
95
+ %w(cfm cfc tem)
96
+ end
88
97
  end
89
98
 
90
99
  class FilteredSource
@@ -10,15 +10,12 @@ class Configuration
10
10
  apply
11
11
  end
12
12
 
13
- def [](k)
14
- @doc[k]
15
- end
16
-
17
13
  private
18
14
 
19
15
  def apply
20
16
  read_custom_tag_dirs
21
17
  read_cfc_dirs
18
+ read_substitutions
22
19
  end
23
20
 
24
21
  def read_custom_tag_dirs
@@ -29,6 +26,14 @@ class Configuration
29
26
  Cfc.directories = absolutize_directories(@doc['cfc_directories'] || [])
30
27
  end
31
28
 
29
+ def read_substitutions
30
+ if @doc['substitutions']
31
+ @doc['substitutions'].each_pair do |re, repl|
32
+ CaseCheck::Reference.substitutions << [Regexp.new(re, Regexp::IGNORECASE), repl]
33
+ end
34
+ end
35
+ end
36
+
32
37
  private
33
38
 
34
39
  def absolutize_directories(dirs)
@@ -5,8 +5,14 @@ require 'activesupport'
5
5
 
6
6
  module CaseCheck
7
7
 
8
- # base class
8
+ # abstract base class
9
9
  class Reference < Struct.new(:source, :text, :line)
10
+ class << self
11
+ def substitutions
12
+ @substitutions ||= []
13
+ end
14
+ end
15
+
10
16
  # abstract methods
11
17
  # - expected_path
12
18
  # returns the exact relative path to which this reference refers
@@ -43,6 +49,15 @@ class Reference < Struct.new(:source, :text, :line)
43
49
  self.class.name.split('::').last.underscore.gsub('_', ' ')
44
50
  end
45
51
 
52
+ def substituted_text
53
+ re, sub = CaseCheck::Reference.substitutions.detect { |expr, _| expr =~ text }
54
+ if re
55
+ text.sub(re, sub)
56
+ else
57
+ text
58
+ end
59
+ end
60
+
46
61
  protected
47
62
 
48
63
  def case_sensitive_match?
@@ -14,24 +14,32 @@ module CaseCheck
14
14
  def search(source)
15
15
  source.scan(/createObject\((.*?)\)/mi) do |match, l|
16
16
  args = match[1].split(/\s*,\s*/).collect { |a| a.gsub(/['"]/, '') }
17
- unless args.size == 2 && args.first =~ /component/i
17
+ if args.size == 2 && args.first =~ /component/i
18
+ new(source, args.last, l)
19
+ elsif args.first =~ /java/
20
+ # quietly skip
21
+ else
22
+ # loudly skip
18
23
  $stderr.puts "Non-CFC call on line #{l} of #{source.filename}: #{match[0]}"
19
24
  end
20
- new(source, args.last, l)
21
- end
25
+ end.compact
22
26
  end
23
27
  end
24
28
 
25
29
  def initialize(source, text, line_number)
26
30
  super
27
- @expected_path = text.gsub('.', '/') + ".cfc"
28
- @resolved_to = self.class.directories.inject(nil) do |resolved, dir|
31
+ @expected_path = substituted_text.gsub('.', '/') + ".cfc"
32
+ @resolved_to = search_path.inject(nil) do |resolved, dir|
29
33
  resolved || resolve_in(dir)
30
34
  end
31
35
  end
32
36
 
33
37
  protected
34
38
 
39
+ def search_path
40
+ [File.dirname(source.filename)] + self.class.directories
41
+ end
42
+
35
43
  def case_sensitive_match?
36
44
  return true if super
37
45
  # According to the CF docs:
@@ -12,7 +12,7 @@ module CaseCheck
12
12
 
13
13
  def initialize(source, text, line)
14
14
  super
15
- @expected_path = text
15
+ @expected_path = substituted_text
16
16
  @resolved_to = resolve_in(File.dirname(source.filename))
17
17
  end
18
18
  end
@@ -21,7 +21,7 @@ module CaseCheck
21
21
  class Name < Cfmodule
22
22
  def initialize(source, text, line)
23
23
  super
24
- @expected_path = text.gsub('.', '/') + ".cfm"
24
+ @expected_path = substituted_text.gsub('.', '/') + ".cfm"
25
25
  @resolved_to = CustomTag.directories.inject(nil) do |resolved, dir|
26
26
  resolved || resolve_in(dir)
27
27
  end
@@ -35,7 +35,7 @@ module CaseCheck
35
35
  class Template < Cfmodule
36
36
  def initialize(source, text, line)
37
37
  super
38
- @expected_path = text
38
+ @expected_path = substituted_text
39
39
  @resolved_to = resolve_in(File.dirname(source.filename))
40
40
  end
41
41
 
@@ -137,11 +137,22 @@ describe ColdfusionSource do
137
137
  actual = perform_scan(<<-CFM, "cfparam")
138
138
  <cfparam name ="foo" default= "42">
139
139
  <cfparam name="bar" default = "11">
140
+ <cfparam name= baz default = 19>
140
141
  CFM
141
- actual.should have(2).matches
142
+ actual.should have(3).matches
142
143
  actual[0][:attributes].should == { :name => 'foo', :default => '42' }
143
144
  actual[1][:attributes].should == { :name => 'bar', :default => '11' }
144
- actual[1][:line_number].should == 2
145
+ actual[2][:attributes].should == { :name => 'baz', :default => '19' }
146
+ end
147
+
148
+ it "matches unquoted attributes" do
149
+ actual = perform_scan(<<-CFM, "cfinclude")
150
+ <cfinclude template=GetAccountIDStringMatch.cfm >
151
+ <cfinclude template=alpha option=beta>
152
+ CFM
153
+ actual.should have(2).matches
154
+ actual[0][:attributes].should == { :template => 'GetAccountIDStringMatch.cfm' }
155
+ actual[1][:attributes].should == { :template => 'alpha', :option => 'beta' }
145
156
  end
146
157
 
147
158
  it "downcases attribute keys" do
@@ -39,4 +39,23 @@ describe CaseCheck::Configuration do
39
39
  read_config
40
40
  CaseCheck::CustomTag.directories.should == %w(/tmp/cf_case_check/zappo/customtags)
41
41
  end
42
+
43
+ it "reads substitutions" do
44
+ config_file <<-YAML
45
+ substitutions:
46
+ '#application.cfcPath#': /var/www/cfcs
47
+ YAML
48
+ read_config
49
+ CaseCheck::Reference.should have(1).substitutions
50
+ end
51
+
52
+ it "interprets substitution patterns as case insensitive REs" do
53
+ config_file <<-YAML
54
+ substitutions:
55
+ '#application.cfcPath#': /var/www/cfcs
56
+ YAML
57
+ read_config
58
+ CaseCheck::Reference.substitutions.first.should == [/#application.cfcPath#/i, '/var/www/cfcs']
59
+ CaseCheck::Reference.substitutions.first.first.should be_casefold
60
+ end
42
61
  end
@@ -68,5 +68,32 @@ describe File, ' extensions ' do
68
68
  touch("baz/bar/foo/quux")
69
69
  File.case_insensitive_canonical_name(testfile("baz/bar/quod/../fOO/quux")).should == testfile("baz/bar/foo/quux")
70
70
  end
71
+
72
+ describe "with symlinks" do
73
+ before do
74
+ @actual_dir = File.join(@tmpdir, "A")
75
+ FileUtils.mkdir_p @actual_dir
76
+ @linked_dir = File.join(@tmpdir, "B")
77
+ FileUtils.ln_s(@actual_dir, @linked_dir)
78
+ end
79
+
80
+ it "finds exact matches" do
81
+ expected = File.join(@linked_dir, "foo.cfm")
82
+ FileUtils.touch expected
83
+ File.case_insensitive_canonical_name(expected).should == expected
84
+ end
85
+
86
+ it "finds case-insensitive matches where the filename case differs" do
87
+ expected = File.join(@linked_dir, "Foo.cfm")
88
+ FileUtils.touch expected
89
+ File.case_insensitive_canonical_name(File.join(@linked_dir, "foo.cfm")).should == expected
90
+ end
91
+
92
+ it "finds case-insensitive matches where the symlink case differs" do
93
+ expected = File.join(@linked_dir, "foo.cfm")
94
+ FileUtils.touch expected
95
+ File.case_insensitive_canonical_name(File.join(@tmpdir, 'b', "foo.cfm")).should == expected
96
+ end
97
+ end
71
98
  end
72
99
  end
@@ -32,6 +32,21 @@ describe CaseCheck::Reference do
32
32
  end
33
33
  end
34
34
 
35
+ describe "text substitution" do
36
+ it "has none by default" do
37
+ CaseCheck::Reference.substitutions.should == []
38
+ end
39
+
40
+ it "subs in the first matching option" do
41
+ CaseCheck::Reference.substitutions << [/#application#/i, 'foo'] << [/#app.*?#/i, 'bar']
42
+ SampleReference.new(nil, nil, 0, "#application#/quux").substituted_text.should == "foo/quux"
43
+ end
44
+
45
+ it "returns the original text if there are no matching subs" do
46
+ SampleReference.new(nil, nil, 0, "#application#/quux").substituted_text.should == "#application#/quux"
47
+ end
48
+ end
49
+
35
50
  describe 'default message' do
36
51
  it "indicates when it is unresolved" do
37
52
  SampleReference.new("/foo/patient.cfm", nil, 11, "FOO_Patient").message.should ==
@@ -11,6 +11,7 @@ describe CaseCheck::Cfc do
11
11
  <cfscript>
12
12
  utilsObj = CreateObject("component","bspore.Utils").init(datasource=application.personnel_db,username=session.netid,userIP=cgi.remote_addr);
13
13
  summaryObj = createObject("component","bspore.Summary").init(datasource=application.db,username=session.netid,userIP=cgi.remote_addr);
14
+ color = createObject("java","org.apache.poi.hssf.util.HSSFColor$CORAL");
14
15
  </cfscript>
15
16
  CFM
16
17
  end
@@ -31,6 +32,10 @@ describe CaseCheck::Cfc do
31
32
  actual_search.should have(2).references
32
33
  end
33
34
 
35
+ it "ignores java invocations" do
36
+ actual_search.detect { |s| s.expected_path =~ /CORAL/ }.should be_nil
37
+ end
38
+
34
39
  it "finds lower case createObject style" do
35
40
  actual_search.last.expected_path.should == "bspore/Summary.cfc"
36
41
  end
@@ -59,4 +64,19 @@ describe CaseCheck::Cfc do
59
64
  actual_search.first.resolved_to.should == expected_file
60
65
  actual_search.first.resolution.should == :case_insensitive
61
66
  end
67
+
68
+ it "resolves against a match in the same directory as the file" do
69
+ expected_file = File.join(File.dirname(@source.filename), 'bspore/Utils.cfc')
70
+ touch expected_file
71
+ actual_search.first.resolved_to.should == expected_file
72
+ actual_search.first.resolution.should == :exact
73
+ end
74
+
75
+ it "performs substitutions before attempting to resolve" do
76
+ @source.content = <<-CFM
77
+ CreateObject("component", application.pathToComponents&"Personnel")
78
+ CFM
79
+ CaseCheck::Reference.substitutions << [/application.pathToComponents&/i, "pipes."]
80
+ actual_search.first.expected_path.should == "pipes/Personnel.cfc"
81
+ end
62
82
  end
@@ -63,4 +63,12 @@ describe CaseCheck::Cfinclude do
63
63
  CFM
64
64
  actual_search.should have(1).reference
65
65
  end
66
+
67
+ it "performs substitutions before resolving" do
68
+ @source.content = <<-CFM
69
+ <cfinclude template="#application.myroot#whatever.cfm">
70
+ CFM
71
+ CaseCheck::Reference.substitutions << [/#application.myroot#/, 'etc/']
72
+ actual_search.first.expected_path.should == 'etc/whatever.cfm'
73
+ end
66
74
  end
@@ -76,6 +76,14 @@ describe CaseCheck::Cfmodule::Name do
76
76
  CFM
77
77
  actual_search.should have(1).reference
78
78
  end
79
+
80
+ it "performs substitutions before resolving" do
81
+ @source.content = <<-CFM
82
+ <cfmodule name="#application.myroot#whatever">
83
+ CFM
84
+ CaseCheck::Reference.substitutions << [/#application.myroot#/, 'etc.']
85
+ actual_search.first.expected_path.should == 'etc/whatever.cfm'
86
+ end
79
87
  end
80
88
 
81
89
  describe CaseCheck::Cfmodule::Template do
@@ -136,4 +144,13 @@ describe CaseCheck::Cfmodule::Template do
136
144
  CFM
137
145
  actual_search.should have(1).reference
138
146
  end
147
+
148
+
149
+ it "performs substitutions before resolving" do
150
+ @source.content = <<-CFM
151
+ <cfmodule template="#application.somePath#whatever.cfm">
152
+ CFM
153
+ CaseCheck::Reference.substitutions << [/#application.somePath#/, 'etc/']
154
+ actual_search.first.expected_path.should == 'etc/whatever.cfm'
155
+ end
139
156
  end
data/spec/spec_helper.rb CHANGED
@@ -9,6 +9,7 @@ Spec::Runner.configure do |config|
9
9
 
10
10
  config.after do
11
11
  CaseCheck.status_stream = nil
12
+ CaseCheck::Reference.substitutions.clear
12
13
  end
13
14
 
14
15
  # == Mock Framework
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsutphin-cf_case_check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rhett Sutphin
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-14 00:00:00 -08:00
12
+ date: 2009-09-23 00:00:00 -07:00
13
13
  default_executable: cf_case_check
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -23,12 +24,13 @@ dependencies:
23
24
  version:
24
25
  - !ruby/object:Gem::Dependency
25
26
  name: bones
27
+ type: :development
26
28
  version_requirement:
27
29
  version_requirements: !ruby/object:Gem::Requirement
28
30
  requirements:
29
31
  - - ">="
30
32
  - !ruby/object:Gem::Version
31
- version: 2.1.0
33
+ version: 2.5.0
32
34
  version:
33
35
  description: A utility which walks a ColdFusion application's source and determines which includes, custom tags, etc, will not work with a case-sensitive filesystem
34
36
  email: rhett@detailedbalance.net
@@ -38,13 +40,11 @@ extensions: []
38
40
 
39
41
  extra_rdoc_files:
40
42
  - History.txt
41
- - README.txt
43
+ - Manifest.txt
42
44
  - bin/cf_case_check
43
45
  files:
44
- - .gitignore
45
46
  - History.txt
46
47
  - Manifest.txt
47
- - README.txt
48
48
  - Rakefile
49
49
  - bin/cf_case_check
50
50
  - lib/case_check.rb
@@ -67,8 +67,9 @@ files:
67
67
  - spec/references/cfmodule_spec.rb
68
68
  - spec/references/custom_tag_spec.rb
69
69
  - spec/spec_helper.rb
70
- has_rdoc: true
70
+ has_rdoc: false
71
71
  homepage: http://github.com/rsutphin/cf_case_check
72
+ licenses:
72
73
  post_install_message:
73
74
  rdoc_options:
74
75
  - --main
@@ -89,12 +90,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  version:
90
91
  requirements: []
91
92
 
92
- rubyforge_project: !binary |
93
- AA==
94
-
95
- rubygems_version: 1.2.0
93
+ rubyforge_project:
94
+ rubygems_version: 1.3.5
96
95
  signing_key:
97
- specification_version: 2
96
+ specification_version: 3
98
97
  summary: A utility which walks a ColdFusion application's source and determines which includes, custom tags, etc, will not work with a case-sensitive filesystem
99
98
  test_files: []
100
99
 
data/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- pkg
2
- coverage
3
- *.gem
data/README.txt DELETED
@@ -1,75 +0,0 @@
1
- `cf_case_check`
2
- ===============
3
- http://github.com/rsutphin/cf_case_check
4
-
5
- Description
6
- -----------
7
-
8
- `cf_case_check` is a utility which walks a ColdFusion application's source and
9
- determines which references to other files will not work with a case-sensitive
10
- filesystem. Its intended audience is developers/sysadmins who are migrating
11
- a CF application from Windows hosting to Linux or another UNIX.
12
-
13
- `cf_case_check` was developed at the [Northwestern University Biomedical
14
- Informatics Center][NUBIC].
15
-
16
- [NUBIC]: http://www.nucats.northwestern.edu/centers/nubic/index.html
17
-
18
- Features
19
- --------
20
-
21
- * Resolves references of the following types:
22
- - `CF_`-style custom tags
23
- - `cfinclude`
24
- - `cfmodule` (both `template` and `name`)
25
- - `createObject` (for CFCs only)
26
- * Prints report to stdout
27
- * Allows for designation of custom tag & CFC search paths outside the
28
- application root
29
-
30
- Synopsis
31
- --------
32
-
33
- myapp$ cf_case_check
34
-
35
- For command-line options, do:
36
-
37
- $ cf_case_check --help
38
-
39
- Requirements
40
- ------------
41
-
42
- * Ruby 1.8.6 or later (may work with earlier, but not tested)
43
-
44
- Install
45
- -------
46
-
47
- Follow the GitHub rubygems [setup directions](http://gems.github.com/), then
48
-
49
- $ sudo gem install rsutphin-cf_case_check
50
-
51
- License
52
- -------
53
-
54
- (The MIT License)
55
-
56
- Copyright (c) 2008 Rhett Sutphin
57
-
58
- Permission is hereby granted, free of charge, to any person obtaining
59
- a copy of this software and associated documentation files (the
60
- 'Software'), to deal in the Software without restriction, including
61
- without limitation the rights to use, copy, modify, merge, publish,
62
- distribute, sublicense, and/or sell copies of the Software, and to
63
- permit persons to whom the Software is furnished to do so, subject to
64
- the following conditions:
65
-
66
- The above copyright notice and this permission notice shall be
67
- included in all copies or substantial portions of the Software.
68
-
69
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
70
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
71
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
72
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
73
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
74
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
75
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.