File Coverage

blib/lib/App/PerlShell.pm
Criterion Covered Total %
statement 35 306 11.4
branch 0 174 0.0
condition 0 39 0.0
subroutine 13 29 44.8
pod 15 15 100.0
total 63 563 11.1


line stmt bran cond sub pod time code
1             package App::PerlShell;
2              
3             ########################################################
4             # AUTHOR = Michael Vincent
5             # www.VinsWorld.com
6             ########################################################
7              
8 1     1   76544 use strict;
  1         11  
  1         33  
9 1     1   6 use warnings;
  1         2  
  1         27  
10 1     1   5 use Carp;
  1         2  
  1         99  
11              
12             our $VERSION = "1.07";
13              
14 1     1   7 use Cwd;
  1         2  
  1         79  
15 1     1   611 use Term::ReadLine;
  1         3075  
  1         41  
16 1     1   709 use Data::Dumper;
  1         7698  
  1         173  
17             $Data::Dumper::Sortkeys = 1;
18              
19             my $HAVE_LexPersist = 0;
20 1     1   233 eval "use Lexical::Persistence 1.01 ()";
  0         0  
  0         0  
21             if ( !$@ ) {
22             eval "use App::PerlShell::LexPersist";
23             $HAVE_LexPersist = 1;
24             }
25             my $HAVE_ModRefresh = 0;
26 1     1   259 eval "use Module::Refresh";
  0            
  0            
27             if ( !$@ ) {
28             eval "use App::PerlShell::ModRefresh";
29             $HAVE_ModRefresh = 1;
30             }
31              
32             my %COMMANDS_INT = (
33             debug => 'print command',
34             exit => 'exit shell'
35             );
36              
37             my %COMMANDS = (
38             cd => 'change directory',
39             cls => 'clear screen',
40             clear => 'clear screen',
41             commands => 'print available "commands" (sub)',
42             dir => 'directory listing',
43             dumper => 'use Data::Dumper to display variable',
44             help => 'shell help - print this',
45             ls => 'directory listing',
46             modules => 'list used modules',
47             perldoc => 'perldoc for current package',
48             pwd => 'print working directory',
49             session => 'start / stop logging session',
50             variables => 'list defined variables'
51             );
52              
53 1     1   11 use Exporter;
  1         2  
  1         795  
