steffen: server/kolab-horde-framework/kolab-horde-framework/VC/VC cvs.php, NONE, 1.1 rcs.php, NONE, 1.1 svn.php, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Fri Oct 14 16:33:17 CEST 2005


Author: steffen

Update of /kolabrepository/server/kolab-horde-framework/kolab-horde-framework/VC/VC
In directory doto:/tmp/cvs-serv28903/kolab-horde-framework/kolab-horde-framework/VC/VC

Added Files:
	cvs.php rcs.php svn.php 
Log Message:
Separated Horde Framework from kolab-resource-handlers

--- NEW FILE: cvs.php ---
<?php

require_once dirname(__FILE__) . '/rcs.php';

/**
 * VC_cvs implementation.
 *
 * Copyright 2000-2004 Anil Madhavapeddy, <anil at recoil.org>
 *
 * $Horde: framework/VC/VC/cvs.php,v 1.22 2004/05/28 18:41:21 chuck Exp $
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @package VC
 */
class VC_cvs extends VC_rcs {

    /**
     * Constructor.
[...1094 lines suppressed...]
                                                                'to' => $revs[1]);
                }
                break;
            }
        }

        return true;
    }

    /**
     * Return the fully qualified filename of this object.
     *
     * @return string  Fully qualified filename of this object
     */
    function getFullPath()
    {
        return $this->_dir . '/' . $this->_name;
    }

}

--- NEW FILE: rcs.php ---
<?php
/**
 * VC_rcs implementation.
 *
 * Copyright 2004 Jeff Schwentner <jeffrey.schwentner at lmco.com>
 *
 * $Horde: framework/VC/VC/rcs.php,v 1.2 2004/04/17 15:14:48 jan Exp $
 *
 * @author  Jeff Schwentner <jeffrey.schwentner at lmco.com>
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @package VC
 */
class VC_rcs extends VC {

    /**
     * Checks an RCS file in with a specified change log.
     *
     * @param string $filepath    Location of file to check in.
     * @param string $message     Log of changes since last version.
     * @param string $user        The user name to use for the check in.
     * @param boolean $newBinary  Does the change involve binary data?
     *
     * @return string|object  The new revision number on success, or a
     *                        PEAR_Error object on failure.
     */
    function ci($filepath, $message, $user = null, $newBinary = false)
    {
        if ($user) {
            putenv('LOGNAME=' . $user);
        } else {
            putenv('LOGNAME=guest');
        }

        $Q = 'OS_WINDOWS' ? '"' : "'" ;

        $ci_cmd = $this->getPath('ci') . ' ' . $Q . $filepath . $Q.' 2>&1';
        $rcs_cmd = $this->getPath('rcs') . ' -i -kb ' . $Q . $filepath . $Q.' 2>&1';
        $output = '';

        $message_lines = explode("\n", $message);

        $pipe_def = array(0 => array("pipe", 'r'),
                          1 => array("pipe", 'w'));

        if ($newBinary) {
            $process = proc_open($rcs_cmd, $pipe_def, $pipes);
        } else {
            $process = proc_open($ci_cmd, $pipe_def, $pipes);
        }

        if (is_resource($process)) {
            foreach ($message_lines as $line) {
                if ($line == '.\n') {
                    $line = '. \n';
                }
                fwrite($pipes[0], $line);
            }

            fwrite($pipes[0], "\n.\n");
            fclose($pipes[0]);

            while (!feof($pipes[1])) {
                $output .= fread($pipes[1], 8192);
            }
            fclose($pipes[1]);
            proc_close($process);
        } else {
            return PEAR::raiseError('Failed to open pipe in ci()');
        }

        if ($newBinary) {
            exec($ci_cmd . ' 2>&1', $return_array, $retval);

            if ($retval) {
                return PEAR::raiseError("Unable to spawn ci on $filepath from ci()");
            } else {
                foreach ($return_array as $line) {
                    $output .= $line;
                }
            }
        }

        $rev_start = strpos($output, 'new revision: ');

        // If no new revision, see if this is an initial checkin.
        if ($rev_start === false) {
            $rev_start = strpos($output, 'initial revision: ');
            $rev_end = strpos($output, ' ', $rev_start);
        } else {
            $rev_end = strpos($output, ';', $rev_start);
        }

        if ($rev_start !== false && $rev_end !== false) {
            $rev_start += 14;
            return substr($output, $rev_start, $rev_end - $rev_start);
        } else {
            unlock($filepath);
            $temp_pos = strpos($output, 'file is unchanged');
            if ($temp_pos !== false) {
                return PEAR::raiseError('Check-in Failure: ' . basename($filepath) . ' has not been modified');
            } else {
                return PEAR::raiseError("Failed to checkin $filepath, $ci_cmd, $output");
            }
        }
    }

