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