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