Module: LibPath::Util::Windows::LibPath_Util_Windows_Methods

Included in:
LibPath::Util::Windows, LibPath::Util::Windows
Defined in:
lib/libpath/util/windows.rb

Overview

Module defining instance functions that will be included and extended into any class or module including/extending module LibPath::Util::Windows

Instance Method Summary collapse

Instance Method Details

#combine_paths(*args, **options) ⇒ Object

Combines a number of path parts into a single path, ignoring any parts that are preceded by an absolute part

Because Windows paths’ absolute nature comprises two elements - the volume(/drive) and the root directory - it is possible to combine elements where either the volume or the root directory is missing.

NOTE: The behaviour of this method is undefined if any of the parts are malformed. See ::LibPath::Form::Windows::name_is_malformed?



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/libpath/util/windows.rb', line 74

def combine_paths *args, **options

	_Form_Windows			=	Form::Windows
	_Internal_Windows_Form	=	Internal_::Windows::Form

	args.each_with_index { |arg, index| Diagnostics.check_string_parameter(arg, "arg#{index}", allow_nil: true) } if $DEBUG

	first	=	[]
	dirs	=	[]
	last	=	[]

	if options[:elide_single_dots]

		args	=	args.map do |arg|

			case arg
			when '.', './'

				nil
			else

				arg
			end
		end
	end

	args	=	args.reject { |arg| arg.nil? || arg.empty? }

	rix_abs	=	nil
	rix_drv	=	nil
	rix_dir	=	nil

	args.each_with_index do |arg, index|

		vol, rem, _ = _Internal_Windows_Form.get_windows_volume arg

		rem = nil unless rem && _Internal_Windows_Form.char_is_path_name_separator?(rem[0])

		if vol

			if rem

				rix_abs	=	index
			else

				rix_drv	=	index
			end
		elsif rem

			rix_dir	=	index
		end
	end

	rix_drv	=	nil if (rix_drv || -1) <= (rix_abs || -1)
	rix_dir	=	nil if (rix_dir || -1) <= (rix_abs || -1)

	if rix_drv && rix_dir && rix_abs

		if rix_abs < rix_drv && rix_abs < rix_dir

			rix_abs	+=	1
			args	=	args[rix_abs..-1]
			rix_drv	-=	rix_abs
			rix_dir	-=	rix_abs
			rix_abs	=	nil
		end
	end

	if rix_drv.nil? && rix_dir.nil?

		if rix_abs

			args	=	args[rix_abs..-1]
		end

		dirs	=	args
		last	<<	args.pop unless args.empty?
	else

		if false

			;
		elsif rix_drv

			if rix_dir

				drv		=	args.delete_at rix_drv
				rix_dir	-=	1 if rix_drv < rix_dir
				dir		=	args.delete_at rix_dir

				args	=	args[rix_dir..-1]

				if dir.size > 1

					args.unshift dir[1..-1]
					dir	=	dir[0]
				end

				root	=	_Internal_Windows_Form.append_trailing_slash("#{drv}#{dir}")

				first	<<	root
				last	<<	args.pop unless args.empty?
				dirs	=	args
			elsif rix_abs

				drv		=	args.delete_at rix_drv
				rix_abs	-=	1 if rix_drv < rix_abs
				abs		=	args.delete_at rix_abs

				_, _, dir, bas, _, _, _, _	=	_Internal_Windows_Form.split_path abs

				args	=	args[rix_abs..-1]

				if dir.size > 1

					args.unshift dir[1..-1]
					dir	=	dir[0]
				end

				root	=	_Internal_Windows_Form.append_trailing_slash("#{drv}#{dir}#{bas}")

				first	<<	root
				last	<<	args.pop unless args.empty?
				dirs	=	args
			else

				first	<<	args.delete_at(rix_drv)
				last	<<	args.pop unless args.empty?
				dirs	=	args
			end
		elsif rix_dir

			if rix_abs

				abs		=	args.delete_at rix_abs
				rix_dir	-=	1 if rix_abs < rix_dir
				dir		=	args.delete_at rix_dir

				_, vol, _, _, _, _, _, _	=	_Internal_Windows_Form.split_path abs

				args	=	args[rix_dir..-1]

				root	=	_Internal_Windows_Form.append_trailing_slash("#{vol}#{dir}")

				first	<<	root
				last	<<	args.pop unless args.empty?
				dirs	=	args
			else

				args	=	args[rix_dir..-1]
				last	<<	args.pop unless args.empty?
				dirs	=	args
			end
		else

			;
		end
	end

	dirs	=	dirs.map { |el| _Internal_Windows_Form.append_trailing_slash el }

	(first + dirs + last).join('')
