File Coverage

blib/lib/Term/ShellKit.pm
Criterion Covered Total %
statement 79 110 71.8
branch 25 60 41.6
condition 2 3 66.6
subroutine 15 17 88.2
pod 0 8 0.0
total 121 198 61.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             # my $self_running = ( ! caller );
4             # END { Term::ShellKit::shell if ( $self_running ); }
5              
6             ######################################################################
7              
8             package Term::ShellKit;
9              
10             $VERSION = 1.002;
11             @EXPORT = qw( shell );
12 1 50   1   1675 sub import { require Exporter and goto &Exporter::import } # lazy Exporter
13              
14 1     1   682 use strict;
  1         3  
  1         42  
15 1     1   6 use Carp;
  1         2  
  1         122  
16 1     1   975 use Text::ParseWords;
  1         1685  
  1         87  
17              
18             ######################################################################
19              
20 1     1   7 use vars qw( $Prompt $SubReadLine );
  1         2  
  1         322  
21              
22             $SubReadLine = ( -t STDIN ) ? \&readline_term : \&readline_raw;
23             $Prompt ||= 'Term::ShellKit> ';
24              
25             sub readline_raw {
26 1 50   1 0 4 my $prompt = scalar(@_) ? shift : $Prompt;
27 1         2 print($prompt);
28 1         26 $_ = <>;
29 1 50       4 chomp if defined $_;
30 1         4 print($_."\n");
31 1         5 $_
32             }
33              
34             my $TermReadLine;
35              
36             sub readline_term {
37 0 0   0 0 0 if ( ! $TermReadLine ) {
38 0         0 require Term::ReadLine;
39 0         0 $TermReadLine = Term::ReadLine->new('Term-ShellKit');
40            
41             # $ShellReadLine->OUT is autoflushed anyway
42 0         0 my $odef = select STDERR;
43 0         0 $| = 1;
44 0         0 select STDOUT;
45 0         0 $| = 1;
46 0         0 select $odef;
47             }
48 0 0       0 my $prompt = scalar(@_) ? shift : $Prompt;
49 0         0 $TermReadLine->readline( $prompt )
50             }
51              
52             ######################################################################
53              
54 1     1   12 use vars qw( $SubDoCommand $CurrentPackage @CommandPackages );
  1         2  
  1         88  
55              
56             $SubDoCommand = \&do_command;
57             $CurrentPackage ||= 'main';
58              
59 1     1   5 use vars qw( @CommandQueue );
  1         2  
  1         630  
60              
61             sub do_command {
62 3     3 0 6 my $input = shift;
63              
64 3 50       9 length $input or return;
65            
66 3 50       26 $input =~ /\A\s*(\S+)(?:\s(?![\)])(.*))?\Z/
67             or die "Can't parse command line '$input'\n";
68            
69 3         13 my ($command, $args) = ( $1, $2 );
70            
71 3         4 my $sub;
72 3 50       23 if ( $command =~ /^(.*)::([^:]+)$/ ) {
73 3         11 my ($pack, $func) = ($1, $2);
74 3         19 $sub = UNIVERSAL::can($pack, $func);
75             } else {
76 0         0 foreach my $package ( $CurrentPackage, @CommandPackages ) {
77 0 0       0 if ( $sub = UNIVERSAL::can($package, $command) ) {
78 0         0 last;
79             }
80             }
81             }
82 3 50       9 if ( ! $sub ) {
83 0         0 foreach my $package ( @CommandPackages ) {
84 0 0       0 my $rewriter = UNIVERSAL::can($package, '_shell_rewrite') or next;
85 0         0 my @out = &$rewriter( $input );
86 0 0       0 if ( scalar @out ) {
87 0         0 unshift @CommandQueue, @out;
88 0         0 return;
89             }
90             }
91 0         0 die "Can't find command or function named '$command'\n";
92             }
93            
94 3         6 my $ptype = prototype( $sub );
95 3         4 my @args;
96 3 100 66     16 if ( ! defined $ptype or $ptype eq '@' ) {
    50          
    0          
97 2         3 eval { @args = Text::ParseWords::shellwords($args) };
  2         9  
98 2 50       335 croak("Can't parse arguments for $command($ptype): $@") if $@;
99             } elsif ( $ptype eq ';$' ) {
100 1 50       5 @args = defined($args) ? $args : ();
101             } elsif ( $ptype eq '$' ) {
102 0 0       0 croak("Missing required argument for $command($ptype)") unless (length $args);
103 0         0 @args = $args;
104             } else {
105 0         0 eval { @args = Text::ParseWords::shellwords($args) };
  0         0  
106 0 0       0 croak("Can't parse arguments for $command($ptype): $@") if $@;
107             }
108 3         11 &$sub( @args );
109             }
110              
111             sub command_rewrite {
112 0     0 0 0 unshift @CommandQueue, @_;
113 0         0 die "Term::ShellKit command completed";
114             }
115              
116             ######################################################################
117              
118 1     1   6 use vars qw( $PrintResultsSub );
  1         2  
  1         138  
