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/ximpl/util.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
#
|
2
|
-
# File:
|
1
|
+
# ######################################################################## #
|
2
|
+
# File: recls/ximpl/util.rb
|
3
3
|
#
|
4
|
-
# Purpose:
|
4
|
+
# Purpose: Internal implementation constructs for the recls 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
|
require 'recls/ximpl/os'
|
@@ -41,839 +42,839 @@ require 'recls/flags'
|
|
41
42
|
|
42
43
|
require 'pathname'
|
43
44
|
|
45
|
+
|
44
46
|
=begin
|
45
47
|
=end
|
46
48
|
|
47
|
-
module Recls # :nodoc:
|
48
|
-
|
49
49
|
# :stopdoc:
|
50
50
|
|
51
|
-
|
51
|
+
module Recls # :nodoc:
|
52
|
+
|
53
|
+
# @!visibility private
|
54
|
+
module Ximpl # :nodoc: all
|
52
55
|
|
53
|
-
|
56
|
+
module Util # :nodoc: all
|
54
57
|
|
55
|
-
|
56
|
-
|
58
|
+
# @!visibility private
|
59
|
+
def self.is_path_name_separator(c) # :nodoc:
|
57
60
|
|
58
|
-
|
61
|
+
return true if ?/ == c
|
59
62
|
|
60
|
-
|
63
|
+
if Recls::Ximpl::OS::OS_IS_WINDOWS
|
61
64
|
|
62
|
-
|
63
|
-
|
65
|
+
return true if ?\\ == c
|
66
|
+
end
|
64
67
|
|
65
|
-
|
66
|
-
|
68
|
+
return false
|
69
|
+
end
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
# Indicates whether a trailing slash is on the given path
|
72
|
+
#
|
73
|
+
# dependencies: none
|
74
|
+
#
|
75
|
+
# @!visibility private
|
76
|
+
def self.has_trailing_slash(p) # :nodoc:
|
74
77
|
|
75
|
-
|
78
|
+
return p if p.nil? or p.empty?
|
76
79
|
|
77
|
-
|
78
|
-
|
80
|
+
return self.is_path_name_separator(p[-1])
|
81
|
+
end
|
79
82
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
# returns the trailing slash, or nil if none present
|
84
|
+
#
|
85
|
+
# dependencies: none
|
86
|
+
#
|
87
|
+
# @!visibility private
|
88
|
+
def self.get_trailing_slash(p, args = {}) # :nodoc:
|
86
89
|
|
87
|
-
|
88
|
-
|
90
|
+
return nil if p.nil?
|
91
|
+
return nil if p.empty?
|
89
92
|
|
90
|
-
|
91
|
-
|
93
|
+
return self.is_path_name_separator(p[-1]) ? p[-1] : nil
|
94
|
+
end
|
92
95
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
96
|
+
# appends trailing slash to a path if not already
|
97
|
+
# present
|
98
|
+
#
|
99
|
+
# dependencies: none
|
100
|
+
#
|
101
|
+
# @!visibility private
|
102
|
+
def self.append_trailing_slash(p, slash = nil) # :nodoc:
|
100
103
|
|
101
|
-
|
104
|
+
return p if not p or p.empty?
|
102
105
|
|
103
|
-
|
106
|
+
return p if self.is_path_name_separator(p[-1])
|
104
107
|
|
105
|
-
|
108
|
+
slash = '/' if not slash
|
106
109
|
|
107
|
-
|
108
|
-
|
110
|
+
"#{p}#{slash}"
|
111
|
+
end
|
109
112
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
113
|
+
# trims trailing slash from a path, unless it is the
|
114
|
+
# root
|
115
|
+
#
|
116
|
+
# dependencies: none
|
117
|
+
#
|
118
|
+
# @!visibility private
|
119
|
+
def self.trim_trailing_slash(p) # :nodoc:
|
117
120
|
|
118
|
-
|
121
|
+
return p if not p or p.empty?
|
119
122
|
|
120
|
-
|
123
|
+
p = p[0 ... -1] if self.is_path_name_separator(p[-1])
|
121
124
|
|
122
|
-
|
123
|
-
|
125
|
+
return p
|
126
|
+
end
|
124
127
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
128
|
+
# From path p, returns a tuple containing either:
|
129
|
+
#
|
130
|
+
# [ nil, p ] if p does not contain a Windows root, or
|
131
|
+
#
|
132
|
+
# [ wroot, remainder ] if p does contain a Windows root, or
|
133
|
+
#
|
134
|
+
# [ nil, nil ] if p is nil
|
135
|
+
#
|
136
|
+
# dependencies: none
|
137
|
+
#
|
138
|
+
# @!visibility private
|
139
|
+
def self.get_windows_root(p) # :nodoc:
|
137
140
|
|
138
|
-
|
141
|
+
return [ nil, nil ] if not p
|
139
142
|
|
140
|
-
|
143
|
+
if Recls::Ximpl::OS::OS_IS_WINDOWS
|
141
144
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
145
|
+
# Windows local drive (e.g. 'H:')
|
146
|
+
#
|
147
|
+
# NOTE: this works for both rooted and unrooted paths
|
148
|
+
if p =~ /^([a-zA-Z]:)/
|
146
149
|
|
147
|
-
|
148
|
-
|
150
|
+
return [ $1, $' ]
|
151
|
+
end
|
149
152
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
153
|
+
# UNC network drive
|
154
|
+
#
|
155
|
+
# NOTE: there are several permutations ...
|
156
|
+
if p =~ /^(\\\\[^\\\/:*?<>|]+\\[^\\\/:*?<>|]+)([\\\/].*)$/
|
154
157
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
158
|
+
# \\server\share{\{... rest of path}}
|
159
|
+
return [ $1, $2 ]
|
160
|
+
end
|
161
|
+
if p =~ /^(\\\\[^\\\/:*?<>|]+\\[^\\\/:*?<>|]+)$/
|
159
162
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
163
|
+
# \\server\share
|
164
|
+
return [ $1, nil ]
|
165
|
+
end
|
166
|
+
if p =~ /^(\\\\[^\\\/:*?<>|]+\\)$/
|
167
|
+
|
168
|
+
# \\server\
|
169
|
+
return [ $1, nil ]
|
170
|
+
end
|
171
|
+
if p =~ /^(\\\\[^\\\/:*?<>|]+)$/
|
172
|
+
|
173
|
+
# \\server
|
174
|
+
return [ $1, nil ]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
return [ nil, p ]
|
179
|
+
end
|
180
|
+
|
181
|
+
# obtains the parts from a path, including any Windows root and
|
182
|
+
# the file basename
|
183
|
+
#
|
184
|
+
# @!visibility private
|
185
|
+
def self.path_parts(path) # :nodoc:
|
186
|
+
|
187
|
+
return nil if path.nil?
|
188
|
+
return [] if path.empty?
|
189
|
+
|
190
|
+
parts = []
|
191
|
+
|
192
|
+
wr, rem = self.get_windows_root(path)
|
193
|
+
|
194
|
+
parts << wr if wr
|
195
|
+
|
196
|
+
until rem.nil? || rem.empty?
|
197
|
+
|
198
|
+
if rem =~ /^([^\\\/]*[\\\/])/
|
199
|
+
|
200
|
+
parts << $1
|
201
|
+
rem = $'
|
202
|
+
else
|
200
203
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
204
|
+
parts << rem
|
205
|
+
rem = ''
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
parts
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns a tuple consisting of the following
|
213
|
+
# elements (or nil, for any element that is not)
|
214
|
+
# present
|
215
|
+
#
|
216
|
+
# f1. Windows root, or nil
|
217
|
+
# f2. directory, or nil
|
218
|
+
# f3. basename, or nil
|
219
|
+
# f4. basename-minus-extension, or nil
|
220
|
+
# f5. extension, or nil
|
221
|
+
# f6. array of directory path parts, which may be empty (not nil)
|
222
|
+
# f7. array of all path parts, which may be empty (not nil)
|
223
|
+
#
|
224
|
+
# dependencies: Util.path_parts, Util.get_windows_root
|
225
|
+
#
|
226
|
+
# @!visibility private
|
227
|
+
def self.split_path(p) # :nodoc:
|
225
228
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
+
f1_windows_root, remainder = self.get_windows_root p
|
230
|
+
f1_windows_root = nil if not f1_windows_root or f1_windows_root.empty?
|
231
|
+
remainder = nil if not remainder or remainder.empty?
|
229
232
|
|
230
|
-
|
233
|
+
if not remainder or remainder.empty?
|
231
234
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
235
|
+
f2_directory = nil
|
236
|
+
f3_basename = nil
|
237
|
+
f4_nameonly = nil
|
238
|
+
f5_extension = nil
|
239
|
+
else
|
237
240
|
|
238
|
-
|
241
|
+
if remainder =~ /^(.*[\\\/])([^\\\/]*)$/
|
239
242
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
+
f2_directory = $1
|
244
|
+
f3_basename = $2
|
245
|
+
else
|
243
246
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
247
|
+
f2_directory = nil
|
248
|
+
f3_basename = remainder
|
249
|
+
f4_nameonly = nil
|
250
|
+
f5_extension = nil
|
251
|
+
end
|
249
252
|
|
250
|
-
|
251
|
-
|
253
|
+
f2_directory = nil if not f2_directory or f2_directory.empty?
|
254
|
+
f3_basename = nil if not f3_basename or f3_basename.empty?
|
252
255
|
|
253
|
-
|
256
|
+
if f3_basename
|
254
257
|
|
255
|
-
|
256
|
-
|
258
|
+
# special case: treat '.' and '..' as file-name only
|
259
|
+
if '.' == f3_basename or '..' == f3_basename
|
257
260
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
+
f4_nameonly = f3_basename
|
262
|
+
f5_extension = nil
|
263
|
+
elsif f3_basename =~ /^(.*)(\.[^.]*)$/
|
261
264
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
+
f4_nameonly = $1
|
266
|
+
f5_extension = $2
|
267
|
+
else
|
265
268
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
269
|
+
f4_nameonly = f3_basename
|
270
|
+
f5_extension = nil
|
271
|
+
end
|
272
|
+
else
|
270
273
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
274
|
+
f4_nameonly = nil
|
275
|
+
f5_extension = nil
|
276
|
+
end
|
277
|
+
end
|
275
278
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
279
|
+
f4_nameonly = nil if not f4_nameonly or f4_nameonly.empty?
|
280
|
+
f5_extension = nil if not f5_extension or f5_extension.empty?
|
281
|
+
f6_directory_parts = self.path_parts(f2_directory)
|
282
|
+
f7_path_parts = self.path_parts(p)
|
280
283
|
|
281
|
-
|
282
|
-
|
284
|
+
return [ f1_windows_root, f2_directory, f3_basename, f4_nameonly, f5_extension, f6_directory_parts, f7_path_parts ]
|
285
|
+
end
|
283
286
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
287
|
+
# Returns a tuple consisting of:
|
288
|
+
#
|
289
|
+
# f1. The canonicalised array of parts
|
290
|
+
# f2. A boolean indicating whether to 'consume' the basename
|
291
|
+
#
|
292
|
+
# dependencies: OS.is_root_dir_, OS.get_number_of_dots_dir_,
|
293
|
+
#
|
294
|
+
# @!visibility private
|
295
|
+
def self.canonicalise_parts(parts, basename = nil) # :nodoc:
|
293
296
|
|
294
|
-
|
297
|
+
newParts = []
|
295
298
|
|
296
|
-
|
299
|
+
lastSingleDots = nil
|
297
300
|
|
298
|
-
|
301
|
+
path_is_rooted = nil
|
299
302
|
|
300
|
-
|
301
|
-
|
303
|
+
index = -1
|
304
|
+
parts.each do |part|
|
302
305
|
|
303
|
-
|
306
|
+
index += 1
|
304
307
|
|
305
|
-
|
306
|
-
|
308
|
+
next if not part
|
309
|
+
next if part.empty?
|
307
310
|
|
308
|
-
|
311
|
+
if path_is_rooted.nil?
|
309
312
|
|
310
|
-
|
311
|
-
|
313
|
+
path_is_rooted = self.is_path_name_separator(part[0])
|
314
|
+
end
|
312
315
|
|
313
|
-
|
316
|
+
if ?. == part[0]
|
314
317
|
|
315
|
-
|
318
|
+
if self.is_path_name_separator(part[1])
|
316
319
|
|
317
|
-
|
320
|
+
# single dots, so ...
|
318
321
|
|
319
|
-
|
320
|
-
|
322
|
+
# ... remember the last instance, and ...
|
323
|
+
lastSingleDots = part
|
321
324
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
+
# ... skip to leave this out of the result
|
326
|
+
next
|
327
|
+
elsif ?. == part[1]
|
325
328
|
|
326
|
-
|
329
|
+
if self.is_path_name_separator(part[2])
|
327
330
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
331
|
+
# double dots, so ...
|
332
|
+
# ... skip this and pop prior from the new list iff:
|
333
|
+
#
|
334
|
+
# 1. there is a prior elements in the new list (size > 1); AND
|
335
|
+
# 2. the last element in the new list is not the root directory; AND
|
336
|
+
# 3. the last element in the list is not a dots directory
|
337
|
+
if not newParts.empty? # 1.
|
335
338
|
|
336
|
-
|
337
|
-
|
339
|
+
priorPart = newParts[-1]
|
340
|
+
if 1 == newParts.size and OS.is_root_dir_(priorPart)
|
338
341
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
+
# 2.
|
343
|
+
next
|
344
|
+
else
|
342
345
|
|
343
|
-
|
344
|
-
|
346
|
+
dirtype = OS.get_number_of_dots_dir_(priorPart)
|
347
|
+
if 0 == dirtype # 3.
|
345
348
|
|
346
|
-
|
349
|
+
if newParts.pop
|
347
350
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
351
|
+
next
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
else
|
354
357
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
+
# it's a ..X part
|
359
|
+
end
|
360
|
+
else
|
358
361
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
+
# it's a .X part
|
363
|
+
end
|
364
|
+
else
|
362
365
|
|
363
|
-
|
364
|
-
|
366
|
+
# it's a non-dots part
|
367
|
+
end
|
365
368
|
|
366
|
-
|
367
|
-
|
369
|
+
newParts << part
|
370
|
+
end
|
368
371
|
|
369
|
-
|
372
|
+
consume_basename = false
|
370
373
|
|
371
|
-
|
374
|
+
if basename
|
372
375
|
|
373
|
-
|
376
|
+
if ?. == basename[0]
|
374
377
|
|
375
|
-
|
378
|
+
if 1 == basename.size
|
376
379
|
|
377
|
-
|
378
|
-
|
380
|
+
# single dots
|
381
|
+
if newParts.empty?
|
379
382
|
|
380
|
-
|
381
|
-
|
383
|
+
lastSingleDots = false
|
384
|
+
else
|
382
385
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
+
consume_basename = true
|
387
|
+
end
|
388
|
+
elsif ?. == basename[1] and 2 == basename.size
|
386
389
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
390
|
+
# double dots, so ...
|
391
|
+
#
|
392
|
+
# ... pop unless we already have some outstanding double dots
|
393
|
+
if newParts.empty?
|
391
394
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
+
newParts << '..'
|
396
|
+
consume_basename = true
|
397
|
+
elsif 1 == newParts.size && 1 == newParts[0].size && Util.is_path_name_separator(newParts[0][0])
|
395
398
|
|
396
|
-
|
397
|
-
|
399
|
+
consume_basename = true
|
400
|
+
else
|
398
401
|
|
399
|
-
|
402
|
+
if 2 != OS.get_number_of_dots_dir_(newParts[-1])
|
400
403
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
404
|
+
newParts.pop
|
405
|
+
consume_basename = true
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
408
411
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
+
# push lastSingleDots (which may contain a trailing slash) if
|
413
|
+
# exists and newParts is empty
|
414
|
+
newParts << lastSingleDots if lastSingleDots and newParts.empty?
|
412
415
|
|
413
|
-
|
416
|
+
if not newParts.empty?
|
414
417
|
|
415
|
-
|
418
|
+
if 2 == OS.get_number_of_dots_dir_(newParts[-1])
|
416
419
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
420
|
+
# the last element is the double-dots directory, but
|
421
|
+
# need to determine whether to ensure/remote a
|
422
|
+
# trailing slash
|
423
|
+
if basename and not basename.empty?
|
421
424
|
|
422
|
-
|
425
|
+
if not consume_basename
|
423
426
|
|
424
|
-
|
425
|
-
|
427
|
+
# leave as is
|
428
|
+
else
|
426
429
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
else
|
430
|
+
newParts[-1] = '..'
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
else
|
433
435
|
|
434
|
-
|
435
|
-
|
436
|
-
|
436
|
+
# handle case where all (double)-dots have eliminated
|
437
|
+
# all regular directories
|
438
|
+
if not basename or basename.empty? or consume_basename
|
437
439
|
|
438
|
-
|
439
|
-
|
440
|
-
|
440
|
+
newParts << '.'
|
441
|
+
end
|
442
|
+
end
|
441
443
|
|
442
|
-
|
443
|
-
|
444
|
-
|
444
|
+
[ newParts.join(''), consume_basename ]
|
445
|
+
end
|
446
|
+
end # module Util
|
445
447
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
448
|
+
# Canonicalises a path
|
449
|
+
#
|
450
|
+
# Note: contains a trailing slash if, in the context of the given
|
451
|
+
# path, the last element of the canonicalised path is a directory
|
452
|
+
# unequivocally
|
453
|
+
#
|
454
|
+
# @!visibility private
|
455
|
+
def self.canonicalise_path(path) # :nodoc:
|
454
456
|
|
455
|
-
|
456
|
-
|
457
|
+
return nil if not path
|
458
|
+
return '' if path.empty?
|
457
459
|
|
458
|
-
|
460
|
+
path = File.expand_path(path) if '~' == path[0].to_s
|
459
461
|
|
460
|
-
|
462
|
+
f1_windows_root, f2_directory, f3_basename, dummy1, dummy2, directory_parts, dummy3 = Util.split_path(path)
|
461
463
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
464
|
+
# suppress unused warnings
|
465
|
+
dummy1 = dummy1
|
466
|
+
dummy2 = dummy2
|
467
|
+
dummy3 = dummy3
|
466
468
|
|
467
|
-
|
469
|
+
if not f2_directory
|
468
470
|
|
469
|
-
|
470
|
-
|
471
|
+
canonicalised_directory = nil
|
472
|
+
else
|
471
473
|
|
472
|
-
|
473
|
-
|
474
|
-
|
474
|
+
canonicalised_directory, consume_basename = Util.canonicalise_parts(directory_parts, f3_basename)
|
475
|
+
f3_basename = nil if consume_basename
|
476
|
+
end
|
475
477
|
|
476
|
-
|
477
|
-
|
478
|
+
return "#{f1_windows_root}#{canonicalised_directory}#{f3_basename}"
|
479
|
+
end
|
478
480
|
|
479
|
-
|
480
|
-
|
481
|
+
# @!visibility private
|
482
|
+
def self.absolute_path?(path) # :nodoc:
|
481
483
|
|
482
|
-
|
483
|
-
|
484
|
+
case path
|
485
|
+
when nil
|
484
486
|
|
485
|
-
|
486
|
-
|
487
|
+
return nil
|
488
|
+
when ::String
|
487
489
|
|
488
|
-
|
490
|
+
return nil if path.empty?
|
489
491
|
|
490
|
-
|
491
|
-
|
492
|
+
path = File.expand_path(path) if '~' == path[0]
|
493
|
+
when ::Recls::Entry
|
492
494
|
|
493
|
-
|
494
|
-
|
495
|
+
return path
|
496
|
+
else
|
495
497
|
|
496
|
-
|
497
|
-
|
498
|
+
raise TypeError, "parameter path ('#{path}') is of type #{path.class} must be nil or an instance of #{::String} or #{::Recls::Entry}"
|
499
|
+
end
|
498
500
|
|
499
|
-
|
501
|
+
f1_windows_root, f2_directory, dummy1, dummy2, dummy3, dummy4, dummy5 = Util.split_path(path)
|
500
502
|
|
501
|
-
|
503
|
+
dummy1 = dummy2 = dummy3 = dummy4 = dummy5 = nil
|
502
504
|
|
503
|
-
|
505
|
+
unless f1_windows_root
|
504
506
|
|
505
|
-
|
507
|
+
return nil unless f2_directory
|
506
508
|
|
507
|
-
|
508
|
-
|
509
|
+
return nil unless Util.is_path_name_separator(f2_directory[0])
|
510
|
+
end
|
509
511
|
|
510
|
-
|
511
|
-
|
512
|
+
Recls::Ximpl.stat_prep(path, nil, Recls::DETAILS_LATER)
|
513
|
+
end
|
512
514
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
515
|
+
# determines the absolute path of a given path
|
516
|
+
#
|
517
|
+
# @!visibility private
|
518
|
+
def self.absolute_path(path, refdir = nil) # :nodoc:
|
517
519
|
|
518
|
-
|
519
|
-
|
520
|
+
case path
|
521
|
+
when ::NilClass
|
520
522
|
|
521
|
-
|
522
|
-
|
523
|
+
return nil
|
524
|
+
when ::String
|
523
525
|
|
524
|
-
|
525
|
-
|
526
|
+
path = File.expand_path(path) if '~' == path[0]
|
527
|
+
when ::Recls::Entry
|
526
528
|
|
527
|
-
|
528
|
-
|
529
|
+
return path.path
|
530
|
+
else
|
529
531
|
|
530
|
-
|
531
|
-
|
532
|
+
raise TypeError, "parameter path ('#{path}') is of type #{path.class} must be an instance of #{::String} or Recls::Entry"
|
533
|
+
end
|
532
534
|
|
533
|
-
|
535
|
+
return '' if path.empty?
|
534
536
|
|
535
|
-
|
537
|
+
dummy1, f2_directory, dummy2, dummy3, dummy4, dummy5, dummy6 = Util.split_path(path)
|
536
538
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
539
|
+
# suppress unused warnings
|
540
|
+
dummy1 = dummy1
|
541
|
+
dummy2 = dummy2
|
542
|
+
dummy3 = dummy3
|
543
|
+
dummy4 = dummy4
|
544
|
+
dummy5 = dummy5
|
545
|
+
dummy6 = dummy6
|
544
546
|
|
545
|
-
|
547
|
+
if f2_directory =~ /^[\\\/]/
|
546
548
|
|
547
|
-
|
548
|
-
|
549
|
+
return path
|
550
|
+
end
|
549
551
|
|
550
|
-
|
552
|
+
cwd = refdir ? refdir : Dir.getwd
|
551
553
|
|
552
|
-
|
554
|
+
trailing_slash = Util.get_trailing_slash(path)
|
553
555
|
|
554
|
-
|
556
|
+
if '.' == path
|
555
557
|
|
556
|
-
|
557
|
-
|
558
|
+
return Util.trim_trailing_slash cwd
|
559
|
+
elsif 2 == path.size and trailing_slash
|
558
560
|
|
559
|
-
|
560
|
-
|
561
|
+
return Util.append_trailing_slash(cwd, path[1..1])
|
562
|
+
end
|
561
563
|
|
562
|
-
|
564
|
+
cwd = Util.append_trailing_slash(cwd)
|
563
565
|
|
564
|
-
|
566
|
+
path = "#{cwd}#{path}"
|
565
567
|
|
566
|
-
|
568
|
+
path = canonicalise_path path
|
567
569
|
|
568
|
-
|
570
|
+
if trailing_slash
|
569
571
|
|
570
|
-
|
571
|
-
|
572
|
+
path = Util.append_trailing_slash path, trailing_slash
|
573
|
+
else
|
572
574
|
|
573
|
-
|
574
|
-
|
575
|
+
path = Util.trim_trailing_slash path
|
576
|
+
end
|
575
577
|
|
576
|
-
|
577
|
-
|
578
|
+
path
|
579
|
+
end
|
578
580
|
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
581
|
+
# obtains the basename of a path, e.g.
|
582
|
+
# the basename of
|
583
|
+
# abc/def/ghi.jkl
|
584
|
+
# or (on Windows)
|
585
|
+
# C:\abc\def\ghi.jkl
|
586
|
+
# is
|
587
|
+
# ghi.jkl
|
588
|
+
#
|
589
|
+
# @!visibility private
|
590
|
+
def self.basename(path) # :nodoc:
|
589
591
|
|
590
|
-
|
592
|
+
return nil if not path
|
591
593
|
|
592
|
-
|
593
|
-
|
594
|
-
|
594
|
+
# NOTE: we don't implement in terms of split_path
|
595
|
+
# because all but the UNC case work with just
|
596
|
+
# detecting the last (back)slash
|
595
597
|
|
596
|
-
|
598
|
+
if Recls::Ximpl::OS::OS_IS_WINDOWS
|
597
599
|
|
598
|
-
|
600
|
+
wr, rem = Util.get_windows_root(path)
|
599
601
|
|
600
|
-
|
601
|
-
|
602
|
+
# suppress unused warning
|
603
|
+
wr = wr
|
602
604
|
|
603
|
-
|
605
|
+
if not rem
|
604
606
|
|
605
|
-
|
606
|
-
|
607
|
+
return ''
|
608
|
+
else
|
607
609
|
|
608
|
-
|
609
|
-
|
610
|
-
|
610
|
+
path = rem
|
611
|
+
end
|
612
|
+
end
|
611
613
|
|
612
|
-
|
614
|
+
if not path.is_a? String
|
613
615
|
|
614
|
-
|
615
|
-
|
616
|
+
path = path.to_s
|
617
|
+
end
|
616
618
|
|
617
|
-
|
619
|
+
if path =~ /^.*[\/\\](.*)/
|
618
620
|
|
619
|
-
|
620
|
-
|
621
|
+
$1
|
622
|
+
else
|
621
623
|
|
622
|
-
|
623
|
-
|
624
|
-
|
624
|
+
path
|
625
|
+
end
|
626
|
+
end
|
625
627
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
628
|
+
# obtains the file extension of a basename, e.g.
|
629
|
+
# the file_ext of
|
630
|
+
# ghi.jkl
|
631
|
+
# is
|
632
|
+
# .jkl
|
633
|
+
#
|
634
|
+
# @!visibility private
|
635
|
+
def self.file_ext(path) # :nodoc:
|
634
636
|
|
635
|
-
|
637
|
+
return nil if not path
|
636
638
|
|
637
|
-
|
639
|
+
use_split_path = false
|
638
640
|
|
639
|
-
|
641
|
+
if Recls::Ximpl::OS::OS_IS_WINDOWS
|
640
642
|
|
641
|
-
|
643
|
+
if path.include? ?\\
|
642
644
|
|
643
|
-
|
644
|
-
|
645
|
-
|
645
|
+
use_split_path = true
|
646
|
+
end
|
647
|
+
end
|
646
648
|
|
647
|
-
|
649
|
+
if path.include? ?/
|
648
650
|
|
649
|
-
|
650
|
-
|
651
|
+
use_split_path = true
|
652
|
+
end
|
651
653
|
|
652
|
-
|
654
|
+
if use_split_path
|
653
655
|
|
654
|
-
|
655
|
-
|
656
|
+
ext = Util.split_path(path)[4]
|
657
|
+
else
|
656
658
|
|
657
|
-
|
659
|
+
if path =~ /^.*(\.[^.]*)$/
|
658
660
|
|
659
|
-
|
660
|
-
|
661
|
+
ext = $1
|
662
|
+
else
|
661
663
|
|
662
|
-
|
663
|
-
|
664
|
-
|
664
|
+
ext = nil
|
665
|
+
end
|
666
|
+
end
|
665
667
|
|
666
|
-
|
667
|
-
|
668
|
+
return ext ? ext : ''
|
669
|
+
end
|
668
670
|
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
671
|
+
# obtains the directory from the directory path
|
672
|
+
#
|
673
|
+
# @!visibility private
|
674
|
+
def self.directory_from_directory_path(directory_path) # :nodoc:
|
673
675
|
|
674
|
-
|
676
|
+
wr, rem = Util.get_windows_root(directory_path)
|
675
677
|
|
676
|
-
|
677
|
-
|
678
|
+
# suppress unused warning
|
679
|
+
wr = wr
|
678
680
|
|
679
|
-
|
680
|
-
|
681
|
+
rem
|
682
|
+
end
|
681
683
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
684
|
+
# obtains the directory parts from a directory
|
685
|
+
#
|
686
|
+
# @!visibility private
|
687
|
+
def self.directory_parts_from_directory(directory) # :nodoc:
|
686
688
|
|
687
|
-
|
689
|
+
return nil if not directory
|
688
690
|
|
689
|
-
|
691
|
+
directory_parts = []
|
690
692
|
|
691
|
-
|
693
|
+
until directory.empty?
|
692
694
|
|
693
|
-
|
695
|
+
if directory =~ /^([^\\\/]*[\\\/])/
|
694
696
|
|
695
|
-
|
696
|
-
|
697
|
-
|
697
|
+
directory_parts << $1
|
698
|
+
directory = $'
|
699
|
+
else
|
698
700
|
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
701
|
+
directory_parts << directory
|
702
|
+
directory = ''
|
703
|
+
end
|
704
|
+
end
|
703
705
|
|
704
|
-
|
705
|
-
|
706
|
+
directory_parts
|
707
|
+
end
|
706
708
|
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
709
|
+
# obtains the relative path of a given path and
|
710
|
+
# a reference directory
|
711
|
+
#
|
712
|
+
# @!visibility private
|
713
|
+
def self.derive_relative_path(origin, path) # :nodoc:
|
712
714
|
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
715
|
+
return nil if path.nil?
|
716
|
+
return nil if path.empty?
|
717
|
+
return path if origin.nil?
|
718
|
+
return path if origin.empty?
|
717
719
|
|
718
|
-
|
719
|
-
|
720
|
+
path = self.canonicalise_path path
|
721
|
+
origin = self.canonicalise_path origin
|
720
722
|
|
721
|
-
|
722
|
-
|
723
|
+
path = self.absolute_path path
|
724
|
+
origin = self.absolute_path origin
|
723
725
|
|
724
|
-
|
726
|
+
return path if /^\.[\\\/]*$/ =~ origin
|
725
727
|
|
726
|
-
|
727
|
-
|
728
|
+
path_splits = Util.split_path(path)
|
729
|
+
origin_splits = Util.split_path(origin)
|
728
730
|
|
729
|
-
|
731
|
+
# if different windows root, then cannot provide relative
|
730
732
|
|
731
|
-
|
733
|
+
if path_splits[0] and origin_splits[0]
|
732
734
|
|
733
|
-
|
734
|
-
|
735
|
+
return path if path_splits[0] != origin_splits[0]
|
736
|
+
end
|
735
737
|
|
736
|
-
|
738
|
+
trailing_slash = Util.get_trailing_slash(path)
|
737
739
|
|
738
|
-
|
739
|
-
|
740
|
+
path_parts = path_splits[6]
|
741
|
+
origin_parts = origin_splits[6]
|
740
742
|
|
741
|
-
|
743
|
+
loop do
|
742
744
|
|
743
|
-
|
744
|
-
|
745
|
+
break if path_parts.empty?
|
746
|
+
break if origin_parts.empty?
|
745
747
|
|
746
|
-
|
747
|
-
|
748
|
+
path_part = path_parts[0]
|
749
|
+
origin_part = origin_parts[0]
|
748
750
|
|
749
|
-
|
751
|
+
if 1 == path_parts.size || 1 == origin_parts.size
|
750
752
|
|
751
|
-
|
752
|
-
|
753
|
-
|
753
|
+
path_part = Util.append_trailing_slash(path_part)
|
754
|
+
origin_part = Util.append_trailing_slash(origin_part)
|
755
|
+
end
|
754
756
|
|
755
|
-
|
757
|
+
if path_part == origin_part
|
756
758
|
|
757
|
-
|
758
|
-
|
759
|
-
|
759
|
+
path_parts.shift
|
760
|
+
origin_parts.shift
|
761
|
+
else
|
760
762
|
|
761
|
-
|
762
|
-
|
763
|
-
|
763
|
+
break
|
764
|
+
end
|
765
|
+
end
|
764
766
|
|
765
|
-
|
767
|
+
return ".#{trailing_slash}" if path_parts.empty? and origin_parts.empty?
|
766
768
|
|
767
|
-
|
769
|
+
# at this point, all reference parts should be converted into '..'
|
768
770
|
|
769
|
-
|
771
|
+
return origin_parts.map { |rp| '..' }.join('/') if path_parts.empty?
|
770
772
|
|
771
|
-
|
772
|
-
|
773
|
+
return '../' * origin_parts.size + path_parts.join('')
|
774
|
+
end
|
773
775
|
|
774
|
-
|
775
|
-
|
776
|
+
# @!visibility private
|
777
|
+
def self.combine_paths(paths, options) # :nodoc:
|
776
778
|
|
777
|
-
|
778
|
-
|
779
|
+
paths = [ paths ] unless ::Array === paths
|
780
|
+
abs_ix = 0
|
779
781
|
|
780
|
-
|
782
|
+
paths = paths.map { |path| '~' == path[0].to_s ? File.expand_path(path) : path }
|
781
783
|
|
782
|
-
|
784
|
+
paths.each_with_index do |path, index|
|
783
785
|
|
784
|
-
|
786
|
+
dummy1, f2_directory, dummy2, dummy3, dummy4, dummy5, dummy6 = Util.split_path(path)
|
785
787
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
788
|
+
# suppress unused warnings
|
789
|
+
dummy1 = dummy1
|
790
|
+
dummy2 = dummy2
|
791
|
+
dummy3 = dummy3
|
792
|
+
dummy4 = dummy4
|
793
|
+
dummy5 = dummy5
|
794
|
+
dummy6 = dummy6
|
793
795
|
|
794
|
-
|
796
|
+
if f2_directory && Util.is_path_name_separator(f2_directory[0])
|
795
797
|
|
796
|
-
|
797
|
-
|
798
|
-
|
798
|
+
abs_ix = index
|
799
|
+
end
|
800
|
+
end
|
799
801
|
|
800
|
-
|
802
|
+
paths = paths[abs_ix..-1]
|
801
803
|
|
802
|
-
|
804
|
+
r = File.join(*paths)
|
803
805
|
|
804
|
-
|
806
|
+
cap = options[:canonicalise] || options[:canonicalize]
|
805
807
|
|
806
|
-
|
808
|
+
if cap
|
807
809
|
|
808
|
-
|
809
|
-
|
810
|
+
r = Recls.canonicalise_path r
|
811
|
+
else
|
810
812
|
|
811
|
-
|
813
|
+
clp = options[:clean] || options[:clean_path]
|
812
814
|
|
813
|
-
|
815
|
+
if clp
|
814
816
|
|
815
|
-
|
816
|
-
|
817
|
-
|
817
|
+
r = Pathname.new(r).cleanpath.to_s
|
818
|
+
end
|
819
|
+
end
|
818
820
|
|
819
|
-
|
820
|
-
|
821
|
+
r
|
822
|
+
end
|
821
823
|
|
822
824
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
825
|
+
# Elicits the contents of the given directory, or, if the flag
|
826
|
+
# STOP_ON_ACCESS_FAILURE is specified throws an exception if the
|
827
|
+
# directory does not exist
|
828
|
+
#
|
829
|
+
# Some known conditions:
|
830
|
+
#
|
831
|
+
# * (Mac OSX) /dev/fd/<N> - some of these stat() as directories but
|
832
|
+
# Dir.new fails with ENOTDIR
|
833
|
+
#
|
834
|
+
# @!visibility private
|
835
|
+
def self.dir_entries_maybe(dir, flags) # :nodoc:
|
834
836
|
|
835
|
-
|
837
|
+
begin
|
836
838
|
|
837
|
-
|
839
|
+
Dir.new(dir).to_a
|
838
840
|
|
839
|
-
|
841
|
+
rescue SystemCallError => x
|
840
842
|
|
841
|
-
|
843
|
+
$stderr.puts "exception (#{x.class}): #{x}" if $DEBUG
|
842
844
|
|
843
|
-
|
845
|
+
if(0 != (STOP_ON_ACCESS_FAILURE & flags))
|
844
846
|
|
845
|
-
|
846
|
-
|
847
|
+
raise
|
848
|
+
end
|
847
849
|
|
848
|
-
|
849
|
-
|
850
|
-
|
850
|
+
return []
|
851
|
+
end
|
852
|
+
end
|
851
853
|
|
852
|
-
|
853
|
-
|
854
|
+
# @!visibility private
|
855
|
+
def self.stat_prep(path, search_root, flags) # :nodoc:
|
854
856
|
|
855
|
-
|
857
|
+
begin
|
856
858
|
|
857
|
-
|
858
|
-
|
859
|
+
Recls::Entry.new(path, Recls::Ximpl::FileStat.stat(path), search_root, flags)
|
860
|
+
rescue Errno::ENOENT, Errno::ENXIO => x
|
859
861
|
|
860
|
-
|
862
|
+
x = x # suppress warning
|
861
863
|
|
862
|
-
|
864
|
+
if 0 != (flags & Recls::DETAILS_LATER)
|
863
865
|
|
864
|
-
|
865
|
-
|
866
|
+
Recls::Entry.new(path, nil, search_root, flags)
|
867
|
+
else
|
866
868
|
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
869
|
+
nil
|
870
|
+
end
|
871
|
+
end
|
872
|
+
end
|
873
|
+
end # module Ximpl
|
874
|
+
end # module Recls
|
872
875
|
|
873
876
|
# :startdoc:
|
874
877
|
|
875
|
-
end # module Recls
|
876
878
|
|
877
879
|
# ############################## end of file ############################# #
|
878
880
|
|
879
|
-
|