[ Index ]
 

Code source de Horde 3.1.3

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/Horde/VC/ -> cvs.php (source)

   1  <?php
   2  
   3  require_once dirname(__FILE__) . '/rcs.php';
   4  
   5  /**
   6   * VC_cvs implementation.
   7   *
   8   * Copyright 2000-2006 Anil Madhavapeddy, <anil@recoil.org>
   9   *
  10   * $Horde: framework/VC/VC/cvs.php,v 1.32.2.24 2006/05/12 04:47:08 chuck Exp $
  11   *
  12   * @author  Anil Madhavapeddy <anil@recoil.org>
  13   * @package VC
  14   */
  15  class VC_cvs extends VC_rcs {
  16  
  17      /**
  18       * Constructor.
  19       *
  20       * @param array $params  Any parameter the class expects.
  21       *                       <pre>
  22       *                       Current parameters:
  23       *                       'sourceroot': The source root for this
  24       *                                     repository
  25       *                       'paths': Hash with the locations of all
  26       *                                necessary binaries: 'rcsdiff', 'rlog',
  27       *                                'cvsps', 'cvsps_home' and the temp
  28       *                                path: 'temp'
  29       *                       </pre>
  30       */
  31      function VC_cvs($params)
  32      {
  33          $this->_sourceroot = $params['sourceroot'];
  34          $this->_paths = $params['paths'];
  35      }
  36  
  37      function isFile($where)
  38      {
  39          return @is_file($where . ',v');
  40      }
  41  
  42      function &queryDir($where)
  43      {
  44          $dir = new VC_Directory_cvs($this, $where);
  45          return $dir;
  46      }
  47  
  48      function getCheckout($file, $rev)
  49      {
  50          return VC_Checkout_cvs::get($this, $file->queryFullPath(), $rev);
  51      }
  52  
  53      function getDiff($file, $rev1, $rev2, $type = 'context', $num = 3, $ws = true)
  54      {
  55          return VC_Diff_cvs::get($this, $file, $rev1, $rev2, $type, $num, $ws);
  56      }
  57  
  58      function &getFileObject($filename, $cache = null, $quicklog = false)
  59      {
  60          if (substr($filename, 0, 1) != '/') {
  61              $filename = '/' . $filename;
  62          }
  63          $fo = &VC_File_cvs::getFileObject($this, $this->sourceroot() . $filename, $cache, $quicklog);
  64          return $fo;
  65      }
  66  
  67      function &getAnnotateObject($filename)
  68      {
  69          $blame = new VC_Annotate_cvs($this, $filename, Util::getTempFile('vc', true, $this->_paths['temp']));
  70          return $blame;
  71      }
  72  
  73      function &getPatchsetObject($filename, $cache = null)
  74      {
  75          $po = &VC_Patchset_cvs::getPatchsetObject($this, $this->sourceroot() . '/' . $filename, $cache);
  76          return $po;
  77      }
  78  
  79  }
  80  
  81  /**
  82   * VC_cvs annotate class.
  83   *
  84   * Anil Madhavapeddy, <anil@recoil.org>
  85   *
  86   * @author  Anil Madhavapeddy <anil@recoil.org>
  87   * @package VC
  88   */
  89  class VC_Annotate_cvs {
  90  
  91      var $file;
  92      var $rep;
  93      var $tmpfile;
  94  
  95      function VC_Annotate_cvs($rep, $file, $tmpfile)
  96      {
  97          $this->rep = $rep;
  98          $this->file = $file;
  99          $this->tmpfile = $tmpfile;
 100      }
 101  
 102      function doAnnotate($rev)
 103      {
 104          /* Make sure that the file values for this object is valid. */
 105          if (is_a($this->file, 'PEAR_Error')) {
 106              return false;
 107          }
 108  
 109          /* Make sure that the cvsrep parameter is valid. */
 110          if (is_a($this->rep, 'PEAR_Error')) {
 111              return false;
 112          }
 113  
 114          if (!VC_Revision::valid($rev)) {
 115              return false;
 116          }
 117  
 118          $where = $this->file->queryModulePath();
 119          $sourceroot = $this->rep->sourceroot();
 120  
 121          $pipe = popen($this->rep->getPath('cvs') . ' -n server > ' . $this->tmpfile, VC_WINDOWS ? 'wb' : 'w');
 122  
 123          $out = array();
 124          $out[] = "Root $sourceroot";
 125          $out[] = 'Valid-responses ok error Valid-requests Checked-in Updated Merged Removed M E';
 126          $out[] = 'UseUnchanged';
 127          $out[] = 'Argument -r';
 128          $out[] = "Argument $rev";
 129          $out[] = "Argument $where";
 130          $dirs = explode('/', dirname($where));
 131          while (count($dirs)) {
 132              $out[] = 'Directory ' . implode('/', $dirs);
 133              $out[] = "$sourceroot/" . implode('/', $dirs);
 134              array_pop($dirs);
 135          }
 136          $out[] = 'Directory .';
 137          $out[] = $sourceroot;
 138          $out[] = 'annotate';
 139  
 140          foreach ($out as $line) {
 141              fwrite($pipe, "$line\n");
 142          }
 143          pclose($pipe);
 144  
 145          if (!($fl = fopen($this->tmpfile, VC_WINDOWS ? 'rb' : 'r'))) {
 146              return false;
 147          }
 148  
 149          $lines = array();
 150          $line = fgets($fl, 4096);
 151  
 152          // Windows versions of cvs always return $where with forwards slashes.
 153          if (VC_WINDOWS) {
 154              $where = str_replace(DIRECTORY_SEPARATOR, '/', $where);
 155          }
 156  
 157          while ($line && !preg_match("|^E\s+Annotations for $where|", $line)) {
 158              $line = fgets($fl, 4096);
 159          }
 160  
 161          if (!$line) {
 162              return PEAR::raiseError('Unable to annotate; server said: ' . $line);
 163          }
 164  
 165          $lineno = 1;
 166          while ($line = fgets($fl, 4096)) {
 167              if (preg_match('/^M\s+([\d\.]+)\s+\((.+)\s+(\d+-\w+-\d+)\):.(.*)$/', $line, $regs)) {
 168                  $entry = array();
 169                  $entry['rev']    = $regs[1];
 170                  $entry['author'] = trim($regs[2]);
 171                  $entry['date']   = $regs[3];
 172                  $entry['line']   = $regs[4];
 173                  $entry['lineno'] = $lineno++;
 174                  $lines[] = $entry;
 175              }
 176          }
 177  
 178          fclose($fl);
 179          return $lines;
 180      }
 181  
 182  }
 183  
 184  /**
 185   * VC_cvs checkout class.
 186   *
 187   * See the README file that came with this library for more
 188   * information, and read the inline documentation.
 189   *
 190   * Anil Madhavapeddy, <anil@recoil.org>
 191   *
 192   * @author  Anil Madhavapeddy <anil@recoil.org>
 193   * @package VC
 194   */
 195  class VC_Checkout_cvs {
 196  
 197      /**
 198       * Static function which returns a file pointing to the head of the
 199       * requested revision of an RCS file.
 200       *
 201       * @param VC_cvs $rep       A repository object
 202       * @param string $fullname  Fully qualified pathname of the desired RCS
 203       *                          file to checkout
 204       * @param string $rev       RCS revision number to check out
 205       *
 206       * @return resource|object  Either a PEAR_Error object, or a stream
 207       *                          pointer to the head of the checkout
 208       */
 209      function get($rep, $fullname, $rev)
 210      {
 211          if (!VC_Revision::valid($rev)) {
 212              return PEAR::raiseError('Invalid revision number');
 213          }
 214  
 215          if (VC_WINDOWS) {
 216              $mode = 'rb';
 217              $q_name = '"' . escapeshellcmd(str_replace('\\', '/', $fullname)) . '"';
 218          } else {
 219              $mode = 'r';
 220              $q_name = escapeshellarg($fullname);
 221          }
 222  
 223          if (!($RCS = popen($rep->getPath('co') . " -p$rev $q_name 2>&1", $mode))) {
 224              return PEAR::raiseError('Couldn\'t perform checkout of the requested file');
 225          }
 226  
 227          /* First line from co should be of the form :
 228           * /path/to/filename,v  -->  standard out
 229           * and we check that this is the case and error otherwise
 230           */
 231  
 232          $co = fgets($RCS, 1024);
 233          if (!preg_match('/^([\S ]+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/', $co, $regs) || $regs[1].',v' != $fullname) {
 234              return PEAR::raiseError('Unexpected output from checkout: ' . $co);
 235          }
 236  
 237          /* Next line from co is of the form:
 238           * revision 1.2.3
 239           * TODO: compare this to $rev for consistency, atm we just
 240           *       discard the value to move input pointer along - avsm
 241           */
 242          $co = fgets($RCS, 1024);
 243  
 244          return $RCS;
 245      }
 246  
 247  }
 248  
 249  /**
 250   * VC_cvs diff class.
 251   *
 252   * Copyright Anil Madhavapeddy, <anil@recoil.org>
 253   *
 254   * @author  Anil Madhavapeddy <anil@recoil.org>
 255   * @package VC
 256   */
 257  class VC_Diff_cvs {
 258  
 259      /**
 260       * Obtain the differences between two revisions of a file.
 261       *
 262       * @param VC_cvs $rep        A repository object.
 263       * @param VC_File_cvs $file  The desired file.
 264       * @param string $rev1       Original revision number to compare from.
 265       * @param string $rev2       New revision number to compare against.
 266       * @param string $type       The type of diff (e.g. 'unified').
 267       * @param integer $num       Number of lines to be used in context and
 268       *                           unified diffs.
 269       * @param boolean $ws        Show whitespace in the diff?
 270       *
 271       * @return string|boolean  False on failure, or a string containing the
 272       *                         diff on success.
 273       */
 274      function get($rep, $file, $rev1, $rev2, $type = 'context', $num = 3,
 275                   $ws = true)
 276      {
 277          /* Make sure that the file parameter is valid. */
 278          if (is_a($file, 'PEAR_Error')) {
 279              return false;
 280          }
 281  
 282          /* Check that the revision numbers are valid. */
 283          $rev1 = VC_Revision::valid($rev1) ? $rev1 : '1.1';
 284          $rev2 = VC_Revision::valid($rev1) ? $rev2 : '1.1';
 285  
 286          $fullName = $file->queryFullPath();
 287          $diff = array();
 288          $options = '-kk ';
 289          if (!$ws) {
 290              $opts = ' -bB ';
 291              $options .= $opts;
 292          } else {
 293              $opts = '';
 294          }
 295          switch ($type) {
 296          case 'context':
 297              $options = $opts . '-p --context=' . (int)$num;
 298              break;
 299  
 300          case 'unified':
 301              $options = $opts . '-p --unified=' . (int)$num;
 302              break;
 303  
 304          case 'column':
 305              $options = $opts . '--side-by-side --width=120';
 306              break;
 307  
 308          case 'ed':
 309              $options = $opts . '-e';
 310              break;
 311          }
 312  
 313          // Windows versions of cvs always return $where with forwards slashes.
 314          if (VC_WINDOWS) {
 315              $fullName = str_replace(DIRECTORY_SEPARATOR, '/', $fullName);
 316          }
 317  
 318          // TODO: add options for $hr options - however these may not be
 319          // compatible with some diffs.
 320          $command = $rep->getPath('rcsdiff') . " $options -r$rev1 -r$rev2 \"" . escapeshellcmd($fullName) . '" 2>&1';
 321          if (VC_WINDOWS) {
 322              $command .= ' < "' . __FILE__ . '"';
 323          }
 324  
 325          exec($command, $diff, $retval);
 326          return ($retval > 0) ? $diff : array();
 327      }
 328  
 329  }
 330  
 331  /**
 332   * VC_cvs directory class.
 333   *
 334   * Copyright Anil Madhavapeddy, <anil@recoil.org>
 335   *
 336   * @author  Anil Madhavapeddy <anil@recoil.org>
 337   * @package VC
 338   */
 339  class VC_Directory_cvs {
 340  
 341      var $rep;
 342      var $dirName;
 343      var $files;
 344      var $atticFiles;
 345      var $mergedFiles;
 346      var $dirs;
 347      var $parent;
 348      var $moduleName;
 349      var $sortDir;
 350  
 351      /**
 352       * Creates a CVS Directory object to store information
 353       * about the files in a single directory in the repository.
 354       *
 355       * @param VC_cvs $rep           A repository object
 356       * @param string $dn            Path to the directory.
 357       * @param VC_Directory_cvs $pn  The parent VC_Directory to this one.
 358       */
 359      function VC_Directory_cvs($rep, $dn, $pn = '')
 360      {
 361          $this->rep = $rep;
 362          $this->parent = $pn;
 363          $this->moduleName = $dn;
 364          $this->dirName = $rep->sourceroot() . "/$dn";
 365          $this->files = array();
 366          $this->dirs = array();
 367      }
 368  
 369      /**
 370       * Return fully qualified pathname to this directory with no
 371       * trailing /.
 372       *
 373       * @return string  Pathname of this directory
 374       */
 375      function queryDir()
 376      {
 377          return $this->dirName;
 378      }
 379  
 380      function &queryDirList()
 381      {
 382          reset($this->dirs);
 383          return $this->dirs;
 384      }
 385  
 386      function &queryFileList($showattic = false)
 387      {
 388          if ($showattic && isset($this->mergedFiles)) {
 389              return $this->mergedFiles;
 390          } else {
 391              return $this->files;
 392          }
 393      }
 394  
 395      /**
 396       * Tell the object to open and browse its current directory, and
 397       * retrieve a list of all the objects in there.  It then populates
 398       * the file/directory stack and makes it available for retrieval.
 399       *
 400       * @return boolean|object  PEAR_Error object on an error, true on success.
 401       */
 402      function browseDir($cache = null, $quicklog = true, $showattic = false)
 403      {
 404          /* Make sure we are trying to list a directory */
 405          if (!@is_dir($this->dirName)) {
 406              return PEAR::raiseError('Unable to find directory ' . $this->dirName);
 407          }
 408  
 409          /* Open the directory for reading its contents */
 410          if (!($DIR = @opendir($this->dirName))) {
 411              $errmsg = (!empty($php_errormsg)) ? $php_errormsg : 'Permission denied';
 412              return PEAR::raiseError($errmsg);
 413          }
 414  
 415          /* Create two arrays - one of all the files, and the other of
 416           * all the directories. */
 417          while (($name = readdir($DIR)) !== false) {
 418              if ($name == '.' || $name == '..') {
 419                  continue;
 420              }
 421  
 422              $path = $this->dirName . '/' . $name;
 423              if (@is_dir($path)) {
 424                  /* Skip Attic directory. */
 425                  if ($name != 'Attic') {
 426                      $this->dirs[] = $name;
 427                  }
 428              } elseif (@is_file($path) && substr($name, -2) == ',v') {
 429                  /* Spawn a new file object to represent this file. */
 430                  $fl = $this->rep->getFileObject(substr($path, strlen($this->rep->sourceroot()), -2), $cache, $quicklog);
 431                  if (is_a($fl, 'PEAR_Error')) {
 432                      return $fl;
 433                  } else {
 434                      $this->files[] = $fl;
 435                  }
 436              }
 437          }
 438  
 439          /* Close the filehandle; we've now got a list of dirs and
 440           * files. */
 441          closedir($DIR);
 442  
 443          /* If we want to merge the attic, add it in here. */
 444          if ($showattic) {
 445              $atticDir = new VC_Directory_cvs($this->rep, $this->moduleName . '/Attic', $this);
 446              if (!is_a($atticDir->browseDir($cache, $quicklog), 'PEAR_Error')) {
 447                  $this->atticFiles = $atticDir->queryFileList();
 448                  $this->mergedFiles = array_merge($this->files, $this->atticFiles);
 449              }
 450          }
 451  
 452          return true;
 453      }
 454  
 455      /**
 456       * Sort the contents of the directory in a given fashion and
 457       * order.
 458       *
 459       * @param integer $how  Of the form VC_SORT_* where * can be:
 460       *                      NONE, NAME, AGE, REV for sorting by name, age or
 461       *                      revision.
 462       * @param integer $dir  Of the form VC_SORT_* where * can be:
 463       *                      ASCENDING, DESCENDING for the order of the sort.
 464       */
 465      function applySort($how = VC_SORT_NONE, $dir = VC_SORT_ASCENDING)
 466      {
 467          // Always sort directories by name.
 468          natcasesort($this->dirs);
 469  
 470          $this->doFileSort($this->files, $how, $dir);
 471          if (isset($this->atticFiles)) {
 472              $this->doFileSort($this->atticFiles, $how, $dir);
 473          }
 474          if (isset($this->mergedFiles)) {
 475              $this->doFileSort($this->mergedFiles, $how, $dir);
 476          }
 477      }
 478  
 479      function doFileSort(&$fileList, $how = VC_SORT_NONE, $dir = VC_SORT_ASCENDING)
 480      {
 481          $this->sortDir = $dir;
 482  
 483          switch ($how) {
 484          case VC_SORT_AGE:
 485              usort($fileList, array($this, 'fileAgeSort'));
 486              break;
 487  
 488          case VC_SORT_NAME:
 489              usort($fileList, array($this, 'fileNameSort'));
 490              break;
 491  
 492          case VC_SORT_AUTHOR:
 493              usort($fileList, array($this, 'fileAuthorSort'));
 494              break;
 495  
 496          case VC_SORT_REV:
 497              usort($fileList, array($this, 'fileRevSort'));
 498              break;
 499          }
 500  
 501          unset($this->sortDir);
 502      }
 503  
 504      /**
 505       * Sort function for file age.
 506       */
 507      function fileAgeSort($a, $b)
 508      {
 509          $aa = $a->queryLastLog();
 510          $bb = $b->queryLastLog();
 511          if ($aa->queryDate() == $bb->queryDate()) {
 512              return 0;
 513          } elseif ($this->sortDir == VC_SORT_ASCENDING) {
 514              return ($aa->queryDate() < $bb->queryDate()) ? 1 : -1;
 515          } else {
 516              return ($bb->queryDate() < $aa->queryDate()) ? 1 : -1;
 517          }
 518      }
 519  
 520      /**
 521       * Sort function by author name.
 522       */
 523      function fileAuthorSort($a, $b)
 524      {
 525          $aa = $a->queryLastLog();
 526          $bb = $b->queryLastLog();
 527          if ($aa->queryAuthor() == $bb->queryAuthor()) {
 528              return 0;
 529          } elseif ($this->sortDir == VC_SORT_ASCENDING) {
 530              return ($aa->queryAuthor() > $bb->queryAuthor()) ? 1 : -1;
 531          } else {
 532              return ($bb->queryAuthor() > $aa->queryAuthor()) ? 1 : -1;
 533          }
 534      }
 535  
 536      /**
 537       * Sort function for filename.
 538       */
 539      function fileNameSort($a, $b)
 540      {
 541          if ($this->sortDir == VC_SORT_ASCENDING) {
 542              return strcasecmp($a->name, $b->name);
 543          } else {
 544              return strcasecmp($b->name, $a->name);
 545          }
 546      }
 547  
 548      /**
 549       * Sort function for revision.
 550       */
 551      function fileRevSort($a, $b)
 552      {
 553          if ($this->sortDir == VC_SORT_ASCENDING) {
 554              return VC_Revision::cmp($a->queryHead(), $b->queryHead());
 555          } else {
 556              return VC_Revision::cmp($b->queryHead(), $a->queryHead());
 557          }
 558      }
 559  
 560  }
 561  
 562  /**
 563   * VC_cvs file class.
 564   *
 565   * Copyright Anil Madhavapeddy, <anil@recoil.org>
 566   *
 567   * @author  Anil Madhavapeddy <anil@recoil.org>
 568   * @package VC
 569   */
 570  class VC_File_cvs extends VC_File {
 571  
 572      /**
 573       * Create a repository file object, and give it information about
 574       * what its parent directory and repository objects are.
 575       *
 576       * @param string $fl  Full path to this file.
 577       */
 578      function VC_File_cvs($fl, $quicklog = false)
 579      {
 580          $fl .= ',v';
 581          $this->name = basename($fl);
 582          $this->dir = dirname($fl);
 583          $this->logs = array();
 584          $this->quicklog = $quicklog;
 585          $this->revs = array();
 586          $this->branches = array();
 587      }
 588  
 589      function &getFileObject($rep, $filename, $cache = null, $quicklog = false)
 590      {
 591          /* The version of the cached data. Increment this whenever the
 592           * internal storage format changes, such that we must
 593           * invalidate prior cached data. */
 594          $cacheVersion = 2;
 595          $cacheId = $rep->sourceroot() . '_n' . $filename . '_f' . (int)$quicklog . '_v' . $cacheVersion;
 596  
 597          $ctime = time() - @filemtime($filename . ',v');
 598          if ($cache &&
 599              $cache->exists($cacheId, $ctime)) {
 600              $fileOb = unserialize($cache->get($cacheId, $ctime));
 601              $fileOb->setRepository($rep);
 602          } else {
 603              $fileOb = new VC_File_cvs($filename, $quicklog);
 604              $fileOb->setRepository($rep);
 605              if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
 606                  return $result;
 607              }
 608              $fileOb->applySort(VC_SORT_AGE);
 609  
 610              if ($cache) {
 611                  $cache->set($cacheId, serialize($fileOb));
 612              }
 613          }
 614  
 615          return $fileOb;
 616      }
 617  
 618      /**
 619       * If this file is present in an Attic directory, this indicates
 620       * it.
 621       *
 622       * @return boolean  True if file is in the Attic, and false otherwise
 623       */
 624      function isAtticFile()
 625      {
 626          return substr($this->dir, -5) == 'Attic';
 627      }
 628  
 629      /**
 630       * Returns the name of the current file as in the repository
 631       *
 632       * @return string  Filename (without the path)
 633       */
 634      function queryRepositoryName()
 635      {
 636          return $this->name;
 637      }
 638  
 639      /**
 640       * Returns name of the current file without the repository
 641       * extensions (usually ,v)
 642       *
 643       * @return string  Filename without repository extension
 644       */
 645      function queryName()
 646      {
 647          return preg_replace('/,v$/', '', $this->name);
 648      }
 649  
 650      /**
 651       * Return the last revision of the current file on the HEAD branch
 652       *
 653       * @return string|object  Last revision of the current file or PEAR_Error
 654       *                        on failure.
 655       */
 656      function queryRevision()
 657      {
 658          if (!isset($this->revs[0])) {
 659              return PEAR::raiseError('No revisions');
 660          }
 661          return $this->revs[0];
 662      }
 663  
 664      function queryPreviousRevision($rev)
 665      {
 666          return VC_Revision::prev($rev);
 667      }
 668  
 669      /**
 670       * Return the HEAD (most recent) revision number for this file.
 671       *
 672       * @return string  HEAD revision number
 673       */
 674      function queryHead()
 675      {
 676          return $this->head;
 677      }
 678  
 679      /**
 680       * Return the last VC_log object in the file.
 681       *
 682       * @return VC_log of the last entry in the file
 683       */
 684      function queryLastLog()
 685      {
 686          if (!isset($this->revs[0]) || !isset($this->logs[$this->revs[0]])) {
 687              return PEAR::raiseError('No revisions');
 688          }
 689          return $this->logs[$this->revs[0]];
 690      }
 691  
 692      /**
 693       * Sort the list of VC_log objects that this file contains.
 694       *
 695       * @param integer $how  VC_SORT_REV (sort by revision),
 696       *                      VC_SORT_NAME (sort by author name),
 697       *                      VC_SORT_AGE (sort by commit date)
 698       */
 699      function applySort($how = VC_SORT_REV)
 700      {
 701          switch ($how) {
 702          case VC_SORT_NAME:
 703              $func = 'Name';
 704              break;
 705  
 706          case VC_SORT_AGE:
 707              $func = 'Age';
 708              break;
 709  
 710          case VC_SORT_REV:
 711          default:
 712              $func = 'Revision';
 713          }
 714  
 715          uasort($this->logs, array($this, "sortBy$func"));
 716          return true;
 717      }
 718  
 719      /**
 720       * The sortBy*() functions are internally used by applySort.
 721       */
 722      function sortByRevision($a, $b)
 723      {
 724          return VC_Revision::cmp($b->rev, $a->rev);
 725      }
 726  
 727      function sortByAge($a, $b)
 728      {
 729          return $b->date - $a->date;
 730      }
 731  
 732      function sortByName($a, $b)
 733      {
 734          return strcmp($a->author, $b->author);
 735      }
 736  
 737      /**
 738       * Populate the object with information about the revisions logs and dates
 739       * of the file.
 740       *
 741       * @return boolean|object  PEAR_Error object on error, or true on success
 742       */
 743      function getBrowseInfo()
 744      {
 745          /* Check that we are actually in the filesystem. */
 746          $file = $this->queryFullPath();
 747          if (!is_file($file)) {
 748              return PEAR::raiseError('File Not Found: ' . $file);
 749          }
 750  
 751          /* Call the RCS rlog command to retrieve the file
 752           * information. */
 753          $flag = $this->quicklog ? ' -r ' : ' ';
 754          $q_file = VC_WINDOWS ? '"' . escapeshellcmd($file) . '"' : escapeshellarg($file);
 755  
 756          $cmd = $this->rep->getPath('rlog') . $flag . $q_file;
 757          exec($cmd, $return_array, $retval);
 758  
 759          if ($retval) {
 760              return PEAR::raiseError('Failed to spawn rlog to retrieve file log information for ' . $file);
 761          }
 762  
 763          $accum = array();
 764          $symrev = array();
 765          $revsym = array();
 766          $state = 'init';
 767          foreach ($return_array as $line) {
 768              switch ($state) {
 769              case 'init':
 770                  if (!strncmp('head: ', $line, 6)) {
 771                      $this->head = substr($line, 6);
 772                  } elseif (!strncmp('branch:', $line, 7)) {
 773                      $state = 'rev';
 774                  }
 775                  break;
 776  
 777              case 'rev':
 778                  if (!strncmp('----------', $line, 10)) {
 779                      $state = 'info';
 780                      $this->symrev = $symrev;
 781                      $this->revsym = $revsym;
 782                  } elseif (preg_match("/^\s+([^:]+):\s+([\d\.]+)/", $line, $regs)) {
 783                      // Check to see if this is a branch
 784                      if (preg_match('/^(\d+(\.\d+)+)\.0\.(\d+)$/',$regs[2])) {
 785                          $branchRev = VC_Revision::toBranch($regs[2]);
 786                          if (!isset($this->branches[$branchRev])) {
 787                              $this->branches[$branchRev] = $regs[1];
 788                          }
 789                      } else {
 790                          $symrev[$regs[1]] = $regs[2];
 791                          if (empty($revsym[$regs[2]])) $revsym[$regs[2]]=array();
 792                          $revsym[$regs[2]][] = $regs[1];
 793                      }
 794                  }
 795                  break;
 796  
 797              case 'info':
 798                  if (strncmp('==============================', $line, 30) &&
 799                      strcmp('----------------------------', $line)) {
 800                      $accum[] = $line;
 801                  } elseif (count($accum)) {
 802                      // spawn a new VC_log object and add it to the logs hash
 803                      $log = new VC_Log_cvs($this);
 804                      $err = $log->processLog($accum);
 805                      // TODO: error checks - avsm
 806                      $this->logs[$log->queryRevision()] = $log;
 807                      $this->revs[] = $log->queryRevision();
 808                      $accum = array();
 809                  }
 810                  break;
 811              }
 812          }
 813  
 814          return true;
 815      }
 816  
 817      /**
 818       * Return the fully qualified filename of this object.
 819       *
 820       * @return Fully qualified filename of this object
 821       */
 822      function queryFullPath()
 823      {
 824          return $this->dir . '/' . $this->name;
 825      }
 826  
 827      /**
 828       * Return the name of this file relative to its sourceroot.
 829       *
 830       * @return string  Pathname relative to the sourceroot.
 831       */
 832      function queryModulePath()
 833      {
 834          return preg_replace('|^'. $this->rep->sourceroot() . '/?(.*),v$|', '\1', $this->queryFullPath());
 835      }
 836  
 837  }
 838  
 839  /**
 840   * VC_cvs log class.
 841   *
 842   * @author  Anil Madhavapeddy <anil@recoil.org>
 843   * @package VC
 844   */
 845  class VC_Log_cvs {
 846  
 847      var $rep;
 848      var $file;
 849      var $tags;
 850      var $rev;
 851      var $date;
 852      var $log;
 853      var $author;
 854      var $state;
 855      var $lines;
 856      var $branches;
 857  
 858      /**
 859       *
 860       */
 861      function VC_Log_cvs($fl)
 862      {
 863          $this->file = $fl;
 864          $this->branches = array();
 865      }
 866  
 867      function processLog($raw)
 868      {
 869          /* Initialise a simple state machine to parse the output of rlog */
 870          $state = 'init';
 871          while (!empty($raw) && $state != 'done') {
 872              switch ($state) {
 873              /* Found filename, now looking for the revision number */
 874              case 'init':
 875                  $line = array_shift($raw);
 876                  if (preg_match("/revision (.+)$/", $line, $parts)) {
 877                      $this->rev = $parts[1];
 878                      $state = 'date';
 879                  }
 880                  break;
 881  
 882              /* Found revision and filename, now looking for date */
 883              case 'date':
 884                  $line = array_shift($raw);
 885                  if (preg_match("|^date:\s+(\d+)[-/](\d+)[-/](\d+)\s+(\d+):(\d+):(\d+).*?;\s+author:\s+(.+);\s+state:\s+(\S+);(\s+lines:\s+([0-9\s+-]+))?|", $line, $parts)) {
 886                      $this->date = gmmktime($parts[4], $parts[5], $parts[6], $parts[2], $parts[3], $parts[1]);
 887                      $this->author = $parts[7];
 888                      $this->state = $parts[8];
 889                      $this->lines = isset($parts[10]) ? $parts[10] : '';
 890                      $state = 'branches';
 891                  }
 892                  break;
 893  
 894              /* Look for a branch point here - format is 'branches:  x.y.z;  a.b.c;' */
 895              case 'branches':
 896                  /* If we find a branch tag, process and pop it,
 897                     otherwise leave input stream untouched */
 898                  if (!empty($raw) && preg_match("/^branches:\s+(.*)/", $raw[0], $br)) {
 899                      /* Get the list of branches from the string, and
 900                       * push valid revisions into the branches array */
 901                      $brs = preg_split('/;\s*/', $br[1]);
 902                      foreach ($brs as $brpoint) {
 903                          if (VC_Revision::valid($brpoint)) {
 904                              $this->branches[] = $brpoint;
 905                          }
 906                      }
 907                      array_shift($raw);
 908  
 909                  }
 910  
 911                  $state = 'done';
 912                  break;
 913              }
 914          }
 915  
 916          /* Assume the rest of the lines are the log message */
 917          $this->log = implode("\n", $raw);
 918          $this->tags = @$this->file->revsym[$this->rev];
 919          if (empty($this->tags)) {
 920              $this->tags = array();
 921          }
 922      }
 923  
 924      function queryDate()
 925      {
 926          return $this->date;
 927      }
 928  
 929      function queryRevision()
 930      {
 931          return $this->rev;
 932      }
 933  
 934      function queryAuthor()
 935      {
 936          return $this->author;
 937      }
 938  
 939      function queryLog()
 940      {
 941          return $this->log;
 942      }
 943  
 944      function queryChangedLines()
 945      {
 946          return isset($this->lines) ? ($this->lines) : '';
 947      }
 948  
 949      /**
 950       * Given a branch revision number, this function remaps it
 951       * accordingly, and performs a lookup on the file object to
 952       * return the symbolic name(s) of that branch in the tree.
 953       *
 954       * @return array  Hash of symbolic names => branch numbers
 955       */
 956      function querySymbolicBranches()
 957      {
 958          $symBranches = array();
 959          foreach ($this->branches as $branch) {
 960              $parts = explode('.', $branch);
 961              $last = array_pop($parts);
 962              $parts[] = '0';
 963              $parts[] = $last;
 964              $rev = implode('.', $parts);
 965              if (isset($this->file->branches[$branch])) {
 966                  $symBranches[$this->file->branches[$branch]] = $branch;
 967              }
 968          }
 969          return $symBranches;
 970      }
 971  
 972  }
 973  
 974  /**
 975   * VC_cvs Patchset class.
 976   *
 977   * Copyright Anil Madhavapeddy, <anil@recoil.org>
 978   *
 979   * @author  Anil Madhavapeddy <anil@recoil.org>
 980   * @package VC
 981   */
 982  class VC_Patchset_cvs extends VC_Patchset {
 983  
 984      var $_dir;
 985      var $_name;
 986  
 987      /**
 988       * Create a patchset object.
 989       *
 990       * @param string $file  The filename to get patchsets for.
 991       */
 992      function VC_Patchset_cvs($file)
 993      {
 994          $this->_name = basename($file);
 995          $this->_dir = dirname($file);
 996      }
 997  
 998      function &getPatchsetObject($rep, $filename, $cache = null)
 999      {
1000          /* The version of the cached data. Increment this whenever the
1001           * internal storage format changes, such that we must
1002           * invalidate prior cached data. */
1003          $cacheVersion = 1;
1004          $cacheId = $rep->sourceroot() . '_n' . $filename . '_f_v' . $cacheVersion;
1005  
1006          $ctime = time() - @filemtime($filename . ',v');
1007          if ($cache &&
1008              $cache->exists($cacheId, $ctime)) {
1009              $psOb = unserialize($cache->get($cacheId, $ctime));
1010              $psOb->setRepository($rep);
1011          } else {
1012              $psOb = new VC_Patchset_cvs($filename);
1013              $psOb->setRepository($rep);
1014              if (is_a(($result = $psOb->getPatchsets()), 'PEAR_Error')) {
1015                  return $result;
1016              }
1017  
1018              if ($cache) {
1019                  $cache->set($cacheId, serialize($psOb));
1020              }
1021          }
1022  
1023          return $psOb;
1024      }
1025  
1026      /**
1027       * Populate the object with information about the patchsets that
1028       * this file is involved in.
1029       *
1030       * @return boolean|object  PEAR_Error object on error, or true on success.
1031       */
1032      function getPatchsets()
1033      {
1034          /* Check that we are actually in the filesystem. */
1035          if (!is_file($this->getFullPath() . ',v')) {
1036              return PEAR::raiseError('File Not Found');
1037          }
1038  
1039          /* Call cvsps to retrieve all patchsets for this file. */
1040          $q_root = $this->_rep->sourceroot();
1041          $q_root = VC_WINDOWS ? '"' . escapeshellcmd($q_root) . '"' : escapeshellarg($q_root);
1042  
1043          $cvsps_home = $this->_rep->getPath('cvsps_home');
1044          $HOME = !empty($cvsps_home) ?
1045              'HOME=' . $cvsps_home . ' ' :
1046              '';
1047  
1048          $cmd = $HOME . $this->_rep->getPath('cvsps') . ' -u --cvs-direct --root ' . $q_root . ' -f ' . escapeshellarg($this->_name) . ' ' . escapeshellarg($this->_dir);
1049          exec($cmd, $return_array, $retval);
1050          if ($retval) {
1051              return PEAR::raiseError('Failed to spawn cvsps to retrieve patchset information');
1052          }
1053  
1054          $this->_patchsets = array();
1055          $state = 'begin';
1056          foreach ($return_array as $line) {
1057              $line = trim($line);
1058              if ($line == '---------------------') {
1059                  $state = 'begin';
1060                  continue;
1061              }
1062  
1063              switch ($state) {
1064              case 'begin':
1065                  $id = str_replace('PatchSet ', '', $line);
1066                  $this->_patchsets[$id] = array();
1067                  $state = 'info';
1068                  break;
1069  
1070              case 'info':
1071                  $info = explode(':', $line, 2);
1072                  switch ($info[0]) {
1073                  case 'Date':
1074                      if (preg_match('|(\d{4})/(\d{2})/(\d{2}) (\d{2}):(\d{2}):(\d{2})|', $info[1], $date)) {
1075                          $this->_patchsets[$id]['date'] = gmmktime($date[4], $date[5], $date[6], $date[2], $date[3], $date[1]);
1076                      }
1077                      break;
1078  
1079                  case 'Author':
1080                      $this->_patchsets[$id]['author'] = trim($info[1]);
1081                      break;
1082  
1083                  case 'Branch':
1084                      if (trim($info[1]) != 'HEAD') {
1085                          $this->_patchsets[$id]['branch'] = trim($info[1]);
1086                      }
1087                      break;
1088  
1089                  case 'Tag':
1090                      if (trim($info[1]) != '(none)') {
1091                          $this->_patchsets[$id]['tag'] = trim($info[1]);
1092                      }
1093                      break;
1094  
1095                  case 'Log':
1096                      $state = 'log';
1097                      $this->_patchsets[$id]['log'] = '';
1098                      break;
1099                  }
1100                  break;
1101  
1102              case 'log':
1103                  if ($line == 'Members:') {
1104                      $state = 'members';
1105                      $this->_patchsets[$id]['log'] = trim($this->_patchsets[$id]['log']);
1106                      $this->_patchsets[$id]['members'] = array();
1107                  } else {
1108                      $this->_patchsets[$id]['log'] .= $line . "\n";
1109                  }
1110                  break;
1111  
1112              case 'members':
1113                  if (!empty($line)) {
1114                      $parts = explode(':', $line);
1115                      $revs = explode('->', $parts[1]);
1116                      $this->_patchsets[$id]['members'][] = array('file' => $parts[0],
1117                                                                  'from' => $revs[0],
1118                                                                  'to' => $revs[1]);
1119                  }
1120                  break;
1121              }
1122          }
1123  
1124          return true;
1125      }
1126  
1127      /**
1128       * Return the fully qualified filename of this object.
1129       *
1130       * @return string  Fully qualified filename of this object
1131       */
1132      function getFullPath()
1133      {
1134          return $this->_dir . '/' . $this->_name;
1135      }
1136  
1137  }


Généré le : Sun Feb 25 18:01:28 2007 par Balluche grâce à PHPXref 0.7