recls-ruby 2.12.0 → 2.12.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 +4 -4
- 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 +76 -73
- data/lib/recls/combine_paths_1.rb +26 -23
- data/lib/recls/combine_paths_2plus.rb +32 -29
- data/lib/recls/entry.rb +277 -273
- data/lib/recls/file_search.rb +194 -193
- data/lib/recls/flags.rb +48 -45
- data/lib/recls/foreach.rb +105 -98
- data/lib/recls/obsolete.rb +85 -79
- data/lib/recls/recls.rb +19 -24
- data/lib/recls/stat.rb +137 -134
- data/lib/recls/util.rb +95 -92
- data/lib/recls/version.rb +22 -17
- data/lib/recls/ximpl/os.rb +49 -48
- data/lib/recls/ximpl/unix.rb +41 -38
- data/lib/recls/ximpl/util.rb +600 -599
- data/lib/recls/ximpl/windows.rb +142 -139
- 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 +7 -7
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,295 +34,295 @@
|
|
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
|
|
48
|
+
# @!visibility private
|
46
49
|
class Object; end # :nodoc:
|
47
50
|
|
48
51
|
module Recls
|
49
52
|
|
50
|
-
|
53
|
+
class FileSearch # :nodoc: all
|
51
54
|
|
52
|
-
|
55
|
+
include Enumerable
|
53
56
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
def initialize(search_root, patterns, options={})
|
57
|
+
# Initialises a +FileSearch+ instance, which acts as an +Enumerable+
|
58
|
+
# of +Recls::Entry+
|
59
|
+
#
|
60
|
+
# === Signature
|
61
|
+
#
|
62
|
+
# * *Parameters:*
|
63
|
+
# - +search_root+ (+String+, +Recls::Entry+) The root directory of the search. May be +nil+, in which case the current directory is assumed;
|
64
|
+
# - +patterns+ (+String+, +Array+) The pattern(s) for which to search. May be +nil+, in which case +Recls::WILDCARDS_ALL+ is assumed;
|
65
|
+
# - +options+ (+Hash+, +Integer+) Combination of flags (with behaviour as described below for the +flags+ option), or an options hash;
|
66
|
+
#
|
67
|
+
# * *Options:*
|
68
|
+
# - +flags+ (+Integer+) Combination of flags - +Recls::FILES+, +Recls::DIRECTORIES+, +Recls::RECURSIVE+, etc. If the value modulo +Recls::TYPEMASK+ is 0, then +Recls::FILES+ is assumed;
|
69
|
+
#
|
70
|
+
# === Return
|
71
|
+
# An instance of the class.
|
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
|
-
# entry.
|
221
|
+
# searches all entries - files, directories, links, devices - that match
|
222
|
+
# the given +patterns+ in the given directory (+dir+) according to the
|
223
|
+
# given +flags+, invoking the given block (+blk+). The search directory
|
224
|
+
# (+search_root+) is passed in order to allow calculation of
|
225
|
+
# +#search_relative_path+ in the entry.
|
225
226
|
|
226
|
-
|
227
|
-
|
227
|
+
# @!visibility private
|
228
|
+
def FileSearch.search_directory_(search_root, dir, patterns, flags, &blk) # :nodoc:
|
228
229
|
|
229
|
-
|
230
|
-
|
230
|
+
# array of FileStat instances
|
231
|
+
entries = []
|
231
232
|
|
232
|
-
|
233
|
+
patterns.each do |pattern|
|
233
234
|
|
234
|
-
|
235
|
+
Recls::Ximpl::dir_entries_maybe(dir, flags).each do |name|
|
235
236
|
|
236
|
-
|
237
|
+
next if is_dots(name)
|
237
238
|
|
238
|
-
|
239
|
+
if not name =~ /^#{pattern}$/
|
239
240
|
|
240
|
-
|
241
|
-
|
241
|
+
next
|
242
|
+
end
|
242
243
|
|
243
|
-
|
244
|
+
entry_path = File::join(dir, name)
|
244
245
|
|
245
|
-
|
246
|
+
fs = stat_or_nil_(entry_path, flags)
|
246
247
|
|
247
|
-
|
248
|
-
|
249
|
-
|
248
|
+
entries << fs
|
249
|
+
end
|
250
|
+
end
|
250
251
|
|
251
|
-
|
252
|
-
|
252
|
+
# array of FileStat instances
|
253
|
+
subdirectories = []
|
253
254
|
|
254
|
-
|
255
|
+
Recls::Ximpl::dir_entries_maybe(dir, flags).each do |subdir|
|
255
256
|
|
256
|
-
|
257
|
+
next if is_dots(subdir)
|
257
258
|
|
258
|
-
|
259
|
+
subdir_path = File::join(dir, subdir)
|
259
260
|
|
260
|
-
|
261
|
+
fs = stat_or_nil_(subdir_path, flags)
|
261
262
|
|
262
|
-
|
263
|
+
next if not fs
|
263
264
|
|
264
|
-
|
265
|
+
next unless fs.directory?
|
265
266
|
|
266
|
-
|
267
|
-
|
267
|
+
subdirectories << fs
|
268
|
+
end
|
268
269
|
|
269
270
|
|
270
|
-
|
271
|
-
|
272
|
-
|
271
|
+
# now filter the file-stat instances and send each
|
272
|
+
# remaining to the block in Entry instance
|
273
|
+
entries.each do |fs|
|
273
274
|
|
274
|
-
|
275
|
+
next if not fs
|
275
276
|
|
276
|
-
|
277
|
+
if(0 == (Recls::SHOW_HIDDEN & flags))
|
277
278
|
|
278
|
-
|
279
|
+
if fs.hidden?
|
279
280
|
|
280
|
-
|
281
|
-
|
282
|
-
|
281
|
+
next
|
282
|
+
end
|
283
|
+
end
|
283
284
|
|
284
|
-
|
285
|
+
match = false
|
285
286
|
|
286
|
-
|
287
|
+
match ||= (0 == (Recls::TYPEMASK & flags))
|
287
288
|
|
288
|
-
|
289
|
-
|
290
|
-
|
289
|
+
match ||= (0 != (Recls::FILES & flags) && fs.file?)
|
290
|
+
match ||= (0 != (Recls::DIRECTORIES & flags) && fs.directory?)
|
291
|
+
match ||= (0 != (Recls::DEVICES & flags) && fs.blockdev?)
|
291
292
|
|
292
|
-
|
293
|
+
next unless match
|
293
294
|
|
294
|
-
|
295
|
-
|
295
|
+
blk.call Recls::Entry.new(fs.path, fs, search_root, flags)
|
296
|
+
end
|
296
297
|
|
297
|
-
|
298
|
+
# sub-directories
|
298
299
|
|
299
|
-
|
300
|
+
return unless (0 != (Recls::RECURSIVE & flags))
|
300
301
|
|
301
|
-
|
302
|
+
subdirectories.each do |fs|
|
302
303
|
|
303
|
-
|
304
|
+
if(0 == (Recls::SHOW_HIDDEN & flags))
|
304
305
|
|
305
|
-
|
306
|
+
if fs.hidden?
|
306
307
|
|
307
|
-
|
308
|
-
|
309
|
-
|
308
|
+
next
|
309
|
+
end
|
310
|
+
end
|
310
311
|
|
311
|
-
|
312
|
+
if(0 == (Recls::SEARCH_THROUGH_LINKS & flags))
|
312
313
|
|
313
|
-
|
314
|
+
if File.symlink? fs.path
|
314
315
|
|
315
|
-
|
316
|
-
|
317
|
-
|
316
|
+
next
|
317
|
+
end
|
318
|
+
end
|
318
319
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
320
|
+
FileSearch::search_directory_(search_root, fs.path, patterns, flags, &blk)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end # class FileSearch
|
323
324
|
end # module Recls
|
324
325
|
|
325
|
-
# ############################## end of file ############################# #
|
326
326
|
|
327
|
+
# ############################## end of file ############################# #
|
327
328
|
|
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,62 +34,64 @@
|
|
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
|
40
41
|
=end
|
41
42
|
|
43
|
+
# @!visibility private
|
42
44
|
class Object; end # :nodoc:
|
43
45
|
|
44
46
|
module Recls
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
# Specifies that files are to be listed
|
49
|
+
FILES = 0x00000001
|
50
|
+
# Specifies that directories are to be listed
|
51
|
+
DIRECTORIES = 0x00000002
|
52
|
+
# Specifies that links are to be listed (and not followed)
|
53
|
+
LINKS = 0x00000004
|
54
|
+
# Specifies that devices are to be listed
|
55
|
+
DEVICES = 0x00000008
|
56
|
+
# Type mask (combination of +Recls::FILES+, +Recls::DIRECTORIES+, +Recls::LINKS+, +Recls::DEVICES+)
|
57
|
+
TYPEMASK = 0x000000ff
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
59
|
+
# Specifies that hidden items are to be shown and hidden directories are
|
60
|
+
# to be searched
|
61
|
+
SHOW_HIDDEN = 0x00000100
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
# [IGNORED] This for compatibility with *recls* libraries written in other languages
|
64
|
+
DIR_PROGRESS = 0x00001000
|
65
|
+
# Causes search to terminate if a directory cannot be entered or an
|
66
|
+
# entry's information cannot be +stat()+'d
|
67
|
+
STOP_ON_ACCESS_FAILURE = 0x00002000
|
68
|
+
# [IGNORED] This for compatibility with *recls* libraries written in other languages
|
69
|
+
LINK_COUNT = 0000004000
|
70
|
+
# [IGNORED] This for compatibility with *recls* libraries written in other languages
|
71
|
+
NODE_INDEX = 0x00008000
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
+
# Causes search to operate recursively
|
74
|
+
RECURSIVE = 0x00010000
|
73
75
|
private
|
74
|
-
|
76
|
+
# @!visibility private
|
77
|
+
NO_SEARCH_LINKS = 0x00020000 # :nodoc:
|
75
78
|
public
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
# [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
|
80
|
+
DIRECTORY_PARTS = 0x00040000
|
81
|
+
# Causes operations (such as +Recls::stat()+) to obtain a result even when
|
82
|
+
# no corresponding file-system entity does not exist
|
83
|
+
DETAILS_LATER = 0x00080000
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
85
|
+
# Causes the +Recls::Entry#path+ and +Recls::Entry#search_relative_path+
|
86
|
+
# attributes to contain a trailing path-name-separator for directory
|
87
|
+
# entries
|
88
|
+
MARK_DIRECTORIES = 0x00200000
|
86
89
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
+
# Causes sub-directories that are links to be searched; default is not
|
91
|
+
# to search through links
|
92
|
+
SEARCH_THROUGH_LINKS = 0x00100000
|
90
93
|
end # module Recls
|
91
94
|
|
92
|
-
# ############################## end of file ############################# #
|
93
95
|
|
96
|
+
# ############################## end of file ############################# #
|
94
97
|
|