Adding language settings to config.php so that people can control the language output...
[viewsvn.git] / includes / class_diff.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # ViewSVN [#]version[#]
5 || # Copyright ©2002-[#]year[#] Blue Static
6 || #
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version [#]gpl[#] of the License.
10 || #
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 || # more details.
15 || #
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
21
22 /**
23 * Diff
24 *
25 * This class parses a diff file from a unified diff file and turns it
26 * into a HTML-prettied output
27 *
28 * @author Blue Static
29 * @copyright Copyright (c)2002 - [#]year[#], Blue Static
30 * @version $Revision$
31 * @package ViewSVN
32 *
33 */
34 class Diff
35 {
36 /**
37 * Raw unified diff text
38 * @var array
39 */
40 private $raw;
41
42 /**
43 * The current index (file)
44 * @var string
45 */
46 private $index;
47
48 /**
49 * The array index of where capturing of diff data starts
50 * @var integer
51 */
52 private $captureStart = 0;
53
54 /**
55 * Current hunk that is being processed
56 * @var integer
57 */
58 private $hunkId = 0;
59
60 /**
61 * The lines on which hunks start
62 * @var array
63 */
64 private $hunkLines = array('old' => array('line' => 0, 'count' => 0), 'new' => array('line' => 0, 'count' => 0));
65
66 /**
67 * The parsed diff output
68 * @var array
69 */
70 private $diff = array();
71
72 // ###################################################################
73 /**
74 * Constructor
75 *
76 * @param string Raw unified diff output
77 */
78 public function __construct($raw)
79 {
80 $this->raw = $raw;
81 }
82
83 // ###################################################################
84 /**
85 * This function initiates the parsing process and returns the output
86 * in hunks, line-by-line
87 *
88 * @return array Array of outputted hunked information
89 */
90 public function fetch()
91 {
92 $this->diff = array();
93
94 for ($i = 0; $i < sizeof($this->raw); $i++)
95 {
96 $line = $this->raw[$i];
97
98 if (preg_match('/^Index: (.*)/', $line, $index))
99 {
100 $this->_processFile(array_slice($this->raw, $this->captureStart, $i - $this->captureStart));
101 $this->captureStart = $i;
102 $this->index = $index[1];
103 }
104 else if (preg_match('/Property changes on: (.*)/', $line, $index))
105 {
106 $this->index = $index[1];
107 $this->_processProperties(array_slice($this->raw, 2));
108 }
109 }
110 $this->_processFile(array_slice($this->raw, $this->captureStart));
111
112 return $this->diff;
113 }
114
115 // ###################################################################
116 /**
117 * Processes a file part of a diff
118 *
119 * @param array An array of lines that make up a file chunk
120 */
121 private function _processFile($lines)
122 {
123 preg_match_all('/[\-\+]{3}\s+(.*)\s+\(revision ([0-9]*)\)/', $lines[2] . "\n" . $lines[3], $revs);
124 $this->diff[$this->index]['revision']['low'] = $revs[2][0];
125 $this->diff[$this->index]['revision']['high'] = $revs[2][1];
126
127 $this->hunkId = 0;
128
129 $captureStart = 4;
130
131 for ($i = 4; $i < sizeof($lines); $i++)
132 {
133 $line = $lines[$i];
134
135 if (preg_match('/^@@ \-([0-9]*),([0-9]*) \+([0-9]*),([0-9]*) @@/', $line, $hunkHead))
136 {
137 $this->hunkLines = array('old' => array('line' => $hunkHead[1], 'count' => $hunkHead[2]), 'new' => array('line' => $hunkHead[3], 'count' => $hunkHead[4]));
138 $this->_processHunk(array_slice($lines, $captureStart, $i - $captureStart));
139 $captureStart = $i;
140 $this->hunkId++;
141 }
142 else if (preg_match('#^__*_$#', $line))
143 {
144 $this->_processHunk(array_slice($lines, $captureStart, $i - $captureStart));
145 $this->_processProperties(array_slice($lines, $i));
146 return;
147 }
148 }
149 $this->_processHunk(array_slice($lines, $captureStart));
150 }
151
152 // ###################################################################
153 /**
154 * This processes an individual hunk of a file
155 *
156 * @param array Array of lines
157 */
158 private function _processHunk($lines)
159 {
160 $delstack = array();
161 for ($i = 1; $i < sizeof($lines); $i++)
162 {
163 $line = $lines[$i];
164
165 if (preg_match('/^([\+\- ])(.*)/', $line, $matches))
166 {
167 $action = $matches[1];
168 $content = $matches[2];
169
170 // leader line
171 if ($action == ' ')
172 {
173 $this->diff[$this->index]['lines'][$this->hunkId][] = array(
174 'line' => $content,
175 'act' => '',
176 'oldlineno' => ++$this->hunkLines['old']['line'],
177 'newlineno' => ++$this->hunkLines['new']['line']
178 );
179
180 $delstack = array();
181 }
182 // addition
183 else if ($action == '+')
184 {
185 // potential line delta
186 if (sizeof($delstack) > 0)
187 {
188 $lastline = array_shift($delstack);
189
190 if ($delta = @$this->_fetchDiffExtent($lastline['line'], $content))
191 {
192 if (strlen($lastline['line']) > ($delta['start'] - $delta['end']))
193 {
194 $end = strlen($lastline['line']) + $delta['end'];
195 $change = '{@-' . '-}' . substr($lastline['line'], $delta['start'], $end - $delta['start']) . '{/@-' . '-}';
196 $this->diff[$this->index]['lines'][$this->hunkId][$lastline['index']]['line'] = substr($lastline['line'], 0, $delta['start']) . $change . substr($lastline['line'], $end);
197 }
198
199 if (strlen($content) > $delta['start'] - $delta['end'])
200 {
201 $end = strlen($content) + $delta['end'];
202 $change = '{@+' . '+}' . substr($content, $delta['start'], $end - $delta['start']) . '{/@+' . '+}';
203 $content = substr($content, 0, $delta['start']) . $change . substr($content, $end);
204 }
205 }
206 }
207
208 $this->diff[$this->index]['lines'][$this->hunkId][] = array(
209 'line' => $content,
210 'act' => '+',
211 'oldlineno' => 0,
212 'newlineno' => ++$this->hunkLines['new']['line']
213 );
214 }
215 // deletion
216 else if ($action == '-')
217 {
218 $this->diff[$this->index]['lines'][$this->hunkId][] = $temp = array(
219 'line' => $content,
220 'act' => '-',
221 'oldlineno' => ++$this->hunkLines['old']['line'],
222 'newlineno' => 0
223 );
224
225 $temp['index'] = sizeof($this->diff[$this->index]['lines'][$this->hunkId]) - 1;
226 array_push($delstack, $temp);
227 }
228 }
229 }
230 }
231
232 // ###################################################################
233 /**
234 * Processes properties
235 *
236 * @param array Lines of properties
237 */
238 private function _processProperties($lines)
239 {
240 $curprop = '';
241 $mode = '';
242 $captureStart = 1;
243 for ($i = 1; $i < sizeof($lines); $i++)
244 {
245 $line = $lines[$i];
246 if (preg_match('#Name: (.*?)$#', $line, $matches))
247 {
248 $curprop = $matches[1];
249 BSRegister::Debug("prop: $curprop");
250 }
251 else
252 {
253 if (preg_match('#^\s+?\+(.*)#', $line, $matches))
254 {
255 $mode = 'add';
256 $this->diff[$this->index]['props'][$curprop]['add'] .= $matches[1];
257 }
258 else if (preg_match('#^\s+?\-(.*)#', $line, $matches))
259 {
260 $mode = 'del';
261 $this->diff[$this->index]['props'][$curprop]['del'] .= $matches[1];
262 }
263 else if (!preg_match('#^\s+[\+\- ](.*)#', $line) AND trim($line) != '')
264 {
265 $this->diff[$this->index]['props'][$curprop][$mode] .= "\n" . $line;
266 }
267 }
268 }
269 }
270
271 // ###################################################################
272 /**
273 * Returns the amount of change that occured between two lines
274 *
275 * @param string Old line
276 * @param string New line
277 *
278 * @return array Difference of positions
279 */
280 private function _fetchDiffExtent($old, $new)
281 {
282 $start = 0;
283 $min = min(strlen($old), strlen($new));
284
285 while ($start < $min AND $old[$start] == $new[$start])
286 {
287 $start++;
288 }
289
290 $end = -1;
291 $min = $min - $start;
292
293 while (-$end <= $min AND $old[strlen($old) + $end] == $new[strlen($new) + $end])
294 {
295 $end--;
296 }
297
298 return array('start' => $start, 'end' => $end + 1);
299 }
300 }
301
302 /*=====================================================================*\
303 || ###################################################################
304 || # $HeadURL$
305 || # $Id$
306 || ###################################################################
307 \*=====================================================================*/
308 ?>