end

#derive_relative_path(origin, path, **options) ⇒ Object

Obtains the form of the given path relative to the given origin

NOTE: The behaviour of this method is undefined if any of the parts are malformed. See ::LibPath::Form::Windows::name_is_malformed?

Signature

  • Options:

+:home+:: (String)
+:locator+:: (boolean)
+:make_canonical+:: (boolean)
+:pwd+:: (String)


251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/libpath/util/windows.rb', line 251

def derive_relative_path origin, path, **options

	return path if origin.nil? || origin.empty?
	return path if path.nil? || path.empty?

	_Form_Windows			=	Form::Windows
	_Util_Windows			=	Util::Windows
	_Internal_Windows_Form	=	Internal_::Windows::Form

	_MPA_COMMON_OPTIONS	=	%i{ home locator pwd }

	tr_sl					=	_Internal_Windows_Form.get_trailing_slash(path)

	# Possibly naive home-correction

	return derive_relative_path(absolute_path(origin), path, **options) if _Form_Windows.path_is_homed?(origin)
	return derive_relative_path(origin, absolute_path(path), **options) if _Form_Windows.path_is_homed?(path)


	o_vol, o_rem, _ = _Internal_Windows_Form.get_windows_volume origin
	p_vol, p_rem, _ = _Internal_Windows_Form.get_windows_volume path

	if o_vol && p_vol

		# always give absolute answer when 'volume's are different

		if o_vol != p_vol

			if options[:make_path_canonical]

				path	=	_Util_Windows.make_path_canonical(path, make_slashes_canonical: true)
			else

				path	=	path.tr('/', '\\')
			end

			return path
		end
	end


	o_is_rooted	=	o_rem && _Internal_Windows_Form.char_is_path_name_separator?(o_rem[0])
	p_is_rooted	=	p_rem && _Internal_Windows_Form.char_is_path_name_separator?(p_rem[0])

	o_is_abs	=	o_vol && o_is_rooted
	p_is_abs	=	p_vol && p_is_rooted

	mpa_opts	=	options.select { |k| _MPA_COMMON_OPTIONS.include?(k) }

	if o_is_abs != p_is_abs || o_is_rooted != p_is_rooted

		origin	=	_Util_Windows.make_path_absolute(origin, **mpa_opts) unless o_is_abs
		path	=	_Util_Windows.make_path_absolute(path, **mpa_opts) unless p_is_abs

		return derive_relative_path(origin, path, **options)
	end

	origin	=	_Util_Windows.make_path_canonical(origin, make_slashes_canonical: true)
	path	=	_Util_Windows.make_path_canonical(path, make_slashes_canonical: true)

	return '.' + tr_sl.to_s if origin == path
	return path if '.\\' == origin

	if o_is_abs != p_is_abs || '.\\' == path

		origin	=	_Util_Windows.make_path_absolute(origin, make_canonical: true, **options.select { |k| _MPA_COMMON_OPTIONS.include?(k) })
		path	=	_Util_Windows.make_path_absolute(path, make_canonical: true, **options.select { |k| _MPA_COMMON_OPTIONS.include?(k) })
	end


	_, _, _, o3_basename, _, _, o6_parts, _	=	_Internal_Windows_Form.split_path(origin)
	_, _, _, p3_basename, _, _, p6_parts, _	=	_Internal_Windows_Form.split_path(path)

	o_parts	=	o6_parts
	o_parts	<<	o3_basename if o3_basename && '.' != o3_basename

	p_parts	=	p6_parts
	p_parts	<<	p3_basename if p3_basename && '.' != p3_basename


	while true

		break if o_parts.empty?
		break if p_parts.empty?

		o_part	=	o_parts[0]
		p_part	=	p_parts[0]

		if 1 == o_parts.size || 1 == p_parts.size

			o_part	=	_Internal_Windows_Form.append_trailing_slash o_part
			p_part	=	_Internal_Windows_Form.append_trailing_slash p_part
		end

		parts_equal = false

		if o_part.size == p_part.size

			o_part	=	o_part.tr('/', '\\') if o_part.include? '/'
			p_part	=	p_part.tr('/', '\\') if p_part.include? '/'

			parts_equal = o_part == p_part
		end


		if parts_equal

			o_parts.shift
			p_parts.shift
		else

			break
		end
	end


	return '.' + tr_sl.to_s if 0 == (o_parts.size + p_parts.size)

	return o_parts.map { |rp| '..' }.join('\\') + (tr_sl || (o_parts.size > 0 ? '\\' : nil)).to_s if p_parts.empty?


	ar		=	[ '..' ] * o_parts.size + p_parts
	last	=	ar.pop
	ar		=	ar.map { |el| _Internal_Windows_Form.append_trailing_slash(el) }

	last[-1] = '\\' if '/' == last[-1]

	ar.join + last.to_s
