Overview

Packages

  • None
  • Vcs

Classes

  • Horde_Vcs
  • Horde_Vcs_Base
  • Horde_Vcs_Cvs
  • Horde_Vcs_Directory_Base
  • Horde_Vcs_Directory_Cvs
  • Horde_Vcs_Directory_Git
  • Horde_Vcs_Directory_Rcs
  • Horde_Vcs_Directory_Svn
  • Horde_Vcs_File_Base
  • Horde_Vcs_File_Cvs
  • Horde_Vcs_File_Git
  • Horde_Vcs_File_Rcs
  • Horde_Vcs_File_Svn
  • Horde_Vcs_Git
  • Horde_Vcs_Log_Base
  • Horde_Vcs_Log_Cvs
  • Horde_Vcs_Log_Git
  • Horde_Vcs_Log_Rcs
  • Horde_Vcs_Log_Svn
  • Horde_Vcs_Patchset
  • Horde_Vcs_Patchset_Base
  • Horde_Vcs_Patchset_Cvs
  • Horde_Vcs_Patchset_Git
  • Horde_Vcs_Patchset_Svn
  • Horde_Vcs_QuickLog_Base
  • Horde_Vcs_QuickLog_Cvs
  • Horde_Vcs_QuickLog_Git
  • Horde_Vcs_QuickLog_Rcs
  • Horde_Vcs_QuickLog_Svn
  • Horde_Vcs_Rcs
  • Horde_Vcs_Svn
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde_Vcs_cvs implementation.
  4:  *
  5:  * Constructor args:
  6:  * <pre>
  7:  * 'sourceroot': The source root for this repository
  8:  * 'paths': Hash with the locations of all necessary binaries: 'rcsdiff',
  9:  *          'rlog', 'cvsps', 'cvsps_home', and 'temp' (the temp path).
 10:  * </pre>
 11:  *
 12:  * Copyright 2000-2012 Horde LLC (http://www.horde.org/)
 13:  *
 14:  * See the enclosed file COPYING for license information (LGPL). If you
 15:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 16:  *
 17:  * @author  Anil Madhavapeddy <anil@recoil.org>
 18:  * @author  Michael Slusarz <slusarz@horde.org>
 19:  * @package Vcs
 20:  */
 21: class Horde_Vcs_Cvs extends Horde_Vcs_Rcs
 22: {
 23:     /**
 24:      * The current driver.
 25:      *
 26:      * @var string
 27:      */
 28:     protected $_driver = 'Cvs';
 29: 
 30:     /**
 31:      * Driver features.
 32:      *
 33:      * @var array
 34:      */
 35:     protected $_features = array(
 36:         'deleted'   => true,
 37:         'patchsets' => true,
 38:         'branches'  => true,
 39:         'snapshots' => false);
 40: 
 41:     /**
 42:      * Constructor.
 43:      */
 44:     public function __construct($params = array())
 45:     {
 46:         parent::__construct($params);
 47:         if (!$this->getPath('cvsps')) {
 48:             $this->_features['patchsets'] = false;
 49:         }
 50:     }
 51: 
 52:     /**
 53:      * TODO
 54:      */
 55:     public function getFile($filename, $opts = array())
 56:     {
 57:         $filename = ltrim($filename, '/');
 58:         $fname = $filename . ',v';
 59: 
 60:         /* Assume file is in the Attic if it doesn't exist. */
 61:         if (!@is_file($this->sourceroot . '/' . $fname)) {
 62:             $fname = dirname($filename) . '/Attic/' . basename($filename) . ',v';
 63:         }
 64: 
 65:         if (!@is_file($this->sourceroot . '/' . $fname)) {
 66:             throw new Horde_Vcs_Exception(sprintf('File "%s" not found', $filename));
 67:         }
 68: 
 69:         return Horde_Vcs_Base::getFile($fname, $opts);
 70:     }
 71: 
 72:     /**
 73:      * TODO
 74:      */
 75:     public function isFile($where)
 76:     {
 77:         return @is_file($where . ',v') ||
 78:                @is_file(dirname($where) . '/Attic/' . basename($where) . ',v');
 79:     }
 80: 
 81:     /**
 82:      * Obtain the differences between two revisions of a file.
 83:      *
 84:      * @param Horde_Vcs_File_Cvs $file  The desired file.
 85:      * @param string $rev1              Original revision number to compare
 86:      *                                  from.
 87:      * @param string $rev2              New revision number to compare against.
 88:      * @param array $opts               The following optional options:
 89:      *                                  - 'num': (integer) DEFAULT: 3
 90:      *                                  - 'type': (string) DEFAULT: 'unified'
 91:      *                                  - 'ws': (boolean) DEFAULT: true
 92:      *
 93:      * @return string|boolean  False on failure, or a string containing the
 94:      *                         diff on success.
 95:      */
 96:     protected function _diff(Horde_Vcs_File_Base $file, $rev1, $rev2, $opts)
 97:     {
 98:         $fullName = $file->getPath();
 99:         $diff = array();
100:         $flags = '-kk ';
101: 
102:         if (!$opts['ws']) {
103:             $flags .= ' -bB ';
104:         }
105: 
106:         switch ($opts['type']) {
107:         case 'context':
108:             $flags .= '-p --context=' . escapeshellarg((int)$opts['num']);
109:             break;
110: 
111:         case 'unified':
112:             $flags .= '-p --unified=' . escapeshellarg((int)$opts['num']);
113:             break;
114: 
115:         case 'column':
116:             $flags .= '--side-by-side --width=120';
117:             break;
118: 
119:         case 'ed':
120:             $flags .= '-e';
121:             break;
122:         }
123: 
124:         // Windows versions of cvs always return $where with forwards slashes.
125:         if (VC_WINDOWS) {
126:             $fullName = str_replace(DIRECTORY_SEPARATOR, '/', $fullName);
127:         }
128: 
129:         // TODO: add options for $hr options - however these may not be
130:         // compatible with some diffs.
131:         $command = escapeshellcmd($this->getPath('rcsdiff')) . ' ' . $flags . ' -r' . escapeshellarg($rev1) . ' -r' . escapeshellarg($rev2) . ' ' . escapeshellarg($fullName) . ' 2>&1';
132:         if (VC_WINDOWS) {
133:             $command .= ' < ' . escapeshellarg(__FILE__);
134:         }
135: 
136:         exec($command, $diff, $retval);
137:         return ($retval > 0) ? $diff : array();
138:     }
139: 
140:     /**
141:      * TODO
142:      *
143:      * @throws Horde_Vcs_Exception
144:      */
145:     public function annotate($fileob, $rev)
146:     {
147:         $this->assertValidRevision($rev);
148: 
149:         $tmpfile = Horde_Util::getTempFile('vc', true, $this->_paths['temp']);
150:         $where = $fileob->getSourcerootPath();
151: 
152:         $pipe = popen(escapeshellcmd($this->getPath('cvs')) . ' -n server > ' . escapeshellarg($tmpfile), VC_WINDOWS ? 'wb' : 'w');
153: 
154:         $out = array(
155:             'Root ' . $this->sourceroot,
156:             'Valid-responses ok error Valid-requests Checked-in Updated Merged Removed M E',
157:             'UseUnchanged',
158:             'Argument -r',
159:             'Argument ' . $rev,
160:             'Argument ' . $where
161:         );
162: 
163:         $dirs = explode('/', dirname($where));
164:         while (count($dirs)) {
165:             $out[] = 'Directory ' . implode('/', $dirs);
166:             $out[] = $this->sourceroot . '/' . implode('/', $dirs);
167:             array_pop($dirs);
168:         }
169: 
170:         $out[] = 'Directory .';
171:         $out[] = $this->sourceroot;
172:         $out[] = 'annotate';
173: 
174:         foreach ($out as $line) {
175:             fwrite($pipe, "$line\n");
176:         }
177:         pclose($pipe);
178: 
179:         if (!($fl = fopen($tmpfile, VC_WINDOWS ? 'rb' : 'r'))) {
180:             return false;
181:         }
182: 
183:         $lines = array();
184:         $line = fgets($fl, 4096);
185: 
186:         // Windows versions of cvs always return $where with forwards slashes.
187:         if (VC_WINDOWS) {
188:             $where = str_replace(DIRECTORY_SEPARATOR, '/', $where);
189:         }
190: 
191:         while ($line && !preg_match("|^E\s+Annotations for $where|", $line)) {
192:             $line = fgets($fl, 4096);
193:         }
194: 
195:         if (!$line) {
196:             throw new Horde_Vcs_Exception('Unable to annotate; server said: ' . $line);
197:         }
198: 
199:         $lineno = 1;
200:         while ($line = fgets($fl, 4096)) {
201:             if (preg_match('/^M\s+([\d\.]+)\s+\((.+)\s+(\d+-\w+-\d+)\):.(.*)$/', $line, $regs)) {
202:                 $lines[] = array(
203:                     'rev' => $regs[1],
204:                     'author' => trim($regs[2]),
205:                     'date' => $regs[3],
206:                     'line' => $regs[4],
207:                     'lineno' => $lineno++
208:                 );
209:             }
210:         }
211: 
212:         fclose($fl);
213:         return $lines;
214:     }
215: 
216:     /**
217:      * Returns a file pointing to the head of the requested revision of a
218:      * file.
219:      *
220:      * @param string $fullname  Fully qualified pathname of the desired file
221:      *                          to checkout.
222:      * @param string $rev       Revision number to check out.
223:      *
224:      * @return resource  A stream pointer to the head of the checkout.
225:      * @throws Horde_Vcs_Exception
226:      */
227:     public function checkout($fullname, $rev)
228:     {
229:         $this->assertValidRevision($rev);
230: 
231:         if (!($RCS = popen(escapeshellcmd($this->getPath('co')) . ' ' . escapeshellarg('-p' . $rev) . ' ' . escapeshellarg($fullname) . " 2>&1", VC_WINDOWS ? 'rb' : 'r'))) {
232:             throw new Horde_Vcs_Exception('Couldn\'t perform checkout of the requested file');
233:         }
234: 
235:         /* First line from co should be of the form :
236:          * /path/to/filename,v  -->  standard out
237:          * and we check that this is the case and error otherwise
238:          */
239:         $co = fgets($RCS, 1024);
240:         if (!preg_match('/^([\S ]+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/', $co, $regs) ||
241:             ($regs[1] != $fullname)) {
242:             throw new Horde_Vcs_Exception('Unexpected output from checkout: ' . $co);
243:         }
244: 
245:         /* Next line from co is of the form:
246:          * revision 1.2.3
247:          * TODO: compare this to $rev for consistency, atm we just
248:          *       discard the value to move input pointer along - avsm
249:          */
250:         $co = fgets($RCS, 1024);
251: 
252:         return $RCS;
253:     }
254: 
255: }
256: 
API documentation generated by ApiGen