Add the link for the log on browse.php
[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 string
39 */
40 private $raw;
41
42 /**
43 * The current index (file)
44 * @var string
45 */
46 private $index;
47
48 // ###################################################################
49 /**
50 * Constructor
51 *
52 * @param string Raw unified diff output
53 */
54 public function __construct($raw)
55 {
56 $this->raw = $raw;
57 }
58
59 // ###################################################################
60 /**
61 * This function initiates the parsing process and returns the output
62 * in hunks, line-by-line
63 *
64 * @return array Array of outputted hunked information
65 */
66 public function fetch()
67 {
68 global $viewsvn;
69
70 $chunk = 0;
71 $indexcounter = null;
72 $curprop = '';
73
74 $delstack = array();
75
76 foreach ($this->raw AS $line)
77 {
78 if (preg_match('#^@@ \-([0-9]*),([0-9]*) \+([0-9]*),([0-9]*) @@$#', $line, $bits))
79 {
80 $property = false;
81 $delstack = array();
82 $this->diff["$index"][ ++$chunk ]['hunk'] = array('old' => array('line' => $bits[1], 'count' => $bits[2]), 'new' => array('line' => $bits[3], 'count' => $bits[4]));
83 $lines['old'] = $this->diff["$index"]["$chunk"]['hunk']['old']['line'] - 1;
84 $lines['new'] = $this->diff["$index"]["$chunk"]['hunk']['new']['line'] - 1;
85 continue;
86 }
87 else if (preg_match('#^Property changes on: (.*?)$#', $line, $bits))
88 {
89 $property = true;
90 $index = $bits[1];
91 $this->diff["$index"]['props'] = array();
92 continue;
93 }
94
95 if ($indexcounter <= 3 AND $indexcounter !== null)
96 {
97 $indexcounter++;
98 continue;
99 }
100 else if ($indexcounter == 3)
101 {
102 $indexcounter = null;
103 continue;
104 }
105
106 if (preg_match('#^([\+\- ])(.*)#', $line, $matches) AND !$property)
107 {
108 $act = $matches[1];
109 $content = $matches[2];
110
111 if ($act == ' ')
112 {
113 $this->diff["$index"]["$chunk"][] = array(
114 'line' => $content,
115 'act' => '',
116 'oldlineno' => ++$lines['old'],
117 'newlineno' => ++$lines['new']
118 );
119
120 $delstack = array();
121 }
122 else if ($act == '+')
123 {
124 // potential line delta
125 if (sizeof($delstack) > 0)
126 {
127 $lastline = array_shift($delstack);
128
129 if ($delta = @$this->_fetchDiffExtent($lastline['line'], $content))
130 {
131 if (strlen($lastline['line']) > ($delta['start'] - $delta['end']))
132 {
133 $end = strlen($lastline['line']) + $delta['end'];
134 BSRegister::Debug("RM delta- = " . $end);
135 $change = '{@-' . '-}' . substr($lastline['line'], $delta['start'], $end - $delta['start']) . '{/@-' . '-}';
136 $this->diff["$index"]["$chunk"]["$lastline[INDEX]"]['line'] = substr($lastline['line'], 0, $delta['start']) . $change . substr($lastline['line'], $end);
137 }
138
139 if (strlen($content) > $delta['start'] - $delta['end'])
140 {
141 $end = strlen($content) + $delta['end'];
142 BSRegister::Debug("MK delta+ = " . $end);
143 $change = '{@+' . '+}' . substr($content, $delta['start'], $end - $delta['start']) . '{/@+' . '+}';
144 $content = substr($content, 0, $delta['start']) . $change . substr($content, $end);
145 }
146 }
147 }
148
149 $this->diff["$index"]["$chunk"][] = array(
150 'line' => $content,
151 'act' => '+',
152 'oldlineno' => '',
153 'newlineno' => ++$lines['new']
154 );
155 }
156 else if ($act == '-')
157 {
158 $this->diff["$index"]["$chunk"][] = $thearray = array(
159 'line' => $content,
160 'act' => '-',
161 'oldlineno' => ++$lines['old'],
162 'newlineno' => ''
163 );
164
165 $key = sizeof($this->diff["$index"]["$chunk"]) - 2;
166 $thearray['INDEX'] = $key;
167
168 array_push($delstack, $thearray);
169 }
170 }
171 // whitespace lines
172 else
173 {
174 if (preg_match('#^Index: (.*?)$#', $line, $matches))
175 {
176 $index = $matches[1];
177 $indexcounter = 1;
178 $chunk = 0;
179 continue;
180 }
181
182 if ($property)
183 {
184 if (preg_match('#^__*_$#', trim($line)))
185 {
186 BSRegister::Debug("skipping: $line");
187 continue;
188 }
189
190 if (preg_match('#Name: (.*?)$#', $line, $matches))
191 {
192 $curprop = $matches[1];
193 BSRegister::Debug("prop: $curprop");
194 continue;
195 }
196 else
197 {
198 if (preg_match('#^\s+?\+(.*)#', $line, $matches))
199 {
200 $mode = 'add';
201 $this->diff["$index"]['props']["$curprop"]['add'] .= $matches[1];
202 }
203 else if (preg_match('#^\s+?\-(.*)#', $line, $matches))
204 {
205 $mode = 'del';
206 $this->diff["$index"]['props']["$curprop"]['del'] .= $matches[1];
207 }
208 else if (!preg_match('#^\s+[\+\- ](.*)#', $line) AND trim($line) != '')
209 {
210 $this->diff["$index"]['props']["$curprop"]["$mode"] .= "\n" . $line;
211 }
212 continue;
213 }
214 }
215
216 $this->diff["$index"]["$chunk"][] = array(
217 'line' => '',
218 'act' => '',
219 'oldlineno' => ++$lines['old'],
220 'newlineno' => ++$lines['new']
221 );
222
223 $delstack = array();
224 }
225 }
226
227 return $this->diff;
228 }
229
230 // ###################################################################
231 /**
232 * Returns the amount of change that occured between two lines
233 *
234 * @param string Old line
235 * @param string New line
236 *
237 * @return array Difference of positions
238 */
239 private function _fetchDiffExtent($old, $new)
240 {
241 global $viewsvn;
242
243 $start = 0;
244 $min = min(strlen($old), strlen($new));
245
246 BSRegister::Debug("min1 = $min");
247
248 while ($start < $min AND $old["$start"] == $new["$start"])
249 {
250 $start++;
251 }
252
253 $end = -1;
254 $min = $min - $start;
255
256 BSRegister::Debug("min2 = $min");
257
258 BSRegister::Debug("checking: " . $old[ strlen($old) + $end ] . ' == ' . $new[ strlen($new) + $end ]);
259
260 while (-$end <= $min AND $old[ strlen($old) + $end ] == $new[ strlen($new) + $end ])
261 {
262 $end--;
263 }
264
265 return array('start' => $start, 'end' => $end + 1);
266 }
267 }
268
269 /*=====================================================================*\
270 || ###################################################################
271 || # $HeadURL$
272 || # $Id$
273 || ###################################################################
274 \*=====================================================================*/
275 ?>