ptools 1.3.2-universal-mingw32 → 1.3.3-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,139 +1,135 @@
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
+ 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
+ require 'rubygems/package'
20
+ spec = eval(IO.read('ptools.gemspec'))
21
+ spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
22
+ Gem::Package.build(spec)
23
+ end
24
+
25
+ desc 'Install the ptools gem'
26
+ task :install => [:create] do
27
+ file = Dir["*.gem"].first
28
+ if RUBY_PLATFORM == 'java'
29
+ sh "jruby -S gem install -l #{file}"
30
+ else
31
+ sh "gem install -l #{file}"
32
+ end
33
+ end
34
+ end
35
+
36
+ Rake::TestTask.new do |t|
37
+ task :test => :clean
38
+ t.verbose = true
39
+ t.warning = true
40
+ end
41
+
42
+ namespace 'test' do
43
+ desc "Check test coverage using rcov"
44
+ task :coverage => [:clean] do
45
+ require 'rcov'
46
+ rm_rf 'coverage'
47
+ sh "rcov -Ilib test/test*.rb"
48
+ end
49
+
50
+ Rake::TestTask.new('binary') do |t|
51
+ t.libs << 'test'
52
+ t.verbose = true
53
+ t.warning = true
54
+ t.test_files = FileList['test/test_binary.rb']
55
+ end
56
+
57
+ Rake::TestTask.new('constants') do |t|
58
+ t.libs << 'test'
59
+ t.verbose = true
60
+ t.warning = true
61
+ t.test_files = FileList['test/test_constants.rb']
62
+ end
63
+
64
+ Rake::TestTask.new('head') do |t|
65
+ t.libs << 'test'
66
+ t.verbose = true
67
+ t.warning = true
68
+ t.test_files = FileList['test/test_head.rb']
69
+ end
70
+
71
+ Rake::TestTask.new('image') do |t|
72
+ t.libs << 'test'
73
+ t.verbose = true
74
+ t.warning = true
75
+ t.test_files = FileList['test/test_image.rb']
76
+ end
77
+
78
+ Rake::TestTask.new('nlconvert') do |t|
79
+ t.libs << 'test'
80
+ t.verbose = true
81
+ t.warning = true
82
+ t.test_files = FileList['test/test_nlconvert.rb']
83
+ end
84
+
85
+ Rake::TestTask.new('null') do |t|
86
+ t.libs << 'test'
87
+ t.verbose = true
88
+ t.warning = true
89
+ t.test_files = FileList['test/test_null.rb']
90
+ end
91
+
92
+ Rake::TestTask.new('sparse') do |t|
93
+ t.libs << 'test'
94
+ t.verbose = true
95
+ t.warning = true
96
+ t.test_files = FileList['test/test_is_sparse.rb']
97
+ end
98
+
99
+ Rake::TestTask.new('tail') do |t|
100
+ t.libs << 'test'
101
+ t.verbose = true
102
+ t.warning = true
103
+ t.test_files = FileList['test/test_tail.rb']
104
+ end
105
+
106
+ Rake::TestTask.new('touch') do |t|
107
+ t.libs << 'test'
108
+ t.verbose = true
109
+ t.warning = true
110
+ t.test_files = FileList['test/test_touch.rb']
111
+ end
112
+
113
+ Rake::TestTask.new('wc') do |t|
114
+ t.libs << 'test'
115
+ t.verbose = true
116
+ t.warning = true
117
+ t.test_files = FileList['test/test_wc.rb']
118
+ end
119
+
120
+ Rake::TestTask.new('whereis') do |t|
121
+ t.libs << 'test'
122
+ t.verbose = true
123
+ t.warning = true
124
+ t.test_files = FileList['test/test_whereis.rb']
125
+ end
126
+
127
+ Rake::TestTask.new('which') do |t|
128
+ t.libs << 'test'
129
+ t.verbose = true
130
+ t.warning = true
131
+ t.test_files = FileList['test/test_which.rb']
132
+ end
133
+ end
134
+
135
+ task :default => :test
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
3
+ cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
4
+ MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
5
+ ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
6
+ bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
7
+ Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
8
+ S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
9
+ gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
10
+ FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
11
+ zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
12
+ DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
13
+ nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
14
+ bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
15
+ ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
16
+ tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
17
+ /sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
18
+ wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
19
+ EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
20
+ tGSHgAmcLlkdGgan182qsE/4kKM=
21
+ -----END CERTIFICATE-----
@@ -1,482 +1,482 @@
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.3.3'
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