File Coverage

blib/lib/Term/Shell.pm
Criterion Covered Total %
statement 216 530 40.7
branch 56 208 26.9
condition 24 135 17.7
subroutine 37 83 44.5
pod 2 60 3.3
total 335 1016 32.9


line stmt bran cond sub pod time code
1             package Term::Shell;
2             $Term::Shell::VERSION = '0.13';
3 3     3   216174 use strict;
  3         36  
  3         96  
4 3     3   16 use warnings;
  3         6  
  3         84  
5              
6 3     3   77 use 5.014;
  3         11  
7              
8 3     3   2001 use Data::Dumper;
  3         21787  
  3         215  
9 3     3   1595 use Term::ReadLine ();
  3         8757  
  3         2270  
10              
11             #=============================================================================
12             # Term::Shell API methods
13             #=============================================================================
14             sub new
15             {
16 3     3 0 1139 my $cls = shift;
17             my $o = bless {
18 3   50     10 term => eval {
      33        
19              
20             # Term::ReadKey throws ugliness all over the place if we're not
21             # running in a terminal, which we aren't during "make test", at
22             # least on FreeBSD. Suppress warnings here.
23       0     local $SIG{__WARN__} = sub { };
24             Term::ReadLine->new('shell');
25             }
26             || undef,
27             },
28             ref($cls)
29             || $cls;
30              
31             # Set up the API hash:
32 3         52428 $o->{command} = {};
33             $o->{API} = {
34             args => \@_,
35             case_ignore => ( $^O eq 'MSWin32' ? 1 : 0 ),
36             check_idle => 0, # changing this isn't supported
37             class => $cls,
38             command => $o->{command},
39             cmd => $o->{command}, # shorthand
40             match_uniq => 1,
41             pager => $ENV{PAGER} || 'internal',
42 3 50 50     89 readline => eval { $o->{term}->ReadLine } || 'none',
      50        
43             script => ( caller(0) )[1],
44             version => $Term::Shell::VERSION,
45             };
46              
47             # Note: the rl_completion_function doesn't pass an object as the first
48             # argument, so we have to use a closure. This has the unfortunate effect
49             # of preventing two instances of Term::ReadLine from coexisting.
50             my $completion_handler = sub {
51 0     0   0 $o->rl_complete(@_);
52 3         258 };
53 3 50       28 if ( $o->{API}{readline} eq 'Term::ReadLine::Gnu' )
    50          
54             {
55 0         0 my $attribs = $o->{term}->Attribs;
56 0         0 $attribs->{completion_function} = $completion_handler;
57             }
58             elsif ( $o->{API}{readline} eq 'Term::ReadLine::Perl' )
59             {
60 0         0 $readline::rl_completion_function = $readline::rl_completion_function =
61             $completion_handler;
62             }
63 3         87 $o->find_handlers;
64 3         35 $o->init;
65 3         25 $o;
66             }
67              
68             sub DESTROY
69             {
70 3     3   3287 my $o = shift;
71 3         72 $o->fini;
72             }
73              
74             sub cmd
75             {
76 0     0 0 0 my $o = shift;
77 0         0 $o->{line} = shift;
78 0 0       0 if ( $o->line =~ /\S/ )
79             {
80 0         0 my ( $cmd, @args ) = $o->line_parsed;
81 0         0 $o->run( $cmd, @args );
82 0 0       0 unless ( $o->{command}{run}{found} )
83             {
84 0         0 my @c = sort $o->possible_actions( $cmd, 'run' );
85 0 0 0     0 if ( @c and $o->{API}{match_uniq} )
86             {
87 0         0 print $o->msg_ambiguous_cmd( $cmd, @c );
88             }
89             else
90             {
91 0         0 print $o->msg_unknown_cmd($cmd);
92             }
93             }
94             }
95             else
96             {
97 0         0 $o->run('');
98             }
99             }
100              
101 0     0 0 0 sub stoploop { $_[0]->{stop}++ }
102              
103             sub cmdloop
104             {
105 0     0 0 0 my $o = shift;
106 0         0 $o->{stop} = 0;
107 0         0 $o->preloop;
108 0         0 while ( defined( my $line = $o->readline( $o->prompt_str ) ) )
109             {
110 0         0 $o->cmd($line);
111 0 0       0 last if $o->{stop};
112             }
113 0         0 $o->postloop;
114             }
115             *mainloop = \&cmdloop;
116              
117             sub readline
118             {
119 0     0 1 0 my $o = shift;
120 0         0 my $prompt = shift;
121             return $o->{term}->readline($prompt)
122             if $o->{API}{check_idle} == 0
123 0 0 0     0 or not defined $o->{term}->IN;
124              
125             # They've asked for idle-time running of some user command.
126 0         0 local $Term::ReadLine::toloop = 1;
127             local *Tk::fileevent = sub {
128 0     0   0 my $cls = shift;
129 0         0 my ( $file, $boring, $callback ) = @_;
130 0         0 $o->{fh} = $file; # save the filehandle!
131 0         0 $o->{cb} = $callback; # save the callback!
132 0         0 };
133             local *Tk::DoOneEvent = sub {
134              
135             # We'll totally cheat and do a select() here -- the timeout will be
136             # $o->{API}{check_idle}; if the handle is ready, we'll call &$cb;
137             # otherwise we'll call $o->idle(), which can do some processing.
138 0     0   0 my $timeout = $o->{API}{check_idle};
139 3     3   1605 use IO::Select;
  3         5297  
  3         15209  
140 0 0       0 if ( IO::Select->new( $o->{fh} )->can_read($timeout) )
141             {
142             # Input is ready: stop the event loop.
143 0         0 $o->{cb}->();
144             }
145             else
146             {
147 0         0 $o->idle;
148             }
149 0         0 };
150 0         0 $o->{term}->readline($prompt);
151             }
152              
153 0     0 0 0 sub term { $_[0]->{term} }
154              
155             # These are likely candidates for overriding in subclasses
156       3 0   sub init { } # called last in the ctor
157       3 0   sub fini { } # called first in the dtor
158       0 0   sub preloop { }
159       0 0   sub postloop { }
160       2 0   sub precmd { }
161       2 0   sub postcmd { }
162 0     0 0 0 sub prompt_str { 'shell> ' }
163       0 0   sub idle { }
164 55     55 0 122 sub cmd_prefix { '' }
165 55     55 0 99 sub cmd_suffix { '' }
166              
167             #=============================================================================
168             # The pager
169             #=============================================================================
170             sub page
171             {
172 1     1 0 9 my $o = shift;
173 1         7 my $text = shift;
174 1   33     28 my $maxlines = shift || $o->termsize->{rows};
175 1         22 my $pager = $o->{API}{pager};
176              
177             # First, count the number of lines in the text:
178 1         9 my $lines = ( $text =~ tr/\n// );
179              
180             # If there are fewer lines than the page-lines, just print it.
181 1 50 33     123 if ( $lines < $maxlines or $maxlines == 0 or $pager eq 'none' )
    0 33        
      0        
182             {
183 1         64 print $text;
184             }
185              
186             # If there are more, page it, either using the external pager...
187             elsif ( $pager and $pager ne 'internal' )
188             {
189 0         0 require File::Temp;
190 0         0 my ( $handle, $name ) = File::Temp::tempfile();
191 0         0 select( ( select($handle), $| = 1 )[0] );
192 0         0 print $handle $text;
193 0         0 close $handle;
194 0 0       0 system( $pager, $name ) == 0
195             or print <
196             Warning: can't run external pager '$pager': $!.
197             END
198 0         0 unlink $name;
199             }
200              
201             # ... or the internal one
202             else
203             {
204 0         0 my $togo = $lines;
205 0         0 my $line = 0;
206 0         0 my @lines = split '^', $text;
207 0         0 while ( $togo > 0 )
208             {
209 0         0 my @text = @lines[ $line .. $#lines ];
210 0         0 my $ret = $o->page_internal( \@text, $maxlines, $togo, $line );
211 0 0       0 last if $ret == -1;
212 0         0 $line += $ret;
213 0         0 $togo -= $ret;
214             }
215 0         0 return $line;
216             }
217 1         47 return $lines;
218             }
219              
220             sub page_internal
221             {
222 0     0 0 0 my $o = shift;
223 0         0 my $lines = shift;
224 0         0 my $maxlines = shift;
225 0         0 my $togo = shift;
226 0         0 my $start = shift;
227              
228 0         0 my $line = 1;
229 0         0 while ( $_ = shift @$lines )
230             {
231 0         0 print;
232 0 0       0 last if $line >= ( $maxlines - 1 ); # leave room for the prompt
233 0         0 $line++;
234             }
235 0         0 my $lines_left = $togo - $line;
236 0         0 my $current_line = $start + $line;
237 0         0 my $total_lines = $togo + $start;
238              
239 0         0 my $instructions;
240 0 0       0 if ( $o->have_readkey )
241             {
242 0         0 $instructions = "any key for more, or q to quit";
243             }
244             else
245             {
246 0         0 $instructions = "enter for more, or q to quit";
247             }
248              
249 0 0       0 if ( $lines_left > 0 )
250             {
251 0         0 local $| = 1;
252 0         0 my $l = "---line $current_line/$total_lines ($instructions)---";
253 0         0 my $b = ' ' x length($l);
254 0         0 print $l;
255 0         0 my $ans = $o->readkey;
256 0 0       0 print "\r$b\r" if $o->have_readkey;
257 0 0 0     0 print "\n" if $ans =~ /q/i or not $o->have_readkey;
258 0 0       0 $line = -1 if $ans =~ /q/i;
259             }
260 0         0 $line;
261             }
262              
263             #=============================================================================
264             # Run actions
265             #=============================================================================
266             sub run
267             {
268 0     0 0 0 my $o = shift;
269 0         0 my $action = shift;
270 0         0 my @args = @_;
271 0         0 $o->do_action( $action, \@args, 'run' );
272             }
273              
274             sub complete
275             {
276 0     0 0 0 my $o = shift;
277 0         0 my $action = shift;
278 0         0 my @args = @_;
279 0         0 my @compls = $o->do_action( $action, \@args, 'comp' );
280 0 0       0 return () unless $o->{command}{comp}{found};
281 0         0 return @compls;
282             }
283              
284             sub help
285             {
286 0     0 0 0 my $o = shift;
287 0         0 my $topic = shift;
288 0         0 my @subtopics = @_;
289 0         0 $o->do_action( $topic, \@subtopics, 'help' );
290             }
291              
292             sub summary
293             {
294 3     3 0 31 my $o = shift;
295 3         4 my $topic = shift;
296 3         31 $o->do_action( $topic, [], 'smry' );
297             }
298              
299             #=============================================================================
300             # Manually add & remove handlers
301             #=============================================================================
302             sub add_handlers
303             {
304 5     5 0 13 my $o = shift;
305             my $match = sub {
306 498 100   498   1147 if ( my ($ret) = shift =~ /\A(run|help|smry|comp|catch|alias)_/ )
307             {
308 64         136 return $ret;
309             }
310 434         576 return;
311 5         40 };
312             LOOP1:
313 5         30 for my $hnd (@_)
314             {
315 249         351 my $t = $match->($hnd);
316 249 100       456 next LOOP1 if !defined $t;
317 32         62 my $s = substr( $hnd, length($t) + 1 );
318              
319             # Add on the prefix and suffix if the command is defined
320 32 100       66 if ( length $s )
321             {
322 26         91 substr( $s, 0, 0 ) = $o->cmd_prefix;
323 26         67 $s .= $o->cmd_suffix;
324             }
325 32         140 $o->{handlers}{$s}{$t} = $hnd;
326             }
327             LOOP2:
328 5         14 for my $hnd (@_)
329             {
330 249         348 my $t = $match->($hnd);
331 249 100       440 next LOOP2 if !defined $t;
332 32         57 my $s = substr( $hnd, length($t) + 1 );
333              
334             # Add on the prefix and suffix if the command is defined
335 32 100       59 if ( length $s )
336             {
337 26         45 substr( $s, 0, 0 ) = $o->cmd_prefix;
338 26         47 $s .= $o->cmd_suffix;
339             }
340 32 50       80 if ( $o->has_aliases($s) )
341             {
342 0         0 my @s = $o->get_aliases($s);
343 0         0 for my $alias (@s)
344             {
345 0         0 substr( $alias, 0, 0 ) = $o->cmd_prefix;
346 0         0 $alias .= $o->cmd_suffix;
347 0         0 $o->{handlers}{$alias}{$t} = $hnd;
348             }
349             }
350             }
351             }
352              
353             sub add_commands
354             {
355 0     0 0 0 my $o = shift;
356 0         0 while (@_)
357             {
358 0         0 my ( $cmd, $hnd ) = ( shift, shift );
359 0         0 $o->{handlers}{$cmd} = $hnd;
360             }
361             }
362              
363             sub remove_handlers
364             {
365 0     0 0 0 my $o = shift;
366 0         0 for my $hnd (@_)
367             {
368 0 0       0 next unless $hnd =~ /^(run|help|smry|comp|catch|alias)_/o;
369 0         0 my $t = $1;
370 0         0 my $a = substr( $hnd, length($t) + 1 );
371              
372             # Add on the prefix and suffix if the command is defined
373 0 0       0 if ( length $a )
374             {
375 0         0 substr( $a, 0, 0 ) = $o->cmd_prefix;
376 0         0 $a .= $o->cmd_suffix;
377             }
378 0         0 delete $o->{handlers}{$a}{$t};
379             }
380             }
381              
382             sub remove_commands
383             {
384 0     0 0 0 my $o = shift;
385 0         0 for my $name (@_)
386             {
387 0         0 delete $o->{handlers}{$name};
388             }
389             }
390              
391             *add_handler = \&add_handlers;
392             *add_command = \&add_commands;
393             *remove_handler = \&remove_handlers;
394             *remove_command = \&remove_commands;
395              
396             #=============================================================================
397             # Utility methods
398             #=============================================================================
399             sub termsize
400             {
401 3     3 0 8 my $o = shift;
402 3         9 my ( $rows, $cols ) = ( 24, 78 );
403              
404             # Try several ways to get the terminal size
405             TERMSIZE:
406             {
407 3         11 my $TERM = $o->{term};
  3         8  
408 3 50       8 last TERMSIZE unless $TERM;
409              
410 3         33 my $OUT = $TERM->OUT;
411              
412 3 50 33     34 if ( $TERM and $o->{API}{readline} eq 'Term::ReadLine::Gnu' )
413             {
414 0         0 ( $rows, $cols ) = $TERM->get_screen_size;
415 0         0 last TERMSIZE;
416             }
417              
418 3 50 33     22 if ( $^O eq 'MSWin32' and eval { require Win32::Console } )
  0         0  
419             {
420 0         0 Win32::Console->import;
421              
422             # Win32::Console's DESTROY does a CloseHandle(), so save the object:
423 0   0     0 $o->{win32_stdout} ||= Win32::Console->new( STD_OUTPUT_HANDLE() );
424 0         0 my @info = $o->{win32_stdout}->Info;
425 0         0 $cols = $info[7] - $info[5] + 1; # right - left + 1
426 0         0 $rows = $info[8] - $info[6] + 1; # bottom - top + 1
427 0         0 last TERMSIZE;
428             }
429              
430 3 50       7 if ( eval { require Term::Size } )
  3         545  
431             {
432 0         0 my @x = Term::Size::chars($OUT);
433 0 0 0     0 if ( @x == 2 and $x[0] )
434             {
435 0         0 ( $cols, $rows ) = @x;
436 0         0 last TERMSIZE;
437             }
438             }
439              
440 3 50       16 if ( eval { require Term::Screen } )
  3         483  
441             {
442 0         0 my $screen = Term::Screen->new;
443 0         0 ( $rows, $cols ) = @$screen{qw(ROWS COLS)};
444 0         0 last TERMSIZE;
445             }
446              
447 3 50       13 if ( eval { require Term::ReadKey } )
  3         739  
448             {
449 3         2490 ( $cols, $rows ) = eval {
450 3     3   37 local $SIG{__WARN__} = sub { };
451 3         12 Term::ReadKey::GetTerminalSize($OUT);
452             };
453 3 50       158 last TERMSIZE unless $@;
454             }
455              
456 0 0 0     0 if ( $ENV{LINES} or $ENV{ROWS} or $ENV{COLUMNS} )
      0        
457             {
458 0   0     0 $rows = $ENV{LINES} || $ENV{ROWS} || $rows;
459 0   0     0 $cols = $ENV{COLUMNS} || $cols;
460 0         0 last TERMSIZE;
461             }
462              
463             {
464 0         0 local $^W;
  0         0  
465 0 0       0 if ( open( my $STTY, "-|", "stty", "size" ) )
466             {
467 0         0 my $l = <$STTY>;
468 0         0 ( $rows, $cols ) = split /\s+/, $l;
469 0         0 close $STTY;
470             }
471             }
472             }
473              
474 3         85 return { rows => $rows, cols => $cols };
475             }
476              
477             sub readkey
478             {
479 0     0 0 0 my $o = shift;
480 0 0       0 $o->have_readkey unless $o->{readkey};
481 0         0 $o->{readkey}->();
482             }
483              
484             sub have_readkey
485             {
486 0     0 0 0 my $o = shift;
487 0 0       0 return 1 if $o->{have_readkey};
488 0         0 my $IN = $o->{term}->IN;
489 0 0 0     0 if ( eval { require Term::InKey } )
  0 0       0  
    0          
490             {
491 0         0 $o->{readkey} = \&Term::InKey::ReadKey;
492             }
493 0         0 elsif ( $^O eq 'MSWin32' and eval { require Win32::Console } )
494             {
495             $o->{readkey} = sub {
496 0     0   0 my $c;
497              
498             # from Term::InKey:
499 0         0 eval {
500             # Win32::Console's DESTROY does a CloseHandle(), so save it:
501 0         0 Win32::Console->import;
502 0   0     0 $o->{win32_stdin} ||= Win32::Console->new( STD_INPUT_HANDLE() );
503 0 0       0 my $mode = my $orig = $o->{win32_stdin}->Mode or die $^E;
504 0         0 $mode &= ~( ENABLE_LINE_INPUT() | ENABLE_ECHO_INPUT() );
505 0 0       0 $o->{win32_stdin}->Mode($mode) or die $^E;
506              
507 0 0       0 $o->{win32_stdin}->Flush or die $^E;
508 0         0 $c = $o->{win32_stdin}->InputChar(1);
509 0 0       0 die $^E unless defined $c;
510 0 0       0 $o->{win32_stdin}->Mode($orig) or die $^E;
511             };
512 0 0       0 die "Not implemented on $^O: $@" if $@;
513 0         0 $c;
514 0         0 };
515             }
516 0         0 elsif ( eval { require Term::ReadKey } )
517             {
518             $o->{readkey} = sub {
519 0     0   0 Term::ReadKey::ReadMode( 4, $IN );
520 0         0 my $c = getc($IN);
521 0         0 Term::ReadKey::ReadMode( 0, $IN );
522 0         0 $c;
523 0         0 };
524             }
525             else
526             {
527 0     0   0 $o->{readkey} = sub { scalar <$IN> };
  0         0  
528 0         0 return $o->{have_readkey} = 0;
529             }
530 0         0 return $o->{have_readkey} = 1;
531             }
532             *has_readkey = \&have_readkey;
533              
534             sub prompt
535             {
536 0     0 0 0 my $o = shift;
537 0         0 my ( $prompt, $default, $completions, $casei ) = @_;
538 0         0 my $term = $o->{term};
539              
540             # A closure to read the line.
541 0         0 my $line;
542             my $readline = sub {
543 0     0   0 my ( $sh, $gh ) = @{ $term->Features }{qw(setHistory getHistory)};
  0         0  
544 0 0       0 my @history = $gh ? $term->GetHistory : ();
545 0 0       0 $term->SetHistory() if $sh;
546 0         0 $line = $o->readline($prompt);
547 0 0 0     0 $line = $default
      0        
548             if ( ( not defined $line or $line =~ /^\s*$/ )
549             and defined $default );
550              
551             # Restore the history
552 0 0       0 $term->SetHistory(@history) if $sh;
553 0         0 $line;
554 0         0 };
555              
556             # A closure to complete the line.
557             my $complete = sub {
558 0     0   0 my ( $word, $line, $start ) = @_;
559 0         0 return $o->completions( $word, $completions, $casei );
560 0         0 };
561              
562 0 0 0     0 if ( $term and $term->ReadLine eq 'Term::ReadLine::Gnu' )
    0 0        
563             {
564 0         0 my $attribs = $term->Attribs;
565 0         0 local $attribs->{completion_function} = $complete;
566 0         0 &$readline;
567             }
568             elsif ( $term and $term->ReadLine eq 'Term::ReadLine::Perl' )
569             {
570 0         0 local $readline::rl_completion_function = $complete;
571 0         0 &$readline;
572             }
573             else
574             {
575 0         0 &$readline;
576             }
577 0         0 $line;
578             }
579              
580             sub format_pairs
581             {
582 2     2 0 6 my $o = shift;
583 2         4 my @keys = @{ shift(@_) };
  2         7  
584 2         3 my @vals = @{ shift(@_) };
  2         5  
585 2   50     10 my $sep = shift || ": ";
586 2   50     6 my $left = shift || 0;
587 2   50     23 my $ind = shift || "";
588 2   50     23 my $len = shift || 0;
589 2   50     8 my $wrap = shift || 0;
590              
591 2 50       5 if ($wrap)
592             {
593 0         0 eval {
594 0         0 require Text::Autoformat;
595 0         0 Text::Autoformat->import(qw(autoformat));
596             };
597 0 0       0 if ($@)
598             {
599 0 0       0 warn(
600             "Term::Shell::format_pairs(): Text::Autoformat is required "
601             . "for wrapping. Wrapping disabled" )
602             if $^W;
603 0         0 $wrap = 0;
604             }
605             }
606 2   33     24 my $cols = shift || $o->termsize->{cols};
607 2   66     30 $len < length($_) and $len = length($_) for @keys;
608 2         19 my @text;
609 2         19 for my $i ( 0 .. $#keys )
610             {
611 3 50       14 next unless defined $vals[$i];
612 3         7 my $sz = ( $len - length( $keys[$i] ) );
613 3 50       17 my $lpad = $left ? "" : " " x $sz;
614 3 50       17 my $rpad = $left ? " " x $sz : "";
615 3         9 my $l = "$ind$lpad$keys[$i]$rpad$sep";
616 3   33     53 my $wrap = $wrap & ( $vals[$i] =~ /\s/ and $vals[$i] !~ /^\d/ );
617 3 50       19 my $form = (
618             $wrap
619             ? autoformat(
620             "$vals[$i]", # force stringification
621             { left => length($l) + 1, right => $cols, all => 1 },
622             )
623             : "$l$vals[$i]\n"
624             );
625 3         9 substr( $form, 0, length($l), $l );
626 3         8 push @text, $form;
627             }
628 2         11 my $text = join '', @text;
629 2 50       35 return wantarray ? ( $text, $len ) : $text;
630             }
631              
632             sub print_pairs
633             {
634 0     0 0 0 my $o = shift;
635 0         0 my ( $text, $len ) = $o->format_pairs(@_);
636 0         0 $o->page($text);
637 0         0 return $len;
638             }
639              
640             # Handle backslash translation; doesn't do anything complicated yet.
641             sub process_esc
642             {
643 0     0 0 0 my $o = shift;
644 0         0 my $c = shift;
645 0         0 my $q = shift;
646 0         0 my $n;
647 0 0       0 return '\\' if $c eq '\\';
648 0 0       0 return $q if $c eq $q;
649 0         0 return "\\$c";
650             }
651              
652             # Parse a quoted string
653             sub parse_quoted
654             {
655 0     0 0 0 my $o = shift;
656 0         0 my $raw = shift;
657 0         0 my $quote = shift;
658 0         0 my $i = 1;
659 0         0 my $string = '';
660 0         0 my $c;
661 0   0     0 while ( $i <= length($raw) and ( $c = substr( $raw, $i, 1 ) ) ne $quote )
662             {
663              
664 0 0       0 if ( $c eq '\\' )
665             {
666 0         0 $string .= $o->process_esc( substr( $raw, $i + 1, 1 ), $quote );
667 0         0 $i++;
668             }
669             else
670             {
671 0         0 $string .= substr( $raw, $i, 1 );
672             }
673 0         0 $i++;
674             }
675 0         0 return ( $string, $i );
676             }
677              
678             sub line
679             {
680 0     0 0 0 my $o = shift;
681 0         0 $o->{line};
682             }
683              
684             sub line_args
685             {
686 0     0 0 0 my $o = shift;
687 0   0     0 my $line = shift || $o->line;
688 0         0 $o->line_parsed($line);
689 0 0       0 $o->{line_args} || '';
690             }
691              
692             sub line_parsed
693             {
694 0     0 0 0 my $o = shift;
695 0   0     0 my $args = shift || $o->line || return ();
696 0         0 my @args;
697              
698             # Parse an array of arguments. Whitespace separates, unless quoted.
699 0         0 my $arg = undef;
700 0         0 $o->{line_args} = undef;
701 0         0 for ( my $i = 0 ; $i < length($args) ; $i++ )
702             {
703 0         0 my $c = substr( $args, $i, 1 );
704 0 0 0     0 if ( $c =~ /\S/ and @args == 1 )
705             {
706 0   0     0 $o->{line_args} ||= substr( $args, $i );
707             }
708 0 0       0 if ( $c =~ /['"]/ )
    0          
709             {
710 0         0 my ( $str, $n ) = $o->parse_quoted( substr( $args, $i ), $c );
711 0         0 $i += $n;
712 0 0       0 $arg = ( defined($arg) ? $arg : '' ) . $str;
713             }
714              
715             # We do not parse outside of strings
716             # elsif ($c eq '\\') {
717             # $arg = (defined($arg) ? $arg : '')
718             # . $o->process_esc(substr($args,$i+1,1));
719             # $i++;
720             # }
721             elsif ( $c =~ /\s/ )
722             {
723 0 0       0 push @args, $arg if defined $arg;
724 0         0 $arg = undef;
725             }
726             else
727             {
728 0         0 $arg .= substr( $args, $i, 1 );
729             }
730             }
731 0 0       0 push @args, $arg if defined($arg);
732 0         0 return @args;
733             }
734              
735             sub handler
736             {
737 3     3 1 6 my $o = shift;
738 3         9 my ( $command, $type, $args, $preserve_args ) = @_;
739              
740             # First try finding the standard handler, then fallback to the
741             # catch_$type method. The columns represent "action", "type", and "push",
742             # which control whether the name of the command should be pushed onto the
743             # args.
744 3         10 my @tries = (
745             [ $command, $type, 0 ],
746             [ $o->cmd_prefix . $type . $o->cmd_suffix, 'catch', 1 ],
747             );
748              
749             # The user can control whether or not to search for "unique" matches,
750             # which means calling $o->possible_actions(). We always look for exact
751             # matches.
752 3         12 my @matches = qw(exact_action);
753 3 50       24 push @matches, qw(possible_actions) if $o->{API}{match_uniq};
754              
755 3         12 for my $try (@tries)
756             {
757 4         9 my ( $cmd, $type, $add_cmd_name ) = @$try;
758 4         8 for my $match (@matches)
759             {
760 6         22 my @handlers = $o->$match( $cmd, $type );
761 6 100       30 next unless @handlers == 1;
762 2 50 33     7 unshift @$args, $command
763             if $add_cmd_name and not $preserve_args;
764 2         10 return $o->unalias( $handlers[0], $type );
765             }
766             }
767 1         4 return;
768             }
769              
770             sub completions
771             {
772 0     0 0 0 my $o = shift;
773 0         0 my $action = shift;
774 0   0     0 my $compls = shift || [];
775 0         0 my $casei = shift;
776 0 0       0 $casei = $o->{API}{case_ignore} unless defined $casei;
777 0 0       0 $casei = $casei ? '(?i)' : '';
778 0         0 return grep { $_ =~ /$casei^\Q$action\E/ } @$compls;
  0         0  
779             }
780              
781             #=============================================================================
782             # Term::Shell error messages
783             #=============================================================================
784             sub msg_ambiguous_cmd
785             {
786 0     0 0 0 my ( $o, $cmd, @c ) = @_;
787 0         0 local $" = "\n\t";
788 0         0 <
789             Ambiguous command '$cmd': possible commands:
790             @c
791             END
792             }
793              
794             sub msg_unknown_cmd
795             {
796 0     0 0 0 my ( $o, $cmd ) = @_;
797 0         0 <
798             Unknown command '$cmd'; type 'help' for a list of commands.
799             END
800             }
801              
802             #=============================================================================
803             # Term::Shell private methods
804             #=============================================================================
805             sub do_action
806             {
807 3     3 0 9 my $o = shift;
808 3         4 my $cmd = shift;
809 3   50     8 my $args = shift || [];
810 3   50     6 my $type = shift || 'run';
811 3         21 my ( $fullname, $cmdname, $handler ) = $o->handler( $cmd, $type, $args );
812 3 100       27 $o->{command}{$type} = {
813             cmd => $cmd,
814             name => $cmd,
815             found => defined $handler ? 1 : 0,
816             cmdfull => $fullname,
817             cmdreal => $cmdname,
818             handler => $handler,
819             };
820 3 100       17 if ( defined $handler )
821             {
822             # We've found a handler. Set up a value which will call the postcmd()
823             # action as the subroutine leaves. Then call the precmd(), then return
824             # the result of running the handler.
825 2         12 $o->precmd( \$handler, \$cmd, $args );
826             my $postcmd = Term::Shell::OnScopeLeave->new(
827             sub {
828 2     2   49 $o->postcmd( \$handler, \$cmd, $args );
829             }
830 2         21 );
831 2         14 return $o->$handler(@$args);
832             }
833             }
834              
835             sub uniq
836             {
837 0     0 0 0 my $o = shift;
838 0         0 my %seen;
839 0         0 $seen{$_}++ for @_;
840 0         0 my @ret;
841 0 0       0 for (@_) { push @ret, $_ if $seen{$_}-- == 1 }
  0         0  
842 0         0 @ret;
843             }
844              
845             sub possible_actions
846             {
847 8     8 0 4529 my $o = shift;
848 8         20 my $action = shift;
849 8         22 my $type = shift;
850 8 50       35 my $casei = $o->{API}{case_ignore} ? '(?i)' : '';
851 26         176 my @keys = grep { $_ =~ /$casei^\Q$action\E/ }
852 38         111 grep { exists $o->{handlers}{$_}{$type} }
853 8         16 keys %{ $o->{handlers} };
  8         30  
854 8         47 return @keys;
855             }
856              
857             sub exact_action
858             {
859 4     4 0 5 my $o = shift;
860 4         6 my $action = shift;
861 4         6 my $type = shift;
862 4 50       9 my $casei = $o->{API}{case_ignore} ? '(?i)' : '';
863 6         79 my @key = grep { $action =~ /$casei^\Q$_\E$/ }
864 16         32 grep { exists $o->{handlers}{$_}{$type} }
865 4         5 keys %{ $o->{handlers} };
  4         13  
866 4 100       15 return () unless @key == 1;
867 2         7 return $key[0];
868             }
869              
870             sub is_alias
871             {
872 0     0 0 0 my $o = shift;
873 0         0 my $action = shift;
874 0 0       0 exists $o->{handlers}{$action}{alias} ? 1 : 0;
875             }
876              
877             sub has_aliases
878             {
879 32     32 0 46 my $o = shift;
880 32         42 my $action = shift;
881 32         62 my @a = $o->get_aliases($action);
882 32 50       96 @a ? 1 : 0;
883             }
884              
885             sub get_aliases
886             {
887 32     32 0 42 my $o = shift;
888 32         47 my $action = shift;
889 32         38 my @a = eval {
890 32         54 my $hndlr = $o->{handlers}{$action}{alias};
891 32 50       79 return () unless $hndlr;
892 0         0 $o->$hndlr();
893             };
894 32         60 $o->{aliases}{$_} = $action for @a;
895 32         46 @a;
896             }
897              
898             sub unalias
899             {
900 2     2 0 4 my $o = shift;
901 2         3 my $cmd = shift; # i.e 'foozle'
902 2         2 my $type = shift; # i.e 'run'
903 2 50       7 return () unless $type;
904             return ( $cmd, $cmd, $o->{handlers}{$cmd}{$type} )
905 2 50       22 unless exists $o->{aliases}{$cmd};
906 0         0 my $alias = $o->{aliases}{$cmd};
907              
908             # I'm allowing aliases to call handlers which have been removed. This
909             # means I can set up an alias of '!' for 'shell', then delete the 'shell'
910             # command, so that you can only access it through '!'. That's why I'm
911             # checking the {handlers} entry _and_ building a string.
912 0   0     0 my $handler = $o->{handlers}{$alias}{$type} || "${type}_${alias}";
913 0         0 return ( $cmd, $alias, $handler );
914             }
915              
916             sub find_handlers
917             {
918 5     5 0 16 my $o = shift;
919 5   66     36 my $pkg = shift || $o->{API}{class};
920              
921             # Find the handlers in the given namespace:
922 5         9 my %handlers;
923             {
924             ## no critic
925 3     3   44 no strict 'refs';
  3         7  
  3         213  
926 5         8 my @r = keys %{ $pkg . "::" };
  5         477  
927 5         78 $o->add_handlers(@r);
928             }
929              
930             # Find handlers in its base classes.
931             {
932             ## no critic
933 3     3   20 no strict 'refs';
  3         7  
  3         3339  
  5         9  
  5         10  
934 5         10 my @isa = @{ $pkg . "::ISA" };
  5         77  
935 5         31 for my $pkg (@isa)
936             {
937 2         21 $o->find_handlers($pkg);
938             }
939             }
940             }
941              
942             sub rl_complete
943             {
944 0     0 0 0 my $o = shift;
945 0         0 my ( $word, $line, $start ) = @_;
946              
947             # If it's a command, complete 'run_':
948 0 0 0     0 if ( $start == 0 or substr( $line, 0, $start ) =~ /^\s*$/ )
949             {
950 0         0 my @compls = $o->complete( '', $word, $line, $start );
951 0 0       0 return @compls if $o->{command}{comp}{found};
952             }
953              
954             # If it's a subcommand, send it to any custom completion function for the
955             # function:
956             else
957             {
958 0         0 my $command = ( $o->line_parsed($line) )[0];
959 0         0 my @compls = $o->complete( $command, $word, $line, $start );
960 0 0       0 return @compls if $o->{command}{comp}{found};
961             }
962              
963 0         0 ();
964             }
965              
966             #=============================================================================
967             # Two action handlers provided by default: help and exit.
968             #=============================================================================
969 1     1 0 13 sub smry_exit { "exits the program" }
970              
971             sub help_exit
972             {
973 0     0 0 0 <<'END';
974             Exits the program.
975             END
976             }
977              
978             sub run_exit
979             {
980 0     0 0 0 my $o = shift;
981 0         0 $o->stoploop;
982             }
983              
984 1     1 0 4 sub smry_help { "prints this screen, or help on 'command'" }
985              
986             sub help_help
987             {
988 0     0 0 0 <<'END';
989             Provides help on commands...
990             END
991             }
992              
993             sub comp_help
994             {
995 0     0 0 0 my ( $o, $word, $line, $start ) = @_;
996 0         0 my @words = $o->line_parsed($line);
997 0 0 0     0 return []
      0        
998             if ( @words > 2 or @words == 2 and $start == length($line) );
999 0         0 sort $o->possible_actions( $word, 'help' );
1000             }
1001              
1002             sub run_help
1003             {
1004 1     1 0 21 my $o = shift;
1005 1         2 my $cmd = shift;
1006 1 50       3 if ($cmd)
1007             {
1008 0         0 my $txt = $o->help( $cmd, @_ );
1009 0 0       0 if ( $o->{command}{help}{found} )
1010             {
1011 0         0 $o->page($txt);
1012             }
1013             else
1014             {
1015 0         0 my @c = sort $o->possible_actions( $cmd, 'help' );
1016 0 0 0     0 if ( @c and $o->{API}{match_uniq} )
1017             {
1018 0         0 local $" = "\n\t";
1019 0         0 print <
1020             Ambiguous help topic '$cmd': possible help topics:
1021             @c
1022             END
1023             }
1024             else
1025             {
1026 0         0 print <
1027             Unknown help topic '$cmd'; type 'help' for a list of help topics.
1028             END
1029             }
1030             }
1031             }
1032             else
1033             {
1034 1         54 print "Type 'help command' for more detailed help on a command.\n";
1035 1         7 my ( %cmds, %docs );
1036 1         0 my %done;
1037 1         0 my %handlers;
1038 1         2 for my $h ( keys %{ $o->{handlers} } )
  1         6  
1039             {
1040 4 100       11 next unless length($h);
1041             next
1042 3 50       7 unless grep { defined $o->{handlers}{$h}{$_} }
  9         30  
1043             qw(run smry help);
1044 3 50       9 my $dest = exists $o->{handlers}{$h}{run} ? \%cmds : \%docs;
1045 3 100       5 my $smry = do { my $x = $o->summary($h); $x ? $x : "undocumented" };
  3         22  
  3         10  
1046             my $help =
1047             exists $o->{handlers}{$h}{help}
1048             ? (
1049             exists $o->{handlers}{$h}{smry}
1050 3 50       15 ? ""
    100          
1051             : " - but help available"
1052             )
1053             : " - no help available";
1054 3         16 $dest->{" $h"} = "$smry$help";
1055             }
1056 1         2 my @t;
1057 1 50       4 push @t, " Commands:\n" if %cmds;
1058             push @t,
1059             scalar $o->format_pairs(
1060             [ sort keys %cmds ],
1061 1         11 [ map { $cmds{$_} } sort keys %cmds ],
  3         20  
1062             ' - ', 1
1063             );
1064 1 50       19 push @t, " Extra Help Topics: (not commands)\n" if %docs;
1065             push @t,
1066             scalar $o->format_pairs(
1067             [ sort keys %docs ],
1068 1         21 [ map { $docs{$_} } sort keys %docs ],
  0         0  
1069             ' - ', 1
1070             );
1071 1         70 $o->page( join '', @t );
1072             }
1073             }
1074              
1075       0 0   sub run_ { }
1076              
1077             sub comp_
1078             {
1079 0     0 0 0 my ( $o, $word, $line, $start ) = @_;
1080 0         0 my @comp = grep { length($_) } sort $o->possible_actions( $word, 'run' );
  0         0  
1081 0         0 return @comp;
1082             }
1083              
1084             package Term::Shell::OnScopeLeave;
1085             $Term::Shell::OnScopeLeave::VERSION = '0.13';
1086             sub new
1087             {
1088 2   33 2   26 return bless [ @_[ 1 .. $#_ ] ], ref( $_[0] ) || $_[0];
1089             }
1090              
1091             sub DESTROY
1092             {
1093 2     2   5 my $o = shift;
1094 2         15 for my $c (@$o)
1095             {
1096 2         5 $c->();
1097             }
1098              
1099 2         14 return;
1100             }
1101              
1102             1;
1103              
1104             __END__