libpath-ruby 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+