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