]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/Text/Diff/Engine/shell.php
Wordpress 3.0
[autoinstalls/wordpress.git] / wp-includes / Text / Diff / Engine / shell.php
1 <?php
2 /**
3  * Class used internally by Diff to actually compute the diffs.
4  *
5  * This class uses the Unix `diff` program via shell_exec to compute the
6  * differences between the two input arrays.
7  *
8  * Copyright 2007-2010 The Horde Project (http://www.horde.org/)
9  *
10  * See the enclosed file COPYING for license information (LGPL). If you did
11  * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
12  *
13  * @author  Milian Wolff <mail@milianw.de>
14  * @package Text_Diff
15  * @since   0.3.0
16  */
17 class Text_Diff_Engine_shell {
18
19     /**
20      * Path to the diff executable
21      *
22      * @var string
23      */
24     var $_diffCommand = 'diff';
25
26     /**
27      * Returns the array of differences.
28      *
29      * @param array $from_lines lines of text from old file
30      * @param array $to_lines   lines of text from new file
31      *
32      * @return array all changes made (array with Text_Diff_Op_* objects)
33      */
34     function diff($from_lines, $to_lines)
35     {
36         array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
37         array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
38
39         $temp_dir = Text_Diff::_getTempDir();
40
41         // Execute gnu diff or similar to get a standard diff file.
42         $from_file = tempnam($temp_dir, 'Text_Diff');
43         $to_file = tempnam($temp_dir, 'Text_Diff');
44         $fp = fopen($from_file, 'w');
45         fwrite($fp, implode("\n", $from_lines));
46         fclose($fp);
47         $fp = fopen($to_file, 'w');
48         fwrite($fp, implode("\n", $to_lines));
49         fclose($fp);
50         $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
51         unlink($from_file);
52         unlink($to_file);
53
54         if (is_null($diff)) {
55             // No changes were made
56             return array(new Text_Diff_Op_copy($from_lines));
57         }
58
59         $from_line_no = 1;
60         $to_line_no = 1;
61         $edits = array();
62
63         // Get changed lines by parsing something like:
64         // 0a1,2
65         // 1,2c4,6
66         // 1,5d6
67         preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
68             $matches, PREG_SET_ORDER);
69
70         foreach ($matches as $match) {
71             if (!isset($match[5])) {
72                 // This paren is not set every time (see regex).
73                 $match[5] = false;
74             }
75
76             if ($match[3] == 'a') {
77                 $from_line_no--;
78             }
79
80             if ($match[3] == 'd') {
81                 $to_line_no--;
82             }
83
84             if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
85                 // copied lines
86                 assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
87                 array_push($edits,
88                     new Text_Diff_Op_copy(
89                         $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
90                         $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
91             }
92
93             switch ($match[3]) {
94             case 'd':
95                 // deleted lines
96                 array_push($edits,
97                     new Text_Diff_Op_delete(
98                         $this->_getLines($from_lines, $from_line_no, $match[2])));
99                 $to_line_no++;
100                 break;
101
102             case 'c':
103                 // changed lines
104                 array_push($edits,
105                     new Text_Diff_Op_change(
106                         $this->_getLines($from_lines, $from_line_no, $match[2]),
107                         $this->_getLines($to_lines, $to_line_no, $match[5])));
108                 break;
109
110             case 'a':
111                 // added lines
112                 array_push($edits,
113                     new Text_Diff_Op_add(
114                         $this->_getLines($to_lines, $to_line_no, $match[5])));
115                 $from_line_no++;
116                 break;
117             }
118         }
119
120         if (!empty($from_lines)) {
121             // Some lines might still be pending. Add them as copied
122             array_push($edits,
123                 new Text_Diff_Op_copy(
124                     $this->_getLines($from_lines, $from_line_no,
125                                      $from_line_no + count($from_lines) - 1),
126                     $this->_getLines($to_lines, $to_line_no,
127                                      $to_line_no + count($to_lines) - 1)));
128         }
129
130         return $edits;
131     }
132
133     /**
134      * Get lines from either the old or new text
135      *
136      * @access private
137      *
138      * @param array &$text_lines Either $from_lines or $to_lines
139      * @param int   &$line_no    Current line number
140      * @param int   $end         Optional end line, when we want to chop more
141      *                           than one line.
142      *
143      * @return array The chopped lines
144      */
145     function _getLines(&$text_lines, &$line_no, $end = false)
146     {
147         if (!empty($end)) {
148             $lines = array();
149             // We can shift even more
150             while ($line_no <= $end) {
151                 array_push($lines, array_shift($text_lines));
152                 $line_no++;
153             }
154         } else {
155             $lines = array(array_shift($text_lines));
156             $line_no++;
157         }
158
159         return $lines;
160     }
161
162 }