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,636 @@
1
+
2
+ # ######################################################################## #
3
+ # File: libpath/util/windows.rb
4
+ #
5
+ # Purpose: LibPath::Util::Windows module
6
+ #
7
+ # Created: 10th 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/windows'
54
+ require 'libpath/internal_/array'
55
+ require 'libpath/internal_/windows/form'
56
+
57
+ module LibPath # :nodoc:
58
+ module Util # :nodoc:
59
+ module Windows # :nodoc:
60
+
61
+ # Module defining instance functions that will be included and extended into
62
+ # any class or module including/extending module LibPath::Util::Windows
63
+ module LibPath_Util_Windows_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
+ # Because Windows paths' absolute nature comprises two elements - the
69
+ # volume(/drive) and the root directory - it is possible to combine
70
+ # elements where either the volume or the root directory is missing.
71
+ #
72
+ # NOTE: The behaviour of this method is undefined if any of the parts
73
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
74
+ def combine_paths *args, **options
75
+
76
+ _Form_Windows = Form::Windows
77
+ _Internal_Windows_Form = Internal_::Windows::Form
78
+
79
+ args.each_with_index { |arg, index| Diagnostics.check_string_parameter(arg, "arg#{index}", allow_nil: true) } if $DEBUG
80
+
81
+ first = []
82
+ dirs = []
83
+ last = []
84
+
85
+ if options[:elide_single_dots]
86
+
87
+ args = args.map do |arg|
88
+
89
+ case arg
90
+ when '.', './'
91
+
92
+ nil
93
+ else
94
+
95
+ arg
96
+ end
97
+ end
98
+ end
99
+
100
+ args = args.reject { |arg| arg.nil? || arg.empty? }
101
+
102
+ rix_abs = nil
103
+ rix_drv = nil
104
+ rix_dir = nil
105
+
106
+ args.each_with_index do |arg, index|
107
+
108
+ vol, rem, _ = _Internal_Windows_Form.get_windows_volume arg
109
+
110
+ rem = nil unless rem && _Internal_Windows_Form.char_is_path_name_separator?(rem[0])
111
+
112
+ if vol
113
+
114
+ if rem
115
+
116
+ rix_abs = index
117
+ else
118
+
119
+ rix_drv = index
120
+ end
121
+ elsif rem
122
+
123
+ rix_dir = index
124
+ end
125
+ end
126
+
127
+ rix_drv = nil if (rix_drv || -1) <= (rix_abs || -1)
128
+ rix_dir = nil if (rix_dir || -1) <= (rix_abs || -1)
129
+
130
+ if rix_drv && rix_dir && rix_abs
131
+
132
+ if rix_abs < rix_drv && rix_abs < rix_dir
133
+
134
+ rix_abs += 1
135
+ args = args[rix_abs..-1]
136
+ rix_drv -= rix_abs
137
+ rix_dir -= rix_abs
138
+ rix_abs = nil
139
+ end
140
+ end
141
+
142
+ if rix_drv.nil? && rix_dir.nil?
143
+
144
+ if rix_abs
145
+
146
+ args = args[rix_abs..-1]
147
+ end
148
+
149
+ dirs = args
150
+ last << args.pop unless args.empty?
151
+ else
152
+
153
+ if false
154
+
155
+ ;
156
+ elsif rix_drv
157
+
158
+ if rix_dir
159
+
160
+ drv = args.delete_at rix_drv
161
+ rix_dir -= 1 if rix_drv < rix_dir
162
+ dir = args.delete_at rix_dir
163
+
164
+ args = args[rix_dir..-1]
165
+
166
+ if dir.size > 1
167
+
168
+ args.unshift dir[1..-1]
169
+ dir = dir[0]
170
+ end
171
+
172
+ root = _Internal_Windows_Form.append_trailing_slash("#{drv}#{dir}")
173
+
174
+ first << root
175
+ last << args.pop unless args.empty?
176
+ dirs = args
177
+ elsif rix_abs
178
+
179
+ drv = args.delete_at rix_drv
180
+ rix_abs -= 1 if rix_drv < rix_abs
181
+ abs = args.delete_at rix_abs
182
+
183
+ _, _, dir, bas, _, _, _, _ = _Internal_Windows_Form.split_path abs
184
+
185
+ args = args[rix_abs..-1]
186
+
187
+ if dir.size > 1
188
+
189
+ args.unshift dir[1..-1]
190
+ dir = dir[0]
191
+ end
192
+
193
+ root = _Internal_Windows_Form.append_trailing_slash("#{drv}#{dir}#{bas}")
194
+
195
+ first << root
196
+ last << args.pop unless args.empty?
197
+ dirs = args
198
+ else
199
+
200
+ first << args.delete_at(rix_drv)
201
+ last << args.pop unless args.empty?
202
+ dirs = args
203
+ end
204
+ elsif rix_dir
205
+
206
+ if rix_abs
207
+
208
+ abs = args.delete_at rix_abs
209
+ rix_dir -= 1 if rix_abs < rix_dir
210
+ dir = args.delete_at rix_dir
211
+
212
+ _, vol, _, _, _, _, _, _ = _Internal_Windows_Form.split_path abs
213
+
214
+ args = args[rix_dir..-1]
215
+
216
+ root = _Internal_Windows_Form.append_trailing_slash("#{vol}#{dir}")
217
+
218
+ first << root
219
+ last << args.pop unless args.empty?
220
+ dirs = args
221
+ else
222
+
223
+ args = args[rix_dir..-1]
224
+ last << args.pop unless args.empty?
225
+ dirs = args
226
+ end
227
+ else
228
+
229
+ ;
230
+ end
231
+ end
232
+
233
+ dirs = dirs.map { |el| _Internal_Windows_Form.append_trailing_slash el }
234
+
235
+ (first + dirs + last).join('')
236
+ end
237
+
238
+ # Obtains the form of the given +path+ relative to the given +origin+
239
+ #
240
+ # NOTE: The behaviour of this method is undefined if any of the parts
241
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
242
+ #
243
+ #
244
+ # === Signature
245
+ #
246
+ # * *Options:*
247
+ # +:home+:: (String)
248
+ # +:locator+:: (boolean)
249
+ # +:make_canonical+:: (boolean)
250
+ # +:pwd+:: (String)
251
+ def derive_relative_path origin, path, **options
252
+
253
+ return path if origin.nil? || origin.empty?
254
+ return path if path.nil? || path.empty?
255
+
256
+ _Form_Windows = Form::Windows
257
+ _Util_Windows = Util::Windows
258
+ _Internal_Windows_Form = Internal_::Windows::Form
259
+
260
+ _MPA_COMMON_OPTIONS = %i{ home locator pwd }
261
+
262
+ tr_sl = _Internal_Windows_Form.get_trailing_slash(path)
263
+
264
+ # Possibly naive home-correction
265
+
266
+ return derive_relative_path(absolute_path(origin), path, **options) if _Form_Windows.path_is_homed?(origin)
267
+ return derive_relative_path(origin, absolute_path(path), **options) if _Form_Windows.path_is_homed?(path)
268
+
269
+
270
+ o_vol, o_rem, _ = _Internal_Windows_Form.get_windows_volume origin
271
+ p_vol, p_rem, _ = _Internal_Windows_Form.get_windows_volume path
272
+
273
+ if o_vol && p_vol
274
+
275
+ # always give absolute answer when 'volume's are different
276
+
277
+ if o_vol != p_vol
278
+
279
+ if options[:make_path_canonical]
280
+
281
+ path = _Util_Windows.make_path_canonical(path, make_slashes_canonical: true)
282
+ else
283
+
284
+ path = path.tr('/', '\\')
285
+ end
286
+
287
+ return path
288
+ end
289
+ end
290
+
291
+
292
+ o_is_rooted = o_rem && _Internal_Windows_Form.char_is_path_name_separator?(o_rem[0])
293
+ p_is_rooted = p_rem && _Internal_Windows_Form.char_is_path_name_separator?(p_rem[0])
294
+
295
+ o_is_abs = o_vol && o_is_rooted
296
+ p_is_abs = p_vol && p_is_rooted
297
+
298
+ mpa_opts = options.select { |k| _MPA_COMMON_OPTIONS.include?(k) }
299
+
300
+ if o_is_abs != p_is_abs || o_is_rooted != p_is_rooted
301
+
302
+ origin = _Util_Windows.make_path_absolute(origin, **mpa_opts) unless o_is_abs
303
+ path = _Util_Windows.make_path_absolute(path, **mpa_opts) unless p_is_abs
304
+
305
+ return derive_relative_path(origin, path, **options)
306
+ end
307
+
308
+ origin = _Util_Windows.make_path_canonical(origin, make_slashes_canonical: true)
309
+ path = _Util_Windows.make_path_canonical(path, make_slashes_canonical: true)
310
+
311
+ return '.' + tr_sl.to_s if origin == path
312
+ return path if '.\\' == origin
313
+
314
+ if o_is_abs != p_is_abs || '.\\' == path
315
+
316
+ origin = _Util_Windows.make_path_absolute(origin, make_canonical: true, **options.select { |k| _MPA_COMMON_OPTIONS.include?(k) })
317
+ path = _Util_Windows.make_path_absolute(path, make_canonical: true, **options.select { |k| _MPA_COMMON_OPTIONS.include?(k) })
318
+ end
319
+
320
+
321
+ _, _, _, o3_basename, _, _, o6_parts, _ = _Internal_Windows_Form.split_path(origin)
322
+ _, _, _, p3_basename, _, _, p6_parts, _ = _Internal_Windows_Form.split_path(path)
323
+
324
+ o_parts = o6_parts
325
+ o_parts << o3_basename if o3_basename && '.' != o3_basename
326
+
327
+ p_parts = p6_parts
328
+ p_parts << p3_basename if p3_basename && '.' != p3_basename
329
+
330
+
331
+ while true
332
+
333
+ break if o_parts.empty?
334
+ break if p_parts.empty?
335
+
336
+ o_part = o_parts[0]
337
+ p_part = p_parts[0]
338
+
339
+ if 1 == o_parts.size || 1 == p_parts.size
340
+
341
+ o_part = _Internal_Windows_Form.append_trailing_slash o_part
342
+ p_part = _Internal_Windows_Form.append_trailing_slash p_part
343
+ end
344
+
345
+ parts_equal = false
346
+
347
+ if o_part.size == p_part.size
348
+
349
+ o_part = o_part.tr('/', '\\') if o_part.include? '/'
350
+ p_part = p_part.tr('/', '\\') if p_part.include? '/'
351
+
352
+ parts_equal = o_part == p_part
353
+ end
354
+
355
+
356
+ if parts_equal
357
+
358
+ o_parts.shift
359
+ p_parts.shift
360
+ else
361
+
362
+ break
363
+ end
364
+ end
365
+
366
+
367
+ return '.' + tr_sl.to_s if 0 == (o_parts.size + p_parts.size)
368
+
369
+ return o_parts.map { |rp| '..' }.join('\\') + (tr_sl || (o_parts.size > 0 ? '\\' : nil)).to_s if p_parts.empty?
370
+
371
+
372
+ ar = [ '..' ] * o_parts.size + p_parts
373
+ last = ar.pop
374
+ ar = ar.map { |el| _Internal_Windows_Form.append_trailing_slash(el) }
375
+
376
+ last[-1] = '\\' if '/' == last[-1]
377
+
378
+ ar.join + last.to_s
379
+ end
380
+
381
+ # Returns a "compare path" for the given absolute path
382
+ #
383
+ # A compare path is one that would refer definitely to a given entry,
384
+ # regardless of such operating system-specific issues such as
385
+ # case-insensitivity
386
+ #
387
+ # NOTE: the function does not make +path+ absolute. That is up to the
388
+ # caller if required
389
+ #
390
+ # NOTE: The behaviour of this method is undefined if any of the parts
391
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
392
+ #
393
+ # === Signature
394
+ #
395
+ # * *Parameters:*
396
+ # - +path+:: (String) The path whose definitive equivalent is to be
397
+ # obtained
398
+ # - +options+:: (Hash) options
399
+ #
400
+ # * *Options:*
401
+ # - +:splits+:: ([ String ]) An array of string-like objects. If the
402
+ # object at index 1 exists and supports the +form+ attribute and
403
+ # that returns one of { +:form_2+, +:form_3+, +:form_4+, +:form_5+,
404
+ # +:form_6+ } then it is assumed to be the volume, and the objects
405
+ # at indexes 2 and 3 are assumed to be the directory and the
406
+ # basename, respectively. In this case, the compare path is
407
+ # constructed from a UNC-respecting form
408
+ def make_compare_path path, **options
409
+
410
+ if splits = options[:splits]
411
+
412
+ if volume = splits[1]
413
+
414
+ if volume.respond_to?(:form)
415
+
416
+ case volume.form
417
+ when :form_2, :form_3, :form_4, :form_5, :form_6
418
+
419
+ directory = splits[2] || ''
420
+ basename = splits[3] || ''
421
+
422
+ return "#{volume}#{directory.upcase}#{basename.upcase}"
423
+ else
424
+
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ path.upcase
431
+ end
432
+
433
+ # NOTE: The behaviour of this method is undefined if any of the parts
434
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
435
+ #
436
+ def make_path_absolute path, **options
437
+
438
+ _Form_Windows = Form::Windows
439
+ _Internal_Windows_Form = Internal_::Windows::Form
440
+
441
+ Diagnostics.check_string_parameter(path, "path") if $DEBUG
442
+ Diagnostics.check_options(options, known: %i{ home locator make_canonical pwd }) if $DEBUG
443
+
444
+ return path if path.nil? || path.empty?
445
+
446
+ r = nil
447
+
448
+ if false
449
+
450
+ ;
451
+ elsif _Form_Windows.path_is_homed? path
452
+
453
+ home = nil
454
+ home ||= options[:home]
455
+ home ||= options[:locator].home if options.has_key?(:locator)
456
+ home ||= Dir.home
457
+
458
+ unless _Internal_Windows_Form.has_trailing_slash? home
459
+
460
+ home = home + path[1].to_s
461
+ end
462
+
463
+ r = combine_paths(home, path[2..-1])
464
+ elsif _Form_Windows.path_is_UNC? path
465
+
466
+ r = path
467
+ elsif _Form_Windows.path_is_absolute? path
468
+
469
+ r = path
470
+ elsif _Form_Windows.path_is_rooted? path
471
+
472
+ pwd = nil
473
+ pwd ||= options[:pwd]
474
+ pwd ||= options[:locator].pwd if options.has_key?(:locator)
475
+ pwd ||= Dir.pwd
476
+
477
+ r = pwd[0..1] + path
478
+ else
479
+
480
+ pwd = nil
481
+ pwd ||= options[:pwd]
482
+ pwd ||= options[:locator].pwd if options.has_key?(:locator)
483
+ pwd ||= Dir.pwd
484
+
485
+ r = combine_paths(pwd, path, elide_single_dots: false)
486
+ end
487
+
488
+ if options[:make_canonical]
489
+
490
+ r = make_path_canonical r
491
+ else
492
+
493
+ vol, rem, _ = _Internal_Windows_Form.get_windows_volume r
494
+
495
+ _Internal_Windows_Form.elide_redundant_path_name_separators! rem
496
+
497
+ r = "#{vol}#{rem}"
498
+ end
499
+
500
+ return r
501
+ end
502
+
503
+ # Converts a path into canonical form, which is to say that all possible
504
+ # dots directory parts are removed:
505
+ #
506
+ # - single-dot parts - './' or '.\\' - are all removed
507
+ # - double-dot parts - '../' or '..\\' - are removed where they follow a
508
+ # non-dots directory part, or where they follow the root
509
+ #
510
+ # NOTE: The behaviour of this method is undefined if any of the parts
511
+ # are malformed. See +::LibPath::Form::Windows::name_is_malformed?+
512
+ #
513
+ # === Signature
514
+ #
515
+ # * *Parameters:*
516
+ # - +path+:: (String) The path to be evaluated. May not be +nil+
517
+ #
518
+ # * *Options:*
519
+ # - +:make_slashes_canonical+:: (boolean) Determines whether to
520
+ # additionally convert all forward slashes to backslashes
521
+ def make_path_canonical path, **options
522
+
523
+ Diagnostics.check_string_parameter(path, "path") if $DEBUG
524
+
525
+ if path.include?('/') && options[:make_slashes_canonical]
526
+
527
+ path = path.tr '/', '\\'
528
+ end
529
+
530
+ return path unless '.' == path[-1] || path =~ /[.\\\/][\\\/]/
531
+
532
+ _Form = ::LibPath::Internal_::Windows::Form
533
+ _Array = ::LibPath::Internal_::Array
534
+
535
+ path = path[0...-1] if '.' == path[-1] && _Form.char_is_path_name_separator?(path[-2])
536
+
537
+
538
+ f0_path, f1_volume, f2_directory, f3_basename, _, _, f6_dir_parts, _ = _Form.split_path path
539
+
540
+ if f6_dir_parts.empty?
541
+
542
+ case f3_basename
543
+ when '.'
544
+
545
+ return "#{f1_volume}.\\"
546
+ when '..'
547
+
548
+ return "#{f1_volume}..\\"
549
+ else
550
+
551
+ return f0_path
552
+ end
553
+ end
554
+
555
+ last_slash = nil
556
+
557
+ case f3_basename
558
+ when '.', '..'
559
+
560
+ f6_dir_parts << f3_basename + '\\'
561
+ basename = nil
562
+ when nil
563
+
564
+ last_slash = _Form.get_trailing_slash(f2_directory) || '\\'
565
+ else
566
+
567
+ basename = f3_basename
568
+ end
569
+
570
+ is_rooted = _Form.char_is_path_name_separator?(f2_directory[0])
571
+
572
+ new_parts = f6_dir_parts.dup
573
+ new_parts.reject! { |p| '.\\' == p || './' == p }
574
+ ix_nodots = new_parts.index { |p| '../' != p && '..\\' != p } || new_parts.size
575
+ ix_2dots = _Array.index2(new_parts, '../', '..\\', ix_nodots)
576
+
577
+ return "#{f1_volume}#{new_parts.join}#{basename}" unless new_parts.size != f6_dir_parts.size || ix_2dots
578
+
579
+ while (ix_2dots || 0) > 0
580
+
581
+ new_parts.delete_at(ix_2dots - 0)
582
+ new_parts.delete_at(ix_2dots - 1) if ix_2dots != 1 || !is_rooted
583
+
584
+ ix_nodots = new_parts.index { |p| '../' != p && '..\\' != p } or break
585
+ ix_2dots = _Array.index2(new_parts, '../', '..\\', ix_nodots)
586
+ end
587
+
588
+ if new_parts.empty? && (basename || '').empty?
589
+
590
+ case f3_basename
591
+ when nil, '.', '..'
592
+
593
+ return '.' + (last_slash || '\\').to_s
594
+ else
595
+
596
+ return '.'
597
+ end
598
+ return '.' + last_slash.to_s
599
+ end
600
+
601
+ return f1_volume.to_s + new_parts.join('') + basename.to_s
602
+ end
603
+ end # module LibPath_Util_Windows_Methods
604
+
605
+ # @!visibility private
606
+ def self.extended receiver # :nodoc:
607
+
608
+ receiver.class_eval do
609
+
610
+ extend LibPath_Util_Windows_Methods
611
+ end
612
+
613
+ $stderr.puts "#{receiver} extended by #{LibPath_Util_Windows_Methods}" if $DEBUG
614
+ end
615
+
616
+ # @!visibility private
617
+ def self.included receiver # :nodoc:
618
+
619
+ receiver.class_eval do
620
+
621
+ include LibPath_Util_Windows_Methods
622
+ end
623
+
624
+ $stderr.puts "#{receiver} included #{LibPath_Util_Windows_Methods}" if $DEBUG
625
+ end
626
+
627
+ extend LibPath_Util_Windows_Methods
628
+ include LibPath_Util_Windows_Methods
629
+
630
+ end # module Windows
631
+ end # module Util
632
+ end # module LibPath
633
+
634
+ # ############################## end of file ############################# #
635
+
636
+