    /**
     * Checks the locks on a CVS/RCS file.
     *
     * @param string $filepath    Location of file.
     * @param string &$locked_by  Returns the username holding the lock.
     *
     * @return bool|object  True on success, or a PEAR_Error on failure.
     */
    function isLocked($filepath, &$locked_by)
    {
        $rlog_cmd  = $this->getPath('rlog');
        $rlog_flag = ' -L ';

        $Q = OS_WINDOWS ? '"' : "'";

        $cmd = $rlog_cmd . $rlog_flag . $Q . $filepath . $Q;

        exec($cmd.' 2>&1', $return_array, $retval);

        if ($retval) {
            return PEAR::raiseError("Unable to spawn rlog on $filepath from isLocked()");
        } else {
            $output = '';

            foreach ($return_array as $line) {
                $output .= $line;
            }

            $start_name = strpos($output, 'locked by: ');
            $end_name = strpos($output, ';', $start_name);

            if ($start_name !== false && $end_name !== false) {
                $start_name += 11;
                $locked_by = substr($output, $start_name, $end_name - $start_name);
                return true;
            }  elseif (strlen($output) == 0) {
                return false;
            } else {
                return PEAR::raiseError('Failure running rlog in isLocked()');
            }
        }
    }

    /**
     * Locks a CVS/RCS file.
     *
     * @param string $filepath  Location of file.
     * @param string $user      User name to lock the file with
     *
     * @return bool|object  True on success, or a PEAR_Error on failure.
     */
    function lock($filepath, $user = null)
    {
        // Get username for RCS tag.
        if ($user) {
            putenv('LOGNAME=' . $user);
        } else {
            putenv('LOGNAME=guest');
        }

        $rcs_cmd = $this->getPath('rcs');
        $rcs_flag = ' -l ';

        $Q = OS_WINDOWS ? '"' : "'" ;
        $cmd = $rcs_cmd . $rcs_flag . $Q . $filepath . $Q;
        exec($cmd.' 2>&1', $return_array, $retval);

        if ($retval) {
            return PEAR::raiseError('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
        } else {
            $output = '';
            foreach ($return_array as $line) {
                $output .= $line;
            }

            $locked_pos = strpos($output, 'locked');
            if ($locked_pos !== false) {
                return true;
            } else {
                return PEAR::raiseError('Failed to lock "' . $filepath . '" (Ran "' . $cmd . '", got return code ' . $retval . ', output: ' . $output . ')');
            }
        }
    }

    /**
     * Unlocks a CVS/RCS file.
     *
     * @param string $filepath  Location of file.
     * @param string $user      User name to unlock the file with
     *
     * @return bool|object  True on success, or a PEAR_Error on failure.
     */
    function unlock($filepath, $user = null)
    {
        // Get username for RCS tag.
        if ($user) {
            putenv('LOGNAME=' . $user);
        } else {
            putenv('LOGNAME=guest');
        }

        $rcs_cmd = $this->getPath('rcs');
        $rcs_flag = ' -u ';

        $Q = OS_WINDOWS ? '"' : "'" ;
        $cmd = $rcs_cmd . $rcs_flag . $Q . $filepath . $Q;
        exec($cmd . ' 2>&1', $return_array, $retval);

        if ($retval) {
            return PEAR::raiseError('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
        } else {
            $output = '';

            foreach ($return_array as $line) {
                $output .= $line;
            }

            $unlocked_pos = strpos($output, 'unlocked');

            if ($unlocked_pos !== false) {
                return true;
            } else {
                // Already unlocked.
                return true;
            }
        }
    }

}

--- NEW FILE: svn.php ---
<?php
/**
 * VC_svn implementation.
 *
 * Copyright 2000-2004 Anil Madhavapeddy, <anil at recoil.org>
 *
 * $Horde: framework/VC/VC/svn.php,v 1.16 2004/05/28 18:51:29 chuck Exp $
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package VC
 */
class VC_svn extends VC {

    /**
     * Constructor.
     *
     * @param array $params  Any parameter the class expects.
     *                       Current parameters:
     *                       'sourceroot': The source root for this
     *                                     repository
     *                       'paths': Hash with the locations of all
     *                                necessary binaries: 'svn', 'diff'
     */
    function VC_svn($params)
    {
        $this->_sourceroot = $params['sourceroot'];
        $this->_paths = $params['paths'];
    }

    function isFile($where)
    {
        return true;
    }

    function &queryDir($where)
    {
        return new VC_Directory_svn($this, $where);
    }

    function getCheckout($file, $rev)
    {
        return VC_Checkout_svn::get($this, $file->queryFullPath(), $rev);
    }

    function &getDiff(&$file, $rev1, $rev2, $type = 'context', $num = 3, $ws = true)
    {
        return VC_Diff_svn::get($this, $file, $rev1, $rev2, $type, $num, $ws);
    }

    function &getFileObject($filename, $cache = null, $quicklog = false)
    {
        return VC_File_svn::getFileObject($this, $filename, $cache, $quicklog);
    }

    function &getAnnotateObject($filename)
    {
        return new VC_Annotate_svn($this, $filename);
    }

    function &getPatchsetObject($filename, $cache = null)
    {
        return VC_Patchset_svn::getPatchsetObject($this, $filename, $cache);
    }

}

/**
 * VC_svn annotate class.
 *
 * Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_Annotate_svn {

    var $file;
    var $SVN;
    var $tmpfile;

    function VC_Annotate_svn(&$rep, $file)
    {
        $this->SVN = &$rep;
        $this->file = &$file;
    }

    function doAnnotate($rev)
    {
        /* Make sure that the file values for this object is valid */
        if (is_a($this->file, 'PEAR_Error')) {
            return false;
        }

        if (!VC_Revision::valid($rev)) {
            return false;
        }

        $pipe = popen($this->SVN->getPath('svn') . ' annotate -r 1:' . $rev . ' ' . $this->file->queryFullPath() . ' 2>&1', 'r');

        $lines = array();
        $lineno = 1;
        while (!feof($pipe)) {
            $line = fgets($pipe, 4096);
            if (preg_match('/^\s+(\d+)\s+(\w+)\s(.*)$/', $line, $regs)) {
                $entry = array();
                $entry['rev']    = $regs[1];
                $entry['author'] = $regs[2];
                $entry['date']   = _("Not Implemented");
                $entry['line']   = $regs[3];
                $entry['lineno'] = $lineno++;
                $lines[] = $entry;
            }
        }

        pclose($pipe);
        return $lines;
    }

}

/**
 * VC_svn checkout class.
 *
 * Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_Checkout_svn {

    /**
      * Static function which returns a file pointing to the head of the requested
      * revision of an RCS file.
      * @param fullname Fully qualified pathname of the desired RCS file to checkout
      * @param rev RCS revision number to check out
      * @return Either a PEAR_Error object, or a stream pointer to the head of the checkout
      */
    function get(&$rep, $fullname, $rev)
    {
        if (!VC_Revision::valid($rev)) {
            return PEAR::raiseError(_("Invalid revision number"));
        }

        if (!($RCS = popen($rep->getPath('svn') . ' cat -r ' . $rev . ' ' . $fullname . ' 2>&1', 'r'))) {
            return PEAR::raiseError(_("Couldn't perform checkout of the requested file"));
        }

        return $RCS;
    }

}

/**
 * VC_svn diff class.
 *
 * Copyright Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_Diff_svn {

    /**
     * Obtain the differences between two revisions within a file.
     *
     * @param file VC_File_svn object for the desired file
     * @param rev1 Original revision number to compare from
     * @param rev2 New revision number to compare against
     * @param type Constant which indicates the type of diff (e.g. unified)
     * @param num  Number of lines to be used in context and unified diff
     * @param ws   Show whitespace in the diff?
     *
     * @return false on failure, or a string containing the diff on success
     */
    function get(&$rep, &$file, $rev1, $rev2, $type, $num, $ws = 'context', $num = 3, $ws = true)
    {
        /* Make sure that the file parameter is valid */
        if (is_a($file, 'PEAR_Error')) {
            return false;
        }

        /* Check that the revision numbers are valid */
        $rev1 = VC_Revision::valid($rev1) ? $rev1 : '0';
        $rev2 = VC_Revision::valid($rev1) ? $rev2 : '0';

        $fullName = $file->queryFullPath();
        $diff = array();
        $options = '-kk ';
        if (!$ws) {
            $opts = ' -bB ';
            $options .= $opts;
        } else {
            $opts = '';
        }
        switch ($type) {
        case 'context':
            $options = $opts . '-p --context=' . $num;
            break;

        case 'unified':
            $options = $opts . '-p --unified=' . $num;
            break;

        case 'column':
            $options = $opts . '--side-by-side --width=120';
            break;

        case 'ed':
            $options = $opts . '-e';
            break;
        }

        // TODO: add options for $hr options - however these may not
        // be compatible with some diffs - avsm
        $command = $rep->getPath('svn') . " diff --diff-cmd " . $rep->getPath('diff') . " -r $rev1:$rev2 -x '$options' " . $file->queryFullPath() . ' 2>&1';

        exec($command, $diff, $retval);
        return $diff;
    }

}

