1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13: class Horde_Vcs_File_Rcs extends Horde_Vcs_File_Base
14: {
15: 16: 17: 18: 19:
20: protected $_driver = 'Rcs';
21:
22: 23: 24: 25: 26:
27: protected $_accum;
28:
29: 30: 31: 32: 33:
34: protected $_revsym = array();
35:
36: 37: 38: 39: 40:
41: protected $_symrev = array();
42:
43: 44: 45: 46: 47:
48: protected $_revlist = array();
49:
50: 51: 52: 53: 54:
55: protected $_branches = array();
56:
57: 58: 59: 60: 61:
62: protected function _init()
63: {
64:
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:
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: 107: 108:
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:
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: 166: 167: 168: 169:
170: public function getFileName()
171: {
172: return preg_replace('/,v$/', '', $this->_name);
173: }
174:
175: 176: 177: 178: 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: 192: 193: 194:
195: public function getSourcerootPath()
196: {
197: return substr(parent::getSourcerootPath(), 0, -2);
198: }
199:
200: 201: 202: 203: 204: 205: 206:
207: public function getPreviousRevision($rev)
208: {
209: 210:
211: return $this->_rep->prev($rev);
212: }
213:
214: 215: 216: 217: 218: 219:
220: public function getLastLog()
221: {
222:
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: 281:
282: public function getRevisionSymbol($rev)
283: {
284: return isset($this->_revsym[$rev])
285: ? $this->_revsym[$rev]
286: : array();
287: }
288:
289: 290: 291:
292: public function getAccum()
293: {
294: return $this->_accum;
295: }
296: }
297: