| ########################################################################### | 
 | #                                  _   _ ____  _ | 
 | #  Project                     ___| | | |  _ \| | | 
 | #                             / __| | | | |_) | | | 
 | #                            | (__| |_| |  _ <| |___ | 
 | #                             \___|\___/|_| \_\_____| | 
 | # | 
 | # Copyright (C) 2016 - 2022, Evgeny Grin (Karlson2k), <k2k@narod.ru>. | 
 | # | 
 | # This software is licensed as described in the file COPYING, which | 
 | # you should have received as part of this distribution. The terms | 
 | # are also available at https://curl.se/docs/copyright.html. | 
 | # | 
 | # You may opt to use, copy, modify, merge, publish, distribute and/or sell | 
 | # copies of the Software, and permit persons to whom the Software is | 
 | # furnished to do so, under the terms of the COPYING file. | 
 | # | 
 | # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
 | # KIND, either express or implied. | 
 | # | 
 | # SPDX-License-Identifier: curl | 
 | # | 
 | ########################################################################### | 
 |  | 
 | # This Perl package helps with path transforming when running curl tests on | 
 | # Win32 platform with Msys or Cygwin. | 
 | # Three main functions 'sys_native_abs_path', 'sys_native_path' and | 
 | # 'build_sys_abs_path' autodetect format of given pathnames. Following formats | 
 | # are supported: | 
 | #  (1) /some/path   - absolute path in Unix-style | 
 | #  (2) D:/some/path - absolute path in Win32-style | 
 | #  (3) some/path    - relative path | 
 | #  (4) D:some/path  - path relative to current directory on Win32 drive (paths | 
 | #                     like 'D:' are treated as 'D:./') (*) | 
 | #  (5) \some/path   - path from root directory on current Win32 drive (*) | 
 | # All forward '/' and back '\' slashes are treated identically except leading | 
 | # slash in forms (1) and (5). | 
 | # Forward slashes are simpler processed in Perl, do not require extra escaping | 
 | # for shell (unlike back slashes) and accepted by Win32 native programs, so | 
 | # all functions return paths with only forward slashes except | 
 | # 'sys_native_path' which returns paths with first forward slash for form (5). | 
 | # All returned paths don't contain any duplicated slashes, only single slashes | 
 | # are used as directory separators on output. | 
 | # On non-Windows platforms functions acts as transparent wrappers for similar | 
 | # Perl's functions or return unmodified string (depending on functionality), | 
 | # so all functions can be unconditionally used on all platforms. | 
 | # | 
 | # (*) CAUTION! Forms (4) and (5) are not recommended to use as they can be | 
 | #     interpreted incorrectly in Perl and Msys/Cygwin environment have low | 
 | #     control on Win32 current drive and Win32 current path on specific drive. | 
 |  | 
 |  | 
 | package pathhelp; | 
 | use strict; | 
 | use warnings; | 
 | use Cwd 'abs_path'; | 
 |  | 
 | BEGIN { | 
 |     require Exporter; | 
 |  | 
 |     our @ISA    = qw(Exporter); | 
 |  | 
 |     our @EXPORT = qw( | 
 |       sys_native_abs_path | 
 |       sys_native_path | 
 |     ); | 
 |  | 
 |     our @EXPORT_OK = qw( | 
 |       build_sys_abs_path | 
 |       sys_native_current_path | 
 |       normalize_path | 
 |       os_is_win | 
 |       $use_cygpath | 
 |       should_use_cygpath | 
 |       drives_mounted_on_cygdrive | 
 |     ); | 
 | } | 
 |  | 
 |  | 
 | ####################################################################### | 
 | # Block for cached static variables | 
 | # | 
 | { | 
 |     # Cached static variable, Perl 5.0-compatible. | 
 |     my $is_win = $^O eq 'MSWin32' | 
 |               || $^O eq 'cygwin' | 
 |               || $^O eq 'msys'; | 
 |  | 
 |     # Returns boolean true if OS is any form of Windows. | 
 |     sub os_is_win { | 
 |         return $is_win; | 
 |     } | 
 |  | 
 |     # Cached static variable, Perl 5.0-compatible. | 
 |     my $cygdrive_present; | 
 |  | 
 |     # Returns boolean true if Win32 drives mounted with '/cygdrive/' prefix. | 
 |     sub drives_mounted_on_cygdrive { | 
 |         return $cygdrive_present if defined $cygdrive_present; | 
 |         $cygdrive_present = ((-e '/cygdrive/') && (-d '/cygdrive/')) ? 1 : 0; | 
 |         return $cygdrive_present; | 
 |     } | 
 | } | 
 |  | 
 | our $use_cygpath;    # Only for Win32: | 
 |                      #  undef - autodetect | 
 |                      #      1 - use cygpath | 
 |                      #      0 - do not use cygpath | 
 |  | 
 | # Returns boolean true if 'cygpath' utility should be used for path conversion. | 
 | sub should_use_cygpath { | 
 |     unless (os_is_win()) { | 
 |         $use_cygpath = 0; | 
 |         return 0; | 
 |     } | 
 |     return $use_cygpath if defined $use_cygpath; | 
 |  | 
 |     $use_cygpath = (qx{cygpath -u '.\\' 2>/dev/null} eq "./\n" && $? == 0); | 
 |  | 
 |     return $use_cygpath; | 
 | } | 
 |  | 
 | ####################################################################### | 
 | # Performs path "normalization": all slashes converted to forward | 
 | # slashes (except leading slash), all duplicated slashes are replaced | 
 | # with single slashes, all relative directories ('./' and '../') are | 
 | # resolved if possible. | 
 | # Path processed as string, directories are not checked for presence so | 
 | # path for not yet existing directory can be "normalized". | 
 | # | 
 | sub normalize_path; | 
 |  | 
 | ####################################################################### | 
 | # Returns current working directory in Win32 format on Windows. | 
 | # | 
 | sub sys_native_current_path { | 
 |     return Cwd::getcwd() unless os_is_win(); | 
 |  | 
 |     my $cur_dir; | 
 |     if($^O eq 'msys') { | 
 |         # MSys shell has built-in command. | 
 |         chomp($cur_dir = `bash -c 'pwd -W'`); | 
 |         if($? != 0) { | 
 |             warn "Can't determine Win32 current directory.\n"; | 
 |             return undef; | 
 |         } | 
 |         # Add final slash if required. | 
 |         $cur_dir .= '/' if length($cur_dir) > 3; | 
 |     } | 
 |     else { | 
 |         # Do not use 'cygpath' - it falsely succeed on paths like '/cygdrive'. | 
 |         $cur_dir = `cmd "/c;" echo %__CD__%`; | 
 |         if($? != 0 || substr($cur_dir, 0, 1) eq '%') { | 
 |             warn "Can't determine Win32 current directory.\n"; | 
 |             return undef; | 
 |         } | 
 |         # Remove both '\r' and '\n'. | 
 |         $cur_dir =~ s{\n|\r}{}g; | 
 |  | 
 |         # Replace back slashes with forward slashes. | 
 |         $cur_dir =~ s{\\}{/}g; | 
 |     } | 
 |     return $cur_dir; | 
 | } | 
 |  | 
 | ####################################################################### | 
 | # Returns Win32 current drive letter with colon. | 
 | # | 
 | sub get_win32_current_drive { | 
 |     # Notice parameter "/c;" - it's required to turn off Msys's | 
 |     # transformation of '/c' and compatible with Cygwin. | 
 |     my $drive_letter = `cmd "/c;" echo %__CD__:~0,2%`; | 
 |     if($? != 0 || substr($drive_letter, 1, 1) ne ':') { | 
 |         warn "Can't determine current Win32 drive letter.\n"; | 
 |         return undef; | 
 |     } | 
 |  | 
 |     return substr($drive_letter, 0, 2); | 
 | } | 
 |  | 
 | # Internal function. Converts path by using Msys's built-in transformation. | 
 | # Returned path may contain duplicated and back slashes. | 
 | sub do_msys_transform; | 
 |  | 
 | # Internal function. Gets two parameters: first parameter must be single | 
 | # drive letter ('c'), second optional parameter is path relative to drive's | 
 | # current working directory. Returns Win32 absolute normalized path. | 
 | sub get_abs_path_on_win32_drive; | 
 |  | 
 | # Internal function. Tries to find or guess Win32 version of given | 
 | # absolute Unix-style path. Other types of paths are not supported. | 
 | # Returned paths contain only single forward slashes (no back and | 
 | # duplicated slashes). | 
 | # Last resort. Used only when other transformations are not available. | 
 | sub do_dumb_guessed_transform; | 
 |  | 
 | ####################################################################### | 
 | # Converts given path to system native format, i.e. to Win32 format on | 
 | # Windows platform. Relative paths converted to relative, absolute | 
 | # paths converted to absolute. | 
 | # | 
 | sub sys_native_path { | 
 |     my ($path) = @_; | 
 |  | 
 |     # Return untouched on non-Windows platforms. | 
 |     return $path unless (os_is_win()); | 
 |  | 
 |     # Do not process empty path. | 
 |     return $path if ($path eq ''); | 
 |  | 
 |     if($path =~ s{^([a-zA-Z]):$}{\u$1:}) { | 
 |         # Path is single drive with colon. (C:) | 
 |         # This type of paths is not processed correctly by 'cygpath'. | 
 |         # WARNING! | 
 |         # Be careful, this relative path can be accidentally transformed | 
 |         # into wrong absolute path by adding to it some '/dirname' with | 
 |         # slash at font. | 
 |         return $path; | 
 |     } | 
 |     elsif($path =~ m{^\\} || $path =~ m{^[a-zA-Z]:[^/\\]}) { | 
 |         # Path is a directory or filename on Win32 current drive or relative | 
 |         # path on current directory on specific Win32 drive. | 
 |         # ('\path' or 'D:path') | 
 |         # First type of paths is not processed by Msys transformation and | 
 |         # resolved to absolute path by 'cygpath'. | 
 |         # Second type is not processed by Msys transformation and may be | 
 |         # incorrectly processed by 'cygpath' (for paths like 'D:..\../.\') | 
 |  | 
 |         my $first_char = ucfirst(substr($path, 0, 1)); | 
 |  | 
 |         # Replace any back and duplicated slashes with single forward slashes. | 
 |         $path =~ s{[\\/]+}{/}g; | 
 |  | 
 |         # Convert leading slash back to forward slash to indicate | 
 |         # directory on Win32 current drive or capitalize drive letter. | 
 |         substr($path, 0, 1) = $first_char; | 
 |         return $path; | 
 |     } | 
 |     elsif(should_use_cygpath()) { | 
 |         # 'cygpath' is available - use it. | 
 |  | 
 |         # Remove leading duplicated forward and back slashes, as they may | 
 |         # prevent transforming and may be not processed. | 
 |         $path =~ s{^([\\/])[\\/]+}{$1}g; | 
 |  | 
 |         my $has_final_slash = ($path =~ m{[/\\]$}); | 
 |  | 
 |         # Use 'cygpath', '-m' means Win32 path with forward slashes. | 
 |         chomp($path = `cygpath -m '$path'`); | 
 |         if ($? != 0) { | 
 |             warn "Can't convert path by \"cygpath\".\n"; | 
 |             return undef; | 
 |         } | 
 |  | 
 |         # 'cygpath' may remove last slash for existing directories. | 
 |         $path .= '/' if($has_final_slash); | 
 |  | 
 |         # Remove any duplicated forward slashes (added by 'cygpath' for root | 
 |         # directories) | 
 |         $path =~ s{//+}{/}g; | 
 |  | 
 |         return $path; | 
 |     } | 
 |     elsif($^O eq 'msys') { | 
 |         # Msys transforms automatically path to Windows native form in staring | 
 |         # program parameters if program is not Msys-based. | 
 |  | 
 |         $path = do_msys_transform($path); | 
 |         return undef unless defined $path; | 
 |  | 
 |         # Capitalize drive letter for Win32 paths. | 
 |         $path =~ s{^([a-z]:)}{\u$1}; | 
 |  | 
 |         # Replace any back and duplicated slashes with single forward slashes. | 
 |         $path =~ s{[\\/]+}{/}g; | 
 |         return $path; | 
 |     } | 
 |     elsif($path =~ s{^([a-zA-Z]):[/\\]}{\u$1:/}) { | 
 |         # Path is already in Win32 form. ('C:\path') | 
 |  | 
 |         # Replace any back and duplicated slashes with single forward slashes. | 
 |         $path =~ s{[\\/]+}{/}g; | 
 |         return $path; | 
 |     } | 
 |     elsif($path !~ m{^/}) { | 
 |         # Path is in relative form. ('path/name', './path' or '../path') | 
 |  | 
 |         # Replace any back and duplicated slashes with single forward slashes. | 
 |         $path =~ s{[\\/]+}{/}g; | 
 |         return $path; | 
 |     } | 
 |  | 
 |     # OS is Windows, but not Msys, path is absolute, path is not in Win32 | 
 |     # form and 'cygpath' is not available. | 
 |     return do_dumb_guessed_transform($path); | 
 | } | 
 |  | 
 | ####################################################################### | 
 | # Converts given path to system native absolute path, i.e. to Win32 | 
 | # absolute format on Windows platform. Both relative and absolute | 
 | # formats are supported for input. | 
 | # | 
 | sub sys_native_abs_path { | 
 |     my ($path) = @_; | 
 |  | 
 |     unless(os_is_win()) { | 
 |         # Convert path to absolute form. | 
 |         $path = Cwd::abs_path($path); | 
 |  | 
 |         # Do not process further on non-Windows platforms. | 
 |         return $path; | 
 |     } | 
 |  | 
 |     if($path =~ m{^([a-zA-Z]):($|[^/\\].*$)}) { | 
 |         # Path is single drive with colon or relative path on Win32 drive. | 
 |         # ('C:' or 'C:path') | 
 |         # This kind of relative path is not processed correctly by 'cygpath'. | 
 |         # Get specified drive letter | 
 |         return get_abs_path_on_win32_drive($1, $2); | 
 |     } | 
 |     elsif($path eq '') { | 
 |         # Path is empty string. Return current directory. | 
 |         # Empty string processed correctly by 'cygpath'. | 
 |  | 
 |         return sys_native_current_path(); | 
 |     } | 
 |     elsif(should_use_cygpath()) { | 
 |         # 'cygpath' is available - use it. | 
 |  | 
 |         my $has_final_slash = ($path =~ m{[\\/]$}); | 
 |  | 
 |         # Remove leading duplicated forward and back slashes, as they may | 
 |         # prevent transforming and may be not processed. | 
 |         $path =~ s{^([\\/])[\\/]+}{$1}g; | 
 |  | 
 |         print "Inter result: \"$path\"\n"; | 
 |         # Use 'cygpath', '-m' means Win32 path with forward slashes, | 
 |         # '-a' means absolute path | 
 |         chomp($path = `cygpath -m -a '$path'`); | 
 |         if($? != 0) { | 
 |             warn "Can't resolve path by usung \"cygpath\".\n"; | 
 |             return undef; | 
 |         } | 
 |  | 
 |         # 'cygpath' may remove last slash for existing directories. | 
 |         $path .= '/' if($has_final_slash); | 
 |  | 
 |         # Remove any duplicated forward slashes (added by 'cygpath' for root | 
 |         # directories) | 
 |         $path =~ s{//+}{/}g; | 
 |  | 
 |         return $path | 
 |     } | 
 |     elsif($path =~ s{^([a-zA-Z]):[/\\]}{\u$1:/}) { | 
 |         # Path is already in Win32 form. ('C:\path') | 
 |  | 
 |         # Replace any possible back slashes with forward slashes, | 
 |         # remove any duplicated slashes, resolve relative dirs. | 
 |         return normalize_path($path); | 
 |     } | 
 |     elsif(substr($path, 0, 1) eq '\\' ) { | 
 |         # Path is directory or filename on Win32 current drive. ('\Windows') | 
 |  | 
 |         my $w32drive = get_win32_current_drive(); | 
 |         return undef unless defined $w32drive; | 
 |  | 
 |         # Combine drive and path. | 
 |         # Replace any possible back slashes with forward slashes, | 
 |         # remove any duplicated slashes, resolve relative dirs. | 
 |         return normalize_path($w32drive . $path); | 
 |     } | 
 |  | 
 |     unless (substr($path, 0, 1) eq '/') { | 
 |         # Path is in relative form. Resolve relative directories in Unix form | 
 |         # *BEFORE* converting to Win32 form otherwise paths like | 
 |         # '../../../cygdrive/c/windows' will not be resolved. | 
 |  | 
 |         my $cur_dir; | 
 |         # MSys shell has built-in command. | 
 |         if($^O eq 'msys') { | 
 |             $cur_dir = `bash -c 'pwd -L'`; | 
 |         } | 
 |         else { | 
 |             $cur_dir = `pwd -L`; | 
 |         } | 
 |         if($? != 0) { | 
 |             warn "Can't determine current working directory.\n"; | 
 |             return undef; | 
 |         } | 
 |         chomp($cur_dir); | 
 |  | 
 |         $path = $cur_dir . '/' . $path; | 
 |     } | 
 |  | 
 |     # Resolve relative dirs. | 
 |     $path = normalize_path($path); | 
 |     return undef unless defined $path; | 
 |  | 
 |     if($^O eq 'msys') { | 
 |         # Msys transforms automatically path to Windows native form in staring | 
 |         # program parameters if program is not Msys-based. | 
 |         $path = do_msys_transform($path); | 
 |         return undef unless defined $path; | 
 |  | 
 |         # Replace any back and duplicated slashes with single forward slashes. | 
 |         $path =~ s{[\\/]+}{/}g; | 
 |         return $path; | 
 |     } | 
 |     # OS is Windows, but not Msys, path is absolute, path is not in Win32 | 
 |     # form and 'cygpath' is not available. | 
 |  | 
 |     return do_dumb_guessed_transform($path); | 
 | } | 
 |  | 
 | # Internal function. Converts given Unix-style absolute path to Win32 format. | 
 | sub simple_transform_win32_to_unix; | 
 |  | 
 | ####################################################################### | 
 | # Converts given path to build system format absolute path, i.e. to | 
 | # Msys/Cygwin Unix-style absolute format on Windows platform. Both | 
 | # relative and absolute formats are supported for input. | 
 | # | 
 | sub build_sys_abs_path { | 
 |     my ($path) = @_; | 
 |  | 
 |     unless(os_is_win()) { | 
 |         # Convert path to absolute form. | 
 |         $path = Cwd::abs_path($path); | 
 |  | 
 |         # Do not process further on non-Windows platforms. | 
 |         return $path; | 
 |     } | 
 |  | 
 |     if($path =~ m{^([a-zA-Z]):($|[^/\\].*$)}) { | 
 |         # Path is single drive with colon or relative path on Win32 drive. | 
 |         # ('C:' or 'C:path') | 
 |         # This kind of relative path is not processed correctly by 'cygpath'. | 
 |         # Get specified drive letter | 
 |  | 
 |         # Resolve relative dirs in Win32-style path or paths like 'D:/../c/' | 
 |         # will be resolved incorrectly. | 
 |         # Replace any possible back slashes with forward slashes, | 
 |         # remove any duplicated slashes. | 
 |         $path = get_abs_path_on_win32_drive($1, $2); | 
 |         return undef unless defined $path; | 
 |  | 
 |         return simple_transform_win32_to_unix($path); | 
 |     } | 
 |     elsif($path eq '') { | 
 |         # Path is empty string. Return current directory. | 
 |         # Empty string processed correctly by 'cygpath'. | 
 |  | 
 |         # MSys shell has built-in command. | 
 |         if($^O eq 'msys') { | 
 |             chomp($path = `bash -c 'pwd -L'`); | 
 |         } | 
 |         else { | 
 |             chomp($path = `pwd -L`); | 
 |         } | 
 |         if($? != 0) { | 
 |             warn "Can't determine Unix-style current working directory.\n"; | 
 |             return undef; | 
 |         } | 
 |  | 
 |         # Add final slash if not at root dir. | 
 |         $path .= '/' if length($path) > 2; | 
 |         return $path; | 
 |     } | 
 |     elsif(should_use_cygpath()) { | 
 |         # 'cygpath' is available - use it. | 
 |  | 
 |         my $has_final_slash = ($path =~ m{[\\/]$}); | 
 |  | 
 |         # Resolve relative directories, as they may be not resolved for | 
 |         # Unix-style paths. | 
 |         # Remove duplicated slashes, as they may be not processed. | 
 |         $path = normalize_path($path); | 
 |         return undef unless defined $path; | 
 |  | 
 |         # Use 'cygpath', '-u' means Unix-stile path, | 
 |         # '-a' means absolute path | 
 |         chomp($path = `cygpath -u -a '$path'`); | 
 |         if($? != 0) { | 
 |             warn "Can't resolve path by usung \"cygpath\".\n"; | 
 |             return undef; | 
 |         } | 
 |  | 
 |         # 'cygpath' removes last slash if path is root dir on Win32 drive. | 
 |         # Restore it. | 
 |         $path .= '/' if($has_final_slash && | 
 |                         substr($path, length($path) - 1, 1) ne '/'); | 
 |  | 
 |         return $path | 
 |     } | 
 |     elsif($path =~ m{^[a-zA-Z]:[/\\]}) { | 
 |         # Path is already in Win32 form. ('C:\path') | 
 |  | 
 |         # Resolve relative dirs in Win32-style path otherwise paths | 
 |         # like 'D:/../c/' will be resolved incorrectly. | 
 |         # Replace any possible back slashes with forward slashes, | 
 |         # remove any duplicated slashes. | 
 |         $path = normalize_path($path); | 
 |         return undef unless defined $path; | 
 |  | 
 |         return simple_transform_win32_to_unix($path); | 
 |     } | 
 |     elsif(substr($path, 0, 1) eq '\\') { | 
 |         # Path is directory or filename on Win32 current drive. ('\Windows') | 
 |  | 
 |         my $w32drive = get_win32_current_drive(); | 
 |         return undef unless defined $w32drive; | 
 |  | 
 |         # Combine drive and path. | 
 |         # Resolve relative dirs in Win32-style path or paths like 'D:/../c/' | 
 |         # will be resolved incorrectly. | 
 |         # Replace any possible back slashes with forward slashes, | 
 |         # remove any duplicated slashes. | 
 |         $path = normalize_path($w32drive . $path); | 
 |         return undef unless defined $path; | 
 |  | 
 |         return simple_transform_win32_to_unix($path); | 
 |     } | 
 |  | 
 |     # Path is not in any Win32 form. | 
 |     unless (substr($path, 0, 1) eq '/') { | 
 |         # Path in relative form. Resolve relative directories in Unix form | 
 |         # *BEFORE* converting to Win32 form otherwise paths like | 
 |         # '../../../cygdrive/c/windows' will not be resolved. | 
 |  | 
 |         my $cur_dir; | 
 |         # MSys shell has built-in command. | 
 |         if($^O eq 'msys') { | 
 |             $cur_dir = `bash -c 'pwd -L'`; | 
 |         } | 
 |         else { | 
 |             $cur_dir = `pwd -L`; | 
 |         } | 
 |         if($? != 0) { | 
 |             warn "Can't determine current working directory.\n"; | 
 |             return undef; | 
 |         } | 
 |         chomp($cur_dir); | 
 |  | 
 |         $path = $cur_dir . '/' . $path; | 
 |     } | 
 |  | 
 |     return normalize_path($path); | 
 | } | 
 |  | 
 | ####################################################################### | 
 | # Performs path "normalization": all slashes converted to forward | 
 | # slashes (except leading slash), all duplicated slashes are replaced | 
 | # with single slashes, all relative directories ('./' and '../') are | 
 | # resolved if possible. | 
 | # Path processed as string, directories are not checked for presence so | 
 | # path for not yet existing directory can be "normalized". | 
 | # | 
 | sub normalize_path { | 
 |     my ($path) = @_; | 
 |  | 
 |     # Don't process empty paths. | 
 |     return $path if $path eq ''; | 
 |  | 
 |     unless($path =~ m{(?:^|\\|/)\.{1,2}(?:\\|/|$)}) { | 
 |         # Speed up processing of simple paths. | 
 |         my $first_char = substr($path, 0, 1); | 
 |         $path =~ s{[\\/]+}{/}g; | 
 |         # Restore starting backslash if any. | 
 |         substr($path, 0, 1) = $first_char; | 
 |         return $path; | 
 |     } | 
 |  | 
 |     my @arr; | 
 |     my $prefix; | 
 |     my $have_root = 0; | 
 |  | 
 |     # Check whether path starts from Win32 drive. ('C:path' or 'C:\path') | 
 |     if($path =~ m{^([a-zA-Z]:(/|\\)?)(.*$)}) { | 
 |         $prefix = $1; | 
 |         $have_root = 1 if defined $2; | 
 |         # Process path separately from drive letter. | 
 |         @arr = split(m{\/|\\}, $3); | 
 |         # Replace backslash with forward slash if required. | 
 |         substr($prefix, 2, 1) = '/' if $have_root; | 
 |     } | 
 |     else { | 
 |         if($path =~ m{^(\/|\\)}) { | 
 |             $have_root = 1; | 
 |             $prefix = $1; | 
 |         } | 
 |         else { | 
 |             $prefix = ''; | 
 |         } | 
 |         @arr = split(m{\/|\\}, $path); | 
 |     } | 
 |  | 
 |     my $p = 0; | 
 |     my @res; | 
 |  | 
 |     for my $el (@arr) { | 
 |         if(length($el) == 0 || $el eq '.') { | 
 |             next; | 
 |         } | 
 |         elsif($el eq '..' && @res > 0 && $res[$#res] ne '..') { | 
 |             pop @res; | 
 |             next; | 
 |         } | 
 |         push @res, $el; | 
 |     } | 
 |     if($have_root && @res > 0 && $res[0] eq '..') { | 
 |         warn "Error processing path \"$path\": " . | 
 |              "Parent directory of root directory does not exist!\n"; | 
 |         return undef; | 
 |     } | 
 |  | 
 |     my $ret = $prefix . join('/', @res); | 
 |     $ret .= '/' if($path =~ m{\\$|/$} && scalar @res > 0); | 
 |  | 
 |     return $ret; | 
 | } | 
 |  | 
 | # Internal function. Converts path by using Msys's built-in | 
 | # transformation. | 
 | sub do_msys_transform { | 
 |     my ($path) = @_; | 
 |     return undef if $^O ne 'msys'; | 
 |     return $path if $path eq ''; | 
 |  | 
 |     # Remove leading double forward slashes, as they turn off Msys | 
 |     # transforming. | 
 |     $path =~ s{^/[/\\]+}{/}; | 
 |  | 
 |     # Msys transforms automatically path to Windows native form in staring | 
 |     # program parameters if program is not Msys-based. | 
 |     # Note: already checked that $path is non-empty. | 
 |     $path = `cmd //c echo '$path'`; | 
 |     if($? != 0) { | 
 |         warn "Can't transform path into Win32 form by using Msys" . | 
 |              "internal transformation.\n"; | 
 |         return undef; | 
 |     } | 
 |  | 
 |     # Remove double quotes, they are added for paths with spaces, | 
 |     # remove both '\r' and '\n'. | 
 |     $path =~ s{^\"|\"$|\"\r|\n|\r}{}g; | 
 |  | 
 |     return $path; | 
 | } | 
 |  | 
 | # Internal function. Gets two parameters: first parameter must be single | 
 | # drive letter ('c'), second optional parameter is path relative to drive's | 
 | # current working directory. Returns Win32 absolute normalized path. | 
 | sub get_abs_path_on_win32_drive { | 
 |     my ($drv, $rel_path) = @_; | 
 |     my $res; | 
 |  | 
 |     # Get current directory on specified drive. | 
 |     # "/c;" is compatible with both Msys and Cygwin. | 
 |     my $cur_dir_on_drv = `cmd "/c;" echo %=$drv:%`; | 
 |     if($? != 0) { | 
 |         warn "Can't determine Win32 current directory on drive $drv:.\n"; | 
 |         return undef; | 
 |     } | 
 |  | 
 |     if($cur_dir_on_drv =~ m{^[%]}) { | 
 |         # Current directory on drive is not set, default is | 
 |         # root directory. | 
 |  | 
 |         $res = ucfirst($drv) . ':/'; | 
 |     } | 
 |     else { | 
 |         # Current directory on drive was set. | 
 |         # Remove both '\r' and '\n'. | 
 |         $cur_dir_on_drv =~ s{\n|\r}{}g; | 
 |  | 
 |         # Append relative path part. | 
 |         $res = $cur_dir_on_drv . '/'; | 
 |     } | 
 |     $res .= $rel_path if defined $rel_path; | 
 |  | 
 |     # Replace any possible back slashes with forward slashes, | 
 |     # remove any duplicated slashes, resolve relative dirs. | 
 |     return normalize_path($res); | 
 | } | 
 |  | 
 | # Internal function. Tries to find or guess Win32 version of given | 
 | # absolute Unix-style path. Other types of paths are not supported. | 
 | # Returned paths contain only single forward slashes (no back and | 
 | # duplicated slashes). | 
 | # Last resort. Used only when other transformations are not available. | 
 | sub do_dumb_guessed_transform { | 
 |     my ($path) = @_; | 
 |  | 
 |     # Replace any possible back slashes and duplicated forward slashes | 
 |     # with single forward slashes. | 
 |     $path =~ s{[/\\]+}{/}g; | 
 |  | 
 |     # Empty path is not valid. | 
 |     return undef if (length($path) == 0); | 
 |  | 
 |     # RE to find Win32 drive letter | 
 |     my $drv_ltr_re = drives_mounted_on_cygdrive() ? | 
 |                         qr{^/cygdrive/([a-zA-Z])($|/.*$)} : | 
 |                         qr{^/([a-zA-Z])($|/.*$)}; | 
 |  | 
 |     # Check path whether path is Win32 directly mapped drive and try to | 
 |     # transform it assuming that drive letter is matched to Win32 drive letter. | 
 |     if($path =~ m{$drv_ltr_re}) { | 
 |         return ucfirst($1) . ':/' if(length($2) == 0); | 
 |         return ucfirst($1) . ':' . $2; | 
 |     } | 
 |  | 
 |     # This may be some custom mapped path. ('/mymount/path') | 
 |  | 
 |     # Must check longest possible path component as subdir can be mapped to | 
 |     # different directory. For example '/usr/bin/' can be mapped to '/bin/' or | 
 |     # '/bin/' can be mapped to '/usr/bin/'. | 
 |     my $check_path = $path; | 
 |     my $path_tail = ''; | 
 |     do { | 
 |         if(-d $check_path) { | 
 |             my $res = | 
 |                 `(cd "$check_path" && cmd /c "echo %__CD__%") 2>/dev/null`; | 
 |             if($? == 0 && substr($path, 0, 1) ne '%') { | 
 |                 # Remove both '\r' and '\n'. | 
 |                 $res =~ s{\n|\r}{}g; | 
 |  | 
 |                 # Replace all back slashes with forward slashes. | 
 |                 $res =~ s{\\}{/}g; | 
 |  | 
 |                 if(length($path_tail) > 0) { | 
 |                     return $res . $path_tail; | 
 |                 } | 
 |                 else { | 
 |                     $res =~ s{/$}{} unless $check_path =~ m{/$}; | 
 |                     return $res; | 
 |                 } | 
 |             } | 
 |         } | 
 |         if($check_path =~ m{(^.*/)([^/]+/*)}) { | 
 |             $check_path = $1; | 
 |             $path_tail = $2 . $path_tail; | 
 |         } | 
 |         else { | 
 |             # Shouldn't happens as root '/' directory should always | 
 |             # be resolvable. | 
 |             warn "Can't determine Win32 directory for path \"$path\".\n"; | 
 |             return undef; | 
 |         } | 
 |     } while(1); | 
 | } | 
 |  | 
 |  | 
 | # Internal function. Converts given Unix-style absolute path to Win32 format. | 
 | sub simple_transform_win32_to_unix { | 
 |     my ($path) = @_; | 
 |  | 
 |     if(should_use_cygpath()) { | 
 |         # 'cygpath' gives precise result. | 
 |         my $res; | 
 |         chomp($res = `cygpath -a -u '$path'`); | 
 |         if($? != 0) { | 
 |             warn "Can't determine Unix-style directory for Win32 " . | 
 |                  "directory \"$path\".\n"; | 
 |             return undef; | 
 |         } | 
 |  | 
 |         # 'cygpath' removes last slash if path is root dir on Win32 drive. | 
 |         $res .= '/' if(substr($res, length($res) - 1, 1) ne '/' && | 
 |                        $path =~ m{[/\\]$}); | 
 |         return $res; | 
 |     } | 
 |  | 
 |     # 'cygpath' is not available, use guessed transformation. | 
 |     unless($path =~ s{^([a-zA-Z]):(?:/|\\)}{/\l$1/}) { | 
 |         warn "Can't determine Unix-style directory for Win32 " . | 
 |              "directory \"$path\".\n"; | 
 |         return undef; | 
 |     } | 
 |  | 
 |     $path = '/cygdrive' . $path if(drives_mounted_on_cygdrive()); | 
 |     return $path; | 
 | } | 
 |  | 
 | 1;    # End of module |