119             $PrintResultsSub = \&print_results;
120              
121             # require Dumpvalue;
122             # $Dumper = Dumpvalue->new();
123             # print $Dumper->dumpValue($value);
124             sub print_results {
125 3 50   3 0 11 print join '', map { /\n\Z/m ? $_ : "$_\n" } grep { length $_ } @_;
  2         21  
  2         5  
126             }
127              
128             ######################################################################
129              
130 1     1   5 use vars qw( @DefaultStartup );
  1         2  
  1         544  
131             @DefaultStartup = (
132             'Term::ShellKit::require_package Term::ShellKit::Commands',
133             'Term::ShellKit::Commands::echo Term::ShellKit: Starting interactive shell; commands include help, exit.',
134             'Term::ShellKit::load_kit Commands',
135             @ARGV,
136             );
137              
138             sub shell {
139 1     1 0 9 local $Prompt = $Prompt;
140 1 50       8 local @CommandQueue = scalar(@_) ? @_ : @DefaultStartup;
141            
142 1         2 while ( 1 ) {
143 4 100       11 if ( ! scalar @CommandQueue ) {
144 1 50       5 my $get_cmd = $SubReadLine or confess "No \$SubReadLine";
145 1         4 @CommandQueue = &$get_cmd();
146             }
147            
148 4         9 my $cmd = shift @CommandQueue;
149 4 100       9 ( defined $cmd ) or last;
150            
151 3         4 my @results = eval { &$SubDoCommand( $cmd ) };
  3         9  
152 3 50       9 if ( $@ ) {
153 0 0       0 if ( $@ =~ /Term::ShellKit command completed/ ) {
154 0         0 next;
155             } else {
156 0         0 warn "Exception: $@";
157             }
158             }
159 3         10 &$PrintResultsSub( @results );
160             }
161             }
162              
163             ######################################################################
164              
165             sub require_package {
166 2     2 0 6 my $package = shift;
167 2         5 $package =~ s/;\s*$//;
168            
169 2         11 (my $file = $package . '.pm' ) =~ s|::|/|go;
170 2 100       9 return $package if ( $::INC{ $file } );
171            
172 1         2 eval {
173 1         7 local $SIG{__DIE__} = '';
174 1         1060 require $file;
175             };
176            
177 1 50       9 if ( $@ ) {
178 0         0 die "Unable to dynamically load $package: $@"
179             }
180            
181             return
182 1         7 }
183              
184             sub load_kit {
185 1     1 0 2 my $package = shift;
186 1         3 $package =~ s/;\s*$//;
187            
188 1 50       7 $package = "Term::ShellKit::$package" unless $package =~ /::/;
189            
190 1         5 require_package($package);
191 1 50       19 $package->import if ( $package->can('import') );
192            
193 1         3 push @CommandPackages, $package;
194 1         6 "Activating $package";
195             }
196              
197             ######################################################################
198              
199             package main;
200             Term::ShellKit::shell unless caller;
201              
202             ######################################################################
203              
204             1;
205              
206             __END__