]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/Text/Diff/Engine/string.php
Wordpress 2.6.2-scripts
[autoinstalls/wordpress.git] / wp-includes / Text / Diff / Engine / string.php
1 <?php
2 /**
3  * Parses unified or context diffs output from eg. the diff utility.
4  *
5  * Example:
6  * <code>
7  * $patch = file_get_contents('example.patch');
8  * $diff = new Text_Diff('string', array($patch));
9  * $renderer = new Text_Diff_Renderer_inline();
10  * echo $renderer->render($diff);
11  * </code>
12  *
13  * $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.7 2008/01/04 10:07:50 jan Exp $
14  *
15  * Copyright 2005 Örjan Persson <o@42mm.org>
16  * Copyright 2005-2008 The Horde Project (http://www.horde.org/)
17  *
18  * See the enclosed file COPYING for license information (LGPL). If you did
19  * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
20  *
21  * @author  Örjan Persson <o@42mm.org>
22  * @package Text_Diff
23  * @since   0.2.0
24  */
25 class Text_Diff_Engine_string {
26
27     /**
28      * Parses a unified or context diff.
29      *
30      * First param contains the whole diff and the second can be used to force
31      * a specific diff type. If the second parameter is 'autodetect', the
32      * diff will be examined to find out which type of diff this is.
33      *
34      * @param string $diff  The diff content.
35      * @param string $mode  The diff mode of the content in $diff. One of
36      *                      'context', 'unified', or 'autodetect'.
37      *
38      * @return array  List of all diff operations.
39      */
40     function diff($diff, $mode = 'autodetect')
41     {
42         if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
43             return PEAR::raiseError('Type of diff is unsupported');
44         }
45
46         if ($mode == 'autodetect') {
47             $context = strpos($diff, '***');
48             $unified = strpos($diff, '---');
49             if ($context === $unified) {
50                 return PEAR::raiseError('Type of diff could not be detected');
51             } elseif ($context === false || $context === false) {
52                 $mode = $context !== false ? 'context' : 'unified';
53             } else {
54                 $mode = $context < $unified ? 'context' : 'unified';
55             }
56         }
57
58         // split by new line and remove the diff header
59         $diff = explode("\n", $diff);
60         array_shift($diff);
61         array_shift($diff);
62
63         if ($mode == 'context') {
64             return $this->parseContextDiff($diff);
65         } else {
66             return $this->parseUnifiedDiff($diff);
67         }
68     }
69
70     /**
71      * Parses an array containing the unified diff.
72      *
73      * @param array $diff  Array of lines.
74      *
75      * @return array  List of all diff operations.
76      */
77     function parseUnifiedDiff($diff)
78     {
79         $edits = array();
80         $end = count($diff) - 1;
81         for ($i = 0; $i < $end;) {
82             $diff1 = array();
83             switch (substr($diff[$i], 0, 1)) {
84             case ' ':
85                 do {
86                     $diff1[] = substr($diff[$i], 1);
87                 } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
88                 $edits[] = &new Text_Diff_Op_copy($diff1);
89                 break;
90
91             case '+':
92                 // get all new lines
93                 do {
94                     $diff1[] = substr($diff[$i], 1);
95                 } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
96                 $edits[] = &new Text_Diff_Op_add($diff1);
97                 break;
98
99             case '-':
100                 // get changed or removed lines
101                 $diff2 = array();
102                 do {
103                     $diff1[] = substr($diff[$i], 1);
104                 } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
105
106                 while ($i < $end && substr($diff[$i], 0, 1) == '+') {
107                     $diff2[] = substr($diff[$i++], 1);
108                 }
109                 if (count($diff2) == 0) {
110                     $edits[] = &new Text_Diff_Op_delete($diff1);
111                 } else {
112                     $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
113                 }
114                 break;
115
116             default:
117                 $i++;
118                 break;
119             }
120         }
121
122         return $edits;
123     }
124
125     /**
126      * Parses an array containing the context diff.
127      *
128      * @param array $diff  Array of lines.
129      *
130      * @return array  List of all diff operations.
131      */
132     function parseContextDiff(&$diff)
133     {
134         $edits = array();
135         $i = $max_i = $j = $max_j = 0;
136         $end = count($diff) - 1;
137         while ($i < $end && $j < $end) {
138             while ($i >= $max_i && $j >= $max_j) {
139                 // Find the boundaries of the diff output of the two files
140                 for ($i = $j;
141                      $i < $end && substr($diff[$i], 0, 3) == '***';
142                      $i++);
143                 for ($max_i = $i;
144                      $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
145                      $max_i++);
146                 for ($j = $max_i;
147                      $j < $end && substr($diff[$j], 0, 3) == '---';
148                      $j++);
149                 for ($max_j = $j;
150                      $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
151                      $max_j++);
152             }
153
154             // find what hasn't been changed
155             $array = array();
156             while ($i < $max_i &&
157                    $j < $max_j &&
158                    strcmp($diff[$i], $diff[$j]) == 0) {
159                 $array[] = substr($diff[$i], 2);
160                 $i++;
161                 $j++;
162             }
163
164             while ($i < $max_i && ($max_j-$j) <= 1) {
165                 if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
166                     break;
167                 }
168                 $array[] = substr($diff[$i++], 2);
169             }
170
171             while ($j < $max_j && ($max_i-$i) <= 1) {
172                 if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
173                     break;
174                 }
175                 $array[] = substr($diff[$j++], 2);
176             }
177             if (count($array) > 0) {
178                 $edits[] = &new Text_Diff_Op_copy($array);
179             }
180
181             if ($i < $max_i) {
182                 $diff1 = array();
183                 switch (substr($diff[$i], 0, 1)) {
184                 case '!':
185                     $diff2 = array();
186                     do {
187                         $diff1[] = substr($diff[$i], 2);
188                         if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
189                             $diff2[] = substr($diff[$j++], 2);
190                         }
191                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
192                     $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
193                     break;
194
195                 case '+':
196                     do {
197                         $diff1[] = substr($diff[$i], 2);
198                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
199                     $edits[] = &new Text_Diff_Op_add($diff1);
200                     break;
201
202                 case '-':
203                     do {
204                         $diff1[] = substr($diff[$i], 2);
205                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
206                     $edits[] = &new Text_Diff_Op_delete($diff1);
207                     break;
208                 }
209             }
210
211             if ($j < $max_j) {
212                 $diff2 = array();
213                 switch (substr($diff[$j], 0, 1)) {
214                 case '+':
215                     do {
216                         $diff2[] = substr($diff[$j++], 2);
217                     } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
218                     $edits[] = &new Text_Diff_Op_add($diff2);
219                     break;
220
221                 case '-':
222                     do {
223                         $diff2[] = substr($diff[$j++], 2);
224                     } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
225                     $edits[] = &new Text_Diff_Op_delete($diff2);
226                     break;
227                 }
228             }
229         }
230
231         return $edits;
232     }
233
234 }