File Coverage

blib/lib/Rcs/Agent.pm
Criterion Covered Total %
statement 140 492 28.4
branch 41 256 16.0
condition 6 46 13.0
subroutine 17 35 48.5
pod 18 25 72.0
total 222 854 26.0


line stmt bran cond sub pod time code
1             # Rcs::Agent
2             #
3             # An RCS frobnicator
4             #
5             # $Id: Agent.pm,v 1.32 2007/08/20 16:39:56 nick Exp $
6              
7             package Rcs::Agent;
8              
9             # Be neutoric about syntax
10 9     9   7242 use strict;
  9         18  
  9         315  
11              
12             # These packages are part of the base perl system
13 9     9   42 use Carp;
  9         19  
  9         727  
14 9     9   45 use File::Basename;
  9         15  
  9         901  
15 9     9   9539 use File::stat;
  9         117847  
  9         97  
16 9     9   668 use Cwd;
  9         15  
  9         666  
17              
18             # These packages are from CPAN
19 9     9   8945 use String::ShellQuote;
  9         8497  
  9         764  
20 9     9   13770 use File::Temp;
  9         234444  
  9         1032  
21              
22             # Data::Dumper is used solely for debugging
23             # use Data::Dumper;
24              
25 9     9   80 use vars qw(@ISA @EXPORT_OK @EXPORT $VERSION $AUTOLOAD);
  9         19  
  9         69381  
