Filename | /var/www/foswikidev/core/lib/Foswiki/Store/Rcs/RcsWrapHandler.pm |
Statements | Executed 79362 statements in 609ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
26327 | 1 | 1 | 389ms | 4.75s | getRevision | Foswiki::Store::Rcs::RcsWrapHandler::
26693 | 1 | 1 | 291ms | 552ms | new | Foswiki::Store::Rcs::RcsWrapHandler::
1 | 1 | 1 | 6.49ms | 11.2ms | BEGIN@23 | Foswiki::Store::Rcs::RcsWrapHandler::
1 | 1 | 1 | 16µs | 29µs | BEGIN@19 | Foswiki::Store::Rcs::RcsWrapHandler::
1 | 1 | 1 | 9µs | 14µs | BEGIN@20 | Foswiki::Store::Rcs::RcsWrapHandler::
1 | 1 | 1 | 9µs | 35µs | BEGIN@21 | Foswiki::Store::Rcs::RcsWrapHandler::
1 | 1 | 1 | 6µs | 6µs | BEGIN@28 | Foswiki::Store::Rcs::RcsWrapHandler::
1 | 1 | 1 | 5µs | 5µs | BEGIN@26 | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | _deleteRevision | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | _lock | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | _numRevisions | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | ci | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | deleteRevision | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | finish | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | getInfo | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | getRevisionAtTime | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | initBinary | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | initText | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | parseRevisionDiff | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | repRev | Foswiki::Store::Rcs::RcsWrapHandler::
0 | 0 | 0 | 0s | 0s | revisionDiff | Foswiki::Store::Rcs::RcsWrapHandler::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Store::Rcs::RcsWrapHandler | ||||
6 | |||||
7 | This class implements the pure methods of the Foswiki::Store::Rcs::Handler | ||||
8 | superclass. See the superclass for detailed documentation of the methods. | ||||
9 | |||||
10 | Wrapper around the RCS commands required by Foswiki. | ||||
11 | An object of this class is created for each file stored under RCS. | ||||
12 | |||||
13 | For readers who are familiar with Foswiki version 1.0, this class | ||||
14 | is analagous to the old =Foswiki::Store::RcsWrap=. | ||||
15 | |||||
16 | =cut | ||||
17 | |||||
18 | package Foswiki::Store::Rcs::RcsWrapHandler; | ||||
19 | 2 | 29µs | 2 | 43µs | # spent 29µs (16+13) within Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@19 which was called:
# once (16µs+13µs) by Foswiki::Store::RcsWrap::BEGIN@26 at line 19 # spent 29µs making 1 call to Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@19
# spent 13µs making 1 call to strict::import |
20 | 2 | 24µs | 2 | 18µs | # spent 14µs (9+4) within Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@20 which was called:
# once (9µs+4µs) by Foswiki::Store::RcsWrap::BEGIN@26 at line 20 # spent 14µs making 1 call to Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@20
# spent 4µs making 1 call to warnings::import |
21 | 2 | 27µs | 2 | 62µs | # spent 35µs (9+26) within Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@21 which was called:
# once (9µs+26µs) by Foswiki::Store::RcsWrap::BEGIN@26 at line 21 # spent 35µs making 1 call to Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@21
# spent 26µs making 1 call to Exporter::import |
22 | |||||
23 | 2 | 157µs | 1 | 11.2ms | # spent 11.2ms (6.49+4.74) within Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@23 which was called:
# once (6.49ms+4.74ms) by Foswiki::Store::RcsWrap::BEGIN@26 at line 23 # spent 11.2ms making 1 call to Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@23 |
24 | 1 | 8µs | our @ISA = ('Foswiki::Store::Rcs::Handler'); | ||
25 | |||||
26 | 2 | 64µs | 1 | 5µs | # spent 5µs within Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@26 which was called:
# once (5µs+0s) by Foswiki::Store::RcsWrap::BEGIN@26 at line 26 # spent 5µs making 1 call to Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@26 |
27 | |||||
28 | # spent 6µs within Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@28 which was called:
# once (6µs+0s) by Foswiki::Store::RcsWrap::BEGIN@26 at line 36 | ||||
29 | 1 | 600ns | if ( $Foswiki::cfg{UseLocale} ) { | ||
30 | require locale; | ||||
31 | import locale(); | ||||
32 | } | ||||
33 | |||||
34 | 1 | 2µs | *_decode = \&Foswiki::Store::decode; | ||
35 | 1 | 5µs | *_encode = \&Foswiki::Store::encode; | ||
36 | 1 | 2.21ms | 1 | 6µs | } # spent 6µs making 1 call to Foswiki::Store::Rcs::RcsWrapHandler::BEGIN@28 |
37 | |||||
38 | # spent 552ms (291+261) within Foswiki::Store::Rcs::RcsWrapHandler::new which was called 26693 times, avg 21µs/call:
# 26693 times (291ms+261ms) by Foswiki::Store::RcsWrap::getHandler at line 30 of /var/www/foswikidev/core/lib/Foswiki/Store/RcsWrap.pm, avg 21µs/call | ||||
39 | 26693 | 286ms | 26693 | 261ms | return shift->SUPER::new(@_); # spent 261ms making 26693 calls to Foswiki::Store::Rcs::Handler::new, avg 10µs/call |
40 | } | ||||
41 | |||||
42 | =begin TML | ||||
43 | |||||
44 | ---++ ObjectMethod finish() | ||||
45 | Break circular references. | ||||
46 | |||||
47 | =cut | ||||
48 | |||||
49 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
50 | # whether they are references or not. That way this method is "golden | ||||
51 | # documentation" of the live fields in the object. | ||||
52 | sub finish { | ||||
53 | my $this = shift; | ||||
54 | $this->SUPER::finish(); | ||||
55 | undef $this->{binary}; | ||||
56 | } | ||||
57 | |||||
58 | # implements Rcs::Handler | ||||
59 | sub initBinary { | ||||
60 | my ($this) = @_; | ||||
61 | |||||
62 | $this->{binary} = 1; | ||||
63 | |||||
64 | $this->mkPathTo( $this->{file} ); | ||||
65 | |||||
66 | return if $this->revisionHistoryExists(); | ||||
67 | |||||
68 | my ( $rcsOutput, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
69 | $Foswiki::cfg{RCS}{initBinaryCmd}, | ||||
70 | FILENAME => _encode( $this->{file} ) | ||||
71 | ); | ||||
72 | if ($exit) { | ||||
73 | throw Error::Simple( $Foswiki::cfg{RCS}{initBinaryCmd} . ' of ' | ||||
74 | . $this->hidePath( $this->{file} ) | ||||
75 | . ' failed: ' | ||||
76 | . $rcsOutput ); | ||||
77 | } | ||||
78 | elsif ( !$this->revisionHistoryExists() ) { | ||||
79 | |||||
80 | # Sometimes (on Windows?) rcs file not formed, so check for it | ||||
81 | throw Error::Simple( $Foswiki::cfg{RCS}{initBinaryCmd} . ' of ' | ||||
82 | . $this->hidePath( $this->{rcsFile} ) | ||||
83 | . ' failed to create history file' ); | ||||
84 | } | ||||
85 | } | ||||
86 | |||||
87 | # implements Rcs::Handler | ||||
88 | sub initText { | ||||
89 | my ($this) = @_; | ||||
90 | $this->{binary} = 0; | ||||
91 | |||||
92 | $this->mkPathTo( $this->{file} ); | ||||
93 | |||||
94 | return if $this->revisionHistoryExists(); | ||||
95 | |||||
96 | my ( $rcsOutput, $exit, $stdErr ) = | ||||
97 | Foswiki::Sandbox->sysCommand( $Foswiki::cfg{RCS}{initTextCmd}, | ||||
98 | FILENAME => _encode( $this->{file} ) ); | ||||
99 | if ($exit) { | ||||
100 | $rcsOutput ||= ''; | ||||
101 | throw Error::Simple( $Foswiki::cfg{RCS}{initTextCmd} . ' of ' | ||||
102 | . $this->hidePath( $this->{file} ) | ||||
103 | . ' failed: ' | ||||
104 | . $rcsOutput ); | ||||
105 | } | ||||
106 | elsif ( !$this->revisionHistoryExists() ) { | ||||
107 | |||||
108 | # Sometimes (on Windows?) rcs file not formed, so check for it | ||||
109 | throw Error::Simple( $Foswiki::cfg{RCS}{initTextCmd} . ' of ' | ||||
110 | . $this->hidePath( $this->{rcsFile} ) | ||||
111 | . ' failed to create history file' ); | ||||
112 | } | ||||
113 | } | ||||
114 | |||||
115 | # implements Rcs::Handler | ||||
116 | |||||
117 | # Designed for calling *only* from the Handler superclass and this class | ||||
118 | sub ci { | ||||
119 | my ( $this, $isStream, $data, $comment, $user, $date ) = @_; | ||||
120 | |||||
121 | _lock($this); | ||||
122 | if ($isStream) { | ||||
123 | $this->saveStream($data); | ||||
124 | } | ||||
125 | else { | ||||
126 | $this->saveFile( $this->{file}, $data ); | ||||
127 | } | ||||
128 | |||||
129 | $comment = 'none' unless $comment; | ||||
130 | |||||
131 | my ( $cmd, $rcsOutput, $exit, $stderr ); | ||||
132 | if ( defined($date) ) { | ||||
133 | require Foswiki::Time; | ||||
134 | $date = Foswiki::Time::formatTime( $date, '$rcs', 'gmtime' ); | ||||
135 | $cmd = $Foswiki::cfg{RCS}{ciDateCmd}; | ||||
136 | ( $rcsOutput, $exit, $stderr ) = Foswiki::Sandbox->sysCommand( | ||||
137 | $cmd, | ||||
138 | USERNAME => $user, | ||||
139 | FILENAME => _encode( $this->{file} ), | ||||
140 | COMMENT => $comment, | ||||
141 | DATE => $date | ||||
142 | ); | ||||
143 | } | ||||
144 | else { | ||||
145 | $cmd = $Foswiki::cfg{RCS}{ciCmd}; | ||||
146 | ( $rcsOutput, $exit, $stderr ) = Foswiki::Sandbox->sysCommand( | ||||
147 | $cmd, | ||||
148 | USERNAME => $user, | ||||
149 | FILENAME => _encode( $this->{file} ), | ||||
150 | COMMENT => $comment | ||||
151 | ); | ||||
152 | } | ||||
153 | $rcsOutput ||= ''; | ||||
154 | |||||
155 | if ($exit) { | ||||
156 | throw Error::Simple( $cmd . ' of ' | ||||
157 | . $this->hidePath( $this->{file} ) | ||||
158 | . ' failed: ' | ||||
159 | . $exit . ' ' | ||||
160 | . $rcsOutput | ||||
161 | . ( (DEBUG) ? $stderr : '' ) ); | ||||
162 | } | ||||
163 | chmod( $Foswiki::cfg{Store}{filePermission}, _encode( $this->{file} ) ); | ||||
164 | } | ||||
165 | |||||
166 | # implements Rcs::Handler | ||||
167 | sub repRev { | ||||
168 | my ( $this, $text, $comment, $user, $date ) = @_; | ||||
169 | |||||
170 | my $rev = $this->_numRevisions() || 0; | ||||
171 | |||||
172 | $comment ||= 'none'; | ||||
173 | |||||
174 | # update repository with same userName and date | ||||
175 | if ( $rev <= 1 ) { | ||||
176 | |||||
177 | # initial revision, so delete repository file and start again | ||||
178 | unlink $this->{rcsFile}; | ||||
179 | } | ||||
180 | else { | ||||
181 | _deleteRevision( $this, $rev ); | ||||
182 | } | ||||
183 | |||||
184 | $this->saveFile( $this->{file}, $text ); | ||||
185 | require Foswiki::Time; | ||||
186 | $date = Foswiki::Time::formatTime( $date, '$rcs', 'gmtime' ); | ||||
187 | |||||
188 | _lock($this); | ||||
189 | my ( $rcsOut, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
190 | $Foswiki::cfg{RCS}{ciDateCmd}, | ||||
191 | DATE => $date, | ||||
192 | USERNAME => $user, | ||||
193 | FILENAME => _encode( $this->{file} ), | ||||
194 | COMMENT => $comment | ||||
195 | ); | ||||
196 | if ($exit) { | ||||
197 | $rcsOut = $Foswiki::cfg{RCS}{ciDateCmd} . "\n" . $rcsOut; | ||||
198 | return $rcsOut; | ||||
199 | } | ||||
200 | chmod( $Foswiki::cfg{Store}{filePermission}, _encode( $this->{file} ) ); | ||||
201 | } | ||||
202 | |||||
203 | # implements Rcs::Handler | ||||
204 | sub deleteRevision { | ||||
205 | my ($this) = @_; | ||||
206 | my $rev = $this->_numRevisions(); | ||||
207 | return if ( $rev <= 1 ); | ||||
208 | return _deleteRevision( $this, $rev ); | ||||
209 | } | ||||
210 | |||||
211 | sub _deleteRevision { | ||||
212 | my ( $this, $rev ) = @_; | ||||
213 | |||||
214 | # delete latest revision (unlock (may not be needed), delete revision) | ||||
215 | my ( $rcsOut, $exit ) = | ||||
216 | Foswiki::Sandbox->sysCommand( $Foswiki::cfg{RCS}{unlockCmd}, | ||||
217 | FILENAME => _encode( $this->{file} ) ); | ||||
218 | if ($exit) { | ||||
219 | throw Error::Simple( | ||||
220 | $Foswiki::cfg{RCS}{unlockCmd} . ' failed: ' . $rcsOut ); | ||||
221 | } | ||||
222 | |||||
223 | chmod( $Foswiki::cfg{Store}{filePermission}, _encode( $this->{file} ) ); | ||||
224 | |||||
225 | ( $rcsOut, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
226 | $Foswiki::cfg{RCS}{delRevCmd}, | ||||
227 | REVISION => '1.' . $rev, | ||||
228 | FILENAME => _encode( $this->{file} ) | ||||
229 | ); | ||||
230 | |||||
231 | if ($exit) { | ||||
232 | throw Error::Simple( $Foswiki::cfg{RCS}{delRevCmd} . ' of ' | ||||
233 | . $this->hidePath( $this->{file} ) | ||||
234 | . ' failed: ' | ||||
235 | . $rcsOut ); | ||||
236 | } | ||||
237 | |||||
238 | # Update the checkout | ||||
239 | $rev--; | ||||
240 | ( $rcsOut, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
241 | $Foswiki::cfg{RCS}{coCmd}, | ||||
242 | REVISION => '1.' . $rev, | ||||
243 | FILENAME => _encode( $this->{file} ) | ||||
244 | ); | ||||
245 | |||||
246 | if ($exit) { | ||||
247 | throw Error::Simple( $Foswiki::cfg{RCS}{coCmd} . ' of ' | ||||
248 | . $this->hidePath( $this->{file} ) | ||||
249 | . ' failed: ' | ||||
250 | . $rcsOut ); | ||||
251 | } | ||||
252 | $this->saveFile( $this->{file}, $rcsOut ); | ||||
253 | } | ||||
254 | |||||
255 | # implements Rcs::Handler | ||||
256 | # spent 4.75s (389ms+4.36) within Foswiki::Store::Rcs::RcsWrapHandler::getRevision which was called 26327 times, avg 180µs/call:
# 26327 times (389ms+4.36s) by Foswiki::Store::Rcs::Store::readTopic at line 114 of /var/www/foswikidev/core/lib/Foswiki/Store/Rcs/Store.pm, avg 180µs/call | ||||
257 | 26327 | 12.9ms | my ( $this, $version ) = @_; | ||
258 | |||||
259 | # If there is no revision history, or if $version is not given, | ||||
260 | # or there is a checkin pending, then consult the .txt | ||||
261 | 26327 | 308ms | 52654 | 4.36s | if ( !$this->revisionHistoryExists() # spent 3.21s making 26327 calls to Foswiki::Store::Rcs::Handler::getRevision, avg 122µs/call
# spent 1.14s making 26327 calls to Foswiki::Store::Rcs::Handler::revisionHistoryExists, avg 43µs/call |
262 | || !$version | ||||
263 | || !$this->noCheckinPending() ) | ||||
264 | { | ||||
265 | |||||
266 | # Get the latest rev from the cache | ||||
267 | return ( $this->SUPER::getRevision($version) ); | ||||
268 | } | ||||
269 | |||||
270 | # We've been asked for an explicit rev. The rev might be outside the | ||||
271 | # range of revs in RCS. RCS will return the latest, though it reports | ||||
272 | # the rev retrieved to STDERR (no use to us, as we have no access | ||||
273 | # to STDERR) | ||||
274 | |||||
275 | # SMELL: we need to determine if the rev we are returning is the latest. | ||||
276 | # co prints the retrieved revision, but unfortunately it prints it | ||||
277 | # to STDERR, which the Sandbox can't retrieve. | ||||
278 | |||||
279 | my $tmpfile; | ||||
280 | my $tmpRevFile; | ||||
281 | my $coCmd = $Foswiki::cfg{RCS}{coCmd}; | ||||
282 | my $file = _encode( $this->{file} ); | ||||
283 | if ( $Foswiki::cfg{RCS}{coMustCopy} ) { | ||||
284 | |||||
285 | # Need to take temporary copy of topic, check it out to file, | ||||
286 | # then read that. Need to put RCS into binary mode to avoid | ||||
287 | # extra \r appearing and read from binmode file rather than | ||||
288 | # stdout to avoid early file read termination | ||||
289 | # See http://twiki.org/cgi-bin/view/Codev/DakarRcsWrapProblem | ||||
290 | # for evidence that this code is needed. | ||||
291 | $tmpfile = Foswiki::Store::Rcs::Handler::mkTmpFilename($this); | ||||
292 | $tmpRevFile = $tmpfile . ',v'; | ||||
293 | $this->_copyFile( $this->{rcsFile}, $tmpRevFile ); | ||||
294 | my ( $rcsOutput, $status ) = | ||||
295 | Foswiki::Sandbox->sysCommand( $Foswiki::cfg{RCS}{tmpBinaryCmd}, | ||||
296 | FILENAME => $tmpRevFile ); | ||||
297 | if ($status) { | ||||
298 | throw Error::Simple( | ||||
299 | $Foswiki::cfg{RCS}{tmpBinaryCmd} . ' failed: ' . $rcsOutput ); | ||||
300 | } | ||||
301 | $file = $tmpfile; | ||||
302 | $coCmd =~ s/-p%REVISION/-r%REVISION/; | ||||
303 | } | ||||
304 | my ( $text, $status, $stderr ) = Foswiki::Sandbox->sysCommand( | ||||
305 | $coCmd, | ||||
306 | REVISION => '1.' . $version, | ||||
307 | FILENAME => $file | ||||
308 | ); | ||||
309 | |||||
310 | # The loaded version is reported on STDERR | ||||
311 | my $isLatest = 0; | ||||
312 | if ( defined $stderr | ||||
313 | && $stderr =~ /revision 1\.(\d+)/s ) | ||||
314 | { | ||||
315 | $isLatest = ( $version >= $1 ); | ||||
316 | } | ||||
317 | |||||
318 | # otherwise we will have to resort to numRevisions to tell if | ||||
319 | # this is the latest rev, which is expensive. By returning false | ||||
320 | # for isLatest we will force a reload upstairs if the latest rev | ||||
321 | # is required. | ||||
322 | |||||
323 | if ($tmpfile) { | ||||
324 | $text = $this->readFile($tmpfile); | ||||
325 | for ( $tmpfile, $tmpRevFile ) { | ||||
326 | my $f = Foswiki::Sandbox::untaintUnchecked($_); | ||||
327 | unlink $f or warn "Could not delete $f: $!"; | ||||
328 | } | ||||
329 | } | ||||
330 | |||||
331 | return ( $text, $isLatest ); | ||||
332 | } | ||||
333 | |||||
334 | # implements Rcs::Handler | ||||
335 | sub getInfo { | ||||
336 | my ( $this, $version ) = @_; | ||||
337 | |||||
338 | my $numRevs = $this->_numRevisions() || 0; | ||||
339 | if ( ( $this->noCheckinPending() ) | ||||
340 | && ( !$version || $version > $numRevs ) ) | ||||
341 | { | ||||
342 | $version = $numRevs; | ||||
343 | } | ||||
344 | else { | ||||
345 | $version = $numRevs + 1 | ||||
346 | unless ( $version && $version <= $numRevs ); | ||||
347 | } | ||||
348 | my ( $rcsOut, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
349 | $Foswiki::cfg{RCS}{infoCmd}, | ||||
350 | REVISION => '1.' . $version, | ||||
351 | FILENAME => _encode( $this->{rcsFile} ) | ||||
352 | ); | ||||
353 | if ( !$exit ) { | ||||
354 | if ( $rcsOut =~ | ||||
355 | /^.*?date: ([^;]+); author: ([^;]*);[^\n]*\n([^\n]*)\n/s ) | ||||
356 | { | ||||
357 | require Foswiki::Time; | ||||
358 | my $info = { | ||||
359 | version => $version, | ||||
360 | date => Foswiki::Time::parseTime($1), | ||||
361 | author => $2, | ||||
362 | comment => $3, | ||||
363 | }; | ||||
364 | if ( $rcsOut =~ /revision 1.([0-9]*)/ ) { | ||||
365 | $info->{version} = $1; | ||||
366 | } | ||||
367 | return $info; | ||||
368 | } | ||||
369 | } | ||||
370 | |||||
371 | return $this->SUPER::getInfo($version); | ||||
372 | } | ||||
373 | |||||
374 | # implements Rcs::Handler | ||||
375 | sub _numRevisions { | ||||
376 | my $this = shift; | ||||
377 | |||||
378 | unless ( $this->revisionHistoryExists() ) { | ||||
379 | |||||
380 | # If there is no history, there can only be one. | ||||
381 | return 1 if $this->storedDataExists(); | ||||
382 | return 0; | ||||
383 | } | ||||
384 | |||||
385 | my ( $rcsOutput, $exit ) = | ||||
386 | Foswiki::Sandbox->sysCommand( $Foswiki::cfg{RCS}{histCmd}, | ||||
387 | FILENAME => _encode( $this->{rcsFile} ) ); | ||||
388 | if ($exit) { | ||||
389 | throw Error::Simple( 'RCS: ' | ||||
390 | . $Foswiki::cfg{RCS}{histCmd} . ' of ' | ||||
391 | . $this->hidePath( $this->{rcsFile} ) | ||||
392 | . ' failed: ' | ||||
393 | . $rcsOutput ); | ||||
394 | } | ||||
395 | if ( $rcsOutput =~ /head:\s+\d+\.(\d+)\n/ ) { | ||||
396 | return $1; | ||||
397 | } | ||||
398 | if ( $rcsOutput =~ /total revisions: (\d+)\n/ ) { | ||||
399 | return $1; | ||||
400 | } | ||||
401 | return 1; | ||||
402 | } | ||||
403 | |||||
404 | # implements Rcs::Handler | ||||
405 | # rev1 is the lower, rev2 is the higher revision | ||||
406 | sub revisionDiff { | ||||
407 | my ( $this, $rev1, $rev2, $contextLines ) = @_; | ||||
408 | my $tmp = ''; | ||||
409 | my $exit; | ||||
410 | if ( $rev1 eq '1' && $rev2 eq '1' ) { | ||||
411 | my $text = $this->getRevision(1); | ||||
412 | $tmp = "1a1\n"; | ||||
413 | foreach ( split( /\r?\n/, $text ) ) { | ||||
414 | $tmp = "$tmp> $_\n"; | ||||
415 | } | ||||
416 | } | ||||
417 | else { | ||||
418 | $contextLines = 3 unless defined($contextLines); | ||||
419 | ( $tmp, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
420 | $Foswiki::cfg{RCS}{diffCmd}, | ||||
421 | REVISION1 => '1.' . $rev1, | ||||
422 | REVISION2 => '1.' . $rev2, | ||||
423 | FILENAME => _encode( $this->{rcsFile} ), | ||||
424 | CONTEXT => $contextLines | ||||
425 | ); | ||||
426 | |||||
427 | # prevent diffing TOPICINFO | ||||
428 | $tmp =~ s/^.%META:TOPICINFO\{(.*)\}%\n//mg; | ||||
429 | |||||
430 | # comment out because we get a non-zero status for a good result! | ||||
431 | #if( $exit ) { | ||||
432 | # throw Error::Simple( 'RCS: '.$Foswiki::cfg{RCS}{diffCmd}. | ||||
433 | # ' failed: '.$! ); | ||||
434 | #} | ||||
435 | } | ||||
436 | |||||
437 | return parseRevisionDiff($tmp); | ||||
438 | } | ||||
439 | |||||
440 | =begin TML | ||||
441 | |||||
442 | ---++ StaticMethod parseRevisionDiff( $text ) -> \@diffArray | ||||
443 | |||||
444 | | Description: | parse the text into an array of diff cells | | ||||
445 | | #Description: | unlike Algorithm::Diff I concatinate lines of the same diffType that are sqential (this might be something that should be left up to the renderer) | | ||||
446 | | Parameter: =$text= | currently unified or rcsdiff format | | ||||
447 | | Return: =\@diffArray= | reference to an array of [ diffType, $right, $left ] | | ||||
448 | | TODO: | move into Rcs::Handler and add indirection in Store | | ||||
449 | |||||
450 | =cut | ||||
451 | |||||
452 | sub parseRevisionDiff { | ||||
453 | my ($text) = @_; | ||||
454 | |||||
455 | my ($diffFormat) = 'normal'; #or rcs, unified... | ||||
456 | my (@diffArray) = (); | ||||
457 | |||||
458 | $diffFormat = 'unified' if ( $text =~ /^---/s ); | ||||
459 | |||||
460 | $text =~ s/\r//go; # cut CR | ||||
461 | |||||
462 | my $lineNumber = 1; | ||||
463 | if ( $diffFormat eq 'unified' ) { | ||||
464 | foreach ( split( /\r?\n/, $text ) ) { | ||||
465 | if ( $lineNumber > 2 ) { #skip the first 2 lines (filenames) | ||||
466 | if (/@@ [-+]([0-9]+)([,0-9]+)? [-+]([0-9]+)(,[0-9]+)? @@/) { | ||||
467 | |||||
468 | #line number | ||||
469 | push @diffArray, [ 'l', $1, $3 ]; | ||||
470 | } | ||||
471 | elsif (/^\-(.*)$/) { | ||||
472 | push @diffArray, [ '-', $1, '' ]; | ||||
473 | } | ||||
474 | elsif (/^\+(.*)$/) { | ||||
475 | push @diffArray, [ '+', '', $1 ]; | ||||
476 | } | ||||
477 | else { | ||||
478 | s/^ (.*)$/$1/go; | ||||
479 | push @diffArray, [ 'u', $_, $_ ]; | ||||
480 | } | ||||
481 | } | ||||
482 | $lineNumber++; | ||||
483 | } | ||||
484 | } | ||||
485 | else { | ||||
486 | |||||
487 | #'normal' rcsdiff output | ||||
488 | foreach ( split( /\r?\n/, $text ) ) { | ||||
489 | if (/^([0-9]+)[0-9\,]*([acd])([0-9]+)/) { | ||||
490 | |||||
491 | #line number | ||||
492 | push @diffArray, [ 'l', $1, $3 ]; | ||||
493 | } | ||||
494 | elsif (/^< (.*)$/) { | ||||
495 | push @diffArray, [ '-', $1, '' ]; | ||||
496 | } | ||||
497 | elsif (/^> (.*)$/) { | ||||
498 | push @diffArray, [ '+', '', $1 ]; | ||||
499 | } | ||||
500 | else { | ||||
501 | |||||
502 | #push @diffArray, ['u', '', '']; | ||||
503 | } | ||||
504 | } | ||||
505 | } | ||||
506 | return \@diffArray; | ||||
507 | } | ||||
508 | |||||
509 | sub _lock { | ||||
510 | my $this = shift; | ||||
511 | |||||
512 | return unless $this->revisionHistoryExists(); | ||||
513 | |||||
514 | # Try and get a lock on the file | ||||
515 | my ( $rcsOutput, $exit ) = | ||||
516 | Foswiki::Sandbox->sysCommand( $Foswiki::cfg{RCS}{lockCmd}, | ||||
517 | FILENAME => _encode( $this->{file} ) ); | ||||
518 | |||||
519 | if ($exit) { | ||||
520 | |||||
521 | # if the lock has been set more than 24h ago, let's try to break it | ||||
522 | # and then retry. Should not happen unless in Cairo upgrade | ||||
523 | # scenarios - see Item2102 | ||||
524 | if ( ( time - ( stat( _encode( $this->{rcsFile} ) ) )[9] ) > 3600 ) { | ||||
525 | warn 'Automatic recovery: breaking lock for ' . $this->{file}; | ||||
526 | Foswiki::Sandbox->sysCommand( | ||||
527 | $Foswiki::cfg{RCS}{breaklockCmd}, | ||||
528 | FILENAME => _encode( $this->{file} ) | ||||
529 | ); | ||||
530 | ( $rcsOutput, $exit ) = | ||||
531 | Foswiki::Sandbox->sysCommand( $Foswiki::cfg{RCS}{lockCmd}, | ||||
532 | FILENAME => _encode( $this->{file} ) ); | ||||
533 | } | ||||
534 | if ($exit) { | ||||
535 | |||||
536 | # still no luck - bailing out | ||||
537 | $rcsOutput ||= ''; | ||||
538 | throw Error::Simple( 'RCS: ' | ||||
539 | . $Foswiki::cfg{RCS}{lockCmd} | ||||
540 | . ' failed: ' | ||||
541 | . $rcsOutput ); | ||||
542 | } | ||||
543 | } | ||||
544 | chmod( $Foswiki::cfg{Store}{filePermission}, _encode( $this->{file} ) ); | ||||
545 | } | ||||
546 | |||||
547 | # implements Rcs::Handler | ||||
548 | sub getRevisionAtTime { | ||||
549 | my ( $this, $date ) = @_; | ||||
550 | |||||
551 | unless ( $this->revisionHistoryExists() ) { | ||||
552 | return ( $date >= ( stat( _encode( $this->{file} ) ) )[9] ) ? 1 : undef; | ||||
553 | } | ||||
554 | |||||
555 | require Foswiki::Time; | ||||
556 | my $sdate = Foswiki::Time::formatTime( $date, '$rcs', 'gmtime' ); | ||||
557 | my ( $rcsOutput, $exit ) = Foswiki::Sandbox->sysCommand( | ||||
558 | $Foswiki::cfg{RCS}{rlogDateCmd}, | ||||
559 | DATE => $sdate, | ||||
560 | FILENAME => _encode( $this->{file} ) | ||||
561 | ); | ||||
562 | |||||
563 | my $version = undef; | ||||
564 | if ( $rcsOutput =~ m/revision \d+\.(\d+)/ ) { | ||||
565 | $version = $1; | ||||
566 | } | ||||
567 | |||||
568 | if ( $version && !$this->noCheckinPending() ) { | ||||
569 | |||||
570 | # Check the file date | ||||
571 | $version++ if ( $date >= ( stat( _encode( $this->{file} ) ) )[9] ); | ||||
572 | } | ||||
573 | return $version; | ||||
574 | } | ||||
575 | |||||
576 | 1 | 3µs | 1; | ||
577 | __END__ |