libpath-ruby 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +61 -0
  3. data/examples/path_from_arg0.md +99 -0
  4. data/examples/path_from_arg0.rb +55 -0
  5. data/lib/libpath.rb +12 -0
  6. data/lib/libpath/constants.rb +42 -0
  7. data/lib/libpath/constants/unix.rb +146 -0
  8. data/lib/libpath/constants/windows.rb +147 -0
  9. data/lib/libpath/diagnostics.rb +6 -0
  10. data/lib/libpath/diagnostics/parameter_checking.rb +46 -0
  11. data/lib/libpath/exceptions.rb +7 -0
  12. data/lib/libpath/exceptions/libpath_base_exception.rb +77 -0
  13. data/lib/libpath/exceptions/malformed_name_exception.rb +85 -0
  14. data/lib/libpath/form.rb +42 -0
  15. data/lib/libpath/form/unix.rb +215 -0
  16. data/lib/libpath/form/windows.rb +358 -0
  17. data/lib/libpath/internal_/array.rb +96 -0
  18. data/lib/libpath/internal_/platform.rb +53 -0
  19. data/lib/libpath/internal_/string.rb +33 -0
  20. data/lib/libpath/internal_/unix/form.rb +160 -0
  21. data/lib/libpath/internal_/windows/drive.rb +84 -0
  22. data/lib/libpath/internal_/windows/form.rb +281 -0
  23. data/lib/libpath/libpath.rb +6 -0
  24. data/lib/libpath/path.rb +43 -0
  25. data/lib/libpath/path/unix.rb +170 -0
  26. data/lib/libpath/path/windows.rb +176 -0
  27. data/lib/libpath/util.rb +42 -0
  28. data/lib/libpath/util/unix.rb +414 -0
  29. data/lib/libpath/util/windows.rb +636 -0
  30. data/lib/libpath/version.rb +73 -0
  31. data/test/performance/benchmark_drive_letter.rb +31 -0
  32. data/test/performance/benchmark_gsub_string_or_regex.rb +45 -0
  33. data/test/performance/benchmark_rindex2.rb +109 -0
  34. data/test/performance/benchmark_split.rb +32 -0
  35. data/test/unit/compare/ts_all.rb +22 -0
  36. data/test/unit/equate/ts_all.rb +22 -0
  37. data/test/unit/equate/unix/ts_all.rb +22 -0
  38. data/test/unit/equate/windows/ts_all.rb +22 -0
  39. data/test/unit/exceptions/tc_libpath_base_exception.rb +27 -0
  40. data/test/unit/exceptions/tc_malformed_name_exception.rb +31 -0
  41. data/test/unit/exceptions/ts_all.rb +22 -0
  42. data/test/unit/form/tc_absolute_functions.rb +369 -0
  43. data/test/unit/form/ts_all.rb +22 -0
  44. data/test/unit/form/unix/tc_absolute_functions.rb +269 -0
  45. data/test/unit/form/unix/ts_all.rb +22 -0
  46. data/test/unit/form/windows/tc_absolute_functions.rb +854 -0
  47. data/test/unit/form/windows/ts_all.rb +22 -0
  48. data/test/unit/internal_/tc_array.rb +62 -0
  49. data/test/unit/internal_/ts_all.rb +22 -0
  50. data/test/unit/internal_/unix/form/tc_slash_functions.rb +60 -0
  51. data/test/unit/internal_/unix/form/ts_all.rb +22 -0
  52. data/test/unit/internal_/unix/tc_split_path.rb +396 -0
  53. data/test/unit/internal_/unix/ts_all.rb +22 -0
  54. data/test/unit/internal_/windows/form/tc_get_windows_volume.rb +220 -0
  55. data/test/unit/internal_/windows/form/tc_slash_functions.rb +61 -0
  56. data/test/unit/internal_/windows/form/ts_all.rb +22 -0
  57. data/test/unit/internal_/windows/tc_split_path.rb +881 -0
  58. data/test/unit/internal_/windows/ts_all.rb +22 -0
  59. data/test/unit/parse/ts_all.rb +22 -0
  60. data/test/unit/path/tc_path.rb +778 -0
  61. data/test/unit/path/ts_all.rb +22 -0
  62. data/test/unit/path/unix/tc_path.rb +565 -0
  63. data/test/unit/path/unix/ts_all.rb +22 -0
  64. data/test/unit/path/windows/tc_path.rb +630 -0
  65. data/test/unit/path/windows/ts_all.rb +22 -0
  66. data/test/unit/tc_version.rb +47 -0
  67. data/test/unit/ts_all.rb +22 -0
  68. data/test/unit/util/tc_combine_paths.rb +179 -0
  69. data/test/unit/util/tc_derive_relative_path.rb +19 -0
  70. data/test/unit/util/tc_make_path_canonical.rb +228 -0
  71. data/test/unit/util/ts_all.rb +22 -0
  72. data/test/unit/util/unix/tc_combine_paths.rb +65 -0
  73. data/test/unit/util/unix/tc_derive_relative_path.rb +123 -0
  74. data/test/unit/util/unix/tc_make_path_absolute.rb +117 -0
  75. data/test/unit/util/unix/tc_make_path_canonical.rb +139 -0
  76. data/test/unit/util/unix/ts_all.rb +22 -0
  77. data/test/unit/util/windows/tc_combine_paths.rb +131 -0
  78. data/test/unit/util/windows/tc_derive_relative_path.rb +155 -0
  79. data/test/unit/util/windows/tc_make_path_absolute.rb +163 -0
  80. data/test/unit/util/windows/tc_make_path_canonical.rb +220 -0
  81. data/test/unit/util/windows/ts_all.rb +22 -0
  82. metadata +144 -0
