ptools 1.3.2-universal-mingw32 → 1.4.0-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,66 +1,68 @@
1
- == Description
2
- The ptools (power tools) library is an additional set of commands for the
3
- File class based on Unix command line tools.
4
-
5
- == Prerequisites
6
- On MS Windows you will need the win32-file gem.
7
-
8
- == Installation
9
- gem install ptools
10
-
11
- == Synopsis
12
- require "ptools"
13
-
14
- File.which("ruby") # '/usr/local/bin/ruby'
15
- File.whereis("ruby") # ['/usr/local/bin/ruby','/opt/bin/ruby']
16
-
17
- File.head("myfile") # Returns first 10 lines of 'myfile'
18
- File.tail("myfile",3) # Returns last 3 lines of 'myfile'
19
- File.wc("myfile",'words') # Returns the number of words in 'myfile'
20
-
21
- File.touch("newfile") # "newfile" now exists
22
- File.null # '/dev/null' on Unix, 'NUL' on Windows
23
- File.binary?('some_file') # true or false
24
- File.sparse?('some_file') # true or false
25
-
26
- # Creates a copy of 'myfile' called 'newfile', in DOS format
27
- File.nl_convert("myfile", "newfile", "dos")
28
-
29
- == Known Bugs
30
- The File.which and File.whereis methods may fail when using JRuby on Windows.
31
- See https://github.com/jruby/jruby/issues/2291 for details.
32
-
33
- Please report any other issues on the github project page.
34
-
35
- http://www.github.com/djberg96/ptools
36
-
37
- == Acknowledgements
38
- The File.which method was originally adopted from the FileWhich code posted
39
- by Michael Granger on the now defunct rubygarden.org website. That code was
40
- later replaced by a version based on the ruby-which library.
41
-
42
- The File.nl_convert method is based on the nlcvt program found at
43
- http://www.perl.com/language/ppt/src/nlcvt/nlcvt, written by Tom Christiansen.
44
-
45
- The binary?() method was based almost entirely on a blog post by Ryan
46
- Davis (who, in turn, based his code on Perl's -B switch).
47
-
48
- Thanks go to Matt Hoyle for help with the File.tail method.
49
-
50
- == Future Plans
51
- Add whatever other tools people think might be useful.
52
-
53
- == License
54
- Artistic 2.0
55
-
56
- == Copyright
57
- (C) 2003-2014 Daniel J. Berger
58
- All Rights Reserved.
59
-
60
- == Warranty
61
- This package is provided "as is" and without any express or
62
- implied warranties, including, without limitation, the implied
63
- warranties of merchantability and fitness for a particular purpose.
64
-
65
- == Author
66
- Daniel J. Berger
1
+ == Description
2
+ The ptools (power tools) library is an additional set of commands for the
3
+ File class based on Unix command line tools.
4
+
5
+ == Prerequisites
6
+ On MS Windows you will need the win32-file gem.
7
+
8
+ == Installation
9
+ gem install ptools
10
+
11
+ == Synopsis
12
+ require "ptools"
13
+
14
+ File.which("ruby") # '/usr/local/bin/ruby'
15
+ File.whereis("ruby") # ['/usr/local/bin/ruby','/opt/bin/ruby']
16
+
17
+ File.head("myfile") # Returns first 10 lines of 'myfile'
18
+ File.tail("myfile",3) # Returns last 3 lines of 'myfile'
19
+ File.wc("myfile",'words') # Returns the number of words in 'myfile'
20
+
21
+ File.touch("newfile") # "newfile" now exists
22
+ File.null # '/dev/null' on Unix, 'NUL' on Windows
23
+ File.binary?('some_file') # true or false
24
+ File.sparse?('some_file') # true or false
25
+
26
+ # Creates a copy of 'myfile' called 'newfile', in DOS format
27
+ File.nl_convert("myfile", "newfile", "dos")
28
+
29
+ == Known Bugs
30
+ The File.which and File.whereis methods may fail when using JRuby on Windows.
31
+ See https://github.com/jruby/jruby/issues/2291 for details.
32
+
33
+ Please report any other issues on the github project page.
34
+
35
+ http://www.github.com/djberg96/ptools
36
+
37
+ == Acknowledgements
38
+ The File.which method was originally adopted from the FileWhich code posted
39
+ by Michael Granger on the now defunct rubygarden.org website. That code was
40
+ later replaced by a version based on the ruby-which library.
41
+
42
+ The File.nl_convert method is based on the nlcvt program found at
43
+ http://www.perl.com/language/ppt/src/nlcvt/nlcvt, written by Tom Christiansen.
44
+
45
+ The binary?() method was based almost entirely on a blog post by Ryan
46
+ Davis (who, in turn, based his code on Perl's -B switch).
47
+
48
+ Thanks go to Matt Hoyle for help with the File.tail method.
49
+
50
+ And thanks to any and all contributors!
51
+
52
+ == Future Plans
53
+ Add whatever other tools people think might be useful.
54
+
55
+ == License
56
+ Artistic-2.0
57
+
58
+ == Copyright
59
+ (C) 2003-2020 Daniel J. Berger
60
+ All Rights Reserved.
61
+
62
+ == Warranty
63
+ This package is provided "as is" and without any express or
64
+ implied warranties, including, without limitation, the implied
65
+ warranties of merchantability and fitness for a particular purpose.
66
+
67
+ == Author
68
+ Daniel J. Berger
data/Rakefile CHANGED
@@ -1,139 +1,87 @@
1
- require 'rake'
2
- require 'rake/clean'
3
- require 'rake/testtask'
4
- require 'rbconfig'
5
- include RbConfig
6
-
7
- CLEAN.include("**/*.gem", "**/*.rbc", "**/*coverage*")
8
-
9
- desc 'Install the ptools package (non-gem)'
10
- task :install do
11
- sitelibdir = CONFIG["sitelibdir"]
12
- file = "lib/ptools.rb"
13
- FileUtils.cp(file, sitelibdir, :verbose => true)
14
- end
15
-
16
- namespace 'gem' do
17
- desc 'Create the ptools gem'
18
- task :create => [:clean] do
19
- Dir["*.gem"].each{ |f| File.delete(f) } # Clean first
20
- spec = eval(IO.read('ptools.gemspec'))
21
- if Gem::VERSION < "2.0.0"
22
- Gem::Builder.new(spec).build
23
- else
24
- require 'rubygems/package'
25
- Gem::Package.build(spec)
26
- end
27
- end
28
-
29
- desc 'Install the ptools gem'
30
- task :install => [:create] do
31
- file = Dir["*.gem"].first
32
- if RUBY_PLATFORM == 'java'
33
- sh "jruby -S gem install -l #{file}"
34
- else
35
- sh "gem install -l #{file}"
36
- end
37
- end
38
- end
39
-
40
- Rake::TestTask.new do |t|
41
- task :test => :clean
42
- t.verbose = true
43
- t.warning = true
44
- end
45
-
46
- namespace 'test' do
47
- desc "Check test coverage using rcov"
48
- task :coverage => [:clean] do
49
- require 'rcov'
50
- rm_rf 'coverage'
51
- sh "rcov -Ilib test/test*.rb"
52
- end
53
-
54
- Rake::TestTask.new('binary') do |t|
55
- t.libs << 'test'
56
- t.verbose = true
57
- t.warning = true
58
- t.test_files = FileList['test/test_binary.rb']
59
- end
60
-
61
- Rake::TestTask.new('constants') do |t|
62
- t.libs << 'test'
63
- t.verbose = true
64
- t.warning = true
65
- t.test_files = FileList['test/test_constants.rb']
66
- end
67
-
68
- Rake::TestTask.new('head') do |t|
69
- t.libs << 'test'
70
- t.verbose = true
71
- t.warning = true
72
- t.test_files = FileList['test/test_head.rb']
73
- end
74
-
75
- Rake::TestTask.new('image') do |t|
76
- t.libs << 'test'
77
- t.verbose = true
78
- t.warning = true
79
- t.test_files = FileList['test/test_image.rb']
80
- end
81
-
82
- Rake::TestTask.new('nlconvert') do |t|
83
- t.libs << 'test'
84
- t.verbose = true
85
- t.warning = true
86
- t.test_files = FileList['test/test_nlconvert.rb']
87
- end
88
-
89
- Rake::TestTask.new('null') do |t|
90
- t.libs << 'test'
91
- t.verbose = true
92
- t.warning = true
93
- t.test_files = FileList['test/test_null.rb']
94
- end
95
-
96
- Rake::TestTask.new('sparse') do |t|
97
- t.libs << 'test'
98
- t.verbose = true
99
- t.warning = true
100
- t.test_files = FileList['test/test_is_sparse.rb']
101
- end
102
-
103
- Rake::TestTask.new('tail') do |t|
104
- t.libs << 'test'
105
- t.verbose = true
106
- t.warning = true
107
- t.test_files = FileList['test/test_tail.rb']
108
- end
109
-
110
- Rake::TestTask.new('touch') do |t|
111
- t.libs << 'test'
112
- t.verbose = true
113
- t.warning = true
114
- t.test_files = FileList['test/test_touch.rb']
115
- end
116
-
117
- Rake::TestTask.new('wc') do |t|
118
- t.libs << 'test'
119
- t.verbose = true
120
- t.warning = true
121
- t.test_files = FileList['test/test_wc.rb']
122
- end
123
-
124
- Rake::TestTask.new('whereis') do |t|
125
- t.libs << 'test'
126
- t.verbose = true
127
- t.warning = true
128
- t.test_files = FileList['test/test_whereis.rb']
129
- end
130
-
131
- Rake::TestTask.new('which') do |t|
132
- t.libs << 'test'
133
- t.verbose = true
134
- t.warning = true
135
- t.test_files = FileList['test/test_which.rb']
136
- end
137
- end
138
-
139
- task :default => :test
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ require 'rspec/core/rake_task'
6
+ include RbConfig
7
+
8
+ CLEAN.include("**/*.gem", "**/*.rbc", "**/*coverage*", "**/*.lock")
9
+
10
+ desc 'Install the ptools package (non-gem)'
11
+ task :install do
12
+ sitelibdir = CONFIG["sitelibdir"]
13
+ file = "lib/ptools.rb"
14
+ FileUtils.cp(file, sitelibdir, :verbose => true)
15
+ end
16
+
17
+ namespace 'gem' do
18
+ desc 'Create the ptools gem'
19
+ task :create => [:clean] do
20
+ require 'rubygems/package'
21
+ spec = eval(IO.read('ptools.gemspec'))
22
+ spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
23
+ Gem::Package.build(spec)
24
+ end
25
+
26
+ desc 'Install the ptools gem'
27
+ task :install => [:create] do
28
+ file = Dir["*.gem"].first
29
+ if RUBY_PLATFORM == 'java'
30
+ sh "jruby -S gem install -l #{file}"
31
+ else
32
+ sh "gem install -l #{file}"
33
+ end
34
+ end
35
+ end
36
+
37
+ namespace 'spec' do
38
+ RSpec::Core::RakeTask.new(:binary) do |t|
39
+ t.pattern = 'spec/binary_spec.rb'
40
+ end
41
+
42
+ RSpec::Core::RakeTask.new(:constants) do |t|
43
+ t.pattern = 'spec/constants_spec.rb'
44
+ end
45
+
46
+ RSpec::Core::RakeTask.new(:head) do |t|
47
+ t.pattern = 'spec/head_spec.rb'
48
+ end
49
+
50
+ RSpec::Core::RakeTask.new(:image) do |t|
51
+ t.pattern = 'spec/image_spec.rb'
52
+ end
53
+
54
+ RSpec::Core::RakeTask.new(:nlconvert) do |t|
55
+ t.pattern = 'spec/nlconvert_spec.rb'
56
+ end
57
+
58
+ RSpec::Core::RakeTask.new(:sparse) do |t|
59
+ t.pattern = 'spec/sparse_spec.rb'
60
+ end
61
+
62
+ RSpec::Core::RakeTask.new(:tail) do |t|
63
+ t.pattern = 'spec/tail_spec.rb'
64
+ end
65
+
66
+ RSpec::Core::RakeTask.new(:touch) do |t|
67
+ t.pattern = 'spec/touch_spec.rb'
68
+ end
69
+
70
+ RSpec::Core::RakeTask.new(:wc) do |t|
71
+ t.pattern = 'spec/wc_spec.rb'
72
+ end
73
+
74
+ RSpec::Core::RakeTask.new(:whereis) do |t|
75
+ t.pattern = 'spec/whereis_spec.rb'
76
+ end
77
+
78
+ RSpec::Core::RakeTask.new(:which) do |t|
79
+ t.pattern = 'spec/which_spec.rb'
80
+ end
81
+
82
+ RSpec::Core::RakeTask.new(:all) do |t|
83
+ t.pattern = 'spec/*_spec.rb'
84
+ end
85
+ end
86
+
87
+ task :default => 'spec:all'
@@ -0,0 +1,26 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MREwDwYDVQQDDAhkamJl
3
+ cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
4
+ MB4XDTE4MDMxODE1MjIwN1oXDTI4MDMxNTE1MjIwN1owPzERMA8GA1UEAwwIZGpi
5
+ ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
6
+ bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALgfaroVM6CI06cxr0/h
7
+ A+j+pc8fgpRgBVmHFaFunq28GPC3IvW7Nvc3Y8SnAW7pP1EQIbhlwRIaQzJ93/yj
8
+ u95KpkP7tA9erypnV7dpzBkzNlX14ACaFD/6pHoXoe2ltBxk3CCyyzx70mTqJpph
9
+ 75IB03ni9a8yqn8pmse+s83bFJOAqddSj009sGPcQO+QOWiNxqYv1n5EHcvj2ebO
10
+ 6hN7YTmhx7aSia4qL/quc4DlIaGMWoAhvML7u1fmo53CYxkKskfN8MOecq2vfEmL
11
+ iLu+SsVVEAufMDDFMXMJlvDsviolUSGMSNRTujkyCcJoXKYYxZSNtIiyd9etI0X3
12
+ ctu0uhrFyrMZXCedutvXNjUolD5r9KGBFSWH1R9u2I3n3SAyFF2yzv/7idQHLJJq
13
+ 74BMnx0FIq6fCpu5slAipvxZ3ZkZpEXZFr3cIBtO1gFvQWW7E/Y3ijliWJS1GQFq
14
+ 058qERadHGu1yu1dojmFRo6W2KZvY9al2yIlbkpDrD5MYQIDAQABo3cwdTAJBgNV
15
+ HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUFZsMapgzJimzsbaBG2Tm8j5e
16
+ AzgwHQYDVR0RBBYwFIESZGpiZXJnOTZAZ21haWwuY29tMB0GA1UdEgQWMBSBEmRq
17
+ YmVyZzk2QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAW2tnYixXQtKxgGXq
18
+ /3iSWG2bLwvxS4go3srO+aRXZHrFUMlJ5W0mCxl03aazxxKTsVVpZD8QZxvK91OQ
19
+ h9zr9JBYqCLcCVbr8SkmYCi/laxIZxsNE5YI8cC8vvlLI7AMgSfPSnn/Epq1GjGY
20
+ 6L1iRcEDtanGCIvjqlCXO9+BmsnCfEVehqZkQHeYczA03tpOWb6pon2wzvMKSsKH
21
+ ks0ApVdstSLz1kzzAqem/uHdG9FyXdbTAwH1G4ZPv69sQAFAOCgAqYmdnzedsQtE
22
+ 1LQfaQrx0twO+CZJPcRLEESjq8ScQxWRRkfuh2VeR7cEU7L7KqT10mtUwrvw7APf
23
+ DYoeCY9KyjIBjQXfbj2ke5u1hZj94Fsq9FfbEQg8ygCgwThnmkTrrKEiMSs3alYR
24
+ ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
25
+ WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
26
+ -----END CERTIFICATE-----
@@ -1,482 +1,476 @@
1
- require 'rbconfig'
2
- require 'win32/file' if File::ALT_SEPARATOR
3
-
4
- class File
5
- # The version of the ptools library.
6
- PTOOLS_VERSION = '1.3.2'
7
-
8
- # :stopdoc:
9
-
10
- # The WIN32EXTS string is used as part of a Dir[] call in certain methods.
11
- if File::ALT_SEPARATOR
12
- MSWINDOWS = true
13
- if ENV['PATHEXT']
14
- WIN32EXTS = ('.{' + ENV['PATHEXT'].tr(';', ',').tr('.','') + '}').downcase
15
- else
16
- WIN32EXTS = '.{exe,com,bat}'
17
- end
18
- else
19
- MSWINDOWS = false
20
- end
21
-
22
- IMAGE_EXT = %w[.bmp .gif .jpg .jpeg .png]
23
-
24
- # :startdoc:
25
-
26
- # Returns whether or not the file is an image. Only JPEG, PNG, BMP and
27
- # GIF are checked against.
28
- #
29
- # This method does some simple read and extension checks. For a version
30
- # that is more robust, but which depends on a 3rd party C library (and is
31
- # difficult to build on MS Windows), see the 'filemagic' library.
32
- #
33
- # Examples:
34
- #
35
- # File.image?('somefile.jpg') # => true
36
- # File.image?('somefile.txt') # => false
37
- #--
38
- # The approach I used here is based on information found at
39
- # http://en.wikipedia.org/wiki/Magic_number_(programming)
40
- #
41
- def self.image?(file)
42
- bool = IMAGE_EXT.include?(File.extname(file).downcase) # Match ext
43
- bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) || tiff?(file) # Check data
44
- bool
45
- end
46
-
47
- # Returns the name of the null device (aka bitbucket) on your platform.
48
- #
49
- # Examples:
50
- #
51
- # # On Linux
52
- # File.null # => '/dev/null'
53
- #
54
- # # On MS Windows
55
- # File.null # => 'NUL'
56
- #--
57
- # The values I used here are based on information from
58
- # http://en.wikipedia.org/wiki//dev/null
59
- #
60
- def self.null
61
- case RbConfig::CONFIG['host_os']
62
- when /mswin|win32|msdos|mingw|windows/i
63
- 'NUL'
64
- when /amiga/i
65
- 'NIL:'
66
- when /openvms/i
67
- 'NL:'
68
- else
69
- '/dev/null'
70
- end
71
- end
72
-
73
- class << self
74
- alias null_device null
75
- end
76
-
77
- # Returns whether or not +file+ is a binary non-image file, i.e. executable,
78
- # shared object, ect. Note that this is NOT guaranteed to be 100% accurate.
79
- # It performs a "best guess" based on a simple test of the first
80
- # +File.blksize+ characters, or 4096, whichever is smaller.
81
- #
82
- # Example:
83
- #
84
- # File.binary?('somefile.exe') # => true
85
- # File.binary?('somefile.txt') # => false
86
- #--
87
- # Based on code originally provided by Ryan Davis (which, in turn, is
88
- # based on Perl's -B switch).
89
- #
90
- def self.binary?(file)
91
- return false if image?(file)
92
- bytes = File.stat(file).blksize
93
- bytes = 4096 if bytes > 4096
94
- s = (File.read(file, bytes) || "")
95
- s = s.encode('US-ASCII', :undef => :replace).split(//)
96
- ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
97
- end
98
-
99
- # Looks for the first occurrence of +program+ within +path+.
100
- #
101
- # On Windows, it looks for executables ending with the suffixes defined
102
- # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
103
- # that isn't defined, which you may optionally include in +program+.
104
- #
105
- # Returns nil if not found.
106
- #
107
- # Examples:
108
- #
109
- # File.which('ruby') # => '/usr/local/bin/ruby'
110
- # File.which('foo') # => nil
111
- #
112
- def self.which(program, path=ENV['PATH'])
113
- if path.nil? || path.empty?
114
- raise ArgumentError, "path cannot be empty"
115
- end
116
-
117
- # Bail out early if an absolute path is provided.
118
- if program =~ /^\/|^[a-z]:[\\\/]/i
119
- program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
120
- found = Dir[program].first
121
- if found && File.executable?(found) && !File.directory?(found)
122
- return found
123
- else
124
- return nil
125
- end
126
- end
127
-
128
- # Iterate over each path glob the dir + program.
129
- path.split(File::PATH_SEPARATOR).each{ |dir|
130
- dir = File.expand_path(dir)
131
-
132
- next unless File.exist?(dir) # In case of bogus second argument
133
- file = File.join(dir, program)
134
-
135
- # Dir[] doesn't handle backslashes properly, so convert them. Also, if
136
- # the program name doesn't have an extension, try them all.
137
- if MSWINDOWS
138
- file = file.tr("\\", "/")
139
- file += WIN32EXTS if File.extname(program).empty?
140
- end
141
-
142
- found = Dir[file].first
143
-
144
- # Convert all forward slashes to backslashes if supported
145
- if found && File.executable?(found) && !File.directory?(found)
146
- found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
147
- return found
148
- end
149
- }
150
-
151
- nil
152
- end
153
-
154
- # Returns an array of each +program+ within +path+, or nil if it cannot
155
- # be found.
156
- #
157
- # On Windows, it looks for executables ending with the suffixes defined
158
- # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
159
- # that isn't defined, which you may optionally include in +program+.
160
- #
161
- # Examples:
162
- #
163
- # File.whereis('ruby') # => ['/usr/bin/ruby', '/usr/local/bin/ruby']
164
- # File.whereis('foo') # => nil
165
- #
166
- def self.whereis(program, path=ENV['PATH'])
167
- if path.nil? || path.empty?
168
- raise ArgumentError, "path cannot be empty"
169
- end
170
-
171
- paths = []
172
-
173
- # Bail out early if an absolute path is provided.
174
- if program =~ /^\/|^[a-z]:[\\\/]/i
175
- program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
176
- program = program.tr("\\", '/') if MSWINDOWS
177
- found = Dir[program]
178
- if found[0] && File.executable?(found[0]) && !File.directory?(found[0])
179
- if File::ALT_SEPARATOR
180
- return found.map{ |f| f.tr('/', "\\") }
181
- else
182
- return found
183
- end
184
- else
185
- return nil
186
- end
187
- end
188
-
189
- # Iterate over each path glob the dir + program.
190
- path.split(File::PATH_SEPARATOR).each{ |dir|
191
- next unless File.exist?(dir) # In case of bogus second argument
192
- file = File.join(dir, program)
193
-
194
- # Dir[] doesn't handle backslashes properly, so convert them. Also, if
195
- # the program name doesn't have an extension, try them all.
196
- if MSWINDOWS
197
- file = file.tr("\\", "/")
198
- file += WIN32EXTS if File.extname(program).empty?
199
- end
200
-
201
- found = Dir[file].first
202
-
203
- # Convert all forward slashes to backslashes if supported
204
- if found && File.executable?(found) && !File.directory?(found)
205
- found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
206
- paths << found
207
- end
208
- }
209
-
210
- paths.empty? ? nil : paths.uniq
211
- end
212
-
213
- # In block form, yields the first +num_lines+ from +filename+. In non-block
214
- # form, returns an Array of +num_lines+
215
- #
216
- # Examples:
217
- #
218
- # # Return an array
219
- # File.head('somefile.txt') # => ['This is line1', 'This is line2', ...]
220
- #
221
- # # Use a block
222
- # File.head('somefile.txt'){ |line| puts line }
223
- #
224
- def self.head(filename, num_lines=10)
225
- a = []
226
-
227
- IO.foreach(filename){ |line|
228
- break if num_lines <= 0
229
- num_lines -= 1
230
- if block_given?
231
- yield line
232
- else
233
- a << line
234
- end
235
- }
236
-
237
- return a.empty? ? nil : a # Return nil in block form
238
- end
239
-
240
- # In block form, yields the last +num_lines+ of file +filename+.
241
- # In non-block form, it returns the lines as an array.
242
- #
243
- # Example:
244
- #
245
- # File.tail('somefile.txt') # => ['This is line7', 'This is line8', ...]
246
- #
247
- # If you're looking for tail -f functionality, please use the file-tail
248
- # gem instead.
249
- #
250
- #--
251
- # Internally I'm using a 64 chunk of memory at a time. I may allow the size
252
- # to be configured in the future as an optional 3rd argument.
253
- #
254
- def self.tail(filename, num_lines=10)
255
- tail_size = 2**16 # 64k chunks
256
-
257
- # MS Windows gets unhappy if you try to seek backwards past the
258
- # end of the file, so we have some extra checks here and later.
259
- file_size = File.size(filename)
260
- read_bytes = file_size % tail_size
261
- read_bytes = tail_size if read_bytes == 0
262
-
263
- line_sep = File::ALT_SEPARATOR ? "\r\n" : "\n"
264
-
265
- buf = ''
266
-
267
- # Open in binary mode to ensure line endings aren't converted.
268
- File.open(filename, 'rb'){ |fh|
269
- position = file_size - read_bytes # Set the starting read position
270
-
271
- # Loop until we have the lines or run out of file
272
- while buf.scan(line_sep).size <= num_lines and position >= 0
273
- fh.seek(position, IO::SEEK_SET)
274
- buf = fh.read(read_bytes) + buf
275
- read_bytes = tail_size
276
- position -= read_bytes
277
- end
278
- }
279
-
280
- lines = buf.split(line_sep).pop(num_lines)
281
-
282
- if block_given?
283
- lines.each{ |line| yield line }
284
- else
285
- lines
286
- end
287
- end
288
-
289
- # Converts a text file from one OS platform format to another, ala
290
- # 'dos2unix'. The possible values for +platform+ include:
291
- #
292
- # * MS Windows -> dos, windows, win32, mswin
293
- # * Unix/BSD -> unix, linux, bsd, osx, darwin, sunos, solaris
294
- # * Mac -> mac, macintosh, apple
295
- #
296
- # You may also specify 'local', in which case your CONFIG['host_os'] value
297
- # will be used. This is the default.
298
- #
299
- # Note that this method is only valid for an ftype of "file". Otherwise a
300
- # TypeError will be raised. If an invalid format value is received, an
301
- # ArgumentError is raised.
302
- #
303
- def self.nl_convert(old_file, new_file = old_file, platform = 'local')
304
- unless File::Stat.new(old_file).file?
305
- raise ArgumentError, 'Only valid for plain text files'
306
- end
307
-
308
- format = nl_for_platform(platform)
309
-
310
- orig = $\ # $OUTPUT_RECORD_SEPARATOR
311
- $\ = format
312
-
313
- if old_file == new_file
314
- require 'fileutils'
315
- require 'tempfile'
316
-
317
- begin
318
- temp_name = Time.new.strftime("%Y%m%d%H%M%S")
319
- tf = Tempfile.new('ruby_temp_' + temp_name)
320
- tf.open
321
-
322
- IO.foreach(old_file){ |line|
323
- line.chomp!
324
- tf.print line
325
- }
326
- ensure
327
- tf.close if tf && !tf.closed?
328
- end
329
-
330
- File.delete(old_file)
331
- FileUtils.mv(tf.path, old_file)
332
- else
333
- begin
334
- nf = File.new(new_file, 'w')
335
- IO.foreach(old_file){ |line|
336
- line.chomp!
337
- nf.print line
338
- }
339
- ensure
340
- nf.close if nf && !nf.closed?
341
- end
342
- end
343
-
344
- $\ = orig
345
- self
346
- end
347
-
348
- # Changes the access and modification time if present, or creates a 0
349
- # byte file +filename+ if it doesn't already exist.
350
- #
351
- def self.touch(filename)
352
- if File.exist?(filename)
353
- time = Time.now
354
- File.utime(time, time, filename)
355
- else
356
- File.open(filename, 'w'){}
357
- end
358
- self
359
- end
360
-
361
- # With no arguments, returns a four element array consisting of the number
362
- # of bytes, characters, words and lines in filename, respectively.
363
- #
364
- # Valid options are 'bytes', 'characters' (or just 'chars'), 'words' and
365
- # 'lines'.
366
- #
367
- def self.wc(filename, option='all')
368
- option.downcase!
369
- valid = %w/all bytes characters chars lines words/
370
-
371
- unless valid.include?(option)
372
- raise ArgumentError, "Invalid option: '#{option}'"
373
- end
374
-
375
- n = 0
376
-
377
- if option == 'lines'
378
- IO.foreach(filename){ n += 1 }
379
- return n
380
- elsif option == 'bytes'
381
- File.open(filename){ |f|
382
- f.each_byte{ n += 1 }
383
- }
384
- return n
385
- elsif option == 'characters' || option == 'chars'
386
- File.open(filename){ |f|
387
- while f.getc
388
- n += 1
389
- end
390
- }
391
- return n
392
- elsif option == 'words'
393
- IO.foreach(filename){ |line|
394
- n += line.split.length
395
- }
396
- return n
397
- else
398
- bytes,chars,lines,words = 0,0,0,0
399
- IO.foreach(filename){ |line|
400
- lines += 1
401
- words += line.split.length
402
- chars += line.split('').length
403
- }
404
- File.open(filename){ |f|
405
- while f.getc
406
- bytes += 1
407
- end
408
- }
409
- return [bytes,chars,words,lines]
410
- end
411
- end
412
-
413
- # Already provided by win32-file on MS Windows
414
- unless respond_to?(:sparse?)
415
- # Returns whether or not +file+ is a sparse file.
416
- #
417
- # A sparse file is a any file where its size is greater than the number
418
- # of 512k blocks it consumes, i.e. its apparent and actual file size is
419
- # not the same.
420
- #
421
- # See http://en.wikipedia.org/wiki/Sparse_file for more information.
422
- #
423
- def self.sparse?(file)
424
- stats = File.stat(file)
425
- stats.size > stats.blocks * 512
426
- end
427
- end
428
-
429
- private
430
-
431
- def self.nl_for_platform(platform)
432
- platform = RbConfig::CONFIG["host_os"] if platform == 'local'
433
-
434
- case platform
435
- when /dos|windows|win32|mswin|mingw/i
436
- return "\cM\cJ"
437
- when /unix|linux|bsd|cygwin|osx|darwin|solaris|sunos/i
438
- return "\cJ"
439
- when /mac|apple|macintosh/i
440
- return "\cM"
441
- else
442
- raise ArgumentError, "Invalid platform string"
443
- end
444
- end
445
-
446
- def self.bmp?(file)
447
- IO.read(file, 3) == "BM6"
448
- end
449
-
450
- def self.jpg?(file)
451
- IO.read(file, 10, nil, :encoding => 'binary') == "\377\330\377\340\000\020JFIF".force_encoding(Encoding::BINARY)
452
- end
453
-
454
- def self.png?(file)
455
- IO.read(file, 4, nil, :encoding => 'binary') == "\211PNG".force_encoding(Encoding::BINARY)
456
- end
457
-
458
- def self.gif?(file)
459
- ['GIF89a', 'GIF97a'].include?(IO.read(file, 6))
460
- end
461
-
462
- def self.tiff?(file)
463
- return false if File.size(file) < 12
464
-
465
- bytes = IO.read(file, 4)
466
-
467
- # II is Intel, MM is Motorola
468
- if bytes[0..1] != 'II'&& bytes[0..1] != 'MM'
469
- return false
470
- end
471
-
472
- if bytes[0..1] == 'II' && bytes[2..3].ord != 42
473
- return false
474
- end
475
-
476
- if bytes[0..1] == 'MM' && bytes[2..3].reverse.ord != 42
477
- return false
478
- end
479
-
480
- true
481
- end
482
- end
1
+ require 'rbconfig'
2
+ require 'win32/file' if File::ALT_SEPARATOR
3
+
4
+ class File
5
+ # The version of the ptools library.
6
+ PTOOLS_VERSION = '1.4.0'.freeze
7
+
8
+ # :stopdoc:
9
+
10
+ # The WIN32EXTS string is used as part of a Dir[] call in certain methods.
11
+ if File::ALT_SEPARATOR
12
+ MSWINDOWS = true
13
+ if ENV['PATHEXT']
14
+ WIN32EXTS = ('.{' + ENV['PATHEXT'].tr(';', ',').tr('.','') + '}').downcase
15
+ else
16
+ WIN32EXTS = '.{exe,com,bat}'
17
+ end
18
+ else
19
+ MSWINDOWS = false
20
+ end
21
+
22
+ IMAGE_EXT = %w[.bmp .gif .jpg .jpeg .png]
23
+
24
+ # :startdoc:
25
+
26
+ # Returns whether or not the file is an image. Only JPEG, PNG, BMP,
27
+ # GIF, and ICO are checked against.
28
+ #
29
+ # This method does some simple read and extension checks. For a version
30
+ # that is more robust, but which depends on a 3rd party C library (and is
31
+ # difficult to build on MS Windows), see the 'filemagic' library.
32
+ #
33
+ # Examples:
34
+ #
35
+ # File.image?('somefile.jpg') # => true
36
+ # File.image?('somefile.txt') # => false
37
+ #--
38
+ # The approach I used here is based on information found at
39
+ # http://en.wikipedia.org/wiki/Magic_number_(programming)
40
+ #
41
+ def self.image?(file)
42
+ bool = IMAGE_EXT.include?(File.extname(file).downcase)
43
+ bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) || tiff?(file) || ico?(file)
44
+ bool
45
+ end
46
+
47
+ # Returns whether or not +file+ is a binary non-image file, i.e. executable,
48
+ # shared object, ect. Note that this is NOT guaranteed to be 100% accurate.
49
+ # It performs a "best guess" based on a simple test of the first
50
+ # +File.blksize+ characters, or 4096, whichever is smaller.
51
+ #
52
+ # By default it will check to see if more than 30 percent of the characters
53
+ # are non-text characters. If so, the method returns true. You can configure
54
+ # this percentage by passing your own as a second argument.
55
+ #
56
+ # Example:
57
+ #
58
+ # File.binary?('somefile.exe') # => true
59
+ # File.binary?('somefile.txt') # => false
60
+ #--
61
+ # Based on code originally provided by Ryan Davis (which, in turn, is
62
+ # based on Perl's -B switch).
63
+ #
64
+ def self.binary?(file, percentage = 0.30)
65
+ return false if File.stat(file).zero?
66
+ return false if image?(file)
67
+ return false if check_bom?(file)
68
+ bytes = File.stat(file).blksize
69
+ bytes = 4096 if bytes > 4096
70
+ s = (File.read(file, bytes) || "")
71
+ s = s.encode('US-ASCII', :undef => :replace).split(//)
72
+ ((s.size - s.grep(" ".."~").size) / s.size.to_f) > percentage
73
+ end
74
+
75
+ # Looks for the first occurrence of +program+ within +path+.
76
+ #
77
+ # On Windows, it looks for executables ending with the suffixes defined
78
+ # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
79
+ # that isn't defined, which you may optionally include in +program+.
80
+ #
81
+ # Returns nil if not found.
82
+ #
83
+ # Examples:
84
+ #
85
+ # File.which('ruby') # => '/usr/local/bin/ruby'
86
+ # File.which('foo') # => nil
87
+ #
88
+ def self.which(program, path=ENV['PATH'])
89
+ if path.nil? || path.empty?
90
+ raise ArgumentError, "path cannot be empty"
91
+ end
92
+
93
+ # Bail out early if an absolute path is provided.
94
+ if program =~ /^\/|^[a-z]:[\\\/]/i
95
+ program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
96
+ found = Dir[program].first
97
+ if found && File.executable?(found) && !File.directory?(found)
98
+ return found
99
+ else
100
+ return nil
101
+ end
102
+ end
103
+
104
+ # Iterate over each path glob the dir + program.
105
+ path.split(File::PATH_SEPARATOR).each{ |dir|
106
+ dir = File.expand_path(dir)
107
+
108
+ next unless File.exist?(dir) # In case of bogus second argument
109
+ file = File.join(dir, program)
110
+
111
+ # Dir[] doesn't handle backslashes properly, so convert them. Also, if
112
+ # the program name doesn't have an extension, try them all.
113
+ if MSWINDOWS
114
+ file = file.tr("\\", "/")
115
+ file += WIN32EXTS if File.extname(program).empty?
116
+ end
117
+
118
+ found = Dir[file].first
119
+
120
+ # Convert all forward slashes to backslashes if supported
121
+ if found && File.executable?(found) && !File.directory?(found)
122
+ found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
123
+ return found
124
+ end
125
+ }
126
+
127
+ nil
128
+ end
129
+
130
+ # Returns an array of each +program+ within +path+, or nil if it cannot
131
+ # be found.
132
+ #
133
+ # On Windows, it looks for executables ending with the suffixes defined
134
+ # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
135
+ # that isn't defined, which you may optionally include in +program+.
136
+ #
137
+ # Examples:
138
+ #
139
+ # File.whereis('ruby') # => ['/usr/bin/ruby', '/usr/local/bin/ruby']
140
+ # File.whereis('foo') # => nil
141
+ #
142
+ def self.whereis(program, path=ENV['PATH'])
143
+ if path.nil? || path.empty?
144
+ raise ArgumentError, "path cannot be empty"
145
+ end
146
+
147
+ paths = []
148
+
149
+ # Bail out early if an absolute path is provided.
150
+ if program =~ /^\/|^[a-z]:[\\\/]/i
151
+ program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
152
+ program = program.tr("\\", '/') if MSWINDOWS
153
+ found = Dir[program]
154
+ if found[0] && File.executable?(found[0]) && !File.directory?(found[0])
155
+ if File::ALT_SEPARATOR
156
+ return found.map{ |f| f.tr('/', "\\") }
157
+ else
158
+ return found
159
+ end
160
+ else
161
+ return nil
162
+ end
163
+ end
164
+
165
+ # Iterate over each path glob the dir + program.
166
+ path.split(File::PATH_SEPARATOR).each{ |dir|
167
+ next unless File.exist?(dir) # In case of bogus second argument
168
+ file = File.join(dir, program)
169
+
170
+ # Dir[] doesn't handle backslashes properly, so convert them. Also, if
171
+ # the program name doesn't have an extension, try them all.
172
+ if MSWINDOWS
173
+ file = file.tr("\\", "/")
174
+ file += WIN32EXTS if File.extname(program).empty?
175
+ end
176
+
177
+ found = Dir[file].first
178
+
179
+ # Convert all forward slashes to backslashes if supported
180
+ if found && File.executable?(found) && !File.directory?(found)
181
+ found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
182
+ paths << found
183
+ end
184
+ }
185
+
186
+ paths.empty? ? nil : paths.uniq
187
+ end
188
+
189
+ # In block form, yields the first +num_lines+ from +filename+. In non-block
190
+ # form, returns an Array of +num_lines+
191
+ #
192
+ # Examples:
193
+ #
194
+ # # Return an array
195
+ # File.head('somefile.txt') # => ['This is line1', 'This is line2', ...]
196
+ #
197
+ # # Use a block
198
+ # File.head('somefile.txt'){ |line| puts line }
199
+ #
200
+ def self.head(filename, num_lines=10)
201
+ a = []
202
+
203
+ IO.foreach(filename){ |line|
204
+ break if num_lines <= 0
205
+ num_lines -= 1
206
+ if block_given?
207
+ yield line
208
+ else
209
+ a << line
210
+ end
211
+ }
212
+
213
+ return a.empty? ? nil : a # Return nil in block form
214
+ end
215
+
216
+ # In block form, yields the last +num_lines+ of file +filename+.
217
+ # In non-block form, it returns the lines as an array.
218
+ #
219
+ # Example:
220
+ #
221
+ # File.tail('somefile.txt') # => ['This is line7', 'This is line8', ...]
222
+ #
223
+ # If you're looking for tail -f functionality, please use the file-tail
224
+ # gem instead.
225
+ #
226
+ #--
227
+ # Internally I'm using a 64 chunk of memory at a time. I may allow the size
228
+ # to be configured in the future as an optional 3rd argument.
229
+ #
230
+ def self.tail(filename, num_lines=10)
231
+ tail_size = 2**16 # 64k chunks
232
+
233
+ # MS Windows gets unhappy if you try to seek backwards past the
234
+ # end of the file, so we have some extra checks here and later.
235
+ file_size = File.size(filename)
236
+ read_bytes = file_size % tail_size
237
+ read_bytes = tail_size if read_bytes == 0
238
+
239
+ line_sep = File::ALT_SEPARATOR ? "\r\n" : "\n"
240
+
241
+ buf = ''
242
+
243
+ # Open in binary mode to ensure line endings aren't converted.
244
+ File.open(filename, 'rb'){ |fh|
245
+ position = file_size - read_bytes # Set the starting read position
246
+
247
+ # Loop until we have the lines or run out of file
248
+ while buf.scan(line_sep).size <= num_lines and position >= 0
249
+ fh.seek(position, IO::SEEK_SET)
250
+ buf = fh.read(read_bytes) + buf
251
+ read_bytes = tail_size
252
+ position -= read_bytes
253
+ end
254
+ }
255
+
256
+ lines = buf.split(line_sep).pop(num_lines)
257
+
258
+ if block_given?
259
+ lines.each{ |line| yield line }
260
+ else
261
+ lines
262
+ end
263
+ end
264
+
265
+ # Converts a text file from one OS platform format to another, ala
266
+ # 'dos2unix'. The possible values for +platform+ include:
267
+ #
268
+ # * MS Windows -> dos, windows, win32, mswin
269
+ # * Unix/BSD -> unix, linux, bsd, osx, darwin, sunos, solaris
270
+ # * Mac -> mac, macintosh, apple
271
+ #
272
+ # You may also specify 'local', in which case your CONFIG['host_os'] value
273
+ # will be used. This is the default.
274
+ #
275
+ # Note that this method is only valid for an ftype of "file". Otherwise a
276
+ # TypeError will be raised. If an invalid format value is received, an
277
+ # ArgumentError is raised.
278
+ #
279
+ def self.nl_convert(old_file, new_file = old_file, platform = 'local')
280
+ unless File::Stat.new(old_file).file?
281
+ raise ArgumentError, 'Only valid for plain text files'
282
+ end
283
+
284
+ format = nl_for_platform(platform)
285
+
286
+ orig = $\ # $OUTPUT_RECORD_SEPARATOR
287
+ $\ = format
288
+
289
+ if old_file == new_file
290
+ require 'fileutils'
291
+ require 'tempfile'
292
+
293
+ begin
294
+ temp_name = Time.new.strftime("%Y%m%d%H%M%S")
295
+ tf = Tempfile.new('ruby_temp_' + temp_name)
296
+ tf.open
297
+
298
+ IO.foreach(old_file){ |line|
299
+ line.chomp!
300
+ tf.print line
301
+ }
302
+ ensure
303
+ tf.close if tf && !tf.closed?
304
+ end
305
+
306
+ File.delete(old_file)
307
+ FileUtils.mv(tf.path, old_file)
308
+ else
309
+ begin
310
+ nf = File.new(new_file, 'w')
311
+ IO.foreach(old_file){ |line|
312
+ line.chomp!
313
+ nf.print line
314
+ }
315
+ ensure
316
+ nf.close if nf && !nf.closed?
317
+ end
318
+ end
319
+
320
+ $\ = orig
321
+ self
322
+ end
323
+
324
+ # Changes the access and modification time if present, or creates a 0
325
+ # byte file +filename+ if it doesn't already exist.
326
+ #
327
+ def self.touch(filename)
328
+ if File.exist?(filename)
329
+ time = Time.now
330
+ File.utime(time, time, filename)
331
+ else
332
+ File.open(filename, 'w'){}
333
+ end
334
+ self
335
+ end
336
+
337
+ # With no arguments, returns a four element array consisting of the number
338
+ # of bytes, characters, words and lines in filename, respectively.
339
+ #
340
+ # Valid options are 'bytes', 'characters' (or just 'chars'), 'words' and
341
+ # 'lines'.
342
+ #
343
+ def self.wc(filename, option='all')
344
+ option.downcase!
345
+ valid = %w/all bytes characters chars lines words/
346
+
347
+ unless valid.include?(option)
348
+ raise ArgumentError, "Invalid option: '#{option}'"
349
+ end
350
+
351
+ n = 0
352
+
353
+ if option == 'lines'
354
+ IO.foreach(filename){ n += 1 }
355
+ return n
356
+ elsif option == 'bytes'
357
+ File.open(filename){ |f|
358
+ f.each_byte{ n += 1 }
359
+ }
360
+ return n
361
+ elsif option == 'characters' || option == 'chars'
362
+ File.open(filename){ |f|
363
+ while f.getc
364
+ n += 1
365
+ end
366
+ }
367
+ return n
368
+ elsif option == 'words'
369
+ IO.foreach(filename){ |line|
370
+ n += line.split.length
371
+ }
372
+ return n
373
+ else
374
+ bytes,chars,lines,words = 0,0,0,0
375
+ IO.foreach(filename){ |line|
376
+ lines += 1
377
+ words += line.split.length
378
+ chars += line.split('').length
379
+ }
380
+ File.open(filename){ |f|
381
+ while f.getc
382
+ bytes += 1
383
+ end
384
+ }
385
+ return [bytes,chars,words,lines]
386
+ end
387
+ end
388
+
389
+ # Already provided by win32-file on MS Windows
390
+ unless respond_to?(:sparse?)
391
+ # Returns whether or not +file+ is a sparse file.
392
+ #
393
+ # A sparse file is a any file where its size is greater than the number
394
+ # of 512k blocks it consumes, i.e. its apparent and actual file size is
395
+ # not the same.
396
+ #
397
+ # See http://en.wikipedia.org/wiki/Sparse_file for more information.
398
+ #
399
+ def self.sparse?(file)
400
+ stats = File.stat(file)
401
+ stats.size > stats.blocks * 512
402
+ end
403
+ end
404
+
405
+ private
406
+
407
+ # Returns whether or not the given +text+ contains a BOM marker.
408
+ # If present, we can generally assume it's a text file.
409
+ #
410
+ def self.check_bom?(file)
411
+ text = File.read(file, 4).force_encoding('utf-8')
412
+
413
+ bool = false
414
+ bool = true if text[0,3] == "\xEF\xBB\xBF"
415
+ bool = true if text[0,4] == "\x00\x00\xFE\xFF" || text[0,4] == "\xFF\xFE\x00\x00"
416
+ bool = true if text[0,2] == "\xFF\xFE" || text[0,2] == "\xFE\xFF"
417
+
418
+ bool
419
+ end
420
+
421
+ def self.nl_for_platform(platform)
422
+ platform = RbConfig::CONFIG["host_os"] if platform == 'local'
423
+
424
+ case platform
425
+ when /dos|windows|win32|mswin|mingw/i
426
+ return "\cM\cJ"
427
+ when /unix|linux|bsd|cygwin|osx|darwin|solaris|sunos/i
428
+ return "\cJ"
429
+ when /mac|apple|macintosh/i
430
+ return "\cM"
431
+ else
432
+ raise ArgumentError, "Invalid platform string"
433
+ end
434
+ end
435
+
436
+ def self.bmp?(file)
437
+ IO.read(file, 3) == "BM6"
438
+ end
439
+
440
+ def self.jpg?(file)
441
+ IO.read(file, 10, nil, :encoding => 'binary') == "\377\330\377\340\000\020JFIF".force_encoding(Encoding::BINARY)
442
+ end
443
+
444
+ def self.png?(file)
445
+ IO.read(file, 4, nil, :encoding => 'binary') == "\211PNG".force_encoding(Encoding::BINARY)
446
+ end
447
+
448
+ def self.gif?(file)
449
+ ['GIF89a', 'GIF97a'].include?(IO.read(file, 6))
450
+ end
451
+
452
+ def self.tiff?(file)
453
+ return false if File.size(file) < 12
454
+
455
+ bytes = IO.read(file, 4)
456
+
457
+ # II is Intel, MM is Motorola
458
+ if bytes[0..1] != 'II'&& bytes[0..1] != 'MM'
459
+ return false
460
+ end
461
+
462
+ if bytes[0..1] == 'II' && bytes[2..3].ord != 42
463
+ return false
464
+ end
465
+
466
+ if bytes[0..1] == 'MM' && bytes[2..3].reverse.ord != 42
467
+ return false
468
+ end
469
+
470
+ true
471
+ end
472
+
473
+ def self.ico?(file)
474
+ ["\000\000\001\000", "\000\000\002\000"].include?(IO.read(file, 4, nil, :encoding => 'binary'))
475
+ end
476
+ end