recls-ruby 2.11.0.3 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +24 -27
- data/README.md +1 -242
- data/examples/find_files_and_directories.md +30 -33
- data/examples/find_files_and_directories.recursive.md +254 -255
- data/examples/show_hidden_files.md +1 -4
- data/examples/show_hidden_files.rb +1 -1
- data/examples/show_readonly_files.md +1 -4
- data/examples/show_readonly_files.rb +1 -1
- data/lib/recls/api.rb +73 -76
- data/lib/recls/combine_paths_1.rb +23 -26
- data/lib/recls/combine_paths_2plus.rb +29 -32
- data/lib/recls/entry.rb +273 -277
- data/lib/recls/file_search.rb +193 -194
- data/lib/recls/flags.rb +45 -48
- data/lib/recls/foreach.rb +98 -105
- data/lib/recls/obsolete.rb +79 -80
- data/lib/recls/recls.rb +22 -16
- data/lib/recls/stat.rb +134 -137
- data/lib/recls/util.rb +92 -95
- data/lib/recls/version.rb +17 -22
- data/lib/recls/ximpl/os.rb +44 -46
- data/lib/recls/ximpl/unix.rb +35 -39
- data/lib/recls/ximpl/util.rb +596 -598
- data/lib/recls/ximpl/windows.rb +137 -141
- data/lib/recls.rb +9 -10
- data/test/scratch/test_display_parts.rb +33 -33
- data/test/scratch/test_entry.rb +6 -6
- data/test/scratch/test_files_and_directories.rb +8 -8
- data/test/scratch/test_foreach.rb +10 -10
- data/test/scratch/test_module_function.rb +33 -33
- data/test/scratch/test_pattern_arrays.rb +5 -5
- data/test/scratch/test_show_dev_and_ino.rb +1 -1
- data/test/scratch/test_show_hidden.rb +3 -3
- data/test/unit/tc_recls_entries.rb +31 -31
- data/test/unit/tc_recls_entry.rb +19 -19
- data/test/unit/tc_recls_file_search.rb +32 -32
- data/test/unit/tc_recls_module.rb +25 -25
- data/test/unit/tc_recls_util.rb +161 -161
- data/test/unit/tc_recls_ximpl_util.rb +676 -676
- data/test/unit/test_all_separately.sh +1 -1
- data/test/unit/ts_all.rb +4 -4
- metadata +7 -7
data/lib/recls/file_search.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
#
|
2
|
-
# File:
|
1
|
+
# ######################################################################### #
|
2
|
+
# File: recls/file_search.rb
|
3
3
|
#
|
4
|
-
# Purpose:
|
4
|
+
# Purpose: Defines the Recls::FileSearch class for the recls.Ruby library.
|
5
5
|
#
|
6
|
-
# Created:
|
7
|
-
# Updated:
|
6
|
+
# Created: 24th July 2012
|
7
|
+
# Updated: 14th April 2019
|
8
8
|
#
|
9
|
-
# Author:
|
9
|
+
# Author: Matthew Wilson
|
10
10
|
#
|
11
|
-
# Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
|
12
11
|
# Copyright (c) 2012-2019, Matthew Wilson and Synesis Software
|
13
12
|
# All rights reserved.
|
14
13
|
#
|
@@ -34,295 +33,295 @@
|
|
34
33
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
35
34
|
# POSSIBILITY OF SUCH DAMAGE.
|
36
35
|
#
|
37
|
-
#
|
36
|
+
# ######################################################################### #
|
38
37
|
|
39
38
|
|
40
39
|
require 'recls/entry'
|
41
40
|
require 'recls/flags'
|
42
41
|
require 'recls/ximpl/os'
|
43
42
|
|
44
|
-
|
45
43
|
=begin
|
46
44
|
=end
|
47
45
|
|
48
|
-
# @!visibility private
|
49
46
|
class Object; end # :nodoc:
|
50
47
|
|
51
48
|
module Recls
|
52
49
|
|
53
|
-
|
50
|
+
class FileSearch # :nodoc: all
|
54
51
|
|
55
|
-
|
52
|
+
include Enumerable
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
54
|
+
# Initialises a +FileSearch+ instance, which acts as an +Enumerable+
|
55
|
+
# of Recls::Entry
|
56
|
+
#
|
57
|
+
# === Signature
|
58
|
+
#
|
59
|
+
# * *Parameters:*
|
60
|
+
# - +search_root+ (String, Recls::Entry) The root directory of the search. May be +nil+, in which case the current directory is assumed
|
61
|
+
# - +patterns+ (String, Array) The pattern(s) for which to search. May be +nil+, in which case +Recls::WILDCARDS_ALL+ is assumed
|
62
|
+
# - +options+ (Hash, Integer) Combination of flags (with behaviour as described below for the +flags+ option), or an options hash
|
63
|
+
#
|
64
|
+
# * *Options:*
|
65
|
+
# - +flags+ (Integer) Combination of flags - FILES, DIRECTORIES, RECURSIVE, etc. If the value modulo TYPEMASK is 0, then FILES is assumed
|
66
|
+
#
|
67
|
+
# === Return
|
68
|
+
# An instance of the class
|
69
|
+
#
|
70
|
+
def initialize(search_root, patterns, options={})
|
73
71
|
|
74
|
-
|
75
|
-
|
72
|
+
# for backwards compatibility, we allow for options to
|
73
|
+
# be a number
|
76
74
|
|
77
|
-
|
75
|
+
flags = 0
|
78
76
|
|
79
|
-
|
80
|
-
|
77
|
+
case options
|
78
|
+
when ::NilClass
|
81
79
|
|
82
|
-
|
83
|
-
|
80
|
+
options = { flags: 0 }
|
81
|
+
when ::Integer
|
84
82
|
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
flags = options
|
84
|
+
options = { flags: flags }
|
85
|
+
when ::Hash
|
88
86
|
|
89
|
-
|
90
|
-
|
87
|
+
flags = options[:flags] || 0
|
88
|
+
else
|
91
89
|
|
92
|
-
|
93
|
-
|
90
|
+
raise ArgumentError, "options parameter must a #{::Hash}, nil, or an integer specifying flags - an instance of #{options.class} given"
|
91
|
+
end
|
94
92
|
|
95
93
|
|
96
|
-
|
94
|
+
if not search_root
|
97
95
|
|
98
|
-
|
99
|
-
|
96
|
+
search_root = '.'
|
97
|
+
else
|
100
98
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
99
|
+
search_root = search_root.to_s
|
100
|
+
end
|
101
|
+
search_root = '.' if search_root.empty?
|
102
|
+
search_root = File.expand_path(search_root) if '~' == search_root[0]
|
105
103
|
|
106
|
-
|
107
|
-
|
104
|
+
case patterns
|
105
|
+
when NilClass
|
108
106
|
|
109
|
-
|
110
|
-
|
107
|
+
patterns = []
|
108
|
+
when String
|
111
109
|
|
112
|
-
|
113
|
-
|
114
|
-
|
110
|
+
patterns = patterns.split(/[|#{Recls::Ximpl::OS::PATH_SEPARATOR}]/)
|
111
|
+
when Array
|
112
|
+
else
|
115
113
|
|
116
|
-
|
117
|
-
|
114
|
+
patterns = patterns.to_a
|
115
|
+
end
|
118
116
|
|
119
|
-
|
117
|
+
patterns = [ Recls::WILDCARDS_ALL ] if patterns.empty?
|
120
118
|
|
121
|
-
|
119
|
+
if(0 == (Recls::TYPEMASK & flags))
|
122
120
|
|
123
|
-
|
124
|
-
|
121
|
+
flags |= Recls::FILES
|
122
|
+
end
|
125
123
|
|
126
|
-
|
127
|
-
|
128
|
-
|
124
|
+
# now de-dup the patterns, to avoid duplicates in search
|
125
|
+
patterns = patterns.flatten
|
126
|
+
patterns = patterns.uniq
|
129
127
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
128
|
+
@search_root = search_root
|
129
|
+
@patterns = patterns
|
130
|
+
@flags = flags
|
131
|
+
end
|
134
132
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
133
|
+
# (String) The search root
|
134
|
+
attr_reader :search_root
|
135
|
+
# (String) The search patterns
|
136
|
+
attr_reader :patterns
|
137
|
+
# (Integer) The search flags
|
138
|
+
attr_reader :flags
|
141
139
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
140
|
+
# Calls the block once for each found file, passing a single
|
141
|
+
# parameter that is a Recls::Entry instance
|
142
|
+
#
|
143
|
+
# @!visibility private
|
144
|
+
def each(&blk) # :nodoc:
|
147
145
|
|
148
|
-
|
149
|
-
|
146
|
+
search_root = @search_root
|
147
|
+
search_root = Recls::Ximpl::absolute_path search_root
|
150
148
|
|
151
|
-
|
149
|
+
search_root = search_root.gsub(/\\/, '/') if Recls::Ximpl::OS::OS_IS_WINDOWS
|
152
150
|
|
153
|
-
|
154
|
-
|
151
|
+
# set the (type part of the) flags to zero if we want
|
152
|
+
# everything, to facilitate later optimisation
|
155
153
|
|
156
|
-
|
154
|
+
flags = @flags
|
157
155
|
|
158
|
-
|
156
|
+
if(Recls::Ximpl::OS::OS_IS_WINDOWS)
|
159
157
|
|
160
|
-
|
161
|
-
|
158
|
+
mask = (Recls::FILES | Recls::DIRECTORIES)
|
159
|
+
else
|
162
160
|
|
163
|
-
|
164
|
-
|
161
|
+
mask = (Recls::FILES | Recls::DIRECTORIES | Recls::LINKS | Recls::DEVICES)
|
162
|
+
end
|
165
163
|
|
166
|
-
|
164
|
+
if(mask == (mask & flags))
|
167
165
|
|
168
|
-
|
169
|
-
|
166
|
+
flags = flags & ~Recls::TYPEMASK
|
167
|
+
end
|
170
168
|
|
171
|
-
|
169
|
+
patterns = @patterns
|
172
170
|
|
173
|
-
|
171
|
+
patterns = patterns.map do |pattern|
|
174
172
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
173
|
+
pattern = pattern.gsub(/\./, '\\.')
|
174
|
+
pattern = pattern.gsub(/\?/, '.')
|
175
|
+
pattern = pattern.gsub(/\*/, '.*')
|
176
|
+
pattern
|
177
|
+
end
|
180
178
|
|
181
|
-
|
182
|
-
|
179
|
+
search_dir = search_root
|
180
|
+
search_root = Recls::Ximpl::Util.append_trailing_slash search_root
|
183
181
|
|
184
|
-
|
185
|
-
|
182
|
+
FileSearch::search_directory_(search_root, search_dir, patterns, flags, &blk)
|
183
|
+
end
|
186
184
|
|
187
|
-
|
188
|
-
|
189
|
-
|
185
|
+
private
|
186
|
+
# @!visibility private
|
187
|
+
def FileSearch.is_dots(name) # :nodoc:
|
190
188
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
189
|
+
case name
|
190
|
+
when '.', '..'
|
191
|
+
true
|
192
|
+
else
|
193
|
+
false
|
194
|
+
end
|
195
|
+
end
|
198
196
|
|
199
|
-
|
200
|
-
|
197
|
+
# @!visibility private
|
198
|
+
def FileSearch.stat_or_nil_(path, flags) # :nodoc:
|
201
199
|
|
202
|
-
|
200
|
+
begin
|
203
201
|
|
204
|
-
|
205
|
-
|
202
|
+
Recls::Ximpl::FileStat.stat path
|
203
|
+
rescue Errno::ENOENT, Errno::ENXIO
|
206
204
|
|
207
|
-
|
208
|
-
|
205
|
+
nil
|
206
|
+
rescue SystemCallError => x
|
209
207
|
|
210
|
-
|
208
|
+
# TODO this should be filtered up and/or logged
|
211
209
|
|
212
|
-
|
210
|
+
if(0 != (STOP_ON_ACCESS_FAILURE & flags))
|
213
211
|
|
214
|
-
|
215
|
-
|
212
|
+
raise
|
213
|
+
end
|
216
214
|
|
217
|
-
|
218
|
-
|
219
|
-
|
215
|
+
nil
|
216
|
+
end
|
217
|
+
end
|
220
218
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
219
|
+
# searches all entries - files, directories, links, devices
|
220
|
+
# - that match the given (patterns) in the given directory
|
221
|
+
# (dir) according to the given (flags), invoking the given
|
222
|
+
# block (blk). The search directory (search_root) is passed in
|
223
|
+
# order to allow calculation of search_relative_path in the
|
224
|
+
# entry.
|
226
225
|
|
227
|
-
|
228
|
-
|
226
|
+
# @!visibility private
|
227
|
+
def FileSearch.search_directory_(search_root, dir, patterns, flags, &blk) # :nodoc:
|
229
228
|
|
230
|
-
|
231
|
-
|
229
|
+
# array of FileStat instances
|
230
|
+
entries = []
|
232
231
|
|
233
|
-
|
232
|
+
patterns.each do |pattern|
|
234
233
|
|
235
|
-
|
234
|
+
Recls::Ximpl::dir_entries_maybe(dir, flags).each do |name|
|
236
235
|
|
237
|
-
|
236
|
+
next if is_dots(name)
|
238
237
|
|
239
|
-
|
238
|
+
if not name =~ /^#{pattern}$/
|
240
239
|
|
241
|
-
|
242
|
-
|
240
|
+
next
|
241
|
+
end
|
243
242
|
|
244
|
-
|
243
|
+
entry_path = File::join(dir, name)
|
245
244
|
|
246
|
-
|
245
|
+
fs = stat_or_nil_(entry_path, flags)
|
247
246
|
|
248
|
-
|
249
|
-
|
250
|
-
|
247
|
+
entries << fs
|
248
|
+
end
|
249
|
+
end
|
251
250
|
|
252
|
-
|
253
|
-
|
251
|
+
# array of FileStat instances
|
252
|
+
subdirectories = []
|
254
253
|
|
255
|
-
|
254
|
+
Recls::Ximpl::dir_entries_maybe(dir, flags).each do |subdir|
|
256
255
|
|
257
|
-
|
256
|
+
next if is_dots(subdir)
|
258
257
|
|
259
|
-
|
258
|
+
subdir_path = File::join(dir, subdir)
|
260
259
|
|
261
|
-
|
260
|
+
fs = stat_or_nil_(subdir_path, flags)
|
262
261
|
|
263
|
-
|
262
|
+
next if not fs
|
264
263
|
|
265
|
-
|
264
|
+
next unless fs.directory?
|
266
265
|
|
267
|
-
|
268
|
-
|
266
|
+
subdirectories << fs
|
267
|
+
end
|
269
268
|
|
270
269
|
|
271
|
-
|
272
|
-
|
273
|
-
|
270
|
+
# now filter the file-stat instances and send each
|
271
|
+
# remaining to the block in Entry instance
|
272
|
+
entries.each do |fs|
|
274
273
|
|
275
|
-
|
274
|
+
next if not fs
|
276
275
|
|
277
|
-
|
276
|
+
if(0 == (Recls::SHOW_HIDDEN & flags))
|
278
277
|
|
279
|
-
|
278
|
+
if fs.hidden?
|
280
279
|
|
281
|
-
|
282
|
-
|
283
|
-
|
280
|
+
next
|
281
|
+
end
|
282
|
+
end
|
284
283
|
|
285
|
-
|
284
|
+
match = false
|
286
285
|
|
287
|
-
|
286
|
+
match ||= (0 == (Recls::TYPEMASK & flags))
|
288
287
|
|
289
|
-
|
290
|
-
|
291
|
-
|
288
|
+
match ||= (0 != (Recls::FILES & flags) && fs.file?)
|
289
|
+
match ||= (0 != (Recls::DIRECTORIES & flags) && fs.directory?)
|
290
|
+
match ||= (0 != (Recls::DEVICES & flags) && fs.blockdev?)
|
292
291
|
|
293
|
-
|
292
|
+
next unless match
|
294
293
|
|
295
|
-
|
296
|
-
|
294
|
+
blk.call Recls::Entry.new(fs.path, fs, search_root, flags)
|
295
|
+
end
|
297
296
|
|
298
|
-
|
297
|
+
# sub-directories
|
299
298
|
|
300
|
-
|
299
|
+
return unless (0 != (Recls::RECURSIVE & flags))
|
301
300
|
|
302
|
-
|
301
|
+
subdirectories.each do |fs|
|
303
302
|
|
304
|
-
|
303
|
+
if(0 == (Recls::SHOW_HIDDEN & flags))
|
305
304
|
|
306
|
-
|
305
|
+
if fs.hidden?
|
307
306
|
|
308
|
-
|
309
|
-
|
310
|
-
|
307
|
+
next
|
308
|
+
end
|
309
|
+
end
|
311
310
|
|
312
|
-
|
311
|
+
if(0 == (Recls::SEARCH_THROUGH_LINKS & flags))
|
313
312
|
|
314
|
-
|
313
|
+
if File.symlink? fs.path
|
315
314
|
|
316
|
-
|
317
|
-
|
318
|
-
|
315
|
+
next
|
316
|
+
end
|
317
|
+
end
|
319
318
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
319
|
+
FileSearch::search_directory_(search_root, fs.path, patterns, flags, &blk)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end # class FileSearch
|
324
323
|
end # module Recls
|
325
324
|
|
326
|
-
|
327
325
|
# ############################## end of file ############################# #
|
328
326
|
|
327
|
+
|
data/lib/recls/flags.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
#
|
2
|
-
# File:
|
1
|
+
# ######################################################################### #
|
2
|
+
# File: recls/flags.rb
|
3
3
|
#
|
4
|
-
# Purpose:
|
4
|
+
# Purpose: Defines the Recls::Flags module for the recls.Ruby library.
|
5
5
|
#
|
6
|
-
# Created:
|
7
|
-
# Updated:
|
6
|
+
# Created: 24th July 2012
|
7
|
+
# Updated: 14th April 2019
|
8
8
|
#
|
9
|
-
# Author:
|
9
|
+
# Author: Matthew Wilson
|
10
10
|
#
|
11
|
-
# Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
|
12
11
|
# Copyright (c) 2012-2019, Matthew Wilson and Synesis Software
|
13
12
|
# All rights reserved.
|
14
13
|
#
|
@@ -34,64 +33,62 @@
|
|
34
33
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
35
34
|
# POSSIBILITY OF SUCH DAMAGE.
|
36
35
|
#
|
37
|
-
#
|
36
|
+
# ######################################################################### #
|
38
37
|
|
39
38
|
|
40
39
|
=begin
|
41
40
|
=end
|
42
41
|
|
43
|
-
# @!visibility private
|
44
42
|
class Object; end # :nodoc:
|
45
43
|
|
46
44
|
module Recls
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
46
|
+
# Specifies that files are to be listed
|
47
|
+
FILES = 0x00000001
|
48
|
+
# Specifies that directories are to be listed
|
49
|
+
DIRECTORIES = 0x00000002
|
50
|
+
# Specifies that links are to be listed (and not followed)
|
51
|
+
LINKS = 0x00000004
|
52
|
+
# Specifies that devices are to be listed
|
53
|
+
DEVICES = 0x00000008
|
54
|
+
# Type mask (combination of Recls::FILES, Recls::DIRECTORIES, Recls::LINKS, Recls::DEVICES)
|
55
|
+
TYPEMASK = 0x000000ff
|
58
56
|
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
# Specifies that hidden items are to be shown and hidden directories are
|
58
|
+
# to be searched
|
59
|
+
SHOW_HIDDEN = 0x00000100
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
61
|
+
# [IGNORED] This for compatibility with *recls* libraries written in other languages
|
62
|
+
DIR_PROGRESS = 0x00001000
|
63
|
+
# Causes search to terminate if a directory cannot be entered or an
|
64
|
+
# entry's information cannot be stat()'d
|
65
|
+
STOP_ON_ACCESS_FAILURE = 0x00002000
|
66
|
+
# [IGNORED] This for compatibility with *recls* libraries written in other languages
|
67
|
+
LINK_COUNT = 0000004000
|
68
|
+
# [IGNORED] This for compatibility with *recls* libraries written in other languages
|
69
|
+
NODE_INDEX = 0x00008000
|
72
70
|
|
73
|
-
|
74
|
-
|
71
|
+
# Causes search to operate recursively
|
72
|
+
RECURSIVE = 0x00010000
|
75
73
|
private
|
76
|
-
|
77
|
-
NO_SEARCH_LINKS = 0x00020000 # :nodoc:
|
74
|
+
NO_SEARCH_LINKS = 0x00020000 # :nodoc:
|
78
75
|
public
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
76
|
+
# [IGNORED] In previous versions the Recls::Entry#directory_parts property was not obtained (for performance reasons) unless this flag was specified. In current version the parts are always obtained
|
77
|
+
DIRECTORY_PARTS = 0x00040000
|
78
|
+
# Causes operations (such as Recls::stat()) to obtain a result even when
|
79
|
+
# no corresponding file-system entity does not exist
|
80
|
+
DETAILS_LATER = 0x00080000
|
84
81
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
82
|
+
# Causes the Recls::Entry#path and Recls::Entry#search_relative_path
|
83
|
+
# attributes to contain a trailing path-name-separator for directory
|
84
|
+
# entries
|
85
|
+
MARK_DIRECTORIES = 0x00200000
|
89
86
|
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
# Causes sub-directories that are links to be searched; default is not
|
88
|
+
# to search through links
|
89
|
+
SEARCH_THROUGH_LINKS = 0x00100000
|
93
90
|
end # module Recls
|
94
91
|
|
95
|
-
|
96
92
|
# ############################## end of file ############################# #
|
97
93
|
|
94
|
+
|