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