? .example.drushrc.php.swp
? drushrc.php
? drushrc.phpe
? example.drushrc.phpe
Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/README.txt,v
retrieving revision 1.22
diff -u -p -r1.22 README.txt
--- README.txt  14 May 2009 07:02:18 -0000      1.22
+++ README.txt  28 May 2009 00:06:55 -0000
@@ -1,4 +1,4 @@
-// $Id: README.txt,v 1.22 2009/05/14 07:02:18 grugnog Exp $
+// $Id: README.txt,v 1.23 2009/05/22 15:19:57 grugnog Exp $
 
 DESCRIPTION
 -----------
@@ -33,7 +33,7 @@ For Linux/Unix/Mac:
        executes the drush.php file directly:
        $ alias drush='/path/to/php/php /path/to/drush/drush.php'
        If you do this, to allow Drush to detect the number of available columns,
-       you need to add the line 'export COLUMNS' to the the .profile file in your
+       you need to add the line 'export COLUMNS' to the .profile file in your
        home folder.
 
   4. Start using drush by running "drush" from your Drupal root directory.
Index: drush.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/drush.php,v
retrieving revision 1.64
diff -u -p -r1.64 drush.php
--- drush.php   6 May 2009 19:35:59 -0000       1.64
+++ drush.php   28 May 2009 00:06:55 -0000
@@ -60,6 +60,33 @@ function drush_main() {
   foreach ($phases as $phase) {
     if (drush_bootstrap($phase)) {
       $command = drush_parse_command();
+
+      // Process a remote command if 'remote_host' option is set.
+      $remote_host = drush_get_option('remote_host');
+      if(isset($remote_host)) {         
+        
+       $args = drush_get_arguments();
+        $command = array_shift($args);
+        $data = drush_get_context('options');
+        foreach (array('r', 'root', 'l', 'uri', 'v', 'd', 'i') as $key) {
+         unset($data[$key]);
+       }
+        foreach (array('root', 'uri') as $key) {
+         $data[$key] = drush_get_option($key);
+       }
+       
+        $drush_path = drush_get_option('drush');
+        $remote_user = drush_get_option('remote_user');
+        $output = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
+       
+        // If we set INTEGRATE to FALSE, we can print the output as shown below:
+       // using 'drush_print' introduces an extra blank line at
+       // the end of the input.
+        //drush_print($output['output']);
+       
+        return;
+      }
+
       if (is_array($command)) {
         if ($command['bootstrap'] == $phase && empty($command['bootstrap_errors'])) {
           drush_log(dt("Found command: !command", array('!command' => $command['command'])), 'bootstrap');
Index: example.drushrc.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/example.drushrc.php,v
retrieving revision 1.11
diff -u -p -r1.11 example.drushrc.php
--- example.drushrc.php 19 Apr 2009 03:13:09 -0000      1.11
+++ example.drushrc.php 28 May 2009 00:06:55 -0000
@@ -78,3 +78,46 @@ $options['structure-tables'] = array(
 #   'theme_default' => 'minnelli',
 #   'anonymous' => 'Visitor',
 # );
+
+/**
+ * Site aliases:
+ *
+ * To create aliases to remote Drupal installations, add entries
+ * to the site-aliases option array here.  These settings are
+ * only respected by "drush sql load" and "drush sync".
+ *
+ * Each entry in the 'site-aliases' array is accessed by its
+ * site alias (e.g. 'stage' or 'dev').  The following settings
+ * are stored in the site alias record:
+ *
+ * - 'db_url': The database connection string from settings.php.
+ * - 'uri': The fully-qualified domain name of the remote system
+ * hosting the Drupal instance.  If not specified, the site alias
+ * is used.  When making site aliases for local Drupal instances,
+ * it is important that the uri be exactly the same as the folder
+ * name for the site as stored in the 'sites' folder (or "localhost").
+ * - 'ssh-user': The username to log in as when using rsync
+ * - 'ssh-password': The password to use with rsync
+ * - 'path-aliases': An array of aliases for common rsync targets.
+ * Relative aliases are always taken from the Drupal root.  The
+ * '!root' alias must not be specified as a relative path.
+ *
+ * Remove the leading hash signs to enable.
+ */
+#$options['site-aliases']['stage'] = array(
+#    'db_url' => 'pgsql://username:password@dbhost.com:port/databasename',
+#    'uri' => 'stage.mydrupalsite.com',
+#    'ssh-user' => 'publisher',
+#    'ssh-password' => 'secretsecret',
+#    'path-aliases' => array(
+#      '!root' => '/path/to/remote/drupal/root',
+#      '!files' => 'sites/mydrupalsite.com/files',
+#      '!data' => '/my/arbitrary/path',    )
+#  );
+#$options['site-aliases']['dev'] = array(
+#    'db_url' => 'pgsql://username:password@localhost/databasename',
+#    'uri' => 'dev.mydrupalsite.com',
+#    'path-aliases' => array(
+#      '!data' => '/my/other/path',    )
+#  );
+
Index: commands/core/core.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v
retrieving revision 1.27
diff -u -p -r1.27 core.drush.inc
--- commands/core/core.drush.inc        8 May 2009 03:46:15 -0000       1.27
+++ commands/core/core.drush.inc        28 May 2009 00:06:55 -0000
@@ -82,13 +82,14 @@ function core_drush_command() {
     
   );
   $items['sync'] = array(
-    'description' => 'Rsync the Drupal tree to/from another server using ssh.',
+    'description' => 'Rsync the Drupal tree to/from another server using ssh.  Relative paths start from the Drupal root folder if a site alias is used; otherwise they start from the current working directory.',
     'arguments' => array(
-      'source' => 'See rsync documentation.',
-      'destination' => 'See rsync documentation.',
+      'source' => 'See rsync documentation and example.drushrc.php.',
+      'destination' => 'See rsync documentation and example.drushrc.php.',
     ),
     'examples' => array(
-      'sync dev:/var/www/files/ stage:/var/www/files/' => 'Rsync \'files\' dir from dev to stage',
+      'sync dev:. stage:.' => 'Rsync entire Drupal tree from dev to stage (one of which must be local).',
+      'sync ./ stage:!files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on stage.',
     ),
   );
   $items['eval'] = array(
@@ -327,15 +328,26 @@ function drush_core_cache_clear() {
  * @return void
  **/
 function drush_core_sync($source, $destination) {
-  // Local paths are relative to Drupal root
-  $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
+  $options = '-az';
+  $remote_user = '';
+  $pwfile = '';
   
-  if (!strstr($source, ':')) {
-    $source = $drupal_root. "/$source";
-  }
-  if (!strstr($destination, ':')) {
-    $destination = $drupal_root . "/$destination";
+  // One of $souruce or $destination (at most) will be remote;
+  // get the site alias settings for the remote component.
+  $remote_alias_settings = _drush_core_get_site_alias_settings_for_remote($source, $destination);
+  if (!empty($remote_alias_settings)) {
+    if (array_key_exists('ssh-user', $remote_alias_settings)) {
+      $remote_user = $remote_alias_settings['ssh-user'] . '@';
+    }
+    if (array_key_exists('ssh-password', $remote_alias_settings)) {
+      $pwfile = drush_save_data_to_temp_file($remote_alias_settings['ssh-password']);
+      $options = "$options --password-file $pwfile";
+    }    
   }
+  
+  // expand file paths
+  $source = _drush_core_expand_path($source, $remote_user);
+  $destination = _drush_core_expand_path($destination, $remote_user);
 
   // Prompt for confirmation. This is destructive.
   if (!drush_get_context('DRUSH_SIMULATE')) {
@@ -345,14 +357,122 @@ function drush_core_sync($source, $desti
     }
   }
 
-  $options = '-az';
   $exec = "rsync -e ssh $options --exclude \"*.svn*\" $source $destination";
   if (drush_get_context('drush_log')) {
     // the drush_op() will be verbose about the command that gets executed.
     $options .= 'v';
   }
 
-  return drush_op('system', $exec) !== FALSE;
+  $exec_result=drush_op('system', $exec) !== FALSE;
+  if (!empty($pwfile)) {
+    drush_op('unlink', $pwfile);
+  }
+  
+  return $exec_result;
+}
+
+// a path is "machine:/path" or "machine:path" or "/path" or "path"
+function _drush_core_expand_path($path, $remote_user) {
+  $machine = '';
+  $path_aliases = array();
+  // Parse site aliases if there is a colon in the path
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+    $machine = $alias;
+    $path = substr($path, $colon_pos + 1);
+    $site_alias_settings=_drush_core_get_site_alias_settings($alias);
+    if (!empty($site_alias_settings)) {
+      if (array_key_exists('path-aliases', $site_alias_settings)) {
+        $path_aliases = $site_alias_settings['path-aliases'];
+      }
+      // Use 'uri' from settings if available; otherwise use alias name itself 
+      if (array_key_exists('uri', $site_alias_settings)) {
+        $machine = $site_alias_settings['uri'];
+      }
+    }
+    // Strip the machine portion of the path if the
+    // alias points to the local machine.
+    if (_drush_core_is_local_machine($machine)) {
+      $machine = '';
+    }
+    else {
+      $machine = "$remote_user$machine:";
+    }
+  }
+  // If path aliases does not contain an alias for '!root',
+  // then assume that the remote Drupal root is the same
+  // as the local Drupal root.
+  if (!array_key_exists('!root', $path_aliases)) {
+    $path_aliases['!root'] = drush_get_context('DRUSH_DRUPAL_ROOT');
+  }
+  // If path aliases does not contain an alias for '!files',
+  // and no machine alias was specified, then fill in file_directory_path().
+  if ((!array_key_exists('!files', $path_aliases)) && (empty($alias))) {
+    $path_aliases['!files'] = file_directory_path();
+  }
+  // Expand all relative path aliases to be based off of the Drupal root
+  foreach ($path_aliases as $key => $value) {
+    if (($value[0] != '/') && ($key != 'root')) {
+      $path_aliases[$key] = $path_aliases['!root'] . '/' . $path_aliases[$key];
+    }
+  }
+  // Check for path aliases if the path does not start with a '/'.
+  if ($path[0] != '/') {
+    $path = dt($path, $path_aliases);
+  }
+  // If we have a relative path that was used with an alias, then make
+  // it relative to the Drupal root.
+  if (($path[0] != '/') && (!empty($alias))) {
+      $path = $path_aliases['!root'] . '/' . $path;
+  }
+  return "$machine$path";
+}
+
+function _drush_core_is_local_machine($alias) {
+  // To do:  the directory in 'sites' might contain
+  // a component for the port or local path, so it
+  // would be better to do a 'contains' test on all
+  // files in 'sites' to determine if 'alias' is local.
+  // This might give a false positive, though: a remote
+  // 'mysite.com' would look local if there was a local
+  // 'dev.mysite.com'.
+  return ($alias == 'localhost') || ($alias == '127.0.0.1') || (file_exists(drush_get_context('DRUSH_DRUPAL_ROOT') . "/sites/$alias"));
+}
+
+function _drush_core_get_site_alias_settings($alias) {
+  $site_alias_settings = array();
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $site_alias_settings = $all_site_aliases[$alias];
+  }
+  return $site_alias_settings;
+}
+
+function _drush_core_get_site_alias_settings_if_remote($path) {
+  $site_alias_settings = array();
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+    if (!_drush_core_is_local_machine($alias)) {
+      $site_alias_settings = _drush_core_get_site_alias_settings($alias);
+    }
+  }
+  return $site_alias_settings;
+}
+
+function _drush_core_get_site_alias_settings_for_remote($source, $destination) {
+  $source_settings = _drush_core_get_site_alias_settings_if_remote($source);
+  $destination_settings = _drush_core_get_site_alias_settings_if_remote($destination);
+  /*if ((!empty($source_settings)) && (!empty($destination_settings))) {
+    return FALSE;
+  }
+  else*/if (!empty($source_settings)) {
+    return $source_settings;
+  }
+  else {
+    return $destination_settings;
+  }
 }
 
 /**
Index: commands/sql/sql.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/sql/sql.drush.inc,v
retrieving revision 1.17
diff -u -p -r1.17 sql.drush.inc
--- commands/sql/sql.drush.inc  11 May 2009 20:53:10 -0000      1.17
+++ commands/sql/sql.drush.inc  28 May 2009 00:06:55 -0000
@@ -54,6 +54,7 @@ function sql_drush_command() {
       'drush sql dump --result-file=../18.sql' => 'Save SQL dump to the directory above Drupal root.',
       'drush sql dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.com',
     ),
+    //  '--cache' => 'Skip dump is result file exists and is less than "cache" hours old. Optional.',
     'options' => array(
       '--result-file' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
       '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
@@ -83,6 +84,25 @@ function sql_drush_command() {
       'from' => 'Name of subdirectory within /sites. Its settings.php defines where to dump from.',
       'to' => 'Name of subdirectory within /sites. Its settings.php defines the destination for the dump.',
     ),
+    'optional-arguments' => TRUE,
+    'options' => array(
+      '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
+      '--structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
+      '--source_database' => 'The SOURCE DB connection key if using multiple connections in settings.php.',
+      '--destination_database' => 'The DESTINATION DB connection key if using multiple DB connections',
+    ),
+  );
+  $items['sql sync'] = array(
+    'callback' => 'drush_sql_sync',
+    'description' => 'Copy source database to target database using rsync.',
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
+    'examples' => array(
+      'drush sql sync prod dev' => 'Copy the DB defined in sites/prod to the DB in sites/dev.',
+    ),
+    'arguments' => array(
+      'from' => 'Name of subdirectory within /sites or a site alias.',
+      'to' => 'Name of subdirectory within /sites or a site alias.',
+    ),
     'options' => array(
       '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
       '--structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
@@ -115,7 +135,7 @@ function drush_sql_conf() {
 /**
  * Command callback. Emits a connect string for mysql or pgsql.
  */
-function drush_sql_connect() {
+function _drush_sql_connect() {
   switch (_drush_sql_get_scheme()) {
     case 'mysql':
       $command = 'mysql ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
@@ -126,7 +146,11 @@ function drush_sql_connect() {
       $command .= _drush_sql_get_credentials();
       break;
   }
-  drush_print($command);
+  return $command;
+}
+
+function drush_sql_connect() {
+  drush_print(_drush_sql_connect());
 }
 
 /**
@@ -254,6 +278,95 @@ function drush_sql_query($query) {
   }
 }
 
+function drush_sql_sync($source, $destination) {
+  drush_expand_site_specification($source, 'source_');
+  drush_expand_site_specification($destination, 'target_');
+  
+  // Prompt for confirmation. This is destructive.
+  if (!drush_get_context('DRUSH_SIMULATE')) {
+    $txt_source = $source;
+    $txt_destination = $destination;
+    drush_print(dt("You will destroy data from !target and replace with data from !source.", array('!source' => $txt_source, '!target' => $txt_destination)));
+    drush_print();
+    // TODO: actually make the backup if desired.
+    drush_print(dt("You might want to make a backup first, using sql_dump command.\n"));
+    if (!drush_confirm(dt('Do you really want to continue?'))) {
+      drush_die('Aborting.');
+    }
+  }
+  
+  // Set up the result file and the remote file.
+  // If the result file is not set, then create a temporary file.
+  // If the remote file is not set, use the same name for the remote
+  // and local files and hope for the best.
+  $local_file = drush_get_option('result-file');
+  if (!isset($local_file)) {
+    $local_file = tempnam(sys_get_temp_dir(), 'sql');
+  }
+  $remote_file = drush_get_option('remote-file');
+  if (!isset($remote_file)) {
+    $remote_file = $local_file;
+  }
+  
+  // If source is remote, then use ssh to dump the database and then rsync to local machine
+  // If source is local, call drush_sql_dump to dump the database to local machine
+  
+  $source = _drush_sql_get_spec_from_options('source_');
+  $source_remote_host = drush_get_option('source_remote_host');
+  $source_remote_port = drush_get_option('source_remote_port');
+  
+  if (isset($source_remote_host)) {
+    if (isset($source_remote_port)) {
+      $source['port'] = $source_remote_port;
+    }
+    $source_remote_user = drush_get_option('source_remote_user'); 
+    if (isset($source_remote_user)) {
+      $source_at ='@';
+      $source_remote_pass = drush_get_option('source_remote_pass') ? ':' . drush_get_option('source_remote_pass') : '';
+    }
+
+    drush_set_option('result-file', $remote_file);
+    $dump_exec = drush_sql_dump($source);
+    $dump_exec = 'ssh ' . $source_remote_user . $source_at . $source_remote_host . ' ' . escapeshellarg($dump_exec);
+    $dump_exec = $dump_exec . '; rsync ' . $source_remote_user . $source_remote_pass . $source_at . $source_remote_host . ':' . $remote_file . ' ' . $local_file;
+  }
+  else {
+    drush_set_option('result-file', $local_file);
+    $dump_exec = drush_sql_dump($dump_exec);
+  }
+  
+  drush_op('system', $dump_exec);
+  
+  // IF FAILURE THEN ABORT
+  
+  // If destination is remote, then use rsync to push the database, then use ssh to import the database
+  // If destination is local, then just import the database locally
+  
+  $target = _drush_sql_get_spec_from_options('target_');
+  $target_remote_host = drush_get_option('target_remote_host');
+  $target_remote_port = drush_get_option('target_remote_port');
+  
+  if (isset($target_remote_host)) {
+    if (isset($target_remote_port)) {
+      $target['port'] = $target_remote_port;
+    }
+    $target_remote_user = drush_get_option('target_remote_user'); 
+    if (isset($target_remote_user)) {
+      $target_at ='@';
+      $target_remote_pass = drush_get_option('target_remote_pass') ? ':' . drush_get_option('target_remote_pass') : '';
+    }
+    
+    $connect_exec = '`' . _drush_sql_connect($target) . '` < ' . $remote_file;
+    $import_exec = 'rsync ' . $local_file . ' ' . $target_remote_user . $target_remote_pass . $target_at . $target_remote_host . ':' . $remote_file;
+    $import_exec = $import_exec . '; ssh ' . $target_remote_user . $target_at . $target_remote_host . ' ' . escapeshellarg($connect_exec);
+  }
+  else {
+    $import_exec = '`' . _drush_sql_connect($target) . '` < ' . $local_file;
+  }
+  
+  drush_op('system', $import_exec);
+}
+
 /**
  * Copy an entire database to another database. For example, migrate from production to dev
  * or dev to staging.
@@ -267,8 +380,10 @@ function drush_sql_query($query) {
  *    be replaced with the contents of `source`. *
  **/
 function drush_sql_load($source, $destination) {
-  $source = _drush_sql_get_spec_from_settings("./sites/$source/settings.php", 'source');
-  $destination = _drush_sql_get_spec_from_settings("./sites/$destination/settings.php", 'target');
+  # $source = _drush_sql_get_spec_from_settings("./sites/$source/settings.php", 'source');
+  # $destination = _drush_sql_get_spec_from_settings("./sites/$destination/settings.php", 'target');
+  $source = _drush_sql_get_spec_from_site_alias($source, 'source');
+  $destination = _drush_sql_get_spec_from_site_alias($destination, 'target');
   
   // Prompt for confirmation. This is destructive.
   if (!drush_get_context('DRUSH_SIMULATE')) {
@@ -367,6 +482,36 @@ function _drush_sql_get_db_spec() {
   }
 }
 
+function _drush_sql_get_spec_and_site_alias($alias, $prefix) {
+  global $db_url;
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $site_alias_settings = $all_site_aliases[$alias];
+    $db_url = $site_alias_settings['db_url'];
+    $db_spec = _drush_sql_get_db_spec();
+    if (($prefer_remote_port == TRUE) && (array_key_exists('db_remote_port', $site_alias_settings))) {
+      $db_spec['port'] = $site_alias_settings['db_remote_port'];
+    }
+    return array('alias_settings' => $site_alias_settings, 'db_spec' => $db_spec);
+  }
+  return array('db_spec' => _drush_sql_get_spec_from_settings("./sites/$alias/settings.php", $prefix));
+}
+
+// Build a $db_spec from a given site alias
+function _drush_sql_get_spec_from_site_alias($alias, $prefix) {
+  global $db_url;
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $site_alias_settings = $all_site_aliases[$alias];
+    $db_url = $site_alias_settings['db_url'];
+    return _drush_sql_get_db_spec();
+  }
+#  if ($db_url = drush_get_option($alias . '_db_url')) {
+#    return _drush_sql_get_db_spec();
+#  }
+  return _drush_sql_get_spec_from_settings("./sites/$alias/settings.php", $prefix);
+}
+
 // Build a $db_spec from a given settings.php.
 function _drush_sql_get_spec_from_settings($file, $prefix) {
   // Reset global databases to whatever is defined in $file
@@ -384,6 +529,22 @@ function _drush_sql_get_spec_from_settin
   return _drush_sql_get_db_spec();
 }
 
+function _drush_sql_get_spec_from_options($prefix) {
+  global $db_url;
+  
+  $db_url = drush_get_option($prefix . 'db_url');
+    
+  // Translate the custom options for sql sync into ones usually recognized (i.e. no prefix).
+  if ($database = drush_get_option($prefix . 'database')) {
+    drush_set_option('database', $value);
+  }
+  if ($target = drush_get_option($prefix . 'target')) {
+    drush_set_option('target', $target);
+  }
+
+  return _drush_sql_get_db_spec();
+}
+
 function _drush_sql_get_scheme($db_spec = NULL) {
   if (is_null($db_spec)) {
     $db_spec = _drush_sql_get_db_spec();
@@ -415,14 +576,14 @@ function _drush_sql_get_credentials($db_
       }
 
       $cred = $db_spec['database'] . ' ' .
-         (empty($db_spec['host']) || ($db_spec['host'] == "localhost") ? '' : '-h ' . $db_spec['host']) . ' ' .
-         (empty($db_spec['port']) ? '' : '-p' . $db_spec['port']) . ' ';
+         (empty($db_spec['host'])  ? '-h localhost ' : '-h ' . $db_spec['host']) . ' ' .
+         (empty($db_spec['port']) ? '-p 5432 ' : '-p ' . $db_spec['port']) . ' ';
       // Adding '-U' will cause Postgres to prompt for a password, so disable this for now.
       // Use "sudo -u postgres drush sql ..." to access the database without a password,
       // presuming that "postgres" is the database superuser and you have
       // "local all all ident sameuser" in your Postgres pg_hba.conf file.
       // See: http://drupal.org/node/438828
-      // $cred .= '-U ' . $db_spec['username'] . ' ';
+      $cred .= '-U ' . $db_spec['username'] . ' ';
       break;
   }
   return escapeshellcmd($cred);
Index: includes/backend.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/backend.inc,v
retrieving revision 1.12
diff -u -p -r1.12 backend.inc
--- includes/backend.inc        3 May 2009 03:45:35 -0000       1.12
+++ includes/backend.inc        28 May 2009 00:06:56 -0000
@@ -212,7 +212,13 @@ function _drush_proc_open($cmd, $data = 
  *   If the command was completed, this will return an associative array containing the data from drush_backend_output().
  */
 function drush_backend_invoke($command, $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL) {
-  $cmd = _drush_backend_generate_command($command, $data, $method, $drush_path, $hostname, $username);
+  $args = explode(" ", $command);
+  $command = array_shift($args);
+  return drush_backend_invoke_args($command, $args, $data, $method, $integrate, $drush_path, $hostname, $username);
+}
+
+function drush_backend_invoke_args($command, $args, $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL) {
+  $cmd = _drush_backend_generate_command($command, $args, $data, $method, $drush_path, $hostname, $username);
   drush_log(dt('Running: !cmd', array('!cmd' => $cmd)), 'command');
   return _drush_backend_invoke($cmd, $data, $integrate);
 }
@@ -254,9 +260,11 @@ function _drush_backend_invoke($cmd, $da
 
 /**
  * Generate a command to execute.
-
+ *
  * @param command
  *    A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'.
+ * @param args
+ *    An array or arguments for the command.
  * @param data
  *    Optional. An array containing options to pass to the remote script.
  *    Array items with a numeric key are treated as optional arguments to the command.
@@ -282,20 +290,18 @@ function _drush_backend_invoke($cmd, $da
  * @return
  *   A text string representing a fully escaped command.
  */
-function _drush_backend_generate_command($command, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null) {
+function _drush_backend_generate_command($command, $args, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null) {
   $drush_path = !is_null($drush_path) ? $drush_path : DRUSH_COMMAND; // Call own drush.php file.
   $data['root'] = ($data['root']) ? $data['root'] : drush_get_context('DRUSH_DRUPAL_ROOT');
   $data['uri'] = array_key_exists('uri', $data) ? $data['uri'] : drush_get_context('DRUSH_URI');
 
   $option_str = _drush_backend_argument_string($data, $method);
-  $args = explode(" ", $command);
   foreach ($data as $key => $arg) {
     if (is_numeric($key)) {
       $args[] = $arg;
       unset($data[$key]);
     }
   }
-  $command = '';
   foreach ($args as $arg) {
     $command .= ' ' . escapeshellarg($arg);
   }
@@ -321,7 +327,9 @@ function _drush_backend_generate_command
  */
 function drush_backend_fork($command, $data, $drush_path = null, $hostname = null, $username = null) {
   $data['quiet'] = TRUE;
-  $cmd = "(" . _drush_backend_generate_command($command, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null';
+  $args = explode(" ", $command);
+  $command = array_shift($args);
+  $cmd = "(" . _drush_backend_generate_command($command, $args, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null';
   drush_log(dt("Forking : !cmd", array('!cmd' => $cmd)));
   system($cmd);
 }
Index: includes/command.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/command.inc,v
retrieving revision 1.31
diff -u -p -r1.31 command.inc
--- includes/command.inc        11 May 2009 14:43:34 -0000      1.31
+++ includes/command.inc        28 May 2009 00:06:56 -0000
@@ -147,6 +147,16 @@ function drush_get_commands() {
  */
 function drush_parse_command() {
   $args = drush_get_arguments();
+  
+  // Test to see if the first arg is a site specification
+  if (drush_expand_site_specification($args[0])) {
+    array_shift($args);
+    // We only need to expand the site specification
+    // once, then we are done.  (Should we set a flag indicating
+    // that we already did this?  Otherwise 'drush alias1 alias2 command'
+    // would behave strangely.)
+    drush_set_arguments($args);
+  }
 
   // Get a list of all implemented commands.
   $implemented = drush_get_commands();
@@ -156,6 +166,7 @@ function drush_parse_command() {
   // Try to determine the handler for the current command.
   while (!$command && count($args)) {
     $part = implode(" ", $args);
+    drush_log(dt("part is !part", array('!part' => $part)));
     if (isset($implemented[$part])) {
       $command = $implemented[$part];
     }
@@ -167,7 +178,15 @@ function drush_parse_command() {
   // We have found a command that matches. Set the appropriate values.
   if ($command) {
     $arguments = array_reverse($arguments);
-
+    // If the command has the 'optional-arguments' flag set, then
+    // insure that there are enough arguments provided on the command
+    // line.  Keep adding null parameters until there are enough.
+    if (isset($command['optional-arguments']) && isset($command['arguments'])) {
+      while (count($arguments) < count($command['arguments'])) {
+        $arguments[] = null;
+      }
+    }
+    
     // Merge specified callback arguments, which precede the arguments passed on the command line.
     if (isset($command['callback arguments']) && is_array($command['callback arguments'])) {
       $arguments = array_merge($command['callback arguments'], $arguments);
Index: includes/environment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/environment.inc,v
retrieving revision 1.31
diff -u -p -r1.31 environment.inc
--- includes/environment.inc    12 May 2009 00:17:24 -0000      1.31
+++ includes/environment.inc    28 May 2009 00:06:56 -0000
@@ -325,6 +325,9 @@ function _drush_bootstrap_drush() {
   // Load a drushrc.php file in the ~/.drush directory.
   drush_load_config('user');
 
+  // If the '-l' / '--uri' option is a site alias, then process it
+  _drush_process_site_alias();
+  
   $backend = drush_set_context('DRUSH_BACKEND', drush_get_option(array('b', 'backend')));
 
   if ($backend) {
@@ -359,6 +362,92 @@ function _drush_bootstrap_drush() {
   _drush_find_commandfiles(DRUSH_BOOTSTRAP_DRUSH);
 }
 
+function _drush_process_site_alias() {
+  return drush_expand_site_alias(drush_get_option(array('l', 'uri')));
+}
+
+function drush_expand_site_alias($alias, $prefix = '') {
+  if(isset($alias)) {
+    // Test to see if uri is a site alias
+    $all_site_aliases = drush_get_option('site-aliases', array());
+    if (array_key_exists($alias, $all_site_aliases)) {
+      $site_alias_settings = $all_site_aliases[$alias];
+      
+      // QUESTION:  Right now we only set specified options in the site alias settings.
+      // Optionally, we could do everything, and special-case path-aliases.
+      // At the moment we hide db_url unless $prefix is set.  Not sure if this is necessary.
+      
+      $settings_to_expand = array('uri', 'remote_host', 'remote_user', 'remote_port', 'database', 'target');
+      if (!empty($prefix)) {
+        $settings_to_expand[] = 'db_url';
+      }
+      foreach ($settings_to_expand as $key) {
+       if (array_key_exists($key, $site_alias_settings)) {
+         drush_set_option($prefix . $key, $site_alias_settings[$key]);
+       }
+      }
+      if (array_key_exists('path-aliases', $site_alias_settings)) {
+       foreach (array('!root', '!drush') as $key) {
+         if (array_key_exists($key, $site_alias_settings['path-aliases'])) {
+           drush_set_option($prefix . substr($key, 1), $site_alias_settings['path-aliases'][$key]);
+         }
+       }
+      }
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+// Site specifications come in several forms:
+// 1.) /path/to/drupal#sitename
+// 2.) user@server/path/to/drupal#sitename
+// 3.) user@server/path/to/drupal            (sitename == server)
+// 4.) user@server#sitename                  (only if $option['r'] already set)
+// 5.) sitename                              (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)      
+function drush_expand_site_specification($alias, $prefix = '') {
+  // First test to see if it is a site alias.
+  $result = drush_expand_site_alias($alias, $prefix);
+  if (isset($alias)) {
+    // If it is not a site alias, perhaps is is a site specification
+    if ($result == FALSE) {
+      // Cases 1.) - 4.):
+      // We will check for a site specification if the alias has at least
+      // two characters from the set '@', '/', '#'.
+      if ((strpos($alias, '@') == FALSE ? 0 : 1) + (strpos($alias, '/') == FALSE ? 0 : 1) + (strpos($alias, '#') == FALSE ? 0 : 1) >= 2) {
+        // Add on a scheme so that "user:pass@server" will always parse correctly
+        $parsed = parse_url('site://' . $alias);
+        foreach (array('host' => 'remote_host', 'user' => 'remote_user', 'pass' => 'remote_pass', 'path' => 'root', 'fragment' => 'uri') as $url_key => $option_key) {
+          if (array_key_exists($url_key, $parsed)) {
+            drush_set_option($option_key, $parsed[$url_key]);
+         }
+        }
+        // Case 3.): If the URL contains a 'host' portion but no fragment, then set the uri to the host
+       // Note: We presume that 'server' is the best default for case 3; without this code, the default would
+       // be whatever is set in $options['l'] on the target machine's drushrc.php settings file.
+        if (array_key_exists('host', $parsed) && !array_key_exists('fragment', $parsed)) {
+          drush_set_option('uri', $parsed['host']);
+        }
+        $result = TRUE;
+      }
+      // Case 5.):
+      // If the alias is the name of a folder in the 'sites' directory, 
+      // then use it as a local site specification.
+      // BUG WORKAROUND:  if we use 'drush_locate_root' we will set 'uri' after 
+      // drush_bootstrap_drush, which works.  If we use drush_get_context('DRUSH_DRUPAL_ROOT'),
+      // then we set 'uri' after drush_bootstrap_drupal_root, which seems like it should work
+      // but doesn't.
+      $drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
+      // drush_get_context('DRUSH_DRUPAL_ROOT');
+      if (isset($drupal_root) && file_exists($drupal_root . '/sites/' . $alias . '/settings.php')) {
+        drush_set_option('uri', $alias);
+        $result = TRUE;
+      }
+    }
+  }
+  return $result;
+}
+
 /**
  * Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
  *
