nokogiri-diff-0.3.0/ 0000755 0000041 0000041 00000000000 14574707366 014314 5 ustar www-data www-data nokogiri-diff-0.3.0/.gitignore 0000644 0000041 0000041 00000000042 14574707366 016300 0 ustar www-data www-data /Gemfile.lock
/coverage
/doc
/pkg
nokogiri-diff-0.3.0/.document 0000644 0000041 0000041 00000000034 14574707366 016130 0 ustar www-data www-data -
ChangeLog.md
LICENSE.txt
nokogiri-diff-0.3.0/.github/ 0000755 0000041 0000041 00000000000 14574707366 015654 5 ustar www-data www-data nokogiri-diff-0.3.0/.github/workflows/ 0000755 0000041 0000041 00000000000 14574707366 017711 5 ustar www-data www-data nokogiri-diff-0.3.0/.github/workflows/ruby.yml 0000644 0000041 0000041 00000001123 14574707366 021412 0 ustar www-data www-data name: CI
on: [ push, pull_request ]
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby:
- '3.0'
- '3.1'
- '3.2'
- '3.3'
- jruby
- truffleruby
name: Ruby ${{ matrix.ruby }}
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Install dependencies
run: bundle install --jobs 4 --retry 3
- name: Run tests
run: bundle exec rake test
nokogiri-diff-0.3.0/lib/ 0000755 0000041 0000041 00000000000 14574707366 015062 5 ustar www-data www-data nokogiri-diff-0.3.0/lib/nokogiri/ 0000755 0000041 0000041 00000000000 14574707366 016703 5 ustar www-data www-data nokogiri-diff-0.3.0/lib/nokogiri/diff/ 0000755 0000041 0000041 00000000000 14574707366 017613 5 ustar www-data www-data nokogiri-diff-0.3.0/lib/nokogiri/diff/xml.rb 0000644 0000041 0000041 00000000133 14574707366 020735 0 ustar www-data www-data # frozen_string_literal: true
require_relative 'xml/node'
require_relative 'xml/document'
nokogiri-diff-0.3.0/lib/nokogiri/diff/xml/ 0000755 0000041 0000041 00000000000 14574707366 020413 5 ustar www-data www-data nokogiri-diff-0.3.0/lib/nokogiri/diff/xml/node.rb 0000644 0000041 0000041 00000004713 14574707366 021672 0 ustar www-data www-data # frozen_string_literal: true
require 'nokogiri'
require 'tdiff'
class Nokogiri::XML::Node
include TDiff
include TDiff::Unordered
#
# Compares the XML/HTML node with another.
#
# @param [Nokogiri::XML::Node] node
# The other XMl/HTML node.
#
# @return [Boolean]
# Specifies whether the two nodes are equal.
#
def tdiff_equal(node)
if (self.class == node.class)
case node
when Nokogiri::XML::Attr
(self.name == node.name && self.value == node.value)
when Nokogiri::XML::Element, Nokogiri::XML::DTD
self.name == node.name
when Nokogiri::XML::Text, Nokogiri::XML::Comment
self.text == node.text
when Nokogiri::XML::ProcessingInstruction
(self.name == node.name && self.content == node.content)
else
false
end
else
false
end
end
#
# Enumerates over the children of another XML/HTML node.
#
# @param [Nokogiri::XML::Node] node
# The other XMl/HTML node.
#
# @yield [child]
# The given block will be passed every child of the node.
#
# @yieldparam [Nokogiri::XML::Node] node
# A child node.
#
def tdiff_each_child(node,&block)
if node.kind_of?(Nokogiri::XML::Element)
node.attribute_nodes.sort_by(&:name).each(&block)
end
node.children.each(&block)
end
#
# Finds the differences between the node and another node.
#
# @param [Nokogiri::XML::Node] other
# The other node to compare against.
#
# @param [Hash] options
# Additional options for filtering changes.
#
# @option options [Boolean] :added
# Yield nodes that were added.
#
# @option options [Boolean] :removed
# Yield nodes that were removed.
#
# @yield [change, node]
# The given block will be passed each changed node.
#
# @yieldparam [' ', '-', '+'] change
# Indicates whether the node stayed the same, was removed or added.
#
# @yieldparam [Nokogiri::XML::Node] node
# The changed node.
#
# @return [Enumerator]
# If no block was given, an Enumerator object will be returned.
#
def diff(other,options={},&block)
return enum_for(__method__,other,options) unless block
if (options[:added] || options[:removed])
tdiff_unordered(other) do |change,node|
if (change == '+' && options[:added]) then yield change, node
elsif (change == '-' && options[:removed]) then yield change, node
end
end
else
tdiff(other,&block)
end
end
end
nokogiri-diff-0.3.0/lib/nokogiri/diff/xml/document.rb 0000644 0000041 0000041 00000001065 14574707366 022560 0 ustar www-data www-data # frozen_string_literal: true
require_relative 'node'
class Nokogiri::XML::Document < Nokogiri::XML::Node
#
# Overrides `tdiff` to only compare the child nodes of the document.
#
def tdiff(tree,&block)
return enum_for(__method__,tree) unless block
tdiff_recursive(tree,&block)
return self
end
#
# Overrides `tdiff_unordered` to only compare the child nodes of the document.
#
def tdiff_unordered(tree,&block)
return enum_for(__method__,tree) unless block
tdiff_recursive_unordered(tree,&block)
return self
end
end
nokogiri-diff-0.3.0/lib/nokogiri/diff/version.rb 0000644 0000041 0000041 00000000171 14574707366 021624 0 ustar www-data www-data # frozen_string_literal: true
module Nokogiri
module Diff
# nokogiri-diff version
VERSION = '0.3.0'
end
end
nokogiri-diff-0.3.0/lib/nokogiri/diff.rb 0000644 0000041 0000041 00000000133 14574707366 020135 0 ustar www-data www-data # frozen_string_literal: true
require_relative 'diff/xml'
require_relative 'diff/version'
nokogiri-diff-0.3.0/LICENSE.txt 0000644 0000041 0000041 00000002045 14574707366 016140 0 ustar www-data www-data Copyright (c) 2010-2024 Hal Brodigan
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
nokogiri-diff-0.3.0/spec/ 0000755 0000041 0000041 00000000000 14574707366 015246 5 ustar www-data www-data nokogiri-diff-0.3.0/spec/spec_helper.rb 0000644 0000041 0000041 00000000064 14574707366 020064 0 ustar www-data www-data require 'rspec'
require 'simplecov'
SimpleCov.start
nokogiri-diff-0.3.0/spec/diff_spec.rb 0000644 0000041 0000041 00000021712 14574707366 017520 0 ustar www-data www-data require 'spec_helper'
require 'nokogiri/diff'
describe "nokogiri/diff" do
let(:contents) { '
' }
let(:doc) { Nokogiri::XML(contents) }
let(:added_text) { Nokogiri::XML('') }
let(:added_element) { Nokogiri::XML('') }
let(:added_attr) { Nokogiri::XML('') }
let(:added_attrs) { Nokogiri::XML('') }
let(:changed_text) { Nokogiri::XML('') }
let(:changed_element) { Nokogiri::XML('one
') }
let(:changed_attr_name) { Nokogiri::XML('') }
let(:changed_attr_value) { Nokogiri::XML('') }
let(:changed_attr_order) { Nokogiri::XML('') }
let(:removed_text) { Nokogiri::XML('') }
let(:removed_element) { Nokogiri::XML('') }
let(:removed_attr) { Nokogiri::XML('') }
it "should add #diff to Nokogiri::XML::Docuemnt" do
expect(doc).to respond_to(:diff)
end
it "should add #diff to Nokogiri::XML::Element" do
expect(added_element.at('div')).to respond_to(:diff)
end
it "should add #diff to Nokogiri::XML::Text" do
expect(added_text.at('p/text()')).to respond_to(:diff)
end
it "should add #diff to Nokogiri::XML::Attr" do
expect(added_attr.at('p/@id')).to respond_to(:diff)
end
it "should not compare the Document objects" do
change = doc.diff(doc).first
expect(change[0]).to eq(' ')
expect(change[1]).to eq(doc.root)
end
it "should determine when two different documents are identical" do
expect(doc.diff(Nokogiri::XML(contents)).all? { |change,node|
change == ' '
}).to eq(true)
end
it "should search down within Nokogiri::XML::Document objects" do
expect(doc.diff(changed_text).any? { |change,node|
change != ' '
}).to eq(true)
end
it "should determine when text nodes are added" do
changes = doc.at('div').diff(added_text.at('div')).to_a
expect(changes.length).to eq(4)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(doc.at('div'))
expect(changes[1][0]).to eq(' ')
expect(changes[1][1]).to eq(doc.at('//p'))
expect(changes[2][0]).to eq('+')
expect(changes[2][1]).to eq(added_text.at('//div/text()'))
expect(changes[3][0]).to eq(' ')
expect(changes[3][1]).to eq(doc.at('//p/text()'))
end
it "should determine when elements are added" do
changes = doc.at('div').diff(added_element.at('div')).to_a
expect(changes.length).to eq(5)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(doc.at('div'))
expect(changes[1][0]).to eq('+')
expect(changes[1][1]).to eq(added_element.at('//p[1]'))
expect(changes[2][0]).to eq(' ')
expect(changes[2][1]).to eq(doc.at('//p'))
expect(changes[3][0]).to eq('-')
expect(changes[3][1]).to eq(doc.at('//p/text()'))
expect(changes[4][0]).to eq('+')
expect(changes[4][1]).to eq(added_element.at('//p[2]/text()'))
end
it "should ignore when attribute order changes" do
changes = added_attrs.at('p').diff(changed_attr_order.at('p')).to_a
expect(changes.all? { |change| change[0] == ' ' }).to be_truthy
end
it "should determine when attributes are added" do
changes = doc.at('p').diff(added_attr.at('p')).to_a
expect(changes.length).to eq(3)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(doc.at('p'))
expect(changes[1][0]).to eq('+')
expect(changes[1][1]).to eq(added_attr.at('//p/@id'))
expect(changes[2][0]).to eq(' ')
expect(changes[2][1]).to eq(doc.at('//p/text()'))
end
it "should determine when text nodes differ" do
changes = doc.at('p').diff(changed_text.at('p')).to_a
expect(changes.length).to eq(3)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(doc.at('p'))
expect(changes[1][0]).to eq('-')
expect(changes[1][1]).to eq(doc.at('//p/text()'))
expect(changes[2][0]).to eq('+')
expect(changes[2][1]).to eq(changed_text.at('//p/text()'))
end
it "should determine when element names differ" do
changes = doc.at('div').diff(changed_element.at('div')).to_a
expect(changes.length).to eq(3)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(doc.at('div'))
expect(changes[1][0]).to eq('-')
expect(changes[1][1]).to eq(doc.at('p'))
expect(changes[2][0]).to eq('+')
expect(changes[2][1]).to eq(changed_element.at('span'))
end
it "should determine when attribute names differ" do
changes = added_attr.at('p').diff(changed_attr_name.at('p')).to_a
expect(changes.length).to eq(4)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(added_attr.at('p'))
expect(changes[1][0]).to eq('-')
expect(changes[1][1]).to eq(added_attr.at('//p/@id'))
expect(changes[2][0]).to eq('+')
expect(changes[2][1]).to eq(changed_attr_name.at('//p/@i'))
expect(changes[3][0]).to eq(' ')
expect(changes[3][1]).to eq(added_attr.at('//p/text()'))
end
it "should determine when attribute values differ" do
changes = added_attr.at('p').diff(changed_attr_value.at('p')).to_a
expect(changes.length).to eq(4)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(added_attr.at('p'))
expect(changes[1][0]).to eq('-')
expect(changes[1][1]).to eq(added_attr.at('//p/@id'))
expect(changes[2][0]).to eq('+')
expect(changes[2][1]).to eq(changed_attr_value.at('//p/@id'))
expect(changes[3][0]).to eq(' ')
expect(changes[3][1]).to eq(added_attr.at('//p/text()'))
end
it "should determine when text nodes are removed" do
changes = added_text.at('div').diff(removed_text.at('div')).to_a
expect(changes.length).to eq(4)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(added_text.at('div'))
expect(changes[1][0]).to eq(' ')
expect(changes[1][1]).to eq(added_text.at('p'))
expect(changes[2][0]).to eq(' ')
expect(changes[2][1]).to eq(added_text.at('//div/text()'))
expect(changes[3][0]).to eq('-')
expect(changes[3][1]).to eq(added_text.at('//p/text()'))
end
it "should determine when elements are removed" do
changes = added_element.at('div').diff(removed_element.at('div')).to_a
expect(changes.length).to eq(3)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(added_element.at('div'))
expect(changes[1][0]).to eq('-')
expect(changes[1][1]).to eq(added_element.at('//p[1]'))
expect(changes[2][0]).to eq('-')
expect(changes[2][1]).to eq(added_element.at('//p[2]'))
end
it "should ignore when attributes change order" do
end
it "should determine when attributes are removed" do
changes = added_attr.at('div').diff(removed_attr.at('div')).to_a
expect(changes.length).to eq(4)
expect(changes[0][0]).to eq(' ')
expect(changes[0][1]).to eq(added_attr.at('div'))
expect(changes[1][0]).to eq(' ')
expect(changes[1][1]).to eq(added_attr.at('p'))
expect(changes[2][0]).to eq('-')
expect(changes[2][1]).to eq(added_attr.at('//p/@id'))
expect(changes[3][0]).to eq(' ')
expect(changes[3][1]).to eq(added_attr.at('//p/text()'))
end
context ":added" do
it "should determine only when text nodes are added" do
changes = doc.at('div').diff(added_text.at('div'), :added => true).to_a
expect(changes.length).to eq(1)
expect(changes[0][0]).to eq('+')
expect(changes[0][1]).to eq(added_text.at('//div/text()'))
end
it "should determine only when elements are added" do
changes = doc.at('div').diff(added_element.at('div'), :added => true).to_a
expect(changes.length).to eq(1)
expect(changes[0][0]).to eq('+')
expect(changes[0][1]).to eq(added_element.at('//div/p[2]'))
end
it "should determine only when attributes are added" do
changes = doc.at('div').diff(added_attr.at('div'), :added => true).to_a
expect(changes.length).to eq(1)
expect(changes[0][0]).to eq('+')
expect(changes[0][1]).to eq(added_attr.at('//p/@id'))
end
end
context ":removed" do
it "should determine only when text nodes are removed" do
changes = doc.at('div').diff(removed_text.at('div'), :removed => true).to_a
expect(changes.length).to eq(1)
expect(changes[0][0]).to eq('-')
expect(changes[0][1]).to eq(doc.at('//p/text()'))
end
it "should determine only when elements are removed" do
changes = doc.at('div').diff(removed_element.at('div'), :removed => true).to_a
expect(changes.length).to eq(1)
expect(changes[0][0]).to eq('-')
expect(changes[0][1]).to eq(doc.at('//div/p'))
end
it "should determine only when attributes are removed" do
changes = added_attr.at('div').diff(removed_attr.at('div'), :removed => true).to_a
expect(changes.length).to eq(1)
expect(changes[0][0]).to eq('-')
expect(changes[0][1]).to eq(added_attr.at('//p/@id'))
end
end
end
nokogiri-diff-0.3.0/.yardopts 0000644 0000041 0000041 00000000104 14574707366 016155 0 ustar www-data www-data --markup markdown --title "nokogiri-diff Documentation" --protected
nokogiri-diff-0.3.0/gemspec.yml 0000644 0000041 0000041 00000001470 14574707366 016464 0 ustar www-data www-data name: nokogiri-diff
summary: Calculate the differences between two XML/HTML documents.
description:
Nokogiri::Diff adds the ability to calculate the differences (added
or removed nodes) between two XML/HTML documents.
license: MIT
authors: Postmodern
email: postmodern.mod3@gmail.com
homepage: https://github.com/postmodern/nokogiri-diff#readme
has_yard: true
metadata:
documentation_uri: https://rubydoc.info/gems/nokogiri-diff
source_code_uri: https://github.com/postmodern/nokogiri-diff
bug_tracker_uri: https://github.com/postmodern/nokogiri-diff/issues
changelog_uri: https://github.com/postmodern/nokogiri-diff/blob/main/ChangeLog.md
rubygems_mfa_required: 'true'
required_ruby_version: ">= 2.0.0"
dependencies:
tdiff: ~> 0.4
nokogiri: ~> 1.5
development_dependencies:
bundler: ~> 2.0
nokogiri-diff-0.3.0/.rspec 0000644 0000041 0000041 00000000040 14574707366 015423 0 ustar www-data www-data --colour --format documentation
nokogiri-diff-0.3.0/Rakefile 0000644 0000041 0000041 00000000272 14574707366 015762 0 ustar www-data www-data require 'rubygems/tasks'
Gem::Tasks.new
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new
task :test => :spec
task :default => :spec
require 'yard'
YARD::Rake::YardocTask.new
nokogiri-diff-0.3.0/nokogiri-diff.gemspec 0000644 0000041 0000041 00000003557 14574707366 020422 0 ustar www-data www-data # encoding: utf-8
require 'yaml'
Gem::Specification.new do |gem|
gemspec = YAML.load_file('gemspec.yml')
gem.name = gemspec.fetch('name')
gem.version = gemspec.fetch('version') do
require_relative 'lib/nokogiri/diff/version'
Nokogiri::Diff::VERSION
end
gem.summary = gemspec['summary']
gem.description = gemspec['description']
gem.licenses = Array(gemspec['license'])
gem.authors = Array(gemspec['authors'])
gem.email = gemspec['email']
gem.homepage = gemspec['homepage']
glob = lambda { |patterns| gem.files & Dir[*patterns] }
gem.files = `git ls-files`.split($/)
gem.files = glob[gemspec['files']] if gemspec['files']
gem.executables = gemspec.fetch('executables') do
glob['bin/*'].map { |path| File.basename(path) }
end
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
gem.require_paths = Array(gemspec.fetch('require_paths') {
%w[ext lib].select { |dir| File.directory?(dir) }
})
gem.requirements = gemspec['requirements']
gem.required_ruby_version = gemspec['required_ruby_version']
gem.required_rubygems_version = gemspec['required_rubygems_version']
gem.post_install_message = gemspec['post_install_message']
split = lambda { |string| string.split(/,\s*/) }
if gemspec['dependencies']
gemspec['dependencies'].each do |name,versions|
gem.add_dependency(name,split[versions])
end
end
if gemspec['development_dependencies']
gemspec['development_dependencies'].each do |name,versions|
gem.add_development_dependency(name,split[versions])
end
end
end
nokogiri-diff-0.3.0/ChangeLog.md 0000644 0000041 0000041 00000002107 14574707366 016465 0 ustar www-data www-data ### 0.3.0 / 2024-01-24
* Require [ruby](http://www.ruby-lang.org/) >= 2.0.0.
* Require [tdiff](http://github.com/postmodern/tdiff) ~> 0.4.
* Switched to using `require_relative` to improve load-times.
* Added `# frozen_string_literal: true` to all files.
### 0.2.0 / 2013-04-22
* {Nokogiri::XML::Node#tdiff_each_child} now sorts attributes by name, so that
changes in attribute order is ignored. (thanks @bhollis)
* {Nokogiri::XML::Node#tdiff_equal} now supports `Nokogiri::XML::Comment`
and `Nokogiri::XML::ProcessingInstruction` objects. (thanks @bhollis)
### 0.1.2 / 2012-05-28
* Require tdiff ~> 0.3, >= 0.3.2.
* Added {Nokogiri::Diff::VERSION}.
* Replaced ore-tasks with
[rubygems-tasks](https://github.com/postmodern/rubygems-tasks#readme).
### 0.1.1 / 2012-05-09
* Require nokogiri ~> 1.5.
### 0.1.0 / 2010-11-29
* Initial release:
* Performs a breadth-first comparison between children nodes.
* Compares XML/HTML Elements, Attributes, Text nodes and DTD nodes.
* Allows calculating differences between documents, or just enumerating
the added or removed nodes.
nokogiri-diff-0.3.0/Gemfile 0000644 0000041 0000041 00000000416 14574707366 015610 0 ustar www-data www-data source 'https://rubygems.org'
gemspec
group :development do
gem 'rake'
gem 'rubygems-tasks', '~> 0.2'
gem 'rspec', '~> 3.0'
gem 'simplecov', '~> 0.20', require: false
gem 'kramdown'
gem 'redcarpet', platform: :mri
gem 'yard', '~> 0.9'
end
nokogiri-diff-0.3.0/README.md 0000644 0000041 0000041 00000004012 14574707366 015570 0 ustar www-data www-data # nokogiri-diff
[](https://github.com/postmodern/nokogiri-diff/actions/workflows/ruby.yml)
* [Source](https://github.com/postmodern/nokogiri-diff)
* [Issues](https://github.com/postmodern/nokogiri-diff/issues)
* [Documentation](http://rubydoc.info/gems/nokogiri-diff/frames)
## Description
nokogiri-diff adds the ability to calculate the differences (added or
removed nodes) between two XML/HTML documents.
## Features
* Performs a breadth-first comparison between children nodes.
* Compares XML/HTML Elements, Attributes, Text nodes and DTD nodes.
* Allows calculating differences between documents, or just enumerating the
added or removed nodes.
## Examples
Enumerate over the differences between two HTML documents:
```ruby
require 'nokogiri/diff'
doc1 = Nokogiri::HTML('')
doc2 = Nokogiri::HTML('')
doc1.diff(doc2) do |change,node|
puts "#{change} #{node.to_html}".ljust(30) + node.parent.path
end
# /
# one
/div
# - two /div
# + /div
# + three
/div
# + id="1" /div/p[1]
# one /div/p
```
Only find the added nodes:
```ruby
doc1.diff(doc2, :added => true) do |change,node|
puts node.to_html.ljust(30) + node.parent.path
end
# /div
# three
/div
# id="1" /div/p[1]
```
Only find the removed nodes:
```ruby
doc1.diff(doc2, :removed => true) do |change,node|
puts node.to_html.ljust(30) + node.parent.path
end
# two /div
```
## Requirements
* [ruby](http://www.ruby-lang.org/) >= 2.0.0
* [tdiff](http://github.com/postmodern/tdiff) ~> 0.4
* [nokogiri](http://nokogiri.rubyforge.org/) ~> 1.5
## Install
```shell
$ gem install nokogiri-diff
```
## License
See {file:LICENSE.txt} for details.