File Coverage

blib/lib/ClearCase/Argv.pm
Criterion Covered Total %
statement 48 547 8.7
branch 4 376 1.0
condition 1 153 0.6
subroutine 15 48 31.2
pod 3 22 13.6
total 71 1146 6.2


line stmt bran cond sub pod time code
1             package ClearCase::Argv;
2              
3             $VERSION = '1.54';
4              
5 1     1   9622 use Argv 1.28;
  1         12117  
  1         94  
6              
7 1 50   1   8 use constant MSWIN => $^O =~ /MSWin|Windows_NT/i ? 1 : 0;
  1         1  
  1         69  
8 1 50   1   5 use constant CYGWIN => $^O =~ /cygwin/i ? 1 : 0;
  1         6  
  1         115  
9              
10             my $NUL = MSWIN ? 'NUL' : '/dev/null';
11              
12             @ISA = qw(Argv);
13             %EXPORT_TAGS = ( 'functional' => [ qw(ctsystem ctexec ctqx ctqv ctpipe chdir) ] );
14             @EXPORT_OK = (@{$EXPORT_TAGS{functional}});
15              
16             *AUTOLOAD = \&Argv::AUTOLOAD;
17              
18 1     1   6 use strict;
  1         2  
  1         3249  
19              
20             my $class = __PACKAGE__;
21             if (CYGWIN) {
22             require Text::ParseWords;
23             $class->inpathnorm(1);
24             $class->outpathnorm(1);
25             }
26             my %pidcount;
27             END {
28 1     1   4027 my $ret = $?;
29 1         35 ClearCase::Argv->ipc(0); #Kill any remaining coprocess
30 1         14 $? |= $ret;
31             }
32             # For programming purposes we can't allow per-user preferences.
33             if ($ENV{CLEARCASE_PROFILE}) {
34             $ENV{_CLEARCASE_PROFILE} = $ENV{CLEARCASE_PROFILE};
35             delete $ENV{CLEARCASE_PROFILE};
36             }
37              
38             # Allow EV's setting class data in the derived class to override the
39             # base class's defaults.There's probably a better way to code this.
40             for (grep !/^_/, keys %Argv::Argv) {
41             (my $ev = uc(join('_', __PACKAGE__, $_))) =~ s%::%_%g;
42             $Argv::Argv{$_} = $ENV{$ev} if defined $ENV{$ev};
43             }
44              
45             my @ct = ('cleartool');
46              
47             # Attempt to find the definitive ClearCase bin path at startup. Don't
48             # try excruciatingly hard, it would take unwarranted time. And don't
49             # do so at all if running setuid or as root. If this doesn't work,
50             # the path can be set explicitly via the 'cleartool_path' class method.
51             if (!MSWIN && ($< == 0 || $< != $> || !grep{$_}@ENV{qw(PATH ATRIAHOME)})) {
52             # running as root, or setuid, even if the same user as the suid one...
53             @ct = ('/opt/rational/clearcase/bin/cleartool');
54             } elsif ($ENV{PATH} !~ m%\W(atria|ClearCase)\Wbin\b%i) {
55             if (!MSWIN) {
56             my $abin = $ENV{ATRIAHOME} ? "$ENV{ATRIAHOME}/bin" : '/usr/atria/bin';
57             $ENV{PATH} .= ":$abin" if -d $abin && $ENV{PATH} !~ m%/atria/bin%;
58             } else {
59             local $^W = 0;
60             for ( ($ENV{ATRIAHOME} || '') . "/bin",
61             'C:/Program Files/Rational/ClearCase/bin',
62             'D:/Program Files/Rational/ClearCase/bin',
63             'C:/atria/bin',
64             'D:/atria/bin') {
65             if (-d $_ && $ENV{PATH} !~ m%$_%) {
66             $ENV{PATH} .= ";$_";
67             last;
68             }
69             }
70             }
71             }
72              
73             # Class method to get/set the location of 'cleartool'.
74             sub cleartool_path {
75 0     0 0 0 my $self = shift;
76 0 0 0     0 if (ref $self and $self->{CT}) {
77 0 0       0 $self->ct(@_) if @_;
78 0         0 return @{$self->ct};
  0         0  
79             } else {
80 0 0       0 @ct = @_ if @_;
81 0         0 return @ct;
82             }
83             }
84             *find_cleartool = \&cleartool_path; # backward compatibility
85              
86             sub perlscript {
87 0     0 0 0 my $self = shift;
88 0         0 for (@{$self->{CT}}) {
  0         0  
89 0 0       0 next unless open my $fd, q(<), $_;
90 0         0 my $line = <$fd>;
91 0         0 close $fd;
92 0 0       0 return 1 if $line =~ m%^(#!|\@rem ).*\bperl\b%i;
93             }
94 0         0 return 0;
95             }
96             sub ct {
97 0     0 0 0 my $self = shift;
98 0 0       0 if (ref $self) {
99 0 0       0 if (@_) {
100 0 0       0 if ($self->ipc) {
101             # servicing attrs after raw copy: the pid count was not
102             # incremented yet, so we cannot call ipc(0)
103 0         0 delete $self->{IPC};
104 0 0       0 $self->{CT} = ref($_[0])? $_[0] : [@_];
105 0         0 $self->ipc(1);
106             } else {
107 0 0       0 $self->{CT} = ref($_[0])? $_[0] : [@_];
108             }
109 0 0       0 $self->{WRAPPER}++ if $self->perlscript;
110 0 0       0 return defined(wantarray)? $self : undef;
111             } else {
112 0 0       0 return $self->{CT}? @{$self->{CT}} : @ct;
  0         0  
113             }
114             } else {
115 0         0 return cleartool_path;
116             }
117             }
118             # Override of base-class method to change a prog value of 'foo' into
119             # qw(cleartool foo). If the value is already an array or array ref
120             # leave it alone. Same thing if the 1st word contains /cleartool/
121             # or is an absolute path.
122             sub prog {
123 0     0 1 0 my $self = shift;
124 0 0       0 return $self->SUPER::prog unless @_;
125 0         0 my $prg = shift;
126 0 0 0     0 if (@_ || ref($prg) || $prg =~ m%^/|^\S*(?:clear|multi)tool%
      0        
      0        
127             || $self->ctcmd) {
128 0         0 return $self->SUPER::prog($prg, @_);
129             } else {
130 0         0 require Text::ParseWords;
131 0 0       0 my @prg = map { s/^'(.*)'$/$1/ || s/^"(.*)"$/$1/; $_ }
  0         0  
  0         0  
132             Text::ParseWords::parse_line('\s+', 1, $prg);
133 0         0 return $self->SUPER::prog([$self->ct, @prg], @_);
134             }
135             }
136              
137             # Overridden to allow for alternate execution modes.
138             sub exec {
139 0 0 0 0 0 0 $class->new(@_)->exec if !ref($_[0]) || ref($_[0]) eq 'HASH';
140 0         0 my $self = shift;
141 0 0       0 if ($self->ctcmd) {
    0          
142 0         0 exit $self->system(@_) >> 8;
143             } elsif ($self->ipc) {
144 0         0 my $rc = $self->system(@_);
145 0   0     0 $rc ||= $self->ipc(0);
146 0         0 exit($rc);
147             } else {
148 0         0 return $self->SUPER::exec(@_);
149             }
150             }
151              
152             # Overridden to allow for alternate execution modes.
153             sub system {
154 0 0 0 0 0 0 return $class->new(@_)->system if !ref($_[0]) || ref($_[0]) eq 'HASH';
155 0         0 my $self = shift;
156 0         0 my @rargs = @_;
157 0         0 $self->_cvt_input_cw() if CYGWIN;
158              
159 0 0 0     0 if (!$self->ctcmd && !$self->ipc) {
160 0         0 if (CYGWIN) {
161             my @ret = $self->SUPER::qv(@rargs);
162             $self->unixpath(@ret) unless $self->{WRAPPER};
163             print join("", @ret) if @ret;
164             return $?;
165             } else {
166 0         0 return $self->SUPER::system(@rargs);
167             }
168             }
169              
170 0         0 my $envp = $self->envp;
171 0         0 my($ifd, $ofd, $efd) = ($self->stdin, $self->stdout, $self->stderr);
172 0 0       0 $self->args($self->glob) if $self->autoglob;
173 0         0 my @prog = @{$self->{AV_PROG}};
  0         0  
174 0         0 shift(@prog) while grep m%(?:clear|multi)tool%, @prog;
175 0         0 my @opts = $self->_sets2opts(@_);
176 0         0 my @args = @{$self->{AV_ARGS}};
  0         0  
177 0         0 my @cmd = (@prog, @opts, @args);
178 0         0 my $dbg = $self->dbglevel;
179 0 0       0 $self->_addstats("cleartool @prog", scalar @args) if %Argv::Summary;
180 0 0       0 $self->warning("cannot close stdin of child process") if $ifd;
181 0 0 0     0 if ($self->noexec && !$self->_read_only) {
182 0         0 $self->_dbg($dbg, '-', \*STDERR, @cmd);
183 0         0 return 0;
184             }
185 0         0 open(_O, '>&STDOUT');
186 0         0 open(_E, '>&STDERR');
187 0         0 my($outplace, $errplace) = (0, 0);
188 0 0       0 if ($self->quiet) {
    0          
    0          
189 0         0 $outplace = 1;
190             } elsif ($ofd == 2) {
191 0 0       0 open(STDOUT, '>&STDERR') || warn "Can't dup stdout";
192             } elsif ($ofd !~ m%^\d*$%) {
193 0 0       0 open(STDOUT, $ofd) || warn "$ofd: $!";
194             } else {
195 0 0       0 warn "Warning: illegal value '$ofd' for stdout" if $ofd > 2;
196 0 0       0 if ($ofd == 0) {
197 0         0 $outplace = -1;
198 0 0       0 open(STDOUT, ">$NUL") if $self->ipc();
199             }
200             }
201 0 0 0     0 $self->_dbg($dbg, '+>', \*STDERR, @cmd) if $dbg && $self->ctcmd;
202 0 0       0 if ($efd == 1) {
    0          
203 0 0       0 open(STDERR, '>&STDOUT') || warn "Can't dup stderr";
204             } elsif ($efd !~ m%^\d*$%) {
205 0 0       0 open(STDERR, $efd) || warn "$efd: $!";
206             } else {
207 0 0       0 warn "Warning: illegal value '$efd' for stderr" if $efd > 2;
208 0 0       0 $errplace = -1 if $efd == 0;
209             }
210 0         0 my $rc = 0;
211 0 0       0 if ($self->ctcmd) {
212 0         0 my $ctc = ClearCase::CtCmd->new(outfunc=>$outplace, errfunc=>$errplace);
213 0 0       0 if ($envp) {
214 0         0 local %ENV = %$envp;
215 0         0 $ctc->exec(@cmd);
216             } else {
217 0         0 $ctc->exec(@cmd);
218             }
219 0         0 $rc = $ctc->status;
220             } else {
221 0 0       0 if ($cmd[0] eq 'setview') {
222 0 0       0 if ($cmd[1] eq '-exec') {
223 0         0 return $self->stderr($efd)->SUPER::system;
224             }
225 0         0 $self->ipc(0);
226 0         0 my @data;
227 0         0 $self->ipc($cmd[1], \@data);
228 0 0       0 if (@data) {
229 0 0       0 if ($efd == 2) {
    0          
230 0         0 print STDERR @data;
231             } elsif ($efd == 1) {
232 0         0 print STDOUT @data;
233             }
234 0         0 my @err = grep !/^cleartool: Warning:/, @data;
235 0 0       0 delete $self->{IPC} if @err; #dtor would send 'exit' to defunct
236 0         0 return scalar @err;
237             } else {
238 0         0 return 0;
239             }
240             }
241 0         0 $rc = $self->_ipc_cmd(undef, $ofd, $efd, @cmd);
242             }
243 0         0 open(STDOUT, '>&_O'); close(_O);
  0         0  
244 0         0 open(STDERR, '>&_E'); close(_E);
  0         0  
245 0 0       0 print STDERR "+ (\$? == $?)\n" if $dbg > 1;
246 0 0       0 $self->fail($self->syfail) if $rc;
247 0         0 $? = $rc;
248 0         0 return $rc;
249             }
250              
251             # Overridden to allow for alternate execution modes.
252             sub qx { # } --for emacs CPerl mode, as 'qx' is a keyword
253 0 0 0 0 0 0 return $class->new(@_)->qx if !ref($_[0]) || ref($_[0]) eq 'HASH';
254 0         0 my $self = shift;
255 0         0 my @rargs = @_;
256 0         0 $self->_cvt_input_cw() if CYGWIN;
257              
258 0 0 0     0 unless ($self->ctcmd || $self->ipc) {
259 0         0 my @ret = $self->SUPER::qx(@rargs);
260 0         0 if (CYGWIN and !$self->{WRAPPER}) {
261             $self->unixpath(@ret);
262             }
263 0 0       0 return wantarray? @ret : join '', @ret;
264             }
265              
266 0         0 my($rc, $data, $errors);
267 0         0 my $dbg = $self->dbglevel;
268              
269 0         0 my $envp = $self->envp;
270 0         0 my($ifd, $ofd, $efd) = ($self->stdin, $self->stdout, $self->stderr);
271 0 0       0 $self->args($self->glob) if $self->autoglob;
272 0         0 my @prog = @{$self->{AV_PROG}};
  0         0  
273 0         0 shift(@prog) while grep m%(?:clear|multi)tool%, @prog;
274 0         0 my @opts = $self->_sets2opts(@_);
275 0         0 my @args = @{$self->{AV_ARGS}};
  0         0  
276 0         0 my @cmd = (@prog, @opts, @args);
277 0 0       0 $self->_addstats("cleartool @prog", scalar @args) if %Argv::Summary;
278 0 0       0 $self->warning("cannot close stdin of child process") if $ifd;
279 0 0 0     0 if ($self->noexec && !$self->_read_only) {
280 0         0 $self->_dbg($dbg, '-', \*STDERR, @cmd);
281 0         0 return 0;
282             }
283 0 0 0     0 $self->_dbg($dbg, '+>', \*STDERR, @cmd) if $dbg && $self->ctcmd;
284 0 0       0 if ($self->ctcmd) {
285 0         0 my $ctc = ClearCase::CtCmd->new;
286 0 0       0 if ($envp) {
287 0         0 local %ENV = %$envp;
288 0         0 ($rc, $data, $errors) = $ctc->exec(@cmd);
289             } else {
290 0         0 ($rc, $data, $errors) = $ctc->exec(@cmd);
291             }
292 0   0     0 $data ||= '';
293 0 0       0 if ($data) {
294 0 0       0 if (!$ofd) {
    0          
295 0         0 $data = '';
296             } elsif ($ofd == 2) {
297 0         0 print STDERR $data;
298             }
299             }
300 0 0       0 if ($errors) {
301 0 0       0 if ($efd == 1) {
    0          
302 0         0 $data .= $errors;
303             } elsif ($efd == 2) {
304 0         0 print STDERR $errors;
305             }
306             }
307 0 0       0 print STDERR "+ (\$? == $?)\n" if $dbg > 1;
308 0 0       0 if (wantarray) {
309 0         0 my @data = split /\n/, $data;
310 0 0       0 if (! $self->autochomp) {
311 0         0 for (@data) { $_ .= "\n" }
  0         0  
312             }
313 0         0 $self->unixpath(@data) if MSWIN && $self->outpathnorm;
314 0 0 0     0 print STDERR map {"+ <- $_"} @data if @data && $dbg >= 2;
  0         0  
315 0 0       0 if ($rc) {
316 0         0 $self->lastresults($rc, @data);
317 0         0 $self->fail($self->qxfail);
318             }
319 0         0 $? = $rc;
320 0         0 return @data;
321             } else {
322 0 0       0 chomp($data) if $self->autochomp;
323 0         0 $self->unixpath($data) if MSWIN && $self->outpathnorm;
324 0 0 0     0 print STDERR "+ <- $data" if $data && $dbg >= 2;
325 0 0       0 if ($rc) {
326 0         0 $self->lastresults($rc, $data);
327 0         0 $self->fail($self->qxfail);
328             }
329 0         0 $? = $rc;
330 0         0 return $data;
331             }
332             } else {
333 0 0       0 if ($cmd[0] eq 'setview') {
334 0 0       0 if ($cmd[1] eq '-exec') {
335 0         0 my $ret = $self->stderr($efd)->SUPER::qx;
336 0 0       0 chomp($ret) if $self->autochomp;
337 0 0       0 return wantarray? split(/\n/, $ret) : $ret;
338             }
339 0         0 $self->ipc(0);
340 0         0 my @data;
341 0         0 $self->ipc($cmd[1], \@data);
342 0 0       0 if ($efd == 2) {
    0          
343 0         0 print STDERR @data;
344             } elsif ($efd == 1) {
345 0 0       0 return wantarray? @data : join '', @data;
346             }
347 0         0 return '';
348             }
349 0         0 my @data = ();
350 0         0 $rc = $self->_ipc_cmd(\@data, $ofd, $efd, @cmd);
351 0 0       0 print STDERR "+ (\$? == $?)\n" if $dbg > 1;
352 0 0       0 if (wantarray) {
353 0 0       0 chomp(@data) if $self->autochomp;
354 0         0 $self->unixpath(@data) if MSWIN && $self->outpathnorm;
355 0 0       0 if ($rc) {
356 0         0 $self->lastresults($rc, @data);
357 0         0 $self->fail($self->qxfail);
358             }
359 0         0 $? = $rc;
360 0         0 return @data;
361             } else {
362 0         0 my $data = join '', @data;
363 0 0       0 chomp($data) if $self->autochomp;
364 0         0 $self->unixpath($data) if MSWIN && $self->outpathnorm;
365 0 0       0 if ($rc) {
366 0         0 $self->lastresults($rc, $data);
367 0         0 $self->fail($self->qxfail);
368             }
369 0         0 $? = $rc;
370 0         0 return $data;
371             }
372             }
373             }
374              
375             # Overridden to allow special handling for CtCmd and IPC modes.
376             sub pipe {
377 0 0 0 0 0 0 return $class->new(@_)->pipe if !ref($_[0]) || ref($_[0]) eq 'HASH';
378              
379 0         0 my $self = shift;
380 0         0 my $mode;
381 0 0       0 if ($self->ctcmd) {
    0          
382 0         0 $mode = 'CtCmd';
383             } elsif ($self->ipc) {
384 0         0 $mode = 'IPC';
385             }
386 0 0       0 if ($mode) {
387 0         0 my $otherSelf = $self->clone();
388 0 0       0 $self->warning("$mode usage incompatible with pipe - temporarily" .
389             " reverting to plain cleartool") if $self->dbglevel;
390 0 0       0 if ($mode eq 'CtCmd') {
391 0         0 $otherSelf->ctcmd(0);
392 0         0 my @prg = $otherSelf->prog; #Depends on the exact invocation path
393 0 0       0 $otherSelf->prog($self->ct, @prg)
394             unless grep m%(?:clear|multi)tool%, @prg;
395             } else {
396 0         0 $otherSelf->ipc(0);
397             }
398 0         0 return $otherSelf->SUPER::pipe(@_);
399             } else {
400 0         0 return $self->SUPER::pipe(@_);
401             }
402             }
403              
404             # Normalizes a path to Unix style (forward slashes).
405             sub unixpath {
406 0     0 0 0 my $self = shift;
407 0         0 if (CYGWIN) {
408 1     1   6 no strict 'subs';
  1         1  
  1         1787  
409             for my $line (@_) {
410             my $nl = chomp $line;
411             my $bs = $line =~ s/\\$//;
412             $line =~ s%\r$%%;
413             my @chars = split//,$line;
414             my $odd = (((grep/'/,@chars) % 2) || ((grep/"/,@chars) % 2));
415             my @bit = $odd? split/(\s+)/,$line
416             : Text::ParseWords::parse_line('\s+', 'delimiters', $line);
417             map {
418             s%\\%/%g if m%(?:^(?:"|[\w/.-]*)|\@)\\%;
419             if (m%^(vob|[A-Za-z]):%) {
420             $_ = Cygwin::win_to_posix_path($_);
421             } else {
422             s%^//view%/view%;
423             }
424             } grep { $_ && m%\S% } @bit;
425             $line = join '', grep {defined($_)} @bit;
426             $line .= '\\' if $bs;
427             $line .= "\n" if $nl;
428             }
429             } else {
430 0         0 $self->SUPER::unixpath(@_);
431             # Now apply CC-specific, @@-sensitive transforms to partial lines.
432 0         0 for my $line (@_) {
433 0         0 my $fixed = '';
434 0         0 for (split(m%(\S+@@\S*)%, $line)) {
435 0 0       0 s%\\%/%g if m%^.*?\S+@@%;
436 0         0 $fixed .= $_;
437             }
438 0         0 $line = $fixed;
439             }
440             }
441             }
442              
443             # Attaches to or detaches from a CtCmd object for execution.
444             sub ctcmd {
445 0     0 0 0 my $self = shift; # this might be an instance or a classname
446 0         0 my $level = shift;
447 0 0 0     0 $level = 2 if !defined($level) && !defined(wantarray);
448 0 0       0 if ($level) {
449 0 0 0     0 if ($self->ipc && !CYGWIN) {
450 0         0 $self->ipc(0); # shut the ipc down
451             }
452 0         0 eval { require ClearCase::CtCmd };
  0         0  
453 0 0       0 if ($@) {
454 0         0 my $msg = $@;
455 0 0 0     0 if ($level == 2 && $msg =~ m%^(Can't locate \S+)%) {
    0          
456 0         0 $msg = $1;
457             } elsif ($level > 2) {
458 0         0 die("Error: $msg");
459             }
460 0 0 0     0 if ($level == 1 || $level == 2) {
461             # On Windows, if the real ClearCase::CtCmd is missing hack
462             # in our own version that uses CAL directly.
463 0         0 if (MSWIN) {
464             eval { require Win32::OLE };
465             if ($@) {
466             warn("Warning: $msg\n") if $level == 2;
467             return undef;
468             } else {
469             warn("Warning: $msg, using CAL instead\n")
470             if $level == 2;
471             *ClearCase::CtCmd::new = \&_ctcmd_new;
472             *ClearCase::CtCmd::status = \&_ctcmd_status;
473             *ClearCase::CtCmd::exec = \&_ctcmd_cmd2cal;
474             *ClearCase::CtCmd::cleartool = \&_ctcmd_cmd2cal;
475             $ClearCase::CtCmd::VERSION = '1.01';
476             Win32::OLE->Option(Warn => 0);
477             }
478             } else {
479 0         0 my $rep = CYGWIN? "ipc" : "fork/exec";
480 0 0       0 warn("Warning: $msg, using $rep instead\n") if $level == 2;
481 0         0 return undef;
482             }
483             } else {
484 1     1   7 no strict 'refs';
  1         2  
  1         55  
485 0         0 $self->{CCAV_CTCMD} = 0;
486 0         0 return undef;
487             }
488             }
489             }
490 1     1   5 no strict 'refs'; # because $self may be a symbolic hash ref
  1         7  
  1         707  
491 0 0       0 if (defined($level)) {
492 0 0       0 if ($level) {
493 0 0       0 ClearCase::CtCmd->VERSION(1.01) if $ClearCase::CtCmd::VERSION;
494 0         0 $self->{CCAV_CTCMD} = 1;
495             # If setting a class attribute, export it to the
496             # env in case we fork a child using ClearCase::Argv.
497             ## NOT SURE WE REALLY WANT THIS IN THIS CASE ...??
498             ## $ENV{CLEARCASE_ARGV_CTCMD} = $self->{CCAV_CTCMD} if !ref($self);
499 0         0 return $self;
500             } else { # close up shop
501 0 0       0 delete $self->{CCAV_CTCMD} if exists $self->{CCAV_CTCMD};
502 0 0 0     0 delete $ENV{CLEARCASE_ARGV_CTCMD}
503             if $ENV{CLEARCASE_ARGV_CTCMD} && !ref($self);
504 0         0 return $self;
505             }
506             } else {
507 0 0 0     0 if (!defined($self->{CCAV_CTCMD}) && !defined($class->{CCAV_CTCMD})) {
508 0 0       0 return $ENV{CLEARCASE_ARGV_CTCMD} ? $self : 0;
509             }
510 0 0 0     0 return ($self->{CCAV_CTCMD} || $class->{CCAV_CTCMD}) ? $self : undef;
511             }
512             }
513              
514             sub _ctcmd_new {
515 0     0   0 my $object = shift;
516 0         0 my $this = {};
517 0         0 %$this = @_;
518 0         0 bless $this, $object;
519 0         0 $this->{'status'} = 0;
520 0         0 return $this;
521             }
522              
523             sub _ctcmd_status {
524 0     0   0 my $this = shift;
525 0         0 return $this->{'status'}
526             }
527              
528             sub _ctcmd_cmd2cal {
529 0 0   0   0 my $self = ref($_[0]) ? shift : undef;
530             # It may make more sense to stash a copy of this object rather than
531             # recreate it each time.
532 0         0 my $ccct = Win32::OLE->new('ClearCase.ClearTool');
533             # Turn list cmds into a quoted string.
534 0 0       0 my $cmd = (@_ == 1) ? $_[0] : join(' ', map {"'$_'"} @_);
  0         0  
535             # Send the actual command to CAL and get stdout returned.
536 0         0 my $out = $ccct->CmdExec($cmd);
537             # Must reap the return code first, then get stderr IFF retcode != 0.
538 0         0 my $rc = int Win32::OLE->LastError;
539 0 0       0 my $err = $rc ? Win32::OLE->LastError . "\n" : '';
540             # Massage the output from CmdExec into cmdline-style format ...
541             # Turn the literal CRLF sequence into \n for subsequent chomp/splits.
542 0         0 for ($out, $err) {
543 0 0       0 s/\015?\012/\n/g if $_;
544             }
545             # Strip the OLE verbosity from any error messages.
546 0 0       0 if ($err) {
547 0         0 $err =~ s%^OLE\s+exception.*%%;
548 0         0 $err =~ s%^\s*%%s;
549 0         0 $err =~ s%\s*Win32::OLE.*%\n%s;
550             }
551 0 0       0 $self->{'status'} = $rc if $self;
552 0         0 my @results = $rc;
553 0 0       0 if (defined($out)) {
554 0 0 0     0 if ($self && exists($self->{outfunc}) && ($self->{outfunc} == 0)) {
      0        
555 0         0 print STDOUT $out;
556             } else {
557 0         0 push(@results, $out);
558             }
559             } else {
560 0         0 push(@results, q()); #Make sure $err will go into the 3rd slot
561             }
562 0 0 0     0 if ($self && exists($self->{errfunc}) && ($self->{errfunc} == 0)) {
      0        
563 0         0 print STDERR $err;
564             } else {
565 0         0 push(@results, $err);
566             }
567 0 0       0 if (wantarray) {
568 0         0 return @results;
569             } else {
570 0 0       0 $? = $rc if $rc;
571 0 0       0 print STDERR $results[2] if defined($results[2]);
572 0         0 return $results[1];
573             }
574             }
575              
576             # Starts or stops a cleartool coprocess.
577             sub ipc {
578 1     1 0 13 my $self = shift; # this might be an instance or a classname
579 1     1   5 no strict 'refs'; # because $self may be a symbolic hash ref
  1         2  
  1         558  
580 1         7 my $level = shift;
581 1         5 my $output = shift;
582 1         12 local $\ = ''; # no extra newlines e.g. under perl -le ...
583 1 50 33     24 if (defined($level) && !$level) {
    0          
584 1 50       19 return 0 unless exists($self->{IPC});
585 0           my $down = $self->{IPC}->{DOWN};
586 0           my $pid = $self->{IPC}->{PID};
587 0           delete $self->{IPC};
588 0 0         return 0 if --$pidcount{$pid};
589             # Send an explicit "exit" command and close up shop.
590 0           print $down "exit\n";
591 0           my $rc = close($down);
592 0           waitpid($pid, 0);
593 0           delete $pidcount{$pid};
594 0   0       return $rc || $?;
595             } elsif (!defined($level)) {
596 0 0         return exists($self->{IPC}) ? $self : 0;
597             }
598              
599 0 0         if ($self->ctcmd) {
600 0           $self->ctcmd(0); # shut down the CtCmd connection
601             }
602 0 0 0       if (exists($class->{IPC}) && $level =~ m%^\d+$%
      0        
      0        
      0        
      0        
      0        
603             && !$self->{CT} && !$class->{CT}
604             || ($self->{CT} && $class->{CT} && $self->{CT} eq $class->{CT})) {
605 0 0 0       if (($self ne $class) && !$self->ipc) {
606 0           ++$pidcount{$class->{IPC}->{PID}};
607 0           $self->{IPC}->{PID} = $class->{IPC}->{PID};
608 0           $self->{IPC}->{DOWN} = $class->{IPC}->{DOWN};
609 0           $self->{IPC}->{BACK} = $class->{IPC}->{BACK};
610             }
611 0           return $self;
612             }
613              
614             # This should never fail to load since it's built in.
615 0           require IPC::Open3;
616              
617             # Dies on failure.
618 0           my($down, $back);
619 0 0         my $view = ($level =~ /^\d+$/) ? '' : $level;
620 0 0         my @cmd = !$view ? ($self->ct, '-status')
621             : ($self->ct, qw(setview -exec), join(' ', $self->ct, '-status'), $view);
622 0           my $pid = IPC::Open3::open3($down, $back, undef, @cmd);
623              
624             # Set the "line discipline" to convert CRLF to \n.
625 0           binmode $back, ':crlf';
626              
627 0 0         if ($view) {
628 0           print $down "des -fmt '\\n' .\n";
629 0           while ($_ = <$back>) {
630 0 0         last if /^Command 1 returned status/;
631 0 0         push @{$output}, $_ unless /^$/;
  0            
632             }
633             }
634              
635 0           $self->{IPC}->{DOWN} = $down;
636 0           $self->{IPC}->{BACK} = $back;
637 0           $self->{IPC}->{PID} = $pid;
638 0           ++$pidcount{$pid};
639              
640 0           return $self;
641             }
642              
643             sub _cw_map {
644 1     1   6 use File::Basename;
  1         2  
  1         131  
645 1     1   5 no warnings;
  1         9  
  1         402  
646 0     0     for (@_) {
647 0 0         next if s%^(vob:)?/cygdrive/([A-Za-z])%$1$2:%;
648 0 0         next if s%^(vob:)?/view%$1//view%;
649 0 0         if (m%^/[^/]%) {
650 0           my $p = dirname $_;
651 0 0 0       if ($p eq '/' && ! -r) {
    0          
652 0           s%^/%\\%; # case of vob tags
653             } elsif (-r $p) {
654 0           $_ = Cygwin::posix_to_win_path($_);
655             }
656             }
657             }
658             }
659              
660             sub _cvt_input_cw {
661 0     0     my $self = shift;
662 0 0         return if $self->{WRAPPER};
663 0           _cw_map @{$self->{AV_ARGS}};
  0            
664 0           for my $o (values %{$self->{AV_OPTS}}) {
  0            
665 0           _cw_map grep{$_}@{$o};
  0            
  0            
666             }
667             }
668              
669             # commands sent to ipc must be single line ones
670             sub _ipc_nl_in_cmt {
671 0     0     my $r = shift;
672 0           my ($i, $c, $v) = 0;
673 0           for (@{$r}) {
  0            
674 0 0 0       if (m%^-c(omment)?$%) {
    0          
    0          
675 0           $c = $i;
676             } elsif (m%^-(nc|c[fq])%) {
677 0           last;
678             } elsif ($c and $i == $c + 1) {
679 0 0         $v = $_ if m%\n%;
680 0           last;
681             }
682 0           $i++;
683             }
684 0 0         if ($v) {
685 1     1   1220 use File::Temp qw(tempfile);
  1         36459  
  1         895  
686 0           my ($fh,$cfile) = tempfile;
687 0           print $fh $v;
688 0           close $fh;
689 0           splice @{$r} ,$c ,2 , ('-cfile', $cfile);
  0            
690             }
691             }
692              
693             sub _ipc_cmd {
694 0     0     my $self = shift;
695 0           my ($disposition, $stdout, $stderr, @cmd) = @_;
696 0           local *_;
697 0           local $\ = ''; # no extra newlines e.g. under perl -le ...
698 0           _ipc_nl_in_cmt(\@cmd);
699             # Send the command to cleartool.
700             my $qm = ((MSWIN || CYGWIN) && !$self->{WRAPPER})?
701 0     0     sub { shift; s%\'%^'%g; return qq('$_'); }
  0            
  0            
702 0     0     : sub { quotemeta shift };
  0            
703 0 0         my $cmd = join(' ', map {
    0          
    0          
704 0           m%^$|\s|[\[\]\(\)*"'?]% ?
705             (m%'% ?
706             (m%"% ?
707             $qm->($_) : qq("$_"))
708             : qq('$_'))
709             : $_
710 0           } grep {defined} @cmd);
711             # Handle verbosity.
712 0           my $dbg = $self->dbglevel;
713 0 0         $self->_dbg($dbg, '=>', \*STDERR, $cmd) if $dbg;
714              
715 0           chomp $cmd;
716 0           my $down = $self->{IPC}->{DOWN};
717 0           print $down $cmd, "\n";
718              
719             # Supply custom comment on standard input if requested.
720 0 0         if (exists $self->{IPC}->{COMMENT}) {
721 0           my $input = $self->{IPC}->{COMMENT};
722 0           print $down $input, "\n.\n";
723 0           delete $self->{IPC}->{COMMENT};
724             }
725 0           my $man = ($self->{AV_PROG}->[1] eq 'man'); #Special case: errors ok
726 0           my $diff = ($self->{AV_PROG}->[1] eq 'diff');
727 0           my $manok; #Some man output already: no complete failure
728             # Read back the results and get command status.
729 0           my $rc = 0;
730 0           my $back = $self->{IPC}->{BACK};
731 0           while($_ = <$back>) {
732 0           my ($last, $next, $err);
733 0           my $out = *STDOUT;
734 0 0 0       if (!$manok && m%^(?:clear|multi)tool.*?: (Error|Warning):%) {
    0 0        
    0 0        
    0          
735 0 0         if ($stderr) {
736 0           $out = *STDERR;
737 0           $err = 1;
738             } else {
739 0           $next = 1;
740             }
741             } elsif (!$man && m%^Comments for %) {
742 0 0         if ($disposition) {
743 0           push(@$disposition, $_);
744             } else {
745 0           print $out $_;
746             }
747 0           while (<>) {
748 0           chomp;
749 0           s%\r$%% if MSWIN;
750 0           print $down "$_\n";
751 0 0         last if m%^\.$%;
752             }
753             } elsif (s%^(.*)Command \d+ returned status (\d+)%$1%) {
754 0 0         $rc = $2 unless $rc;
755 0           chomp;
756 0 0         $_ ? $last = 1 : last;
757             } elsif (!$manok && $man) {
758 0           $manok = 1;
759             }
760 0 0 0       print '+ <=', $_ if $_ && $dbg >= 2;
761 0 0 0       $rc = 1 if $diff && !m%^Files are identical%;
762 0 0         next if $next;
763 0           $self->unixpath($_) if CYGWIN and !$self->{WRAPPER};
764 0 0 0       if ($stdout || ($err && $stderr)) {
      0        
765 0 0 0       if ($disposition && (($err && $stderr == 1) || (!$err && $stdout == 1))) {
      0        
766 0           push(@$disposition, $_);
767             } else {
768 0           print $out $_;
769             }
770             }
771 0 0         last if $last;
772             }
773 0           return $rc;
774             }
775              
776             sub fork_exec {
777 0     0 0   my $self = shift;
778 0           $self->ipc(0);
779 0           $self->ctcmd(0);
780             }
781              
782             sub exec_style {
783 0     0 0   my $self = shift;
784 0           my $style = shift;
785 0 0         if ($style) {
786 1     1   10 no strict 'subs';
  1         2  
  1         815  
787 0   0       $self->$style(shift || 3);
788             }
789 0 0         if ($self->ctcmd) {
    0          
790 0           return "CTCMD";
791             } elsif ($self->ipc) {
792 0           return "IPC";
793             } else {
794 0           return "FORK";
795             }
796             }
797              
798             sub _read_only {
799 0     0     my $self = shift;
800 0 0         if ($self->readonly =~ /^a/i) { # a=automatic
801 0           my @cmd = $self->prog;
802 0 0         if ($cmd[-1] =~ m%^(ls|annotate|apropos|cat|des|diff|dospace|
803             file|getcache|getlog|help|host|man|pw|
804             setview|space)%x) {
805 0           return 1;
806             } else {
807 0           return 0;
808             }
809             } else {
810 0           return $self->SUPER::_read_only;
811             }
812             }
813              
814             # The cleartool command has quoting rules different from any system
815             # shell so we subclass the quoting method to deal with it. Not
816             # currently well tested with esoteric cmd lines such as mkattr.
817             ## THIS STUFF IS REALLY COMPLEX WITH ALL THE PERMUTATIONS
818             ## OF PLATFORMS, SUBCLASSES, SHELLS, AND APIs. WATCH OUT.
819             sub quote {
820 0     0 1   my $self = shift;
821             # Don't quote the 2nd word where @_ = ('cleartool', 'pwv -s');
822 0 0         return @_ if @_ == 2;
823 0 0         return $self->SUPER::quote(@_) if @_ > 2;
824             # Ok, now we're looking at interactive-cleartool quoting ("man cleartool").
825 0 0         if (!MSWIN && $self->ipc) {
826             # Special case - extract multiline comments from the cmd line and
827             # put them in the stdin stream when using UNIX co-process model.
828 0           for (my $i=0; $i < $#_; $i++) {
829 0 0 0       if ($_[$i] eq '-c' && $_[$i+1] =~ m%\n%s) {
830 0           $self->stdin("$_[$i+1]\n.");
831 0           splice(@_, $i, 2, '-cq');
832 0           last;
833             }
834             }
835             }
836             # Single quotes aren't understood by the &*&#$ Windows shell
837             # but cleartool gets them right so this quoting is simpler.
838 0           my $inpathnorm = $self->inpathnorm;
839 0           for (@_) {
840 0           $_ = Cygwin::posix_to_win_path($_) if CYGWIN && m%^/%;
841             # If requested, change / for \ in Windows file paths.
842 0 0         s%/%\\%g if $inpathnorm;
843             # Now quote embedded quotes ...
844 0           s%'%\\'%g;
845             # and then the entire string.
846 0           $_ = qq('$_');
847             }
848 0           return @_;
849             }
850              
851             # Hack - allow a comment to be registered here. The next command will
852             # see it with -c "comment" if in regular mode or with -cq and reading
853             # the comment from stdin if in ->ipc mode.
854             sub comment {
855 0     0 1   my $self = shift;
856 0           my $cmnt = join("\n", @_);
857 0           my @prev = $self->opts;
858 0 0         if ($self->ipc) {
859 0 0         $self->opts('-cq', @prev) if !grep /^-cq/, @prev;
860 0           $self->{IPC}->{COMMENT} = $cmnt;
861             } else {
862 0 0         $self->opts('-c', $cmnt, @prev) if !grep /^-c/, @prev;
863             }
864 0           return $self;
865             }
866              
867             # Add -/ipc and -/ctcmd to list of supported attr-flags.
868             sub attropts {
869 0     0 0   my $self = shift;
870 0           return $self->SUPER::attropts(@_, qw(ipc ctcmd));
871             }
872              
873             sub _chdir {
874 0     0     my $self = shift; # this might be an instance or a classname
875 0           my ($dir) = @_;
876 0           chomp $dir;
877 0           my $rc = CORE::chdir($dir);
878 0 0         if ($self->ipc) {
879 0 0         my $ctcd = ref($self) ? $self->clone : __PACKAGE__->new;
880 0           $ctcd->stdout(0)->argv('cd', $dir)->system;
881             }
882 0           return $rc;
883             }
884              
885             # Export our own functional interfaces as well.
886 0     0 0   sub ctsystem { return __PACKAGE__->new(@_)->system }
887 0     0 0   sub ctexec { return __PACKAGE__->new(@_)->exec }
888 0     0 0   sub ctqx { return __PACKAGE__->new(@_)->qx }
889 0     0 0   sub ctpipe { return __PACKAGE__->new(@_)->pipe }
890 0     0 0   sub chdir { return __PACKAGE__->_chdir(@_) }
891             *ctqv = \&ctqx; # just for consistency
892              
893             # Constructor, and copy constructor as clone, with $proto
894             sub new {
895 0     0 0   my $proto = shift;
896 0           my $self = $proto->SUPER::new(@_);
897 0 0         if ($self->ipc) {
898 1     1   6 no strict 'refs'; # $proto may be a symbolic hash
  1         1  
  1         190  
899 0 0 0       if ($proto->ipc && $self->{IPC}->{PID} == $proto->{IPC}->{PID}) {
900 0           ++$pidcount{$self->{IPC}->{PID}};
901             # Correct the effect of the base cloning on globs
902 0           $self->{IPC}->{DOWN} = $proto->{IPC}->{DOWN};
903 0           $self->{IPC}->{BACK} = $proto->{IPC}->{BACK};
904             }
905             }
906 0 0         bless $self, ref($proto) ? ref($proto) : $proto;
907 0           return $self;
908             }
909             *clone = \&new;
910              
911             # Clean up leftover cleartool processes if user forgot to.
912             sub DESTROY {
913 0     0     my $self = shift;
914 0           my $ret = $?;
915 0 0         $self->ipc(0) if $self->ipc;
916 0           $? |= $ret;
917             }
918              
919             1;
920              
921             __END__