source: trunk/www.guidonia.net/wp/wp-includes/Text/Diff/Engine/string.php@ 44

Last change on this file since 44 was 44, checked in by luciano, 15 years ago
File size: 7.7 KB
Line 
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 */
25class 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}
Note: See TracBrowser for help on using the repository browser.