Annotation of embedaddon/curl/tests/pathhelp.pm, revision 1.1.1.1

1.1       misho       1: ###########################################################################
                      2: #                                  _   _ ____  _
                      3: #  Project                     ___| | | |  _ \| |
                      4: #                             / __| | | | |_) | |
                      5: #                            | (__| |_| |  _ <| |___
                      6: #                             \___|\___/|_| \_\_____|
                      7: #
                      8: # Copyright (C) 2016 - 2020, 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: 
                     51: package pathhelp;
                     52: use strict;
                     53: use warnings;
                     54: use Cwd 'abs_path';
                     55: 
                     56: BEGIN {
                     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: 
                    103: our $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.
                    109: sub 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: #
                    129: sub normalize_path;
                    130: 
                    131: #######################################################################
                    132: # Returns current working directory in Win32 format on Windows.
                    133: #
                    134: sub 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: #
                    167: sub 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.
                    181: sub 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.
                    186: sub 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.
                    193: sub 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: #
                    200: sub 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: #
                    301: sub 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.
                    406: sub 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: #
                    413: sub 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 available - 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: #
                    534: sub 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.
                    600: sub 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.
                    629: sub 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.
                    667: sub 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.
                    731: sub 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: 
                    761: 1;    # End of module

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>