1 | 1 | simandl | <?php |
2 | | | // WebSVN - Subversion repository viewing via the web using PHP |
3 | | | // Copyright (C) 2004-2006 Tim Armes |
4 | | | // |
5 | | | // This program is free software; you can redistribute it and/or modify |
6 | | | // it under the terms of the GNU General Public License as published by |
7 | | | // the Free Software Foundation; either version 2 of the License, or |
8 | | | // (at your option) any later version. |
9 | | | // |
10 | | | // This program is distributed in the hope that it will be useful, |
11 | | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | | // GNU General Public License for more details. |
14 | | | // |
15 | | | // You should have received a copy of the GNU General Public License |
16 | | | // along with this program; if not, write to the Free Software |
17 | | | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | | | // |
19 | | | // -- |
20 | | | // |
21 | | | // svn-look.php |
22 | | | // |
23 | | | // Svn bindings |
24 | | | // |
25 | | | // These binding currently use the svn command line to achieve their goal. Once a proper |
26 | | | // SWIG binding has been produced for PHP, there'll be an option to use that instead. |
27 | | | |
28 | | | require_once("include/utils.php"); |
29 | | | |
30 | | | // {{{ Classes for retaining log information --- |
31 | | | |
32 | | | $debugxml = false; |
33 | | | |
34 | | | class SVNMod { |
35 | | | var $action = ''; |
36 | | | var $copyfrom = ''; |
37 | | | var $copyrev = ''; |
38 | | | var $path = ''; |
39 | | | } |
40 | | | |
41 | | | class SVNListEntry { |
42 | | | var $rev = 1; |
43 | | | var $author = ''; |
44 | | | var $date = ''; |
45 | | | var $committime; |
46 | | | var $age = ''; |
47 | | | var $file = ''; |
48 | | | var $isdir = false; |
49 | | | } |
50 | | | |
51 | | | class SVNList { |
52 | | | var $entries; // Array of entries |
53 | | | var $curEntry; // Current entry |
54 | | | |
55 | | | var $path = ''; // The path of the list |
56 | | | } |
57 | | | |
58 | | | class SVNLogEntry { |
59 | | | var $rev = 1; |
60 | | | var $author = ''; |
61 | | | var $date = ''; |
62 | | | var $committime; |
63 | | | var $age = ''; |
64 | | | var $msg = ''; |
65 | | | var $path = ''; |
66 | | | |
67 | | | var $mods; |
68 | | | var $curMod; |
69 | | | } |
70 | | | |
71 | | | function SVNLogEntry_compare($a, $b) { |
72 | | | return strnatcasecmp($a->path, $b->path); |
73 | | | } |
74 | | | |
75 | | | class SVNLog { |
76 | | | var $entries; // Array of entries |
77 | | | var $curEntry; // Current entry |
78 | | | |
79 | | | var $path = ''; // Temporary variable used to trace path history |
80 | | | |
81 | | | // findEntry |
82 | | | // |
83 | | | // Return the entry for a given revision |
84 | | | |
85 | | | function findEntry($rev) { |
86 | | | foreach ($this->entries as $index => $entry) { |
87 | | | if ($entry->rev == $rev) { |
88 | | | return $index; |
89 | | | } |
90 | | | } |
91 | | | } |
92 | | | } |
93 | | | |
94 | | | // }}} |
95 | | | |
96 | | | // {{{ XML parsing functions--- |
97 | | | |
98 | | | $curTag = ''; |
99 | | | |
100 | | | $curList = 0; |
101 | | | |
102 | | | // {{{ listStartElement |
103 | | | |
104 | | | function listStartElement($parser, $name, $attrs) { |
105 | | | global $curList, $curTag, $debugxml; |
106 | | | |
107 | | | switch ($name) { |
108 | | | case "LIST": |
109 | | | if ($debugxml) print "Starting list\n"; |
110 | | | |
111 | | | if (sizeof($attrs)) { |
112 | | | while (list($k, $v) = each($attrs)) { |
113 | | | switch ($k) { |
114 | | | case "PATH": |
115 | | | if ($debugxml) print "Path $v\n"; |
116 | | | $curList->path = $v; |
117 | | | break; |
118 | | | } |
119 | | | } |
120 | | | } |
121 | | | break; |
122 | | | |
123 | | | case "ENTRY": |
124 | | | if ($debugxml) print "Creating new entry\n"; |
125 | | | $curList->curEntry = new SVNListEntry; |
126 | | | |
127 | | | if (sizeof($attrs)) { |
128 | | | while (list($k, $v) = each($attrs)) { |
129 | | | switch ($k) { |
130 | | | case "KIND": |
131 | | | if ($debugxml) print "Kind $v\n"; |
132 | | | $curList->curEntry->isdir = ($v == 'dir'); |
133 | | | break; |
134 | | | } |
135 | | | } |
136 | | | } |
137 | | | break; |
138 | | | |
139 | | | case "COMMIT": |
140 | | | if ($debugxml) print "Commit\n"; |
141 | | | |
142 | | | if (sizeof($attrs)) { |
143 | | | while (list($k, $v) = each($attrs)) { |
144 | | | switch ($k) { |
145 | | | case "REVISION": |
146 | | | if ($debugxml) print "Revision $v\n"; |
147 | | | $curList->curEntry->rev = $v; |
148 | | | break; |
149 | | | } |
150 | | | } |
151 | | | } |
152 | | | break; |
153 | | | |
154 | | | default: |
155 | | | $curTag = $name; |
156 | | | break; |
157 | | | } |
158 | | | } |
159 | | | |
160 | | | // }}} |
161 | | | |
162 | | | // {{{ listEndElement |
163 | | | |
164 | | | function listEndElement($parser, $name) { |
165 | | | global $curList, $debugxml, $curTag; |
166 | | | |
167 | | | switch ($name) { |
168 | | | case "ENTRY": |
169 | | | if ($debugxml) print "Ending new list entry\n"; |
170 | | | if ($curList->curEntry->isdir) { |
171 | 3 | simandl | $curList->curEntry->file .= '/'; |
172 | 1 | simandl | } |
173 | | | $curList->entries[] = $curList->curEntry; |
174 | | | $curList->curEntry = null; |
175 | | | break; |
176 | | | } |
177 | | | |
178 | | | $curTag = ""; |
179 | | | } |
180 | | | |
181 | | | // }}} |
182 | | | |
183 | | | // {{{ listCharacterData |
184 | | | |
185 | | | function listCharacterData($parser, $data) { |
186 | | | global $curList, $curTag, $lang, $debugxml; |
187 | | | |
188 | | | switch ($curTag) { |
189 | | | case "NAME": |
190 | | | if ($debugxml) print "Name: $data\n"; |
191 | | | if (empty($data)) return; |
192 | | | $curList->curEntry->file .= $data; |
193 | | | break; |
194 | | | |
195 | | | case "AUTHOR": |
196 | | | if ($debugxml) print "Author: $data\n"; |
197 | | | if (empty($data)) return; |
198 | | | $curList->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
199 | | | break; |
200 | | | |
201 | | | case "DATE": |
202 | | | if ($debugxml) print "Date: $data\n"; |
203 | | | $data = trim($data); |
204 | | | if (empty($data)) return; |
205 | | | |
206 | 3 | simandl | $y = 0; |
207 | | | $mo = 0; |
208 | | | $d = 0; |
209 | | | $h = 0; |
210 | | | $m = 0; |
211 | | | $s = 0; |
212 | 1 | simandl | sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s); |
213 | | | |
214 | | | $mo = substr("00".$mo, -2); |
215 | | | $d = substr("00".$d, -2); |
216 | | | $h = substr("00".$h, -2); |
217 | | | $m = substr("00".$m, -2); |
218 | | | $s = substr("00".$s, -2); |
219 | | | |
220 | 3 | simandl | $committime = strtotime("$y-$mo-$d $h:$m:$s GMT"); |
221 | 1 | simandl | |
222 | 3 | simandl | $curList->curEntry->date = strftime('%Y-%m-%d %H:%M:%S', $committime); |
223 | | | |
224 | 1 | simandl | $curList->curEntry->committime = $committime; |
225 | | | $curtime = time(); |
226 | | | |
227 | | | // Get the number of seconds since the commit |
228 | | | $agesecs = $curtime - $committime; |
229 | | | if ($agesecs < 0) $agesecs = 0; |
230 | | | |
231 | | | $curList->curEntry->age = datetimeFormatDuration($agesecs, true, true); |
232 | | | |
233 | | | break; |
234 | | | } |
235 | | | } |
236 | | | |
237 | | | // }}} |
238 | | | |
239 | | | $curLog = 0; |
240 | | | |
241 | | | // {{{ logStartElement |
242 | | | |
243 | | | function logStartElement($parser, $name, $attrs) { |
244 | | | global $curLog, $curTag, $debugxml; |
245 | | | |
246 | | | switch ($name) { |
247 | | | case "LOGENTRY": |
248 | | | if ($debugxml) print "Creating new log entry\n"; |
249 | | | $curLog->curEntry = new SVNLogEntry; |
250 | | | $curLog->curEntry->mods = array(); |
251 | | | |
252 | | | $curLog->curEntry->path = $curLog->path; |
253 | | | |
254 | | | if (sizeof($attrs)) { |
255 | | | while (list($k, $v) = each($attrs)) { |
256 | | | switch ($k) { |
257 | | | case "REVISION": |
258 | | | if ($debugxml) print "Revision $v\n"; |
259 | | | $curLog->curEntry->rev = $v; |
260 | | | break; |
261 | | | } |
262 | | | } |
263 | | | } |
264 | | | break; |
265 | | | |
266 | | | case "PATH": |
267 | | | if ($debugxml) print "Creating new path\n"; |
268 | | | $curLog->curEntry->curMod = new SVNMod; |
269 | | | |
270 | | | if (sizeof($attrs)) { |
271 | | | while (list($k, $v) = each($attrs)) { |
272 | | | switch ($k) { |
273 | | | case "ACTION": |
274 | | | if ($debugxml) print "Action $v\n"; |
275 | | | $curLog->curEntry->curMod->action = $v; |
276 | | | break; |
277 | | | |
278 | | | case "COPYFROM-PATH": |
279 | | | if ($debugxml) print "Copy from: $v\n"; |
280 | | | $curLog->curEntry->curMod->copyfrom = $v; |
281 | | | break; |
282 | | | |
283 | | | case "COPYFROM-REV": |
284 | | | $curLog->curEntry->curMod->copyrev = $v; |
285 | | | break; |
286 | | | } |
287 | | | } |
288 | | | } |
289 | | | |
290 | | | $curTag = $name; |
291 | | | break; |
292 | | | |
293 | | | default: |
294 | | | $curTag = $name; |
295 | | | break; |
296 | | | } |
297 | | | } |
298 | | | |
299 | | | // }}} |
300 | | | |
301 | | | // {{{ logEndElement |
302 | | | |
303 | | | function logEndElement($parser, $name) { |
304 | | | global $curLog, $debugxml, $curTag; |
305 | | | |
306 | | | switch ($name) { |
307 | | | case "LOGENTRY": |
308 | | | if ($debugxml) print "Ending new log entry\n"; |
309 | | | $curLog->entries[] = $curLog->curEntry; |
310 | | | break; |
311 | | | |
312 | | | case "PATH": |
313 | | | if ($debugxml) print "Ending path\n"; |
314 | | | $curLog->curEntry->mods[] = $curLog->curEntry->curMod; |
315 | | | break; |
316 | | | |
317 | | | case "MSG": |
318 | | | $curLog->curEntry->msg = trim($curLog->curEntry->msg); |
319 | | | if ($debugxml) print "Completed msg = '".$curLog->curEntry->msg."'\n"; |
320 | | | break; |
321 | | | } |
322 | | | |
323 | | | $curTag = ""; |
324 | | | } |
325 | | | |
326 | | | // }}} |
327 | | | |
328 | | | // {{{ logCharacterData |
329 | | | |
330 | | | function logCharacterData($parser, $data) { |
331 | | | global $curLog, $curTag, $lang, $debugxml; |
332 | | | |
333 | | | switch ($curTag) { |
334 | | | case "AUTHOR": |
335 | | | if ($debugxml) print "Author: $data\n"; |
336 | | | if (empty($data)) return; |
337 | | | $curLog->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
338 | | | break; |
339 | | | |
340 | | | case "DATE": |
341 | | | if ($debugxml) print "Date: $data\n"; |
342 | | | $data = trim($data); |
343 | | | if (empty($data)) return; |
344 | | | |
345 | 3 | simandl | $y = 0; |
346 | | | $mo = 0; |
347 | | | $d = 0; |
348 | | | $h = 0; |
349 | | | $m = 0; |
350 | | | $s = 0; |
351 | 1 | simandl | sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s); |
352 | | | |
353 | | | $mo = substr("00".$mo, -2); |
354 | | | $d = substr("00".$d, -2); |
355 | | | $h = substr("00".$h, -2); |
356 | | | $m = substr("00".$m, -2); |
357 | | | $s = substr("00".$s, -2); |
358 | | | |
359 | 3 | simandl | $committime = strtotime("$y-$mo-$d $h:$m:$s GMT"); |
360 | 1 | simandl | |
361 | 3 | simandl | $curLog->curEntry->date = strftime('%Y-%m-%d %H:%M:%S', $committime); |
362 | | | |
363 | 1 | simandl | $curLog->curEntry->committime = $committime; |
364 | | | $curtime = time(); |
365 | | | |
366 | | | // Get the number of seconds since the commit |
367 | | | $agesecs = $curtime - $committime; |
368 | | | if ($agesecs < 0) $agesecs = 0; |
369 | | | |
370 | | | $curLog->curEntry->age = datetimeFormatDuration($agesecs, true, true); |
371 | | | |
372 | | | break; |
373 | | | |
374 | | | case "MSG": |
375 | | | if ($debugxml) print "Msg: '$data'\n"; |
376 | | | $curLog->curEntry->msg .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
377 | | | break; |
378 | | | |
379 | | | case "PATH": |
380 | | | if ($debugxml) print "Path name: '$data'\n"; |
381 | | | $data = trim($data); |
382 | | | if (empty($data)) return; |
383 | | | |
384 | | | $curLog->curEntry->curMod->path .= $data; |
385 | | | |
386 | | | // The XML returned when a file is renamed/branched in inconsistant. In the case |
387 | | | // of a branch, the path information doesn't include the leafname. In the case of |
388 | | | // a rename, it does. Ludicrous. |
389 | | | |
390 | | | if (!empty($curLog->path)) { |
391 | | | $pos = strrpos($curLog->path, "/"); |
392 | | | $curpath = substr($curLog->path, 0, $pos); |
393 | | | $leafname = substr($curLog->path, $pos + 1); |
394 | | | } else { |
395 | | | $curpath = ""; |
396 | | | $leafname = ""; |
397 | | | } |
398 | | | |
399 | | | if ($curLog->curEntry->curMod->action == "A") { |
400 | | | if ($debugxml) print "Examining added path '".$curLog->curEntry->curMod->copyfrom."' - Current path = '$curpath', leafname = '$leafname'\n"; |
401 | | | if ($data == $curLog->path) { // For directories and renames |
402 | | | if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."'\n"; |
403 | | | $curLog->path = $curLog->curEntry->curMod->copyfrom; |
404 | | | } else if ($data == $curpath || $data == $curpath."/") { // Logs of files that have moved due to branching |
405 | | | if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."/$leafname'\n"; |
406 | | | $curLog->path = $curLog->curEntry->curMod->copyfrom."/$leafname"; |
407 | | | } |
408 | | | } |
409 | | | break; |
410 | | | } |
411 | | | } |
412 | | | |
413 | | | // }}} |
414 | | | |
415 | | | // }}} |
416 | | | |
417 | | | // {{{ internal functions (_topLevel and _listSort) |
418 | | | |
419 | | | // Function returns true if the give entry in a directory tree is at the top level |
420 | | | |
421 | | | function _topLevel($entry) { |
422 | | | // To be at top level, there must be one space before the entry |
423 | | | return (strlen($entry) > 1 && $entry{0} == " " && $entry{1} != " "); |
424 | | | } |
425 | | | |
426 | 3 | simandl | // Function to sort two given directory entries. |
427 | | | // Directories go at the top if config option alphabetic is not set |
428 | 1 | simandl | |
429 | | | function _listSort($e1, $e2) { |
430 | 3 | simandl | global $config; |
431 | 1 | simandl | |
432 | 3 | simandl | if (!$config->isAlphabeticOrder()) { |
433 | | | $isDir1 = $e1->file{strlen($e1->file) - 1} == "/"; |
434 | | | $isDir2 = $e2->file{strlen($e2->file) - 1} == "/"; |
435 | 1 | simandl | |
436 | 3 | simandl | if ($isDir1 && !$isDir2) return -1; |
437 | | | if ($isDir2 && !$isDir1) return 1; |
438 | | | } |
439 | | | |
440 | 1 | simandl | return strnatcasecmp($e1->file, $e2->file); |
441 | | | } |
442 | | | |
443 | | | // }}} |
444 | | | |
445 | | | // {{{ encodePath |
446 | | | |
447 | | | // Function to encode a URL without encoding the /'s |
448 | | | |
449 | | | function encodePath($uri) { |
450 | | | global $config; |
451 | | | |
452 | | | $uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri); |
453 | | | |
454 | | | $parts = explode('/', $uri); |
455 | | | for ($i = 0; $i < count($parts); $i++) { |
456 | | | if ( function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding")) { |
457 | | | $parts[$i] = mb_convert_encoding($parts[$i], "UTF-8", mb_detect_encoding($parts[$i])); |
458 | | | } |
459 | | | |
460 | 3 | simandl | // do not urlencode the 'svn+ssh://' part! |
461 | | | if ($i != 0 || $parts[$i] != 'svn+ssh:') { |
462 | | | $parts[$i] = rawurlencode($parts[$i]); |
463 | | | } |
464 | 1 | simandl | } |
465 | | | |
466 | | | $uri = implode('/', $parts); |
467 | | | |
468 | | | // Quick hack. Subversion seems to have a bug surrounding the use of %3A instead of : |
469 | | | |
470 | | | $uri = str_replace("%3A" ,":", $uri); |
471 | | | |
472 | | | // Correct for Window share names |
473 | 3 | simandl | if ($config->serverIsWindows) { |
474 | 1 | simandl | if (substr($uri, 0,2) == "//") { |
475 | | | $uri = "\\".substr($uri, 2, strlen($uri)); |
476 | | | } |
477 | | | |
478 | | | if (substr($uri, 0,10)=="file://///" ) { |
479 | | | $uri="file:///\\".substr($uri, 10, strlen($uri)); |
480 | | | } |
481 | | | } |
482 | | | |
483 | | | return $uri; |
484 | | | } |
485 | | | |
486 | | | // }}} |
487 | | | |
488 | | | // The SVNRepository class |
489 | | | |
490 | | | class SVNRepository { |
491 | | | var $repConfig; |
492 | 3 | simandl | var $geshi = null; |
493 | 1 | simandl | |
494 | | | function SVNRepository($repConfig) { |
495 | | | $this->repConfig = $repConfig; |
496 | | | } |
497 | | | |
498 | | | // {{{ highlightLine |
499 | | | // |
500 | | | // Distill line-spanning syntax highlighting so that each line can stand alone |
501 | | | // (when invoking on the first line, $attributes should be an empty array) |
502 | | | // Invoked to make sure all open syntax highlighting tags (<font>, <i>, <b>, etc.) |
503 | | | // are closed at the end of each line and re-opened on the next line |
504 | | | |
505 | | | function highlightLine($line, &$attributes) { |
506 | | | $hline = ""; |
507 | | | |
508 | | | // Apply any highlighting in effect from the previous line |
509 | | | foreach ($attributes as $attr) { |
510 | | | $hline.=$attr['text']; |
511 | | | } |
512 | | | |
513 | | | // append the new line |
514 | | | $hline.=$line; |
515 | | | |
516 | | | // update attributes |
517 | | | for ($line = strstr($line, "<"); $line; $line = strstr(substr($line,1), "<")) { |
518 | | | if (substr($line,1,1) == "/") { // if this closes a tag, remove most recent corresponding opener |
519 | | | $tagNamLen = strcspn($line, "> \t", 2); |
520 | | | $tagNam = substr($line,2,$tagNamLen); |
521 | | | foreach (array_reverse(array_keys($attributes)) as $k) { |
522 | | | if ($attributes[$k]['tag'] == $tagNam) { |
523 | | | unset($attributes[$k]); |
524 | | | break; |
525 | | | } |
526 | | | } |
527 | | | } else { // if this opens a tag, add it to the list |
528 | | | $tagNamLen = strcspn($line, "> \t", 1); |
529 | | | $tagNam = substr($line,1,$tagNamLen); |
530 | | | $tagLen = strcspn($line, ">") + 1; |
531 | | | $attributes[] = array('tag' => $tagNam, 'text' => substr($line,0,$tagLen)); |
532 | | | } |
533 | | | } |
534 | | | |
535 | | | // close any still-open tags |
536 | | | foreach (array_reverse($attributes) as $attr) { |
537 | | | $hline.="</".$attr['tag'].">"; |
538 | | | } |
539 | | | |
540 | | | // XXX: this just simply replaces [ and ] with their entities to prevent |
541 | | | // it from being parsed by the template parser; maybe something more |
542 | | | // elegant is in order? |
543 | | | $hline = str_replace('[', '[', str_replace(']', ']', $hline) ); |
544 | | | return $hline; |
545 | | | } |
546 | | | |
547 | | | // }}} |
548 | | | |
549 | | | // {{{ getFileContents |
550 | | | // |
551 | | | // Dump the content of a file to the given filename |
552 | | | |
553 | | | function getFileContents($path, $filename, $rev = 0, $pipe = "", $perLineHighlighting = false) { |
554 | | | global $config, $extEnscript; |
555 | | | |
556 | 3 | simandl | $highlighted = false; |
557 | | | |
558 | 1 | simandl | // If there's no filename, we'll just deliver the contents as it is to the user |
559 | | | if ($filename == "") { |
560 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
561 | | | passthruCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' '.$pipe); |
562 | | | return $highlighted; |
563 | 1 | simandl | } |
564 | | | |
565 | | | // Get the file contents info |
566 | | | |
567 | | | $ext = strrchr($path, "."); |
568 | | | $l = @$extEnscript[$ext]; |
569 | | | |
570 | | | if ($l == "php") { |
571 | | | // Output the file to the filename |
572 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
573 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename); |
574 | | | $retcode = 0; |
575 | | | execCommand($cmd, $retcode); |
576 | | | if ($retcode != 0) { |
577 | | | print'Unable to call svn command "'.$config->svn.'"'; |
578 | | | exit(0); |
579 | | | } |
580 | 1 | simandl | |
581 | | | // Get the file as a string (memory hogging, but we have no other options) |
582 | | | $content = highlight_file($filename, true); |
583 | | | |
584 | | | // Destroy the previous version, and replace it with the highlighted version |
585 | | | $f = fopen($filename, "w"); |
586 | | | if ($f) { |
587 | 3 | simandl | $highlighted = true; |
588 | 1 | simandl | // The highlight file function doesn't deal with line endings very nicely at all. We'll have to do it |
589 | | | // by hand. |
590 | | | |
591 | | | // Remove the first line generated by highlight() |
592 | | | $pos = strpos($content, "\n"); |
593 | | | $content = substr($content, $pos+1); |
594 | | | |
595 | | | $content = explode("<br />", $content); |
596 | | | |
597 | | | if ($perLineHighlighting) { |
598 | | | // If we need each line independently highlighted (e.g. for diff or blame) |
599 | 3 | simandl | // then we'll need to filter the output of the highlighter |
600 | 1 | simandl | // to make sure tags like <font>, <i> or <b> don't span lines |
601 | | | |
602 | | | // $attributes is used to remember what highlighting attributes |
603 | | | // are in effect from one line to the next |
604 | | | $attributes = array(); // start with no attributes in effect |
605 | | | |
606 | | | foreach ($content as $line) { |
607 | 3 | simandl | fputs($f, $this->highlightLine(rtrim($line), $attributes)."\n"); |
608 | 1 | simandl | } |
609 | | | } else { |
610 | | | foreach ($content as $line) { |
611 | | | fputs($f, rtrim($line)."\n"); |
612 | | | } |
613 | | | } |
614 | | | |
615 | | | fclose($f); |
616 | | | } |
617 | | | |
618 | | | } else { |
619 | 3 | simandl | $tempname = $filename; |
620 | | | if ($perLineHighlighting) { |
621 | | | $tempname = tempnam('temp', ''); |
622 | | | } |
623 | | | $highlighted = true; |
624 | | | if ($config->useGeshi && $geshiLang = $this->highlightLanguageUsingGeshi($ext)) { |
625 | | | $this->applyGeshi($path, $tempname, $rev, $geshiLang); |
626 | 1 | simandl | |
627 | | | } else if ($config->useEnscript) { |
628 | | | // Get the files, feed it through enscript, then remove the enscript headers using sed |
629 | | | // |
630 | | | // Note that the sec command returns only the part of the file between <PRE> and </PRE>. |
631 | | | // It's complicated because it's designed not to return those lines themselves. |
632 | | | |
633 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
634 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' | '. |
635 | | | $config->enscript." --language=html ". |
636 | | | ($l ? "--color --pretty-print=$l" : "")." -o - | ". |
637 | | | $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $tempname"; |
638 | | | $retcode = 0; |
639 | | | execCommand($cmd, $retcode); |
640 | | | if ($retcode != 0) { |
641 | | | print'Unable to call svn command "'.$config->svn.'"'; |
642 | | | if ($tempname != $filename) { |
643 | | | @unlink($tempname); |
644 | | | } |
645 | | | exit(0); |
646 | | | } |
647 | 1 | simandl | |
648 | | | } else { |
649 | 3 | simandl | $highlighted = false; |
650 | | | $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->getSvnpath($path))); |
651 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename); |
652 | | | $retcode = 0; |
653 | | | execCommand($cmd, $retcode); |
654 | | | if ($retcode != 0) { |
655 | | | print'Unable to call svn command "'.$config->svn.'"'; |
656 | | | if ($tempname != $filename) { |
657 | | | @unlink($tempname); |
658 | | | } |
659 | | | exit(0); |
660 | | | } |
661 | 1 | simandl | } |
662 | 3 | simandl | |
663 | | | if ($highlighted && $perLineHighlighting) { |
664 | | | // If we need each line independently highlighted (e.g. for diff or blame) |
665 | | | // then we'll need to filter the output of the highlighter |
666 | | | // to make sure tags like <font>, <i> or <b> don't span lines |
667 | | | |
668 | | | $dst = fopen($filename, 'w'); |
669 | | | if ($dst) { |
670 | | | $content = file_get_contents($tempname); |
671 | | | $content = explode('<br />', $content); |
672 | | | |
673 | | | // $attributes is used to remember what highlighting attributes |
674 | | | // are in effect from one line to the next |
675 | | | $attributes = array(); // start with no attributes in effect |
676 | | | |
677 | | | foreach ($content as $line) { |
678 | | | fputs($dst, $this->highlightLine(trim($line), $attributes)."\n"); |
679 | | | } |
680 | | | fclose($dst); |
681 | | | } |
682 | | | } |
683 | | | if ($tempname != $filename) { |
684 | | | @unlink($tempname); |
685 | | | } |
686 | 1 | simandl | } |
687 | 3 | simandl | return $highlighted; |
688 | 1 | simandl | } |
689 | | | |
690 | | | // }}} |
691 | | | |
692 | 3 | simandl | // {{{ highlightLanguageUsingGeshi |
693 | | | // |
694 | | | // check if geshi can highlight the given extension and return the language |
695 | | | |
696 | | | function highlightLanguageUsingGeshi($ext) { |
697 | | | global $extGeshi; |
698 | | | if (substr($ext, 0, 1) == '.') $ext = substr($ext, 1); |
699 | | | |
700 | | | foreach ($extGeshi as $lang => $extensions) { |
701 | | | if (in_array($ext, $extensions)) { |
702 | | | if ($this->geshi === null) { |
703 | | | require_once 'lib/geshi.php'; |
704 | | | $this->geshi = new GeSHi(); |
705 | | | } else { |
706 | | | $this->geshi->error = false; |
707 | | | } |
708 | | | $this->geshi->set_language($lang); |
709 | | | if ($this->geshi->error() === false) { |
710 | | | return $lang; |
711 | | | } |
712 | | | } |
713 | | | } |
714 | | | return ''; |
715 | | | |
716 | | | |
717 | | | } |
718 | | | |
719 | | | // }}} |
720 | | | |
721 | 1 | simandl | // {{{ applyGeshi |
722 | | | // |
723 | | | // perform syntax highlighting using geshi |
724 | | | |
725 | 3 | simandl | function applyGeshi($path, $filename, $rev, $lang, $return = false) { |
726 | 1 | simandl | global $config; |
727 | | | |
728 | | | // Output the file to the filename |
729 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
730 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename); |
731 | | | $retcode = 0; |
732 | | | execCommand($cmd, $retcode); |
733 | | | if ($retcode != 0) { |
734 | | | print'Unable to call svn command "'.$config->svn.'"'; |
735 | | | exit(0); |
736 | | | } |
737 | 1 | simandl | |
738 | | | $source = file_get_contents($filename); |
739 | 3 | simandl | if ($this->geshi === null) { |
740 | | | require_once 'lib/geshi.php'; |
741 | | | $this->geshi = new GeSHi(); |
742 | | | } |
743 | | | $this->geshi->set_source($source); |
744 | | | $this->geshi->set_language($lang); |
745 | | | $this->geshi->set_header_type(GESHI_HEADER_DIV); |
746 | | | $this->geshi->set_overall_class('geshi'); |
747 | | | $this->geshi->set_tab_width($this->repConfig->getExpandTabsBy()); |
748 | | | |
749 | 1 | simandl | if ($return) { |
750 | 3 | simandl | return $this->geshi->parse_code(); |
751 | 1 | simandl | } else { |
752 | | | $f = @fopen($filename, 'w'); |
753 | 3 | simandl | fwrite($f, $this->geshi->parse_code()); |
754 | 1 | simandl | fclose($f); |
755 | | | } |
756 | | | } |
757 | | | |
758 | | | // }}} |
759 | | | |
760 | | | // {{{ listFileContents |
761 | | | // |
762 | | | // Print the contents of a file without filling up Apache's memory |
763 | | | |
764 | | | function listFileContents($path, $rev = 0) { |
765 | | | global $config, $extEnscript; |
766 | | | |
767 | | | $pre = false; |
768 | | | |
769 | | | // Get the file contents info |
770 | | | |
771 | | | $ext = strrchr($path, "."); |
772 | | | $l = @$extEnscript[$ext]; |
773 | | | |
774 | | | // Deal with php highlighting internally |
775 | | | if ($l == "php") { |
776 | | | $tmp = tempnam("temp", "wsvn"); |
777 | | | |
778 | | | // Output the file to a temporary file |
779 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
780 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.$tmp; |
781 | | | $retcode = 0; |
782 | | | execCommand($cmd, $retcode); |
783 | | | if ($retcode != 0) { |
784 | | | print'Unable to call svn command "'.$config->svn.'"'; |
785 | | | @unlink($tmp); |
786 | | | exit(0); |
787 | | | } |
788 | 1 | simandl | $tmpStr = file_get_contents($tmp); |
789 | | | $tmpStr = str_replace(array("\r\n"), array("\n"), $tmpStr); |
790 | | | highlight_string($tmpStr); |
791 | | | @unlink($tmp); |
792 | 3 | simandl | } else if ($config->useGeshi && $geshiLang = $this->highlightLanguageUsingGeshi($ext)) { |
793 | 1 | simandl | $tmp = tempnam("temp", "wsvn"); |
794 | 3 | simandl | print toOutputEncoding($this->applyGeshi($path, $tmp, $rev, $geshiLang, true), $this->repConfig->getContentEncoding()); |
795 | | | @unlink($tmp); |
796 | 1 | simandl | } else { |
797 | | | if ($config->useEnscript) { |
798 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
799 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' | '. |
800 | 1 | simandl | $config->enscript." --language=html ". |
801 | | | ($l ? "--color --pretty-print=$l" : "")." -o - | ". |
802 | 3 | simandl | $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote; |
803 | 1 | simandl | } else { |
804 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
805 | | | $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev); |
806 | 1 | simandl | $pre = true; |
807 | | | } |
808 | | | |
809 | 3 | simandl | if ($result = popenCommand($cmd, "r")) { |
810 | | | if ($pre) echo "<pre>"; |
811 | 1 | simandl | |
812 | 3 | simandl | $contentEncoding = $this->repConfig->getContentEncoding(); |
813 | 1 | simandl | while (!feof($result)) { |
814 | | | $line = fgets($result, 1024); |
815 | | | if ($pre) $line = replaceEntities($line, $this->repConfig); |
816 | 3 | simandl | else $line = toOutputEncoding($line, $contentEncoding); |
817 | 1 | simandl | |
818 | | | print hardspace($line); |
819 | | | } |
820 | | | |
821 | 3 | simandl | if ($pre) echo "</pre>"; |
822 | 1 | simandl | |
823 | | | pclose($result); |
824 | | | } |
825 | | | } |
826 | | | } |
827 | | | |
828 | | | // }}} |
829 | | | |
830 | | | // {{{ getBlameDetails |
831 | | | // |
832 | | | // Dump the blame content of a file to the given filename |
833 | | | |
834 | | | function getBlameDetails($path, $filename, $rev = 0) { |
835 | | | global $config; |
836 | | | |
837 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
838 | | | $cmd = $config->svn." blame ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename); |
839 | 1 | simandl | |
840 | 3 | simandl | $retcode = 0; |
841 | | | execCommand($cmd, $retcode); |
842 | | | if ($retcode != 0) { |
843 | | | print'Unable to call svn command "'.$config->svn.'"'; |
844 | | | exit(0); |
845 | | | } |
846 | 1 | simandl | } |
847 | | | |
848 | | | // }}} |
849 | | | |
850 | | | // {{{ getProperty |
851 | | | |
852 | | | function getProperty($path, $property, $rev = 0) { |
853 | | | global $config; |
854 | | | |
855 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
856 | 1 | simandl | |
857 | | | if ($rev > 0) { |
858 | 3 | simandl | $rev = '@'.$rev; |
859 | 1 | simandl | } else { |
860 | | | $rev = ''; |
861 | | | } |
862 | | | |
863 | 3 | simandl | $ret = runCommand($config->svn." propget $property ".$this->repConfig->svnParams().quote($path.$rev), true); |
864 | 1 | simandl | |
865 | | | // Remove the surplus newline |
866 | | | if (count($ret)) { |
867 | | | unset($ret[count($ret) - 1]); |
868 | | | } |
869 | | | |
870 | | | return implode("\n", $ret); |
871 | | | } |
872 | | | |
873 | | | // }}} |
874 | | | |
875 | | | // {{{ exportDirectory |
876 | | | // |
877 | | | // Exports the directory to the given location |
878 | | | |
879 | | | function exportDirectory($path, $filename, $rev = 0) { |
880 | | | global $config; |
881 | | | |
882 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
883 | | | $cmd = $config->svn." export ".$this->repConfig->svnParams().quote($path.'@'.$rev).' '.quote($filename); |
884 | 1 | simandl | |
885 | 3 | simandl | $retcode = 0; |
886 | | | execCommand($cmd, $retcode); |
887 | | | if ($retcode != 0) { |
888 | | | print'Unable to call svn command "'.$config->svn.'"'; |
889 | | | exit(0); |
890 | | | } |
891 | 1 | simandl | } |
892 | | | |
893 | | | // }}} |
894 | | | |
895 | | | // {{{ getList |
896 | | | |
897 | | | function getList($path, $rev = 0) { |
898 | | | global $config, $curList, $vars, $lang; |
899 | | | |
900 | | | $xml_parser = xml_parser_create("UTF-8"); |
901 | | | xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true); |
902 | | | xml_set_element_handler($xml_parser, "listStartElement", "listEndElement"); |
903 | | | xml_set_character_data_handler($xml_parser, "listCharacterData"); |
904 | | | |
905 | | | // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove |
906 | | | // the trailing slash from the path for comparison purposes |
907 | | | |
908 | | | if ($path{strlen($path) - 1} == "/" && $path != "/") { |
909 | | | $path = substr($path, 0, -1); |
910 | | | } |
911 | | | |
912 | | | $curList = new SVNList; |
913 | | | $curList->entries = array(); |
914 | | | $curList->path = $path; |
915 | | | |
916 | | | // Get the list info |
917 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
918 | 1 | simandl | |
919 | | | if ($rev == 0) { |
920 | | | $headlog = $this->getLog("/", "", "", true, 1); |
921 | 3 | simandl | if (is_string($headlog)) { |
922 | | | echo $headlog; |
923 | | | exit; |
924 | | | } |
925 | 1 | simandl | if (isset($headlog->entries[0])) $rev = $headlog->entries[0]->rev; |
926 | | | } |
927 | | | |
928 | 3 | simandl | $cmd = quoteCommand($config->svn.' list --xml '.$this->repConfig->svnParams().quote($path.'@'.$rev)); |
929 | 1 | simandl | |
930 | | | $descriptorspec = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')); |
931 | | | |
932 | | | $resource = proc_open($cmd, $descriptorspec, $pipes); |
933 | | | |
934 | | | if (!is_resource($resource)) { |
935 | | | echo "<p>".$lang['BADCMD'].": <code>".$cmd."</code></p>"; |
936 | | | exit; |
937 | | | } |
938 | | | |
939 | | | $handle = $pipes[1]; |
940 | | | $firstline = true; |
941 | | | while (!feof($handle)) { |
942 | | | $line = fgets($handle); |
943 | | | if (!xml_parse($xml_parser, $line, feof($handle))) { |
944 | 3 | simandl | $errorMsg = sprintf("XML error: %s (%d) at line %d column %d byte %d\ncmd: %s", |
945 | 1 | simandl | xml_error_string(xml_get_error_code($xml_parser)), |
946 | | | xml_get_error_code($xml_parser), |
947 | | | xml_get_current_line_number($xml_parser), |
948 | | | xml_get_current_column_number($xml_parser), |
949 | | | xml_get_current_byte_index($xml_parser), |
950 | 3 | simandl | $cmd); |
951 | | | if (xml_get_error_code($xml_parser) != 5) { |
952 | | | // errors can contain sensitive info! don't echo this ~J |
953 | | | error_log($errorMsg); |
954 | 1 | simandl | exit; |
955 | | | } else { |
956 | 3 | simandl | break; |
957 | 1 | simandl | } |
958 | | | } |
959 | | | } |
960 | | | |
961 | 3 | simandl | $error = ''; |
962 | 1 | simandl | while (!feof($pipes[2])) { |
963 | | | $error .= fgets($pipes[2]); |
964 | | | } |
965 | | | |
966 | | | $error = toOutputEncoding(trim($error)); |
967 | | | |
968 | | | fclose($pipes[0]); |
969 | | | fclose($pipes[1]); |
970 | | | fclose($pipes[2]); |
971 | | | |
972 | | | proc_close($resource); |
973 | | | |
974 | | | if (!empty($error)) { |
975 | 3 | simandl | echo '<p>'.$lang['BADCMD'].': <code>'.$cmd.'</code></p><p>'.nl2br($error).'</p>'; |
976 | 1 | simandl | exit; |
977 | | | } |
978 | | | |
979 | | | xml_parser_free($xml_parser); |
980 | | | |
981 | 3 | simandl | // Sort the entries into alphabetical order |
982 | 1 | simandl | usort($curList->entries, "_listSort"); |
983 | | | |
984 | | | return $curList; |
985 | | | } |
986 | | | |
987 | | | // }}} |
988 | | | |
989 | | | // {{{ getLog |
990 | | | |
991 | | | function getLog($path, $brev = "", $erev = 1, $quiet = false, $limit = 2) { |
992 | | | global $config, $curLog, $vars, $lang; |
993 | | | |
994 | | | $xml_parser = xml_parser_create("UTF-8"); |
995 | | | xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true); |
996 | | | xml_set_element_handler($xml_parser, "logStartElement", "logEndElement"); |
997 | | | xml_set_character_data_handler($xml_parser, "logCharacterData"); |
998 | | | |
999 | | | // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove |
1000 | | | // the trailing slash from the path for comparison purposes |
1001 | | | |
1002 | | | if ($path{strlen($path) - 1} == "/" && $path != "/") { |
1003 | | | $path = substr($path, 0, -1); |
1004 | | | } |
1005 | | | |
1006 | | | $curLog = new SVNLog; |
1007 | | | $curLog->entries = array(); |
1008 | | | $curLog->path = $path; |
1009 | | | |
1010 | | | $revStr = ""; |
1011 | | | |
1012 | | | if ($brev && $erev) { |
1013 | | | $revStr = "-r$brev:$erev"; |
1014 | | | } else if ($brev) { |
1015 | | | $revStr = "-r$brev:1"; |
1016 | | | } |
1017 | | | |
1018 | | | if (($config->subversionMajorVersion > 1 || $config->subversionMinorVersion >=2) && $limit != 0) { |
1019 | | | $revStr .= " --limit $limit"; |
1020 | | | } |
1021 | | | |
1022 | | | // Get the log info |
1023 | 3 | simandl | $path = encodepath($this->getSvnpath($path)); |
1024 | 1 | simandl | $info = "--verbose"; |
1025 | | | if ($quiet) $info = "--quiet"; |
1026 | | | |
1027 | 3 | simandl | $pegRev = ''; |
1028 | | | if ($brev) { |
1029 | | | $pegRev = '@'.$brev; |
1030 | | | } |
1031 | | | $cmd = quoteCommand($config->svn." log --xml $info $revStr ".$this->repConfig->svnParams().quote($path.$pegRev)); |
1032 | 1 | simandl | |
1033 | | | $descriptorspec = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')); |
1034 | | | |
1035 | | | $resource = proc_open($cmd, $descriptorspec, $pipes); |
1036 | | | |
1037 | | | if (!is_resource($resource)) { |
1038 | | | echo "<p>".$lang['BADCMD'].": <code>".$cmd."</code></p>"; |
1039 | | | exit; |
1040 | | | } |
1041 | | | |
1042 | | | $handle = $pipes[1]; |
1043 | | | $firstline = true; |
1044 | | | while (!feof($handle)) { |
1045 | | | $line = fgets($handle); |
1046 | | | if (!xml_parse($xml_parser, $line, feof($handle))) { |
1047 | 3 | simandl | $errorMsg = sprintf("XML error: %s (%d) at line %d column %d byte %d\ncmd: %s", |
1048 | 1 | simandl | xml_error_string(xml_get_error_code($xml_parser)), |
1049 | | | xml_get_error_code($xml_parser), |
1050 | | | xml_get_current_line_number($xml_parser), |
1051 | | | xml_get_current_column_number($xml_parser), |
1052 | | | xml_get_current_byte_index($xml_parser), |
1053 | 3 | simandl | $cmd); |
1054 | | | if (xml_get_error_code($xml_parser) != 5) { |
1055 | | | // errors can contain sensitive info! don't echo this ~J |
1056 | | | error_log($errorMsg); |
1057 | 1 | simandl | exit; |
1058 | | | } else { |
1059 | 3 | simandl | break; |
1060 | 1 | simandl | } |
1061 | | | } |
1062 | | | } |
1063 | | | |
1064 | 3 | simandl | $error = ''; |
1065 | 1 | simandl | while (!feof($pipes[2])) { |
1066 | | | $error .= fgets($pipes[2]); |
1067 | | | } |
1068 | | | |
1069 | | | $error = toOutputEncoding(trim($error)); |
1070 | | | |
1071 | | | fclose($pipes[0]); |
1072 | | | fclose($pipes[1]); |
1073 | | | fclose($pipes[2]); |
1074 | | | |
1075 | | | proc_close($resource); |
1076 | | | |
1077 | | | if (!empty($error)) { |
1078 | 3 | simandl | return '<p>'.$lang['BADCMD'].': <code>'.$cmd.'</code></p><p>'.nl2br($error).'</p>'; |
1079 | 1 | simandl | } |
1080 | | | |
1081 | | | xml_parser_free($xml_parser); |
1082 | | | |
1083 | | | foreach ($curLog->entries as $entryKey => $entry) { |
1084 | | | $fullModAccess = true; |
1085 | | | $anyModAccess = (count($entry->mods) == 0); |
1086 | | | foreach ($entry->mods as $modKey => $mod) { |
1087 | | | $access = $this->repConfig->hasReadAccess($mod->path); |
1088 | | | if ($access) { |
1089 | | | $anyModAccess = true; |
1090 | | | } else { |
1091 | | | // hide modified entry when access is prohibited |
1092 | | | unset($curLog->entries[$entryKey]->mods[$modKey]); |
1093 | | | $fullModAccess = false; |
1094 | | | } |
1095 | | | } |
1096 | | | if (!$fullModAccess) { |
1097 | | | // hide commit message when access to any of the entries is prohibited |
1098 | | | $curLog->entries[$entryKey]->msg = ''; |
1099 | | | } |
1100 | | | if (!$anyModAccess) { |
1101 | | | // hide author and date when access to all of the entries is prohibited |
1102 | | | $curLog->entries[$entryKey]->author = ''; |
1103 | | | $curLog->entries[$entryKey]->date = ''; |
1104 | | | $curLog->entries[$entryKey]->committime = ''; |
1105 | | | $curLog->entries[$entryKey]->age = ''; |
1106 | | | } |
1107 | | | } |
1108 | | | |
1109 | | | return $curLog; |
1110 | | | } |
1111 | | | |
1112 | | | // }}} |
1113 | | | |
1114 | 3 | simandl | function isFile($path, $rev = 0) { |
1115 | | | global $config; |
1116 | | | |
1117 | | | $path = encodepath($this->getSvnpath($path)); |
1118 | | | if ($rev != 0) { |
1119 | | | $rev = '@'.$rev; |
1120 | | | } else { |
1121 | | | $rev = ''; |
1122 | | | } |
1123 | | | $cmd = $config->svn." info --xml ".$this->repConfig->svnParams().quote($path.$rev); |
1124 | | | $output = runCommand($cmd, true); |
1125 | | | |
1126 | | | return strpos(implode(' ', $output), 'kind="file"') !== false; |
1127 | | | } |
1128 | | | |
1129 | | | // {{{ getSvnpath |
1130 | | | |
1131 | | | function getSvnpath( $path ) { |
1132 | | | if ($this->repConfig->subpath === null) { |
1133 | | | return $this->repConfig->path.$path; |
1134 | | | } else { |
1135 | | | $path = preg_replace('|^/?'.$this->repConfig->subpath.'|', '', $path); |
1136 | | | return $this->repConfig->path.'/'.$this->repConfig->subpath.$path; |
1137 | | | } |
1138 | | | } |
1139 | | | |
1140 | | | // }}} |
1141 | | | |
1142 | 1 | simandl | } |
1143 | | | |
1144 | | | // {{{ initSvnVersion |
1145 | | | |
1146 | 3 | simandl | function initSvnVersion() { |
1147 | 1 | simandl | global $config; |
1148 | | | |
1149 | | | $ret = runCommand($config->svn_noparams." --version", false); |
1150 | | | |
1151 | | | if (preg_match("~([0-9]?)\.([0-9]?)\.([0-9]?)~",$ret[0],$matches)) { |
1152 | 3 | simandl | $config->setSubversionVersion($matches[0]); |
1153 | | | $config->setSubversionMajorVersion($matches[1]); |
1154 | | | $config->setSubversionMinorVersion($matches[2]); |
1155 | 1 | simandl | } |
1156 | | | } |
1157 | | | |
1158 | | | // }}} |