/**
 * VC_svn directory class.
 *
 * Copyright Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_Directory_svn {

    var $rep;
    var $dirName;
    var $files;
    var $atticFiles;
    var $mergedFiles;
    var $dirs;
    var $parent;
    var $moduleName;

    /**
     * Create a SVN Directory object to store information about the
     * files in a single directory in the repository
     *
     * @param object VC_Repository_svn $rp  The VC_Repository object this directory is part of.
     * @param string                   $dn  Path to the directory.
     * @param object VC_Directory_svn  $pn  (optional) The parent VC_Directory to this one.
     */
    function VC_Directory_svn(&$rep, $dn, $pn = '')
    {
        $this->rep = &$rep;
        $this->parent = &$pn;
        $this->moduleName = $dn;
        $this->dirName = "/$dn";
        $this->files = array();
        $this->dirs = array();
    }

    /**
     * Return fully qualified pathname to this directory with no
     * trailing /.
     *
     * @return Pathname of this directory
     */
    function queryDir()
    {
        return $this->dirName;
    }

    function &queryDirList()
    {
        reset($this->dirs);
        return $this->dirs;
    }

    function &queryFileList($showattic = false)
    {
        if ($showattic && isset($this->mergedFiles)) {
            return $this->mergedFiles;
        } else {
            return $this->files;
        }
    }

    /**
     * Tell the object to open and browse its current directory, and
     * retrieve a list of all the objects in there.  It then populates
     * the file/directory stack and makes it available for retrieval.
     *
     * @return PEAR_Error object on an error, 1 on success.
     */
    function browseDir($cache = null, $quicklog = true, $showattic = false)
    {
        $cmd = $this->rep->getPath('svn') . ' ls ' . $this->rep->sourceroot() . $this->queryDir() . ' 2>&1';
        $dir = popen($cmd, 'r');

        /* Create two arrays - one of all the files, and the other of
         * all the dirs. */
        while (!feof($dir)) {
            $line = fgets($dir, 1024);
            $name = chop($line);
            if (strlen($name) != 0) {
                if (substr($name, -1) == '/') {
                    $this->dirs[] = substr($name, 0, -1);
                } else {
                    $this->files[] = &$this->rep->getFileObject($this->queryDir() . "/$name", $cache, $quicklog);
                }
            }
        }

        pclose($dir);

        return 1;
    }

    /**
     * Sort the contents of the directory in a given fashion and
     * order.
     *
     * @param $how Of the form VC_SORT_* where * can be:
     *             NONE, NAME, AGE, REV for sorting by name, age or revision.
     * @param $dir Of the form VC_SORT_* where * can be:
     *             ASCENDING, DESCENDING for the order of the sort.
     */
    function applySort($how = VC_SORT_NONE, $dir = VC_SORT_ASCENDING)
    {
        /* TODO: this code looks very inefficient! optimise... -
         * avsm. */

        // Assume by name for the moment.
        natcasesort($this->dirs);
        $this->doFileSort($this->files, $how, $dir);
        if (isset($this->atticFiles)) {
            $this->doFileSort($this->atticFiles, $how, $dir);
        }
        if (isset($this->mergedFiles)) {
            $this->doFileSort($this->mergedFiles, $how, $dir);
        }
        if ($dir == VC_SORT_DESCENDING) {
            $this->dirs = array_reverse($this->dirs);
            $this->files = array_reverse($this->files);
            if (isset($this->mergedFiles)) {
                $this->mergedFiles = array_reverse($this->mergedFiles);
            }
        }
    }

    function doFileSort(&$fileList, $how = VC_SORT_NONE, $dir = VC_SORT_ASCENDING)
    {
        switch ($how) {
        case VC_SORT_AGE:
            usort($fileList, array($this, 'fileAgeSort'));
            break;

        case VC_SORT_NAME:
            usort($fileList, array($this, 'fileNameSort'));
            break;

        case VC_SORT_AUTHOR:
            usort($fileList, array($this, 'fileAuthorSort'));
            break;

        case VC_SORT_REV:
            usort($fileList, array($this, 'fileRevSort'));
            break;

        case VC_SORT_NONE:
        default:
            break;
        }
    }

    /**
     * Sort function for ascending age.
     */
    function fileAgeSort($a, $b)
    {
        $aa = $a->queryLastLog();
        $bb = $b->queryLastLog();
        if ($aa->queryDate() == $bb->queryDate()) {
            return 0;
        } else {
            return ($aa->queryDate() < $bb->queryDate()) ? 1 : -1;
        }
    }

    /**
     * Sort function by author name.
     */
    function fileAuthorSort($a, $b)
    {
        $aa = $a->queryLastLog();
        $bb = $b->queryLastLog();
        if ($aa->queryAuthor() == $bb->queryAuthor()) {
            return 0;
        } else {
            return ($aa->queryAuthor() > $bb->queryAuthor()) ? 1 : -1;
        }
    }

    /**
     * Sort function for ascending filename.
     */
    function fileNameSort($a, $b)
    {
        return strcasecmp($a->name, $b->name);
    }

    /**
     * Sort function for ascending revision.
     */
    function fileRevSort($a, $b)
    {
        return VC_Revision::cmp($a->queryHead(), $b->queryHead());
    }

}

