1
Description: Upstream changes introduced in version 4.5-1
2
This patch has been created by dpkg-source during the package build.
3
Here's the last changelog entry, hopefully it gives details on why
4
those changes were made:
6
drush (4.5-1) unstable; urgency=low
8
* new upstream release: http://drupal.org/node/1247086
9
* fix watch file to follow new naming convention
10
* fix a bunch of lintian tips
11
* switch to source format 3.0 (quilt)
13
The person named in the Author field signed this changelog entry.
14
Author: Antoine Beaupré <anarcat@koumbit.org>
17
The information above should follow the Patch Tagging Guidelines, please
18
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
19
are templates for supplementary fields that you might want to add:
21
Origin: <vendor|upstream|other>, <url of original patch>
22
Bug: <url in upstream bugtracker>
23
Bug-Debian: http://bugs.debian.org/<bugnumber>
24
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
25
Forwarded: <no|not-needed|url proving that it has been forwarded>
26
Reviewed-By: <name and email of someone who approved the patch>
27
Last-Update: <YYYY-MM-DD>
30
+++ drush-4.5/includes/table.inc
34
+ * Utility for printing tables from commandline scripts.
36
+ * PHP versions 4 and 5
38
+ * All rights reserved.
40
+ * Redistribution and use in source and binary forms, with or without
41
+ * modification, are permitted provided that the following conditions are met:
43
+ * o Redistributions of source code must retain the above copyright notice,
44
+ * this list of conditions and the following disclaimer.
45
+ * o Redistributions in binary form must reproduce the above copyright notice,
46
+ * this list of conditions and the following disclaimer in the documentation
47
+ * and/or other materials provided with the distribution.
48
+ * o The names of the authors may not be used to endorse or promote products
49
+ * derived from this software without specific prior written permission.
51
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
52
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
55
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61
+ * POSSIBILITY OF SUCH DAMAGE.
64
+ * @package Console_Table
65
+ * @author Richard Heyes <richard@phpguru.org>
66
+ * @author Jan Schneider <jan@horde.org>
67
+ * @copyright 2002-2005 Richard Heyes
68
+ * @copyright 2006-2008 Jan Schneider
69
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
70
+ * @version CVS: $Id$
71
+ * @link http://pear.php.net/package/Console_Table
74
+define('CONSOLE_TABLE_HORIZONTAL_RULE', 1);
75
+define('CONSOLE_TABLE_ALIGN_LEFT', -1);
76
+define('CONSOLE_TABLE_ALIGN_CENTER', 0);
77
+define('CONSOLE_TABLE_ALIGN_RIGHT', 1);
78
+define('CONSOLE_TABLE_BORDER_ASCII', -1);
84
+ * @package Console_Table
85
+ * @author Jan Schneider <jan@horde.org>
86
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
87
+ * @link http://pear.php.net/package/Console_Table
92
+ * The table headers.
96
+ var $_headers = array();
99
+ * The data of the table.
103
+ var $_data = array();
106
+ * The maximum number of columns in a row.
110
+ var $_max_cols = 0;
113
+ * The maximum number of rows in the table.
117
+ var $_max_rows = 0;
120
+ * Lengths of the columns, calculated when rows are added to the table.
124
+ var $_cell_lengths = array();
127
+ * Heights of the rows.
131
+ var $_row_heights = array();
134
+ * How many spaces to use to pad the table.
145
+ var $_filters = array();
148
+ * Columns to calculate totals for.
152
+ var $_calculateTotals;
155
+ * Alignment of the columns.
159
+ var $_col_align = array();
162
+ * Default alignment of columns.
166
+ var $_defaultAlign;
169
+ * Character set of the data.
173
+ var $_charset = 'utf-8';
176
+ * Border character.
180
+ var $_border = CONSOLE_TABLE_BORDER_ASCII;
183
+ * Whether the data has ANSI colors.
187
+ var $_ansiColor = false;
192
+ * @param integer $align Default alignment. One of
193
+ * CONSOLE_TABLE_ALIGN_LEFT,
194
+ * CONSOLE_TABLE_ALIGN_CENTER or
195
+ * CONSOLE_TABLE_ALIGN_RIGHT.
196
+ * @param string $border The character used for table borders or
197
+ * CONSOLE_TABLE_BORDER_ASCII.
198
+ * @param integer $padding How many spaces to use to pad the table.
199
+ * @param string $charset A charset supported by the mbstring PHP
201
+ * @param boolean $color Whether the data contains ansi color codes.
203
+ function Console_Table($align = CONSOLE_TABLE_ALIGN_LEFT,
204
+ $border = CONSOLE_TABLE_BORDER_ASCII, $padding = 1,
205
+ $charset = null, $color = false)
207
+ $this->_defaultAlign = $align;
208
+ $this->_border = $border;
209
+ $this->_padding = $padding;
210
+ $this->_ansiColor = $color;
211
+ if (!empty($charset)) {
212
+ $this->setCharset($charset);
217
+ * Converts an array to a table.
219
+ * @param array $headers Headers for the table.
220
+ * @param array $data A two dimensional array with the table
222
+ * @param boolean $returnObject Whether to return the Console_Table object
223
+ * instead of the rendered table.
227
+ * @return Console_Table|string A Console_Table object or the generated
230
+ function fromArray($headers, $data, $returnObject = false)
232
+ if (!is_array($headers) || !is_array($data)) {
236
+ $table = new Console_Table();
237
+ $table->setHeaders($headers);
239
+ foreach ($data as $row) {
240
+ $table->addRow($row);
243
+ return $returnObject ? $table : $table->getTable();
247
+ * Adds a filter to a column.
249
+ * Filters are standard PHP callbacks which are run on the data before
250
+ * table generation is performed. Filters are applied in the order they
251
+ * are added. The callback function must accept a single argument, which
252
+ * is a single table cell.
254
+ * @param integer $col Column to apply filter to.
255
+ * @param mixed &$callback PHP callback to apply.
259
+ function addFilter($col, &$callback)
261
+ $this->_filters[] = array($col, &$callback);
265
+ * Sets the charset of the provided table data.
267
+ * @param string $charset A charset supported by the mbstring PHP
272
+ function setCharset($charset)
274
+ $locale = setlocale(LC_CTYPE, 0);
275
+ setlocale(LC_CTYPE, 'en_US');
276
+ $this->_charset = strtolower($charset);
277
+ setlocale(LC_CTYPE, $locale);
281
+ * Sets the alignment for the columns.
283
+ * @param integer $col_id The column number.
284
+ * @param integer $align Alignment to set for this column. One of
285
+ * CONSOLE_TABLE_ALIGN_LEFT
286
+ * CONSOLE_TABLE_ALIGN_CENTER
287
+ * CONSOLE_TABLE_ALIGN_RIGHT.
291
+ function setAlign($col_id, $align = CONSOLE_TABLE_ALIGN_LEFT)
294
+ case CONSOLE_TABLE_ALIGN_CENTER:
295
+ $pad = STR_PAD_BOTH;
297
+ case CONSOLE_TABLE_ALIGN_RIGHT:
298
+ $pad = STR_PAD_LEFT;
301
+ $pad = STR_PAD_RIGHT;
304
+ $this->_col_align[$col_id] = $pad;
308
+ * Specifies which columns are to have totals calculated for them and
309
+ * added as a new row at the bottom.
311
+ * @param array $cols Array of column numbers (starting with 0).
315
+ function calculateTotalsFor($cols)
317
+ $this->_calculateTotals = $cols;
321
+ * Sets the headers for the columns.
323
+ * @param array $headers The column headers.
327
+ function setHeaders($headers)
329
+ $this->_headers = array(array_values($headers));
330
+ $this->_updateRowsCols($headers);
334
+ * Adds a row to the table.
336
+ * @param array $row The row data to add.
337
+ * @param boolean $append Whether to append or prepend the row.
341
+ function addRow($row, $append = true)
344
+ $this->_data[] = array_values($row);
346
+ array_unshift($this->_data, array_values($row));
349
+ $this->_updateRowsCols($row);
353
+ * Inserts a row after a given row number in the table.
355
+ * If $row_id is not given it will prepend the row.
357
+ * @param array $row The data to insert.
358
+ * @param integer $row_id Row number to insert before.
362
+ function insertRow($row, $row_id = 0)
364
+ array_splice($this->_data, $row_id, 0, array($row));
366
+ $this->_updateRowsCols($row);
370
+ * Adds a column to the table.
372
+ * @param array $col_data The data of the column.
373
+ * @param integer $col_id The column index to populate.
374
+ * @param integer $row_id If starting row is not zero, specify it here.
378
+ function addCol($col_data, $col_id = 0, $row_id = 0)
380
+ foreach ($col_data as $col_cell) {
381
+ $this->_data[$row_id++][$col_id] = $col_cell;
384
+ $this->_updateRowsCols();
385
+ $this->_max_cols = max($this->_max_cols, $col_id + 1);
389
+ * Adds data to the table.
391
+ * @param array $data A two dimensional array with the table data.
392
+ * @param integer $col_id Starting column number.
393
+ * @param integer $row_id Starting row number.
397
+ function addData($data, $col_id = 0, $row_id = 0)
399
+ foreach ($data as $row) {
400
+ if ($row === CONSOLE_TABLE_HORIZONTAL_RULE) {
401
+ $this->_data[$row_id] = CONSOLE_TABLE_HORIZONTAL_RULE;
405
+ $starting_col = $col_id;
406
+ foreach ($row as $cell) {
407
+ $this->_data[$row_id][$starting_col++] = $cell;
409
+ $this->_updateRowsCols();
410
+ $this->_max_cols = max($this->_max_cols, $starting_col);
416
+ * Adds a horizontal seperator to the table.
420
+ function addSeparator()
422
+ $this->_data[] = CONSOLE_TABLE_HORIZONTAL_RULE;
426
+ * Returns the generated table.
428
+ * @return string The generated table.
430
+ function getTable()
432
+ $this->_applyFilters();
433
+ $this->_calculateTotals();
434
+ $this->_validateTable();
436
+ return $this->_buildTable();
440
+ * Calculates totals for columns.
444
+ function _calculateTotals()
446
+ if (empty($this->_calculateTotals)) {
450
+ $this->addSeparator();
453
+ foreach ($this->_data as $row) {
454
+ if (is_array($row)) {
455
+ foreach ($this->_calculateTotals as $columnID) {
456
+ $totals[$columnID] += $row[$columnID];
461
+ $this->_data[] = $totals;
462
+ $this->_updateRowsCols();
466
+ * Applies any column filters to the data.
470
+ function _applyFilters()
472
+ if (empty($this->_filters)) {
476
+ foreach ($this->_filters as $filter) {
477
+ $column = $filter[0];
478
+ $callback = $filter[1];
480
+ foreach ($this->_data as $row_id => $row_data) {
481
+ if ($row_data !== CONSOLE_TABLE_HORIZONTAL_RULE) {
482
+ $this->_data[$row_id][$column] =
483
+ call_user_func($callback, $row_data[$column]);
490
+ * Ensures that column and row counts are correct.
494
+ function _validateTable()
496
+ if (!empty($this->_headers)) {
497
+ $this->_calculateRowHeight(-1, $this->_headers[0]);
500
+ for ($i = 0; $i < $this->_max_rows; $i++) {
501
+ for ($j = 0; $j < $this->_max_cols; $j++) {
502
+ if (!isset($this->_data[$i][$j]) &&
503
+ (!isset($this->_data[$i]) ||
504
+ $this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE)) {
505
+ $this->_data[$i][$j] = '';
509
+ $this->_calculateRowHeight($i, $this->_data[$i]);
511
+ if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE) {
512
+ ksort($this->_data[$i]);
517
+ $this->_splitMultilineRows();
519
+ // Update cell lengths.
520
+ for ($i = 0; $i < count($this->_headers); $i++) {
521
+ $this->_calculateCellLengths($this->_headers[$i]);
523
+ for ($i = 0; $i < $this->_max_rows; $i++) {
524
+ $this->_calculateCellLengths($this->_data[$i]);
527
+ ksort($this->_data);
531
+ * Splits multiline rows into many smaller one-line rows.
535
+ function _splitMultilineRows()
537
+ ksort($this->_data);
538
+ $sections = array(&$this->_headers, &$this->_data);
539
+ $max_rows = array(count($this->_headers), $this->_max_rows);
540
+ $row_height_offset = array(-1, 0);
542
+ for ($s = 0; $s <= 1; $s++) {
544
+ $new_data = $sections[$s];
546
+ for ($i = 0; $i < $max_rows[$s]; $i++) {
547
+ // Process only rows that have many lines.
548
+ $height = $this->_row_heights[$i + $row_height_offset[$s]];
550
+ // Split column data into one-liners.
552
+ for ($j = 0; $j < $this->_max_cols; $j++) {
553
+ $split[$j] = preg_split('/\r?\n|\r/',
554
+ $sections[$s][$i][$j]);
557
+ $new_rows = array();
558
+ // Construct new 'virtual' rows - insert empty strings for
559
+ // columns that have less lines that the highest one.
560
+ for ($i2 = 0; $i2 < $height; $i2++) {
561
+ for ($j = 0; $j < $this->_max_cols; $j++) {
562
+ $new_rows[$i2][$j] = !isset($split[$j][$i2])
568
+ // Replace current row with smaller rows. $inserted is
569
+ // used to take account of bigger array because of already
571
+ array_splice($new_data, $i + $inserted, 1, $new_rows);
572
+ $inserted += count($new_rows) - 1;
576
+ // Has the data been modified?
577
+ if ($inserted > 0) {
578
+ $sections[$s] = $new_data;
579
+ $this->_updateRowsCols();
585
+ * Builds the table.
587
+ * @return string The generated table string.
589
+ function _buildTable()
591
+ if (!count($this->_data)) {
595
+ $rule = $this->_border == CONSOLE_TABLE_BORDER_ASCII
598
+ $separator = $this->_getSeparator();
601
+ for ($i = 0; $i < count($this->_data); $i++) {
602
+ for ($j = 0; $j < count($this->_data[$i]); $j++) {
603
+ if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE &&
604
+ $this->_strlen($this->_data[$i][$j]) <
605
+ $this->_cell_lengths[$j]) {
606
+ $this->_data[$i][$j] = $this->_strpad($this->_data[$i][$j],
607
+ $this->_cell_lengths[$j],
609
+ $this->_col_align[$j]);
613
+ if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE) {
614
+ $row_begin = $rule . str_repeat(' ', $this->_padding);
615
+ $row_end = str_repeat(' ', $this->_padding) . $rule;
616
+ $implode_char = str_repeat(' ', $this->_padding) . $rule
617
+ . str_repeat(' ', $this->_padding);
618
+ $return[] = $row_begin
619
+ . implode($implode_char, $this->_data[$i]) . $row_end;
620
+ } elseif (!empty($separator)) {
621
+ $return[] = $separator;
626
+ $return = implode("\r\n", $return);
627
+ if (!empty($separator)) {
628
+ $return = $separator . "\r\n" . $return . "\r\n" . $separator;
632
+ if (!empty($this->_headers)) {
633
+ $return = $this->_getHeaderLine() . "\r\n" . $return;
640
+ * Creates a horizontal separator for header separation and table
643
+ * @return string The horizontal separator.
645
+ function _getSeparator()
647
+ if (!$this->_border) {
651
+ if ($this->_border == CONSOLE_TABLE_BORDER_ASCII) {
655
+ $rule = $sect = $this->_border;
659
+ foreach ($this->_cell_lengths as $cl) {
660
+ $return[] = str_repeat($rule, $cl);
663
+ $row_begin = $sect . str_repeat($rule, $this->_padding);
664
+ $row_end = str_repeat($rule, $this->_padding) . $sect;
665
+ $implode_char = str_repeat($rule, $this->_padding) . $sect
666
+ . str_repeat($rule, $this->_padding);
668
+ return $row_begin . implode($implode_char, $return) . $row_end;
672
+ * Returns the header line for the table.
674
+ * @return string The header line of the table.
676
+ function _getHeaderLine()
678
+ // Make sure column count is correct
679
+ for ($j = 0; $j < count($this->_headers); $j++) {
680
+ for ($i = 0; $i < $this->_max_cols; $i++) {
681
+ if (!isset($this->_headers[$j][$i])) {
682
+ $this->_headers[$j][$i] = '';
687
+ for ($j = 0; $j < count($this->_headers); $j++) {
688
+ for ($i = 0; $i < count($this->_headers[$j]); $i++) {
689
+ if ($this->_strlen($this->_headers[$j][$i]) <
690
+ $this->_cell_lengths[$i]) {
691
+ $this->_headers[$j][$i] =
692
+ $this->_strpad($this->_headers[$j][$i],
693
+ $this->_cell_lengths[$i],
695
+ $this->_col_align[$i]);
700
+ $rule = $this->_border == CONSOLE_TABLE_BORDER_ASCII
703
+ $row_begin = $rule . str_repeat(' ', $this->_padding);
704
+ $row_end = str_repeat(' ', $this->_padding) . $rule;
705
+ $implode_char = str_repeat(' ', $this->_padding) . $rule
706
+ . str_repeat(' ', $this->_padding);
708
+ $separator = $this->_getSeparator();
709
+ if (!empty($separator)) {
710
+ $return[] = $separator;
712
+ for ($j = 0; $j < count($this->_headers); $j++) {
713
+ $return[] = $row_begin
714
+ . implode($implode_char, $this->_headers[$j]) . $row_end;
717
+ return implode("\r\n", $return);
721
+ * Updates values for maximum columns and rows.
723
+ * @param array $rowdata Data array of a single row.
727
+ function _updateRowsCols($rowdata = null)
729
+ // Update maximum columns.
730
+ $this->_max_cols = max($this->_max_cols, count($rowdata));
732
+ // Update maximum rows.
733
+ ksort($this->_data);
734
+ $keys = array_keys($this->_data);
735
+ $this->_max_rows = end($keys) + 1;
737
+ switch ($this->_defaultAlign) {
738
+ case CONSOLE_TABLE_ALIGN_CENTER:
739
+ $pad = STR_PAD_BOTH;
741
+ case CONSOLE_TABLE_ALIGN_RIGHT:
742
+ $pad = STR_PAD_LEFT;
745
+ $pad = STR_PAD_RIGHT;
749
+ // Set default column alignments
750
+ for ($i = count($this->_col_align); $i < $this->_max_cols; $i++) {
751
+ $this->_col_align[$i] = $pad;
756
+ * Calculates the maximum length for each column of a row.
758
+ * @param array $row The row data.
762
+ function _calculateCellLengths($row)
764
+ for ($i = 0; $i < count($row); $i++) {
765
+ if (!isset($this->_cell_lengths[$i])) {
766
+ $this->_cell_lengths[$i] = 0;
768
+ $this->_cell_lengths[$i] = max($this->_cell_lengths[$i],
769
+ $this->_strlen($row[$i]));
774
+ * Calculates the maximum height for all columns of a row.
776
+ * @param integer $row_number The row number.
777
+ * @param array $row The row data.
781
+ function _calculateRowHeight($row_number, $row)
783
+ if (!isset($this->_row_heights[$row_number])) {
784
+ $this->_row_heights[$row_number] = 1;
787
+ // Do not process horizontal rule rows.
788
+ if ($row === CONSOLE_TABLE_HORIZONTAL_RULE) {
792
+ for ($i = 0, $c = count($row); $i < $c; ++$i) {
793
+ $lines = preg_split('/\r?\n|\r/', $row[$i]);
794
+ $this->_row_heights[$row_number] = max($this->_row_heights[$row_number],
800
+ * Returns the character length of a string.
802
+ * @param string $str A multibyte or singlebyte string.
804
+ * @return integer The string length.
806
+ function _strlen($str)
808
+ static $mbstring, $utf8;
810
+ // Strip ANSI color codes if requested.
811
+ if ($this->_ansiColor) {
812
+ include_once 'Console/Color.php';
813
+ $str = Console_Color::strip($str);
816
+ // Cache expensive function_exists() calls.
817
+ if (!isset($mbstring)) {
818
+ $mbstring = function_exists('mb_strlen');
820
+ if (!isset($utf8)) {
821
+ $utf8 = function_exists('utf8_decode');
825
+ ($this->_charset == strtolower('utf-8') ||
826
+ $this->_charset == strtolower('utf8'))) {
827
+ return strlen(utf8_decode($str));
830
+ return mb_strlen($str, $this->_charset);
833
+ return strlen($str);
837
+ * Returns part of a string.
839
+ * @param string $string The string to be converted.
840
+ * @param integer $start The part's start position, zero based.
841
+ * @param integer $length The part's length.
843
+ * @return string The string's part.
845
+ function _substr($string, $start, $length = null)
849
+ // Cache expensive function_exists() calls.
850
+ if (!isset($mbstring)) {
851
+ $mbstring = function_exists('mb_substr');
854
+ if (is_null($length)) {
855
+ $length = $this->_strlen($string);
858
+ $ret = @mb_substr($string, $start, $length, $this->_charset);
859
+ if (!empty($ret)) {
863
+ return substr($string, $start, $length);
867
+ * Returns a string padded to a certain length with another string.
869
+ * This method behaves exactly like str_pad but is multibyte safe.
871
+ * @param string $input The string to be padded.
872
+ * @param integer $length The length of the resulting string.
873
+ * @param string $pad The string to pad the input string with. Must
874
+ * be in the same charset like the input string.
875
+ * @param const $type The padding type. One of STR_PAD_LEFT,
876
+ * STR_PAD_RIGHT, or STR_PAD_BOTH.
878
+ * @return string The padded string.
880
+ function _strpad($input, $length, $pad = ' ', $type = STR_PAD_RIGHT)
882
+ $mb_length = $this->_strlen($input);
883
+ $sb_length = strlen($input);
884
+ $pad_length = $this->_strlen($pad);
886
+ /* Return if we already have the length. */
887
+ if ($mb_length >= $length) {
891
+ /* Shortcut for single byte strings. */
892
+ if ($mb_length == $sb_length && $pad_length == strlen($pad)) {
893
+ return str_pad($input, $length, $pad, $type);
898
+ $left = $length - $mb_length;
899
+ $output = $this->_substr(str_repeat($pad, ceil($left / $pad_length)),
900
+ 0, $left, $this->_charset) . $input;
903
+ $left = floor(($length - $mb_length) / 2);
904
+ $right = ceil(($length - $mb_length) / 2);
905
+ $output = $this->_substr(str_repeat($pad, ceil($left / $pad_length)),
906
+ 0, $left, $this->_charset) .
908
+ $this->_substr(str_repeat($pad, ceil($right / $pad_length)),
909
+ 0, $right, $this->_charset);
911
+ case STR_PAD_RIGHT:
912
+ $right = $length - $mb_length;
914
+ $this->_substr(str_repeat($pad, ceil($right / $pad_length)),
915
+ 0, $right, $this->_charset);
924
+++ drush-4.5/docs/index.html
926
+<h1>Drush internals documentation</h1>
929
+<li><a href="bootstrap.html">The Drush Bootstarp Process</a></li>
930
+<li><a href="commands.html">Creating Custom Drush Commands</a></li>
931
+<li><a href="context.html">Drush Contexts</a></li>
932
+<li><a href="shellscripts.html">The Drush Shell Scripts</a></li>
933
+<li><a href="upgrade.html">Using Drush to Upgrade Drupal 6.x to Drupal 7.x</a></li>