e0d57cfa2d69b94ad31ec4f040f5486205d8ede0
[yaffs-website] / command.inc
1 <?php
2
3 use Drush\Log\LogLevel;
4 use Webmozart\PathUtil\Path;
5 use Consolidation\AnnotatedCommand\AnnotationData;
6 use Drush\Command\DrushInputAdapter;
7 use Drush\Command\DrushOutputAdapter;
8 use Consolidation\AnnotatedCommand\CommandData;
9
10 /**
11  * @defgroup dispatching Command dispatching functions.
12  * @{
13  *
14  * These functions handle command dispatching, and can
15  * be used to programatically invoke drush commands in
16  * different ways.
17  */
18
19 /**
20  * Invokes a Drush API call, including all hooks.
21  *
22  * Executes the specified command with the specified arguments on the currently
23  * bootstrapped site using the current option contexts. Note that it will not
24  * bootstrap any further than the current command has already bootstrapped;
25  * therefore, you should only invoke commands that have the same (or lower)
26  * bootstrap requirements.
27  *
28  * Commands execute with the same options that the user provided on the
29  * commandline. If you need to invoke another Drush command with options you
30  * specify, use drush_invoke_process() instead.
31  *
32  * @param string $command
33  *   The command to invoke.
34  * @param array $arguments
35  *   An array of argument to pass into the command.
36  *
37  * @return mixed|false
38  *   The return value from drush_dispatch() or FALSE on error.
39  *
40  * @see drush_invoke_process()
41  */
42 function drush_invoke($command, $arguments = array()) {
43   // Convert a standalone argument to a single-element array.
44   if (!is_array($arguments)) {
45     $arguments = array($arguments);
46   }
47   $commands = drush_get_commands();
48   if (array_key_exists($command, $commands)) {
49     $command = $commands[$command];
50     // Drush overloads the 'arguments' element, which contains the help string
51     // for the allowed arguments for the command when fetched, and is fixed up
52     // by _drush_prepare_command() to contain the actual commandline arguments
53     // during dispatching.
54     $command['arguments'] = array();
55     return drush_dispatch($command, $arguments);
56   }
57   else {
58     return drush_set_error('DRUSH_COMMAND_NOT_FOUND', dt("The drush command '!command' could not be found.", array('!command' => $command)));
59   }
60 }
61
62 /**
63  * Invoke a command in a new process, targeting the site specified by
64  * the provided site alias record.
65  *
66  * @param array $site_alias_record
67  *  The site record to execute the command on.  Use '@self' to run on the current site.
68  * @param string $command_name
69  *  The command to invoke.
70  * @param array $commandline_args
71  *  The arguments to pass to the command.
72  * @param array $commandline_options
73  *  The options (e.g. --select) to provide to the command.
74  * @param mixed $backend_options
75  *   TRUE - integrate errors
76  *   FALSE - do not integrate errors
77  *   array - @see drush_backend_invoke_concurrent
78  *     There are also several options that _only_ work when set in
79  *     this parameter.  They include:
80  *      'invoke-multiple'
81  *        If $site_alias_record represents a single site, then 'invoke-multiple'
82  *        will cause the _same_ command with the _same_ arguments and options
83  *        to be invoked concurrently (e.g. for running concurrent batch processes).
84  *      'concurrency'
85  *        Limits the number of concurrent processes that will run at the same time.
86  *        Defaults to '4'.
87  *      'override-simulated'
88  *        Forces the command to run, even in 'simulated' mode. Useful for
89  *        commands that do not change any state on the machine, e.g. to fetch
90  *        database information for sql-sync via sql-conf.
91  *      'interactive'
92  *        Overrides the backend invoke process to run commands interactively.
93  *      'fork'
94  *        Overrides the backend invoke process to run non blocking commands in
95  *        the background. Forks a new process by adding a '&' at the end of the
96  *        command. The calling process does not receive any output from the child
97  *        process. The fork option is used to spawn a process that outlives its
98  *        parent.
99  *
100  * @return
101  *   If the command could not be completed successfully, FALSE.
102  *   If the command was completed, this will return an associative
103  *   array containing the results of the API call.
104  *   @see drush_backend_get_result()
105  *
106  * Do not change the signature of this function!  drush_invoke_process
107  * is one of the key Drush APIs.  See http://drupal.org/node/1152908
108  */
109 function drush_invoke_process($site_alias_record, $command_name, $commandline_args = array(), $commandline_options = array(), $backend_options = TRUE) {
110   if (is_array($site_alias_record) && array_key_exists('site-list', $site_alias_record)) {
111     list($site_alias_records, $not_found) = drush_sitealias_resolve_sitespecs($site_alias_record['site-list']);
112     if (!empty($not_found)) {
113       drush_log(dt("Not found: @list", array("@list" => implode(', ', $not_found))), LogLevel::WARNING);
114       return FALSE;
115     }
116     $site_alias_records = drush_sitealias_simplify_names($site_alias_records);
117     foreach ($site_alias_records as $alias_name => $alias_record) {
118       $invocations[] = array(
119         'site' => $alias_record,
120         'command' => $command_name,
121         'args' => $commandline_args,
122       );
123     }
124   }
125   else {
126     $invocations[] = array(
127       'site' => $site_alias_record,
128       'command' => $command_name,
129       'args' => $commandline_args);
130     $invoke_multiple = drush_get_option_override($backend_options, 'invoke-multiple', 0);
131     if ($invoke_multiple) {
132       $invocations = array_fill(0, $invoke_multiple, $invocations[0]);
133     }
134   }
135   return drush_backend_invoke_concurrent($invocations, $commandline_options, $backend_options);
136 }
137
138 /**
139  * Given a command record, dispatch it as if it were
140  * the original command.  Executes in the currently
141  * bootstrapped site using the current option contexts.
142  * Note that drush_dispatch will not bootstrap any further than the
143  * current command has already bootstrapped; therefore, you should only invoke
144  * commands that have the same (or lower) bootstrap requirements.
145  *
146  * @param command
147  *   A full $command such as returned by drush_get_commands(),
148  *   or a string containing the name of the command record from
149  *   drush_get_commands() to call.
150  * @param arguments
151  *   An array of argument values.
152  *
153  * @see drush_topic_docs_topic().
154  */
155 function drush_dispatch($command, $arguments = array()) {
156   drush_set_command($command);
157   $return = FALSE;
158
159   if ($command) {
160     // Add arguments, if this has not already been done.
161     // (If the command was fetched from drush_parse_command,
162     // then you cannot provide arguments to drush_dispatch.)
163     if (empty($command['arguments'])) {
164       _drush_prepare_command($command, $arguments);
165     }
166
167     // Merge in the options added by hooks.  We need this
168     // for validation, but this $command is just going to
169     // get thrown away, so we'll have to do this again later.
170     annotationcommand_adapter_add_hook_options($command);
171
172     // Add command-specific options, if applicable.
173     drush_command_default_options($command);
174
175     // Test to see if any of the options in the 'cli' context
176     // are not represented in the command structure.
177     if ((_drush_verify_cli_options($command) === FALSE) || (_drush_verify_cli_arguments($command) === FALSE)) {
178       return FALSE;
179     }
180
181     // Give command files an opportunity to alter the command record
182     drush_command_invoke_all_ref('drush_command_alter', $command);
183
184     // Include and validate command engines.
185     if (drush_load_command_engines($command) === FALSE) {
186       return FALSE;
187     }
188
189     // Do tilde expansion immediately prior to execution,
190     // so that tildes are passed through unchanged for
191     // remote commands and other redispatches.
192     drush_preflight_tilde_expansion($command);
193
194     // Get the arguments for this command. Add the options
195     // on the end if this is that kind of command.
196     $args = $command['arguments'];
197
198     // Call the callback function of the active command.
199     $return = call_user_func_array($command['callback'], $args);
200   }
201
202   // Add a final log entry, just so a timestamp appears.
203   drush_log(dt('Command dispatch complete'), LogLevel::NOTICE);
204
205   return $return;
206 }
207
208 /**
209  * Entry point for commands into the drush_invoke() API
210  *
211  * If a command does not have a callback specified, this function will be called.
212  *
213  * This function will trigger $hook_drush_init, then if no errors occur,
214  * it will call drush_invoke() with the command that was dispatch.
215  *
216  * If no errors have occured, it will run $hook_drush_exit.
217  */
218 function drush_command() {
219   $args = func_get_args();
220   $command = drush_get_command();
221   foreach (drush_command_implements("drush_init") as $name) {
222     $func = $name . '_drush_init';
223     if (drush_get_option('show-invoke')) {
224       drush_log(dt("Calling global init hook: !func", array('!name' => $name, '!func' => $func . '()')), LogLevel::BOOTSTRAP);
225     }
226     call_user_func_array($func, $args);
227     _drush_log_drupal_messages();
228   }
229
230   if (!drush_get_error()) {
231     $result = _drush_invoke_hooks($command, $args);
232   }
233
234   if (!drush_get_error()) {
235     foreach (drush_command_implements('drush_exit') as $name) {
236       $func = $name . '_drush_exit';
237       if (drush_get_option('show-invoke')) {
238         drush_log(dt("Calling global exit hook: !func", array('!name' => $name, '!func' => $func . '()')), LogLevel::BOOTSTRAP);
239       }
240       call_user_func_array($func, $args);
241       _drush_log_drupal_messages();
242     }
243   }
244 }
245
246 /**
247  * Invoke Drush API calls, including all hooks.
248  *
249  * This is an internal function; it is called from drush_dispatch via
250  * drush_command, but only if the command does not specify a 'callback'
251  * function.  If a callback function is specified, it will be called
252  * instead of drush_command + _drush_invoke_hooks.
253  *
254  * Executes the specified command with the specified arguments on the
255  * currently bootstrapped site using the current option contexts.
256  * Note that _drush_invoke_hooks will not bootstrap any further than the
257  * current command has already bootstrapped; therefore, you should only invoke
258  * commands that have the same (or lower) bootstrap requirements.
259  *
260  * Call the correct hook for all the modules that implement it.
261  * Additionally, the ability to rollback when an error has been encountered is also provided.
262  * If at any point during execution, the drush_get_error() function returns anything but 0,
263  * drush_invoke() will trigger $hook_rollback for each of the hooks that implement it,
264  * in reverse order from how they were executed.  Rollbacks are also triggered any
265  * time a hook function returns FALSE.
266  *
267  * This function will also trigger pre_$hook and post_$hook variants of the hook
268  * and its rollbacks automatically.
269  *
270  * HOW DRUSH HOOK FUNCTIONS ARE NAMED:
271  *
272  * The name of the hook is composed from the name of the command and the name of
273  * the command file that the command definition is declared in.  The general
274  * form for the hook filename is:
275  *
276  *      drush_COMMANDFILE_COMMANDNAME
277  *
278  * In many cases, drush commands that are functionally part of a common collection
279  * of similar commands will all be declared in the same file, and every command
280  * defined in that file will start with the same command prefix.  For example, the
281  * command file "pm.drush.inc" defines commands such as "pm-enable" and "pm-disable".
282  * In the case of "pm-enable", the command file is "pm", and and command name is
283  * "pm-enable".  When the command name starts with the same sequence of characters
284  * as the command file, then the repeated sequence is dropped; thus, the command
285  * hook for "pm-enable" is "drush_pm_enable", not "drush_pm_pm_enable".
286  *
287  * There is also a special Drupal-version-specific naming convention that may
288  * be used.  To hide a commandfile from all versions of Drupal except for the
289  * specific one named, add a ".dVERSION" after the command prefix.  For example,
290  * the file "views.d8.drush.inc" defines a "views" commandfile that will only
291  * load with Drupal 8.  This feature is not necessary and should not be used
292  * in contrib modules (any extension with a ".module" file), since these modules
293  * are already version-specific.
294  *
295  * @param command
296  *   The drush command to execute.
297  * @param args
298  *   An array of arguments to the command OR a single non-array argument.
299  * @return
300  *   The return value will be passed along to the caller if --backend option is
301  *   present. A boolean FALSE indicates failure and rollback will be intitated.
302  *
303  * This function should not be called directly.
304  * @see drush_invoke() and @see drush_invoke_process()
305  */
306 function _drush_invoke_hooks($command, $args) {
307   $return = null;
308   // If someone passed a standalone arg, convert it to a single-element array
309   if (!is_array($args)) {
310     $args = array($args);
311   }
312   // Include the external command file used by this command, if there is one.
313   drush_command_include($command['command-hook']);
314   // Generate the base name for the hook by converting all
315   // dashes in the command name to underscores.
316   $hook = str_replace("-", "_", $command['command-hook']);
317
318   // Call the hook init function, if it exists.
319   // If a command needs to bootstrap, it is advisable
320   // to do so in _init; otherwise, new commandfiles
321   // will miss out on participating in any stage that
322   // has passed or started at the time it was discovered.
323   $func = 'drush_' . $hook . '_init';
324   if (function_exists($func)) {
325     drush_log(dt("Calling drush command init function: !func", array('!func' => $func)), LogLevel::BOOTSTRAP);
326     call_user_func_array($func, $args);
327     _drush_log_drupal_messages();
328     if (drush_get_error()) {
329       drush_log(dt('The command @command could not be initialized.', array('@command' => $command['command-hook'])), LogLevel::ERROR);
330       return FALSE;
331     }
332   }
333
334   // We will adapt and call as many of the annotated command hooks as we can.
335   // The following command hooks are not supported in Drush 8.x:
336   //   - Command Event: not called (requires CommandEvent object)
337   //   - Option: Equivalent functionality supported in annotationcommand_adapter.inc
338   //   - Interact: not called (We don't use SymfonyStyle in 8.x at the moment)
339   //   - Status: not called - probably not needed?
340   //   - Extract not called - probably not needed?
341   // The hooks that are called include:
342   //   - Pre-initialize, initialize and post-initialize
343   //   - Pre-validate and validate
344   //   - Pre-command, command and post-command
345   //   - Pre-process, process and post-process
346   //   - Pre-alter, alter and post-alter
347
348   $names = annotationcommand_adapter_command_names($command);
349   // Merge in the options added by hooks (again)
350   annotationcommand_adapter_add_hook_options($command);
351   $annotationData = $command['annotations'];
352
353   $input = new DrushInputAdapter($args, annotationcommand_adapter_get_options($command), $command['command']);
354   $output = new DrushOutputAdapter();
355   $commandData = new CommandData(
356     $annotationData,
357     $input,
358     $output,
359     false,
360     false
361   );
362
363   annotationcommand_adapter_call_initialize($names, $commandData);
364
365   $rollback = FALSE;
366   $completed = array();
367   $available_rollbacks = array();
368   $all_available_hooks = array();
369
370   // Iterate through the different hook variations
371   $variations = array(
372     'pre_validate' => $hook . "_pre_validate",
373     'validate' => $hook . "_validate",
374     'pre_command' => "pre_$hook",
375     'command' => $hook,
376     'post_command' => "post_$hook"
377   );
378   foreach ($variations as $hook_phase => $var_hook) {
379
380     $adapterHookFunction = 'annotationcommand_adapter_call_hook_' . $hook_phase;
381     $adapterHookFunction($names, $commandData, $return);
382
383     // Get the list of command files.
384     // We re-fetch the list every time through
385     // the loop in case one of the hook function
386     // does something that will add additional
387     // commandfiles to the list (i.e. bootstrapping
388     // to a higher phase will do this).
389     $list = drush_commandfile_list();
390
391     // Make a list of function callbacks to call.  If
392     // there is a 'primary function' mentioned, make sure
393     // that it appears first in the list, but only if
394     // we are running the main hook ("$hook").  After that,
395     // make sure that any callback associated with this commandfile
396     // executes before any other hooks defined in any other
397     // commandfiles.
398     $callback_list = array();
399     if (($var_hook == $hook) && ($command['primary function'])) {
400       $callback_list[$command['primary function']] = $list[$command['commandfile']];
401     }
402     else {
403       $primary_func = ($command['commandfile'] . "_" == substr($var_hook . "_",0,strlen($command['commandfile']) + 1)) ? sprintf("drush_%s", $var_hook) : sprintf("drush_%s_%s", $command['commandfile'], $var_hook);
404       $callback_list[$primary_func] = $list[$command['commandfile']];
405     }
406     // We've got the callback for the primary function in the
407     // callback list; now add all of the other callback functions.
408     unset($list[$command['commandfile']]);
409     foreach ($list as $commandfile => $filename) {
410       $func = sprintf("drush_%s_%s", $commandfile, $var_hook);
411       $callback_list[$func] = $filename;
412     }
413     // Run all of the functions available for this variation
414     $accumulated_result = NULL;
415     foreach ($callback_list as $func => $filename) {
416       if (function_exists($func)) {
417         $all_available_hooks[] = $func . ' [* Defined in ' . $filename . ']';
418         $available_rollbacks[] = $func . '_rollback';
419         $completed[] = $func;
420         drush_log(dt("Calling hook !hook", array('!hook' => $func)), LogLevel::DEBUG);
421         try {
422           $result = call_user_func_array($func, $args);
423           drush_log(dt("Returned from hook !hook", array('!hook' => $func)), LogLevel::DEBUG);
424         }
425         catch (Exception $e) {
426           drush_set_error('DRUSH_EXECUTION_EXCEPTION', (string) $e);
427         }
428         // If there is an error, break out of the foreach
429         // $variations and foreach $callback_list
430         if (drush_get_error() || ($result === FALSE)) {
431           $rollback = TRUE;
432           break 2;
433         }
434         // If result values are arrays, then combine them all together.
435         // Later results overwrite earlier results.
436         if (isset($result) && is_array($accumulated_result) && is_array($result)) {
437           $accumulated_result = array_merge($accumulated_result, $result);
438         }
439         else {
440           $accumulated_result = $result;
441         }
442         _drush_log_drupal_messages();
443       }
444       else {
445         $all_available_hooks[] = $func;
446       }
447     }
448     // Process the result value from the 'main' callback hook only.
449     if ($var_hook == $hook) {
450       $return = $accumulated_result;
451       if (isset($return)) {
452         annotationcommand_adapter_call_hook_process_and_alter($names, $commandData, $return);
453         drush_handle_command_output($command, $return);
454       }
455     }
456   }
457
458   // If no hook functions were found, print a warning.
459   if (empty($completed)) {
460     $default_command_hook = sprintf("drush_%s_%s", $command['commandfile'], $hook);
461     if (($command['commandfile'] . "_" == substr($hook . "_",0,strlen($command['commandfile'])+ 1))) {
462       $default_command_hook = sprintf("drush_%s", $hook);
463     }
464     $dt_args = array(
465       '!command' => $command['command-hook'],
466       '!default_func' => $default_command_hook,
467     );
468     $message = "No hook functions were found for !command. The primary hook function is !default_func(). Please implement this function. Run with --show-invoke to see all available hooks.";
469     $return = drush_set_error('DRUSH_FUNCTION_NOT_FOUND', dt($message, $dt_args));
470   }
471   if (drush_get_option('show-invoke')) {
472     // We show all available hooks up to and including the one that failed (or all, if there were no failures)
473     drush_log(dt("Available drush_invoke() hooks for !command: !available", array('!command' => $command['command-hook'], '!available' => "\n" . implode("\n", $all_available_hooks))), LogLevel::OK);
474   }
475   if (drush_get_option('show-invoke') && !empty($available_rollbacks)) {
476     drush_log(dt("Available rollback hooks for !command: !rollback", array('!command' => $command['command-hook'], '!rollback' => "\n" . implode("\n", $available_rollbacks))), LogLevel::OK);
477   }
478
479   // Something went wrong, we need to undo.
480   if ($rollback) {
481     if (drush_get_option('confirm-rollback', FALSE)) {
482       // Optionally ask for confirmation, --yes and --no are ignored from here on as we are about to finish this process.
483       drush_set_context('DRUSH_AFFIRMATIVE', FALSE);
484       drush_set_context('DRUSH_NEGATIVE', FALSE);
485       $rollback = drush_confirm(dt('Do you want to rollback? (manual cleanup might be required otherwise)'));
486     }
487
488     if ($rollback) {
489       foreach (array_reverse($completed) as $func) {
490         $rb_func = $func . '_rollback';
491         if (function_exists($rb_func)) {
492           call_user_func_array($rb_func, $args);
493           _drush_log_drupal_messages();
494           drush_log(dt("Changes made in !func have been rolled back.", array('!func' => $func)), LogLevel::DEBUG);
495         }
496       }
497     }
498     $return = FALSE;
499   }
500
501   if (isset($return)) {
502     return $return;
503   }
504 }
505
506 /**
507  * Convert the structured output array provided from the Drush
508  * command into formatted output.  Output is only printed for commands
509  * that define 'default-format' &/or 'default-pipe-format'; all
510  * other commands are expected to do their own output.
511  */
512 function drush_handle_command_output($command, $structured_output) {
513   // If the hook already called drush_backend_set_result,
514   // then return that value. If it did not, then the return
515   // value from the hook will be the value returned from
516   // this routine.
517   $return = drush_backend_get_result();
518   if (empty($return)) {
519     drush_backend_set_result($structured_output);
520   }
521   // We skip empty strings and empty arrays, but note that 'empty'
522   // returns TRUE for the integer value '0', but we do want to print that.
523   // Only handle output here if the command defined an output format
524   // engine.  If no engine was declared, then we presume that the command
525   // handled its own output.
526   if ((!empty($structured_output) || ($structured_output === 0))) {
527     // If the command specifies a default pipe format and
528     // returned a result, then output the formatted output when
529     // in --pipe mode.
530     $formatter = drush_get_outputformat();
531     if (!$formatter && is_string($structured_output)) {
532       $formatter = drush_load_engine('outputformat', 'string');
533     }
534     if ($formatter) {
535       if ($formatter === TRUE) {
536         return drush_set_error(dt('No outputformat class defined for !format', array('!format' => $format)));
537       }
538       if ((!empty($command['engines']['outputformat'])) && (!in_array($formatter->engine, $command['engines']['outputformat']['usable']))) {
539         return $formatter->format_error(dt("The command '!command' does not produce output in a structure usable by this output format.", array('!command' => $command['command'])));
540       }
541       // Add any user-specified options to the metadata passed to the formatter.
542       $metadata = array();
543       $metadata['strict'] = drush_get_option('strict', FALSE);
544       if (isset($formatter->engine_config['options'])) {
545         $machine_parsable = $formatter->engine_config['engine-info']['machine-parsable'];
546         if (drush_get_option('full', FALSE)) {
547           if (isset($formatter->engine_config['fields-full'])) {
548             $formatter->engine_config['fields-default'] = $formatter->engine_config['fields-full'];
549           }
550           else {
551             $formatter->engine_config['fields-default'] = array_keys($formatter->engine_config['field-labels']);
552           }
553         }
554         elseif ((drush_get_context('DRUSH_PIPE') || $machine_parsable) && isset($formatter->engine_config['fields-pipe'])) {
555           $formatter->engine_config['fields-default'] = $formatter->engine_config['fields-pipe'];
556         }
557
558         // Determine the --format, and options relevant for that format.
559         foreach ($formatter->engine_config['options'] as $option => $option_info) {
560           $default_value = isset($formatter->engine_config[$option . '-default']) ? $formatter->engine_config[$option . '-default'] : FALSE;
561           if (($default_value === FALSE) && array_key_exists('default', $option_info)) {
562             $default_value = $option_info['default'];
563           }
564           if (isset($option_info['list'])) {
565             $user_specified_value = drush_get_option_list($option, $default_value);
566           }
567           else {
568             $user_specified_value = drush_get_option($option, $default_value);
569           }
570           if ($user_specified_value !== FALSE) {
571             if (array_key_exists('key', $option_info)) {
572               $option = $option_info['key'];
573             }
574             $metadata[$option] =$user_specified_value;
575           }
576         }
577       }
578       if (isset($metadata['fields']) && !empty($metadata['fields'])) {
579         if (isset($formatter->engine_config['field-labels'])) {
580           $formatter->engine_config['field-labels'] = drush_select_fields($formatter->engine_config['field-labels'], $metadata['fields'], $metadata['strict']);
581         }
582       }
583       $output = $formatter->process($structured_output, $metadata);
584       if (drush_get_context('DRUSH_PIPE')) {
585         drush_print_pipe($output);
586       }
587       else {
588         drush_print($output);
589       }
590     }
591   }
592 }
593
594 /**
595  * Fail with an error if the user specified options on the
596  * command line that are not documented in the current command
597  * record. Also verify that required options are present.
598  */
599 function _drush_verify_cli_options($command) {
600
601   // Start out with just the options in the current command record.
602   $options = _drush_get_command_options($command);
603   // Skip all tests if the command is marked to allow anything.
604   // Also skip backend commands, which may have options on the commandline
605   // that were inherited from the calling command.
606   if (($command['allow-additional-options'] === TRUE)) {
607     return TRUE;
608   }
609   // If 'allow-additional-options' contains a list of command names,
610   // then union together all of the options from all of the commands.
611   if (is_array($command['allow-additional-options'])) {
612     $implemented = drush_get_commands();
613     foreach ($command['allow-additional-options'] as $subcommand_name) {
614       if (array_key_exists($subcommand_name, $implemented)) {
615         $options = array_merge($options, _drush_get_command_options($implemented[$subcommand_name]));
616       }
617     }
618   }
619   // Also add in global options
620   $options = array_merge($options, drush_get_global_options());
621
622   // Add a placeholder option so that backend requests originating from prior versions of Drush are valid.
623   $options += array('invoke' => '');
624
625   // Now we will figure out which options in the cli context
626   // are not represented in our options list.
627   $cli_options = array_keys(drush_get_context('cli'));
628   $allowed_options = _drush_flatten_options($options);
629   $allowed_options = drush_append_negation_options($allowed_options);
630   $disallowed_options = array_diff($cli_options, $allowed_options);
631   if (!empty($disallowed_options)) {
632     $unknown = count($disallowed_options) > 1 ? dt('Unknown options') : dt('Unknown option');
633     if (drush_get_option('strict', TRUE)) {
634       $msg = dt("@unknown: --@options.  See `drush help @command` for available options. To suppress this error, add the option --strict=0.", array('@unknown' => $unknown, '@options' => implode(', --', $disallowed_options), '@command' => $command['command']));
635       return drush_set_error('DRUSH_UNKNOWN_OPTION', $msg);
636     }
637   }
638
639   // Next check to see if all required options were specified,
640   // and if all specified options with required values have values.
641   $missing_required_options = array();
642   $options_missing_required_values = array();
643   foreach ($command['options'] as $option_name => $option) {
644     if (is_array($option) && !empty($option['required']) && drush_get_option($option_name, NULL) === NULL) {
645       $missing_required_options[] = $option_name;
646     }
647     // Note that drush_get_option() will return TRUE if an option
648     // was specified without a value (--option), as opposed to
649     // the string "1" is --option=1 was used.
650     elseif (is_array($option) && !empty($option['value']) && ($option['value'] == 'required') && drush_get_option($option_name, NULL) === TRUE) {
651       $options_missing_required_values[] = $option_name;
652     }
653   }
654   if (!empty($missing_required_options) || !empty($options_missing_required_values)) {
655     $missing_message = '';
656     if (!empty($missing_required_options)) {
657       $missing = count($missing_required_options) > 1 ? dt('Missing required options') : dt('Missing required option');
658       $missing_message = dt("@missing: --@options.", array('@missing' => $missing, '@options' => implode(', --', $missing_required_options)));
659     }
660     if (!empty($options_missing_required_values)) {
661       if (!empty($missing_message)) {
662         $missing_message .= "  ";
663       }
664       $missing = count($options_missing_required_values) > 1 ? dt('Options used without providing required values') : dt('Option used without a value where one was required');
665       $missing_message .= dt("@missing: --@options.", array('@missing' => $missing, '@options' => implode(', --', $options_missing_required_values)));
666     }
667     return drush_set_error(dt("!message  See `drush help @command` for information on usage.", array('!message' => $missing_message, '@command' => $command['command'])));
668   }
669   return TRUE;
670 }
671
672 function drush_append_negation_options($allowed_options) {
673   $new_allowed = $allowed_options;
674   foreach ($allowed_options as $option) {
675     $new_allowed[] = 'no-' . $option;
676   }
677   return $new_allowed;
678 }
679
680 function _drush_verify_cli_arguments($command) {
681   // Check to see if all of the required arguments
682   // are specified.
683   if ($command['required-arguments']) {
684     $required_arg_count = $command['required-arguments'];
685     if ($required_arg_count === TRUE) {
686       $required_arg_count = count($command['argument-description']);
687     }
688
689     if (count($command['arguments']) < $required_arg_count) {
690       $missing = $required_arg_count > 1 ? dt('Missing required arguments') : dt('Missing required argument');
691       $required = array_slice(array_keys($command['argument-description']), 0, $required_arg_count);
692
693       return drush_set_error(dt("@missing: @required.  See `drush help @command` for information on usage.", array(
694         '@missing' => $missing,
695         '@required' => implode(", ", $required),
696         '@command' => $command['command'],
697       )));
698     }
699   }
700   return TRUE;
701 }
702
703 /**
704  * Return the list of all of the options for the given
705  * command record by merging the 'options' and 'sub-options'
706  * records.
707  */
708 function _drush_get_command_options($command) {
709   drush_command_invoke_all_ref('drush_help_alter', $command);
710   $options = $command['options'];
711   foreach ($command['sub-options'] as $group => $suboptions) {
712     $options = array_merge($options, $suboptions);
713   }
714   return $options;
715 }
716
717 /**
718  * Return the list of all of the options for the given
719  * command record including options provided by engines and additional-options.
720  */
721 function drush_get_command_options_extended($command) {
722   drush_merge_engine_data($command);
723
724   // Start out with just the options in the current command record.
725   $options = _drush_get_command_options($command);
726   // If 'allow-additional-options' contains a list of command names,
727   // then union together all of the options from all of the commands.
728   if (is_array($command['allow-additional-options'])) {
729     $implemented = drush_get_commands();
730     foreach ($command['allow-additional-options'] as $subcommand_name) {
731       if (array_key_exists($subcommand_name, $implemented)) {
732         $options = array_merge($options, _drush_get_command_options($implemented[$subcommand_name]));
733       }
734     }
735   }
736   return $options;
737 }
738
739 /**
740  * Return the array keys of $options, plus any 'short-form'
741  * representations that may appear in the option's value.
742  */
743 function _drush_flatten_options($options) {
744   $flattened_options = array();
745
746   foreach($options as $key => $value) {
747     // engine sections start with 'package-handler=git_drupalorg',
748     // or something similar.  Get rid of everything from the = onward.
749     if (($eq_pos = strpos($key, '=')) !== FALSE) {
750       $key = substr($key, 0, $eq_pos);
751     }
752     $flattened_options[] = $key;
753     if (is_array($value)) {
754       if (array_key_exists('short-form', $value)) {
755         $flattened_options[] = $value['short-form'];
756       }
757     }
758   }
759   return $flattened_options;
760 }
761
762 /**
763  * Get the options that were passed to the current command.
764  *
765  * This function returns an array that contains all of the options
766  * that are appropriate for forwarding along to drush_invoke_process.
767  *
768  * @return
769  *   An associative array of option key => value pairs.
770  */
771 function drush_redispatch_get_options() {
772   $options = array();
773
774   // Add in command-specific and alias options, but for global options only.
775   $options_soup = drush_get_context('specific') + drush_get_context('alias');
776   $global_option_list = drush_get_global_options(FALSE);
777   foreach ($options_soup as $key => $value) {
778     if (array_key_exists($key, $global_option_list)) {
779       $options[$key] = $value;
780     }
781   }
782
783   // Local php settings should not override sitealias settings.
784   $cli_context = drush_get_context('cli');
785   unset($cli_context['php'], $cli_context['php-options']);
786   // Pass along CLI parameters, as higher priority.
787   $options = $cli_context + $options;
788
789   $options = array_diff_key($options, array_flip(drush_sitealias_site_selection_keys()));
790   unset($options['command-specific']);
791   unset($options['path-aliases']);
792   // If we can parse the current command, then examine all contexts
793   // in order for any option that is directly related to the current command
794   $command = drush_parse_command();
795   if (is_array($command)) {
796     foreach (drush_get_command_options_extended($command) as $key => $value) {
797       $value = drush_get_option($key);
798       if (isset($value)) {
799         $options[$key] = $value;
800       }
801     }
802   }
803   // If --bootstrap-to-first-arg is specified, do not
804   // pass it along to remote commands.
805   unset($options['bootstrap-to-first-arg']);
806
807   return $options;
808 }
809
810 /**
811  * @} End of "defgroup dispatching".
812  */
813
814 /**
815  * @file
816  * The drush command engine.
817  *
818  * Since drush can be invoked independently of a proper Drupal
819  * installation and commands may operate across sites, a distinct
820  * command engine is needed.
821  *
822  * It mimics the Drupal module engine in order to economize on
823  * concepts and to make developing commands as familiar as possible
824  * to traditional Drupal module developers.
825  */
826
827 /**
828  * Parse console arguments.
829  */
830 function drush_parse_args() {
831   $args = drush_get_context('argv');
832   $command_args = NULL;
833   $global_options = array();
834   $target_alias_name = NULL;
835   // It would be nice if commandfiles could somehow extend this list,
836   // but it is not possible. We need to parse args before we find commandfiles,
837   // because the specified options may affect how commandfiles are located.
838   // Therefore, commandfiles are loaded too late to affect arg parsing.
839   // There are only a limited number of short options anyway; drush reserves
840   // all for use by drush core.
841   static $arg_opts = array('c', 'u', 'r', 'l', 'i');
842
843   // Check to see if we were executed via a "#!/usr/bin/env drush" script
844   drush_adjust_args_if_shebang_script($args);
845
846   // Now process the command line arguments.  We will divide them
847   // into options (starting with a '-') and arguments.
848   $arguments = $options = array();
849
850   for ($i = 1; $i < count($args); $i++) {
851     $opt = $args[$i];
852     // We set $command_args to NULL until the first argument that is not
853     // an alias is found (the command); we put everything that follows
854     // into $command_args.
855     if (is_array($command_args)) {
856       $command_args[] = $opt;
857     }
858     // Is the arg an option (starting with '-')?
859     if (!empty($opt) && $opt{0} == "-" && strlen($opt) != 1) {
860       // Do we have multiple options behind one '-'?
861       if (strlen($opt) > 2 && $opt{1} != "-") {
862         // Each char becomes a key of its own.
863         for ($j = 1; $j < strlen($opt); $j++) {
864           $options[substr($opt, $j, 1)] = TRUE;
865         }
866       }
867       // Do we have a longopt (starting with '--')?
868       elseif ($opt{1} == "-") {
869         if ($pos = strpos($opt, '=')) {
870           $options[substr($opt, 2, $pos - 2)] = substr($opt, $pos + 1);
871         }
872         else {
873           $options[substr($opt, 2)] = TRUE;
874         }
875       }
876       else {
877         $opt = substr($opt, 1);
878         // Check if the current opt is in $arg_opts (= has to be followed by an argument).
879         if ((in_array($opt, $arg_opts))) {
880           // Raising errors for missing option values should be handled by the
881           // bootstrap or specific command, so we no longer do this here.
882           $options[$opt] = $args[$i + 1];
883           $i++;
884         }
885         else {
886           $options[$opt] = TRUE;
887         }
888       }
889     }
890     // If it's not an option, it's a command.
891     else {
892       $arguments[] = $opt;
893       // Once we find the first argument, record the command args and global options
894       if (!is_array($command_args)) {
895         // Remember whether we set $target_alias_name on a previous iteration,
896         // then record the $target_alias_name iff this arguement references a valid site alias.
897         $already_set_target = is_string($target_alias_name);
898         if (!$already_set_target && drush_sitealias_valid_alias_format($opt)) {
899           $target_alias_name = $opt;
900         }
901         // If an alias record was set on a previous iteration, then this
902         // argument must be the command name.  If we set the target alias
903         // record on this iteration, then this is not the command name.
904         // If we've found the command name, then save $options in $global_options
905         // (all options that came before the command name), and initialize
906         // $command_args to an array so that we will begin storing all args
907         // and options that follow the command name in $command_args.
908         if ($already_set_target || (!is_string($target_alias_name))) {
909           $command_args = array();
910           $global_options = $options;
911         }
912       }
913     }
914   }
915   // If no arguments are specified, then the command will
916   // be either 'help' or 'version' (the latter if --version is specified)
917   // @todo: it would be handy if one could do `drush @remote st --help` and
918   // have that show help for st. Today, that shows --help for help command!
919   if (!count($arguments)) {
920     if (array_key_exists('version', $options)) {
921       $arguments = array('version');
922     }
923     else {
924       $arguments = array('help');
925     }
926   }
927   if (is_array($command_args)) {
928     drush_set_context('DRUSH_COMMAND_ARGS', $command_args);
929   }
930   drush_set_context('DRUSH_GLOBAL_CLI_OPTIONS', $global_options);
931
932   // Handle the "@shift" alias, if present
933   drush_process_bootstrap_to_first_arg($arguments);
934
935   drush_set_arguments($arguments);
936   drush_set_config_special_contexts($options);
937   drush_set_context('cli', $options);
938   return $arguments;
939 }
940
941 /**
942  * Pop an argument off of drush's argument list
943  */
944 function drush_shift() {
945   $arguments = drush_get_arguments();
946   $result = NULL;
947   if (!empty($arguments)) {
948     // The php-script command uses the DRUSH_SHIFT_SKIP
949     // context to cause drush_shift to skip the 'php-script'
950     // command and the script path argument when it is
951     // called from the user script.
952     $skip_count = drush_get_context('DRUSH_SHIFT_SKIP');
953     if (is_numeric($skip_count)) {
954       for ($i = 0; $i < $skip_count; $i++) {
955         array_shift($arguments);
956       }
957       $skip_count = drush_set_context('DRUSH_SHIFT_SKIP', 0);
958     }
959     $result = array_shift($arguments);
960     drush_set_arguments($arguments);
961   }
962   return $result;
963 }
964
965 /**
966  * Special checking for "shebang" script handling.
967  *
968  * If there is a file 'script.php' that begins like so:
969  *   #!/path/to/drush
970  * Then $args will be:
971  *   /path/to/drush /path/to/script userArg1 userArg2 ...
972  * If it instead starts like this:
973  *   #!/path/to/drush --flag php-script
974  * Then $args will be:
975  *   /path/to/drush "--flag php-script" /path/to/script userArg1 userArg2 ...
976  * (Note that execve does not split the parameters from
977  * the shebang line on whitespace; see http://en.wikipedia.org/wiki/Shebang_%28Unix%29)
978  * When drush is called via one of the "shebang" lines above,
979  * the first or second parameter will be the full path
980  * to the "shebang" script file -- and if the path to the
981  * script is in the second position, then we will expect that
982  * the argument in the first position must begin with a
983  * '@' (alias) or '-' (flag).  Under ordinary circumstances,
984  * we do not expect that the drush command must come before
985  * any argument that is the full path to a file.  We use
986  * this assumption to detect "shebang" script execution.
987  */
988 function drush_adjust_args_if_shebang_script(&$args) {
989   if (drush_has_bash()) {
990     // The drush.launcher script may add --php or --php-options at the
991     // head of the argument list; skip past those.
992     $base_arg_number = 1;
993     while (substr($args[$base_arg_number], 0, 5) == '--php') {
994       ++$base_arg_number;
995     }
996     if (_drush_is_drush_shebang_script($args[$base_arg_number])) {
997       // If $args[1] is a drush "shebang" script, we will insert
998       // the option "--bootstrap-to-first-arg" and the command
999       // "php-script" at the beginning of  @args, so the command
1000       // line args become:
1001       //   /path/to/drush --bootstrap-to-first-arg php-script /path/to/script userArg1 userArg2 ...
1002       drush_set_option('bootstrap-to-first-arg', TRUE);
1003       array_splice($args, $base_arg_number, 0, array('php-script'));
1004       drush_set_context('DRUSH_SHEBANG_SCRIPT', TRUE);
1005     }
1006     elseif (((strpos($args[$base_arg_number], ' ') !== FALSE) || (!ctype_alnum($args[$base_arg_number][0]))) && (_drush_is_drush_shebang_script($args[$base_arg_number + 1]))) {
1007       // If $args[2] is a drush "shebang" script, we will insert
1008       // the space-exploded $arg[1] in place of $arg[1], so the
1009       // command line args become:
1010       //   /path/to/drush scriptArg1 scriptArg2 ... /path/to/script userArg1 userArg2 ...
1011       // If none of the script arguments look like a drush command,
1012       // then we will insert "php-script" as the default command to
1013       // execute.
1014       $script_args = explode(' ', $args[$base_arg_number]);
1015       $has_command = FALSE;
1016       foreach ($script_args as $script_arg) {
1017         if (preg_match("/^[a-z][a-z0-9-]*$/",$script_arg)) {
1018           $has_command = TRUE;
1019         }
1020       }
1021       if (!$has_command) {
1022         $script_args[] = 'php-script';
1023       }
1024       array_splice($args, 1, $base_arg_number, $script_args);
1025       drush_set_context('DRUSH_SHEBANG_SCRIPT', TRUE);
1026     }
1027   }
1028 }
1029
1030 /**
1031  * Process the --bootstrap-to-first-arg option, if it is present.
1032  *
1033  * This option checks to see if the first user-provided argument is an alias
1034  * or site specification; if it is, it will be shifted into the first argument
1035  * position, where it will specify the site to bootstrap. The result of this
1036  * is that if your shebang line looks like this:
1037  *
1038  * #!/path/to/drush --bootstrap-to-first-arg php-script
1039  *
1040  * Then when you run that script, you can optionally provide an alias such
1041  * as @dev as the first argument (e.g. $ ./mydrushscript.php @dev scriptarg1
1042  * scriptarg2). Since this is the behavior that one would usually want,
1043  * it is default behavior for a canonical script. That is, a script
1044  * with a simple shebang line, like so:
1045  *
1046  * #!/path/to/drush
1047  *
1048  * will implicitly have "--bootstrap-to-first-arg" and "php-script" prepended, and will therefore
1049  * behave exactly like the first example. To write a script that does not
1050  * use --bootstrap-to-first-arg, then the drush command or at least one flag must be explicitly
1051  * included, like so:
1052  *
1053  * #!/path/to/drush php-script
1054  */
1055 function drush_process_bootstrap_to_first_arg(&$arguments) {
1056   if (drush_get_option('bootstrap-to-first-arg', FALSE)) {
1057     $shift_alias_pos = 1 + (drush_get_context('DRUSH_SHEBANG_SCRIPT') === TRUE);
1058     if (count($arguments) >= $shift_alias_pos) {
1059       $shifted_alias = $arguments[$shift_alias_pos];
1060       $alias_record = drush_sitealias_get_record($shifted_alias);
1061       if (!empty($alias_record)) {
1062         // Move the alias we shifted from its current position
1063         // in the argument list to the front of the list
1064         array_splice($arguments, $shift_alias_pos, 1);
1065         array_unshift($arguments, $shifted_alias);
1066       }
1067     }
1068   }
1069 }
1070
1071 /**
1072  * Get a list of all implemented commands.
1073  * This invokes hook_drush_command().
1074  *
1075  * @return
1076  *   Associative array of currently active command descriptors.
1077  *
1078  */
1079 function drush_get_commands($reset = FALSE) {
1080   static $commands = array();
1081
1082   if ($reset) {
1083     $commands = array();
1084     return;
1085   }
1086   elseif ($commands) {
1087     return $commands;
1088   }
1089
1090   $list = drush_commandfile_list();
1091   foreach ($list as $commandfile => $path) {
1092     if (drush_command_hook($commandfile, 'drush_command')) {
1093       $function = $commandfile . '_drush_command';
1094       $result = $function();
1095       foreach ((array)$result as $key => $command) {
1096         // Add some defaults and normalize the command descriptor.
1097         $command += drush_command_defaults($key, $commandfile, $path);
1098
1099         // Add engine data.
1100         drush_merge_engine_data($command);
1101
1102         // Translate command.
1103         drush_command_translate($command);
1104
1105         // If the command callback is not 'drush_command', then
1106         // copy the callback function to an alternate element
1107         // of the command array that will be called when Drush
1108         // calls the command function hooks.  Then, set the
1109         // callback to drush_command so that the function hooks
1110         // will be called.
1111         if (($command['callback'] != 'drush_command') && $command['invoke hooks']) {
1112           $command['primary function'] = $command['callback'];
1113           $command['callback'] = 'drush_command';
1114         }
1115
1116         $commands[$key] = $command;
1117       }
1118     }
1119   }
1120   $commands = array_merge($commands, annotationcommand_adapter_commands());
1121   foreach ($commands as $command) {
1122     // For every alias, make a copy of the command and store it in the command list
1123     // using the alias as a key
1124     if (isset($command['aliases']) && count($command['aliases'])) {
1125       foreach ($command['aliases'] as $alias) {
1126         $commands[$alias] = $command;
1127         $commands[$alias]['is_alias'] = TRUE;
1128       }
1129     }
1130   }
1131   return $commands;
1132 }
1133
1134 /**
1135  * Organize commands into categories. Used by help listing and core-cli.
1136  *
1137  * @param array $commands
1138  *   A commands array as per drush_get_commands().
1139  *
1140  * @return array $command_categories
1141  *   A categorized associative array of commands.
1142  */
1143 function drush_commands_categorize($commands) {
1144   $command_categories = array();
1145   $category_map = array();
1146   foreach ($commands as $key => $candidate) {
1147     if ((!array_key_exists('is_alias', $candidate) || !$candidate['is_alias']) && !$candidate['hidden']) {
1148       $category = $candidate['category'];
1149       // If we have decided to remap a category, remap every command
1150       if (array_key_exists($category, $category_map)) {
1151         $category = $category_map[$category];
1152       }
1153       if (!array_key_exists($category, $command_categories)) {
1154         $title = drush_command_invoke_all('drush_help', "meta:$category:title");
1155         $alternate_title = '';
1156         if (!$title) {
1157           // If there is no title, then check to see if the
1158           // command file is stored in a folder with the same
1159           // name as some other command file (e.g. 'core') that
1160           // defines a title.
1161           $alternate = basename($candidate['path']);
1162           $alternate_title = drush_command_invoke_all('drush_help', "meta:$alternate:title");
1163         }
1164         if (!empty($alternate_title)) {
1165           $category_map[$category] = $alternate;
1166           $category = $alternate;
1167           $title = $alternate_title;
1168         }
1169         $command_categories[$category]['title'] = empty($title) ? '' : $title[0];
1170         $summary = drush_command_invoke_all('drush_help', "meta:$category:summary");
1171         if ($summary) {
1172           $command_categories[$category]['summary'] = $summary[0];
1173         }
1174       }
1175       $candidate['category'] = $category;
1176       $command_categories[$category]['commands'][$key] = $candidate;
1177     }
1178   }
1179
1180   // Make sure that 'core' is always first in the list
1181   $core_category = array('core' => $command_categories['core']);
1182   unset($command_categories['core']);
1183
1184   // Post-process the categories that have no title.
1185   // Any that have fewer than 4 commands go into a section called "other".
1186   $processed_categories = array();
1187   $misc_categories = array();
1188   $other_commands = array();
1189   $other_categories = array();
1190   foreach ($command_categories as $key => $info) {
1191     if (empty($info['title'])) {
1192       $one_category = $key;
1193       if (count($info['commands']) < 4) {
1194         $other_commands = array_merge($other_commands, $info['commands']);
1195         $other_categories[] = $one_category;
1196       }
1197       else {
1198         $info['title'] = dt("All commands in !category", array('!category' => $key));
1199         $misc_categories[$one_category] = $info;
1200       }
1201     }
1202     else {
1203       $processed_categories[$key] = $info;
1204     }
1205   }
1206   $other_category = array();
1207   if (!empty($other_categories)) {
1208     $other_category[implode(',', $other_categories)] = array('title' => dt("Other commands"), 'commands' => $other_commands);
1209   }
1210   asort($processed_categories);
1211   asort($misc_categories);
1212   $command_categories = array_merge($core_category, $processed_categories, $misc_categories, $other_category);
1213
1214   // If the user specified --sort, then merge all of the remaining
1215   // categories together
1216   if (drush_get_option('sort', FALSE)) {
1217     $combined_commands = array();
1218     foreach ($command_categories as $key => $info) {
1219       $combined_commands = array_merge($combined_commands, $info['commands']);
1220     }
1221     $command_categories = array('all' => array('commands' => $combined_commands, 'title' => dt("Commands:")));
1222   }
1223
1224   return $command_categories;
1225 }
1226
1227 function drush_command_defaults($key, $commandfile, $path) {
1228   $defaults =  array(
1229     'command' => $key,
1230     'command-hook' => $key,
1231     'invoke hooks' => TRUE,
1232     'callback arguments' => array(),
1233     'commandfile' => $commandfile,
1234     'path' => dirname($path),
1235     'engines' => array(), // Helpful for drush_show_help().
1236     'callback' => 'drush_command',
1237     'primary function' => FALSE,
1238     'description' => NULL,
1239     'sections' => array(
1240       'examples' => 'Examples',
1241       'arguments' => 'Arguments',
1242       'options' => 'Options',
1243     ),
1244     'arguments' => array(),
1245     'required-arguments' => FALSE,
1246     'options' => array(),
1247     'sub-options' => array(),
1248     'allow-additional-options' => FALSE,
1249     'global-options' => array(),
1250     'examples' => array(),
1251     'aliases' => array(),
1252     'core' => array(),
1253     'scope' => 'site',
1254     'drush dependencies' => array(),
1255     'handle-remote-commands' => FALSE,
1256     'remote-tty' => FALSE,
1257     'strict-option-handling' => FALSE,
1258     'tilde-expansion' => TRUE,
1259     'bootstrap_errors' => array(),
1260     'topics' => array(),
1261     'hidden' => FALSE,
1262     'category' => $commandfile,
1263     'add-options-to-arguments' => FALSE,
1264     'consolidation-output-formatters' => FALSE,
1265     'annotated-command-callback' => '',
1266     'annotations' => new AnnotationData(['command' => $key]),
1267   );
1268   // We end up here, setting the defaults for a command, when
1269   // called from drush_get_global_options().  Early in the Drush
1270   // bootstrap, there will be no bootstrap object, because we
1271   // need to get the list of global options when loading config
1272   // files, and config files are loaded before the bootstrap object
1273   // is created.  In this early stage, we just use the core global
1274   // options list.  Later, the bootstrap object can also provide
1275   // additional defaults if needed.  The bootstrap command defaults
1276   // will be merged into the command object again just before
1277   // running it in bootstrap_and_dispatch().
1278   if ($bootstrap = drush_get_bootstrap_object()) {
1279     $defaults = array_merge($defaults, $bootstrap->command_defaults());
1280   }
1281   return $defaults;
1282 }
1283
1284 /**
1285  * Translates description and other keys of a command definition.
1286  *
1287  * @param $command
1288  *   A command definition.
1289  */
1290 function drush_command_translate(&$command) {
1291   $command['description'] = _drush_command_translate($command['description']);
1292   $keys = array('arguments', 'options', 'examples', 'sections');
1293   foreach ($keys as $key) {
1294     foreach ($command[$key] as $k => $v) {
1295       if (is_array($v)) {
1296         $v['description'] = _drush_command_translate($v['description']);
1297       }
1298       else {
1299         $v = _drush_command_translate($v);
1300       }
1301       $command[$key][$k] = $v;
1302     }
1303   }
1304 }
1305
1306 /**
1307  * Helper function for drush_command_translate().
1308  *
1309  * @param $source
1310  *   String or array.
1311  */
1312 function _drush_command_translate($source) {
1313   return is_array($source) ? call_user_func_array('dt', $source) : dt($source);
1314 }
1315
1316 /**
1317  * Matches a commands array, as returned by drush_get_arguments, with the
1318  * current command table.
1319  *
1320  * Note that not all commands may be discoverable at the point-of-call,
1321  * since Drupal modules can ship commands as well, and they are
1322  * not available until after bootstrapping.
1323  *
1324  * drush_parse_command returns a normalized command descriptor, which
1325  * is an associative array. Some of its entries are:
1326  * - callback arguments: an array of arguments to pass to the calback.
1327  * - callback: the function to run. Usually, this is 'drush_command', which
1328  *   will determine the primary hook for the function automatically.  Only
1329  *   specify a callback function if you need many commands to call the same
1330  *   function (e.g. drush_print_file).
1331  * - invoke hooks: If TRUE (the default), Drush will invoke all of the pre and
1332  *   post hooks for this command.  Set to FALSE to suppress hooks.  This setting
1333  *   is ignored unless the command 'callback' is also set.
1334  * - primary function: Drush will copy the 'callback' parameter here if
1335  *   necessary.  This value should not be set explicitly; use 'callback' instead.
1336  * - description: description of the command.
1337  * - arguments: an array of arguments that are understood by the command. for help texts.
1338  * - required-arguments: The minimum number of arguments that are required, or TRUE if all are required.
1339  * - options: an array of options that are understood by the command. for help texts.
1340  * - global-options: a list of options from the set of Drush global options (@see:
1341  *   drush_get_global_options()) that relate to this command.  The help for these
1342  *   options will be included in the help output for this command.
1343  * - examples: an array of examples that are understood by the command. for help texts.
1344  * - scope: one of 'system', 'project', 'site'.
1345  * - bootstrap: drupal bootstrap level (depends on Drupal major version). -1=no_bootstrap.
1346  * - core: Drupal major version required.
1347  * - drupal dependencies: drupal modules required for this command.
1348  * - drush dependencies: other drush command files required for this command.
1349  * - handle-remote-commands: set to TRUE if `drush @remote mycommand` should be executed
1350  *   locally rather than remotely dispatched.  When this mode is set, the target site
1351  *   can be obtained via:
1352  *     drush_get_context('DRUSH_TARGET_SITE_ALIAS')
1353  * - remote-tty: set to TRUE if Drush should force ssh to allocate a pseudo-tty
1354  *   when this command is being called remotely.  Important for interactive commands.
1355  *   Remote commands that allocate a psedo-tty always print "Connection closed..." when done.
1356  * - strict-option-handling: set to TRUE if drush should strictly separate local command
1357  *   cli options from the global options.  Usually, drush allows global cli options and
1358  *   command cli options to be interspersed freely on the commandline.  For commands where
1359  *   this flag is set, options are separated, with global options comming before the
1360  *   command names, and command options coming after, like so:
1361  *     drush --global-options command --command-options
1362  *   In this mode, the command options are no longer available via drush_get_option();
1363  *   instead, they can be retrieved via:
1364  *     $args = drush_get_original_cli_args_and_options();
1365  *     $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
1366  *   In this case, $args will contain the command args and options literally, exactly as they
1367  *   were entered on the command line, and in the same order as they appeared.
1368  * - 'outputformat': declares the data format to be used to render the
1369  *   command result.  In addition to the output format engine options
1370  *   listed below, each output format type can take additional metadata
1371  *   items that control the way that the output is rendered.  See the
1372  *   comment in each particular output format class for information. The
1373  *   Drush core output format engines can be found in commands/core/outputformat.
1374  *     - 'default': The default type to render output as. If declared, the
1375  *       command should not print any output on its own, but instead should
1376  *       return a data structure (usually an associative array) that can
1377  *       be rendered by the output type selected.
1378  *     - 'pipe-format': When the command is executed in --pipe mode, the
1379  *       command output will be rendered by the format specified by the
1380  *       pipe-format item instead of the default format.  Note that in
1381  *       either event, the user may specify the format to use via the
1382  *       --format command-line option.
1383  *     - 'formatted-filter': specifies a function callback that will be
1384  *       used to filter the command result if the selected output formatter
1385  *       is NOT declared to be machine-parsable.  "table" is an example of
1386  *       an output format that is not machine-parsable.
1387  *     - 'parsable-filter': function callback that will be used to filter the
1388  *       command result if the selected output formatter is declared to be
1389  *       machine-parsable. "var_export" is an example of an output format that
1390  *       is machine-parsable.
1391  *     - 'output-data-type': An identifier representing the data structure that
1392  *       the command returns.  @see outputformat_drush_engine_outputformat() for
1393  *       a description of the supported values.
1394  *     - 'field-labels': A mapping from machine name to human-readable name
1395  *       for all of the fields in a table-format command result.  All
1396  *       possible field names should appear in this list.
1397  *     - 'fields-default': A list of the machine names of the fields that
1398  *       should be displayed by default in tables.
1399  *     - 'private-fields': A list of any fields that contain sensitive
1400  *       information, such as passwords.  By default, Drush will hide private
1401  *       fields before printing the results to the console, but will include
1402  *       them in backend invoke results. Use --show-passwords to display.
1403  *     - 'column-widths': A mapping from field machine name to the column width
1404  *       that should be used in table output.  Drush will automatically
1405  *       calculate the width of any field not listed here based on the length
1406  *       of the data items in it.
1407  * - engines: declares information on Drush engines the command will load.
1408  *   Available engines can vary by command type.
1409  *
1410  * @return bool|array
1411  *   A command definition.
1412  */
1413 function drush_parse_command() {
1414   $args = drush_get_arguments();
1415   $command = FALSE;
1416
1417   // Get a list of all implemented commands.
1418   $implemented = drush_get_commands();
1419   if (!empty($args) && isset($implemented[$args[0]])) {
1420     $command = $implemented[$args[0]];
1421     $arguments = array_slice($args, 1);
1422   }
1423
1424   // We have found a command that matches. Set the appropriate values.
1425   if ($command) {
1426     // Special case. Force help command if --help option was specified.
1427     if (drush_get_option('help')) {
1428       $arguments = array($command['command']);
1429       $command = $implemented['helpsingle'];
1430       $command['arguments'] = $arguments;
1431       $command['allow-additional-options'] = TRUE;
1432     }
1433     else {
1434       _drush_prepare_command($command, $arguments);
1435     }
1436     drush_set_command($command);
1437   }
1438   return $command;
1439 }
1440
1441 /**
1442  * Called by drush_parse_command().  If a command is dispatched
1443  * directly by drush_dispatch(), then drush_dispatch() will call
1444  * this function.
1445  */
1446 function _drush_prepare_command(&$command, $arguments = array()) {
1447   // Drush overloads $command['arguments']; save the argument description
1448   if (!isset($command['argument-description'])) {
1449     $command['argument-description'] = $command['arguments'];
1450   }
1451   // Merge specified callback arguments, which precede the arguments passed on the command line.
1452   if (isset($command['callback arguments']) && is_array($command['callback arguments'])) {
1453     $arguments = array_merge($command['callback arguments'], $arguments);
1454   }
1455   $command['arguments'] = $arguments;
1456 }
1457
1458 /**
1459  * Invoke a hook in all available command files that implement it.
1460  *
1461  * @see drush_command_invoke_all_ref()
1462  *
1463  * @param $hook
1464  *   The name of the hook to invoke.
1465  * @param ...
1466  *   Arguments to pass to the hook.
1467  * @return
1468  *   An array of return values of the hook implementations. If commands return
1469  *   arrays from their implementations, those are merged into one array.
1470  */
1471 function drush_command_invoke_all() {
1472   $args = func_get_args();
1473   if (count($args) == 1) {
1474     $args[] = NULL;
1475   }
1476   $reference_value = $args[1];
1477   $args[1] = &$reference_value;
1478
1479   return call_user_func_array('drush_command_invoke_all_ref', $args);
1480 }
1481
1482 /**
1483  * A drush_command_invoke_all() that wants the first parameter to be passed by reference.
1484  *
1485  * @see drush_command_invoke_all()
1486  */
1487 function drush_command_invoke_all_ref($hook, &$reference_parameter) {
1488   $args = func_get_args();
1489   array_shift($args);
1490   // Insure that call_user_func_array can alter first parameter
1491   $args[0] = &$reference_parameter;
1492   $return = array();
1493   $modules = drush_command_implements($hook);
1494   if ($hook != 'drush_invoke_alter') {
1495     // Allow modules to control the order of hook invocations
1496     drush_command_invoke_all_ref('drush_invoke_alter', $modules, $hook);
1497   }
1498   foreach ($modules as $module) {
1499     $function = $module .'_'. $hook;
1500     $result = call_user_func_array($function, $args);
1501     if (isset($result) && is_array($result)) {
1502       $return = array_merge_recursive($return, $result);
1503     }
1504     else if (isset($result)) {
1505       $return[] = $result;
1506     }
1507   }
1508   return $return;
1509 }
1510
1511 /**
1512  * Determine which command files are implementing a hook.
1513  *
1514  * @param $hook
1515  *   The name of the hook (e.g. "drush_help" or "drush_command").
1516  *
1517  * @return
1518  *   An array with the names of the command files which are implementing this hook.
1519  */
1520 function drush_command_implements($hook) {
1521   $implementations[$hook] = array();
1522   $list = drush_commandfile_list();
1523   foreach ($list as $commandfile => $file) {
1524     if (drush_command_hook($commandfile, $hook)) {
1525       $implementations[$hook][] = $commandfile;
1526     }
1527   }
1528   return (array)$implementations[$hook];
1529 }
1530
1531 /**
1532  * @param string
1533  *   name of command to check.
1534  *
1535  * @return boolean
1536  *   TRUE if the given command has an implementation.
1537  */
1538 function drush_is_command($command) {
1539   $commands = drush_get_commands();
1540   return isset($commands[$command]);
1541 }
1542
1543 /**
1544  * @param string
1545  *   name of command or command alias.
1546  *
1547  * @return string
1548  *   Primary name of command.
1549  */
1550 function drush_command_normalize_name($command_name) {
1551   $commands = drush_get_commands();
1552   return isset($commands[$command_name]) ? $commands[$command_name]['command'] : $command_name;
1553 }
1554
1555 /**
1556  * Collect a list of all available drush command files.
1557  *
1558  * Scans the following paths for drush command files:
1559  *
1560  * - The "/path/to/drush/commands" folder.
1561  * - Folders listed in the 'include' option (see example.drushrc.php).
1562  * - The system-wide drush commands folder, e.g. /usr/share/drush/commands
1563  * - The ".drush" folder in the user's HOME folder.
1564  * - /drush and sites/all/drush in current Drupal site.
1565  * - Folders belonging to enabled modules in the current Drupal site.
1566  *
1567  * A Drush command file is a file that matches "*.drush.inc".
1568  *
1569  * @see drush_scan_directory()
1570  *
1571  * @return
1572  *   An associative array whose keys and values are the names of all available
1573  *   command files.
1574  */
1575 function drush_commandfile_list() {
1576   return commandfiles_cache()->get();
1577 }
1578
1579 function _drush_find_commandfiles($phase, $phase_max = FALSE) {
1580   drush_log(dt("Find command files for phase !phase (max=!max)", array('!phase' => $phase, '!max' => (string)$phase_max)), LogLevel::DEBUG);
1581   if ($bootstrap = drush_get_bootstrap_object()) {
1582     $searchpath = $bootstrap->commandfile_searchpaths($phase, $phase_max);
1583     _drush_add_commandfiles($searchpath, $phase);
1584     annotationcommand_adapter_discover($searchpath, $phase, $phase_max);
1585   }
1586 }
1587
1588 function _drush_add_commandfiles($searchpath, $phase = NULL, $reset = FALSE) {
1589   static $evaluated = array();
1590   $needs_sort = FALSE;
1591
1592   if (count($searchpath)) {
1593     if (!$reset) {
1594       // Assemble a cid specific to the bootstrap phase and searchpaths.
1595       // Bump $cf_version when making a change to a dev version of Drush
1596       // that invalidates the commandfile cache.
1597       $cf_version = 8;
1598       $cid = drush_get_cid('commandfiles-' . $phase, array(), array_merge($searchpath, array($cf_version)));
1599       $command_cache = drush_cache_get($cid);
1600       if (isset($command_cache->data)) {
1601         $cached_list = $command_cache->data;
1602         // If we want to temporarily ignore modules via 'ignored-modules',
1603         // then we need to take these out of the cache as well.
1604         foreach (drush_get_option_list('ignored-modules') as $ignored) {
1605           unset($cached_list[$ignored]);
1606         }
1607       }
1608     }
1609
1610     // Build a list of all of the modules to attempt to load.
1611     // Start with any modules deferred from a previous phase.
1612     $list = commandfiles_cache()->deferred();
1613     if (isset($cached_list)) {
1614       $list = array_merge($list, $cached_list);
1615     }
1616     else {
1617       // Scan for drush command files; add to list for consideration if found.
1618       foreach (array_unique($searchpath) as $path) {
1619         if (is_dir($path)) {
1620           $nomask = array_merge(drush_filename_blacklist(), drush_get_option_list('ignored-modules'));
1621           $dmv = DRUSH_MAJOR_VERSION;
1622           $files = drush_scan_directory($path, "/\.drush($dmv|)\.inc$/", $nomask);
1623           foreach ($files as $filename => $info) {
1624             $module = basename($filename);
1625             $module = preg_replace('/\.*drush[0-9]*\.inc/', '', $module);
1626             // Only try to bootstrap modules that we have never seen before.
1627             if (!array_key_exists($module, $evaluated) && file_exists($filename)) {
1628               $evaluated[$module] = TRUE;
1629               $list[$module] = Path::canonicalize($filename);
1630             }
1631           }
1632         }
1633       }
1634       if (isset($cid)) {
1635         drush_cache_set($cid, $list);
1636       }
1637     }
1638     // Check to see if the commandfile is valid for this version of Drupal
1639     // and is still present on filesystem (in case of cached commandfile list).
1640     foreach ($list as $module => $filename) {
1641       // Only try to require if the file exists. If not, a file from the
1642       // command file cache may not be available anymore, in which case
1643       // we rebuild the cache for this phase.
1644       if (file_exists($filename)) {
1645         // Avoid realpath() here as Drush commandfiles can have phar:// locations.
1646         $load_command = commandfiles_cache()->add($filename);
1647         if ($load_command) {
1648           $needs_sort = TRUE;
1649         }
1650       }
1651       elseif (!$reset) {
1652         _drush_add_commandfiles($searchpath, $phase, TRUE);
1653         $needs_sort = FALSE;
1654       }
1655     }
1656
1657     if ($needs_sort) {
1658       commandfiles_cache()->sort();
1659     }
1660   }
1661 }
1662
1663 /**
1664  * Substrings to ignore during commandfile and site alias searching.
1665  */
1666 function drush_filename_blacklist() {
1667   $blacklist = array('.', '..', 'drush_make', 'examples', 'tests', 'disabled', 'gitcache', 'cache');
1668   for ($v=4; $v<=(DRUSH_MAJOR_VERSION)+3; ++$v) {
1669     if ($v != DRUSH_MAJOR_VERSION) {
1670       $blacklist[] = 'drush' . $v;
1671     }
1672   }
1673   $blacklist = array_merge($blacklist, drush_get_option_list('exclude'));
1674   return $blacklist;
1675 }
1676
1677 /**
1678  * Conditionally include files based on the command used.
1679  *
1680  * Steps through each of the currently loaded commandfiles and
1681  * loads an optional commandfile based on the key.
1682  *
1683  * When a command such as 'pm-enable' is called, this
1684  * function will find all 'enable.pm.inc' files that
1685  * are present in each of the commandfile directories.
1686  */
1687 function drush_command_include($command) {
1688   $include_files = drush_command_get_includes($command);
1689   foreach($include_files as $filename => $commandfile) {
1690     drush_log(dt('Including !filename', array('!filename' => $filename)), LogLevel::BOOTSTRAP);
1691     include_once($filename);
1692   }
1693 }
1694
1695 function drush_command_get_includes($command) {
1696   $include_files = array();
1697   $parts = explode('-', $command);
1698   $command = implode(".", array_reverse($parts));
1699
1700   $commandfiles = drush_commandfile_list();
1701   $options = array();
1702   foreach ($commandfiles as $commandfile => $file) {
1703     $filename = sprintf("%s/%s.inc", dirname($file), $command);
1704     if (file_exists($filename)) {
1705       $include_files[$filename] = $commandfile;
1706     }
1707   }
1708   return $include_files;
1709 }
1710
1711 /**
1712  * Conditionally include default options based on the command used.
1713  */
1714 function drush_command_default_options($command = NULL) {
1715   $command_default_options = drush_get_context('command-specific');
1716   drush_command_set_command_specific($command_default_options, $command);
1717 }
1718
1719 function drush_sitealias_command_default_options($site_record, $prefix, $command = NULL) {
1720   if (isset($site_record) && array_key_exists($prefix . 'command-specific', $site_record)) {
1721     drush_command_set_command_specific($site_record[$prefix . 'command-specific'], $command);
1722   }
1723   return FALSE;
1724 }
1725
1726 function drush_command_set_command_specific_options($prefix, $command = NULL) {
1727   $command_default_options = drush_get_option($prefix . 'command-specific', array());
1728   drush_command_set_command_specific($command_default_options, $command);
1729 }
1730
1731 function drush_command_set_command_specific($command_default_options, $command = NULL) {
1732   if (!$command) {
1733     $command = drush_get_command();
1734   }
1735   if ($command) {
1736     // Look for command-specific options for this command
1737     // keyed both on the command's primary name, and on each
1738     // of its aliases.
1739     $options_were_set = _drush_command_set_default_options($command_default_options, $command['command']);
1740     if (isset($command['aliases']) && count($command['aliases'])) {
1741       foreach ($command['aliases'] as $alias) {
1742         $options_were_set += _drush_command_set_default_options($command_default_options, $alias);
1743       }
1744     }
1745     // If we set or cleared any options, go back and re-bootstrap any global
1746     // options such as -y and -v.
1747     if (!empty($options_were_set)) {
1748       _drush_preflight_global_options();
1749     }
1750     // If the command uses strict option handling, back out any global
1751     // options that were set.
1752     if ($command['strict-option-handling']) {
1753       $global_options = drush_get_global_options();
1754       foreach ($options_were_set as $key) {
1755         if (array_key_exists($key, $global_options)) {
1756           if (!array_key_exists('context', $global_options[$key])) {
1757             $strict_options_warning =& drush_get_context('DRUSH_STRICT_OPTIONS_WARNING', array());
1758             if (!array_key_exists($key, $strict_options_warning)) {
1759               drush_log(dt("Global option --!option not supported in command-specific options for command !command due to a limitation in strict option handling.", array('!option' => $key, '!command' => $command['command'])), LogLevel::WARNING);
1760               $strict_options_warning[$key] = TRUE;
1761             }
1762           }
1763           drush_unset_option($key, 'specific');
1764         }
1765       }
1766     }
1767   }
1768 }
1769
1770 function _drush_command_set_default_options($command_default_options, $command) {
1771   $options_were_set = array();
1772   if (array_key_exists($command, $command_default_options)) {
1773     foreach ($command_default_options[$command] as $key => $value) {
1774       // We set command-specific options in their own context
1775       // that is higher precedence than the various config file
1776       // context, but lower than command-line options.
1777       if (!drush_get_option('no-' . $key, FALSE)) {
1778         drush_set_option($key, $value, 'specific');
1779         $options_were_set[] = $key;
1780       }
1781     }
1782   }
1783   return $options_were_set;
1784 }
1785
1786 /**
1787  * Return all of the command-specific options defined in the given
1788  * options set for the specified command name.  Note that it is valid
1789  * to use the command name alias rather than the primary command name,
1790  * both in the parameter to this function, and in the options set.
1791  */
1792 function drush_command_get_command_specific_options($options, $command_name, $prefix = '') {
1793   $result = array();
1794   $command_name = drush_command_normalize_name($command_name);
1795   if (isset($options[$prefix . 'command-specific'])) {
1796     foreach ($options[$prefix . 'command-specific'] as $options_for_command => $values) {
1797       if ($command_name == drush_command_normalize_name($options_for_command)) {
1798         $result = array_merge($result, $values);
1799       }
1800     }
1801   }
1802   return $result;
1803 }
1804
1805 /**
1806  * Return the original cli args and options, exactly as they
1807  * appeared on the command line, and in the same order.
1808  * Any command-specific options that were set will also
1809  * appear in this list, appended at the very end.
1810  *
1811  * The args and options returned are raw, and must be
1812  * escaped as necessary before use.
1813  */
1814 function drush_get_original_cli_args_and_options($command = NULL) {
1815   $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
1816   $command_specific_options = drush_get_context('specific');
1817   if ($command == NULL) {
1818     $command = drush_get_command();
1819   }
1820   $command_options = ($command == NULL) ? array() : _drush_get_command_options($command);
1821   foreach ($command_specific_options as $key => $value) {
1822     if (!array_key_exists($key, $command_options)) {
1823       if (($value === TRUE) || (!isset($value))) {
1824         $args[] = "--$key";
1825       }
1826       else {
1827         $args[] = "--$key=$value";
1828       }
1829     }
1830   }
1831   return $args;
1832 }
1833
1834 /**
1835  * Determine whether a command file implements a hook.
1836  *
1837  * @param $module
1838  *   The name of the module (without the .module extension).
1839  * @param $hook
1840  *   The name of the hook (e.g. "help" or "menu").
1841  * @return
1842  *   TRUE if the the hook is implemented.
1843  */
1844 function drush_command_hook($commandfile, $hook) {
1845   return function_exists($commandfile . '_' . $hook);
1846 }
1847
1848 /**
1849  * Check that a command is valid for the current bootstrap phase.
1850  *
1851  * @param $command
1852  *   Command to check. Any errors will be added to the 'bootstrap_errors' element.
1853  *
1854  * @return
1855  *   TRUE if command is valid.
1856  */
1857 function drush_enforce_requirement_bootstrap_phase(&$command) {
1858   $valid = array();
1859   $current_phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
1860   if ($command['bootstrap'] <= $current_phase) {
1861     return TRUE;
1862   }
1863   // TODO: provide description text for each bootstrap level so we can give
1864   // the user something more helpful and specific here.
1865   $command['bootstrap_errors']['DRUSH_COMMAND_INSUFFICIENT_BOOTSTRAP'] = dt('Command !command needs a higher bootstrap level to run - you will need to invoke drush from a more functional Drupal environment to run this command.', array('!command' => $command['command']));
1866 }
1867
1868 /**
1869  * Check that a command has its declared drush dependencies available or have no
1870  * dependencies. Drush dependencies are helpful when a command is invoking
1871  * another command, or implementing its API.
1872  *
1873  * @param $command
1874  *   Command to check. Any errors  will be added to the 'bootstrap_errors' element.
1875  * @return
1876  *   TRUE if dependencies are met.
1877  */
1878 function drush_enforce_requirement_drush_dependencies(&$command) {
1879   // If there are no drush dependencies, then do nothing.
1880   if (!empty($command['drush dependencies'])) {
1881     $commandfiles = drush_commandfile_list();
1882     foreach ($command['drush dependencies'] as $dependency) {
1883       if (!isset($commandfiles[$dependency])) {
1884         $dt_args = array(
1885           '!command' => $command['command'],
1886           '!dependency' => "$dependency.drush.inc",
1887         );
1888         $command['bootstrap_errors']['DRUSH_COMMANDFILE_DEPENDENCY_ERROR'] = dt('Command !command needs the following drush command file to run: !dependency.', $dt_args);
1889         return FALSE;
1890       }
1891     }
1892   }
1893   return TRUE;
1894 }
1895
1896 /**
1897  * Check that a command is valid for the current major version of core. Handles
1898  * explicit version numbers and 'plus' numbers like 7+ (compatible with 7,8 ...).
1899  *
1900  * @param $command
1901  *   Command to check. Any errors  will be added to the 'bootstrap_errors' element.
1902  *
1903  * @return
1904  *   TRUE if command is valid.
1905  */
1906 function drush_enforce_requirement_core(&$command) {
1907   $major = drush_drupal_major_version();
1908   if (!$core = $command['core']) {
1909     return TRUE;
1910   }
1911   foreach ($core as $compat) {
1912     if ($compat == $major) {
1913       return TRUE;
1914     }
1915     elseif (substr($compat, -1) == '+' && $major >= substr($compat, 0, strlen($compat)-1)) {
1916       return TRUE;
1917     }
1918   }
1919   $versions = array_pop($core);
1920   if (!empty($core)) {
1921     $versions = implode(', ', $core) . dt(' or ') . $versions;
1922   }
1923   $command['bootstrap_errors']['DRUSH_COMMAND_CORE_VERSION_ERROR'] = dt('Command !command requires Drupal core version !versions to run.', array('!command' => $command['command'], '!versions' => $versions));
1924 }
1925
1926 /**
1927  * Check if a shell alias exists for current request. If so, re-route to
1928  * core-execute and pass alias value along with rest of CLI arguments.
1929  */
1930 function drush_shell_alias_replace($target_site_alias) {
1931   $escape = TRUE;
1932   $args = drush_get_arguments();
1933   $argv = drush_get_context('argv');
1934   $first = current($args);
1935   // @todo drush_get_option is awkward here.
1936   $shell_aliases = drush_get_context('shell-aliases', array());
1937   if (isset($shell_aliases[$first])) {
1938     // Shell alias found for first argument in the request.
1939     $alias_value = $shell_aliases[$first];
1940     if (!is_array($alias_value)) {
1941       // Shell aliases can have embedded variables such as {{@target}} and {{%root}}
1942       // that are replaced with the name of the target site alias, or the value of a
1943       // path alias defined in the target site alias record.  We only support replacements
1944       // when the alias value is a string; if it is already broken out into an array,
1945       // then the values therein are used literally.
1946       $alias_variables = array( '{{@target}}' => '@none' );
1947       if ($target_site_alias) {
1948         $alias_variables = array( '{{@target}}' => $target_site_alias );
1949         $target = drush_sitealias_get_record($target_site_alias);
1950         foreach ($target as $key => $value) {
1951           if (!is_array($value)) {
1952             $alias_variables['{{' . $key . '}}'] = $value;
1953           }
1954         }
1955         if (array_key_exists('path-aliases', $target)) {
1956           foreach ($target['path-aliases'] as $key => $value) {
1957             // n.b. $key will contain something like "%root" or "%files".
1958             $alias_variables['{{' . $key . '}}'] = $value;
1959           }
1960         }
1961       }
1962       $alias_value = str_replace(array_keys($alias_variables), array_values($alias_variables), $alias_value);
1963       // Check for unmatched replacements
1964       $matches = array();
1965       $match_result = preg_match('/{{[%@#]*[a-z0-9.]*}}/', $alias_value, $matches);
1966       if ($match_result) {
1967         $unmatched_replacements = implode(', ', $matches);
1968         $unmatched_replacements = preg_replace('/[{}]/', '', $unmatched_replacements);
1969         return drush_set_error('DRUSH_SHELL_ALIAS_UNMATCHED_REPLACEMENTS', dt('The shell alias @alias-name uses replacements "@unmatched". You must use this command with a site alias (e.g. `drush @myalias @alias-name ...`) that defines all of these variables.', array('@alias-name' => $first, '@unmatched' => $unmatched_replacements)));
1970       }
1971       if (substr($alias_value, 0, 1) == '!') {
1972         $alias_value = ltrim($alias_value, '!');
1973         $alias_value = array('core-execute', $alias_value);
1974         $escape = FALSE;
1975       }
1976       else {
1977         // Respect quoting. See http://stackoverflow.com/questions/2202435/php-ex
1978         $alias_value = str_getcsv($alias_value, ' ');
1979       }
1980     }
1981     drush_log(dt('Shell alias found: !key => !value', array('!key' => $first, '!value' => implode(' ', $alias_value))), LogLevel::DEBUG);
1982     $pos = array_search($first, $argv);
1983     $number = 1;
1984     if ($target_site_alias && ($argv[$pos - 1] == $target_site_alias)) {
1985       --$pos;
1986       ++$number;
1987     }
1988     array_splice($argv, $pos, $number, $alias_value);
1989     if (!$escape) {
1990       drush_set_option('escape', FALSE);
1991     }
1992     drush_set_context('argv', $argv);
1993     drush_parse_args();
1994     _drush_preflight_global_options();
1995   }
1996 }
1997
1998 function commandfiles_cache() {
1999   static $commandfiles_cache = NULL;
2000
2001   if (!isset($commandfiles_cache)) {
2002     $commandfiles_cache = new Drush\Command\Commandfiles();
2003   }
2004   return $commandfiles_cache;
2005 }