end

#make_compare_path(path, **options) ⇒ Object

Returns a “compare path” for the given absolute path

A compare path is one that would refer definitely to a given entry, regardless of such operating system-specific issues such as case-insensitivity

NOTE: the function does not make path absolute. That is up to the caller if required

NOTE: The behaviour of this method is undefined if any of the parts are malformed. See ::LibPath::Form::Windows::name_is_malformed?

Signature

  • Parameters:

  • path

    (String) The path whose definitive equivalent is to be

    obtained
    
  • options

    (Hash) options

  • Options:

  • :splits

    ([ String ]) An array of string-like objects. If the

    object at index 1 exists and supports the +form+ attribute and
    that returns one of { +:form_2+, +:form_3+, +:form_4+, +:form_5+,
    +:form_6+ } then it is assumed to be the volume, and the objects
    at indexes 2 and 3 are assumed to be the directory and the
    basename, respectively. In this case, the compare path is
    constructed from a UNC-respecting form
    


408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/libpath/util/windows.rb', line 408

def make_compare_path path, **options

	if splits = options[:splits]

		if volume = splits[1]

			if volume.respond_to?(:form)

				case volume.form
				when :form_2, :form_3, :form_4, :form_5, :form_6

					directory	=	splits[2] || ''
					basename	=	splits[3] || ''

					return "#{volume}#{directory.upcase}#{basename.upcase}"
				else

				end
			end
		end
	end

	path.upcase
end

#make_path_absolute(path, **options) ⇒ Object

NOTE: The behaviour of this method is undefined if any of the parts are malformed. See ::LibPath::Form::Windows::name_is_malformed?



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/libpath/util/windows.rb', line 436

