File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / pathhelp.pm
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:16 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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
  762: 

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