26              
27             $VERSION = '1.05';
28              
29             1;
30              
31              
32             =head1 NAME
33              
34             Rcs::Agent - an RCS archive manipulation method library
35              
36             =head1 SYNOPSIS
37              
38             C
39              
40             =head1 DESCRIPTION
41              
42             C is a perl module for manipulating RCS archives. It provides
43             an object-oriented interface to the RCS commands C, C,
44             C and C, in addition to providing easy access to revision
45             information contained in the RCS archive file. A description of how RCS
46             works is beyond the scope of this document, or to put it simply, you need to
47             learn how to use RCS before using this perl interface to it.
48              
49             =head1 METHODS
50              
51             =head2 new
52              
53             The new() method is the C constructor, and is used both to
54             create new RCS archives files if they do not already exist, or manipulate
55             existing ones if they already exist in the specified location.
56              
57             Typically, new() would be called using the following parameters:
58              
59             $rcs = new Rcs::Agent ( file => "/data/src/foobar.c");
60              
61             The C parameter tells the module what the name of the work file is. This
62             is the only parameter which is absolutely necessary: if it is not supplied,
63             then new() will return undef and all subsequent method calls using the C<$rcs>
64             handle will fail.
65              
66             The C parameter can be used to specify the working directory of
67             the file, if for some reason the programmer decides not to specify it using
68             the C parameter. The example above could easily have been written:
69              
70             $rcs = new Rcs::Agent ( file => "foobar.c",
71             workdir => "/data/src");
72              
73             The C parameter specifies the location of the RCS archive. This is
74             normally designated as the "RCS/" directory off the working directory, but
75             there is no reason why C cannot be placed somewhere else if so
76             desired. If this parameter is not specified, then the module uses some
77             simplistic heuristics to determine the location of the RCS directory
78             automatically. If there is a directory off the working directory called "RCS/"
79             then the module will use that. If there is not, then it will use the
80             working directory.
81              
82             The C parameter specifies the RCS archive file suffix to use. On a
83             Unix or a Unix-lookalike system, this is usually ",v". There is normally no
84             need to change this parameter.
85              
86             The C parameter specifies the location of a directory which is
87             writable and which can by used by the L library to create temporary
88             files when necessary. While this defaults to "/tmp", it is strongly suggested
89             for security reasons that a different, application-specific temporary directory
90             be used.
91              
92             =cut
93              
94             ##
95             ## new
96             ##
97              
98             sub new {
99 7 50   7 1 146 my ($type) = shift if @_;
100 7   50     70 my $class = ref($type) || $type || "Rcs::Agent";
101 7         31 my %args = @_;
102 7         12 my ($tag);
103 7         34 my @tags = qw (file workdir rcsdir suffix tmpdir);
104              
105 7         30 my $self = {
106             version => $VERSION,
107             err => "",
108             };
109              
110 7         20 foreach $tag (@tags) {
111 35 100       103 $self->{$tag} = $args{$tag} if (defined ($args{$tag}));
112             }
113              
114             # Default suffix is ",v"
115 7 50       38 $self->{suffix} = ",v" unless (defined ($self->{suffix}));
116              
117             # don't continue unless a filename is supplied
118 7 50       29 return undef if (!defined ($self->{file}));
119              
120             # filename contains path separators?
121 7 100       42 if ($self->{file} =~ /\//) {
122 6         408 my $dir = dirname($self->{file});
123             # if pathname is absolute, then path => workdir, basename => file
124 6 50       37 if ($dir =~ /^\//) {
125 6         20 $self->{workdir} = $dir;
126 6         160 $self->{file} = basename($self->{file});
127             # otherwise append path to workdir, if it already exists.
128             } else {
129 0 0       0 $self->{workdir} = defined ($self->{workdir}) ? $self->{workdir} : "";
130 0         0 $self->{workdir} .= "/$dir";
131             }
132             }
133              
134 7 100       36 $self->{workdir} = "." unless (defined ($self->{workdir}));
135              
136             # don't continue unless the work directory actually exists
137 7 50       191 return undef unless (-d $self->{workdir});
138              
139             # trim trailing slashes off end of workdir
140 7         30 $self->{workdir} =~ s/(\/+)$//g;
141              
142             # Figure out correct rcsdir
143             #
144             # If rcsdir has been supplied, then use that.
145             # If rcsdir hasn't been supplied, then check for workdir/RCS/foo,v and workdir/foo,v in order
146              
147 7 50       27 unless (defined ($self->{rcsdir})) {
148             # if RCS/ directory exists and there's a version file in it, use that.
149 7 50 33     254 if (-d $self->{workdir}."/RCS" &&
    50          
150             -e $self->{workdir}."/RCS/".$self->{file}.$self->{suffix}) {
151 0         0 $self->{rcsdir} = $self->{workdir}."/RCS";
152              
153             # if the version file is in the workdir, use that...
154             } elsif (-e $self->{workdir}."/".$self->{file}.$self->{suffix}) {
155 0         0 $self->{rcsdir} = $self->{workdir};
156              
157             # there's no version file at all => use RCS/ dir if it exists, otherwise workdir
158             } else {
159 7 50       165 $self->{rcsdir} = (-d $self->{workdir}."/RCS") ?
160             $self->{workdir}."/RCS" : $self->{workdir};
161             }
162             }
163              
164 7         28 $self->{rcsdir} =~ s/(\/+)$//g;
165              
166 7         30 $self->{rcsfile} = $self->{rcsdir}."/".$self->{file}.$self->{suffix};
167              
168 7 50       53 $self->{hpux} = 1 if $^O eq 'hpux';
169              
170 7         28 bless $self, $class;
171              
172 7         210 return $self;
173             }
174              
175              
176             =head2 err
177              
178             The err() method returns whatever is currently in the error buffer. Whenever
179             any method in this library fails for some reason, the method will put a message
180             into the error buffer and then return undef to the calling function. This
181             method is used to access the error message. It takes no parameters and returns
182             a scalar text string, which may be zero length if there is no current error.
183              
184             =cut
185              
186             ##
187             ## err
188             ##
189              
190             sub err {
191 0     0 1 0 my $self = shift;
192              
193 0         0 $self->{err};
194             }
195              
196              
197             =head2 head
198              
199             The head() method returns the revision number of the top of the RCS tree.
200              
201             =cut
202              
203             ##
204             ## head
205             ##
206              
207             sub head {
208 0     0 1 0 my $self = shift;
209              
210 0         0 $self->{err} = "";
211              
212 0 0       0 $self->parse || return undef;
213              
214 0         0 return $self->{head};
215             }
216              
217              
218             =head2 timestamp
219              
220             The timestamp() method returns the mtime timestamp of the RCS archive file
221             in C time format (i.e. seconds since the epoch). For convenience, this
222             value can also be accessed by referring to $rcs->{mtime}.
223              
224             =cut
225              
226             ##
227             ## timestamp
228             ##
229              
230             sub timestamp {
231 0     0 1 0 my $self = shift;
232              
233 0         0 $self->{err} = "";
234              
235 0         0 my $sb = stat($self->{rcsfile});
236 0 0       0 unless (defined($sb)) {
237 0         0 $self->{err} = "couldn't open $self->{rcsfile}: $!";
238 0         0 return undef;
239             }
240              
241 0         0 $self->{mtime} = $sb->mtime;
242              
243 0         0 $sb->mtime;
244             }
245              
246              
247             =head2 archived
248              
249             The archived() method indicates whether the file in question is already
250             in RCS control. It is a quick and dirty function which simply tests whether
251             the file has a readable RCS archive file. It returns 1 or 0, depending on
252             whether this test is found to be true or not.
253              
254             =cut
255              
256             ##
257             ## archived
258             ##
259              
260             sub archived {
261 6     6 1 38 my $self = shift;
262              
263 6 50       110 -r $self->{rcsfile} ? 1 : 0;
264             }
265              
266              
267             ##
268             ## parse
269             ##
270             ## parses the RCS archive file. This is an internal-only function. Please ignore it.
271             ##
272             ## returns: 1 if the file already has a readable and parsable RCS archive file
273             ## undef otherwise, putting flag in error buffer
274             ##
275              
276             sub parse {
277 5     5 0 35 my $self = shift;
278 5         27 my %args = @_;
279 5         31 my %branches;
280              
281 5         25 $self->{err} = "";
282              
283             # only parse file if not parsed previously and rcs file has not been modified
284 5 50       20 if ($self->{parsed}) {
285 0         0 my $mtime = $self->{mtime};
286 0 0       0 return 1 if ($mtime == $self->timestamp);
287             }
288              
289 5 50       26 if (!$self->archived) {
290 5         20 $self->{err} = "RCS archive file not found";
291 5         25 return undef;
292             }
293              
294             # This is to prevent recursion
295 0 0       0 if (defined ($self->{parsing})) {
296 0         0 return 1;
297             }
298 0         0 $self->{parsing} = 1;
299              
300             # First, we need to delete a whole bunch of stuff if it's already
301             # defined from previous parsing attempts.
302 0         0 foreach my $tag (qw (access revisions head symbols strict)) {
303 0 0       0 delete $self->{$tag} if defined ($self->{$tag});
304             }
305              
306 0 0       0 unless (open (INPUT, $self->{rcsfile})) {
307 0         0 $self->{err} = "couldn't open $self->{rcsfile}: $!";
308 0         0 return undef;
309             }
310              
311             # The preamble contains information about the archive. We slurp it in as a single
312             # paragraph
313              
314 0         0 my $oldseparator = $/;
315 0         0 $/ = "";
316              
317 0         0 my $data = ;
318 0         0 $data =~ s/[\n\r\s]+/ /g;
319 0         0 my @tokens = split (/\s*;\s*/, $data);
320              
321 0         0 $self->{strict} = 0;
322              
323             # For the moment, we only parse head, locks, symbols and strict.
324 0         0 foreach my $token (@tokens) {
325 0 0       0 if ($token =~ /^access/) {
326 0         0 my @access = split (/ /, $token); shift @access;
  0         0  
327 0 0       0 $self->{access} = \@access if @access;
328 0         0 next;
329             }
330              
331 0 0       0 if ($token =~ /^head\s+([\d\.]+)/) {
332 0         0 $self->{head} = $1;
333 0         0 next;
334             }
335              
336 0 0       0 if ($token =~ /^strict/) {
337 0         0 $self->{strict} = 1;
338 0         0 next;
339             }
340              
341 0 0       0 if ($token =~ /^symbols/) {
342 0         0 my @symtokens = split (/ /, $token); shift @symtokens;
  0         0  
343 0         0 foreach my $tag (@symtokens) {
344 0 0       0 next unless ($tag =~ /(.*):(.*)/);
345 0         0 $self->{symbols}->{$1} = $2;
346             }
347 0         0 next;
348             }
349              
350 0 0       0 if ($token =~ /^locks/) {
351 0         0 my @symtokens = split (/ /, $token); shift @symtokens;
  0         0  
352 0         0 foreach my $tag (@symtokens) {
353 0 0       0 next unless ($tag =~ /(.*):(.*)/);
354 0         0 $self->{revisions}->{$2}->{locker} = $1;
355             }
356 0         0 next;
357             }
358             }
359              
360             # deal with the individual revision entry
361 0         0 while ($data = ) {
362 0         0 my ($nextrev);
363 0         0 $data =~ s/[\n\r\s]+/ /g;
364 0 0       0 next unless ($data =~ /([\d\.]+)\s+(.*)/);
365 0         0 my $revision = $1; $data = $2;
  0         0  
366              
367 0 0       0 delete $branches{$revision} if ($branches{$revision});
368              
369 0         0 @tokens = split (/\s*;\s*/, $data);
370              
371 0         0 foreach my $token (@tokens) {
372 0 0       0 if ($token =~ /^next\s+([\d\.]+)/) {
373 0         0 my ($up, $down) = qw (parent child);
374 0         0 $nextrev = $1;
375              
376             # set up doubly linked list so that each revision knows what's next
377             # to it. For some reason which I don't see, the direction of the
378             # next revisions in the head line is in the opposite direction to
379             # revisions in the branches. If you don't understand what I mean
380             # here, take a look at the direction of the arrows in the ascii art
381             # in the man page for rcsfile(5). This means that we have to
382             # invert child and parent in this context.
383              
384 0 0       0 if ($revision =~ /^\d+\.\d+$/) { # i.e. head branch
385 0         0 ($up, $down) = qw (child parent); # i.e. arseways
386             }
387              
388 0         0 $self->{revisions}->{$revision}->{$down} = $nextrev;
389 0         0 $self->{revisions}->{$nextrev}->{$up} = $revision;
390              
391             # we need a place to put in initial revision numbers. This is as
392             # good as any
393 0         0 $self->{revisions}->{$revision}->{linesadded} = 0;
394 0         0 $self->{revisions}->{$revision}->{linesdeleted} = 0;
395 0         0 next;
396             }
397              
398 0 0       0 if ($token =~ /^author\s+(\S+)/) {
399 0         0 $self->{revisions}->{$revision}->{author} = $1;
400 0         0 next;
401             }
402              
403 0 0       0 if ($token =~ /^state\s+(\S+)/) {
404 0         0 $self->{revisions}->{$revision}->{state} = $1;
405 0         0 next;
406             }
407              
408 0 0       0 if ($token =~ /^date\s+(\S+)/) {
409 0         0 $self->{revisions}->{$revision}->{date} = $1;
410 0         0 next;
411             }
412              
413 0 0       0 if ($token =~ /^branches/) {
414 0         0 my @symtokens = split (/ /, $token); shift @symtokens;
  0         0  
415 0 0       0 next unless ($#symtokens >= 0);
416 0         0 push (@{$self->{revisions}->{$revision}->{branches}}, @symtokens);
  0         0  
417 0         0 foreach my $symtoken (@symtokens) {
418 0         0 $self->{revisions}->{$symtoken}->{parent} = $revision;
419 0         0 $branches{$symtoken} = 1;
420             }
421              
422 0         0 next;
423             }
424             }
425              
426 0 0 0     0 last unless ($nextrev || scalar(%branches));
427             }
428              
429             # Now we need to go back to line-by-line processing mode
430 0         0 $/ = $oldseparator;
431              
432 0         0 my $intext = 0;
433              
434             # pull out the archive description
435 0         0 while (chomp($data = )) {
436              
437 0 0 0     0 if (!$intext && $data =~ /^desc/) {
438 0         0 $intext = 1;
439 0         0 next;
440             }
441              
442             # first line of data text
443 0 0 0     0 if ($intext == 1 && $data =~ /^\@/) {
444 0         0 $data =~ s/^\@//;
445 0         0 $intext++;
446             }
447            
448             # end of text input is marked by @ at EOL
449 0 0 0     0 if ($intext && $data =~ /[^\@]*\@$/) {
450 0         0 $data =~ s/\@$//; # remove trailing
451 0 0       0 push @{$self->{desc}}, $data if ($data);
  0         0  
452 0         0 $intext = 0;
453 0         0 last;
454             }
455              
456 0 0       0 push @{$self->{desc}}, $data if ($intext);
  0         0  
457             }
458              
459 0         0 my $texttype = ""; my $revision = "";
  0         0  
460 0         0 my $donelog = 0; my $donetext = 0;
  0         0  
461              
462             # finally we reach the revision info
463 0         0 while ($data = ) {
464 0         0 my $added = 0;
465 0         0 my $deleted = 0;
466              
467 0         0 chomp ($data);
468              
469 0 0 0     0 next if (!$revision && $data =~ /^\s*$/);
470              
471 0         0 $revision = $data;
472              
473 0         0 $data = ;
474 0 0       0 if ($data =~ /^log/) {
475 0         0 chomp ($data = );
476 0         0 $data =~ s/^\@//;
477 0 0       0 push @{$self->{revisions}->{$revision}->{log}}, log_unquote($data) if ($data =~ /./);
  0         0  
478 0         0 while (chomp($data = )) {
479 0 0       0 if ($data =~ /(|[^\@])\@$/) {
480 0         0 $data =~ s/\@$//;
481 0 0       0 push @{$self->{revisions}->{$revision}->{log}}, log_unquote($data) if ($data);
  0         0  
482 0         0 last;
483             }
484 0         0 push @{$self->{revisions}->{$revision}->{log}}, log_unquote($data);
  0         0  
485             }
486             }
487              
488 0         0 $data = ;
489 0 0       0 if ($data =~ /^text/) {
490 0         0 $data = ;
491 0         0 $data =~ s/^\@//;
492              
493 0         0 TEXT: while ($data) {
494 0         0 chomp($data);
495              
496 0 0       0 if ($data =~ /(|[^\@])\@$/) {
497 0         0 $data =~ s/\@$//;
498 0         0 last TEXT;
499             }
500              
501 0 0       0 if ($revision eq $self->{head}) {
    0          
    0          
502 0         0 $data = ;
503 0         0 next TEXT;
504             } elsif ($data =~ /^d\d+\s+(\d+)$/) {
505 0         0 $deleted += $1;
506             } elsif ($data =~ /^a\d+\s+(\d+)$/) {
507 0         0 my $localadd = $1;
508 0         0 $added += $localadd;
509 0         0 for (my $i = 0; $i <$localadd; $i++) {
510 0         0 $data = ;
511 0 0       0 redo TEXT if ($data =~ /(|[^\@])\@$/); # urk, spaghetti
512             }
513             }
514 0         0 $data = ;
515             };
516              
517 0 0       0 if ($revision eq $self->{head}) {
518 0         0 $revision = "";
519 0         0 next;
520             }
521              
522             # Due to the way that branches are managed, the head branch always lists
523             # diffs relative to the child revision, while sub-branches always list them
524             # relative to the current revision. See the explanation above for more
525             # details
526              
527 0         0 my ($diffrecip);
528              
529 0 0       0 if ($revision =~ /^\d+\.\d+$/) {
530 0         0 $diffrecip = $self->child(revision => $revision);
531 0         0 $self->{revisions}->{$diffrecip}->{linesadded} = $deleted;
532 0         0 $self->{revisions}->{$diffrecip}->{linesdeleted} = $added;
533             } else {
534 0         0 $diffrecip = $revision;
535 0         0 $self->{revisions}->{$diffrecip}->{linesadded} = $added;
536 0         0 $self->{revisions}->{$diffrecip}->{linesdeleted} = $deleted;
537             }
538              
539 0         0 $revision = "";
540             }
541             }
542              
543 0         0 close (INPUT);
544              
545 0         0 delete ($self->{parsing});
546 0         0 $self->timestamp;
547 0         0 $self->{parsed} = 1;
548              
549 0         0 1;
550             }
551              
552              
553             =head2 diff
554              
555             The diff() method returns a list of differences between one version of the
556             RCS archive and another. If neither the C nor C
557             parameters are passed to this method, then it will return the list of diffs
558             between the current working file and the head version. If C alone
559             is specified, then it will return a list of diffs between the current working
560             file and the specified version, and if both parameters are supplied, then it
561             will provide a list of diffs between the version specified in C
562             and C. The method will return undef if either of the revisions
563             specified don't exist.
564              
565             It is also possible to specify the revisions using symbolic names or tags
566             instead of version numbers.
567              
568             The format of the diff output can be controlled using the C parameter.
569             If this is set to C, then it will produce context diffs; if it is set
570             to C, then unified diffs will be returned if the system's version of
571             diff(1) supports unified diffs. If the format is not specified, or if it is
572             set to C, then diff() will return a list of diffs in classic format.
573              
574             =cut
575              
576             ##
577             ## diff
578             ##
579              
580             sub diff {
581 2     2 1 12 my $self = shift;
582 2         6 my %args = @_;
583 2         2 my ($exitcode, $stdout, $stderr);
584              
585 2         6 $self->{err} = "";
586              
587 2 50       6 $self->parse || return undef;
588              
589 0         0 my $cmdargs = "";
590              
591 0         0 my %outputformat = (
592             "context" => "-c",
593             "unified" => "-u",
594             "old" => "",
595             );
596              
597 0 0       0 unless (defined $args{format}) {
598 0         0 $args{format} = "old";
599             }
600              
601 0         0 my $validformats = join('|', keys %outputformat);
602 0 0       0 if ($args{format} =~ /^($validformats)$/i) {
603 0         0 $cmdargs = $outputformat{lc($1)};
604             } else {
605 0         0 $cmdargs = $outputformat{"old"};
606             }
607              
608 0         0 foreach my $rev ($args{revision1}, $args{revision2}) {
609 0 0       0 next unless (defined ($rev));
610             # We can either have a revision or a tag here
611 0 0 0     0 unless (defined ($self->{revisions}->{$rev}) ||
612             defined ($self->{symbols}->{$rev})) {
613 0         0 $self->{err} = "invalid revision number / tag supplied";
614 0         0 return undef;
615             }
616              
617 0         0 $cmdargs .= " -r".shell_quote("$rev");
618             }
619              
620 0 0       0 my $q = $self->{hpux} ? '' : '-q';
621 0         0 my $command = "rcsdiff $q $cmdargs ".shell_quote($self->{rcsfile});
622              
623 0         0 ($exitcode, $stdout, $stderr) = $self->pipestderrout(command => $command, dir => $self->{workdir});
624              
625 0 0       0 if ($exitcode > 1) {
626 0         0 $self->{err} = join("\n", @{$stderr})."\n";
  0         0  
627 0         0 return undef;
628             }
629              
630 0         0 return $stdout;
631             }
632              
633              
634             =head2 checkin
635              
636             The checkin() method allows the programmer to check a version of the file into
637             the RCS archive. By default, the revision will be inserted at the head of the
638             revision tree, unless the revision is specified using the C parameter.
639              
640             A comment can be added to the revision's log using the I parameter. If
641             no comment or a blank comment is specified, then the revision is logged with the
642             text "*** empty log message ***", as happens when using the RCS C program.
643              
644             The revision may be tagged with a symbolic name using the I parameter.
645             If the I parameter is set to "yes" then the symbolic name will override
646             any previous assignment of the symbolic name.
647              
648             If the programmer wishes to check the version out after check-in, then the
649             C parameter should be set to "yes". This is useful if the programmer
650             wishes to keep a working copy of the file outside the archive. If checkout is
651             disabled, then the working copy of the file is deleted on check-in, which may
652             not suit all purposes. By default, this option is turned on.
653              
654             In addition, the programmer may wish to check out and lock the revision
655             immediately after checkin. This can be accomplished setting the C
656             parameter to "yes".
657              
658             These last two options correspond to the I<-u> and I<-l> options in C
659             respectively.
660              
661             The checkin() method will return the numeric value 1 on success and undef on
662             failure. As with all of these methods, in the event of the method returning
663             undef, a failure message will be logged into the error buffer.
664              
665             =cut
666              
667             ##
668             ## checkin
669             ##
670              
671             sub checkin {
672 1     1 1 6 my $self = shift;
673 1         4 my %args = @_;
674 1         3 my $cmdargs = "";
675 1         7 my ($exitcode, $stdout, $stderr);
676              
677 1         7 $self->{err} = "";
678              
679 1 50 33     7 $args{log} = "*** empty log message ***" unless (defined ($args{log}) && $args{log} =~ /\S/);
680              
681 1         27 $cmdargs .= "-m".shell_quote($args{log});
682              
683             # Added 'tag' argument to checkin so we can tag the revision checked in
684 1 50       50 if ( defined $args{tag} ) {
685 0 0 0     0 $cmdargs .= ( defined($args{force}) and istrue($args{force}) ) ? " -N" : " -n";
686 0         0 $cmdargs .= shell_quote($args{tag});
687             }
688              
689 1 50       4 my $lock = defined ($args{lock}) ? istrue ($args{lock}) : 0;
690 1 50 33     8 my $checkout = (defined ($args{checkout}) || $lock) ? istrue ($args{checkout}) : 1;
691              
692 1 50       12 if ($lock) {
    50          
693 0         0 $cmdargs .= " -l";
694             } elsif ($checkout) {
695 1         2 $cmdargs .= " -u";
696             }
697              
698 1 50       4 if (defined ($args{revision})) {
699 0 0       0 unless ($args{revision} =~ /^\d[\d\.]*\d$/) {
700 0         0 $self->{err} = "incorrect revision format";
701 0         0 return undef;
702             }
703 0 0       0 if (defined ($self->{revisions}->{$args{revision}})) {
704 0         0 $self->{err} = "specified revision already exists";
705 0         0 return undef;
706             }
707 0         0 $cmdargs .= " -r".shell_quote($args{revision});
708             }
709              
710 1         6 my $command = "ci $cmdargs ".shell_quote($self->{rcsfile});
711              
712 1         28 ($exitcode, $stdout, $stderr) = $self->pipestderrout(command => $command, dir => $self->{workdir});
713              
714 1 50       16 if ($exitcode > 0) {
715 1         4 $self->{err} = join("\n", @{$stderr})."\n";
  1         26  
716 1         54 return undef;
717             }
718              
719 0         0 return 1;
720             }
721              
722              
723             =head2 checkout
724              
725             The checkout() method allows the programmer to check a version of the file
726             out of the RCS archive. By default, if no revision is specified using the
727             C parameter, then the head revision will be checked out. It is
728             possible to specify the revisions using symbolic names or tags instead of
729             version numbers when checking out revisions.
730              
731             The programmer may put a lock on the revision being checked out by setting
732             the C parameter to be "yes".
733              
734             If there is a version of the archive already locked, or if the working file
735             is writable, the check-out procedure will normally fail. This behaviour is
736             to prevent the programmer from accidentally over-writing the work of another
737             user who may also be editing a revision of the file. Checkouts can be forced
738             by setting the C parameter to be "yes"; this option should not be
739             used unless the operator is certain that no damage will be done.
740              
741             The checkout() method will return the numeric value 1 on success and undef on
742             failure.
743              
744             =cut
745              
746             ##
747             ## checkout
748             ##
749              
750             sub checkout {
751 0     0 1 0 my $self = shift;
752 0         0 my %args = @_;
753 0         0 my $cmdargs = "";
754 0         0 my ($exitcode, $stdout, $stderr);
755              
756 0         0 $self->{err} = "";
757              
758 0 0       0 $self->parse || return undef;
759              
760 0 0 0     0 $cmdargs .= " -l" if (defined ($args{lock}) && istrue ($args{lock}));
761              
762             # HP-UX co does not have -f option, so just delete the file
763 0 0       0 if ( $self->{hpux} ) {
764 0 0 0     0 if (defined($args{force}) and istrue($args{force})) {
765 0         0 unlink "$self->{workdir}/$self->{file}";
766             }
767             } else {
768 0 0 0     0 $cmdargs .= " -f" if (defined ($args{force}) && istrue ($args{force}));
769             }
770              
771 0 0       0 if (defined ($args{revision})) {
772 0 0       0 $self->rexists (revision => $args{revision}) || return undef;
773 0         0 $cmdargs .= " -r".shell_quote($args{revision});
774             }
775              
776 0         0 my $command = "co $cmdargs ".shell_quote($self->{rcsfile});
777              
778 0         0 ($exitcode, $stdout, $stderr) = $self->pipestderrout(command => $command, dir => $self->{workdir});
779              
780 0 0       0 if ($exitcode > 0) {
781 0         0 $self->{err} = join("\n", @{$stderr})."\n";
  0         0  
782 0         0 return undef;
783             }
784              
785 0         0 return 1;
786             }
787              
788              
789             =head2 lock
790              
791             The lock() method permits the operator to lock a specific revision in the
792             RCS archive without actually checking it out. By default, if no revision
793             is specified using the C parameter, then the head revisision will
794             be locked. It is possible to specify the revisions using symbolic names or
795             tags instead of version numbers when checking out revisions.
796              
797             If the specified revision in the archive is already locked, then this method
798             will fail.
799              
800             The checkout() method will return the numeric value 1 on success and undef on
801             failure.
802              
803             =cut
804              
805             ##
806             ## lock
807             ##
808             ## Locks the specified revision in the archive
809             ##
810             ## parameters:
811             ## [revision] the revision number to lock
812             ## [lock] 1 => lock, 0 => unlock [default: lock]
813             ##
814              
815             sub lock {
816 1     1 1 7 my $self = shift;
817 1         4 my %args = @_;
818            
819 1         2 my $cmdargs = "";
820 1         2 my ($exitcode, $stdout, $stderr, $lockcmd);
821              
822 1         6 $self->{err} = "";
823              
824 1 50       4 $self->parse || return undef;
825              
826 0 0       0 if (defined ($args{lock})) {
827 0 0       0 $lockcmd = istrue ($args{lock}) ? "l" : "u";
828             } else {
829 0         0 $lockcmd = "l";
830             }
831              
832 0         0 $cmdargs .= " -$lockcmd";
833            
834 0 0       0 if (defined ($args{revision})) {
835 0 0       0 $self->rexists (revision => $args{revision}) || return undef;
836 0         0 $cmdargs .= shell_quote($args{revision});
837             }
838              
839 0         0 my $command = "rcs $cmdargs ".shell_quote($self->{rcsfile});
840              
841 0         0 ($exitcode, $stdout, $stderr) = $self->pipestderrout(command => $command, dir => $self->{workdir});
842              
843 0 0       0 if ($exitcode > 0) {
844 0         0 $self->{err} = join("\n", @{$stderr})."\n";
  0         0  
845 0         0 return undef;
846             }
847              
848 0         0 return 1;
849             }
850              
851              
852             =head2 unlock
853              
854             The unlock() method performs the exact opposite as the lock() method: it
855             unlocks the specified revision in the archive.
856              
857             If the specified revision in the archive is already unlocked, then this method
858             will fail.
859              
860             The unlock() method will return the numeric value 1 on success and undef on
861             failure.
862              
863             =cut
864              
865             ##
866             ## unlock
867             ##
868              
869             sub unlock {
870 0     0 1 0 my $self = shift;
871              
872 0         0 $self->lock (lock => 0, @_);
873             }
874              
875              
876             =head2 initialize
877              
878             The initialize() method is used to create and initialize an RCS archive for
879             the working file if none existed previously.
880              
881             The archive description can be specified using the "description" parameter.
882              
883             If RCS version 5.7 or higher is installed on the system, the archive can be
884             initialized to be binary safe by setting the "binary" parameter. Note that
885             rcsmerge may not work properly on archives with binary data, and also that
886             if there is a string in the binary file which matches an RCS keyword (i.e.
887             \$Id\$, \$Log\$, etc), RCS may attempt to replace it with the its
888             corresponding expanded value on checkout which may corrupt your binary file.
889             See L for more details both of these issues.
890              
891             The initialize() method returns the numeric value 1 on success and undef on
892             failure.
893              
894             =cut
895              
896             ##
897             ## initialize
898             ##
899              
900             sub initialize {
901 1     1 1 6 my $self = shift;
902 1         3 my %args = @_;
903              
904 1         2 my $cmdargs = "";
905 1         1 my ($exitcode, $stdout, $stderr, $lockcmd);
906              
907 1 50       4 if ($self->archived) {
908 0         0 $self->{err} = "RCS archive already exists";
909 0         0 return undef;
910             }
911              
912 1 50 33     9 $args{description} = "" unless (defined ($args{description}) && $args{description} =~ /\S/);
913              
914 1 50       3 $cmdargs .= " -kb" if ($args{binary});
915 1 50       16 $cmdargs .= " -t-".shell_quote($args{description}) if ($args{description});
916              
917 1         46 my $command = "rcs -i $cmdargs ".shell_quote($self->{rcsfile});
918              
919 1         28 ($exitcode, $stdout, $stderr) = $self->pipestderrout(command => $command, dir => $self->{workdir});
920              
921 1 50       12 if ($exitcode > 0) {
922 1         5 $self->{err} = join("\n", @{$stderr})."\n";
  1         20  
923 1         41 return undef;
924             }
925              
926 0         0 return $self->parse;
927             }
928              
929              
930             =head2 rexists
931              
932             The rexists() method checks to make sure that the revision specified in the
933             parameter list actually exists in the RCS archive. If this is the case,
934             then the revision number will be returned. If it does not exist, or some
935             other error is detected, then undef is returned, and an error is left in the
936             error buffer.
937              
938             =cut
939              
940             ##
941             ## rexists
942             ##
943              
944             sub rexists {
945 0     0 1 0 my $self = shift;
946 0         0 my %args = @_;
947 0         0 my $revision;
948              
949 0         0 $self->{err} = "";
950              
951 0 0       0 $self->parse || return undef;
952              
953 0 0       0 unless (defined ($args{revision})) {
954 0         0 $self->{err} = "revision parameter not defined";
955 0         0 return undef;
956             }
957              
958 0         0 $revision = $self->symbol_lookup(symbol => $args{revision});
959              
960 0 0       0 unless (defined($self->{revisions})) {
961 0         0 $self->{err} = "revision tree does not exist - RCS archive not yet set up";
962 0         0 return undef;
963             }
964              
965 0 0       0 if (defined ($self->{revisions}->{$revision})){
966 0         0 return $revision;
967             }
968              
969 0         0 $self->{err} = "revision not found in RCS archive";
970 0         0 return undef;
971             }
972              
973             =head2 parent
974              
975             The parent() method returns the previous revision relative to the revision
976             specified in the parameter list, or undef if it does not exist:
977              
978             In the following example, $parent might be assigned the value '1.1'.
979              
980             my $parent = $rcs->parent (revision => '1.2');
981              
982             When dealing with branches, the real parent branch is returned, and not the
983             virtual branch fork revision. So, for example, the following code sets the
984             value of $parent to be '1.5' rather than '1.5.3':
985              
986             my $parent = $rcs->parent (revision => '1.5.3.1');
987              
988             If the I parameter is omitted, the revision defaults to the head
989             revision.
990              
991             =cut
992              
993             ##
994             ## parent
995             ##
996              
997             sub parent {
998 0     0 1 0 my $self = shift;
999 0         0 my %args = @_;
1000              
1001 0         0 $self->{err} = "";
1002              
1003 0 0       0 $self->parse || return undef;
1004              
1005 0 0       0 my $revision = defined ($args{revision}) ? $self->symbol_lookup(symbol => $args{revision}) : $self->{head};
1006              
1007 0 0       0 $self->rexists (revision => $revision) || return undef;
1008              
1009 0         0 return $self->{revisions}->{$revision}->{parent};
1010             }
1011              
1012              
1013             =head2 child
1014              
1015             Similar to parent(), child() returns the next revision relative to the
1016             revision specified in the parameter list, or undef if it does not exist.
1017              
1018             =cut
1019              
1020             ##
1021             ## child
1022             ##
1023              
1024             sub child {
1025 0     0 1 0 my $self = shift;
1026 0         0 my %args = @_;
1027              
1028 0         0 $self->{err} = "";
1029              
1030 0 0       0 $self->parse || return undef;
1031              
1032 0 0       0 my $revision = defined ($args{revision}) ? $self->symbol_lookup(symbol => $args{revision}) : $self->{head};
1033              
1034 0 0       0 $self->rexists (revision => $revision) || return undef;
1035              
1036 0         0 return $self->{revisions}->{$revision}->{child};
1037             }
1038              
1039              
1040             =head2 revisions
1041              
1042             The revisions() method returns a reference to an array containing the names
1043             of all of the revisions listed in the RCS archive.
1044              
1045             =cut
1046              
1047             ##
1048             ## revisions
1049             ##
1050              
1051             sub revisions {
1052 0     0 1 0 my $self = shift;
1053 0         0 my %args = @_;
1054              
1055             # FIXME: we need to do something about branches here
1056              
1057 0         0 $self->{err} = "";
1058              
1059 0 0       0 $self->parse || return undef;
1060              
1061 0         0 my @array = keys (%{$self->{revisions}});
  0         0  
1062              
1063 0         0 \@array;
1064             }
1065              
1066              
1067             =head2 symbols
1068              
1069             The symbols() method returns a reference to an array containing the names
1070             of all of the symbolic names listed in the RCS archive.
1071              
1072             =cut
1073              
1074             ##
1075             ## symbols
1076             ##
1077              
1078             sub symbols {
1079 0     0 1 0 my $self = shift;
1080 0         0 my %args = @_;
1081              
1082 0         0 $self->{err} = "";
1083              
1084 0 0       0 $self->parse || return undef;
1085              
1086 0         0 my @array = keys (%{$self->{symbols}});
  0         0  
1087              
1088 0         0 \@array;
1089             }
1090              
1091             =head2 access
1092              
1093             The access() method returns a reference to an array containing the names
1094             of all of the logins who have access to lock the RCS file, or undef
1095             if it is an empty list.
1096              
1097             =cut
1098              
1099             ##
1100             ## access
1101             ##
1102              
1103             # Added access method to return access list of rcs file
1104             sub access {
1105 0     0 1 0 my $self = shift;
1106 0         0 my %args = @_;
1107              
1108 0         0 $self->{err} = "";
1109              
1110 0 0       0 $self->parse || return undef;
1111              
1112 0 0       0 return unless exists $self->{access};
1113              
1114 0 0       0 my @array = @{$self->{access}} or return;
  0         0  
1115              
1116 0         0 \@array;
1117             }
1118              
1119             =head2 description
1120              
1121             description() is used to read or write the archive description. This is the
1122             text which is logged in the RCS archive using the "-t-" parameter. If the
1123             "description" parameter is set in the argument list, then the description in
1124             the archive file is set to the value specified.
1125              
1126             my $description = $rcs->description ();
1127              
1128             In this code, the $description variable will be set to the archive's
1129             description field, if it exists.
1130              
1131             $rcs->description (description => 'Main source file');
1132              
1133             In this code snippet, the RCS archive description is set to be be the value
1134             "Main source file".
1135              
1136             =cut
1137              
1138             ##
1139             ## description
1140             ##
1141              
1142             sub description {
1143 1     1 1 9 my $self = shift;
1144 1         4 my %args = @_;
1145              
1146 1         8 $self->{err} = "";
1147              
1148 1 50       16 $self->parse || return undef;
1149              
1150 0 0       0 if ($args{description}) {
1151 0         0 my ($exitcode, $stdout, $stderr);
1152 0         0 my $cmdargs .= " -t-".shell_quote($args{description});
1153 0         0 my $command = "rcs -q $cmdargs ".shell_quote($self->{rcsfile});
1154            
1155 0         0 ($exitcode, $stdout, $stderr) = $self->pipestderrout(command => $command, dir => $self->{workdir});
1156            
1157 0 0       0 if ($exitcode > 1) {
1158 0         0 $self->{err} = join("\n", @{$stderr})."\n";
  0         0  
1159 0         0 return undef;
1160             }
1161 0         0 $self->{desc} = $args{description};
1162             }
1163              
1164 0         0 return $self->{desc};
1165             }
1166              
1167             =head2 locked, locker, state, author, date, log
1168              
1169             These methods return the RCS archive data specified by the method name. If
1170             the "revision" parameter is given, then the method will return data relevant
1171             to the specified revision. Otherwise, the method will return data relevant
1172             to the head revision. All of the methods except for "log" return a scalar
1173             value. "log" returns a reference to an array of scalars, each of which
1174             corresponds to a line of the log message for the specified revision.
1175              
1176             The locked() method is the same as locker(), and is included to allow more
1177             readable code such as
1178              
1179             if ($rcs->locked(revision => "1.3")) {
1180            
1181             } else {
1182            
1183             }
1184              
1185             As another example, the following line of code will return the author of
1186             revision 1.2 of the current RCS object:
1187              
1188             my $author = $rcs->author(revision => "1.3");
1189              
1190             If the data for the specified revision does not exist, then the method will
1191             return undef.
1192              
1193             =cut
1194              
1195             ##
1196             ## AUTOLOAD
1197             ##
1198              
1199             sub AUTOLOAD {
1200 0     0   0 my $self = shift;
1201 0         0 my ($method) = $AUTOLOAD;
1202 0         0 my %args = @_;
1203 0         0 my $revision;
1204              
1205 0         0 $method =~ s/^.*:://;
1206              
1207 0 0       0 ($method =~ /^(access|locked|locker|state|author|date|log)$/) ||
1208             confess ("Can't locate object method \"$method\"");
1209              
1210 0 0       0 $method = "locker" if ($method eq "locked");
1211              
1212 0         0 $self->{err} = "";
1213              
1214 0 0       0 $self->parse || return undef;
1215              
1216 0 0       0 $revision = defined ($args{revision}) ? $self->symbol_lookup(symbol => $args{revision}) : $self->{head};
1217              
1218 0 0       0 $self->rexists (revision => $revision) || return undef;
1219              
1220 0 0       0 if (defined ($self->{revisions}->{$revision}->{$method})) {
1221 0         0 return $self->{revisions}->{$revision}->{$method};
1222             }
1223              
1224 0         0 return undef;
1225             }
1226              
1227             ##
1228             ## DESTROY
1229             ##
1230             ## Some methods explicitly call DESTROY(). We need something to return
1231             ## success.
1232             ##
1233              
1234             sub DESTROY {
1235 0     0   0 return;
1236             }
1237              
1238              
1239             ##
1240             ## pipestderrout
1241             ##
1242             ## executes a command, trapping the output from both STDERR and STDOUT. This sort of thing is a
1243             ## real pain in the ass in perl, and the simplest (but not the most efficient) way to do it is
1244             ## to simply use shell redirects to files and then slurp up the contents of these files. The best
1245             ## way to do it is probably to use IPC::Run which is unfortunately a pretty heavyweight package.
1246             ##
1247             ## this command should not be called from any user programs. The API may change at any stage
1248             ## without warning. Use at your own risk. May bite if not handled carefully.
1249             ##
1250             ## returns: an array containing the exit code and references to stdout and stderr respectively
1251             ## undef if no command is issued, or if the temporary directory is unwritable,
1252             ## putting flag in error buffer.
1253             ##
1254              
1255             sub pipestderrout {
1256 2     2 0 4 my $self = shift;
1257 2         8 my %args = @_;
1258 2         3 my ($exitcode, $stdout, $stderr, $cwd);
1259              
1260 2 50       11 unless (defined ($args{command})) {
1261 0         0 $self->{err} = "must supply \"command\" argument";
1262 0         0 return undef;
1263             }
1264              
1265 2 50       8 my $tmpdir = defined ($self->{tmpdir}) ? $self->{tmpdir} : "/tmp";
1266 2 50 33     88 unless (-d $tmpdir && -r$tmpdir) {
1267 0         0 $self->{err} = "cannot write to tmpdir: \"$tmpdir\"";
1268 0         0 return undef;
1269             }
1270              
1271 2         14 my $tmpstdout = File::Temp::mktemp("$tmpdir/tempXXXXXX");
1272 2         617 my $tmpstderr = File::Temp::mktemp("$tmpdir/tempXXXXXX");
1273              
1274 2 50       316 if (defined ($args{dir})) {
1275 2         11575 $cwd = cwd();
1276             # $cwd is tainted. we need to untaint
1277 2         126 $cwd =~ m|^([/\w\-\._]+)$|;
1278 2         32 $cwd = $1;
1279 2 50       129 unless (chdir ($args{dir})) {
1280 0         0 $self->{err} = "cannot change to working directory";
1281 0         0 return undef;
1282             }
1283             }
1284              
1285 2         14593 my $retval = system ("$args{command} < /dev/null 1> $tmpstdout 2> $tmpstderr");
1286              
1287 2 50       168 if (defined ($cwd)) {
1288 2         88 chdir ($cwd);
1289             }
1290              
1291 2         29 $exitcode = $retval >> 8;
1292            
1293 2         25 my @buf1 = ();
1294 2         199 open (INPUT, $tmpstdout);
1295 2         159 while () {
1296 0         0 chomp;
1297 0         0 push @buf1, $_;
1298             }
1299 2         45 close (INPUT);
1300 2         356 unlink ($tmpstdout);
1301 2         16 $stdout = \@buf1;
1302              
1303 2         17 my @buf2 = ();
1304 2         101 open (INPUT, $tmpstderr);
1305 2         60 while () {
1306 2         17 chomp;
1307 2         34 push @buf2, $_;
1308             }
1309 2         43 close (INPUT);
1310 2         249 unlink ($tmpstderr);
1311 2         11 $stderr = \@buf2;
1312              
1313 2         119 return ($exitcode, $stdout, $stderr);
1314             }
1315              
1316              
1317             ##
1318             ## symbol_lookup
1319             ##
1320             ## Looks up the parameter "symbol" in the RCS symbols table, and if found,
1321             ## returns the revision version of the symbol. Otherwise, returns the
1322             ## original version of the "symbol" parameter.
1323             ##
1324             ## This function allow you do do things like:
1325             ##
1326             ## $args{revision} = $self->symbol_lookup(symbol => $args{revision});
1327             ##
1328             ## which allows the programmer to use rcs symbols everywhere instead of
1329             ## version numbers.
1330             ##
1331              
1332             sub symbol_lookup {
1333 0     0 0   my $self = shift;
1334 0           my %args = @_;
1335              
1336 0 0         if (defined ($self->{symbols}->{$args{symbol}})) {
1337 0           return $self->{symbols}->{$args{symbol}};
1338             }
1339              
1340 0           return $args{symbol};
1341             }
1342              
1343              
1344             ##
1345             ## log_unquote
1346             ##
1347             ## Converts from internal RCS quoted-log format to normal format
1348             ##
1349              
1350             sub log_unquote {
1351 0     0 0   my $arg = shift;
1352            
1353 0           $arg =~ s/\@\@/\@/g;
1354              
1355 0           $arg;
1356             }
1357              
1358              
1359             ##
1360             ## log_quote
1361             ##
1362             ## Converts from normal text format to internal RCS quoted-log format.
1363             ##
1364              
1365             sub log_quote {
1366 0     0 0   my $arg = shift;
1367            
1368 0           $arg =~ s/\@/\@\@/g;
1369              
1370 0           $arg;
1371             }
1372              
1373              
1374             ##
1375             ## true
1376             ##
1377             ## evaluates to one or zero, depending on the argument supplied
1378             ##
1379              
1380             sub istrue {
1381 0     0 0   my $arg = shift;
1382            
1383 0 0         if ($arg =~ /^(y|ye|yes|t|tr|tru|true|1)/i) {
1384 0           return 1;
1385             }
1386            
1387 0           return 0;
1388             }
1389              
1390             ##
1391             ## usinghpux
1392             ##
1393             ## Setting this invokes some hackery elsewhere
1394             ## to get around crippled behaviour on HP's version of rcs
1395             ##
1396              
1397             sub usinghpux {
1398 0     0 0   my $self = shift;
1399 0           $self->{hpux} = shift;
1400             }
1401              
1402              
1403              
1404             =head1 BUGS
1405              
1406             =over 4
1407              
1408             =item o
1409              
1410             unfortunately, it was all but impossible to call this module RCS::Agent,
1411             which is probably the more natural name. The reason for this is left as an
1412             exercise for the reader.
1413              
1414             =item o
1415              
1416             the code hasn't been tested on non-unix operating systems like the
1417             Windows family, MacOS, VMS and so forth. It will almost certainly not work
1418             on them.
1419              
1420             =item o
1421              
1422             "Merge is Hard!". Rcs::Agent does not support merging branches because this
1423             is something which often requires manual intervention. On the grounds that
1424             providing broken functionality along these lines would just encourage a bad
1425             habit, it's been left out completely. There are no plans to change this
1426             policy - at least not until the code develops self awareness.
1427              
1428             =item o
1429              
1430             L does not yet grok CVS's magic branch tags.
1431              
1432             =item o
1433              
1434             revisions() and symbols() both contain references to branch revisions.
1435             This needs to be changed.
1436              
1437             Please mail rcs-agent-lib@netability.ie if you find any more bugs. Patches
1438             should be sent in unified diff format (i.e. I), or context diff
1439             format (I) if your version of diff doesn't support unified diffs.
1440              
1441             =head1 WARRANTY AND LIABILITY
1442              
1443             THIS SOFTWARE IS PROVIDED BY NETWORK ABILITY LIMITED ``AS IS'' AND ANY
1444             EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1445             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1446             DISCLAIMED. IN NO EVENT SHALL NETWORK ABILITY LIMITED OR ANY CONTRIBUTORS
1447             BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1448             CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1449             SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1450             INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1451             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1452             ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1453             POSSIBILITY OF SUCH DAMAGE.
1454              
1455             =head1 COPYRIGHT
1456              
1457             Copyright (C) 2001 - 2007 Network Ability Ltd. All rights reserved. This
1458             software may be redistributed under the terms of the license included in
1459             this software distribution. Please see the file "LICENSE" for further
1460             details.
1461              
1462             =cut
1463              
1464             =head1 SEE ALSO
1465              
1466             L, L, L, L, L, L,
1467             L, L.
1468              
1469             =cut