@@ -0,0 +1,6 @@
1
+
2
+ # Top-level module for the *libpath.Ruby*
3
+ module LibPath
4
+
5
+ end # module LibPath
6
+
@@ -0,0 +1,43 @@
1
+
2
+ require 'libpath/internal_/platform'
3
+
4
+ if ::LibPath::Internal_::Platform::Constants::PLATFORM_IS_WINDOWS then
5
+
6
+ require 'libpath/path/windows'
7
+ else
8
+
9
+ require 'libpath/path/unix'
10
+ end
11
+
12
+ module LibPath # :nodoc:
13
+ module Path # :nodoc:
14
+
15
+ if ::LibPath::Internal_::Platform::Constants::PLATFORM_IS_WINDOWS then
16
+
17
+ extend ::LibPath::Path::Windows
18
+ include ::LibPath::Path::Windows
19
+ else
20
+
21
+ extend ::LibPath::Path::Unix
22
+ include ::LibPath::Path::Unix
23
+ end
24
+
25
+ # @!visibility private
26
+ def self.extended receiver # :nodoc:
27
+
28
+ $stderr.puts "#{receiver} extended by #{self}" if $DEBUG
29
+ end
30
+
31
+ # @!visibility private
32
+ def self.included receiver # :nodoc:
33
+
34
+ $stderr.puts "#{receiver} included #{self}" if $DEBUG
35
+ end
36
+
37
+ end # module Path
38
+ end # module LibPath
39
+
40
+ # ############################## end of file ############################# #
41
+
42
+
43
+
@@ -0,0 +1,170 @@
1
+
2
+ # ######################################################################## #
3
+ # File: libpath/path/unix.rb
4
+ #
5
+ # Purpose: LibPath::Path::Unix module
6
+ #
7
+ # Created: 21st January 2019
8
+ # Updated: 16th April 2019
9
+ #
10
+ # Home: http://github.com/synesissoftware/libpath.Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2019, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+
48
+ =begin
49
+ =end
50
+
51
+ require 'libpath/diagnostics'
52
+ require 'libpath/internal_/unix/form'
53
+ require 'libpath/util/unix'
54
+
55
+ module LibPath # :nodoc:
56
+ module Path # :nodoc:
57
+ module Unix # :nodoc:
58
+
59
+ # Class representing a parsed path (for UNIX)
60
+ class ParsedPath
61
+
62
+ # @!visibility private
63
+ module ParsedPath_Constants # :nodoc: all
64
+
65
+ INIT_VALID_OPTIONS = %i{ home locator pwd }
66
+ INIT_MPA_COMMON_OPTIONS = %i{ home locator pwd }
67
+ INIT_DRP_COMMON_OPTIONS = %i{ home locator pwd }
68
+ end
69
+
70
+ # Initialises an instance from the given +path+, optional
71
+ # +search_directory+ and options
72
+ #
73
+ # === Signature
74
+ #
75
+ # * *Parameters:*
76
+ # - +path+ (String) The path. May not be +nil+
77
+ # - +search_directory+ (String) The search_directory, from which the relative attributes are calculated for the path. May be +nil+
78
+ # - +options+ (Hash) Options
79
+ #
80
+ # * *Options:*
81
+ # - +????+
82
+ #
83
+ # * *Exceptions:*
84
+ # - +ArgumentError+ Raised if +path+ is +nil+
85
+ def initialize path, search_directory = nil, **options
86
+
87
+ raise ::ArgumentError, "path may not be nil or empty" if path.nil? || path.empty?
88
+
89
+ _Diagnostics = ::LibPath::Diagnostics
90
+ _Internal_Form = ::LibPath::Internal_::Unix::Form
91
+ _Util = ::LibPath::Util::Unix
92
+ _C = ::LibPath::Path::Unix::ParsedPath::ParsedPath_Constants
93
+
94
+ _Diagnostics.check_options(options, known: _C::INIT_VALID_OPTIONS)
95
+
96
+
97
+ abs_path = _Util.make_path_absolute(path, make_canonical: true, **options.select { |k| _C::INIT_MPA_COMMON_OPTIONS.include?(k) })
98
+
99
+ _, _, f2_dir, f3_basename, f4_stem, f5_ext, f6_dir_parts, _ = _Internal_Form.split_path(abs_path)
100
+
101
+ @given_path = path
102
+ @absolute_path = abs_path
103
+ @compare_path = _Util.make_compare_path abs_path
104
+ @directory = f2_dir
105
+ @directory_path = f2_dir
106
+ @directory_parts = f6_dir_parts
107
+
108
+ @file_full_name = f3_basename
109
+ @file_name_only = f4_stem
110
+ @file_extension = f5_ext
111
+
112
+ if search_directory
113
+
114
+ drp_options = options.select { |k| _C::INIT_DRP_COMMON_OPTIONS.include?(k) }
115
+
116
+ search_directory = _Util.make_path_absolute(search_directory, make_canonical: true, **options.select { |k| _C::INIT_MPA_COMMON_OPTIONS.include?(k) })
117
+ search_directory = _Internal_Form.append_trailing_slash search_directory
118
+
119
+ @search_directory = search_directory
120
+ @search_relative_path = _Util.derive_relative_path(search_directory, abs_path, **drp_options)
121
+ @search_relative_path = _Internal_Form.append_trailing_slash(@search_relative_path) if _Internal_Form.char_is_path_name_separator?(abs_path[-1])
122
+ @search_relative_directory_path = _Internal_Form.append_trailing_slash _Util.derive_relative_path(search_directory, f2_dir, **drp_options)
123
+ @search_relative_directory_parts = @search_relative_directory_path.split('/').map { |v| v + '/' }
124
+ end
125
+ end
126
+
127
+ # (String) The path given to initialise the instance
128
+ attr_reader :given_path
129
+ # (String) The full-path of the instance
130
+ attr_reader :absolute_path
131
+ # (String) A normalised form of #path that can be used in comparisons
132
+ attr_reader :compare_path
133
+ # (String) The entry's directory (excluding the #drive if on Windows)
134
+ attr_reader :directory
135
+ # (String) The full path of the entry's directory
136
+ attr_reader :directory_path
137
+ alias_method :dirname, :directory_path
138
+ # ([String]) An array of directory parts, where each part ends in the path name separator
139
+ attr_reader :directory_parts
140
+ # (String) The entry's file name (combination of #stem + #extension)
141
+ attr_reader :file_full_name
142
+ alias_method :basename, :file_full_name
143
+ # (String) The entry's file stem
144
+ attr_reader :file_name_only
145
+ alias_method :stem, :file_name_only
146
+ # (String) The entry's file extension
147
+ attr_reader :file_extension
148
+ alias_method :extension, :file_extension
149
+ # (String) The search directory if specified; +nil+ otherwise
150
+ attr_reader :search_directory
151
+ # (String) The #path relative to #search_directory; +nil+ if no search directory specified
152
+ attr_reader :search_relative_path
153
+ # (String) The #directory_path relative to #search_directory; +nil+ if no search directory specified
154
+ attr_reader :search_relative_directory_path
155
+ # ([String]) The #directory_parts relative to #search_directory; +nil+ if no search directory specified
156
+ attr_reader :search_relative_directory_parts
157
+
158
+ # (String) String form of path
159
+ def to_s
160
+
161
+ absolute_path
162
+ end
163
+ end
164
+
165
+ end # module Unix
166
+ end # module Path
167
+ end # module LibPath
168
+
169
+ # ############################## end of file ############################# #
170
+
@@ -0,0 +1,176 @@
1
+
2
+ # ######################################################################## #
3
+ # File: libpath/path/windows.rb
4
+ #
5
+ # Purpose: LibPath::Path::Windows module
6
+ #
7
+ # Created: 21st January 2019
8
+ # Updated: 16th April 2019
9
+ #
10
+ # Home: http://github.com/synesissoftware/libpath.Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2019, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+
48
+ =begin
49
+ =end
50
+
51
+ require 'libpath/diagnostics'
52
+ require 'libpath/internal_/windows/form'
53
+ require 'libpath/util/windows'
54
+
55
+ module LibPath # :nodoc:
56
+ module Path # :nodoc:
57
+ module Windows # :nodoc:
58
+
59
+ # Class representing a parsed path (for Windows)
60
+ class ParsedPath
61
+
62
+ # @!visibility private
63
+ module ParsedPath_Constants # :nodoc: all
64
+
65
+ INIT_VALID_OPTIONS = %i{ home locator pwd }
66
+ INIT_MPA_COMMON_OPTIONS = %i{ home locator pwd }
67
+ INIT_DRP_COMMON_OPTIONS = %i{ home locator pwd }
68
+ end
69
+
70
+ # Initialises an instance from the given +path+, optional
71
+ # +search_directory+ and options
72
+ #
73
+ # === Signature
74
+ #
75
+ # * *Parameters:*
76
+ # - +path+ (String) The path. May not be +nil+
77
+ # - +search_directory+ (String) The search_directory, from which the relative attributes are calculated for the path. May be +nil+
78
+ # - +options+ (Hash) Options
79
+ #
80
+ # * *Options:*
81
+ # - +????+
82
+ #
83
+ # * *Exceptions:*
84
+ # - +ArgumentError+ Raised if +path+ is +nil+
85
+ def initialize path, search_directory = nil, **options
86
+
87
+ raise ::ArgumentError, "path may not be nil or empty" if path.nil? || path.empty?
88
+
89
+ _Diagnostics = ::LibPath::Diagnostics
90
+ _Internal_Form = ::LibPath::Internal_::Windows::Form
91
+ _Util = ::LibPath::Util::Windows
92
+ _C = ::LibPath::Path::Windows::ParsedPath::ParsedPath_Constants
93
+
94
+ _Diagnostics.check_options(options, known: _C::INIT_VALID_OPTIONS)
95
+
96
+
97
+ abs_path = _Util.make_path_absolute(path, make_canonical: true, **options.select { |k| _C::INIT_MPA_COMMON_OPTIONS.include?(k) })
98
+
99
+ splits = _Internal_Form.split_path(abs_path)
100
+
101
+ _, f1_vol, f2_dir, f3_basename, f4_stem, f5_ext, f6_dir_parts, f7_abs_parts = *splits
102
+
103
+ @given_path = path
104
+ @absolute_path = abs_path
105
+ @compare_path = _Util.make_compare_path abs_path, splits: splits
106
+ @volume = f1_vol
107
+ @directory = f2_dir
108
+ @directory_path = "#{f1_vol}#{f2_dir}"
109
+ @directory_parts = f6_dir_parts
110
+
111
+ @file_full_name = f3_basename
112
+ @file_name_only = f4_stem
113
+ @file_extension = f5_ext
114
+
115
+ if search_directory
116
+
117
+ drp_options = options.select { |k| _C::INIT_DRP_COMMON_OPTIONS.include?(k) }
118
+
119
+ search_directory = _Util.make_path_canonical search_directory, make_slashes_canonical: true
120
+ search_directory = _Internal_Form.append_trailing_slash search_directory
121
+
122
+ @search_directory = search_directory
123
+ @search_relative_path = _Util.derive_relative_path(search_directory, abs_path, **drp_options)
124
+ @search_relative_path = _Internal_Form.append_trailing_slash(@search_relative_path) if _Internal_Form.char_is_path_name_separator?(abs_path[-1])
125
+ @search_relative_directory_path = _Internal_Form.append_trailing_slash _Util.derive_relative_path(search_directory, "#{f1_vol}#{f2_dir}", **drp_options)
126
+ @search_relative_directory_parts = @search_relative_directory_path.split('\\').map { |v| v + '\\' }
127
+ end
128
+ end
129
+
130
+ # (String) The path given to initialise the instance
131
+ attr_reader :given_path
132
+ # (String) The full-path of the instance
133
+ attr_reader :absolute_path
134
+ # (String) A normalised form of #path that can be used in comparisons
135
+ attr_reader :compare_path
136
+ # (String) The Windows volume, which may be a drive, or a UNC specification
137
+ attr_reader :volume
138
+ # (String) The entry's directory (excluding the #drive if on Windows)
139
+ attr_reader :directory
140
+ # (String) The full path of the entry's directory (taking into account the
141
+ # #drive if on Windows)
142
+ attr_reader :directory_path
143
+ alias_method :dirname, :directory_path
144
+ # ([String]) An array of directory parts, where each part ends in the path name separator
145
+ attr_reader :directory_parts
146
+ # (String) The entry's file name (combination of #stem + #extension)
147
+ attr_reader :file_full_name
148
+ alias_method :basename, :file_full_name
149
+ # (String) The entry's file stem
150
+ attr_reader :file_name_only
151
+ alias_method :stem, :file_name_only
152
+ # (String) The entry's file extension
153
+ attr_reader :file_extension
154
+ alias_method :extension, :file_extension
155
+ # (String) The search directory if specified; +nil+ otherwise
156
+ attr_reader :search_directory
157
+ # (String) The #path relative to #search_directory; +nil+ if no search directory specified
158
+ attr_reader :search_relative_path
159
+ # (String) The #directory_path relative to #search_directory; +nil+ if no search directory specified
160
+ attr_reader :search_relative_directory_path
161
+ # ([String]) The #directory_parts relative to #search_directory; +nil+ if no search directory specified
162
+ attr_reader :search_relative_directory_parts
163
+
164
+ # (String) String form of path
165
+ def to_s
166
+
167
+ absolute_path
168
+ end
169
+ end
170
+
171
+ end # module Windows
172
+ end # module Path
173
+ end # module LibPath
174
+
175
+ # ############################## end of file ############################# #
176
+
@@ -0,0 +1,42 @@
1
+
2
+ require 'libpath/internal_/platform'
3
+
4
+ if ::LibPath::Internal_::Platform::Constants::PLATFORM_IS_WINDOWS then
5
+
6
+ require 'libpath/util/windows'
7
+ else
8
+
9
+ require 'libpath/util/unix'
10
+ end
11
+
12
+ module LibPath # :nodoc:
13
+ module Util # :nodoc:
14
+
15
+ if ::LibPath::Internal_::Platform::Constants::PLATFORM_IS_WINDOWS then
16
+
17
+ extend ::LibPath::Util::Windows
18
+ include ::LibPath::Util::Windows
19
+ else
20
+
21
+ extend ::LibPath::Util::Unix
22
+ include ::LibPath::Util::Unix
23
+ end
24
+
25
+ # @!visibility private
26
+ def self.extended receiver # :nodoc:
27
+
28
+ $stderr.puts "#{receiver} extended by #{self}" if $DEBUG
29
+ end
30
+
31
+ # @!visibility private
32
+ def self.included receiver # :nodoc:
33
+
34
+ $stderr.puts "#{receiver} included #{self}" if $DEBUG
35
+ end
36
+
37
+ end # module Util
38
+ end # module LibPath
39
+
40
+ # ############################## end of file ############################# #
41
+
42
+
@@ -0,0 +1,414 @@
1
+
2
+ # ######################################################################## #
3
+ # File: libpath/util/unix.rb
4
+ #
5
+ # Purpose: LibPath::Util::Unix module
6
+ #
7
+ # Created: 14th January 2019
8
+ # Updated: 16th April 2019
9
+ #
10
+ # Home: http://github.com/synesissoftware/libpath.Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2019, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+
48
+ =begin
49
+ =end
50
+
51
+
52
+ require 'libpath/diagnostics'
53
+ require 'libpath/form/unix'
54
+ require 'libpath/internal_/array'
55
+ require 'libpath/internal_/unix/form'
56
+
57
+ module LibPath # :nodoc:
58
+ module Util # :nodoc:
59
+ module Unix # :nodoc:
60
+
61
+ # Module defining instance functions that will be included and extended into
62
+ # any class or module including/extending module LibPath::Util::Unix
63
+ module LibPath_Util_Unix_Methods
64
+
65
+ # Combines a number of path parts into a single path, ignoring any parts
66
+ # that are preceded by an absolute part
67
+ #
68
+ # NOTE: The behaviour of this method is undefined if any of the parts
69
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
70
+ def combine_paths *args, **options
71
+
72
+ _Form_Unix = Form::Unix
73
+ _Internal_Unix_Form = Internal_::Unix::Form
74
+
75
+ args.each_with_index { |arg, index| Diagnostics.check_string_parameter(arg, "arg#{index}", allow_nil: true) } if $DEBUG
76
+
77
+ if options[:elide_single_dots]
78
+
79
+ args = args.map do |arg|
80
+
81
+ case arg
82
+ when '.', './'
83
+
84
+ nil
85
+ else
86
+
87
+ arg
88
+ end
89
+ end
90
+ end
91
+
92
+ args = args.reject { |arg| arg.nil? || arg.empty? }
93
+
94
+ case args.size
95
+ when 0
96
+
97
+ ''
98
+ when 1
99
+
100
+ args[0]
101
+ else
102
+
103
+ rix = args.rindex { |arg| arg && _Form_Unix.path_is_absolute?(arg) }
104
+ rix ||= 0
105
+
106
+ els = args[rix..-1]
107
+
108
+ File.join(*els)
109
+ end
110
+ end
111
+
112
+ # Obtains the form of the given +path+ relative to the given +origin+
113
+ #
114
+ # NOTE: The behaviour of this method is undefined if any of the parts
115
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
116
+ #
117
+ # === Signature
118
+ #
119
+ # * *Options:*
120
+ # +:home+:: (String)
121
+ # +:locator+:: (boolean)
122
+ # +:pwd+:: (String)
123
+ def derive_relative_path origin, path, **options
124
+
125
+ return path if origin.nil? || origin.empty?
126
+ return path if path.nil? || path.empty?
127
+
128
+ _Form_Unix = Form::Unix
129
+ _Util_Unix = Util::Unix
130
+ _Internal_Unix_Form = Internal_::Unix::Form
131
+
132
+ _MPA_COMMON_OPTIONS = %i{ home locator pwd }
133
+
134
+ tr_sl = _Internal_Unix_Form.get_trailing_slash(path)
135
+
136
+ origin = _Util_Unix.make_path_canonical(origin)
137
+ path = _Util_Unix.make_path_canonical(path)
138
+
139
+ return '.' + tr_sl.to_s if origin == path
140
+ return path if '.' == origin || './' == origin
141
+
142
+ o_is_abs = _Form_Unix.path_is_absolute?(origin)
143
+ p_is_abs = _Form_Unix.path_is_absolute?(path)
144
+
145
+ if o_is_abs != p_is_abs || './' == path
146
+
147
+ origin = _Util_Unix.make_path_absolute(origin, make_canonical: true, **options.select { |k| _MPA_COMMON_OPTIONS.include?(k) })
148
+ path = _Util_Unix.make_path_absolute(path, make_canonical: true, **options.select { |k| _MPA_COMMON_OPTIONS.include?(k) })
149
+ end
150
+
151
+ origin = _Internal_Unix_Form.trim_trailing_slash(origin) unless origin.size < 2
152
+ path = _Internal_Unix_Form.trim_trailing_slash(path) if tr_sl && path.size > 1
153
+
154
+
155
+ _, _, _, o3_basename, _, _, o6_parts, _ = _Internal_Unix_Form.split_path(origin)
156
+ _, _, _, p3_basename, _, _, p6_parts, _ = _Internal_Unix_Form.split_path(path)
157
+
158
+ o_parts = o6_parts
159
+ o_parts << o3_basename if o3_basename && '.' != o3_basename
160
+
161
+ p_parts = p6_parts
162
+ p_parts << p3_basename if p3_basename && '.' != p3_basename
163
+
164
+
165
+ while true
166
+
167
+ break if o_parts.empty?
168
+ break if p_parts.empty?
169
+
170
+ o_part = o_parts[0]
171
+ p_part = p_parts[0]
172
+
173
+ if 1 == o_parts.size || 1 == p_parts.size
174
+
175
+ o_part = _Internal_Unix_Form.append_trailing_slash o_part
176
+ p_part = _Internal_Unix_Form.append_trailing_slash p_part
177
+ end
178
+
179
+ if o_part == p_part
180
+
181
+ o_parts.shift
182
+ p_parts.shift
183
+ else
184
+
185
+ break
186
+ end
187
+ end
188
+
189
+
190
+ return '.' + tr_sl.to_s if 0 == (o_parts.size + p_parts.size)
191
+
192
+ return o_parts.map { |rp| '..' }.join('/') + (tr_sl || (o_parts.size != 0 ? '/' : nil)).to_s if p_parts.empty?
193
+
194
+
195
+ ar = [ '..' ] * o_parts.size + p_parts
196
+ last = ar.pop
197
+ ar = ar.map { |el| _Internal_Unix_Form.append_trailing_slash(el) }
198
+
199
+ ar.join + last.to_s + tr_sl.to_s
200
+ end
201
+
202
+ # Returns a "compare path" for the given absolute path
203
+ #
204
+ # A compare path is one that would refer definitely to a given entry,
205
+ # regardless of such operating system-specific issues such as
206
+ # case-insensitivity
207
+ #
208
+ # NOTE: the function does not make +path+ absolute. That is up to the
209
+ # caller if required
210
+ #
211
+ # NOTE: The behaviour of this method is undefined if any of the parts
212
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
213
+ #
214
+ # === Signature
215
+ #
216
+ # * *Parameters:*
217
+ # - +path+:: (String) The path whose definitive equivalent is to be
218
+ # obtained
219
+ # - +options+:: (Hash) options
220
+ #
221
+ # * *Options:*
222
+ # For reasons of compatibility (with the Windows version) no options
223
+ # are currently supported; none are proscribed.
224
+ def make_compare_path path, **options
225
+
226
+ path
227
+ end
228
+
229
+ #
230
+ # NOTE: The behaviour of this method is undefined if any of the parts
231
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
232
+ #
233
+ # === Signature
234
+ #
235
+ # * *Parameters:*
236
+ # - +path+:: (String) The path to be evaluated. May not be +nil+
237
+ # - +options+:: (Hash) Options that moderate the behaviour of the
238
+ # function
239
+ #
240
+ # * *Options:*
241
+ # - +:home+:: (String) A specific home to assume, which means that the
242
+ # +locator+'s +home+ method will not be invoked
243
+ # - +:locator+:: (object) An object that provides the methods
244
+ # +pwd+ and +home+, as needed. This allows for mocking. If not
245
+ # given, then the functions +Dir::pwd+ and +Dir::home+ are used
246
+ # - +:make_canonical+:: (boolean) Determines whether canonicalisation
247
+ # is conducted on the result
248
+ # - +:pwd+:: (String) A specific directory to assume, which means that
249
+ # the +locator+'s +pwd+ method will not be invoked
250
+ def make_path_absolute path, **options
251
+
252
+ Diagnostics.check_string_parameter(path, "path") if $DEBUG
253
+ Diagnostics.check_options(options, known: %i{ home locator make_canonical pwd }) if $DEBUG
254
+
255
+ return path if path.nil? || path.empty?
256
+
257
+ r = nil
258
+
259
+ case path[0]
260
+ when '/'
261
+
262
+ r = path
263
+ when '~'
264
+
265
+ case path[1]
266
+ when nil, '/'
267
+
268
+ home = nil
269
+ home ||= options[:home]
270
+ home ||= options[:locator].home if options.has_key?(:locator)
271
+ home ||= Dir.home
272
+
273
+ r = File.join(home, path[2..-1].to_s)
274
+ end
275
+ end
276
+
277
+ unless r
278
+
279
+ pwd = nil
280
+ pwd ||= options[:pwd]
281
+ pwd ||= options[:locator].pwd if options.has_key?(:locator)
282
+ pwd ||= Dir.pwd
283
+
284
+ r = File.join(pwd, path)
285
+ end
286
+
287
+ r ||= path
288
+
289
+ r = make_path_canonical r if options[:make_canonical]
290
+
291
+ return r
292
+ end
293
+
294
+ # Converts a path into canonical form, which is to say that all possible
295
+ # dots directory parts are removed:
296
+ #
297
+ # - single-dot (trailing) parts - '???/.' are converted to '???/' (where
298
+ # ??? represents 0+ other characters);
299
+ # - single-dot parts - './' - are all removed
300
+ # - double-dot parts - '../' - are removed where they follow a non-dots
301
+ # directory part, or where they follow the root
302
+ #
303
+ # NOTE: The behaviour of this method is undefined if any of the parts
304
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
305
+ #
306
+ # === Signature
307
+ #
308
+ # * *Parameters:*
309
+ # - +path+:: (String) The path to be evaluated. May not be +nil+
310
+ def make_path_canonical path, **options
311
+
312
+ Diagnostics.check_string_parameter(path, "path") if $DEBUG
313
+
314
+ return path unless '.' == path[-1] || path.include?('./') || path.include?('//')
315
+
316
+ _Form = ::LibPath::Internal_::Unix::Form
317
+ _Array = ::LibPath::Internal_::Array
318
+
319
+ path = path[0...-1] if '.' == path[-1] && '/' == path[-2]
320
+
321
+
322
+ f0_path, _, f2_dir, f3_basename, _, _, f6_dir_parts, _ = _Form.split_path path
323
+
324
+ if f6_dir_parts.empty?
325
+
326
+ case f3_basename
327
+ when '.'
328
+
329
+ return './'
330
+ when '..'
331
+
332
+ return '../'
333
+ else
334
+
335
+ return f0_path
336
+ end
337
+ end
338
+
339
+ case f3_basename
340
+ when '.', '..'
341
+
342
+ f6_dir_parts << f3_basename + '/'
343
+ basename = nil
344
+ else
345
+
346
+ basename = f3_basename
347
+ end
348
+
349
+ is_rooted = '/' == f2_dir[0]
350
+
351
+ new_parts = f6_dir_parts.dup
352
+ new_parts.reject! { |p| './' == p }
353
+ ix_nodots = new_parts.index { |p| '../' != p } || new_parts.size
354
+ ix_2dots = _Array.index(new_parts, '../', ix_nodots)
355
+
356
+ return "#{new_parts.join}#{basename}" unless new_parts.size != f6_dir_parts.size || ix_2dots
357
+
358
+ while (ix_2dots || 0) > 0
359
+
360
+ new_parts.delete_at(ix_2dots - 0)
361
+ new_parts.delete_at(ix_2dots - 1) if ix_2dots != 1 || !is_rooted
362
+
363
+ ix_nodots = new_parts.index { |p| '../' != p } or break
364
+ ix_2dots = _Array.index(new_parts, '../', ix_nodots)
365
+ end
366
+
367
+ if new_parts.empty? && (basename || '').empty?
368
+
369
+ case f3_basename
370
+ when nil, '.', '..'
371
+
372
+ return './'
373
+ else
374
+
375
+ return '.'
376
+ end
377
+ end
378
+
379
+ return new_parts.join('') + basename.to_s
380
+ end
381
+ end # module LibPath_Util_Unix_Methods
382
+
383
+ # @!visibility private
384
+ def self.extended receiver # :nodoc:
385
+
386
+ receiver.class_eval do
387
+
388
+ extend LibPath_Util_Unix_Methods
389
+ end
390
+
391
+ $stderr.puts "#{receiver} extended by #{LibPath_Util_Unix_Methods}" if $DEBUG
392
+ end
393
+
394
+ # @!visibility private
395
+ def self.included receiver # :nodoc:
396
+
397
+ receiver.class_eval do
398
+
399
+ include LibPath_Util_Unix_Methods
400
+ end
401
+
402
+ $stderr.puts "#{receiver} included #{LibPath_Util_Unix_Methods}" if $DEBUG
403
+ end
404
+
405
+ extend LibPath_Util_Unix_Methods
406
+ include LibPath_Util_Unix_Methods
407
+
408
+ end # module Unix
409
+ end # module Util
410
+ end # module LibPath
411
+
412
+ # ############################## end of file ############################# #
413
+
414
+