1########################################################################### 2# _ _ ____ _ 3# Project ___| | | | _ \| | 4# / __| | | | |_) | | 5# | (__| |_| | _ <| |___ 6# \___|\___/|_| \_\_____| 7# 8# Copyright (C) 2016 - 2021, 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.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 376 my $cur_dir; 377 # MSys shell has built-in command. 378 if($^O eq 'msys') { 379 $cur_dir = `bash -c 'pwd -L'`; 380 } 381 else { 382 $cur_dir = `pwd -L`; 383 } 384 if($? != 0) { 385 warn "Can't determine current working directory.\n"; 386 return undef; 387 } 388 chomp($cur_dir); 389 390 $path = $cur_dir . '/' . $path; 391 } 392 393 # Resolve relative dirs. 394 $path = normalize_path($path); 395 return undef unless defined $path; 396 397 if($^O eq 'msys') { 398 # Msys transforms automatically path to Windows native form in staring 399 # program parameters if program is not Msys-based. 400 $path = do_msys_transform($path); 401 return undef unless defined $path; 402 403 # Replace any back and duplicated slashes with single forward slashes. 404 $path =~ s{[\\/]+}{/}g; 405 return $path; 406 } 407 # OS is Windows, but not Msys, path is absolute, path is not in Win32 408 # form and 'cygpath' is not available. 409 410 return do_dumb_guessed_transform($path); 411} 412 413# Internal function. Converts given Unix-style absolute path to Win32 format. 414sub simple_transform_win32_to_unix; 415 416####################################################################### 417# Converts given path to build system format absolute path, i.e. to 418# Msys/Cygwin Unix-style absolute format on Windows platform. Both 419# relative and absolute formats are supported for input. 420# 421sub build_sys_abs_path { 422 my ($path) = @_; 423 424 unless(os_is_win()) { 425 # Convert path to absolute form. 426 $path = Cwd::abs_path($path); 427 428 # Do not process further on non-Windows platforms. 429 return $path; 430 } 431 432 if($path =~ m{^([a-zA-Z]):($|[^/\\].*$)}) { 433 # Path is single drive with colon or relative path on Win32 drive. 434 # ('C:' or 'C:path') 435 # This kind of relative path is not processed correctly by 'cygpath'. 436 # Get specified drive letter 437 438 # Resolve relative dirs in Win32-style path or paths like 'D:/../c/' 439 # will be resolved incorrectly. 440 # Replace any possible back slashes with forward slashes, 441 # remove any duplicated slashes. 442 $path = get_abs_path_on_win32_drive($1, $2); 443 return undef unless defined $path; 444 445 return simple_transform_win32_to_unix($path); 446 } 447 elsif($path eq '') { 448 # Path is empty string. Return current directory. 449 # Empty string processed correctly by 'cygpath'. 450 451 # MSys shell has built-in command. 452 if($^O eq 'msys') { 453 chomp($path = `bash -c 'pwd -L'`); 454 } 455 else { 456 chomp($path = `pwd -L`); 457 } 458 if($? != 0) { 459 warn "Can't determine Unix-style current working directory.\n"; 460 return undef; 461 } 462 463 # Add final slash if not at root dir. 464 $path .= '/' if length($path) > 2; 465 return $path; 466 } 467 elsif(should_use_cygpath()) { 468 # 'cygpath' is available - use it. 469 470 my $has_final_slash = ($path =~ m{[\\/]$}); 471 472 # Resolve relative directories, as they may be not resolved for 473 # Unix-style paths. 474 # Remove duplicated slashes, as they may be not processed. 475 $path = normalize_path($path); 476 return undef unless defined $path; 477 478 # Use 'cygpath', '-u' means Unix-stile path, 479 # '-a' means absolute path 480 chomp($path = `cygpath -u -a '$path'`); 481 if($? != 0) { 482 warn "Can't resolve path by usung \"cygpath\".\n"; 483 return undef; 484 } 485 486 # 'cygpath' removes last slash if path is root dir on Win32 drive. 487 # Restore it. 488 $path .= '/' if($has_final_slash && 489 substr($path, length($path) - 1, 1) ne '/'); 490 491 return $path 492 } 493 elsif($path =~ m{^[a-zA-Z]:[/\\]}) { 494 # Path is already in Win32 form. ('C:\path') 495 496 # Resolve relative dirs in Win32-style path otherwise paths 497 # like 'D:/../c/' will be resolved incorrectly. 498 # Replace any possible back slashes with forward slashes, 499 # remove any duplicated slashes. 500 $path = normalize_path($path); 501 return undef unless defined $path; 502 503 return simple_transform_win32_to_unix($path); 504 } 505 elsif(substr($path, 0, 1) eq '\\') { 506 # Path is directory or filename on Win32 current drive. ('\Windows') 507 508 my $w32drive = get_win32_current_drive(); 509 return undef unless defined $w32drive; 510 511 # Combine drive and path. 512 # Resolve relative dirs in Win32-style path or paths like 'D:/../c/' 513 # will be resolved incorrectly. 514 # Replace any possible back slashes with forward slashes, 515 # remove any duplicated slashes. 516 $path = normalize_path($w32drive . $path); 517 return undef unless defined $path; 518 519 return simple_transform_win32_to_unix($path); 520 } 521 522 # Path is not in any Win32 form. 523 unless (substr($path, 0, 1) eq '/') { 524 # Path in relative form. Resolve relative directories in Unix form 525 # *BEFORE* converting to Win32 form otherwise paths like 526 # '../../../cygdrive/c/windows' will not be resolved. 527 528 my $cur_dir; 529 # MSys shell has built-in command. 530 if($^O eq 'msys') { 531 $cur_dir = `bash -c 'pwd -L'`; 532 } 533 else { 534 $cur_dir = `pwd -L`; 535 } 536 if($? != 0) { 537 warn "Can't determine current working directory.\n"; 538 return undef; 539 } 540 chomp($cur_dir); 541 542 $path = $cur_dir . '/' . $path; 543 } 544 545 return normalize_path($path); 546} 547 548####################################################################### 549# Performs path "normalization": all slashes converted to forward 550# slashes (except leading slash), all duplicated slashes are replaced 551# with single slashes, all relative directories ('./' and '../') are 552# resolved if possible. 553# Path processed as string, directories are not checked for presence so 554# path for not yet existing directory can be "normalized". 555# 556sub normalize_path { 557 my ($path) = @_; 558 559 # Don't process empty paths. 560 return $path if $path eq ''; 561 562 unless($path =~ m{(?:^|\\|/)\.{1,2}(?:\\|/|$)}) { 563 # Speed up processing of simple paths. 564 my $first_char = substr($path, 0, 1); 565 $path =~ s{[\\/]+}{/}g; 566 # Restore starting backslash if any. 567 substr($path, 0, 1) = $first_char; 568 return $path; 569 } 570 571 my @arr; 572 my $prefix; 573 my $have_root = 0; 574 575 # Check whether path starts from Win32 drive. ('C:path' or 'C:\path') 576 if($path =~ m{^([a-zA-Z]:(/|\\)?)(.*$)}) { 577 $prefix = $1; 578 $have_root = 1 if defined $2; 579 # Process path separately from drive letter. 580 @arr = split(m{\/|\\}, $3); 581 # Replace backslash with forward slash if required. 582 substr($prefix, 2, 1) = '/' if $have_root; 583 } 584 else { 585 if($path =~ m{^(\/|\\)}) { 586 $have_root = 1; 587 $prefix = $1; 588 } 589 else { 590 $prefix = ''; 591 } 592 @arr = split(m{\/|\\}, $path); 593 } 594 595 my $p = 0; 596 my @res; 597 598 for my $el (@arr) { 599 if(length($el) == 0 || $el eq '.') { 600 next; 601 } 602 elsif($el eq '..' && @res > 0 && $res[$#res] ne '..') { 603 pop @res; 604 next; 605 } 606 push @res, $el; 607 } 608 if($have_root && @res > 0 && $res[0] eq '..') { 609 warn "Error processing path \"$path\": " . 610 "Parent directory of root directory does not exist!\n"; 611 return undef; 612 } 613 614 my $ret = $prefix . join('/', @res); 615 $ret .= '/' if($path =~ m{\\$|/$} && scalar @res > 0); 616 617 return $ret; 618} 619 620# Internal function. Converts path by using Msys's built-in 621# transformation. 622sub do_msys_transform { 623 my ($path) = @_; 624 return undef if $^O ne 'msys'; 625 return $path if $path eq ''; 626 627 # Remove leading double forward slashes, as they turn off Msys 628 # transforming. 629 $path =~ s{^/[/\\]+}{/}; 630 631 # Msys transforms automatically path to Windows native form in staring 632 # program parameters if program is not Msys-based. 633 # Note: already checked that $path is non-empty. 634 $path = `cmd //c echo '$path'`; 635 if($? != 0) { 636 warn "Can't transform path into Win32 form by using Msys" . 637 "internal transformation.\n"; 638 return undef; 639 } 640 641 # Remove double quotes, they are added for paths with spaces, 642 # remove both '\r' and '\n'. 643 $path =~ s{^\"|\"$|\"\r|\n|\r}{}g; 644 645 return $path; 646} 647 648# Internal function. Gets two parameters: first parameter must be single 649# drive letter ('c'), second optional parameter is path relative to drive's 650# current working directory. Returns Win32 absolute normalized path. 651sub get_abs_path_on_win32_drive { 652 my ($drv, $rel_path) = @_; 653 my $res; 654 655 # Get current directory on specified drive. 656 # "/c;" is compatible with both Msys and Cygwin. 657 my $cur_dir_on_drv = `cmd "/c;" echo %=$drv:%`; 658 if($? != 0) { 659 warn "Can't determine Win32 current directory on drive $drv:.\n"; 660 return undef; 661 } 662 663 if($cur_dir_on_drv =~ m{^[%]}) { 664 # Current directory on drive is not set, default is 665 # root directory. 666 667 $res = ucfirst($drv) . ':/'; 668 } 669 else { 670 # Current directory on drive was set. 671 # Remove both '\r' and '\n'. 672 $cur_dir_on_drv =~ s{\n|\r}{}g; 673 674 # Append relative path part. 675 $res = $cur_dir_on_drv . '/'; 676 } 677 $res .= $rel_path if defined $rel_path; 678 679 # Replace any possible back slashes with forward slashes, 680 # remove any duplicated slashes, resolve relative dirs. 681 return normalize_path($res); 682} 683 684# Internal function. Tries to find or guess Win32 version of given 685# absolute Unix-style path. Other types of paths are not supported. 686# Returned paths contain only single forward slashes (no back and 687# duplicated slashes). 688# Last resort. Used only when other transformations are not available. 689sub do_dumb_guessed_transform { 690 my ($path) = @_; 691 692 # Replace any possible back slashes and duplicated forward slashes 693 # with single forward slashes. 694 $path =~ s{[/\\]+}{/}g; 695 696 # Empty path is not valid. 697 return undef if (length($path) == 0); 698 699 # RE to find Win32 drive letter 700 my $drv_ltr_re = drives_mounted_on_cygdrive() ? 701 qr{^/cygdrive/([a-zA-Z])($|/.*$)} : 702 qr{^/([a-zA-Z])($|/.*$)}; 703 704 # Check path whether path is Win32 directly mapped drive and try to 705 # transform it assuming that drive letter is matched to Win32 drive letter. 706 if($path =~ m{$drv_ltr_re}) { 707 return ucfirst($1) . ':/' if(length($2) == 0); 708 return ucfirst($1) . ':' . $2; 709 } 710 711 # This may be some custom mapped path. ('/mymount/path') 712 713 # Must check longest possible path component as subdir can be mapped to 714 # different directory. For example '/usr/bin/' can be mapped to '/bin/' or 715 # '/bin/' can be mapped to '/usr/bin/'. 716 my $check_path = $path; 717 my $path_tail = ''; 718 do { 719 if(-d $check_path) { 720 my $res = 721 `(cd "$check_path" && cmd /c "echo %__CD__%") 2>/dev/null`; 722 if($? == 0 && substr($path, 0, 1) ne '%') { 723 # Remove both '\r' and '\n'. 724 $res =~ s{\n|\r}{}g; 725 726 # Replace all back slashes with forward slashes. 727 $res =~ s{\\}{/}g; 728 729 if(length($path_tail) > 0) { 730 return $res . $path_tail; 731 } 732 else { 733 $res =~ s{/$}{} unless $check_path =~ m{/$}; 734 return $res; 735 } 736 } 737 } 738 if($check_path =~ m{(^.*/)([^/]+/*)}) { 739 $check_path = $1; 740 $path_tail = $2 . $path_tail; 741 } 742 else { 743 # Shouldn't happens as root '/' directory should always 744 # be resolvable. 745 warn "Can't determine Win32 directory for path \"$path\".\n"; 746 return undef; 747 } 748 } while(1); 749} 750 751 752# Internal function. Converts given Unix-style absolute path to Win32 format. 753sub simple_transform_win32_to_unix { 754 my ($path) = @_; 755 756 if(should_use_cygpath()) { 757 # 'cygpath' gives precise result. 758 my $res; 759 chomp($res = `cygpath -a -u '$path'`); 760 if($? != 0) { 761 warn "Can't determine Unix-style directory for Win32 " . 762 "directory \"$path\".\n"; 763 return undef; 764 } 765 766 # 'cygpath' removes last slash if path is root dir on Win32 drive. 767 $res .= '/' if(substr($res, length($res) - 1, 1) ne '/' && 768 $path =~ m{[/\\]$}); 769 return $res; 770 } 771 772 # 'cygpath' is not available, use guessed transformation. 773 unless($path =~ s{^([a-zA-Z]):(?:/|\\)}{/\l$1/}) { 774 warn "Can't determine Unix-style directory for Win32 " . 775 "directory \"$path\".\n"; 776 return undef; 777 } 778 779 $path = '/cygdrive' . $path if(drives_mounted_on_cygdrive()); 780 return $path; 781} 782 7831; # End of module 784