/**
 * VC_svn file class.
 *
 * Copyright Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_File_svn extends VC_File {

    /**
     * Create a repository file object, and give it information about
     * what its parent directory and repository objects are.
     *
     * @param string $fl  Full path to this file.
     */
    function VC_File_svn(&$rep, $fl, $quicklog = false)
    {
        $this->rep = &$rep;
        $this->name = basename($fl);
        $this->dir = dirname($fl);
        $this->logs = array();
        $this->quicklog = $quicklog;
        $this->revs = array();
        $this->revsym = array();
        $this->symrev = array();
        $this->branches = array();
    }

    function &getFileObject(&$rep, $filename, $cache = null, $quicklog = false)
    {
        /**
         * The version of the cached data. Increment this whenever the
         * internal storage format changes, such that we must
         * invalidate prior cached data.
         *
         * @var integer $_cacheVersion
         */
        $_cacheVersion = 2;

        if ($cache) {
            $cacheId = $filename . '_f' . (int)$quicklog . '_v' . $_cacheVersion;
            // The file is cached for one hour no matter what, because
            // there is no way to determine with Subversion the time
            // the file last changed.
            $fileOb = unserialize($cache->getData($cacheId, "serialize(VC_File_svn::_getFileObject('$filename', '$quicklog'))", time() - 360));
        } else {
            $fileOb = &VC_File_svn::_getFileObject($filename, $quicklog);
        }

        $fileOb->setRepository($rep);

        if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
            return $result;
        }

        return $fileOb;
    }

    function &_getFileObject($filename, $quicklog = false)
    {
        $fileOb = &new VC_File_svn($rep, $filename, $quicklog);
        $fileOb->applySort(VC_SORT_AGE);
        return $fileOb;
    }

    /**
     * If this file is present in an Attic directory, this indicates
     * it.
     *
     * @return true if file is in the Attic, and false otherwise
     */
    function isAtticFile()
    {
        // Not implemented yet
        return false;
    }

    /**
     * Returns the name of the current file as in the repository
     *
     * @return Filename (without the path)
     */
    function queryRepositoryName()
    {
        return $this->name;
    }

    /**
     * Returns name of the current file without the repository
     * extensions (usually ,v)
     *
     * @return Filename without repository extension
     */
    function queryName()
    {
       return preg_replace('/,v$/', '', $this->name);
    }

    /**
     * Return the last revision of the current file on the HEAD branch
     *
     * @return Last revision of the current file
     */
    function queryRevision()
    {
        if (!isset($this->revs[0])) {
            return PEAR::raiseError(_("No revisions"));
        }
        return $this->revs[0];
    }

    function queryPreviousRevision($rev)
    {
        $last = false;
        foreach ($this->revs as $entry) {
            if ($last)
                return $entry;
            if ($entry == $rev)
                $last = true;
        }

        return false;
    }

    /**
     * Return the HEAD (most recent) revision number for this file.
     *
     * @return HEAD revision number
     */
    function queryHead()
    {
        return $this->queryRevision();
    }

    /**
     * Return the last VC_Log object in the file.
     *
     * @return VC_Log of the last entry in the file
     */
    function queryLastLog()
    {
        if (!isset($this->revs[0]) || !isset($this->logs[$this->revs[0]])) {
            return PEAR::raiseError(_("No revisions"));
        }
        return $this->logs[$this->revs[0]];
    }

    /**
     * Sort the list of VC_Log objects that this file contains.
     *
     * @param how VC_SORT_REV (sort by revision),
     *            VC_SORT_NAME (sort by author name),
     *            VC_SORT_AGE (sort by commit date)
     */
    function applySort($how = VC_SORT_REV)
    {
        switch ($how) {
        case VC_SORT_REV:
            $func = 'Revision';
            break;
        case VC_SORT_NAME:
            $func = 'Name';
            break;
        case VC_SORT_AGE:
            $func = 'Age';
            break;
        default:
            $func = 'Revision';
        }
        uasort($this->logs, array($this, "sortBy$func"));
        return true;
    }

    /**
     * The sortBy*() functions are internally used by applySort.
     */
    function sortByRevision($a, $b)
    {
        return VC_Revision::cmp($b->rev, $a->rev);
    }

    function sortByAge($a, $b)
    {
        return $b->date - $a->date;
    }

    function sortByName($a, $b)
    {
        return strcmp($a->author, $b->author);
    }

    /**
     * Populate the object with information about the revisions logs
     * and dates of the file.
     *
     * @return PEAR_Error object on error, or true on success
     */
    function getBrowseInfo()
    {
        /* XXX: $flag = ($this->quicklog ? '-r HEAD ' : ''; */
        $flag = '';
        $cmd = $this->rep->getPath('svn') . ' log -v ' . $flag . $this->queryFullPath() . ' 2>&1';
        $pipe = popen($cmd, 'r');
        fgets($pipe);
        while (!feof($pipe)) {
            $log = &new VC_Log_svn($this->rep, $this);
            $err = $log->processLog($pipe);
            if ($err) {
                $rev = $log->queryRevision();
                $this->logs[$rev] = $log;
                $this->revs[] = $rev;
            }

            if ($this->quicklog) {
                break;
            }
        }

        pclose($pipe);
        return true;
    }

    /**
     * Return the fully qualified filename of this object.
     *
     * @return Fully qualified filename of this object
     */
    function queryFullPath()
    {
        return $this->rep->sourceroot() . '/' . $this->queryModulePath();
    }

    /**
     * Return the name of this file relative to its sourceroot.
     *
     * @return string  Pathname relative to the sourceroot.
     */
    function queryModulePath()
    {
        return $this->dir . '/' . $this->name;
    }

}

