ptools 1.4.1 → 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +13 -0
- data/Gemfile +1 -10
- data/MANIFEST.md +1 -0
- data/README.md +29 -24
- data/Rakefile +4 -1
- data/lib/ptools.rb +120 -120
- data/ptools.gemspec +10 -7
- data/spec/binary_spec.rb +30 -30
- data/spec/constants_spec.rb +4 -14
- data/spec/head_spec.rb +18 -18
- data/spec/image_spec.rb +163 -26
- data/spec/img/jpg_no_ext +0 -0
- data/spec/img/test.bmp +0 -0
- data/spec/img/test.tiff +0 -0
- data/spec/nlconvert_spec.rb +51 -53
- data/spec/sparse_spec.rb +17 -21
- data/spec/spec_helper.rb +7 -0
- data/spec/tail_spec.rb +55 -55
- data/spec/touch_spec.rb +22 -22
- data/spec/wc_spec.rb +28 -28
- data/spec/whereis_spec.rb +29 -30
- data/spec/which_spec.rb +52 -60
- data.tar.gz.sig +0 -0
- metadata +37 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df90888f6e358438d4c5d59955eccb7b9c9f345e9c90383987cac8b949653122
|
4
|
+
data.tar.gz: 186d89c2a1e47a9feb46c950dfbe35c703a8cb185cefec2bdd82449cc6a89c4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a375d13cfb5d9a5b9ca69e473e0ad6e55fee38f3fe29fa15b7071fa3677be356a4fbaf2fd1330b2d12ae74e9402079a5664c96d056ff3612b3f1882318a2b9ad
|
7
|
+
data.tar.gz: 560bddce5d94f270c334746ca97e239aecd127534566d9171b8022bf5fda9981104cafecccee1440fac564a5fe87901a0542fd33d48485d021f35bce710e4ea8
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 1.4.3 - 26-Nov-2022
|
2
|
+
* Added more image specs.
|
3
|
+
* Updated the File.bmp? method. It now looks at the first six bytes, and
|
4
|
+
uses four of those bytes to verify the file size.
|
5
|
+
|
6
|
+
## 1.4.2 - 6-Jan-2021
|
7
|
+
* Fixed a private access modifier that wasn't actually working as intended.
|
8
|
+
* Two Windows constants that were never meant for public consumption have been marked private.
|
9
|
+
* Minor tweak to one of the touch specs.
|
10
|
+
* The Gemfile now just uses the gemspec.
|
11
|
+
* The image? singleton method was effectively ignoring filename extensions. This
|
12
|
+
has been fixed, though with the option to disable that check if desired.
|
13
|
+
|
1
14
|
## 1.4.1 - 29-Dec-2020
|
2
15
|
* Switch from rdoc to markdown since github is not rendering rdoc properly.
|
3
16
|
* Added metadata to gemspec.
|
data/Gemfile
CHANGED
data/MANIFEST.md
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,18 @@
|
|
1
|
+
[![Ruby](https://github.com/djberg96/ptools/actions/workflows/ruby.yml/badge.svg)](https://github.com/djberg96/ptools/actions/workflows/ruby.yml)
|
2
|
+
|
1
3
|
## Description
|
2
|
-
|
3
|
-
|
4
|
+
The ptools (power tools) library is an additional set of commands for the
|
5
|
+
File class based on Unix command line tools.
|
4
6
|
|
5
7
|
## Prerequisites
|
6
|
-
|
8
|
+
On MS Windows you will need the win32-file gem.
|
7
9
|
|
8
10
|
## Installation
|
9
11
|
`gem install ptools`
|
10
12
|
|
13
|
+
## Adding the trusted cert
|
14
|
+
`gem cert --add <(curl -Ls https://raw.githubusercontent.com/djberg96/ptools/main/certs/djberg96_pub.pem)`
|
15
|
+
|
11
16
|
## Synopsis
|
12
17
|
```ruby
|
13
18
|
require "ptools"
|
@@ -29,42 +34,42 @@ File.nl_convert("myfile", "newfile", "dos")
|
|
29
34
|
```
|
30
35
|
|
31
36
|
## Known Bugs
|
32
|
-
|
33
|
-
|
37
|
+
The File.which and File.whereis methods may fail when using JRuby on Windows.
|
38
|
+
See https://github.com/jruby/jruby/issues/2291 for details.
|
34
39
|
|
35
|
-
|
40
|
+
Please report any other issues on the github project page.
|
36
41
|
|
37
|
-
|
42
|
+
http://www.github.com/djberg96/ptools
|
38
43
|
|
39
44
|
## Acknowledgements
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
The `File.which` method was originally adopted from the `FileWhich` code posted
|
46
|
+
by Michael Granger on the now defunct rubygarden.org website. That code was
|
47
|
+
later replaced by a version based on the `ruby-which` library.
|
43
48
|
|
44
|
-
|
45
|
-
|
49
|
+
The `File.nl_convert` method is based on the nlcvt program found at
|
50
|
+
http://www.perl.com/language/ppt/src/nlcvt/nlcvt, written by Tom Christiansen.
|
46
51
|
|
47
|
-
|
48
|
-
|
52
|
+
The `binary?` method was based almost entirely on a blog post by Ryan
|
53
|
+
Davis (who, in turn, based his code on Perl's -B switch).
|
49
54
|
|
50
|
-
|
55
|
+
Thanks go to Matt Hoyle for help with the File.tail method.
|
51
56
|
|
52
|
-
|
57
|
+
And thanks to any and all contributors!
|
53
58
|
|
54
59
|
## Future Plans
|
55
|
-
|
60
|
+
Add whatever other tools people think might be useful.
|
56
61
|
|
57
62
|
## License
|
58
|
-
|
63
|
+
Artistic-2.0
|
59
64
|
|
60
65
|
## Copyright
|
61
|
-
|
62
|
-
|
66
|
+
(C) 2003-2022 Daniel J. Berger
|
67
|
+
All Rights Reserved.
|
63
68
|
|
64
69
|
## Warranty
|
65
|
-
|
66
|
-
|
67
|
-
|
70
|
+
This package is provided "as is" and without any express or
|
71
|
+
implied warranties, including, without limitation, the implied
|
72
|
+
warranties of merchantability and fitness for a particular purpose.
|
68
73
|
|
69
74
|
## Author
|
70
|
-
|
75
|
+
Daniel J. Berger
|
data/Rakefile
CHANGED
@@ -3,6 +3,7 @@ require 'rake/clean'
|
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'rbconfig'
|
5
5
|
require 'rspec/core/rake_task'
|
6
|
+
require 'rubocop/rake_task'
|
6
7
|
include RbConfig
|
7
8
|
|
8
9
|
CLEAN.include("**/*.gem", "**/*.rbc", "**/*coverage*", "**/*.lock")
|
@@ -18,7 +19,7 @@ namespace 'gem' do
|
|
18
19
|
desc 'Create the ptools gem'
|
19
20
|
task :create => [:clean] do
|
20
21
|
require 'rubygems/package'
|
21
|
-
spec =
|
22
|
+
spec = Gem::Specification.load('ptools.gemspec')
|
22
23
|
spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
|
23
24
|
Gem::Package.build(spec)
|
24
25
|
end
|
@@ -84,4 +85,6 @@ namespace 'spec' do
|
|
84
85
|
end
|
85
86
|
end
|
86
87
|
|
88
|
+
RuboCop::RakeTask.new
|
89
|
+
|
87
90
|
task :default => 'spec:all'
|
data/lib/ptools.rb
CHANGED
@@ -3,7 +3,7 @@ require 'win32/file' if File::ALT_SEPARATOR
|
|
3
3
|
|
4
4
|
class File
|
5
5
|
# The version of the ptools library.
|
6
|
-
PTOOLS_VERSION = '1.4.
|
6
|
+
PTOOLS_VERSION = '1.4.3'.freeze
|
7
7
|
|
8
8
|
# :stopdoc:
|
9
9
|
|
@@ -11,25 +11,34 @@ class File
|
|
11
11
|
if File::ALT_SEPARATOR
|
12
12
|
MSWINDOWS = true
|
13
13
|
if ENV['PATHEXT']
|
14
|
-
WIN32EXTS =
|
14
|
+
WIN32EXTS = ".{#{ENV['PATHEXT'].tr(';', ',').tr('.', '')}}".downcase
|
15
15
|
else
|
16
|
-
WIN32EXTS = '.{exe,com,bat}'
|
16
|
+
WIN32EXTS = '.{exe,com,bat}'.freeze
|
17
17
|
end
|
18
18
|
else
|
19
19
|
MSWINDOWS = false
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
if File::ALT_SEPARATOR
|
23
|
+
private_constant :WIN32EXTS
|
24
|
+
private_constant :MSWINDOWS
|
25
|
+
end
|
26
|
+
|
27
|
+
IMAGE_EXT = %w[.bmp .gif .jpg .jpeg .png .ico].freeze
|
23
28
|
|
24
29
|
# :startdoc:
|
25
30
|
|
26
31
|
# Returns whether or not the file is an image. Only JPEG, PNG, BMP,
|
27
32
|
# GIF, and ICO are checked against.
|
28
33
|
#
|
29
|
-
# This
|
34
|
+
# This reads and checks the first few bytes of the file. For a version
|
30
35
|
# that is more robust, but which depends on a 3rd party C library (and is
|
31
36
|
# difficult to build on MS Windows), see the 'filemagic' library.
|
32
37
|
#
|
38
|
+
# By default the filename extension is also checked. You can disable this
|
39
|
+
# by passing false as the second argument, in which case only the contents
|
40
|
+
# are checked.
|
41
|
+
#
|
33
42
|
# Examples:
|
34
43
|
#
|
35
44
|
# File.image?('somefile.jpg') # => true
|
@@ -38,9 +47,11 @@ class File
|
|
38
47
|
# The approach I used here is based on information found at
|
39
48
|
# http://en.wikipedia.org/wiki/Magic_number_(programming)
|
40
49
|
#
|
41
|
-
def self.image?(file)
|
42
|
-
bool = IMAGE_EXT.include?(File.extname(file).downcase)
|
50
|
+
def self.image?(file, check_file_extension: true)
|
43
51
|
bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) || tiff?(file) || ico?(file)
|
52
|
+
|
53
|
+
bool &&= IMAGE_EXT.include?(File.extname(file).downcase) if check_file_extension
|
54
|
+
|
44
55
|
bool
|
45
56
|
end
|
46
57
|
|
@@ -65,11 +76,12 @@ class File
|
|
65
76
|
return false if File.stat(file).zero?
|
66
77
|
return false if image?(file)
|
67
78
|
return false if check_bom?(file)
|
79
|
+
|
68
80
|
bytes = File.stat(file).blksize
|
69
81
|
bytes = 4096 if bytes > 4096
|
70
|
-
s = (File.read(file, bytes) ||
|
71
|
-
s = s.encode('US-ASCII', :undef => :replace).
|
72
|
-
((s.size - s.grep(
|
82
|
+
s = (File.read(file, bytes) || '')
|
83
|
+
s = s.encode('US-ASCII', :undef => :replace).chars
|
84
|
+
((s.size - s.grep(' '..'~').size) / s.size.to_f) > percentage
|
73
85
|
end
|
74
86
|
|
75
87
|
# Looks for the first occurrence of +program+ within +path+.
|
@@ -85,10 +97,8 @@ class File
|
|
85
97
|
# File.which('ruby') # => '/usr/local/bin/ruby'
|
86
98
|
# File.which('foo') # => nil
|
87
99
|
#
|
88
|
-
def self.which(program, path=ENV['PATH'])
|
89
|
-
if path.nil? || path.empty?
|
90
|
-
raise ArgumentError, "path cannot be empty"
|
91
|
-
end
|
100
|
+
def self.which(program, path = ENV['PATH'])
|
101
|
+
raise ArgumentError, 'path cannot be empty' if path.nil? || path.empty?
|
92
102
|
|
93
103
|
# Bail out early if an absolute path is provided.
|
94
104
|
if program =~ /^\/|^[a-z]:[\\\/]/i
|
@@ -102,16 +112,17 @@ class File
|
|
102
112
|
end
|
103
113
|
|
104
114
|
# Iterate over each path glob the dir + program.
|
105
|
-
path.split(File::PATH_SEPARATOR).each
|
115
|
+
path.split(File::PATH_SEPARATOR).each do |dir|
|
106
116
|
dir = File.expand_path(dir)
|
107
117
|
|
108
118
|
next unless File.exist?(dir) # In case of bogus second argument
|
119
|
+
|
109
120
|
file = File.join(dir, program)
|
110
121
|
|
111
122
|
# Dir[] doesn't handle backslashes properly, so convert them. Also, if
|
112
123
|
# the program name doesn't have an extension, try them all.
|
113
124
|
if MSWINDOWS
|
114
|
-
file = file.tr(
|
125
|
+
file = file.tr(File::ALT_SEPARATOR, File::SEPARATOR)
|
115
126
|
file += WIN32EXTS if File.extname(program).empty?
|
116
127
|
end
|
117
128
|
|
@@ -122,7 +133,7 @@ class File
|
|
122
133
|
found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
123
134
|
return found
|
124
135
|
end
|
125
|
-
|
136
|
+
end
|
126
137
|
|
127
138
|
nil
|
128
139
|
end
|
@@ -139,21 +150,19 @@ class File
|
|
139
150
|
# File.whereis('ruby') # => ['/usr/bin/ruby', '/usr/local/bin/ruby']
|
140
151
|
# File.whereis('foo') # => nil
|
141
152
|
#
|
142
|
-
def self.whereis(program, path=ENV['PATH'])
|
143
|
-
if path.nil? || path.empty?
|
144
|
-
raise ArgumentError, "path cannot be empty"
|
145
|
-
end
|
153
|
+
def self.whereis(program, path = ENV['PATH'])
|
154
|
+
raise ArgumentError, 'path cannot be empty' if path.nil? || path.empty?
|
146
155
|
|
147
156
|
paths = []
|
148
157
|
|
149
158
|
# Bail out early if an absolute path is provided.
|
150
159
|
if program =~ /^\/|^[a-z]:[\\\/]/i
|
151
160
|
program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
|
152
|
-
program = program.tr(
|
161
|
+
program = program.tr(File::ALT_SEPARATOR, File::SEPARATOR) if MSWINDOWS
|
153
162
|
found = Dir[program]
|
154
163
|
if found[0] && File.executable?(found[0]) && !File.directory?(found[0])
|
155
164
|
if File::ALT_SEPARATOR
|
156
|
-
return found.map{ |f| f.tr(
|
165
|
+
return found.map{ |f| f.tr(File::SEPARATOR, File::ALT_SEPARATOR) }
|
157
166
|
else
|
158
167
|
return found
|
159
168
|
end
|
@@ -163,14 +172,15 @@ class File
|
|
163
172
|
end
|
164
173
|
|
165
174
|
# Iterate over each path glob the dir + program.
|
166
|
-
path.split(File::PATH_SEPARATOR).each
|
175
|
+
path.split(File::PATH_SEPARATOR).each do |dir|
|
167
176
|
next unless File.exist?(dir) # In case of bogus second argument
|
177
|
+
|
168
178
|
file = File.join(dir, program)
|
169
179
|
|
170
180
|
# Dir[] doesn't handle backslashes properly, so convert them. Also, if
|
171
181
|
# the program name doesn't have an extension, try them all.
|
172
182
|
if MSWINDOWS
|
173
|
-
file = file.tr(
|
183
|
+
file = file.tr(File::ALT_SEPARATOR, File::SEPARATOR)
|
174
184
|
file += WIN32EXTS if File.extname(program).empty?
|
175
185
|
end
|
176
186
|
|
@@ -181,7 +191,7 @@ class File
|
|
181
191
|
found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
182
192
|
paths << found
|
183
193
|
end
|
184
|
-
|
194
|
+
end
|
185
195
|
|
186
196
|
paths.empty? ? nil : paths.uniq
|
187
197
|
end
|
@@ -197,20 +207,21 @@ class File
|
|
197
207
|
# # Use a block
|
198
208
|
# File.head('somefile.txt'){ |line| puts line }
|
199
209
|
#
|
200
|
-
def self.head(filename, num_lines=10)
|
210
|
+
def self.head(filename, num_lines = 10)
|
201
211
|
a = []
|
202
212
|
|
203
|
-
|
213
|
+
File.foreach(filename) do |line|
|
204
214
|
break if num_lines <= 0
|
215
|
+
|
205
216
|
num_lines -= 1
|
206
217
|
if block_given?
|
207
218
|
yield line
|
208
219
|
else
|
209
220
|
a << line
|
210
221
|
end
|
211
|
-
|
222
|
+
end
|
212
223
|
|
213
|
-
|
224
|
+
a.empty? ? nil : a # Return nil in block form
|
214
225
|
end
|
215
226
|
|
216
227
|
# In block form, yields the last +num_lines+ of file +filename+.
|
@@ -227,7 +238,7 @@ class File
|
|
227
238
|
# Internally I'm using a 64 chunk of memory at a time. I may allow the size
|
228
239
|
# to be configured in the future as an optional 3rd argument.
|
229
240
|
#
|
230
|
-
def self.tail(filename, num_lines=10)
|
241
|
+
def self.tail(filename, num_lines = 10, &block)
|
231
242
|
tail_size = 2**16 # 64k chunks
|
232
243
|
|
233
244
|
# MS Windows gets unhappy if you try to seek backwards past the
|
@@ -241,7 +252,7 @@ class File
|
|
241
252
|
buf = ''
|
242
253
|
|
243
254
|
# Open in binary mode to ensure line endings aren't converted.
|
244
|
-
File.open(filename, 'rb')
|
255
|
+
File.open(filename, 'rb') do |fh|
|
245
256
|
position = file_size - read_bytes # Set the starting read position
|
246
257
|
|
247
258
|
# Loop until we have the lines or run out of file
|
@@ -251,12 +262,12 @@ class File
|
|
251
262
|
read_bytes = tail_size
|
252
263
|
position -= read_bytes
|
253
264
|
end
|
254
|
-
|
265
|
+
end
|
255
266
|
|
256
267
|
lines = buf.split(line_sep).pop(num_lines)
|
257
268
|
|
258
269
|
if block_given?
|
259
|
-
lines.each
|
270
|
+
lines.each(&block)
|
260
271
|
else
|
261
272
|
lines
|
262
273
|
end
|
@@ -277,47 +288,33 @@ class File
|
|
277
288
|
# ArgumentError is raised.
|
278
289
|
#
|
279
290
|
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
|
291
|
+
raise ArgumentError, 'Only valid for plain text files' unless File::Stat.new(old_file).file?
|
283
292
|
|
284
293
|
format = nl_for_platform(platform)
|
285
294
|
|
286
|
-
orig = $\ # $OUTPUT_RECORD_SEPARATOR
|
287
|
-
$\ = format
|
288
|
-
|
289
295
|
if old_file == new_file
|
290
|
-
require 'fileutils'
|
291
296
|
require 'tempfile'
|
297
|
+
temp_name = Time.new.strftime('%Y%m%d%H%M%S')
|
298
|
+
nf = Tempfile.new("ruby_temp_#{temp_name}")
|
299
|
+
else
|
300
|
+
nf = File.new(new_file, 'w')
|
301
|
+
end
|
292
302
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
IO.foreach(old_file){ |line|
|
299
|
-
line.chomp!
|
300
|
-
tf.print line
|
301
|
-
}
|
302
|
-
ensure
|
303
|
-
tf.close if tf && !tf.closed?
|
303
|
+
begin
|
304
|
+
nf.open if old_file == new_file
|
305
|
+
File.foreach(old_file) do |line|
|
306
|
+
line.chomp!
|
307
|
+
nf.print("#{line}#{format}")
|
304
308
|
end
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
nf
|
311
|
-
IO.foreach(old_file){ |line|
|
312
|
-
line.chomp!
|
313
|
-
nf.print line
|
314
|
-
}
|
315
|
-
ensure
|
316
|
-
nf.close if nf && !nf.closed?
|
309
|
+
ensure
|
310
|
+
nf.close if nf && !nf.closed?
|
311
|
+
if old_file == new_file
|
312
|
+
require 'fileutils'
|
313
|
+
File.delete(old_file)
|
314
|
+
FileUtils.mv(nf.path, old_file)
|
317
315
|
end
|
318
316
|
end
|
319
317
|
|
320
|
-
$\ = orig
|
321
318
|
self
|
322
319
|
end
|
323
320
|
|
@@ -340,49 +337,43 @@ class File
|
|
340
337
|
# Valid options are 'bytes', 'characters' (or just 'chars'), 'words' and
|
341
338
|
# 'lines'.
|
342
339
|
#
|
343
|
-
def self.wc(filename, option='all')
|
340
|
+
def self.wc(filename, option = 'all')
|
344
341
|
option.downcase!
|
345
|
-
valid = %w
|
342
|
+
valid = %w[all bytes characters chars lines words]
|
346
343
|
|
347
|
-
unless valid.include?(option)
|
348
|
-
raise ArgumentError, "Invalid option: '#{option}'"
|
349
|
-
end
|
344
|
+
raise ArgumentError, "Invalid option: '#{option}'" unless valid.include?(option)
|
350
345
|
|
351
346
|
n = 0
|
352
347
|
|
353
348
|
if option == 'lines'
|
354
|
-
|
355
|
-
|
349
|
+
File.foreach(filename){ n += 1 }
|
350
|
+
n
|
356
351
|
elsif option == 'bytes'
|
357
|
-
File.open(filename)
|
352
|
+
File.open(filename) do |f|
|
358
353
|
f.each_byte{ n += 1 }
|
359
|
-
|
360
|
-
|
361
|
-
elsif
|
362
|
-
File.open(filename)
|
363
|
-
while f.getc
|
364
|
-
|
365
|
-
|
366
|
-
}
|
367
|
-
return n
|
354
|
+
end
|
355
|
+
n
|
356
|
+
elsif %w[characters chars].include?(option)
|
357
|
+
File.open(filename) do |f|
|
358
|
+
n += 1 while f.getc
|
359
|
+
end
|
360
|
+
n
|
368
361
|
elsif option == 'words'
|
369
|
-
|
362
|
+
File.foreach(filename) do |line|
|
370
363
|
n += line.split.length
|
371
|
-
|
372
|
-
|
364
|
+
end
|
365
|
+
n
|
373
366
|
else
|
374
|
-
bytes,chars,lines,words = 0,0,0,0
|
375
|
-
|
367
|
+
bytes, chars, lines, words = 0, 0, 0, 0
|
368
|
+
File.foreach(filename) do |line|
|
376
369
|
lines += 1
|
377
370
|
words += line.split.length
|
378
|
-
chars += line.
|
379
|
-
|
380
|
-
File.open(filename)
|
381
|
-
while f.getc
|
382
|
-
|
383
|
-
|
384
|
-
}
|
385
|
-
return [bytes,chars,words,lines]
|
371
|
+
chars += line.chars.length
|
372
|
+
end
|
373
|
+
File.open(filename) do |f|
|
374
|
+
bytes += 1 while f.getc
|
375
|
+
end
|
376
|
+
[bytes, chars, words, lines]
|
386
377
|
end
|
387
378
|
end
|
388
379
|
|
@@ -402,8 +393,6 @@ class File
|
|
402
393
|
end
|
403
394
|
end
|
404
395
|
|
405
|
-
private
|
406
|
-
|
407
396
|
# Returns whether or not the given +text+ contains a BOM marker.
|
408
397
|
# If present, we can generally assume it's a text file.
|
409
398
|
#
|
@@ -411,66 +400,77 @@ class File
|
|
411
400
|
text = File.read(file, 4).force_encoding('utf-8')
|
412
401
|
|
413
402
|
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"
|
403
|
+
bool = true if text[0, 3] == "\xEF\xBB\xBF"
|
404
|
+
bool = true if text[0, 4] == "\x00\x00\xFE\xFF" || text[0, 4] == "\xFF\xFE\x00\x00"
|
405
|
+
bool = true if text[0, 2] == "\xFF\xFE" || text[0, 2] == "\xFE\xFF"
|
417
406
|
|
418
407
|
bool
|
419
408
|
end
|
420
409
|
|
410
|
+
private_class_method :check_bom?
|
411
|
+
|
412
|
+
# Returns the newline characters for the given platform.
|
413
|
+
#
|
421
414
|
def self.nl_for_platform(platform)
|
422
|
-
platform = RbConfig::CONFIG[
|
415
|
+
platform = RbConfig::CONFIG['host_os'] if platform == 'local'
|
423
416
|
|
424
417
|
case platform
|
425
418
|
when /dos|windows|win32|mswin|mingw/i
|
426
|
-
|
419
|
+
"\cM\cJ"
|
427
420
|
when /unix|linux|bsd|cygwin|osx|darwin|solaris|sunos/i
|
428
|
-
|
421
|
+
"\cJ"
|
429
422
|
when /mac|apple|macintosh/i
|
430
|
-
|
423
|
+
"\cM"
|
431
424
|
else
|
432
|
-
raise ArgumentError,
|
425
|
+
raise ArgumentError, 'Invalid platform string'
|
433
426
|
end
|
434
427
|
end
|
435
428
|
|
429
|
+
# Is the file a bitmap file?
|
430
|
+
#
|
436
431
|
def self.bmp?(file)
|
437
|
-
|
432
|
+
data = File.read(file, 6, nil, :encoding => 'binary')
|
433
|
+
data[0,2] == 'BM' && File.size(file) == data[2,4].unpack('i').first
|
438
434
|
end
|
439
435
|
|
436
|
+
# Is the file a jpeg file?
|
437
|
+
#
|
440
438
|
def self.jpg?(file)
|
441
|
-
|
439
|
+
File.read(file, 10, nil, :encoding => 'binary') == "\377\330\377\340\000\020JFIF".force_encoding(Encoding::BINARY)
|
442
440
|
end
|
443
441
|
|
442
|
+
# Is the file a png file?
|
443
|
+
#
|
444
444
|
def self.png?(file)
|
445
|
-
|
445
|
+
File.read(file, 4, nil, :encoding => 'binary') == "\211PNG".force_encoding(Encoding::BINARY)
|
446
446
|
end
|
447
447
|
|
448
|
+
# Is the file a gif?
|
449
|
+
#
|
448
450
|
def self.gif?(file)
|
449
|
-
[
|
451
|
+
%w[GIF89a GIF97a].include?(File.read(file, 6))
|
450
452
|
end
|
451
453
|
|
454
|
+
# Is the file a tiff?
|
455
|
+
#
|
452
456
|
def self.tiff?(file)
|
453
457
|
return false if File.size(file) < 12
|
454
458
|
|
455
|
-
bytes =
|
459
|
+
bytes = File.read(file, 4)
|
456
460
|
|
457
461
|
# II is Intel, MM is Motorola
|
458
|
-
if bytes[0..1] != 'II' && bytes[0..1] != 'MM'
|
459
|
-
return false
|
460
|
-
end
|
462
|
+
return false if bytes[0..1] != 'II' && bytes[0..1] != 'MM'
|
461
463
|
|
462
|
-
if bytes[0..1] == 'II' && bytes[2..3].ord != 42
|
463
|
-
return false
|
464
|
-
end
|
464
|
+
return false if bytes[0..1] == 'II' && bytes[2..3].ord != 42
|
465
465
|
|
466
|
-
if bytes[0..1] == 'MM' && bytes[2..3].reverse.ord != 42
|
467
|
-
return false
|
468
|
-
end
|
466
|
+
return false if bytes[0..1] == 'MM' && bytes[2..3].reverse.ord != 42
|
469
467
|
|
470
468
|
true
|
471
469
|
end
|
472
470
|
|
471
|
+
# Is the file an ico file?
|
472
|
+
#
|
473
473
|
def self.ico?(file)
|
474
|
-
["\000\000\001\000", "\000\000\002\000"].include?(
|
474
|
+
["\000\000\001\000", "\000\000\002\000"].include?(File.read(file, 4, nil, :encoding => 'binary'))
|
475
475
|
end
|
476
476
|
end
|
data/ptools.gemspec
CHANGED
@@ -2,7 +2,7 @@ require 'rbconfig'
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'ptools'
|
5
|
-
spec.version = '1.4.
|
5
|
+
spec.version = '1.4.3'
|
6
6
|
spec.license = 'Artistic-2.0'
|
7
7
|
spec.author = 'Daniel J. Berger'
|
8
8
|
spec.email = 'djberg96@gmail.com'
|
@@ -19,16 +19,19 @@ Gem::Specification.new do |spec|
|
|
19
19
|
EOF
|
20
20
|
|
21
21
|
spec.metadata = {
|
22
|
-
'homepage_uri'
|
23
|
-
'bug_tracker_uri'
|
24
|
-
'changelog_uri'
|
25
|
-
'documentation_uri'
|
26
|
-
'source_code_uri'
|
27
|
-
'wiki_uri'
|
22
|
+
'homepage_uri' => 'https://github.com/djberg96/ptools',
|
23
|
+
'bug_tracker_uri' => 'https://github.com/djberg96/ptools/issues',
|
24
|
+
'changelog_uri' => 'https://github.com/djberg96/ptools/blob/main/CHANGES.md',
|
25
|
+
'documentation_uri' => 'https://github.com/djberg96/ptools/wiki',
|
26
|
+
'source_code_uri' => 'https://github.com/djberg96/ptools',
|
27
|
+
'wiki_uri' => 'https://github.com/djberg96/ptools/wiki',
|
28
|
+
'rubygems_mfa_required' => 'true'
|
28
29
|
}
|
29
30
|
|
30
31
|
spec.add_development_dependency('rake')
|
31
32
|
spec.add_development_dependency('rspec', '~> 3.9')
|
33
|
+
spec.add_development_dependency('rubocop')
|
34
|
+
spec.add_development_dependency('rubocop-rspec')
|
32
35
|
|
33
36
|
if File::ALT_SEPARATOR
|
34
37
|
spec.platform = Gem::Platform.new(['universal', 'mingw32'])
|