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