def make_path_absolute path, **options

	_Form_Windows			=	Form::Windows
	_Internal_Windows_Form	=	Internal_::Windows::Form

	Diagnostics.check_string_parameter(path, "path") if $DEBUG
	Diagnostics.check_options(options, known: %i{ home locator make_canonical pwd }) if $DEBUG

	return path if path.nil? || path.empty?

	r	=	nil

	if false

		;
	elsif _Form_Windows.path_is_homed? path

		home	=	nil
		home	||=	options[:home]
		home	||=	options[:locator].home if options.has_key?(:locator)
		home	||=	Dir.home

		unless _Internal_Windows_Form.has_trailing_slash? home

			home = home + path[1].to_s
		end

		r = combine_paths(home, path[2..-1])
	elsif _Form_Windows.path_is_UNC? path

		r	=	path
	elsif _Form_Windows.path_is_absolute? path

		r	=	path
	elsif _Form_Windows.path_is_rooted? path

		pwd	=	nil
		pwd	||=	options[:pwd]
		pwd	||=	options[:locator].pwd if options.has_key?(:locator)
		pwd	||=	Dir.pwd

		r = pwd[0..1] + path
	else

		pwd	=	nil
		pwd	||=	options[:pwd]
		pwd	||=	options[:locator].pwd if options.has_key?(:locator)
		pwd	||=	Dir.pwd

		r = combine_paths(pwd, path, elide_single_dots: false)
	end

	if options[:make_canonical]

		r	=	make_path_canonical r
	else

		vol, rem, _	=	_Internal_Windows_Form.get_windows_volume r

		_Internal_Windows_Form.elide_redundant_path_name_separators! rem

		r			=	"#{vol}#{rem}"
	end

	return r
end

#make_path_canonical(path, **options) ⇒ Object

Converts a path into canonical form, which is to say that all possible dots directory parts are removed:

  • single-dot parts - ‘./’ or ‘.\’ - are all removed

  • double-dot parts - ‘../’ or ‘..\’ - are removed where they follow a

    non-dots directory part, or where they follow the root
    

NOTE: The behaviour of this method is undefined if any of the parts are malformed. See ::LibPath::Form::Windows::name_is_malformed?

Signature

  • Parameters:

    • path

      (String) The path to be evaluated. May not be nil

  • Options:

    • :make_slashes_canonical

      (boolean) Determines whether to

      additionally convert all forward slashes to backslashes
      


521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
# File 'lib/libpath/util/windows.rb', line 521

def make_path_canonical path, **options

	Diagnostics.check_string_parameter(path, "path") if $DEBUG

	if path.include?('/') && options[:make_slashes_canonical]

		path = path.tr '/', '\\'
	end

	return path unless '.' == path[-1] || path =~ /[.\\\/][\\\/]/

	_Form	=	::LibPath::Internal_::Windows::Form
	_Array	=	::LibPath::Internal_::Array

	path	=	path[0...-1] if '.' == path[-1] && _Form.char_is_path_name_separator?(path[-2])


	f0_path, f1_volume, f2_directory, f3_basename, _, _, f6_dir_parts, _ = _Form.split_path path

	if f6_dir_parts.empty?

		case f3_basename
		when '.'

			return "#{f1_volume}.\\"
		when '..'

			return "#{f1_volume}..\\"
		else

			return f0_path
		end
	end

	last_slash = nil

	case f3_basename
	when '.', '..'

		f6_dir_parts	<<	f3_basename + '\\'
		basename		=	nil
	when nil

		last_slash		=	_Form.get_trailing_slash(f2_directory) || '\\'
	else

		basename		=	f3_basename
	end

	is_rooted	=	_Form.char_is_path_name_separator?(f2_directory[0])

	new_parts	=	f6_dir_parts.dup
	new_parts.reject! { |p| '.\\' == p || './' == p }
	ix_nodots	=	new_parts.index { |p| '../' != p && '..\\' != p } || new_parts.size
	ix_2dots	=	_Array.index2(new_parts, '../', '..\\', ix_nodots)

	return "#{f1_volume}#{new_parts.join}#{basename}" unless new_parts.size != f6_dir_parts.size || ix_2dots

	while (ix_2dots || 0) > 0

		new_parts.delete_at(ix_2dots - 0)
		new_parts.delete_at(ix_2dots - 1) if ix_2dots != 1 || !is_rooted

		ix_nodots	=	new_parts.index { |p| '../' != p && '..\\' != p } or break
		ix_2dots	=	_Array.index2(new_parts, '../', '..\\', ix_nodots)
	end

	if new_parts.empty? && (basename || '').empty?

		case f3_basename
		when nil, '.', '..'

			return '.' + (last_slash || '\\').to_s
		else

			return '.'
		end
		return '.' + last_slash.to_s
	end

	return f1_volume.to_s + new_parts.join('') + basename.to_s
end