/**
 * VC_svn log class.
 *
 * Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_Log_svn {

    var $rep, $file, $files, $tags, $rev, $date, $log, $author, $state, $lines, $branches;

    /**
     *
     */
    function VC_Log_svn($rep, &$fl)
    {
        $this->rep = $rep;
        $this->file = &$fl;
        $this->branches = array();
    }

    function processLog($pipe)
    {
        $line = fgets($pipe);

        if (feof($pipe)) {
            return false;
        }

        preg_match('/^r([0-9]*) \| ([^ ]*) \| (.*) \(.*\) \| ([0-9]*) lines?$/', $line, $matches);
        $this->rev = $matches[1];
        $this->author = $matches[2];
        $this->date = strtotime($matches[3]);
        $size = $matches[4];

        fgets($pipe);

        $this->files = array();
        while (($line = trim(fgets($pipe))) != '') {
            $this->files[] = $line;
        }

        for ($i = 0; $i != $size; ++$i) {
            $this->log = $this->log . chop(fgets($pipe)) . "\n";
        }

        $this->log = chop($this->log);
        fgets($pipe);

        return true;
    }

    function queryDate()
    {
        return $this->date;
    }

    function queryRevision()
    {
        return $this->rev;
    }

    function queryAuthor()
    {
        return $this->author;
    }

    function queryLog()
    {
        return $this->log;
    }

    function queryChangedLines()
    {
        return isset($this->lines) ? ($this->lines) : '';
    }

    /**
     * Given a branch revision number, this function remaps it
     * accordingly, and performs a lookup on the file object to
     * return the symbolic name(s) of that branch in the tree.
     *
     * @return hash of symbolic names => branch numbers
     */
    function querySymbolicBranches()
    {
        $symBranches = array();
        foreach ($this->branches as $branch) {
            $parts = explode('.', $branch);
            $last = array_pop($parts);
            $parts[] = '0';
            $parts[] = $last;
            $rev = implode('.', $parts);
            if (isset($this->file->branches[$branch])) {
                $symBranches[$this->file->branches[$branch]] = $branch;
            }
        }
        return $symBranches;
    }

}

