blob: 10a87aaf990b9eb2cc98223ee6eb4796f26cd9d5 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001###########################################################################
2# _ _ ____ _
3# Project ___| | | | _ \| |
4# / __| | | | |_) | |
5# | (__| |_| | _ <| |___
6# \___|\___/|_| \_\_____|
7#
8# Copyright (C) 2016, Evgeny Grin (Karlson2k), <k2k@narod.ru>.
9#
10# This software is licensed as described in the file COPYING, which
11# you should have received as part of this distribution. The terms
12# are also available at https://curl.haxx.se/docs/copyright.html.
13#
14# You may opt to use, copy, modify, merge, publish, distribute and/or sell
15# copies of the Software, and permit persons to whom the Software is
16# furnished to do so, under the terms of the COPYING file.
17#
18# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19# KIND, either express or implied.
20#
21###########################################################################
22
23# This Perl package helps with path transforming when running curl tests on
24# Win32 platform with Msys or Cygwin.
25# Three main functions 'sys_native_abs_path', 'sys_native_path' and
26# 'build_sys_abs_path' autodetect format of given pathnames. Following formats
27# are supported:
28# (1) /some/path - absolute path in Unix-style
29# (2) D:/some/path - absolute path in Win32-style
30# (3) some/path - relative path
31# (4) D:some/path - path relative to current directory on Win32 drive (paths
32# like 'D:' are treated as 'D:./') (*)
33# (5) \some/path - path from root directory on current Win32 drive (*)
34# All forward '/' and back '\' slashes are treated identically except leading
35# slash in forms (1) and (5).
36# Forward slashes are simpler processed in Perl, do not require extra escaping
37# for shell (unlike back slashes) and accepted by Win32 native programs, so
38# all functions return paths with only forward slashes except
39# 'sys_native_path' which returns paths with first forward slash for form (5).
40# All returned paths don't contain any duplicated slashes, only single slashes
41# are used as directory separators on output.
42# On non-Windows platforms functions acts as transparent wrappers for similar
43# Perl's functions or return unmodified string (depending on functionality),
44# so all functions can be unconditionally used on all platforms.
45#
46# (*) CAUTION! Forms (4) and (5) are not recommended to use as they can be
47# interpreted incorrectly in Perl and Msys/Cygwin environment have low
48# control on Win32 current drive and Win32 current path on specific drive.
49
50
51package pathhelp;
52use strict;
53use warnings;
54use Cwd 'abs_path';
55
56BEGIN {
57 require Exporter;
58
59 our @ISA = qw(Exporter);
60
61 our @EXPORT = qw(
62 sys_native_abs_path
63 sys_native_path
64 );
65
66 our @EXPORT_OK = qw(
67 build_sys_abs_path
68 sys_native_current_path
69 normalize_path
70 os_is_win
71 $use_cygpath
72 should_use_cygpath
73 drives_mounted_on_cygdrive
74 );
75}
76
77
78#######################################################################
79# Block for cached static variables
80#
81{
82 # Cached static variable, Perl 5.0-compatible.
83 my $is_win = $^O eq 'MSWin32'
84 || $^O eq 'cygwin'
85 || $^O eq 'msys';
86
87 # Returns boolean true if OS is any form of Windows.
88 sub os_is_win {
89 return $is_win;
90 }
91
92 # Cached static variable, Perl 5.0-compatible.
93 my $cygdrive_present;
94
95 # Returns boolean true if Win32 drives mounted with '/cygdrive/' prefix.
96 sub drives_mounted_on_cygdrive {
97 return $cygdrive_present if defined $cygdrive_present;
98 $cygdrive_present = ((-e '/cygdrive/') && (-d '/cygdrive/')) ? 1 : 0;
99 return $cygdrive_present;
100 }
101}
102
103our $use_cygpath; # Only for Win32:
104 # undef - autodetect
105 # 1 - use cygpath
106 # 0 - do not use cygpath
107
108# Returns boolean true if 'cygpath' utility should be used for path conversion.
109sub should_use_cygpath {
110 unless (os_is_win()) {
111 $use_cygpath = 0;
112 return 0;
113 }
114 return $use_cygpath if defined $use_cygpath;
115
116 $use_cygpath = (qx{cygpath -u '.\\' 2>/dev/null} eq "./\n" && $? == 0);
117
118 return $use_cygpath;
119}
120
121#######################################################################
122# Performs path "normalization": all slashes converted to forward
123# slashes (except leading slash), all duplicated slashes are replaced
124# with single slashes, all relative directories ('./' and '../') are
125# resolved if possible.
126# Path processed as string, directories are not checked for presence so
127# path for not yet existing directory can be "normalized".
128#
129sub normalize_path;
130
131#######################################################################
132# Returns current working directory in Win32 format on Windows.
133#
134sub sys_native_current_path {
135 return Cwd::getcwd() unless os_is_win();
136
137 my $cur_dir;
138 if($^O eq 'msys') {
139 # MSys shell has built-in command.
140 chomp($cur_dir = `bash -c 'pwd -W'`);
141 if($? != 0) {
142 warn "Can't determine Win32 current directory.\n";
143 return undef;
144 }
145 # Add final slash if required.
146 $cur_dir .= '/' if length($cur_dir) > 3;
147 }
148 else {
149 # Do not use 'cygpath' - it falsely succeed on paths like '/cygdrive'.
150 $cur_dir = `cmd "/c;" echo %__CD__%`;
151 if($? != 0 || substr($cur_dir, 0, 1) eq '%') {
152 warn "Can't determine Win32 current directory.\n";
153 return undef;
154 }
155 # Remove both '\r' and '\n'.
156 $cur_dir =~ s{\n|\r}{}g;
157
158 # Replace back slashes with forward slashes.
159 $cur_dir =~ s{\\}{/}g;
160 }
161 return $cur_dir;
162}
163
164#######################################################################
165# Returns Win32 current drive letter with colon.
166#
167sub get_win32_current_drive {
168 # Notice parameter "/c;" - it's required to turn off Msys's
169 # transformation of '/c' and compatible with Cygwin.
170 my $drive_letter = `cmd "/c;" echo %__CD__:~0,2%`;
171 if($? != 0 || substr($drive_letter, 1, 1) ne ':') {
172 warn "Can't determine current Win32 drive letter.\n";
173 return undef;
174 }
175
176 return substr($drive_letter, 0, 2);
177}
178
179# Internal function. Converts path by using Msys's built-in transformation.
180# Returned path may contain duplicated and back slashes.
181sub do_msys_transform;
182
183# Internal function. Gets two parameters: first parameter must be single
184# drive letter ('c'), second optional parameter is path relative to drive's
185# current working directory. Returns Win32 absolute normalized path.
186sub get_abs_path_on_win32_drive;
187
188# Internal function. Tries to find or guess Win32 version of given
189# absolute Unix-style path. Other types of paths are not supported.
190# Returned paths contain only single forward slashes (no back and
191# duplicated slashes).
192# Last resort. Used only when other transformations are not available.
193sub do_dumb_guessed_transform;
194
195#######################################################################
196# Converts given path to system native format, i.e. to Win32 format on
197# Windows platform. Relative paths converted to relative, absolute
198# paths converted to absolute.
199#
200sub sys_native_path {
201 my ($path) = @_;
202
203 # Return untouched on non-Windows platforms.
204 return $path unless (os_is_win());
205
206 # Do not process empty path.
207 return $path if ($path eq '');
208
209 if($path =~ s{^([a-zA-Z]):$}{\u$1:}) {
210 # Path is single drive with colon. (C:)
211 # This type of paths is not processed correctly by 'cygpath'.
212 # WARNING!
213 # Be careful, this relative path can be accidentally transformed
214 # into wrong absolute path by adding to it some '/dirname' with
215 # slash at font.
216 return $path;
217 }
218 elsif($path =~ m{^\\} || $path =~ m{^[a-zA-Z]:[^/\\]}) {
219 # Path is a directory or filename on Win32 current drive or relative
220 # path on current directory on specific Win32 drive.
221 # ('\path' or 'D:path')
222 # First type of paths is not processed by Msys transformation and
223 # resolved to absolute path by 'cygpath'.
224 # Second type is not processed by Msys transformation and may be
225 # incorrectly processed by 'cygpath' (for paths like 'D:..\../.\')
226
227 my $first_char = ucfirst(substr($path, 0, 1));
228
229 # Replace any back and duplicated slashes with single forward slashes.
230 $path =~ s{[\\/]+}{/}g;
231
232 # Convert leading slash back to forward slash to indicate
233 # directory on Win32 current drive or capitalize drive letter.
234 substr($path, 0, 1) = $first_char;
235 return $path;
236 }
237 elsif(should_use_cygpath()) {
238 # 'cygpath' is available - use it.
239
240 # Remove leading duplicated forward and back slashes, as they may
241 # prevent transforming and may be not processed.
242 $path =~ s{^([\\/])[\\/]+}{$1}g;
243
244 my $has_final_slash = ($path =~ m{[/\\]$});
245
246 # Use 'cygpath', '-m' means Win32 path with forward slashes.
247 chomp($path = `cygpath -m '$path'`);
248 if ($? != 0) {
249 warn "Can't convert path by \"cygpath\".\n";
250 return undef;
251 }
252
253 # 'cygpath' may remove last slash for existing directories.
254 $path .= '/' if($has_final_slash);
255
256 # Remove any duplicated forward slashes (added by 'cygpath' for root
257 # directories)
258 $path =~ s{//+}{/}g;
259
260 return $path;
261 }
262 elsif($^O eq 'msys') {
263 # Msys transforms automatically path to Windows native form in staring
264 # program parameters if program is not Msys-based.
265
266 $path = do_msys_transform($path);
267 return undef unless defined $path;
268
269 # Capitalize drive letter for Win32 paths.
270 $path =~ s{^([a-z]:)}{\u$1};
271
272 # Replace any back and duplicated slashes with single forward slashes.
273 $path =~ s{[\\/]+}{/}g;
274 return $path;
275 }
276 elsif($path =~ s{^([a-zA-Z]):[/\\]}{\u$1:/}) {
277 # Path is already in Win32 form. ('C:\path')
278
279 # Replace any back and duplicated slashes with single forward slashes.
280 $path =~ s{[\\/]+}{/}g;
281 return $path;
282 }
283 elsif($path !~ m{^/}) {
284 # Path is in relative form. ('path/name', './path' or '../path')
285
286 # Replace any back and duplicated slashes with single forward slashes.
287 $path =~ s{[\\/]+}{/}g;
288 return $path;
289 }
290
291 # OS is Windows, but not Msys, path is absolute, path is not in Win32
292 # form and 'cygpath' is not available.
293 return do_dumb_guessed_transform($path);
294}
295
296#######################################################################
297# Converts given path to system native absolute path, i.e. to Win32
298# absolute format on Windows platform. Both relative and absolute
299# formats are supported for input.
300#
301sub sys_native_abs_path {
302 my ($path) = @_;
303
304 unless(os_is_win()) {
305 # Convert path to absolute form.
306 $path = Cwd::abs_path($path);
307
308 # Do not process further on non-Windows platforms.
309 return $path;
310 }
311
312 if($path =~ m{^([a-zA-Z]):($|[^/\\].*$)}) {
313 # Path is single drive with colon or relative path on Win32 drive.
314 # ('C:' or 'C:path')
315 # This kind of relative path is not processed correctly by 'cygpath'.
316 # Get specified drive letter
317 return get_abs_path_on_win32_drive($1, $2);
318 }
319 elsif($path eq '') {
320 # Path is empty string. Return current directory.
321 # Empty string processed correctly by 'cygpath'.
322
323 return sys_native_current_path();
324 }
325 elsif(should_use_cygpath()) {
326 # 'cygpath' is available - use it.
327
328 my $has_final_slash = ($path =~ m{[\\/]$});
329
330 # Remove leading duplicated forward and back slashes, as they may
331 # prevent transforming and may be not processed.
332 $path =~ s{^([\\/])[\\/]+}{$1}g;
333
334 print "Inter result: \"$path\"\n";
335 # Use 'cygpath', '-m' means Win32 path with forward slashes,
336 # '-a' means absolute path
337 chomp($path = `cygpath -m -a '$path'`);
338 if($? != 0) {
339 warn "Can't resolve path by usung \"cygpath\".\n";
340 return undef;
341 }
342
343 # 'cygpath' may remove last slash for existing directories.
344 $path .= '/' if($has_final_slash);
345
346 # Remove any duplicated forward slashes (added by 'cygpath' for root
347 # directories)
348 $path =~ s{//+}{/}g;
349
350 return $path
351 }
352 elsif($path =~ s{^([a-zA-Z]):[/\\]}{\u$1:/}) {
353 # Path is already in Win32 form. ('C:\path')
354
355 # Replace any possible back slashes with forward slashes,
356 # remove any duplicated slashes, resolve relative dirs.
357 return normalize_path($path);
358 }
359 elsif(substr($path, 0, 1) eq '\\' ) {
360 # Path is directory or filename on Win32 current drive. ('\Windows')
361
362 my $w32drive = get_win32_current_drive();
363 return undef unless defined $w32drive;
364
365 # Combine drive and path.
366 # Replace any possible back slashes with forward slashes,
367 # remove any duplicated slashes, resolve relative dirs.
368 return normalize_path($w32drive . $path);
369 }
370
371 unless (substr($path, 0, 1) eq '/') {
372 # Path is in relative form. Resolve relative directories in Unix form
373 # *BEFORE* converting to Win32 form otherwise paths like
374 # '../../../cygdrive/c/windows' will not be resolved.
375 my $cur_dir = `pwd -L`;
376 if($? != 0) {
377 warn "Can't determine current working directory.\n";
378 return undef;
379 }
380 chomp($cur_dir);
381
382 $path = $cur_dir . '/' . $path;
383 }
384
385 # Resolve relative dirs.
386 $path = normalize_path($path);
387 return undef unless defined $path;
388
389 if($^O eq 'msys') {
390 # Msys transforms automatically path to Windows native form in staring
391 # program parameters if program is not Msys-based.
392 $path = do_msys_transform($path);
393 return undef unless defined $path;
394
395 # Replace any back and duplicated slashes with single forward slashes.
396 $path =~ s{[\\/]+}{/}g;
397 return $path;
398 }
399 # OS is Windows, but not Msys, path is absolute, path is not in Win32
400 # form and 'cygpath' is not available.
401
402 return do_dumb_guessed_transform($path);
403}
404
405# Internal function. Converts given Unix-style absolute path to Win32 format.
406sub simple_transform_win32_to_unix;
407
408#######################################################################
409# Converts given path to build system format absolute path, i.e. to
410# Msys/Cygwin Unix-style absolute format on Windows platform. Both
411# relative and absolute formats are supported for input.
412#
413sub build_sys_abs_path {
414 my ($path) = @_;
415
416 unless(os_is_win()) {
417 # Convert path to absolute form.
418 $path = Cwd::abs_path($path);
419
420 # Do not process further on non-Windows platforms.
421 return $path;
422 }
423
424 if($path =~ m{^([a-zA-Z]):($|[^/\\].*$)}) {
425 # Path is single drive with colon or relative path on Win32 drive.
426 # ('C:' or 'C:path')
427 # This kind of relative path is not processed correctly by 'cygpath'.
428 # Get specified drive letter
429
430 # Resolve relative dirs in Win32-style path or paths like 'D:/../c/'
431 # will be resolved incorrectly.
432 # Replace any possible back slashes with forward slashes,
433 # remove any duplicated slashes.
434 $path = get_abs_path_on_win32_drive($1, $2);
435 return undef unless defined $path;
436
437 return simple_transform_win32_to_unix($path);
438 }
439 elsif($path eq '') {
440 # Path is empty string. Return current directory.
441 # Empty string processed correctly by 'cygpath'.
442
443 chomp($path = `pwd -L`);
444 if($? != 0) {
445 warn "Can't determine Unix-style current working directory.\n";
446 return undef;
447 }
448
449 # Add final slash if not at root dir.
450 $path .= '/' if length($path) > 2;
451 return $path;
452 }
453 elsif(should_use_cygpath()) {
454 # 'cygpath' is avalable - use it.
455
456 my $has_final_slash = ($path =~ m{[\\/]$});
457
458 # Resolve relative directories, as they may be not resolved for
459 # Unix-style paths.
460 # Remove duplicated slashes, as they may be not processed.
461 $path = normalize_path($path);
462 return undef unless defined $path;
463
464 # Use 'cygpath', '-u' means Unix-stile path,
465 # '-a' means absolute path
466 chomp($path = `cygpath -u -a '$path'`);
467 if($? != 0) {
468 warn "Can't resolve path by usung \"cygpath\".\n";
469 return undef;
470 }
471
472 # 'cygpath' removes last slash if path is root dir on Win32 drive.
473 # Restore it.
474 $path .= '/' if($has_final_slash &&
475 substr($path, length($path) - 1, 1) ne '/');
476
477 return $path
478 }
479 elsif($path =~ m{^[a-zA-Z]:[/\\]}) {
480 # Path is already in Win32 form. ('C:\path')
481
482 # Resolve relative dirs in Win32-style path otherwise paths
483 # like 'D:/../c/' will be resolved incorrectly.
484 # Replace any possible back slashes with forward slashes,
485 # remove any duplicated slashes.
486 $path = normalize_path($path);
487 return undef unless defined $path;
488
489 return simple_transform_win32_to_unix($path);
490 }
491 elsif(substr($path, 0, 1) eq '\\') {
492 # Path is directory or filename on Win32 current drive. ('\Windows')
493
494 my $w32drive = get_win32_current_drive();
495 return undef unless defined $w32drive;
496
497 # Combine drive and path.
498 # Resolve relative dirs in Win32-style path or paths like 'D:/../c/'
499 # will be resolved incorrectly.
500 # Replace any possible back slashes with forward slashes,
501 # remove any duplicated slashes.
502 $path = normalize_path($w32drive . $path);
503 return undef unless defined $path;
504
505 return simple_transform_win32_to_unix($path);
506 }
507
508 # Path is not in any Win32 form.
509 unless (substr($path, 0, 1) eq '/') {
510 # Path in relative form. Resolve relative directories in Unix form
511 # *BEFORE* converting to Win32 form otherwise paths like
512 # '../../../cygdrive/c/windows' will not be resolved.
513 my $cur_dir = `pwd -L`;
514 if($? != 0) {
515 warn "Can't determine current working directory.\n";
516 return undef;
517 }
518 chomp($cur_dir);
519
520 $path = $cur_dir . '/' . $path;
521 }
522
523 return normalize_path($path);
524}
525
526#######################################################################
527# Performs path "normalization": all slashes converted to forward
528# slashes (except leading slash), all duplicated slashes are replaced
529# with single slashes, all relative directories ('./' and '../') are
530# resolved if possible.
531# Path processed as string, directories are not checked for presence so
532# path for not yet existing directory can be "normalized".
533#
534sub normalize_path {
535 my ($path) = @_;
536
537 # Don't process empty paths.
538 return $path if $path eq '';
539
540 unless($path =~ m{(?:^|\\|/)\.{1,2}(?:\\|/|$)}) {
541 # Speed up processing of simple paths.
542 my $first_char = substr($path, 0, 1);
543 $path =~ s{[\\/]+}{/}g;
544 # Restore starting backslash if any.
545 substr($path, 0, 1) = $first_char;
546 return $path;
547 }
548
549 my @arr;
550 my $prefix;
551 my $have_root = 0;
552
553 # Check whether path starts from Win32 drive. ('C:path' or 'C:\path')
554 if($path =~ m{^([a-zA-Z]:(/|\\)?)(.*$)}) {
555 $prefix = $1;
556 $have_root = 1 if defined $2;
557 # Process path separately from drive letter.
558 @arr = split(m{\/|\\}, $3);
559 # Replace backslash with forward slash if required.
560 substr($prefix, 2, 1) = '/' if $have_root;
561 }
562 else {
563 if($path =~ m{^(\/|\\)}) {
564 $have_root = 1;
565 $prefix = $1;
566 }
567 else {
568 $prefix = '';
569 }
570 @arr = split(m{\/|\\}, $path);
571 }
572
573 my $p = 0;
574 my @res;
575
576 for my $el (@arr) {
577 if(length($el) == 0 || $el eq '.') {
578 next;
579 }
580 elsif($el eq '..' && @res > 0 && $res[$#res] ne '..') {
581 pop @res;
582 next;
583 }
584 push @res, $el;
585 }
586 if($have_root && @res > 0 && $res[0] eq '..') {
587 warn "Error processing path \"$path\": " .
588 "Parent directory of root directory does not exist!\n";
589 return undef;
590 }
591
592 my $ret = $prefix . join('/', @res);
593 $ret .= '/' if($path =~ m{\\$|/$} && scalar @res > 0);
594
595 return $ret;
596}
597
598# Internal function. Converts path by using Msys's built-in
599# transformation.
600sub do_msys_transform {
601 my ($path) = @_;
602 return undef if $^O ne 'msys';
603 return $path if $path eq '';
604
605 # Remove leading double forward slashes, as they turn off Msys
606 # transforming.
607 $path =~ s{^/[/\\]+}{/};
608
609 # Msys transforms automatically path to Windows native form in staring
610 # program parameters if program is not Msys-based.
611 # Note: already checked that $path is non-empty.
612 $path = `cmd //c echo '$path'`;
613 if($? != 0) {
614 warn "Can't transform path into Win32 form by using Msys" .
615 "internal transformation.\n";
616 return undef;
617 }
618
619 # Remove double quotes, they are added for paths with spaces,
620 # remove both '\r' and '\n'.
621 $path =~ s{^\"|\"$|\"\r|\n|\r}{}g;
622
623 return $path;
624}
625
626# Internal function. Gets two parameters: first parameter must be single
627# drive letter ('c'), second optional parameter is path relative to drive's
628# current working directory. Returns Win32 absolute normalized path.
629sub get_abs_path_on_win32_drive {
630 my ($drv, $rel_path) = @_;
631 my $res;
632
633 # Get current directory on specified drive.
634 # "/c;" is compatible with both Msys and Cygwin.
635 my $cur_dir_on_drv = `cmd "/c;" echo %=$drv:%`;
636 if($? != 0) {
637 warn "Can't determine Win32 current directory on drive $drv:.\n";
638 return undef;
639 }
640
641 if($cur_dir_on_drv =~ m{^[%]}) {
642 # Current directory on drive is not set, default is
643 # root directory.
644
645 $res = ucfirst($drv) . ':/';
646 }
647 else {
648 # Current directory on drive was set.
649 # Remove both '\r' and '\n'.
650 $cur_dir_on_drv =~ s{\n|\r}{}g;
651
652 # Append relative path part.
653 $res = $cur_dir_on_drv . '/';
654 }
655 $res .= $rel_path if defined $rel_path;
656
657 # Replace any possible back slashes with forward slashes,
658 # remove any duplicated slashes, resolve relative dirs.
659 return normalize_path($res);
660}
661
662# Internal function. Tries to find or guess Win32 version of given
663# absolute Unix-style path. Other types of paths are not supported.
664# Returned paths contain only single forward slashes (no back and
665# duplicated slashes).
666# Last resort. Used only when other transformations are not available.
667sub do_dumb_guessed_transform {
668 my ($path) = @_;
669
670 # Replace any possible back slashes and duplicated forward slashes
671 # with single forward slashes.
672 $path =~ s{[/\\]+}{/}g;
673
674 # Empty path is not valid.
675 return undef if (length($path) == 0);
676
677 # RE to find Win32 drive letter
678 my $drv_ltr_re = drives_mounted_on_cygdrive() ?
679 qr{^/cygdrive/([a-zA-Z])($|/.*$)} :
680 qr{^/([a-zA-Z])($|/.*$)};
681
682 # Check path whether path is Win32 directly mapped drive and try to
683 # transform it assuming that drive letter is matched to Win32 drive letter.
684 if($path =~ m{$drv_ltr_re}) {
685 return ucfirst($1) . ':/' if(length($2) == 0);
686 return ucfirst($1) . ':' . $2;
687 }
688
689 # This may be some custom mapped path. ('/mymount/path')
690
691 # Must check longest possible path component as subdir can be mapped to
692 # different directory. For example '/usr/bin/' can be mapped to '/bin/' or
693 # '/bin/' can be mapped to '/usr/bin/'.
694 my $check_path = $path;
695 my $path_tail = '';
696 do {
697 if(-d $check_path) {
698 my $res =
699 `(cd "$check_path" && cmd /c "echo %__CD__%") 2>/dev/null`;
700 if($? == 0 && substr($path, 0, 1) ne '%') {
701 # Remove both '\r' and '\n'.
702 $res =~ s{\n|\r}{}g;
703
704 # Replace all back slashes with forward slashes.
705 $res =~ s{\\}{/}g;
706
707 if(length($path_tail) > 0) {
708 return $res . $path_tail;
709 }
710 else {
711 $res =~ s{/$}{} unless $check_path =~ m{/$};
712 return $res;
713 }
714 }
715 }
716 if($check_path =~ m{(^.*/)([^/]+/*)}) {
717 $check_path = $1;
718 $path_tail = $2 . $path_tail;
719 }
720 else {
721 # Shouldn't happens as root '/' directory should always
722 # be resolvable.
723 warn "Can't determine Win32 directory for path \"$path\".\n";
724 return undef;
725 }
726 } while(1);
727}
728
729
730# Internal function. Converts given Unix-style absolute path to Win32 format.
731sub simple_transform_win32_to_unix {
732 my ($path) = @_;
733
734 if(should_use_cygpath()) {
735 # 'cygpath' gives precise result.
736 my $res;
737 chomp($res = `cygpath -a -u '$path'`);
738 if($? != 0) {
739 warn "Can't determine Unix-style directory for Win32 " .
740 "directory \"$path\".\n";
741 return undef;
742 }
743
744 # 'cygpath' removes last slash if path is root dir on Win32 drive.
745 $res .= '/' if(substr($res, length($res) - 1, 1) ne '/' &&
746 $path =~ m{[/\\]$});
747 return $res;
748 }
749
750 # 'cygpath' is not available, use guessed transformation.
751 unless($path =~ s{^([a-zA-Z]):(?:/|\\)}{/\l$1/}) {
752 warn "Can't determine Unix-style directory for Win32 " .
753 "directory \"$path\".\n";
754 return undef;
755 }
756
757 $path = '/cygdrive' . $path if(drives_mounted_on_cygdrive());
758 return $path;
759}
760
7611; # End of module