54             our @EXPORT = sort ( keys ( %COMMANDS ) );
55             our @ISA = qw ( Exporter );
56              
57             sub _shellCommands {
58 0     0     return ( %COMMANDS, %COMMANDS_INT );
59             }
60              
61             sub new {
62 0     0 1   my $self = shift;
63 0   0       my $class = ref($self) || $self;
64              
65             # Default parameters
66             my %params = (
67             homedir => ( $^O eq "MSWin32" ) ? $ENV{USERPROFILE} : $ENV{HOME},
68 0 0         package => __PACKAGE__,
69             prompt => 'Perl> '
70             );
71              
72 0           my $lex = 0;
73 0 0         if ( @_ == 1 ) {
74 0           croak("Insufficient number of args - @_");
75             } else {
76 0           my %cfg = @_;
77 0           for ( keys(%cfg) ) {
78 0 0         if (/^-?homedir$/i) {
    0          
    0          
    0          
    0          
    0          
    0          
79 0 0         if ( -d $cfg{$_} ) {
80 0           $params{homedir} = $cfg{$_};
81             } else {
82 0           croak("Cannot find directory `$cfg{$_}'");
83             }
84             } elsif (/^-?execute$/i) {
85 0           $params{execute} = $cfg{$_};
86             } elsif (/^-?lex(?:ical)?$/i) {
87 0           $lex = 1;
88             } elsif (/^-?package$/i) {
89 0           $params{package} = $cfg{$_};
90             } elsif (/^-?prompt$/i) {
91 0           $params{prompt} = $cfg{$_};
92             } elsif (/^-?session$/i) {
93              
94             # assign, will test open in run()
95 0           $params{session} = $cfg{$_};
96             } elsif (/^-?skipvars$/i) {
97 0 0         if ( ref $cfg{$_} eq 'ARRAY' ) {
98 0           $params{skipvars} = $cfg{$_};
99             } else {
100 0           croak("Not array reference `$cfg{$_}'");
101             }
102             } else {
103 0           croak("Unknown parameter `$_' => `$cfg{$_}'");
104             }
105             }
106             }
107              
108 0 0         if ($lex) {
109 0 0         if ($HAVE_LexPersist) {
110             $params{shellLexEnv}
111 0           = App::PerlShell::LexPersist->new( $params{package} );
112             } else {
113 0           croak(
114             "-lexical specified, `Lexical::Persistence' required but not found"
115             );
116             }
117             } else {
118 0           $ENV{PERLSHELL_PACKAGE} = $params{package};
119             }
120 0           $ENV{PERLSHELL_HOME} = $params{homedir};
121 0           $ENV{PERLSHELL_PROMPT} = $params{prompt};
122 0 0         if ( defined $params{skipvars} ) {
123 0           $ENV{PERLSHELL_SKIPVARS} = join ';', @{$params{skipvars}};
  0            
124             }
125              
126             # clean up object
127 0           delete $params{homedir};
128 0           delete $params{package};
129 0           delete $params{prompt};
130 0           delete $params{skipvars};
131 0           return bless \%params, $class;
132             }
133              
134             sub run {
135 0     0 1   my $App_PerlShell_Shell = shift;
136              
137             # handle session if requested
138 0 0         if ( defined $App_PerlShell_Shell->{session} ) {
139 0 0         if ( not defined session( $App_PerlShell_Shell->{session} ) ) {
140 0           croak(
141             "Cannot open session file `$App_PerlShell_Shell->{session}'");
142             }
143             }
144              
145 0 0         if ( exists $App_PerlShell_Shell->{shellLexEnv} ) {
146             $App_PerlShell_Shell->{shell}
147 0           = $App_PerlShell_Shell->{shellLexEnv}->get_package();
148             } else {
149 0           $App_PerlShell_Shell->{shell} = $ENV{PERLSHELL_PACKAGE};
150             }
151             $App_PerlShell_Shell->{shell}
152 0           = Term::ReadLine->new( $App_PerlShell_Shell->{shell} );
153 0           $App_PerlShell_Shell->{shell}->ornaments(0);
154              
155             #'use strict' is not used to allow "$p=" instead of "my $p=" at the prompt
156 1     1   10 no strict 'vars';
  1         3  
  1         1455  
157              
158             # will always exeucte without readline first time through (do ... while)
159 0           while (1) {
160              
161             # do ... while loop won't support next and last
162             # First check then clear {execute} to autopopulate
163             # $App_PerlShell_Shell->{shellCmdLine}
164             # otherwise, just do the readline.
165              
166 0 0         if ( defined $App_PerlShell_Shell->{execute} ) {
167             $App_PerlShell_Shell->{shellCmdLine}
168 0           = $App_PerlShell_Shell->{execute};
169              
170             # clear - it will never happen again
171 0           delete $App_PerlShell_Shell->{execute};
172             } else {
173             $App_PerlShell_Shell->{shellCmdLine}
174             .= $App_PerlShell_Shell->{shell}->readline(
175             ( defined $App_PerlShell_Shell->{shellCmdLine} )
176             ? 'More? '
177             : $ENV{PERLSHELL_PROMPT}
178 0 0         );
179             }
180              
181 0           chomp $App_PerlShell_Shell->{shellCmdLine};
182              
183             # nothing
184 0 0         if ( $App_PerlShell_Shell->{shellCmdLine} =~ /^\s*$/ ) {
185 0           undef $App_PerlShell_Shell->{shellCmdLine};
186 0           next;
187             }
188              
189             # exit
190 0 0         if ( $App_PerlShell_Shell->{shellCmdLine} =~ /^\s*exit\s*(;)?\s*$/ ) {
191 0 0         if ( not defined $1 ) {
192 0           $App_PerlShell_Shell->{shellCmdLine} .= ';';
193             }
194 0           last;
195             }
196              
197             # debug multiline
198 0 0         if ( $App_PerlShell_Shell->{shellCmdLine} =~ /\ndebug$/ ) {
199 0           $App_PerlShell_Shell->{shellCmdLine} =~ s/debug$//;
200 0           print Dumper $App_PerlShell_Shell;
201 0           next;
202             }
203              
204             # variables if in -lexical
205 0 0         if ( exists $App_PerlShell_Shell->{shellLexEnv} ) {
206 0 0         if ( $App_PerlShell_Shell->{shellCmdLine}
207             =~ /^\s*variables\s*;\s*$/ ) {
208 0           for my $var (
209             sort( keys(
210             %{ $App_PerlShell_Shell->{shellLexEnv}
211 0           ->get_context('_')
212             }
213             ) )
214             ) {
215 0           print "$var\n";
216             }
217 0           undef $App_PerlShell_Shell->{shellCmdLine};
218 0           next;
219             }
220             }
221              
222             # Complete statement
223 0           %{$App_PerlShell_Shell->{shellCmdComplete}} = (
  0            
224             'parenthesis' => 0,
225             'bracket' => 0,
226             'brace' => 0
227             );
228 0 0         if ( my @c = ( $App_PerlShell_Shell->{shellCmdLine} =~ /\(/g ) ) {
229             $App_PerlShell_Shell->{shellCmdComplete}->{parenthesis}
230 0           += scalar(@c);
231             }
232 0 0         if ( my @c = ( $App_PerlShell_Shell->{shellCmdLine} =~ /\)/g ) ) {
233             $App_PerlShell_Shell->{shellCmdComplete}->{parenthesis}
234 0           -= scalar(@c);
235             }
236 0 0         if ( my @c = ( $App_PerlShell_Shell->{shellCmdLine} =~ /\[/g ) ) {
237 0           $App_PerlShell_Shell->{shellCmdComplete}->{bracket} += scalar(@c);
238             }
239 0 0         if ( my @c = ( $App_PerlShell_Shell->{shellCmdLine} =~ /\]/g ) ) {
240 0           $App_PerlShell_Shell->{shellCmdComplete}->{bracket} -= scalar(@c);
241             }
242 0 0         if ( my @c = ( $App_PerlShell_Shell->{shellCmdLine} =~ /\{/g ) ) {
243 0           $App_PerlShell_Shell->{shellCmdComplete}->{brace} += scalar(@c);
244             }
245 0 0         if ( my @c = ( $App_PerlShell_Shell->{shellCmdLine} =~ /\}/g ) ) {
246 0           $App_PerlShell_Shell->{shellCmdComplete}->{brace} -= scalar(@c);
247             }
248              
249 0 0 0       if (( ( $App_PerlShell_Shell->{shellCmdLine} =~ /,\s*$/ )
      0        
      0        
      0        
      0        
      0        
      0        
250             or ( $App_PerlShell_Shell->{shellCmdComplete}->{parenthesis} != 0 )
251             or ( $App_PerlShell_Shell->{shellCmdComplete}->{bracket} != 0 )
252             or ( $App_PerlShell_Shell->{shellCmdComplete}->{brace} != 0 )
253             )
254             or
255              
256             # valid endings are ; or }, but only if all groupings are closed
257             ( ( $App_PerlShell_Shell->{shellCmdLine} !~ /(;|\})\s*$/ )
258             and ( $App_PerlShell_Shell->{shellCmdComplete}->{parenthesis} == 0 )
259             and ( $App_PerlShell_Shell->{shellCmdComplete}->{bracket} == 0 )
260             and ( $App_PerlShell_Shell->{shellCmdComplete}->{brace} == 0 )
261             )
262             ) {
263 0 0         if ( $App_PerlShell_Shell->{shellCmdLine} !~ /\n;$/ ) {
264 0           $App_PerlShell_Shell->{shellCmdLine} .= "\n";
265 0           next;
266             }
267             }
268              
269             # import subs if not default package
270             # use redundant code in the if block so no variables are
271             # created at top level in case we're not using LexPersist
272 0 0         if ( exists $App_PerlShell_Shell->{shellLexEnv} ) {
273 0 0         if ( $App_PerlShell_Shell->{shellLexEnv}->get_package() ne
274             __PACKAGE__ ) {
275 0           my $sp = $App_PerlShell_Shell->{shellLexEnv}->get_package();
276 0           my $p = __PACKAGE__;
277 0           eval "package $sp; $p->import;";
278 0 0         if ($HAVE_ModRefresh) {
279 0           App::PerlShell::ModRefresh->refresh($sp);
280             }
281             }
282              
283             # execute
284 0           eval {
285             $App_PerlShell_Shell->{shellLexEnv}
286 0           ->do( $App_PerlShell_Shell->{shellCmdLine} );
287             };
288             } else {
289 0 0         if ( $ENV{PERLSHELL_PACKAGE} ne __PACKAGE__ ) {
290 0           my $sp = $ENV{PERLSHELL_PACKAGE};
291 0           my $p = __PACKAGE__;
292 0           eval "package $sp; $p->import;";
293 0 0         if ($HAVE_ModRefresh) {
294 0           App::PerlShell::ModRefresh->refresh($sp);
295             }
296             }
297             $App_PerlShell_Shell->{shellCmdLine}
298             = "package "
299             . $ENV{PERLSHELL_PACKAGE} . ";\n"
300             . $App_PerlShell_Shell->{shellCmdLine}
301 0           . "\nBEGIN {\$ENV{PERLSHELL_PACKAGE} = __PACKAGE__}";
302              
303             # execute
304 0           eval $App_PerlShell_Shell->{shellCmdLine};
305             }
306              
307             # error from execute?
308 0 0         warn $@ if ($@);
309              
310             # logging if requested and no error
311 0 0 0       if ( defined( $ENV{PERLSHELL_SESSION} ) and !$@ ) {
312              
313             # don't log session start command
314 0           $App_PerlShell_Shell->{shellCmdLine} =~ s/\s*session\s*.*//;
315              
316             # clean up command if we added stuff while not in -lex mode
317 0           $App_PerlShell_Shell->{shellCmdLine} =~ s/^package .*;\n//;
318             $App_PerlShell_Shell->{shellCmdLine}
319 0           =~ s/(?:\n)?BEGIN \{\$ENV\{PERLSHELL_PACKAGE\} = __PACKAGE__\}//;
320              
321 0           open( my $OUT, '>>', $ENV{PERLSHELL_SESSION} );
322             print $OUT "$App_PerlShell_Shell->{shellCmdLine}\n"
323 0 0         if ( $App_PerlShell_Shell->{shellCmdLine} ne '' );
324 0           close $OUT;
325             }
326              
327             # reset to normal before next input
328             $App_PerlShell_Shell->{shellLastCmd}
329 0           = $App_PerlShell_Shell->{shellCmdLine};
330 0           undef $App_PerlShell_Shell->{shellCmdLine};
331 0           print "\n";
332             }
333             }
334              
335             ########################################################
336             # commands
337             ########################################################
338              
339             sub cd {
340 0     0 1   my ($arg) = @_;
341              
342 0           my $ret = getcwd;
343 0 0         if ( not defined $arg ) {
344 0           chdir $ENV{PERLSHELL_HOME};
345             } else {
346 0 0         if ( -e $arg ) {
347 0           chdir $arg;
348             } else {
349 0           print "Cannot find directory `$arg'\n";
350             }
351             }
352 0 0         if ( defined wantarray ) {
353 0           return $ret;
354             }
355             }
356              
357             sub cls {
358 0     0 1   return clear();
359             }
360              
361             sub clear {
362 0 0   0 1   if ( $^O eq "MSWin32" ) {
363 0           system('cls');
364             } else {
365 0           system('clear');
366             }
367             }
368              
369             sub commands {
370 0     0 1   my ($arg) = @_;
371              
372 0           my @rets;
373 0           my $retType = wantarray;
374              
375 0           my $stash = $ENV{PERLSHELL_PACKAGE} . '::';
376              
377 1     1   11 no strict 'refs';
  1         2  
  1         259  
378              
379 0           my $regex = qr/^.*$/;
380 0 0         if ( defined($arg) ) {
381 0           $regex = qr/$arg/;
382             }
383              
384 0           for my $name ( sort( keys( %{$stash} ) ) ) {
  0            
385 0 0         next if ( $name =~ /^_/ );
386              
387 0           my $sub = *{"${stash}${name}"}{CODE};
  0            
388 0 0         next unless defined $sub;
389              
390 0           my $proto = prototype($sub);
391 0 0 0       next if defined $proto and length($proto) == 0;
392              
393 0 0         if ( $name =~ /$regex/ ) {
394 0           push @rets, $name;
395             }
396             }
397              
398 0 0         if ( not defined $retType ) {
    0          
399 0           for (@rets) {
400 0           print "$_\n";
401             }
402             } elsif ($retType) {
403 0           return @rets;
404             } else {
405 0           return \@rets;
406             }
407             }
408              
409             sub dumper {
410 0     0 1   my (@dump) = @_;
411              
412 1     1   9 use Data::Dumper;
  1         3  
  1         1255  
413 0           $Data::Dumper::Sortkeys = 1;
414 0           print Dumper @dump;
415             }
416              
417             sub help {
418 0     0 1   my %cmds = _shellCommands();
419 0           for my $h ( sort( keys(%cmds) ) ) {
420 0           printf "%-15s %s\n", $h, $cmds{$h};
421             }
422             }
423              
424             sub dir {
425 0     0 1   return ls(@_);
426             }
427              
428             sub ls {
429 0     0 1   my (@args) = @_;
430              
431 0           my $dircmd = 'ls';
432 0 0         if ( $^O eq "MSWin32" ) {
433 0           $dircmd = 'dir';
434             }
435              
436 0           my @allArgs;
437 0           for my $arg ( @args ) {
438 0           my @temp = split /\s+/, $arg;
439 0           push @allArgs, @temp;
440             }
441 0           @args = @allArgs;
442              
443 0           my @ret;
444 0           my $retType = wantarray;
445 0 0         if ( not defined $retType ) {
446 0           system( $dircmd, @args );
447             } else {
448 0           @ret = `$dircmd @args`;
449 0 0         if ($retType) {
450 0           return @ret;
451             } else {
452 0           return \@ret;
453             }
454             }
455             }
456              
457             sub modules {
458 0     0 1   my ($arg) = @_;
459              
460 0           my %rets;
461 0           my $retType = wantarray;
462              
463 0           my $FOUND = 0;
464 0           for my $module ( sort( keys(%INC) ) ) {
465 0           my $path = $INC{$module};
466 0           $module =~ s/\//::/g;
467 0           $module =~ s/\.pm$//;
468              
469 0 0         if ( defined $arg ) {
470 0 0         if ( $module =~ /$arg/ ) {
471 0           $rets{$module} = $path;
472 0           $FOUND = 1;
473             }
474             } else {
475 0           $rets{$module} = $path;
476 0           $FOUND = 1;
477             }
478             }
479              
480 0 0         if ( !$FOUND ) {
481 0 0         printf "Module(s) not found%s",
482             ( defined $arg ) ? " - `$arg'\n" : "\n";
483             }
484              
485 0 0         if ( not defined $retType ) {
    0          
486 0           for my $module ( sort( keys(%rets) ) ) {
487             printf "$module %s\n",
488 0 0         ( defined $rets{$module} ) ? $rets{$module} : "[NOT LOADED]";
489             }
490             } elsif ($retType) {
491 0           return %rets;
492             } else {
493 0           return \%rets;
494             }
495             }
496              
497             sub perldoc {
498 0     0 1   my (@args) = @_;
499              
500 0 0         if ( $#args == -1 ) {
501 0           push @args, $ENV{PERLSHELL_PACKAGE};
502             }
503              
504 0           my @allArgs;
505 0           for my $arg ( @args ) {
506 0           my @temp = split /\s+/, $arg;
507 0           push @allArgs, @temp;
508             }
509 0           @args = @allArgs;
510              
511 0 0         if ( $args[0] =~ /^::/ ) {
512 0           $args[0] = $ENV{PERLSHELL_PACKAGE} . $args[0];
513             }
514              
515 0           system( "perldoc", @args );
516             }
517              
518             sub pwd {
519 0 0   0 1   if ( not defined wantarray ) {
520 0           print getcwd;
521             } else {
522 0           return getcwd;
523             }
524             }
525              
526             sub session {
527 0     0 1   my ($arg) = @_;
528              
529 0 0         if ( not defined $arg ) {
530 0 0         if ( defined $ENV{PERLSHELL_SESSION} ) {
531 0 0         if ( not defined wantarray ) {
532 0           print $ENV{PERLSHELL_SESSION} . "\n";
533             }
534 0           return $ENV{PERLSHELL_SESSION};
535             } else {
536 0 0         if ( not defined wantarray ) {
537 0           print "No current session file\n";
538             }
539 0           return undef;
540             }
541             }
542              
543 0 0         if ( $arg eq ":close" ) {
544 0 0         if ( defined $ENV{PERLSHELL_SESSION} ) {
545 0 0         if ( not defined wantarray ) {
546 0           print "$ENV{PERLSHELL_SESSION} closed\n";
547             }
548 0           $ENV{PERLSHELL_SESSION} = undef;
549 0           return;
550             } else {
551 0 0         if ( not defined wantarray ) {
552 0           print "No current session file\n";
553             }
554 0           return undef;
555             }
556             }
557              
558 0 0         if ( not defined $ENV{PERLSHELL_SESSION} ) {
559 0 0         if ( -e $arg ) {
560 0 0         if ( not defined wantarray ) {
561 0           print "File `$arg' exists - will append\n";
562             }
563             }
564              
565 0 0         if ( open( my $fh, '>>', $arg ) ) {
566 0           close $fh;
567 0           $ENV{PERLSHELL_SESSION} = $arg;
568 0 0         if ( not defined wantarray ) {
569 0           print $ENV{PERLSHELL_SESSION} . "\n";
570             }
571 0           return $ENV{PERLSHELL_SESSION};
572             } else {
573 0 0         if ( not defined wantarray ) {
574 0           print "Cannot open file `$arg' for writing\n";
575             }
576 0           return undef;
577             }
578             } else {
579 0 0         if ( not defined wantarray ) {
580 0           print "Session file already open - `$ENV{PERLSHELL_SESSION}'\n";
581             }
582 0           return;
583             }
584             }
585              
586             sub variables {
587              
588 0     0 1   my %SKIP = (
589             '$VERSION' => 1,
590             '@ISA' => 1,
591             '@EXPORT' => 1
592             );
593              
594 0 0         if ( !exists $ENV{PERLSHELL_PACKAGE} ) {
595 0           print "In -lexical mode, try again on line by iteslf\n";
596 0           return;
597             }
598              
599 0 0         if ( defined $ENV{PERLSHELL_SKIPVARS} ) {
600 0           for ( split /;/, $ENV{PERLSHELL_SKIPVARS} ) {
601 0           $SKIP{"$_"} = 1;
602             }
603             }
604              
605 0           my @rets;
606 0           my $retType = wantarray;
607              
608 1     1   12 no strict 'refs';
  1         3  
  1         297  
609 0           for my $var ( sort( keys( %{$ENV{PERLSHELL_PACKAGE} . '::'} ) ) ) {
  0            
610 0 0 0       if ( defined( ${$ENV{PERLSHELL_PACKAGE} . "::$var"} )
  0 0 0        
    0 0        
611             and not defined( $SKIP{'$' . $var} ) ) {
612 0           push @rets, "\$$var";
613 0           } elsif ( @{$ENV{PERLSHELL_PACKAGE} . "::$var"}
614             and not defined( $SKIP{'@' . $var} ) ) {
615 0           push @rets, "\@$var";
616 0           } elsif ( %{$ENV{PERLSHELL_PACKAGE} . "::$var"}
617             and not defined( $SKIP{'%' . $var} ) ) {
618 0           push @rets, "\%$var";
619             }
620             }
621              
622 0 0         if ( not defined $retType ) {
    0          
623 0           for (@rets) {
624 0           print "$_\n";
625             }
626             } elsif ($retType) {
627 0           return @rets;
628             } else {
629 0           return \@rets;
630             }
631             }
632              
633             1;
634              
635             __END__