/**
 * VC_svn Patchset class.
 *
 * Copyright Anil Madhavapeddy, <anil at recoil.org>
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @package VC
 */
class VC_Patchset_svn {

    var $_rep;
    var $_file;
    var $_patchsets = array();

    /**
     * Create a patchset object.
     *
     * @param string $file  The filename to get patchsets for.
     */
    function VC_Patchset_svn($file)
    {
        $this->_file = $file;
    }

    function &getPatchsetObject(&$rep, $filename, $cache = null)
    {
        /**
         * The version of the cached data. Increment this whenever the
         * internal storage format changes, such that we must
         * invalidate prior cached data.
         *
         * @var integer $_cacheVersion
         */
        $_cacheVersion = 1;

        if ($cache) {
            $cacheId = $filename . '_f_v' . $_cacheVersion;
            // The file is cached for one hour no matter what, because there
            // is no way to determine with svn the time the file last changed.
            $psOb = unserialize($cache->getData($cacheId, "serialize(VC_Patchset_svn::_getPatchsetObject('$filename'))", time() - 360));
        } else {
            $psOb = &VC_Patchset_svn::_getPatchsetObject($filename);
        }

        $psOb->_rep = &$rep;

        if (is_a(($result = $psOb->getPatchsets($rep)), 'PEAR_Error')) {
            return $result;
        }

        return $psOb;
    }

    function &_getPatchsetObject($filename)
    {
        return new VC_Patchset_svn($filename);
    }

    /**
     * Populate the object with information about the patchsets that
     * this file is involved in.
     *
     * @param string $repository  The full repository location.
     *
     * @return mixed  PEAR_Error object on error, or true on success.
     */
    function getPatchsets($repository)
    {
        $fileOb = &new VC_File_svn($this->_rep, $this->_file);
        if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
            return $result;
        }

        $this->_patchsets = array();
        foreach ($fileOb->logs as $rev => $log) {
            $this->_patchsets[$rev] = array();
            $this->_patchsets[$rev]['date'] = $log->queryDate();
            $this->_patchsets[$rev]['author'] = $log->queryAuthor();
            $this->_patchsets[$rev]['branch'] = '';
            $this->_patchsets[$rev]['tag'] = '';
            $this->_patchsets[$rev]['log'] = $log->queryLog();
            $this->_patchsets[$rev]['members'] = array();
            foreach ($log->files as $file) {
                $action = substr($file, 0, 1);
                $file = preg_replace('/.*?\s(.*?)(\s|$).*/', '\\1', $file);
                $to = $rev;
                if ($action == 'A') {
                    $from = 'INITIAL';
                } elseif ($action == 'D') {
                    $from = $to;
                    $to = '(DEAD)';
                } else {
                    // This technically isn't the previous revision,
                    // but it works for diffing purposes.
                    $from = $to - 1;
                }

                $this->_patchsets[$rev]['members'][] = array('file' => $file,
                                                             'from' => $from,
                                                             'to' => $to);
            }
        }

        return true;
    }

}





More information about the commits mailing list