recls-ruby 2.6.4

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.
data/lib/recls/stat.rb ADDED
@@ -0,0 +1,104 @@
1
+ # ######################################################################### #
2
+ # File: recls/stat.rb
3
+ #
4
+ # Purpose: Defines the Recls.stat() method for the recls.Ruby library.
5
+ #
6
+ # Created: 24th July 2012
7
+ # Updated: 28th December 2015
8
+ #
9
+ # Author: Matthew Wilson
10
+ #
11
+ # Copyright (c) 2012-2015, Matthew Wilson and Synesis Software
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are met:
16
+ #
17
+ # * Redistributions of source code must retain the above copyright notice,
18
+ # this list of conditions and the following disclaimer.
19
+ #
20
+ # * Redistributions in binary form must reproduce the above copyright notice,
21
+ # this list of conditions and the following disclaimer in the documentation
22
+ # and/or other materials provided with the distribution.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # ######################################################################### #
37
+
38
+
39
+ require 'recls/entry'
40
+ require 'recls/flags'
41
+
42
+ module Recls
43
+
44
+ # USAGE:
45
+ #
46
+ # - stat(path)
47
+ # - stat(path, flags)
48
+ # - stat(path, search_root)
49
+ # - stat(path, search_root, flags)
50
+ # - stat(path, flags, search_root)
51
+ def self.stat(path, *args)
52
+
53
+ flags = 0
54
+ search_root = nil
55
+ message = nil
56
+
57
+ path = File.expand_path(path) if path =~ /^~[\\\/]*/
58
+
59
+ case args.size
60
+ when 0
61
+ ;
62
+ when 1
63
+ case args[0]
64
+ when ::Integer
65
+ flags = args[0]
66
+ when ::String
67
+ search_root = args[0]
68
+ else
69
+ message = "argument '#{args[0]}' (#{args[0].class}) not valid"
70
+ end
71
+ when 2
72
+ if false
73
+ elsif ::Integer === args[0] && ::String === args[1]
74
+ flags = args[0]
75
+ search_root = args[1]
76
+ elsif ::String === args[0] && ::Integer === args[1]
77
+ search_root = args[0]
78
+ flags = args[1]
79
+ else
80
+ message = "invalid combination of arguments"
81
+ end
82
+ else
83
+ message = "too many arguments"
84
+ end
85
+
86
+ raise ArgumentError, "#{message}: Recls.stat() takes one (path), two (path+flags or path+search_root), or three (path+search_root+flags) arguments" if message
87
+
88
+ begin
89
+ Recls::Entry.new(path, Recls::Ximpl::FileStat.stat(path), search_root, flags)
90
+ rescue Errno::ENOENT => x
91
+
92
+ if 0 != (flags & Recls::DETAILS_LATER)
93
+
94
+ Recls::Entry.new(path, nil, search_root, flags)
95
+ else
96
+
97
+ nil
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ # ############################## end of file ############################# #
104
+
data/lib/recls/util.rb ADDED
@@ -0,0 +1,73 @@
1
+ # ######################################################################### #
2
+ # File: recls/util.rb
3
+ #
4
+ # Purpose: Utility module functions for recls library
5
+ #
6
+ # Created: 17th February 2014
7
+ # Updated: 27th August 2015
8
+ #
9
+ # Author: Matthew Wilson
10
+ #
11
+ # Copyright (c) 2012-2015, Matthew Wilson and Synesis Software
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are met:
16
+ #
17
+ # * Redistributions of source code must retain the above copyright notice,
18
+ # this list of conditions and the following disclaimer.
19
+ #
20
+ # * Redistributions in binary form must reproduce the above copyright notice,
21
+ # this list of conditions and the following disclaimer in the documentation
22
+ # and/or other materials provided with the distribution.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # ######################################################################### #
37
+
38
+
39
+ require 'recls/ximpl/util'
40
+ require 'recls/ximpl/os'
41
+
42
+ module Recls
43
+
44
+ # Obtains the absolute form of the given path
45
+ def self.absolute_path(path)
46
+
47
+ return Recls::Ximpl.absolute_path path
48
+ end
49
+
50
+ # Canonicalises the given path, by removing dots ('.' and '..')
51
+ # directories
52
+ def self.canonicalise_path(path)
53
+
54
+ return Recls::Ximpl.canonicalise_path path
55
+ end
56
+
57
+ # Derives a given path relative to an origin, unless the path is
58
+ # absolute
59
+ def self.derive_relative_path(origin, path)
60
+
61
+ return Recls::Ximpl.derive_relative_path origin, path
62
+ end
63
+
64
+ # Combines paths, optionally canonicalising them
65
+ #
66
+ def self.combine_paths(origin, path, options={})
67
+
68
+ return Recls::Ximpl.combine_paths origin, path, options
69
+ end
70
+ end
71
+
72
+ # ############################## end of file ############################# #
73
+
@@ -0,0 +1,53 @@
1
+ # ######################################################################### #
2
+ # File: recls/version.rb
3
+ #
4
+ # Purpose: Version for recls library
5
+ #
6
+ # Created: 14th February 2014
7
+ # Updated: 31st May 2016
8
+ #
9
+ # Author: Matthew Wilson
10
+ #
11
+ # Copyright (c) 2012-2016, Matthew Wilson and Synesis Software
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are met:
16
+ #
17
+ # * Redistributions of source code must retain the above copyright notice,
18
+ # this list of conditions and the following disclaimer.
19
+ #
20
+ # * Redistributions in binary form must reproduce the above copyright notice,
21
+ # this list of conditions and the following disclaimer in the documentation
22
+ # and/or other materials provided with the distribution.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # ######################################################################### #
37
+
38
+
39
+ module Recls
40
+
41
+ # Current version of the recls.Ruby library
42
+ VERSION = '2.6.4'
43
+
44
+ private
45
+ VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
46
+ public
47
+ VERSION_MAJOR = VERSION_PARTS_[0] # :nodoc:
48
+ VERSION_MINOR = VERSION_PARTS_[1] # :nodoc:
49
+ VERSION_REVISION = VERSION_PARTS_[2] # :nodoc:
50
+ end
51
+
52
+ # ############################## end of file ############################# #
53
+
@@ -0,0 +1,85 @@
1
+ # ######################################################################### #
2
+ # File: recls/ximpl/os.rb
3
+ #
4
+ # Purpose: Operating system internal implementation constructs for the
5
+ # recls library.
6
+ #
7
+ # Created: 16th February 2014
8
+ # Updated: 27th August 2015
9
+ #
10
+ # Author: Matthew Wilson
11
+ #
12
+ # Copyright (c) 2012-2015, Matthew Wilson and Synesis Software
13
+ # All rights reserved.
14
+ #
15
+ # Redistribution and use in source and binary forms, with or without
16
+ # modification, are permitted provided that the following conditions are met:
17
+ #
18
+ # * Redistributions of source code must retain the above copyright notice,
19
+ # this list of conditions and the following disclaimer.
20
+ #
21
+ # * Redistributions in binary form must reproduce the above copyright notice,
22
+ # this list of conditions and the following disclaimer in the documentation
23
+ # and/or other materials provided with the distribution.
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
+ # POSSIBILITY OF SUCH DAMAGE.
36
+ #
37
+ # ######################################################################### #
38
+
39
+
40
+ module Recls
41
+
42
+ module Ximpl
43
+
44
+ module OS
45
+
46
+ OS_IS_WINDOWS = (RUBY_PLATFORM =~ /(mswin|mingw|bccwin|wince)/i) ? true : false
47
+
48
+ PATH_NAME_SEPARATOR = OS_IS_WINDOWS ? '\\' : '/'
49
+
50
+ PATH_SEPARATOR = OS_IS_WINDOWS ? ';' : ':'
51
+
52
+ WILDCARDS_ALL = OS_IS_WINDOWS ? '*' : '*'
53
+
54
+ def OS.get_number_of_dots_dir_(p)
55
+
56
+ if p
57
+ if ?. == p[0]
58
+ return 1 if 1 == p.size
59
+ return 1 if ?/ == p[1]
60
+ return 1 if OS_IS_WINDOWS and ?\\ == p[1]
61
+ if ?. == p[1]
62
+ return 2 if 2 == p.size
63
+ return 2 if ?/ == p[2]
64
+ return 2 if OS_IS_WINDOWS and ?\\ == p[2]
65
+ end
66
+ end
67
+ end
68
+
69
+ return 0
70
+ end
71
+
72
+ def OS.is_root_dir_(p)
73
+
74
+ return nil if not p
75
+ return true if '/' == p
76
+ return true if OS_IS_WINDOWS and '\\' == p
77
+
78
+ return false
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # ############################## end of file ############################# #
85
+
@@ -0,0 +1,81 @@
1
+ # ######################################################################### #
2
+ # File: recls/ximpl/unix.rb
3
+ #
4
+ # Purpose: UNIX-specific constructs for the recls library.
5
+ #
6
+ # Created: 19th February 2014
7
+ # Updated: 27th August 2015
8
+ #
9
+ # Author: Matthew Wilson
10
+ #
11
+ # Copyright (c) 2012-2015, Matthew Wilson and Synesis Software
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are met:
16
+ #
17
+ # * Redistributions of source code must retain the above copyright notice,
18
+ # this list of conditions and the following disclaimer.
19
+ #
20
+ # * Redistributions in binary form must reproduce the above copyright notice,
21
+ # this list of conditions and the following disclaimer in the documentation
22
+ # and/or other materials provided with the distribution.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # ######################################################################### #
37
+
38
+
39
+ require 'recls/ximpl/util'
40
+
41
+ module Recls
42
+
43
+ module Ximpl
44
+
45
+ class FileStat < File::Stat
46
+
47
+ private
48
+ def initialize(path)
49
+
50
+ @path = path
51
+
52
+ super(path)
53
+ end
54
+
55
+ public
56
+ attr_reader :path
57
+
58
+ def hidden?
59
+
60
+ basename = File.basename @path
61
+
62
+ return false if basename.empty?
63
+ return false if '.' == basename
64
+ return false if '..' == basename
65
+ return false if ?. != basename[0]
66
+
67
+ return true
68
+ end
69
+
70
+ public
71
+ def FileStat.stat(path)
72
+
73
+ Recls::Ximpl::FileStat.new(path)
74
+
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # ############################## end of file ############################# #
81
+
@@ -0,0 +1,650 @@
1
+ # ######################################################################### #
2
+ # File: recls/ximpl/util.rb
3
+ #
4
+ # Purpose: Internal implementation constructs for the recls library.
5
+ #
6
+ # Created: 24th July 2012
7
+ # Updated: 20th December 2015
8
+ #
9
+ # Author: Matthew Wilson
10
+ #
11
+ # Copyright (c) 2012-2015, Matthew Wilson and Synesis Software
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are met:
16
+ #
17
+ # * Redistributions of source code must retain the above copyright notice,
18
+ # this list of conditions and the following disclaimer.
19
+ #
20
+ # * Redistributions in binary form must reproduce the above copyright notice,
21
+ # this list of conditions and the following disclaimer in the documentation
22
+ # and/or other materials provided with the distribution.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # ######################################################################### #
37
+
38
+
39
+ require 'recls/ximpl/os'
40
+ require 'recls/flags'
41
+
42
+ require 'pathname'
43
+
44
+ module Recls
45
+
46
+ module Ximpl
47
+
48
+ module Util
49
+
50
+ def self.is_path_name_separator(c)
51
+
52
+ return true if ?/ == c
53
+
54
+ if Recls::Ximpl::OS::OS_IS_WINDOWS
55
+ return true if ?\\ == c
56
+ end
57
+
58
+ return false
59
+ end
60
+
61
+ # Indicates whether a trailing slash is on the given path
62
+ #
63
+ # dependencies: none
64
+ def self.has_trailing_slash(p)
65
+
66
+ return p if p.nil? or p.empty?
67
+
68
+ return self.is_path_name_separator(p[-1])
69
+ end
70
+
71
+ # returns the trailing slash, or nil if none present
72
+ #
73
+ # dependencies: none
74
+ def self.get_trailing_slash(p, args = {})
75
+
76
+ return nil if p.nil?
77
+ return nil if p.empty?
78
+
79
+ return self.is_path_name_separator(p[-1]) ? p[-1] : nil
80
+ end
81
+
82
+ # appends trailing slash to a path if not already
83
+ # present
84
+ #
85
+ # dependencies: none
86
+ def self.append_trailing_slash(p, slash = nil)
87
+
88
+ return p if not p or p.empty?
89
+
90
+ return p if self.is_path_name_separator(p[-1])
91
+
92
+ slash = '/' if not slash
93
+
94
+ "#{p}#{slash}"
95
+ end
96
+
97
+ # trims trailing slash from a path, unless it is the
98
+ # root
99
+ #
100
+ # dependencies: none
101
+ def self.trim_trailing_slash(p)
102
+
103
+ return p if not p or p.empty?
104
+
105
+ p = p[0 ... -1] if self.is_path_name_separator(p[-1])
106
+
107
+ return p
108
+ end
109
+
110
+ # From path p, returns a tuple containing either:
111
+ #
112
+ # [ nil, p ] if p does not contain a Windows root, or
113
+ #
114
+ # [ wroot, remainder ] if p does contain a Windows root, or
115
+ #
116
+ # [ nil, nil ] if p is nil
117
+ #
118
+ # dependencies: none
119
+ def self.get_windows_root(p)
120
+
121
+ return [ nil, nil ] if not p
122
+
123
+ if Recls::Ximpl::OS::OS_IS_WINDOWS
124
+
125
+ # Windows local drive (e.g. 'H:')
126
+ #
127
+ # NOTE: this works for both rooted and unrooted paths
128
+ if p =~ /^([a-zA-Z]:)/
129
+ return [ $1, $' ]
130
+ end
131
+
132
+ # UNC network drive
133
+ #
134
+ # NOTE: there are several permutations ...
135
+ if p =~ /^(\\\\[^\\\/:*?<>|]+\\[^\\\/:*?<>|]+)([\\\/].*)$/
136
+ # \\server\share{\{... rest of path}}
137
+ return [ $1, $2 ]
138
+ end
139
+ if p =~ /^(\\\\[^\\\/:*?<>|]+\\[^\\\/:*?<>|]+)$/
140
+ # \\server\share
141
+ return [ $1, nil ]
142
+ end
143
+ if p =~ /^(\\\\[^\\\/:*?<>|]+\\)$/
144
+ # \\server\
145
+ return [ $1, nil ]
146
+ end
147
+ if p =~ /^(\\\\[^\\\/:*?<>|]+)$/
148
+ # \\server
149
+ return [ $1, nil ]
150
+ end
151
+ end
152
+
153
+ return [ nil, p ]
154
+ end
155
+
156
+ # obtains the parts from a path, including any Windows root and
157
+ # the file basename
158
+ def self.path_parts(path)
159
+
160
+ return nil if path.nil?
161
+ return [] if path.empty?
162
+
163
+ parts = []
164
+
165
+ wr, rem = self.get_windows_root(path)
166
+
167
+ parts << wr if wr
168
+
169
+ until rem.nil? || rem.empty?
170
+ if rem =~ /^([^\\\/]*[\\\/])/
171
+ parts << $1
172
+ rem = $'
173
+ else
174
+ parts << rem
175
+ rem = ''
176
+ end
177
+ end
178
+
179
+ parts
180
+ end
181
+
182
+ # Returns a tuple consisting of the following
183
+ # elements (or nil, for any element that is not)
184
+ # present
185
+ #
186
+ # f1. Windows root, or nil
187
+ # f2. directory, or nil
188
+ # f3. basename, or nil
189
+ # f4. basename-minus-extension, or nil
190
+ # f5. extension, or nil
191
+ # f6. array of directory path parts, which may be empty (not nil)
192
+ # f7. array of all path parts, which may be empty (not nil)
193
+ #
194
+ # dependencies: Util.path_parts, Util.get_windows_root
195
+ def self.split_path(p)
196
+
197
+ f1_windows_root, remainder = self.get_windows_root p
198
+ f1_windows_root = nil if not f1_windows_root or f1_windows_root.empty?
199
+ remainder = nil if not remainder or remainder.empty?
200
+
201
+ if not remainder or remainder.empty?
202
+ f2_directory = nil
203
+ f3_basename = nil
204
+ f4_nameonly = nil
205
+ f5_extension = nil
206
+ else
207
+ if remainder =~ /^(.*[\\\/])([^\\\/]*)$/
208
+ f2_directory = $1
209
+ f3_basename = $2
210
+ else
211
+ f2_directory = nil
212
+ f3_basename = remainder
213
+ f4_nameonly = nil
214
+ f5_extension = nil
215
+ end
216
+
217
+ f2_directory = nil if not f2_directory or f2_directory.empty?
218
+ f3_basename = nil if not f3_basename or f3_basename.empty?
219
+
220
+ if f3_basename
221
+ # special case: treat '.' and '..' as file-name only
222
+ if '.' == f3_basename or '..' == f3_basename
223
+ f4_nameonly = f3_basename
224
+ f5_extension = nil
225
+ elsif f3_basename =~ /^(.*)(\.[^.]*)$/
226
+ f4_nameonly = $1
227
+ f5_extension = $2
228
+ else
229
+ f4_nameonly = f3_basename
230
+ f5_extension = nil
231
+ end
232
+ else
233
+ f4_nameonly = nil
234
+ f5_extension = nil
235
+ end
236
+ end
237
+
238
+ f4_nameonly = nil if not f4_nameonly or f4_nameonly.empty?
239
+ f5_extension = nil if not f5_extension or f5_extension.empty?
240
+ f6_directory_parts = self.path_parts(f2_directory)
241
+ f7_path_parts = self.path_parts(p)
242
+
243
+ return [ f1_windows_root, f2_directory, f3_basename, f4_nameonly, f5_extension, f6_directory_parts, f7_path_parts ]
244
+ end
245
+
246
+ # Returns a tuple consisting of:
247
+ #
248
+ # f1. The canonicalised array of parts
249
+ # f2. A boolean indicating whether to 'consume' the basename
250
+ #
251
+ # dependencies: OS.is_root_dir_, OS.get_number_of_dots_dir_,
252
+ def self.canonicalise_parts(parts, basename = nil)
253
+
254
+ newParts = []
255
+
256
+ trailing_slash = parts.empty? ? nil : self.get_trailing_slash(parts[-1])
257
+
258
+ lastSingleDots = nil
259
+
260
+ path_is_rooted = nil
261
+
262
+ index = -1
263
+ parts.each do |part|
264
+
265
+ index += 1
266
+
267
+ next if not part
268
+ next if part.empty?
269
+
270
+ if path_is_rooted.nil?
271
+ path_is_rooted = self.is_path_name_separator(part[0])
272
+ end
273
+
274
+ if ?. == part[0]
275
+ if self.is_path_name_separator(part[1])
276
+ # single dots, so ...
277
+
278
+ # ... remember the last instance, and ...
279
+ lastSingleDots = part
280
+
281
+ # ... skip to leave this out of the result
282
+ next
283
+ elsif ?. == part[1]
284
+ if self.is_path_name_separator(part[2])
285
+ # double dots, so ...
286
+ # ... skip this and pop prior from the new list iff:
287
+ #
288
+ # 1. there is a prior elements in the new list (size > 1); AND
289
+ # 2. the last element in the new list is not the root directory; AND
290
+ # 3. the last element in the list is not a dots directory
291
+ if not newParts.empty? # 1.
292
+ priorPart = newParts[-1]
293
+ if 1 == newParts.size and OS.is_root_dir_(priorPart)
294
+ # 2.
295
+ next
296
+ else
297
+ dirtype = OS.get_number_of_dots_dir_(priorPart)
298
+ if 0 == dirtype # 3.
299
+ if newParts.pop
300
+ next
301
+ end
302
+ end
303
+ end
304
+ end
305
+ else
306
+ # it's a ..X part
307
+ end
308
+ else
309
+ # it's a .X part
310
+ end
311
+ else
312
+ # it's a non-dots part
313
+ end
314
+
315
+ newParts << part
316
+ end
317
+
318
+ consume_basename = false
319
+
320
+ if basename
321
+ if ?. == basename[0]
322
+ if 1 == basename.size
323
+ # single dots
324
+ if newParts.empty?
325
+ lastSingleDots = false
326
+ else
327
+ consume_basename = true
328
+ end
329
+ elsif ?. == basename[1] and 2 == basename.size
330
+ # double dots, so ...
331
+ #
332
+ # ... pop unless we already have some outstanding double dots
333
+ if newParts.empty?
334
+ newParts << '..'
335
+ consume_basename = true
336
+ elsif 1 == newParts.size && 1 == newParts[0].size && Util.is_path_name_separator(newParts[0][0])
337
+ consume_basename = true
338
+ else
339
+ if 2 != OS.get_number_of_dots_dir_(newParts[-1])
340
+ newParts.pop
341
+ consume_basename = true
342
+ end
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ # push lastSingleDots (which may contain a trailing slash) if
349
+ # exists and newParts is empty
350
+ newParts << lastSingleDots if lastSingleDots and newParts.empty?
351
+
352
+ if not newParts.empty?
353
+ if 2 == OS.get_number_of_dots_dir_(newParts[-1])
354
+ # the last element is the double-dots directory, but
355
+ # need to determine whether to ensure/remote a
356
+ # trailing slash
357
+ if basename and not basename.empty?
358
+ if not consume_basename
359
+ # leave as is
360
+ else
361
+ #
362
+ newParts[-1] = '..'
363
+ end
364
+ end
365
+ end
366
+ else
367
+ # handle case where all (double)-dots have eliminated
368
+ # all regular directories
369
+ if not basename or basename.empty? or consume_basename
370
+ newParts << '.'
371
+ end
372
+ end
373
+
374
+ [ newParts.join(''), consume_basename ]
375
+ end
376
+ end
377
+
378
+ # Canonicalises a path
379
+ #
380
+ # Note: contains a trailing slash if, in the context of the given
381
+ # path, the last element of the canonicalised path is a directory
382
+ # unequivocally
383
+ def self.canonicalise_path(path)
384
+
385
+ return nil if not path
386
+ return '' if path.empty?
387
+
388
+ f1_windows_root, f2_directory, f3_basename, dummy1, dummy2, directory_parts, dummy3 = Util.split_path(path)
389
+
390
+ if not f2_directory
391
+ canonicalised_directory = nil
392
+ else
393
+ canonicalised_directory, consume_basename = Util.canonicalise_parts(directory_parts, f3_basename)
394
+ f3_basename = nil if consume_basename
395
+ end
396
+
397
+ return "#{f1_windows_root}#{canonicalised_directory}#{f3_basename}"
398
+ end
399
+
400
+ # determines the absolute path of a given path
401
+ def self.absolute_path(path, refdir = nil)
402
+
403
+ case path
404
+ when ::NilClass
405
+ return nil
406
+ when ::String
407
+ when ::Recls::Entry
408
+ path = path.to_s
409
+ else
410
+ raise TypeError, "parameter path ('#{path}') is of type #{path.class} must be an instance of #{::String} or #{::Recls::Entry}"
411
+ end
412
+
413
+ return '' if path.empty?
414
+
415
+ f1_windows_root, f2_directory, f3_basename, dummy1, dummy2, dummy3, dummy4 = Util.split_path(path)
416
+
417
+ if f2_directory =~ /^[\\\/]/
418
+ return path
419
+ end
420
+
421
+ cwd = refdir ? refdir : Dir.getwd
422
+
423
+ trailing_slash = Util.get_trailing_slash(path)
424
+
425
+ if '.' == path
426
+ return Util.trim_trailing_slash cwd
427
+ elsif 2 == path.size and trailing_slash
428
+ return Util.append_trailing_slash(cwd, path[1..1])
429
+ end
430
+
431
+ cwd = Util.append_trailing_slash(cwd)
432
+
433
+ path = "#{cwd}#{path}"
434
+
435
+ path = canonicalise_path path
436
+
437
+ if trailing_slash
438
+ path = Util.append_trailing_slash path, trailing_slash
439
+ else
440
+ path = Util.trim_trailing_slash path
441
+ end
442
+
443
+ path
444
+ end
445
+
446
+ # obtains the basename of a path, e.g.
447
+ # the basename of
448
+ # abc/def/ghi.jkl
449
+ # or (on Windows)
450
+ # C:\abc\def\ghi.jkl
451
+ # is
452
+ # ghi.jkl
453
+ #
454
+ def self.basename(path)
455
+
456
+ return nil if not path
457
+
458
+ # NOTE: we don't implement in terms of split_path
459
+ # because all but the UNC case work with just
460
+ # detecting the last (back)slash
461
+
462
+ if Recls::Ximpl::OS::OS_IS_WINDOWS
463
+ wr, rem = Util.get_windows_root(path)
464
+
465
+ if not rem
466
+ return ''
467
+ else
468
+ path = rem
469
+ end
470
+ end
471
+
472
+ if not path.is_a? String
473
+ path = path.to_s
474
+ end
475
+
476
+ if path =~ /^.*[\/\\](.*)/
477
+ $1
478
+ else
479
+ path
480
+ end
481
+ end
482
+
483
+ # obtains the file extension of a basename, e.g.
484
+ # the file_ext of
485
+ # ghi.jkl
486
+ # is
487
+ # .jkl
488
+ def self.file_ext(path)
489
+
490
+ return nil if not path
491
+
492
+ use_split_path = false
493
+
494
+ if Recls::Ximpl::OS::OS_IS_WINDOWS
495
+ if path.include? ?\\
496
+ use_split_path = true
497
+ end
498
+ end
499
+
500
+ if path.include? ?/
501
+ use_split_path = true
502
+ end
503
+
504
+ if use_split_path
505
+ ext = Util.split_path(path)[4]
506
+ else
507
+ if path =~ /^.*(\.[^.]*)$/
508
+ ext = $1
509
+ else
510
+ ext = nil
511
+ end
512
+ end
513
+
514
+ return ext ? ext : ''
515
+ end
516
+
517
+ # obtains the directory from the directory path
518
+ #
519
+ def self.directory_from_directory_path(directory_path)
520
+
521
+ wr, rem = Util.get_windows_root(directory_path)
522
+
523
+ rem
524
+ end
525
+
526
+ # obtains the directory parts from a directory
527
+ def self.directory_parts_from_directory(directory)
528
+
529
+ return nil if not directory
530
+
531
+ directory_parts = []
532
+
533
+ until directory.empty?
534
+ if directory =~ /^([^\\\/]*[\\\/])/
535
+ directory_parts << $1
536
+ directory = $'
537
+ else
538
+ directory_parts << directory
539
+ directory = ''
540
+ end
541
+ end
542
+
543
+ directory_parts
544
+ end
545
+
546
+ # obtains the relative path of a given path and
547
+ # a reference directory
548
+ def self.derive_relative_path(origin, path)
549
+
550
+ return nil if path.nil?
551
+ return nil if path.empty?
552
+ return path if origin.nil?
553
+ return path if origin.empty?
554
+
555
+ path = self.canonicalise_path path
556
+ origin = self.canonicalise_path origin
557
+
558
+ path_splits = Util.split_path(path)
559
+ origin_splits = Util.split_path(origin)
560
+
561
+ # if different windows root, then cannot provide relative
562
+ if path_splits[0] and origin_splits[0]
563
+ return path if path_splits[0] != origin_splits[0]
564
+ end
565
+
566
+ trailing_slash = Util.get_trailing_slash(path)
567
+
568
+ path_parts = path_splits[6]
569
+ origin_parts = origin_splits[6]
570
+
571
+ while true
572
+
573
+ break if path_parts.empty?
574
+ break if origin_parts.empty?
575
+
576
+ path_part = path_parts[0]
577
+ origin_part = origin_parts[0]
578
+
579
+ if 1 == path_parts.size || 1 == origin_parts.size
580
+ path_part = Util.append_trailing_slash(path_part)
581
+ origin_part = Util.append_trailing_slash(origin_part)
582
+ end
583
+
584
+ if path_part == origin_part
585
+ path_parts.shift
586
+ origin_parts.shift
587
+ else
588
+ break
589
+ end
590
+ end
591
+
592
+ return ".#{trailing_slash}" if path_parts.empty? and origin_parts.empty?
593
+
594
+ # at this point, all reference parts should be converted into '..'
595
+
596
+ return origin_parts.map { |rp| '..' }.join('/') if path_parts.empty?
597
+
598
+ return '../' * origin_parts.size + path_parts.join('')
599
+ end
600
+
601
+
602
+ def self.combine_paths(origin, path, options)
603
+
604
+ f1_windows_root, f2_directory, f3_basename, dummy1, dummy2, dummy3, dummy4 = Util.split_path(path)
605
+
606
+ # return path if f1_windows_root
607
+ return path if f2_directory && Util.is_path_name_separator(f2_directory[0])
608
+
609
+ r = File.join origin, path
610
+
611
+ if options[:clean_path]
612
+
613
+ r = Pathname.new(r).cleanpath
614
+ end
615
+
616
+ r
617
+ end
618
+
619
+
620
+ # Elicits the contents of the given directory, or, if the flag
621
+ # STOP_ON_ACCESS_FAILURE is specified throws an exception if the
622
+ # directory does not exist
623
+ #
624
+ # Some known conditions:
625
+ #
626
+ # * (Mac OSX) /dev/fd/<N> - some of these stat() as directories but
627
+ # Dir.new fails with ENOTDIR
628
+ #
629
+ def self.dir_entries_maybe(dir, flags)
630
+
631
+ begin
632
+
633
+ Dir.new(dir).to_a
634
+
635
+ rescue SystemCallError => x
636
+
637
+ # TODO this should be filtered up and/or logged
638
+
639
+ if(0 != (STOP_ON_ACCESS_FAILURE & flags))
640
+ raise
641
+ end
642
+
643
+ return []
644
+ end
645
+ end
646
+ end
647
+ end
648
+
649
+ # ############################## end of file ############################# #
650
+