? eightball
? includes/table.inc
Index: commands/core/core.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v
retrieving revision 1.136
diff -u -p -r1.136 core.drush.inc
--- commands/core/core.drush.inc	5 Dec 2010 07:27:32 -0000	1.136
+++ commands/core/core.drush.inc	6 Dec 2010 07:26:42 -0000
@@ -50,7 +50,7 @@ function core_drush_command() {
   );
   $items['core-status'] = array(
     'description' => 'Provides a birds-eye view of the current Drupal installation, if any.',
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX,
     'aliases' => array('status', 'st'),
     'examples' => array(
       'drush status version' => 'Show all status lines that contain version information.',
@@ -80,7 +80,7 @@ function core_drush_command() {
     'options' => array(
       '--script-path' => "Additional paths to search for scripts, separated by : (Unix-based systems) or ; (Windows).",
     ),
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap.
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX, // No bootstrap.
     'aliases' => array('scr'),
     'deprecated-aliases' => array('script'),
   );
@@ -198,7 +198,7 @@ function core_drush_command() {
       'on @alias status' => 'Run the command "status" on the site indicated by @alias',
     ),
     'aliases' => array('cli'),
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX,
   );
 
   $items['batch-process'] = array(
@@ -604,15 +604,6 @@ function drush_core_cron() {
 }
 
 /**
- * Bootstrap 'status' command during the init phase.
- * Always use bootstrap_max from _init() so that command
- * hooks have a chance to be initialized.
- */
-function drush_core_status_init() {
-  drush_bootstrap_max();
-}
-
-/**
  * Command callback. Provides a birds-eye view of the current Drupal
  * installation.
  */
@@ -663,15 +654,6 @@ function _drush_core_is_named_in_array($
 }
 
 /**
- * Bootstrap 'php-script' command during the init phase.
- * Always use bootstrap_max from _init() so that command
- * hooks have a chance to be initialized.
- */
-function drush_php_script_init() {
-  drush_bootstrap_max();
-}
-
-/**
  * Command callback. Runs "naked" php scripts
  * and drush "shebang" scripts ("#!/usr/bin/env drush").
  */
@@ -872,15 +854,6 @@ function drush_core_find_project_path($t
   return NULL;
 }
 
-/**
- * Bootstrap 'cli' command during the init phase.
- * Always use bootstrap_max from _init() so that command
- * hooks have a chance to be initialized.
- */
-function drush_core_cli_init() {
-  drush_bootstrap_max();
-}
-
 function drush_core_cli() {
   // Do not allow cli to start recursively, or from backend invoke.
   if (drush_get_option('in-cli', FALSE)) {
Index: commands/pm/pm.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/pm.drush.inc,v
retrieving revision 1.149
diff -u -p -r1.149 pm.drush.inc
--- commands/pm/pm.drush.inc	3 Dec 2010 16:32:56 -0000	1.149
+++ commands/pm/pm.drush.inc	6 Dec 2010 07:26:43 -0000
@@ -184,6 +184,7 @@ function pm_drush_command() {
       'drush rln pathauto zen' => 'View release notes for the recommended version of Pathauto and Zen projects.',
     ),
     'aliases' => array('rln'),
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX, // No bootstrap required, but we will use a Drupal instance if it is available.
   );
   $items['pm-releases'] = array(
     'description' => 'Print release information for given projects.',
@@ -191,10 +192,15 @@ function pm_drush_command() {
     'arguments' => array(
       'projects' => 'A list of drupal.org project names. Defaults to \'drupal\'',
     ),
+    'options' => array(
+      '--dev' => "Show only development releases. Optional.",
+      '--all' => "Shows all available releases instead of the default short list of recent releases.",
+    ),
     'examples' => array(
       'drush pm-releases cck zen' => 'View releases for cck and Zen projects.',
     ),
     'deprecated-aliases' => array('info'),
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX, // No bootstrap required, but we will use a Drupal instance if it is available.
   );
   $items['pm-download'] = array(
     'description' => 'Download projects from drupal.org or other sources.',
@@ -205,6 +211,7 @@ function pm_drush_command() {
       'drush dl cck zen es' => 'Download latest versions of CCK, Zen and Spanish translations for your Drupal version.',
       'drush dl og-1.3' => 'Download a specfic version of Organic groups module for my version of Drupal.',
       'drush dl diff-6.x-2.x' => 'Download a specific development branch of diff module for a specific Drupal version.',
+      'drush dl views --dev' => 'Download the most recent development branch of the views module.',
     ),
     'arguments' => array(
       'projects' => 'A list of drpal.org project names, with optional version. Defaults to \'drupal\'',
@@ -215,10 +222,13 @@ function pm_drush_command() {
       '--source' => 'The base URL which provides project release history in XML. Defaults to http://updates.drupal.org/release-history.',
       '--notes' => 'Show release notes after each project is downloaded.',
       '--variant' => "Only useful for install profiles. Possible values: 'core', 'no-core', 'make'.",
+      '--dev' => "Download a development release. Optional.",
+      '--select' => "Select the version to download interactively from a list of available releases.",
+      '--all' => "Useful only with --select; shows all available releases instead of a short list of recent releases.",
       '--drupal-project-rename' => 'Alternate name for "drupal-x.y" directory when downloading Drupal project. If empty, will defaults to "drupal".',
       '--pipe' => 'Returns a list of the names of the extensions (modules and themes) contained in the downloaded projects.',
     ),
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap required.
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX, // No bootstrap required, but we will download into a Drupal instance if it is available.
     'aliases' => array('dl'),
     'deprecated-aliases' => array('download'),
   ) + $engines;
@@ -953,10 +963,11 @@ function drush_pm_releases() {
     $header = dt('------- RELEASES FOR \'!name\' PROJECT -------', array('!name' => strtoupper($name)));
     $rows = array();
     $rows[] = array(dt('Release'), dt('Date'), dt('Status'));
-    foreach ($project['releases'] as $release) {
+    $releases = _drush_pm_filter_releases($project['releases'], drush_get_option('all', FALSE), drush_get_option('dev', FALSE));
+    foreach ($releases as $release) {
       $rows[] = array(
         $release['version'],
-        format_date($release['date'], 'custom', 'Y-M-d'),
+        date('Y-M-d', $release['date']),
         implode(', ', $release['release_status'])
       );
     }
@@ -965,6 +976,156 @@ function drush_pm_releases() {
   }
 }
 
+
+function _drush_pm_get_releases_from_xml($xml, $project) {
+  foreach (array('title', 'short_name', 'dc:creator', 'api_version', 'recommended_major', 'supported_majors', 'default_major', 'project_status', 'link') as $item) {
+    if (array_key_exists($item, $xml)) {
+      $value = $xml->xpath($item);
+      $project_info[$item] = (string)$value[0];
+    }
+  }
+
+  $recommended_major = @$xml->xpath("/project/recommended_major");
+  $recommended_major = (string)$recommended_major[0];
+  $supported_majors = @$xml->xpath("/project/supported_majors");
+  $supported_majors = array_flip(explode(',', (string)$supported_majors[0]));
+  $releases_xml = @$xml->xpath("/project/releases/release[status='published']");
+  $recommended_version = NULL;
+  $latest_version = NULL;
+  foreach ($releases_xml as $release) {
+    $release_info = array();
+    foreach (array('name', 'version', 'tag', 'version_major', 'version_extra', 'status', 'release_link', 'download_link', 'date', 'mdhash', 'filesize') as $item) {
+      if (array_key_exists($item, $release)) {
+        $value = $release->xpath($item);
+        $release_info[$item] = (string)$value[0];
+      }
+    }
+    $statuses = array();
+    if (array_key_exists($release_info['version_major'], $supported_majors)) {
+      $statuses[] = "Supported";
+      unset($supported_majors[$release_info['version_major']]);
+    }
+    if ($release_info['version_major'] == $recommended_major) {
+      if (!isset($latest_version)) {
+        $latest_version = $release_info['version'];
+      }
+      // The first stable version (no 'version extra') in the recommended major
+      // is the recommended release
+      if (empty($release_info['version_extra']) && (!isset($recommended_version))) {
+        $statuses[] = "Recommended";
+        $recommended_version = $release_info['version'];
+      }
+    }
+    if (!empty($release_info['version_extra']) && ($release_info['version_extra'] == "dev")) {
+      $statuses[] = "Development";
+    }
+    foreach ($release->xpath('terms/term/value') as $release_type) {
+      // There are three kinds of release types that we recognize:
+      // "Bug fixes", "New features" and "Security update".
+      // We will add "Security" for security updates, and nothing
+      // for the other kinds.
+      if (strpos($release_type, "Security") !== FALSE) {
+        $statuses[] = "Security";
+      }
+    }
+    $release_info['release_status'] = $statuses;
+    $releases[$release_info['version']] = $release_info;
+  }
+  // If there is no -stable- release in the recommended major,
+  // then take the latest verion in the recommended major to be
+  // the recommended release.
+  if (!isset($recommended_version) && isset($latest_version)) {
+    $recommended_version = $latest_version;
+    $releases[$recommended_version]['release_status'][] = "Recommended";
+  }
+  
+  $project_info['releases'] = $releases;
+  $project_info['recommended'] = $recommended_version;
+  
+  return $project_info;
+}
+
+/**
+ * Return an array of available releases for given project(s).
+ * Generates content equivalent to the 'releases' array returned by _drush_pm_get_releases.
+ */
+function _drush_pm_releases_from_xml($xml, $project) {
+  // If we are bootstrapped, we will get the release info from _drush_pm_get_releases,
+  // so that we can include information about the installed release.
+  $releases = array();
+  if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL) {
+    $info = _drush_pm_get_releases_from_update_status(array($project));
+    $releases = $info[$project]['releases'];
+  }
+  // If we are not bootstrapped, then we will extract the release info from
+  // the release-history xml.
+  else {
+    $project_info = _drush_pm_get_releases_from_xml($xml, $project);
+    $releases = $project_info['releases'];
+  }
+  return $releases;
+}
+ 
+function _drush_pm_filter_releases($releases, $all = FALSE, $dev = FALSE, $show_all_until_installed = TRUE) {
+  $options = array();
+  $limits_list = array();
+  foreach ($releases as $version => $release_info) {
+    if (!$dev || ((array_key_exists('version_extra', $release_info)) && ($release_info['version_extra'] == 'dev'))) {
+      $saw_unique_status = FALSE;
+      foreach ($release_info['release_status'] as $one_status) {
+        // We will show the first release of a given kind;
+        // after we show the first security release, we show
+        // no other.  We do this on a per-major-version basis,
+        // though, so if a project has three major versions, then
+        // we will show the first security release from each.
+        // This rule is overridden by $all and $show_all_until_installed.
+        $test_key = $release_info['version_major'] . $one_status;
+        if (!array_key_exists($test_key, $limits_list)) {
+          $limits_list[$test_key] = TRUE;
+          $saw_unique_status = TRUE;
+          // Once we see the "Installed" release we will stop
+          // showing all releases
+          if ($one_status == "Installed") {
+            $show_all_until_installed = FALSE;
+          }
+        }
+      }
+      if ($all || $show_all_until_installed || $saw_unique_status) {
+        $options[$release_info['version']] = $release_info;
+      }
+    }
+  }
+  // If "show all until installed" is still true, that means that
+  // we never encountered the installed release anywhere in releases,
+  // and therefore we did not filter out any releases at all.  If this
+  // is the case, then call ourselves again, this time with 
+  // $show_all_until_installed set to FALSE from the beginning.
+  // The other situation we might encounter is when we do not encounter
+  // the installed release, and $options is still empty.  This means
+  // that there were no supported or recommented or security or development
+  // releases found.  If this is the case, then we will force ALL to TRUE
+  // and show everything on the second iteration.
+  if ($show_all_until_installed === TRUE) {
+    $options = _drush_pm_filter_releases($releases, empty($options) ? TRUE : $all, $dev, FALSE);
+  }
+  return $options;
+}
+
+/**
+ * Return an array of available releases for given project(s).
+ *
+ * Helper function for pm-download.
+ */
+function _drush_pm_download_releases_choice($xml, $project, $all = FALSE, $dev = FALSE) {
+  $releases = _drush_pm_filter_releases(_drush_pm_releases_from_xml($xml, $project), $all, $dev);
+  
+  $options = array();
+  foreach($releases as $version => $release) {
+    $options[$version] = array($version, '-', date('Y-M-d', $release['date']), '-', implode(', ', $release['release_status']));
+  }
+  return $options;
+}
+
 /**
  * Get releases for given projects and fill in status information.
  *
@@ -975,6 +1136,15 @@ function drush_pm_releases() {
  * @see _drush_pm_releasenotes()
  */
 function _drush_pm_get_releases($projects) {
+  if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL) {
+    return _drush_pm_get_releases_from_update_status($projects);
+  }
+  else {
+    return _drush_pm_get_releases_from_release_history($projects);    
+  }
+}
+
+function _drush_pm_get_releases_from_update_status($projects) {
   // We don't provide for other options here, so we supply an explicit path.
   drush_include_engine('update_info', 'drupal', NULL, DRUSH_BASE_PATH . '/commands/pm/update_info');
 
@@ -986,8 +1156,8 @@ function _drush_pm_get_releases($project
       continue;
     }
     if ($info[$project]['project_status'] == 'unpublished') {
-      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished ans has no releases available.", array('!project' => $key)), 'warning');
-      unset($info[$key]);
+      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished and has no releases available.", array('!project' => $project)), 'warning');
+      unset($info[$project]);
       continue;
     }
   }
@@ -1075,6 +1245,7 @@ function drush_pm_releasenotes() {
   return _drush_pm_releasenotes($requests);
 }
 
+
 /**
  * Internal function: prints release notes for given drupal projects.
  *
@@ -1097,7 +1268,7 @@ function _drush_pm_releasenotes($request
   foreach ($info as $key => $project) {
     $selected_versions = array();
     // #TODO# project<->package-name problem. See _drush_pm_get_releases().
-    if (!isset($project['installed'])) {
+    if (!isset($project['installed']) && (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
       // Installed package's version (e.g. cck) is detected by
       // _drush_pm_get_releases(). Attempted to obtain information
       // about it from update_cache.
@@ -1152,12 +1323,24 @@ function _drush_pm_releasenotes($request
           continue;
         }
       }
-      $data = drupal_http_request($release_page_url);
-      if (isset($data->error)) {
-        drush_log(dt("Error (!error) while requesting the release notes' page for !project project.", array('!error' => $data->error, '!project' => $key)), 'error');
-        continue;
+      // We'll use drupal_http_request if available; it provides better error reporting.
+      if (function_exists('drupal_http_request')) {
+        $data = drupal_http_request($release_page_url);
+        if (isset($data->error)) {
+          drush_log(dt("Error (!error) while requesting the release notes page for !project project.", array('!error' => $data->error, '!project' => $key)), 'error');
+          continue;
+        }
+        @$dom = DOMDocument::loadHTML($data->data);
+      }
+      else {
+        $filename = _drush_download_file($release_page_url);
+        @$dom = DOMDocument::loadHTMLFile($filename);
+        @unlink($filename);
+        if ($dom === FALSE) {
+          drush_log(dt("Error while requesting the release notes page for !project project.", array('!project' => $key)), 'error');
+          continue;
+        }
       }
-      @$dom = DOMDocument::loadHTML($data->data);
       if ($dom) {
         drush_log(dt("Successfully parsed and loaded the HTML contained in the release notes' page for !project (!version) project.", array('!project' => $key, '!version' => $version)), 'notice');
       }
@@ -1169,31 +1352,10 @@ function _drush_pm_releasenotes($request
       $notes_last_update = $node_content[0]->div[1]->div[0]->asXML();
       unset($node_content[0]->div);
       $project_notes = $node_content[0]->asXML();
-      if (isset($project['installed'])) {
-        if (isset($request['version'])) {
-          $status_msg = 'Note: type in only the project name to see the release notes for all newer versions.';
-        }
-        else {
-          if ($version == $project['installed']) {
-            $status_msg = 'Note: this is the installed version.';
-            if ($version == $project['recommended']) {
-              $status_msg = 'Note: this is the installed & recommended version.';
-            }
-          }
-          elseif ($version == $project['recommended']) {
-            $status_msg = 'Note: this is the recommended version.';
-          }
-          else {
-            $status_msg = dt('Note: the installed version is !version.', array('!version' => $project['installed']));
-          }
-        }
-      }
-      else {
-        $status_msg = 'Note: specific non-installed version.';
-        if (!isset($request['version'])) {
-          $status_msg = 'Note: recommended non-installed version.';
-        }
-      }
+
+      // Build the status message from the info from _drush_pm_get_releases
+      $status_msg = implode(', ', $project['releases'][$version]['release_status']);
+
       // #TODO# d5 compatibility && try a cleaner way to do this.
       if (drush_drupal_major_version() < 7) {
         $break = '<br>';
@@ -1204,12 +1366,13 @@ function _drush_pm_releasenotes($request
         $status_msg = '> ' . $status_msg . ' -<hr>';
       }
       $notes_header = dt("<hr>
-        > RELEASE NOTES FOR '!name' PROJECT, VERSION !version:!break
-        > !time.!break
-        !status
-        <hr>", array('!status' => $print_status ? $status_msg : '', '!name' => strtoupper($key), '!break' => $break, '!version' => $version, '!time' => $notes_last_update));
+ > RELEASE NOTES FOR '!name' PROJECT, VERSION !version:!break
+ > !time.!break
+ !status
+<hr>
+", array('!status' => $print_status ? $status_msg : '', '!name' => strtoupper($key), '!break' => $break, '!version' => $version, '!time' => $notes_last_update));
       // Finally we print the release notes for the requested project.
-      $print = drupal_html_to_text($notes_header . $project_notes, array('br', 'p', 'ul', 'ol', 'li', 'hr'));
+      $print = drush_html_to_text($notes_header . $project_notes, array('br', 'p', 'ul', 'ol', 'li', 'hr'));
       drush_print($print);
       if (drush_drupal_major_version() < 7) {drush_print();}
     }
@@ -1623,17 +1786,6 @@ function drush_pm_include_version_contro
 }
 
 /**
- * Implementation of drush_COMMAND_init().
- *
- * pm-download doesn't require any bootstrap level to operate but it needs to
- * know if running on a drupal directory in order to select the best location
- * for projects to download.
- */
-function drush_pm_download_init() {
-  drush_bootstrap_max();
-}
-
-/**
  * Implementation of drush_COMMAND_validate().
  */
 function drush_pm_download_validate() {
@@ -1692,27 +1844,57 @@ function drush_pm_download_validate() {
       drush_log(dt('Switching to --variant=!variant.', array('!variant' => $new_variant)), 'ok');
     }
   }
-  
-  // At present we depend on update module to show release notes.
-  $notes = TRUE;
-  if (drush_get_option('notes', FALSE)) {
-    $update = 'update';
-    if (drush_drupal_major_version() == 5) {
-      $update = 'update_status';
-    }  
-    if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') < DRUSH_BOOTSTRAP_DRUPAL_FULL) {
-      $msg = dt('A higher bootstrap level is needed to show release notes.');
-      $notes = FALSE;
-    }
-    elseif (!module_exists($update)) {
-      $msg = dt('!module is required to show release notes.', array('!module' => $update));
-      $notes = FALSE;
-    }
-    if (!$notes) {
-      drush_log($msg . ' ' . dt('Disabling --notes option.'), 'warning');
-      drush_set_option('notes', FALSE);
+}
+
+function _drush_pm_get_releases_from_release_history($requests) {
+  $info = array();
+
+  // Parse out project name and version.
+  $requests = pm_parse_project_version($requests);
+
+  // Get release history for each request and download the project.
+  $project_types = pm_project_types();
+  $project_types_xpath = '(value="' . implode('" or value="', $project_types) . '")';
+  foreach ($requests as $name => $request) {
+    $xml = _drush_pm_get_release_history_xml($request);
+
+    // Unpublished project?
+    $project_status = $xml->xpath('/project/project_status');
+    if ($project_status[0][0] == 'unpublished') {
+      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished and has no releases available.", array('!project' => $name)), 'warning');
+      continue;
     }
+
+    $project_info = _drush_pm_get_releases_from_xml($xml, $name);
+    $info[$name] = $project_info;
+  }
+  return $info;
+}
+
+/**
+ * Download the release history xml for the specified release.
+ */
+function _drush_pm_get_release_history_xml($request) {
+  // Don't rely on UPDATE_DEFAULT_URL since perhaps we are not fully
+  // bootstrapped.
+  $url = drush_get_option('source', 'http://updates.drupal.org/release-history') . '/' . $request['name'] . '/' . $request['drupal_version'];
+  drush_log('Downloading release history from ' . $url);
+  // Some hosts have allow_url_fopen disabled.
+  if (!$xml = @simplexml_load_file($url)) {
+    $filename = _drush_download_file($url);
+    $xml = simplexml_load_file($filename);
+    drush_op('unlink', $filename);
+  }
+  if (!$xml) {
+    // We are not getting here since drupal.org always serves an XML response.
+    drush_set_error('DRUSH_PM_DOWNLOAD_FAILED', dt('Could not download project status information from !url', array('!url' => $url)));
+    continue;
+  }
+  if ($error = $xml->xpath('/error')) {
+    drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]);
+    continue;
   }
+  return $xml;
 }
 
 /**
@@ -1735,29 +1917,14 @@ function drush_pm_download() {
   foreach ($requests as $name => $request) {
     // Cleanup. We need to unset those variables to avoid using garbage from
     // previous iteration.
-    unset($error, $release, $releases, $types);
+    unset($release, $releases, $types);
 
-    // Don't rely on UPDATE_DEFAULT_URL since perhaps we are not fully
-    // bootstrapped.
-    $url = drush_get_option('source', 'http://updates.drupal.org/release-history') . '/' . $request['name'] . '/' . $request['drupal_version'];
-    drush_log('Downloading release history from ' . $url);
-    // Some hosts have allow_url_fopen disabled.
-    if (!$xml = @simplexml_load_file($url)) {
-      if (!drush_shell_exec("wget $url")) {
-        drush_shell_exec("curl -O $url");
-      }
-      $filename = explode('/', $url);
-      $filename = array_pop($filename);
-      $xml = simplexml_load_file($filename);
-      drush_op('unlink', $filename);
-    }
-    if (!$xml) {
-      // We are not getting here since drupal.org always serves an XML response.
-      drush_set_error('DRUSH_PM_DOWNLOAD_FAILED', dt('Could not download project status information from !url', array('!url' => $url)));
-      continue;
-    }
-    if ($error = $xml->xpath('/error')) {
-      drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]);
+    $xml = _drush_pm_get_release_history_xml($request);
+    
+    // Unpublished project?
+    $project_status = $xml->xpath('/project/project_status');
+    if ($project_status[0][0] == 'unpublished') {
+      drush_set_error('DRUSH_PM_PROJECT_UNPUBLISHED', dt("Project !project is unpublished and has no releases available.", array('!project' => $name)), 'warning');
       continue;
     }
 
@@ -1791,6 +1958,24 @@ function drush_pm_download() {
     if (!empty($recommended_releases)) {
       $releases = $recommended_releases;
     }
+    $release_type = 'recommended';
+    if (drush_get_option('dev', FALSE)) {
+      $releases = @$xml->xpath("/project/releases/release[status='published'][version_extra='dev']");
+      $release_type = 'development';
+    }
+    if (drush_get_option('select', FALSE) || empty($releases)) {
+      $options = _drush_pm_download_releases_choice($xml, $request['name'], drush_get_option('all', FALSE), drush_get_option('dev', FALSE));
+      if (empty($releases)) {
+        drush_print(dt('There is no !type release for project !project.', array('!type' => $release_type, '!project' => $request['name'])));
+      }
+      $choice = drush_choice($options, dt('Choose one of the available releases:'));
+      if ($choice) {
+        $releases = $xml->xpath("/project/releases/release[status='published'][version='" . $choice . "']");
+      }
+      else {
+        continue;
+      }
+    }
     if (empty($releases)) {
       drush_log(dt('There is no *recommended* release for project !project on Drupal !drupal_version. Ask the maintainer to review http://drupal.org/node/197584 and create/recommend a release in order to be compatible with drush and the drupal.org security broadcast system. A recommended development snapshot release is sufficient. Alternatively, run pm-releases command and explicity pm-download any non-recommended release that might be available.', array('!drupal_version' => $request['drupal_version'], '!project' => $request['name'])), 'ok');
       continue;
Index: includes/command.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/command.inc,v
retrieving revision 1.89
diff -u -p -r1.89 command.inc
--- includes/command.inc	5 Dec 2010 07:27:32 -0000	1.89
+++ includes/command.inc	6 Dec 2010 07:26:44 -0000
@@ -949,17 +949,28 @@ function drush_enforce_requirement_boots
  *   TRUE if command is valid.
  */
 function drush_enforce_requirement_drupal_dependencies(&$command) {
-  if (empty($command['drupal dependencies'])) {
-    return TRUE;
+  // If the command bootstrap is DRUSH_BOOTSTRAP_MAX, then we will 
+  // allow the requirements to pass if we have not successfully
+  // bootstrapped Drupal.  The combination of DRUSH_BOOTSTRAP_MAX
+  // and 'drupal dependencies' indicates that the drush command
+  // will use the dependent modules only if they are available.
+  if ($command['bootstrap'] == DRUSH_BOOTSTRAP_MAX) {
+    // If we have not bootstrapped, then let the dependencies pass;
+    // if we have bootstrapped, then enforce them.
+    if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') < DRUSH_BOOTSTRAP_DRUPAL_FULL) {
+      return TRUE;
+    }
   }
-  else {
+  // If there are no drupal dependencies, then do nothing
+  if (!empty($command['drupal dependencies'])) {
     foreach ($command['drupal dependencies'] as $dependency) {
-      if (function_exists('module_exists') && module_exists($dependency)) {
-        return TRUE;
+      if(!function_exists('module_exists') || !module_exists($dependency)) {
+        $command['bootstrap_errors']['DRUSH_COMMAND_DEPENDENCY_ERROR'] = dt('Command !command needs the following modules installed/enabled to run: !dependencies.', array('!command' => $command['command'], '!dependencies' => implode(', ', $command['drupal dependencies'])));
+        return FALSE;
       }
     }
   }
-  $command['bootstrap_errors']['DRUSH_COMMAND_DEPENDENCY_ERROR'] = dt('Command !command needs the following modules installed/enabled to run: !dependencies.', array('!command' => $command['command'], '!dependencies' => implode(', ', $command['drupal dependencies'])));
+  return TRUE;
 }
 
 /**
Index: includes/drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/drush.inc,v
retrieving revision 1.155
diff -u -p -r1.155 drush.inc
--- includes/drush.inc	5 Dec 2010 21:40:37 -0000	1.155
+++ includes/drush.inc	6 Dec 2010 07:26:45 -0000
@@ -767,6 +767,50 @@ function dt($string, $args = array()) {
 }
 
 /**
+ * Convert html to readable text.  Calls through
+ * to drupal_html_to_text if it is available, or
+ * reverts to a simpler algorithm when it is not.
+ *
+ * @param string $html
+ *   The html text to convert.
+ *
+ * @return string
+ *   The plain-text representation of the input.
+ */
+function drush_html_to_text($html, $allowed_tags = NULL) {
+  if (function_exists('drupal_html_to_text')) {
+    return drupal_html_to_text($html, $allowed_tags);
+  }
+  else {
+    $replacements = array(
+      '<hr>' => '------------------------------------------------------------------------------',
+      '<li>' => ' * ',  
+    );
+    $text = str_replace(array_keys($replacements), array_values($replacements), $html);
+    return preg_replace('/ *<[^>]*> */', ' ', $text);
+  }
+}
+
+/**
+ * Download a file using wget or curl.
+ *
+ * @param string $url
+ *   The path to the file to download
+ *
+ * @return string
+ *   The filename that was downloaded
+ */
+function _drush_download_file($url) {
+  if (!drush_shell_exec("wget $url")) {
+    drush_shell_exec("curl -O $url");
+  }
+  $filename = explode('/', $url);
+  $filename = array_pop($filename);
+  
+  return $filename;
+}
+
+/**
  * Convert a csv string, or an array of items which
  * may contain csv strings, into an array of items.
  *
@@ -1245,19 +1289,27 @@ function drush_confirm($msg, $indent = 0
 function drush_choice($options, $prompt = 'Enter a number.', $label = '!value') {
   print dt($prompt) . "\n";
 
-  drush_print('  [0] : Cancel');
+  $rows[] = array('[0]', ':', 'Cancel');
   $selection_number = 0;
   foreach ($options as $key => $option) {
     if ((substr($key, 0, 3) == '-- ') && (substr($key, -3) == ' --')) {
-      drush_print("  ".$option);
-      continue;
+      $rows[] = array('', '', $option);
+    }
+    else {
+      $selection_number++;
+      $row = array("[$selection_number]", ':');
+      if (is_array($option)) {
+        $row = array_merge($row, $option);
+      }
+      else {
+        $row[] = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
+      }
+      $rows[] = $row;
+      $selection_list[$selection_number] = $key;
     }
-    $selection_number++;
-    $message = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
-    drush_print(dt("  [!number] : !message", array('!number' => $selection_number, '!message' => $message)));
-    $selection_list[$selection_number] = $key;
   }
-
+  drush_print_table($rows);
+  
   while ($line = trim(fgets(STDIN))) {
     if (array_key_exists($line, $selection_list)) {
       return $selection_list[$line];
Index: includes/environment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/environment.inc,v
retrieving revision 1.106
diff -u -p -r1.106 environment.inc
--- includes/environment.inc	3 Dec 2010 20:53:02 -0000	1.106
+++ includes/environment.inc	6 Dec 2010 07:26:46 -0000
@@ -27,6 +27,15 @@ define('DRUSH_DRUPAL_BOOTSTRAP', 'includ
 define('DRUSH_BOOTSTRAP_NONE', -1);
 
 /**
+ * Use drush_bootstrap_max instead of drush_bootstrap_to_phase
+ *
+ * This constant is only usable as the value of the 'bootstrap'
+ * item of a command object, or as the parameter to
+ * drush_bootstrap_to_phase.  It is not a real bootstrap state.
+ */
+define('DRUSH_BOOTSTRAP_MAX', -2);
+
+/**
  * Only bootstrap Drush, without any Drupal specific code.
  *
  * Any code that operates on the Drush installation, and not specifically
@@ -277,6 +286,15 @@ function drush_bootstrap_validate($phase
  *   Only attempt bootstrap to the specified level.
  */
 function drush_bootstrap_to_phase($max_phase_index) {
+  // If $max_phase_index is DRUSH_BOOTSTRAP_MAX, then
+  // we will bootstrap as far as we can.  drush_bootstrap_max
+  // is different than drush_bootstrap_to_phase in that
+  // it is not an error if DRUSH_BOOTSTRAP_LOGIN is not reached.
+  if ($max_phase_index == DRUSH_BOOTSTRAP_MAX) {
+    drush_bootstrap_max();
+    return TRUE;
+  }
+  
   drush_log(dt("Bootstrap to phase !phase.", array('!phase' => $max_phase_index)), 'bootstrap');
   $phases = _drush_bootstrap_phases();
   $result = TRUE;
