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:  * RCS file class.
  4:  *
  5:  * Copyright 2011-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file COPYING for license information (LGPL). If you
  8:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9:  *
 10:  * @author  Jan Schneider <jan@horde.org>
 11:  * @package Vcs
 12:  */
 13: class Horde_Vcs_File_Rcs extends Horde_Vcs_File_Base
 14: {
 15:     /**
 16:      * The current driver.
 17:      *
 18:      * @var string
 19:      */
 20:     protected $_driver = 'Rcs';
 21: 
 22:     /**
 23:      * TODO
 24:      *
 25:      * @var string
 26:      */
 27:     protected $_accum;
 28: 
 29:     /**
 30:      * TODO
 31:      *
 32:      * @var array
 33:      */
 34:     protected $_revsym = array();
 35: 
 36:     /**
 37:      * TODO
 38:      *
 39:      * @var array
 40:      */
 41:     protected $_symrev = array();
 42: 
 43:     /**
 44:      * TODO
 45:      *
 46:      * @var array
 47:      */
 48:     protected $_revlist = array();
 49: 
 50:     /**
 51:      * TODO
 52:      *
 53:      * @var array
 54:      */
 55:     protected $_branches = array();
 56: 
 57:     /**
 58:      * This method parses branches even though RCS doesn't support
 59:      * branches. But rlog from the RCS tools supports them, and displays them
 60:      * even on RCS repositories.
 61:      */
 62:     protected function _init()
 63:     {
 64:         /* Check that we are actually in the filesystem. */
 65:         $file = $this->getFullPath();
 66:         if (!is_file($file)) {
 67:             throw new Horde_Vcs_Exception('File Not Found: ' . $file);
 68:         }
 69: 
 70:         $ret_array = array();
 71:         $cmd = escapeshellcmd($this->_rep->getPath('rlog'))
 72:             . ' ' . escapeshellarg($file);
 73:         exec($cmd, $ret_array, $retval);
 74: 
 75:         if ($retval) {
 76:             throw new Horde_Vcs_Exception('Failed to spawn rlog to retrieve file log information for ' . $file);
 77:         }
 78: 
 79:         $branches = array();
 80:         $state = 'init';
 81: 
 82:         foreach ($ret_array as $line) {
 83:             switch ($state) {
 84:             case 'init':
 85:                 if (strpos($line, 'head: ') === 0) {
 86:                     $this->_branches['HEAD'] = substr($line, 6);
 87:                     $this->_revlist['HEAD'] = $this->_rep->getRevisionRange($this, '1.1', $this->_branches['HEAD']);
 88:                 } elseif (strpos($line, 'branch:') === 0) {
 89:                     $state = 'rev';
 90:                 }
 91:                 break;
 92: 
 93:             case 'rev':
 94:                 if (strpos($line, '----------') === 0) {
 95:                     $state = 'info';
 96:                 } elseif (preg_match("/^\s+([^:]+):\s+([\d\.]+)/", $line, $regs)) {
 97:                     // Check to see if this is a branch.
 98:                     if (preg_match('/^(\d+(\.\d+)+)\.0\.(\d+)$/', $regs[2])) {
 99:                         $rev = $regs[2];
100:                         $end = strrpos($rev, '.');
101:                         $rev[$end] = 0;
102:                         $branchRev = (($end2 = strrpos($rev, '.')) === false)
103:                             ? substr($rev, ++$end)
104:                             : substr_replace($rev, '.', $end2, ($end - $end2 + 1));
105: 
106:                         /* $branchRev is only the branching point, NOT the
107:                          * HEAD of the branch. To determine the HEAD, we need
108:                          * to parse all of the log data first. Yuck. */
109:                         $branches[$regs[1]] = $branchRev . '.';
110:                     } else {
111:                         $this->_symrev[$regs[1]] = $regs[2];
112:                         if (empty($this->_revsym[$regs[2]])) {
113:                             $this->_revsym[$regs[2]] = array();
114:                         }
115:                         $this->_revsym[$regs[2]][] = $regs[1];
116:                     }
117:                 }
118:                 break;
119: 
120:             case 'info':
121:                 if ((strpos($line, '==============================') === false) &&
122:                     (strpos($line, '----------------------------') === false)) {
123:                     $this->_accum[] = $line;
124:                 } elseif (count($this->_accum)) {
125:                     $log = $this->_getLog();
126:                     $rev = $log->getRevision();
127:                     $onbranch = false;
128:                     $onhead = substr_count($rev, '.') == 1;
129: 
130:                     // Determine branch information.
131:                     if ($onhead) {
132:                         $onbranch = (empty($this->_branch) || $this->_branch == 'HEAD') ||
133:                             ($this->_rep->cmp($branches[$this->_branch], $rev) === 1);
134:                         $log->setBranch('HEAD');
135:                     } else {
136:                         foreach ($branches as $key => $val) {
137:                             if (strpos($rev, $val) === 0) {
138:                                 $onbranch = true;
139:                                 $log->setBranch($key);
140:                                 if (!isset($this->_branches[$key])) {
141:                                     $this->_branches[$key] = $rev;
142:                                     $this->_revlist[$key] = $this->_rep->getRevisionRange($this, '1.1', $rev);
143:                                 }
144:                                 if ($this->_branch == 'HEAD') {
145:                                     break 2;
146:                                 }
147:                                 break;
148:                             }
149:                         }
150:                     }
151: 
152:                     if ($onbranch) {
153:                         $this->_revs[] = $rev;
154:                         $this->_logs[$rev] = $log;
155:                     }
156: 
157:                     $this->_accum = array();
158:                 }
159:                 break;
160:             }
161:         }
162:     }
163: 
164:     /**
165:      * Returns name of the current file without the repository
166:      * extensions (usually ,v)
167:      *
168:      * @return string  Filename without repository extension
169:      */
170:     public function getFileName()
171:     {
172:         return preg_replace('/,v$/', '', $this->_name);
173:     }
174: 
175:     /**
176:      * Return the fully qualified filename of this object.
177:      *
178:      * @return string  Fully qualified filename of this object.
179:      */
180:     public function getFullPath()
181:     {
182:         $path = $this->_rep->sourceroot;
183:         if (strlen($this->_dir)) {
184:             $path .= '/' . $this->_dir;
185:         }
186:         $path .= '/' . $this->_name;
187:         return $path;
188:     }
189: 
190:     /**
191:      * Return the name of this file relative to its sourceroot.
192:      *
193:      * @return string  Pathname relative to the sourceroot.
194:      */
195:     public function getSourcerootPath()
196:     {
197:         return substr(parent::getSourcerootPath(), 0, -2);
198:     }
199: 
200:     /**
201:      * Returns the revision before the specified revision.
202:      *
203:      * @param string $rev  A revision.
204:      *
205:      * @return string  The previous revision or null if the first revision.
206:      */
207:     public function getPreviousRevision($rev)
208:     {
209:         /* Revisions in RCS/CVS logs are not ordered by date, so use the logic
210:          * from the base object. */
211:         return $this->_rep->prev($rev);
212:     }
213: 
214:     /**
215:      * Returns a log object for the most recent log entry of this file.
216:      *
217:      * @return Horde_Vcs_QuickLog_Rcs  Log object of the last entry in the file.
218:      * @throws Horde_Vcs_Exception
219:      */
220:     public function getLastLog()
221:     {
222:         /* Check that we are actually in the filesystem. */
223:         $file = $this->getFullPath();
224:         if (!is_file($file)) {
225:             throw new Horde_Vcs_Exception('File Not Found: ' . $file);
226:         }
227: 
228:         $cmd = escapeshellcmd($this->_rep->getPath('rlog')) . ' -r';
229:         if (!empty($this->_branch)) {
230:             $branches = $this->getBranches();
231:             $branch = $branches[$this->_branch];
232:             $cmd .= substr($branch, 0, strrpos($branch, '.')) . '.';
233:         }
234:         $cmd .= ' ' . escapeshellarg($file);
235:         exec($cmd, $ret_array, $retval);
236: 
237:         if ($retval) {
238:             throw new Horde_Vcs_Exception('Failed to spawn rlog to retrieve file log information for ' . $file);
239:         }
240: 
241:         $state = 'init';
242:         $log = '';
243:         foreach ($ret_array as $line) {
244:             switch ($state) {
245:             case 'init':
246:                 if (strpos($line, '----------') === 0) {
247:                     $state = 'revision';
248:                 }
249:                 break;
250: 
251:             case 'revision':
252:                 if (preg_match("/revision (.+)$/", $line, $parts)) {
253:                     $rev = $parts[1];
254:                     $state = 'details';
255:                 }
256:                 break;
257: 
258:             case 'details':
259:                 if (preg_match("|^date:\s+(\d+)[-/](\d+)[-/](\d+)\s+(\d+):(\d+):(\d+).*?;\s+author:\s+(.+);\s+state:\s+(\S+);(\s+lines:\s+\+(\d+)\s\-(\d+))?|", $line, $parts)) {
260:                     $date = gmmktime($parts[4], $parts[5], $parts[6], $parts[2], $parts[3], $parts[1]);
261:                     $author = $parts[7];
262:                     $state = 'log';
263:                 }
264:                 break;
265: 
266:             case 'log':
267:                 if (strpos($line, '==============================') === 0) {
268:                     $log = substr($log, 0, -1);
269:                     break 2;
270:                 }
271:                 $log .= $line . "\n";
272:             }
273:         }
274: 
275:         $class = 'Horde_Vcs_QuickLog_' . $this->_driver;
276:         return new $class($this->_rep, $rev, $date, $author, $log);
277:     }
278: 
279:     /**
280:      * TODO
281:      */
282:     public function getRevisionSymbol($rev)
283:     {
284:         return isset($this->_revsym[$rev])
285:             ? $this->_revsym[$rev]
286:             : array();
287:     }
288: 
289:     /**
290:      * TODO
291:      */
292:     public function getAccum()
293:     {
294:         return $this->_accum;
295:     }
296: }
297: 
API documentation generated by ApiGen