recls-ruby 2.6.4

Sign up to get free protection for your applications and to get access to all the features.
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
+