jablonka.czprosek.czf

websvn

Subversion Repositories:
[/] [include/] [svnlook.php] - Blame information for rev 1

 

Line No. Rev Author Line
11simandl<?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 
28require_once("include/utils.php");
29 
30// {{{ Classes for retaining log information ---
31 
32$debugxml = false;
33 
34class SVNMod {
35 var $action = '';
36 var $copyfrom = '';
37 var $copyrev = '';
38 var $path = '';
39}
40 
41class 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 
51class SVNList {
52 var $entries; // Array of entries
53 var $curEntry; // Current entry
54 
55 var $path = ''; // The path of the list
56}
57 
58class 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 
71function SVNLogEntry_compare($a, $b) {
72 return strnatcasecmp($a->path, $b->path);
73}
74 
75class 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 
104function 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 
164function 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 $curList->curEntry->file .= '/';
172 }
173 $curList->entries[] = $curList->curEntry;
174 $curList->curEntry = null;
175 break;
176 }
177 
178 $curTag = "";
179}
180 
181// }}}
182 
183// {{{ listCharacterData
184 
185function 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 sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s);
207 
208 $mo = substr("00".$mo, -2);
209 $d = substr("00".$d, -2);
210 $h = substr("00".$h, -2);
211 $m = substr("00".$m, -2);
212 $s = substr("00".$s, -2);
213 
214 $curList->curEntry->date = "$y-$mo-$d $h:$m:$s GMT";
215 
216 $committime = strtotime($curList->curEntry->date);
217 $curList->curEntry->committime = $committime;
218 $curtime = time();
219 
220 // Get the number of seconds since the commit
221 $agesecs = $curtime - $committime;
222 if ($agesecs < 0) $agesecs = 0;
223 
224 $curList->curEntry->age = datetimeFormatDuration($agesecs, true, true);
225 
226 break;
227 }
228}
229 
230// }}}
231 
232$curLog = 0;
233 
234// {{{ logStartElement
235 
236function logStartElement($parser, $name, $attrs) {
237 global $curLog, $curTag, $debugxml;
238 
239 switch ($name) {
240 case "LOGENTRY":
241 if ($debugxml) print "Creating new log entry\n";
242 $curLog->curEntry = new SVNLogEntry;
243 $curLog->curEntry->mods = array();
244 
245 $curLog->curEntry->path = $curLog->path;
246 
247 if (sizeof($attrs)) {
248 while (list($k, $v) = each($attrs)) {
249 switch ($k) {
250 case "REVISION":
251 if ($debugxml) print "Revision $v\n";
252 $curLog->curEntry->rev = $v;
253 break;
254 }
255 }
256 }
257 break;
258 
259 case "PATH":
260 if ($debugxml) print "Creating new path\n";
261 $curLog->curEntry->curMod = new SVNMod;
262 
263 if (sizeof($attrs)) {
264 while (list($k, $v) = each($attrs)) {
265 switch ($k) {
266 case "ACTION":
267 if ($debugxml) print "Action $v\n";
268 $curLog->curEntry->curMod->action = $v;
269 break;
270 
271 case "COPYFROM-PATH":
272 if ($debugxml) print "Copy from: $v\n";
273 $curLog->curEntry->curMod->copyfrom = $v;
274 break;
275 
276 case "COPYFROM-REV":
277 $curLog->curEntry->curMod->copyrev = $v;
278 break;
279 }
280 }
281 }
282 
283 $curTag = $name;
284 break;
285 
286 default:
287 $curTag = $name;
288 break;
289 }
290}
291 
292// }}}
293 
294// {{{ logEndElement
295 
296function logEndElement($parser, $name) {
297 global $curLog, $debugxml, $curTag;
298 
299 switch ($name) {
300 case "LOGENTRY":
301 if ($debugxml) print "Ending new log entry\n";
302 $curLog->entries[] = $curLog->curEntry;
303 break;
304 
305 case "PATH":
306 if ($debugxml) print "Ending path\n";
307 $curLog->curEntry->mods[] = $curLog->curEntry->curMod;
308 break;
309 
310 case "MSG":
311 $curLog->curEntry->msg = trim($curLog->curEntry->msg);
312 if ($debugxml) print "Completed msg = '".$curLog->curEntry->msg."'\n";
313 break;
314 }
315 
316 $curTag = "";
317}
318 
319// }}}
320 
321// {{{ logCharacterData
322 
323function logCharacterData($parser, $data) {
324 global $curLog, $curTag, $lang, $debugxml;
325 
326 switch ($curTag) {
327 case "AUTHOR":
328 if ($debugxml) print "Author: $data\n";
329 if (empty($data)) return;
330 $curLog->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8");
331 break;
332 
333 case "DATE":
334 if ($debugxml) print "Date: $data\n";
335 $data = trim($data);
336 if (empty($data)) return;
337 
338 sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s);
339 
340 $mo = substr("00".$mo, -2);
341 $d = substr("00".$d, -2);
342 $h = substr("00".$h, -2);
343 $m = substr("00".$m, -2);
344 $s = substr("00".$s, -2);
345 
346 $curLog->curEntry->date = "$y-$mo-$d $h:$m:$s GMT";
347 
348 $committime = strtotime($curLog->curEntry->date);
349 $curLog->curEntry->committime = $committime;
350 $curtime = time();
351 
352 // Get the number of seconds since the commit
353 $agesecs = $curtime - $committime;
354 if ($agesecs < 0) $agesecs = 0;
355 
356 $curLog->curEntry->age = datetimeFormatDuration($agesecs, true, true);
357 
358 break;
359 
360 case "MSG":
361 if ($debugxml) print "Msg: '$data'\n";
362 $curLog->curEntry->msg .= htmlentities($data, ENT_COMPAT, "UTF-8");
363 break;
364 
365 case "PATH":
366 if ($debugxml) print "Path name: '$data'\n";
367 $data = trim($data);
368 if (empty($data)) return;
369 
370 $curLog->curEntry->curMod->path .= $data;
371 
372 // The XML returned when a file is renamed/branched in inconsistant. In the case
373 // of a branch, the path information doesn't include the leafname. In the case of
374 // a rename, it does. Ludicrous.
375 
376 if (!empty($curLog->path)) {
377 $pos = strrpos($curLog->path, "/");
378 $curpath = substr($curLog->path, 0, $pos);
379 $leafname = substr($curLog->path, $pos + 1);
380 } else {
381 $curpath = "";
382 $leafname = "";
383 }
384 
385 if ($curLog->curEntry->curMod->action == "A") {
386 if ($debugxml) print "Examining added path '".$curLog->curEntry->curMod->copyfrom."' - Current path = '$curpath', leafname = '$leafname'\n";
387 if ($data == $curLog->path) { // For directories and renames
388 if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."'\n";
389 $curLog->path = $curLog->curEntry->curMod->copyfrom;
390 } else if ($data == $curpath || $data == $curpath."/") { // Logs of files that have moved due to branching
391 if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."/$leafname'\n";
392 $curLog->path = $curLog->curEntry->curMod->copyfrom."/$leafname";
393 }
394 }
395 break;
396 }
397}
398 
399// }}}
400 
401// }}}
402 
403// {{{ internal functions (_topLevel and _listSort)
404 
405// Function returns true if the give entry in a directory tree is at the top level
406 
407function _topLevel($entry) {
408 // To be at top level, there must be one space before the entry
409 return (strlen($entry) > 1 && $entry{0} == " " && $entry{1} != " ");
410}
411 
412// Function to sort two given directory entries. Directories go at the top
413 
414function _listSort($e1, $e2) {
415 $isDir1 = $e1->file{strlen($e1->file) - 1} == "/";
416 $isDir2 = $e2->file{strlen($e2->file) - 1} == "/";
417 
418 if ($isDir1 && !$isDir2) return -1;
419 if ($isDir2 && !$isDir1) return 1;
420 
421 return strnatcasecmp($e1->file, $e2->file);
422}
423 
424// }}}
425 
426// {{{ encodePath
427 
428// Function to encode a URL without encoding the /'s
429 
430function encodePath($uri) {
431 global $config;
432 
433 $uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri);
434 
435 $parts = explode('/', $uri);
436 for ($i = 0; $i < count($parts); $i++) {
437 if ( function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding")) {
438 $parts[$i] = mb_convert_encoding($parts[$i], "UTF-8", mb_detect_encoding($parts[$i]));
439 }
440 
441 $parts[$i] = rawurlencode($parts[$i]);
442 }
443 
444 $uri = implode('/', $parts);
445 
446 // Quick hack. Subversion seems to have a bug surrounding the use of %3A instead of :
447 
448 $uri = str_replace("%3A" ,":", $uri);
449 
450 // Correct for Window share names
451 if ( $config->serverIsWindows==true ) {
452 if (substr($uri, 0,2) == "//") {
453 $uri = "\\".substr($uri, 2, strlen($uri));
454 }
455 
456 if (substr($uri, 0,10)=="file://///" ) {
457 $uri="file:///\\".substr($uri, 10, strlen($uri));
458 }
459 }
460 
461 return $uri;
462}
463 
464// }}}
465 
466// The SVNRepository class
467 
468class SVNRepository {
469 var $repConfig;
470 
471 function SVNRepository($repConfig) {
472 $this->repConfig = $repConfig;
473 }
474 
475 // {{{ highlightLine
476 //
477 // Distill line-spanning syntax highlighting so that each line can stand alone
478 // (when invoking on the first line, $attributes should be an empty array)
479 // Invoked to make sure all open syntax highlighting tags (<font>, <i>, <b>, etc.)
480 // are closed at the end of each line and re-opened on the next line
481 
482 function highlightLine($line, &$attributes) {
483 $hline = "";
484 
485 // Apply any highlighting in effect from the previous line
486 foreach ($attributes as $attr) {
487 $hline.=$attr['text'];
488 }
489 
490 // append the new line
491 $hline.=$line;
492 
493 // update attributes
494 for ($line = strstr($line, "<"); $line; $line = strstr(substr($line,1), "<")) {
495 if (substr($line,1,1) == "/") { // if this closes a tag, remove most recent corresponding opener
496 $tagNamLen = strcspn($line, "> \t", 2);
497 $tagNam = substr($line,2,$tagNamLen);
498 foreach (array_reverse(array_keys($attributes)) as $k) {
499 if ($attributes[$k]['tag'] == $tagNam) {
500 unset($attributes[$k]);
501 break;
502 }
503 }
504 } else { // if this opens a tag, add it to the list
505 $tagNamLen = strcspn($line, "> \t", 1);
506 $tagNam = substr($line,1,$tagNamLen);
507 $tagLen = strcspn($line, ">") + 1;
508 $attributes[] = array('tag' => $tagNam, 'text' => substr($line,0,$tagLen));
509 }
510 }
511 
512 // close any still-open tags
513 foreach (array_reverse($attributes) as $attr) {
514 $hline.="</".$attr['tag'].">";
515 }
516 
517 // XXX: this just simply replaces [ and ] with their entities to prevent
518 // it from being parsed by the template parser; maybe something more
519 // elegant is in order?
520 $hline = str_replace('[', '&#91;', str_replace(']', '&#93;', $hline) );
521 return $hline;
522 }
523 
524 // }}}
525 
526 // {{{ getFileContents
527 //
528 // Dump the content of a file to the given filename
529 
530 function getFileContents($path, $filename, $rev = 0, $pipe = "", $perLineHighlighting = false) {
531 global $config, $extEnscript;
532 
533 // If there's no filename, we'll just deliver the contents as it is to the user
534 if ($filename == "") {
535 $path = encodepath($this->repConfig->path.$path);
536 passthru(quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' '.$pipe));
537 return;
538 }
539 
540 // Get the file contents info
541 
542 $ext = strrchr($path, ".");
543 $l = @$extEnscript[$ext];
544 
545 if ($l == "php") {
546 // Output the file to the filename
547 $path = encodepath($this->repConfig->path.$path);
548 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.quote($filename));
549 @exec($cmd);
550 
551 // Get the file as a string (memory hogging, but we have no other options)
552 $content = highlight_file($filename, true);
553 
554 // Destroy the previous version, and replace it with the highlighted version
555 $f = fopen($filename, "w");
556 if ($f) {
557 // The highlight file function doesn't deal with line endings very nicely at all. We'll have to do it
558 // by hand.
559 
560 // Remove the first line generated by highlight()
561 $pos = strpos($content, "\n");
562 $content = substr($content, $pos+1);
563 
564 $content = explode("<br />", $content);
565 
566 if ($perLineHighlighting) {
567 // If we need each line independently highlighted (e.g. for diff or blame)
568 // hen we'll need to filter the output of the highlighter
569 // to make sure tags like <font>, <i> or <b> don't span lines
570 
571 // $attributes is used to remember what highlighting attributes
572 // are in effect from one line to the next
573 $attributes = array(); // start with no attributes in effect
574 
575 foreach ($content as $line) {
576 fputs($f, $this->highlightLine(rtrim($line),$attributes)."\n");
577 }
578 } else {
579 foreach ($content as $line) {
580 fputs($f, rtrim($line)."\n");
581 }
582 }
583 
584 fclose($f);
585 }
586 
587 } else {
588 if ($l !== null && $config->useGeshi) {
589 $this->applyGeshi($path, $filename, $rev, $l);
590 
591 } else if ($config->useEnscript) {
592 // Get the files, feed it through enscript, then remove the enscript headers using sed
593 //
594 // Note that the sec command returns only the part of the file between <PRE> and </PRE>.
595 // It's complicated because it's designed not to return those lines themselves.
596 
597 $path = encodepath($this->repConfig->path.$path);
598 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' | '.
599 $config->enscript." --language=html ".
600 ($l ? "--color --pretty-print=$l" : "")." -o - | ".
601 $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $filename");
602 @exec($cmd);
603 
604 } else {
605 $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->repConfig->path.$path));
606 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.quote($filename));
607 @exec($cmd);
608 }
609 }
610 }
611 
612 // }}}
613 
614 // {{{ applyGeshi
615 //
616 // perform syntax highlighting using geshi
617 
618 function applyGeshi($path, $filename, $rev = 0, $l, $return = false) {
619 global $config;
620 
621 // Output the file to the filename
622 $path = encodepath($this->repConfig->path.$path);
623 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.quote($filename));
624 @exec($cmd);
625 
626 $source = file_get_contents($filename);
627 require_once 'lib/geshi.php';
628 $geshi = new GeSHi($source, $l);
629 if ($return) {
630 return $geshi->parse_code();
631 } else {
632 $code = $geshi->parse_code();
633 $code = preg_replace("/^<pre.*?>/", '', $code);
634 $code = preg_replace("/<\/pre>$/", '', $code);
635 $f = @fopen($filename, 'w');
636 fwrite($f, $code);
637 fclose($f);
638 }
639 }
640 
641 // }}}
642 
643 // {{{ listFileContents
644 //
645 // Print the contents of a file without filling up Apache's memory
646 
647 function listFileContents($path, $rev = 0) {
648 global $config, $extEnscript;
649 
650 $pre = false;
651 
652 // Get the file contents info
653 
654 $ext = strrchr($path, ".");
655 $l = @$extEnscript[$ext];
656 
657 // Deal with php highlighting internally
658 if ($l == "php") {
659 $tmp = tempnam("temp", "wsvn");
660 
661 // Output the file to a temporary file
662 $path = encodepath($this->repConfig->path.$path);
663 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.$tmp);
664 @exec($cmd);
665 $tmpStr = file_get_contents($tmp);
666 $tmpStr = str_replace(array("\r\n"), array("\n"), $tmpStr);
667 highlight_string($tmpStr);
668 @unlink($tmp);
669 } else if ($l !== null && $config->useGeshi) {
670 $tmp = tempnam("temp", "wsvn");
671 print $this->applyGeshi($path, $tmp, $rev, $l, true);
672 unlink($tmp);
673 } else {
674 if ($config->useEnscript) {
675 $path = encodepath($this->repConfig->path.$path);
676 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' | '.
677 $config->enscript." --language=html ".
678 ($l ? "--color --pretty-print=$l" : "")." -o - | ".
679 $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote
680 );
681 } else {
682 $path = encodepath($this->repConfig->path.$path);
683 $cmd = quoteCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev);
684 $pre = true;
685 }
686 
687 if ($result = popen($cmd, "r")) {
688 if ($pre) echo "<PRE>";
689 
690 while (!feof($result)) {
691 $line = fgets($result, 1024);
692 if ($pre) $line = replaceEntities($line, $this->repConfig);
693 
694 print hardspace($line);
695 }
696 
697 if ($pre) echo "</PRE>";
698 
699 pclose($result);
700 }
701 }
702 }
703 
704 // }}}
705 
706 // {{{ getBlameDetails
707 //
708 // Dump the blame content of a file to the given filename
709 
710 function getBlameDetails($path, $filename, $rev = 0) {
711 global $config;
712 
713 $path = encodepath($this->repConfig->path.$path);
714 $cmd = quoteCommand($config->svn." blame ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.quote($filename));
715 
716 @exec($cmd);
717 }
718 
719 // }}}
720 
721 // {{{ getProperty
722 
723 function getProperty($path, $property, $rev = 0) {
724 global $config;
725 
726 $path = encodepath($this->repConfig->path.$path);
727 
728 if ($rev > 0) {
729 $rev = ' -r '.$rev;
730 } else {
731 $rev = '';
732 }
733 
734 $ret = runCommand($config->svn." propget $property ".$this->repConfig->svnParams().quote($path).$rev, true);
735 
736 // Remove the surplus newline
737 if (count($ret)) {
738 unset($ret[count($ret) - 1]);
739 }
740 
741 return implode("\n", $ret);
742 }
743 
744 // }}}
745 
746 // {{{ exportDirectory
747 //
748 // Exports the directory to the given location
749 
750 function exportDirectory($path, $filename, $rev = 0) {
751 global $config;
752 
753 $path = encodepath($this->repConfig->path.$path);
754 $cmd = quoteCommand($config->svn." export ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' '.quote($filename));
755 
756 @exec($cmd);
757 }
758 
759 // }}}
760 
761 // {{{ getList
762 
763 function getList($path, $rev = 0) {
764 global $config, $curList, $vars, $lang;
765 
766 $xml_parser = xml_parser_create("UTF-8");
767 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
768 xml_set_element_handler($xml_parser, "listStartElement", "listEndElement");
769 xml_set_character_data_handler($xml_parser, "listCharacterData");
770 
771 // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove
772 // the trailing slash from the path for comparison purposes
773 
774 if ($path{strlen($path) - 1} == "/" && $path != "/") {
775 $path = substr($path, 0, -1);
776 }
777 
778 $curList = new SVNList;
779 $curList->entries = array();
780 $curList->path = $path;
781 
782 // Get the list info
783 $path = encodepath($this->repConfig->path.$path);
784 
785 if ($rev == 0) {
786 $headlog = $this->getLog("/", "", "", true, 1);
787 if (isset($headlog->entries[0])) $rev = $headlog->entries[0]->rev;
788 }
789 $revStr = "-r $rev";
790 
791 $cmd = quoteCommand($config->svn." list --xml $revStr ".$this->repConfig->svnParams().quote($path));
792 
793 $descriptorspec = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
794 
795 $resource = proc_open($cmd, $descriptorspec, $pipes);
796 $error = "";
797 
798 if (!is_resource($resource)) {
799 echo "<p>".$lang['BADCMD'].": <code>".$cmd."</code></p>";
800 exit;
801 }
802 
803 $handle = $pipes[1];
804 $firstline = true;
805 while (!feof($handle)) {
806 $line = fgets($handle);
807 if (!xml_parse($xml_parser, $line, feof($handle))) {
808 if (xml_get_error_code($xml_parser) != 5) {
809 // errors can contain sensitive info! don't echo this ~J
810 error_log(sprintf("XML error: %s (%d) at line %d column %d byte %d\ncmd: %s",
811 xml_error_string(xml_get_error_code($xml_parser)),
812 xml_get_error_code($xml_parser),
813 xml_get_current_line_number($xml_parser),
814 xml_get_current_column_number($xml_parser),
815 xml_get_current_byte_index($xml_parser),
816 $cmd));
817 exit;
818 } else {
819 $vars["error"] = $lang["UNKNOWNREVISION"];
820 return 0;
821 }
822 }
823 }
824 
825 while (!feof($pipes[2])) {
826 $error .= fgets($pipes[2]);
827 }
828 
829 $error = toOutputEncoding(trim($error));
830 
831 fclose($pipes[0]);
832 fclose($pipes[1]);
833 fclose($pipes[2]);
834 
835 proc_close($resource);
836 
837 if (!empty($error)) {
838 echo "<p>".$lang['BADCMD'].": <code>".$cmd."</code></p><p>".nl2br($error)."</p>";
839 exit;
840 }
841 
842 xml_parser_free($xml_parser);
843 
844 // Sort the entries into alphabetical order with the directories at the top of the list
845 usort($curList->entries, "_listSort");
846 
847 return $curList;
848 }
849 
850 // }}}
851 
852 // {{{ getLog
853 
854 function getLog($path, $brev = "", $erev = 1, $quiet = false, $limit = 2) {
855 global $config, $curLog, $vars, $lang;
856 
857 $xml_parser = xml_parser_create("UTF-8");
858 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
859 xml_set_element_handler($xml_parser, "logStartElement", "logEndElement");
860 xml_set_character_data_handler($xml_parser, "logCharacterData");
861 
862 // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove
863 // the trailing slash from the path for comparison purposes
864 
865 if ($path{strlen($path) - 1} == "/" && $path != "/") {
866 $path = substr($path, 0, -1);
867 }
868 
869 $curLog = new SVNLog;
870 $curLog->entries = array();
871 $curLog->path = $path;
872 
873 $revStr = "";
874 
875 if ($brev && $erev) {
876 $revStr = "-r$brev:$erev";
877 } else if ($brev) {
878 $revStr = "-r$brev:1";
879 }
880 
881 if (($config->subversionMajorVersion > 1 || $config->subversionMinorVersion >=2) && $limit != 0) {
882 $revStr .= " --limit $limit";
883 }
884 
885 // Get the log info
886 $path = encodepath($this->repConfig->path.$path);
887 $info = "--verbose";
888 if ($quiet) $info = "--quiet";
889 
890 $cmd = quoteCommand($config->svn." log --xml $info $revStr ".$this->repConfig->svnParams().quote($path));
891 
892 $descriptorspec = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
893 
894 $resource = proc_open($cmd, $descriptorspec, $pipes);
895 $error = "";
896 
897 if (!is_resource($resource)) {
898 echo "<p>".$lang['BADCMD'].": <code>".$cmd."</code></p>";
899 exit;
900 }
901 
902 $handle = $pipes[1];
903 $firstline = true;
904 while (!feof($handle)) {
905 $line = fgets($handle);
906 if (!xml_parse($xml_parser, $line, feof($handle))) {
907 if (xml_get_error_code($xml_parser) != 5) {
908 // errors can contain sensitive info! don't echo this ~J
909 error_log(sprintf("XML error: %s (%d) at line %d column %d byte %d\ncmd: %s",
910 xml_error_string(xml_get_error_code($xml_parser)),
911 xml_get_error_code($xml_parser),
912 xml_get_current_line_number($xml_parser),
913 xml_get_current_column_number($xml_parser),
914 xml_get_current_byte_index($xml_parser),
915 $cmd));
916 exit;
917 } else {
918 $vars["error"] = $lang["UNKNOWNREVISION"];
919 return 0;
920 }
921 }
922 }
923 
924 while (!feof($pipes[2])) {
925 $error .= fgets($pipes[2]);
926 }
927 
928 $error = toOutputEncoding(trim($error));
929 
930 fclose($pipes[0]);
931 fclose($pipes[1]);
932 fclose($pipes[2]);
933 
934 proc_close($resource);
935 
936 if (!empty($error)) {
937 echo "<p>".$lang['BADCMD'].": <code>".$cmd."</code></p><p>".nl2br($error)."</p>";
938 exit;
939 }
940 
941 xml_parser_free($xml_parser);
942 
943 foreach ($curLog->entries as $entryKey => $entry) {
944 $fullModAccess = true;
945 $anyModAccess = (count($entry->mods) == 0);
946 foreach ($entry->mods as $modKey => $mod) {
947 $access = $this->repConfig->hasReadAccess($mod->path);
948 if ($access) {
949 $anyModAccess = true;
950 } else {
951 // hide modified entry when access is prohibited
952 unset($curLog->entries[$entryKey]->mods[$modKey]);
953 $fullModAccess = false;
954 }
955 }
956 if (!$fullModAccess) {
957 // hide commit message when access to any of the entries is prohibited
958 $curLog->entries[$entryKey]->msg = '';
959 }
960 if (!$anyModAccess) {
961 // hide author and date when access to all of the entries is prohibited
962 $curLog->entries[$entryKey]->author = '';
963 $curLog->entries[$entryKey]->date = '';
964 $curLog->entries[$entryKey]->committime = '';
965 $curLog->entries[$entryKey]->age = '';
966 }
967 }
968 
969 return $curLog;
970 }
971 
972 // }}}
973 
974}
975 
976// {{{ initSvnVersion
977 
978function initSvnVersion(&$major, &$minor) {
979 global $config;
980 
981 $ret = runCommand($config->svn_noparams." --version", false);
982 
983 if (preg_match("~([0-9]?)\.([0-9]?)\.([0-9]?)~",$ret[0],$matches)) {
984 $major = $matches[1];
985 $minor = $matches[2];
986 }
987 
988 $config->setSubversionMajorVersion($major);
989 $config->setSubversionMinorVersion($minor);
990}
991 
992// }}}

Powered by WebSVN 2.2.1