1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11: class Horde_Vcs_Rcs extends Horde_Vcs_Base
12: {
13: 14: 15: 16: 17:
18: protected $_driver = 'Rcs';
19:
20: 21: 22:
23: public function isValidRevision($rev)
24: {
25: return $rev && preg_match('/^\d+\.(\d+\.)*\d+$/', $rev);
26: }
27:
28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
38: public function getRevisionRange(Horde_Vcs_File_Base $file, $r1, $r2)
39: {
40: switch ($this->cmp($r1, $r2)) {
41: case 0:
42: return array();
43:
44: case 1:
45: $curr = $this->prev($r1);
46: $stop = $this->prev($r2);
47: $flip = true;
48: break;
49:
50: case -1:
51: $curr = $r2;
52: $stop = $r1;
53: $flip = false;
54: break;
55: }
56:
57: $ret_array = array();
58:
59: do {
60: $ret_array[] = $curr;
61: $curr = $this->prev($curr);
62: if ($curr === $stop) {
63: return ($flip) ? array_reverse($ret_array) : $ret_array;
64: }
65: } while (!is_null($curr) && ($this->cmp($curr, $stop) != -1));
66:
67: return array();
68: }
69:
70: 71: 72:
73: public function getFile($filename, $opts = array())
74: {
75: $filename = ltrim($filename, '/');
76: $fname = $filename . ',v';
77: if (!@is_file($this->sourceroot . '/' . $fname)) {
78: throw new Horde_Vcs_Exception(sprintf('File "%s" not found', $filename));
79: }
80: return parent::getFile($fname, $opts);
81: }
82:
83: 84: 85: 86: 87: 88: 89: 90: 91: 92:
93: public function ci($filepath, $message, $user = null, $newBinary = false)
94: {
95: putenv('LOGNAME=' . ($user ? $user : 'guest'));
96:
97: $ci_cmd = $this->getPath('ci') . ' ' . escapeshellarg($filepath) . ' 2>&1';
98: $rcs_cmd = $this->getPath('rcs') . ' -i -kb ' . escapeshellarg($filepath) . ' 2>&1';
99: $output = '';
100:
101: $message_lines = explode("\n", $message);
102:
103: $pipe_def = array(0 => array("pipe", 'r'),
104: 1 => array("pipe", 'w'));
105:
106: $process = proc_open($newBinary ? $rcs_cmd : $ci_cmd, $pipe_def, $pipes);
107: if (is_resource($process)) {
108: foreach ($message_lines as $line) {
109: if ($line == '.\n') {
110: $line = '. \n';
111: }
112: fwrite($pipes[0], $line);
113: }
114:
115: fwrite($pipes[0], "\n.\n");
116: fclose($pipes[0]);
117:
118: while (!feof($pipes[1])) {
119: $output .= fread($pipes[1], 8192);
120: }
121: fclose($pipes[1]);
122: proc_close($process);
123: } else {
124: throw new Horde_Vcs_Exception('Failed to open pipe in ci()');
125: }
126:
127: if ($newBinary) {
128: exec($ci_cmd . ' 2>&1', $return_array, $retval);
129:
130: if ($retval) {
131: throw new Horde_Vcs_Exception("Unable to spawn ci on $filepath from ci()");
132: } else {
133: foreach ($return_array as $line) {
134: $output .= $line;
135: }
136: }
137: }
138:
139: $rev_start = strpos($output, 'new revision: ');
140:
141:
142: if ($rev_start === false) {
143: $rev_start = strpos($output, 'initial revision: ');
144: $rev_end = strpos($output, ' ', $rev_start);
145: } else {
146: $rev_end = strpos($output, ';', $rev_start);
147: }
148:
149: if ($rev_start !== false && $rev_end !== false) {
150: $rev_start += 14;
151: return substr($output, $rev_start, $rev_end - $rev_start);
152: } else {
153: unlock($filepath);
154: $temp_pos = strpos($output, 'file is unchanged');
155: if ($temp_pos !== false) {
156: throw new Horde_Vcs_Exception('Check-in Failure: ' . basename($filepath) . ' has not been modified');
157: } else {
158: throw new Horde_Vcs_Exception("Failed to checkin $filepath, $ci_cmd, $output");
159: }
160: }
161: }
162:
163: 164: 165: 166: 167: 168: 169: 170:
171: public function isLocked($filepath, &$locked_by)
172: {
173: $cmd = $this->getPath('rlog') . ' -L ' . escapeshellarg($filepath);
174:
175: exec($cmd . ' 2>&1', $return_array, $retval);
176:
177: if ($retval) {
178: throw new Horde_Vcs_Exception("Unable to spawn rlog on $filepath from isLocked()");
179: } else {
180: $output = '';
181:
182: foreach ($return_array as $line) {
183: $output .= $line;
184: }
185:
186: $start_name = strpos($output, 'locked by: ');
187: $end_name = strpos($output, ';', $start_name);
188:
189: if ($start_name !== false && $end_name !== false) {
190: $start_name += 11;
191: $locked_by = substr($output, $start_name, $end_name - $start_name);
192: return true;
193: } elseif (strlen($output) == 0) {
194: return false;
195: } else {
196: throw new Horde_Vcs_Exception('Failure running rlog in isLocked()');
197: }
198: }
199: }
200:
201: 202: 203: 204: 205: 206: 207: 208:
209: public function lock($filepath, $user = null)
210: {
211:
212: if ($user) {
213: putenv('LOGNAME=' . $user);
214: } else {
215: putenv('LOGNAME=guest');
216: }
217:
218: $cmd = $this->getPath('rcs') . ' -l ' . escapeshellarg($filepath);
219: exec($cmd . ' 2>&1', $return_array, $retval);
220:
221: if ($retval) {
222: throw new Horde_Vcs_Exception('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
223: } else {
224: $output = '';
225: foreach ($return_array as $line) {
226: $output .= $line;
227: }
228:
229: $locked_pos = strpos($output, 'locked');
230: if ($locked_pos !== false) {
231: return true;
232: } else {
233: throw new Horde_Vcs_Exception('Failed to lock "' . $filepath . '" (Ran "' . $cmd . '", got return code ' . $retval . ', output: ' . $output . ')');
234: }
235: }
236: }
237:
238: 239: 240: 241: 242: 243: 244: 245:
246: public function unlock($filepath, $user = null)
247: {
248:
249: if ($user) {
250: putenv('LOGNAME=' . $user);
251: } else {
252: putenv('LOGNAME=guest');
253: }
254:
255: $cmd = $this->getPath('rcs') . ' -u ' . escapeshellarg($filepath);
256: exec($cmd . ' 2>&1', $return_array, $retval);
257:
258: if ($retval) {
259: throw new Horde_Vcs_Exception('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
260: } else {
261: $output = '';
262:
263: foreach ($return_array as $line) {
264: $output .= $line;
265: }
266:
267: $unlocked_pos = strpos($output, 'unlocked');
268:
269: if ($unlocked_pos !== false) {
270: return true;
271: } else {
272:
273: return true;
274: }
275: }
276: }
277:
278: 279: 280: 281: 282: 283: 284: 285: 286: 287:
288: public function strip($val, $amount = 1)
289: {
290: $this->assertValidRevision($val);
291:
292: $pos = 0;
293: while ($amount-- > 0 && ($pos = strrpos($val, '.')) !== false) {
294: $val = substr($val, 0, $pos);
295: }
296:
297: return ($pos !== false) ? $val : false;
298: }
299:
300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311:
312: public function cmp($rev1, $rev2)
313: {
314: return strnatcasecmp($rev1, $rev2);
315: }
316:
317: 318: 319: 320: 321: 322: 323: 324: 325: 326:
327: public function prev($rev)
328: {
329: $last_dot = strrpos($rev, '.');
330: $val = substr($rev, ++$last_dot);
331:
332: if (--$val > 0) {
333: return substr($rev, 0, $last_dot) . $val;
334: } else {
335: --$last_dot;
336: while (--$last_dot) {
337: if ($rev[$last_dot] == '.') {
338: return substr($rev, 0, $last_dot);
339: } elseif (is_null($rev[$last_dot])) {
340: return null;
341: }
342: }
343: }
344: }
345:
346: }
347: