File Coverage

blib/lib/ClearCase/Wrapper.pm
Criterion Covered Total %
statement 81 131 61.8
branch 15 66 22.7
condition 2 11 18.1
subroutine 15 20 75.0
pod 0 5 0.0
total 113 233 48.5


line stmt bran cond sub pod time code
1             package ClearCase::Wrapper;
2              
3             $VERSION = '1.19';
4              
5             require 5.006;
6              
7 1     1   4189 use AutoLoader 'AUTOLOAD';
  1         4751  
  1         7  
8 1     1   36 use B;
  1         2  
  1         70  
9 1     1   10 use strict;
  1         2  
  1         30  
10 1     1   5 use warnings;
  1         1  
  1         39  
11              
12 1     1   6 use vars qw(%Packages %ExtMap $libdir $prog $dieexit $dieexec $diemexec);
  1         1  
  1         134  
13              
14             # Inherit some symbols from the main package. We will later "donate"
15             # these to all overlay packages as well.
16             BEGIN {
17 1     1   3 *prog = \$::prog;
18 1         2 *dieexit = \$::dieexit;
19 1         1 *dieexec = \$::dieexec;
20 1         78 *diemexec = \$::diemexec;
21             }
22              
23             # For some reason this can't be handled the same as $prog above ...
24 1 50   1   6 use constant MSWIN => $^O =~ /MSWin|Windows_NT/i ? 1 : 0;
  1         2  
  1         252  
25              
26             # This is the list of functions we want to export to overlay pkgs.
27             my @exports = qw(MSWIN GetOptions Assert Burrow Msg Pred ViewTag
28             AutoCheckedOut AutoNotCheckedOut AutoViewPrivate);
29              
30             # Hacks for portability with Windows env vars.
31             BEGIN {
32 1   33 1   16 $ENV{LOGNAME} ||= $ENV{USERNAME};
33 1   33     252 $ENV{HOME} ||= "$ENV{HOMEDRIVE}/$ENV{HOMEPATH}";
34             }
35              
36             # Unless the user has their own CLEARCASE_PROFILE, set it to the global one.
37             BEGIN {
38             # Learn where this module was found so we can look there for other files.
39 1     1   7 ($libdir = $INC{'ClearCase/Wrapper.pm'}) =~ s%\.pm$%%;
40              
41 1 50       23 if (defined $ENV{CLEARCASE_PROFILE}) {
    50          
    50          
42 0         0 $ENV{_CLEARCASE_WRAPPER_PROFILE} = $ENV{CLEARCASE_PROFILE};
43             } elsif ($ENV{_CLEARCASE_WRAPPER_PROFILE}) {
44 0         0 $ENV{CLEARCASE_PROFILE} = $ENV{_CLEARCASE_WRAPPER_PROFILE};
45             } elsif (! -f "$ENV{HOME}/.clearcase_profile") {
46 1         3 my $rc = join('/', $libdir, 'clearcase_profile');
47 1 50       386 $ENV{CLEARCASE_PROFILE} = $rc if -r $rc;
48             }
49             }
50              
51             # Skip the Getopt::Long->import(), we need our own GetOptions().
52             require Getopt::Long;
53              
54             # Getopt::Long::GetOptions() respects '--' but strips it, while
55             # we want to respect '--' and leave it in. Thus this override.
56             sub GetOptions {
57 0 0   0 0 0 @ARGV = map {/^--$/ ? qw(=--= --) : $_} @ARGV;
  0         0  
58 0         0 my $ret = Getopt::Long::GetOptions(@_);
59 0 0       0 @ARGV = map {/^=--=$/ ? qw(--) : $_} @ARGV;
  0         0  
60 0         0 return $ret;
61             }
62              
63             # Technically we should use Getopt::Long::Configure() for these but
64             # there's a tangled version history and this is faster anyway.
65             $Getopt::Long::passthrough = 1; # required for wrapper programs
66             $Getopt::Long::ignorecase = 0; # global override for dumb default
67              
68             # Any subroutine declared in a module located via this code
69             # will eclipse one of the same name declared above.
70             ## NOTE: functions defined in modules found here should not
71             ## be placed directly into ClearCase::Wrapper. They MUST be
72             ## placed in the standard package analogous to their pathname
73             ## (e.g. ClearCase::Wrapper::Foo). Magic occurs here to get
74             ## them into ClearCase::Wrapper where they belong.
75             sub _FindAndLoadModules {
76 22     22   36 my ($dir, $subdir) = @_;
77             # Not sure how glob() sorts so force a standard order.
78 22         1244 my @pms = sort glob("$dir/$subdir/*.pm");
79 22         62 for my $pm (@pms) {
80 1         4 my $dirQuoted = quotemeta($dir);
81 1         32 $pm =~ s%^$dirQuoted/(.*)\.pm$%$1%;
82 1         7 (my $pkg = $pm) =~ s%[/\\]+%::%g;
83 1         90 eval "*${pkg}::exit = \$dieexit";
84 1         61 eval "*${pkg}::exec = \$dieexec";
85              
86             # In this block we temporarily enter the overlay's package
87             # just in case the overlay module forgot its package stmt.
88             # We then require the overlay file and also, if it's
89             # an autoloaded module (which is recommended), we drag
90             # in the index file too. This is because we need to
91             # derive a list of all functions defined in the overlay
92             # in order to import them to our own namespace.
93             {
94 1         3 eval qq(package $pkg); # default the pkg correctly
  1         27  
95 1     1   7 no warnings qw(redefine);
  1         1  
  1         226  
96 1         2 eval {
97 1         48 eval "require $pkg";
98 1 50       1899 warn $@ if $@;
99             };
100 1 50       4 next if $@;
101 1         3 my $ix = "auto/$pm/autosplit.ix";
102 1 50       53 if (-e "$dir/$ix") {
103 1         2 eval { require $ix };
  1         12694  
104 1 50       23 warn $@ if $@;
105             }
106             }
107              
108             # Now the overlay module is read in. We need to examine its
109             # newly-created symbol table, determine which functions
110             # it defined, and import them here. The same basic thing is
111             # done for the base package later.
112 1     1   6 no strict 'refs';
  1         2  
  1         1733  
113 1         3 my %names = %{"${pkg}::"};
  1         123  
114 1         12 for (keys %names) {
115             # Skip symbols that can't be names of valid cleartool ops.
116 32 100       289 next if m%^(?:_?[A-Z]|__|[ab]$)%;
117 29         62 my $tglob = "${pkg}::$_";
118 29         34 my $coderef = \&{$tglob};
  29         98  
119 29 50       77 next unless ref $coderef;
120 29         106 my $cv = B::svref_2object($coderef);
121 29 50       191 next unless $cv->isa('B::CV');
122 29 50       195 next if $cv->GV->isa('B::SPECIAL');
123 29         173 my $p = $cv->GV->STASH->NAME;
124 29 50       105 next unless $p eq $pkg;
125              
126             # Take what survives the above tests and create a hash
127             # mapping defined functions to the pkg that defines them.
128 29         68 $ExtMap{$_} = $pkg;
129             # We import the entire typeglob for 'foo' when we
130             # find an extension func named foo(). This allows usage
131             # msg extensions (in the form $foo) to come over too.
132 29         1903 eval qq(*$_ = *$tglob);
133             }
134              
135             # The base module defines a few functions which the
136             # overlay's code might want to use. Make aliases
137             # for those in the overlay's symbol table.
138 1         7 for (@exports) {
139 10         852 eval "*${pkg}::$_ = \\&$_";
140             }
141 1         87 eval "*${pkg}::prog = \\\$prog";
142              
143 1         25 $Packages{$pkg} = $INC{"$pm.pm"};
144             }
145             }
146             for my $subdir (qw(ClearCase/Wrapper ClearCase/Wrapper/Site)) {
147             for my $dir (@INC) {
148             _FindAndLoadModules($dir, $subdir);
149             }
150             }
151              
152             $Packages{'ClearCase::Wrapper'} = __FILE__;
153              
154             # Piggyback on the -ver flag to show our version too.
155             if (@ARGV && $ARGV[0] =~ /^-ver/i) {
156             my $fmt = "*%-32s %s (%s)\n";
157             local $| = 1;
158             for (sort keys %Packages) {
159             my $ver = eval "\$$_\::VERSION" || '????';
160             my $mtime = localtime((stat $Packages{$_})[9]);
161             printf $fmt, $_, $ver, $mtime || '----';
162             }
163             exit 0 if $ARGV[0] =~ /^-verw/i;
164             }
165              
166             # Take a string and an array, return the index of the 1st occurrence
167             # of the string in the array.
168             sub _FirstIndex {
169 3     3   5 my $flag = shift;
170 3         7 for my $i (0..$#_) {
171 0 0       0 return $i if $flag eq $_[$i];
172             }
173 3         21 return undef;
174             }
175              
176             # Implements the -me -tag convention (see POD).
177             if (my $me = _FirstIndex('-me', @ARGV)) {
178             if ($ARGV[0] =~ /^(?:set|start|end)view$|^rdl$|^work/) {
179             my $delim = 0;
180             for (@ARGV) {
181             last if /^--$/;
182             $delim++;
183             }
184             for (reverse @ARGV[0..$delim-1]) {
185             if (/^\w+$/) {
186             $_ = join('_', $ENV{LOGNAME}, $_);
187             last;
188             }
189             }
190             splice(@ARGV, $me, 1);
191             } elsif (my $tag = _FirstIndex('-tag', @ARGV)) {
192             $ARGV[$tag+1] = join('_', $ENV{LOGNAME}, $ARGV[$tag+1]);
193             splice(@ARGV, $me, 1);
194             }
195             }
196              
197             # Implements the -M flag (see POD).
198             if (my $mflag = _FirstIndex('-M', @ARGV) || $ENV{CLEARCASE_WRAPPER_PAGER}) {
199             splice(@ARGV, $mflag, 1) if $mflag && !$ENV{CLEARCASE_WRAPPER_PAGER};
200             pipe(READER, WRITER);
201             my $pid;
202             if ($pid = fork) {
203             close WRITER;
204             open(STDIN, ">&READER") || die Msg('E', "STDIN: $!");
205             my $pager = $ENV{CLEARCASE_WRAPPER_PAGER} || $ENV{PAGER};
206             if (!$pager) {
207             require Config;
208             $pager = $Config::Config{pager} || 'more';
209             }
210             exec $pager || warn Msg('W', "can't run $pager: $!");
211             } else {
212             die Msg('E', "can't fork") if !defined($pid);
213             close READER;
214             open(STDOUT, ">&WRITER") || die Msg('E', "STDOUT: $!");
215             }
216             }
217              
218             # Implements the -P flag to pause after a GUI operation.
219             if (my $pflag = _FirstIndex('-P', @ARGV)) {
220             splice(@ARGV, $pflag, 1);
221             if (MSWIN) {
222             eval "END { system qw(cmd /c pause) }";
223             } else {
224             my $foo = ;
225             }
226             }
227              
228             #############################################################################
229             # Usage Message Extensions
230             #############################################################################
231             {
232 1     1   9 no strict 'vars';
  1         2  
  1         528  
233              
234             # Extended messages for actual cleartool commands that we extend.
235             $checkin = "\n* [-dir|-rec|-all|-avobs] [-ok] [-diff [diff-opts]]" .
236             "\n* [-revert [-mkhlink]]";
237             $checkout = "\n* [-dir|-rec] [-ok]";
238             $diff = "\n* [-] [-dir|-rec|-all|-avobs]";
239             $diffcr = "\n* [-data]";
240             $lsprivate = "\n* [-dir|-rec|-all] [-ecl/ipsed] [-type d|f]" .
241             "\n* [-rel/ative] [-ext] [pname]";
242             $lsview = "\n* [-me]";
243             $mkelem = "\n* [-dir|-rec] [-do] [-ok]";
244             $uncheckout = " * [-nc]";
245              
246             # Extended messages for pseudo cleartool commands that we implement here.
247             my $z = $ARGV[0] || '';
248             $edit = "$z [-ci] pname ...";
249             $extensions = "$z [-long]";
250             }
251              
252             #############################################################################
253             # Command Aliases
254             #############################################################################
255             *ci = *checkin;
256             *co = *checkout;
257             *lsp = *lsprivate;
258             *lspriv = *lsprivate;
259             *unco = *uncheckout;
260             *vi = *edit;
261              
262             #############################################################################
263             # Allow per-user configurability. Give the individual access to @ARGV just
264             # before we hand it off to the local wrapper function and/or cleartool.
265             # Access to this feature is suppressed if the 'NO_OVERRIDES' file exists.
266             #############################################################################
267             if (-r "$ENV{HOME}/.clearcase_profile.pl" && ! -e "$libdir/NO_OVERRIDES") {
268             require "$ENV{HOME}/.clearcase_profile.pl";
269 1     1   7 no warnings qw(redefine);
  1         11  
  1         1318  
270             *Argv::exec = $diemexec;
271             }
272              
273             # Add to ExtMap the names of extensions defined in the base package.
274             for (keys %ClearCase::Wrapper::) {
275             # Skip functions that can't be names of valid cleartool ops.
276             next if m%^(?:_?[A-Z]|__)%;
277             # Skip typeglobs that don't involve functions.
278             my $tglob = "ClearCase::Wrapper::$_";
279             next unless ref \&{$tglob};
280             # Take what survives the above tests and create a hash
281             # mapping defined functions to the pkg that defines them.
282             $ExtMap{$_} ||= __PACKAGE__;
283             }
284              
285             # Returns undefined if is not being extended and returns the
286             # package that extends it otherwise. Potentially useful for extension
287             # writers.
288             sub Extension {
289 0     0 0   my $op = shift;
290 0           return $ExtMap{$op};
291             }
292              
293             # Returns the full name of a command, whether native or not; unchanged in error
294             sub Canonic {
295 0     0 0   my $op = shift;
296 0           my $tglob = $ExtMap{$op} . "::" . $op;
297 0           my $coderef = \&{$tglob};
  0            
298 0 0         return $op unless ref $coderef;
299 0           my $cv = B::svref_2object($coderef);
300 0           return $cv->GV->NAME;
301             }
302              
303             # Returns a boolean indicating whether the named cmd is native to
304             # CC or not. Note: the first call to this func has a "cost" of one
305             # "cleartool help" operation; subsequent calls are free.
306             {
307             my %native;
308             sub Native {
309 0     0 0   my $op = shift;
310 0 0         return 1 if $op =~ m%^lsp(riv)?%;
311 0 0         if (! $op) {
312 0           ($op = (caller(1))[3]) =~ s%.*:%%;
313             }
314 0 0         if (! keys %native) {
315 0           my @usg = grep /^Usage:/, ClearCase::Argv->help->qx;
316 0           for (@usg) {
317 0 0         if (/^Usage:\s*(\w+)\s*(\|\s*(\w+))?/) {
318 0 0         $native{$1} = 1 if $1;
319 0 0         $native{$3} = 1 if $3;
320             }
321             }
322             }
323 0 0         if (exists($native{$op})) {
    0          
324 0           return 1;
325             } elsif ($op =~ m%^(?:des|lsh)%) {
326 0           return 1;
327             } else {
328 0           return 0;
329             }
330             }
331             }
332              
333             # This is an enhancement like the ones below but is kept "above the
334             # fold" because wrapping of cleartool man is an integral and generic
335             # part of the module. It runs "cleartool man " as requested,
336             # followed by "perldoc ClearCase::Wrapper" iff is extended below.
337             sub man {
338 0     0 0   $_ = Canonic($_) for @ARGV[1..$#ARGV];
339 0           my $page = (grep !/^-/, @ARGV)[1];
340 0 0         return 0 unless $page;
341 0 0         ClearCase::Argv->new(@ARGV)->system if Native($page);
342 0 0         if (exists($ClearCase::Wrapper::{$page})) {
    0          
343             # This EV hack causes perldoc to search for the right keyword
344             # within the module's perldoc.
345 0           if (!MSWIN) {
346 0           require Config;
347 0           my $pager = $Config::Config{pager};
348 0 0 0       $ENV{PERLDOC_PAGER} ||= "$pager +/" . uc($page)
349             if $pager =~ /more|less/;
350             }
351             } elsif ($page ne $::prog) {
352 0 0         if (!Native($page)) {
353 0           ClearCase::Argv->new(@ARGV)->exec;
354             } else {
355 0 0         exit($? ? 1 : 0);
356             }
357             }
358 0           my $psep = MSWIN ? ';' : ':';
359 0           require File::Basename;
360 0           $ENV{PATH} = join($psep, File::Basename::dirname($^X), $ENV{PATH});
361 0   0       my $module = $ExtMap{$page} || __PACKAGE__;
362 0           Argv->perldoc($module)->exec;
363 0           exit $?;
364             }
365              
366             1;
367              
368             __END__