diff --git a/composer.json b/composer.json index b713da2df8..fb5a5f7230 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,8 @@ "symfony/console": "2.7.*", "symfony/event-dispatcher": "2.7.*", "symfony/config": "~2.2", - "pear/console_table": "~1.3.0" + "pear/console_table": "~1.3.0", + "webmozart/path-util": "~2" }, "require-dev": { "phpunit/phpunit": "4.*", diff --git a/composer.lock b/composer.lock index 7395070eed..8759eff5df 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "8e1a0fdaad7f9daf50ac34059b8d17ec", - "content-hash": "b2dc2c69ea76c5b0e02bb32212a8b34b", + "hash": "ee03967222b181222351395eb76aaa87", + "content-hash": "eb66e2339bb18f2d4c846a46f8c33f6a", "packages": [ { "name": "consolidation/annotated-command", @@ -1338,6 +1338,52 @@ "validate" ], "time": "2016-08-09 15:02:57" + }, + { + "name": "webmozart/path-util", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/path-util.git", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "webmozart/assert": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\PathUtil\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "time": "2015-12-17 08:42:14" } ], "packages-dev": [ diff --git a/docs/install-alternative.md b/docs/install-alternative.md index aa2bec506c..3cdded0ab5 100644 --- a/docs/install-alternative.md +++ b/docs/install-alternative.md @@ -68,7 +68,7 @@ cd /opt/drush-9.x Windows ------------ -Drush on Windows is not recommended, since Drush's test suite is not running there ([help wanted](https://github.com/drush-ops/drush/issues/1612)). +Drush on Windows is experimental, since Drush's test suite is not running there ([help wanted](https://github.com/drush-ops/drush/issues/1612)). - [Acquia Dev Desktop](https://www.acquia.com/downloads) is excellent, and includes Drush. See the terminal icon after setting up a web site. - Or consider running Linux/OSX via Virtualbox. [Drupal VM](http://www.drupalvm.com/) and [Vlad](https://github.com/hashbangcode/vlad) are popular. diff --git a/includes/environment.inc b/includes/environment.inc index 6996109116..d7f4cc8db4 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -506,16 +506,6 @@ function drush_build_drush_command($drush_path = NULL, $php = NULL, $os = NULL, return trim($prefix . ' ' . drush_escapeshellarg($drush_path, $os) . $additional_options); } -/** - * Check if the operating system is Windows. - * This will return TRUE under DOS, Powershell - * Cygwin and MSYSGIT shells, so test for the - * Windows variant FIRST if you care. - */ -function drush_is_windows($os = NULL) { - return _drush_test_os($os, array("WIN","CYGWIN","CWRSYNC","MINGW")); -} - /** * Check if the operating system is Winodws * running some variant of cygwin -- either diff --git a/includes/exec.inc b/includes/exec.inc index e58b427aff..ce2062f091 100644 --- a/includes/exec.inc +++ b/includes/exec.inc @@ -322,40 +322,6 @@ function drush_wrap_with_quotes($arg) { * Use raw to get an unquoted version of the escaped arg. * Notice that you can't add quotes later until you know the platform. */ -function drush_escapeshellarg($arg, $os = NULL, $raw = FALSE) { - // Short-circuit escaping for simple params (keep stuff readable) - if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) { - return $arg; - } - elseif (drush_is_windows($os)) { - return _drush_escapeshellarg_windows($arg, $raw); - } - else { - return _drush_escapeshellarg_linux($arg, $raw); - } -} - -/** - * Windows version of escapeshellarg(). - */ -function _drush_escapeshellarg_windows($arg, $raw = FALSE) { - // Double up existing backslashes - $arg = preg_replace('/\\\/', '\\\\\\\\', $arg); - - // Double up double quotes - $arg = preg_replace('/"/', '""', $arg); - - // Double up percents. - $arg = preg_replace('/%/', '%%', $arg); - - // Only wrap with quotes when needed. - if(!$raw) { - // Add surrounding quotes. - $arg = '"' . $arg . '"'; - } - - return $arg; -} /** * Stores output for the most recent shell command. diff --git a/includes/filesystem.inc b/includes/filesystem.inc index a2568ed44f..5b293af272 100644 --- a/includes/filesystem.inc +++ b/includes/filesystem.inc @@ -4,6 +4,7 @@ * @file * Filesystem utilities. */ +use Webmozart\PathUtil\Path; /** * @defgroup filesystemfunctions Filesystem convenience functions. @@ -738,9 +739,8 @@ function drush_file_append_data($file, $data) { * the other. */ function drush_is_nested_directory($base_dir, $test_is_nested) { - // Ensure there is always exactly one '/' at the end of $base_dir - $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - return substr($test_is_nested, 0, strlen($base_dir)) == $base_dir; + $common = Path::getLongestCommonBasePath([$test_is_nested, $base_dir]); + return $common == Path::canonicalize($base_dir); } /** diff --git a/includes/startup.inc b/includes/startup.inc index e61388f71d..19ff5a428f 100644 --- a/includes/startup.inc +++ b/includes/startup.inc @@ -125,6 +125,26 @@ function find_wrapper_or_launcher_at_location($location) { return ""; } +/** + * Determine whether current OS is a Windows variant. + */ +function drush_is_windows() { + return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; +} + +function drush_escapeshellarg($arg, $os = NULL, $raw = FALSE) { + // Short-circuit escaping for simple params (keep stuff readable) + if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) { + return $arg; + } + elseif (drush_is_windows($os)) { + return _drush_escapeshellarg_windows($arg, $raw); + } + else { + return _drush_escapeshellarg_linux($arg, $raw); + } +} + /** * Linux version of escapeshellarg(). * @@ -157,6 +177,28 @@ function _drush_escapeshellarg_linux($arg, $raw = FALSE) { return $arg; } +/** + * Windows version of escapeshellarg(). + */ +function _drush_escapeshellarg_windows($arg, $raw = FALSE) { + // Double up existing backslashes + $arg = preg_replace('/\\\/', '\\\\\\\\', $arg); + + // Double up double quotes + $arg = preg_replace('/"/', '""', $arg); + + // Double up percents. + $arg = preg_replace('/%/', '%%', $arg); + + // Only wrap with quotes when needed. + if(!$raw) { + // Add surrounding quotes. + $arg = '"' . $arg . '"'; + } + + return $arg; +} + /** * drush_startup is called once, by the Drush "finder" * script -- the "drush" script at the Drush root. @@ -313,8 +355,9 @@ function drush_startup($argv) { } } - // A Phar can't use the Bash niceties of the Launcher so just proceed to drush_main(). - if (class_exists('Phar') && Phar::running()) { + // Didn't find any site-local Drush, or @use'd Drush. + // If a Phar finds itself (https://github.com/drush-ops/drush/pull/2246), skip the Bash niceties of the launcher and proceed to drush_main(). + if (!empty($found_script) && class_exists('Phar') && $found_script == Phar::running(FALSE)) { drush_run_main($DEBUG, $sep, "Phar detected. Proceeding to drush_main()."); } @@ -322,11 +365,16 @@ function drush_startup($argv) { // There should be a drush.launcher in same directory as this script. if (empty($found_script)) { $found_script = dirname(__DIR__) . "{$sep}drush.launcher"; - // Phar clause added due to https://github.com/drush-ops/drush/pull/2246 - if (!is_executable($found_script)) { - // Can't execute the found launcher, so proceed with the current drush, - // bypassing the Bash niceties of the launcher. - drush_run_main($DEBUG, $sep, 'No executable launcher found. Proceeding to drush_main()."'); + } + + if (drush_is_windows()) { + // Sometimes we found launcher in /bin, and sometimes not. Adjust accordingly. + if (strpos($found_script, 'bin')) { + $found_script = dirname($found_script). $sep. 'drush.php.bat'; + } + else { + array_unshift($arguments, dirname($found_script). $sep. 'drush.php'); + $found_script = 'php'; } } @@ -370,7 +418,7 @@ function drush_startup($argv) { exit(1); } else { - $escaped_args = array_map(function($item) { return _drush_escapeshellarg_linux($item); }, $arguments); + $escaped_args = array_map(function($item) { return drush_escapeshellarg($item); }, $arguments); $process = proc_open($found_script . ' ' . implode(' ', $escaped_args), array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes); $proc_status = proc_get_status($process); $exit_code = proc_close($process); diff --git a/lib/Drush/Boot/DrupalBoot.php b/lib/Drush/Boot/DrupalBoot.php index 682ada05b4..c6aa728bfd 100644 --- a/lib/Drush/Boot/DrupalBoot.php +++ b/lib/Drush/Boot/DrupalBoot.php @@ -285,7 +285,7 @@ function bootstrap_drupal_root_validate() { return drush_set_error('DRUSH_DRUPAL_VERSION_UNSUPPORTED', dt('Drush !drush_version does not support Drupal !major_version.', array('!drush_version' => DRUSH_VERSION, '!major_version' => $major_version))); } - drush_bootstrap_value('drupal_root', realpath($drupal_root)); + drush_bootstrap_value('drupal_root', $drupal_root); define('DRUSH_DRUPAL_SIGNATURE', $signature); return TRUE;