ljr/livejournal/test/bin/ipl

405 lines
9.4 KiB
Plaintext
Raw Permalink Normal View History

2019-02-05 21:49:12 +00:00
#!/usr/bin/perl
package IPL::Shell;
use strict;
BEGIN {
use vars qw{$VERSION $RCSID};
$VERSION = do { my @r = (q$Revision: 1.3 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
$RCSID = q$Id: ipl,v 1.3 2004/11/11 00:51:38 deveiant Exp $;
use base qw{Shell::Base};
use Carp qw{carp croak confess};
use Data::Dumper qw{};
use Term::ANSIColor qw{color};
use Cwd qw{cwd};
use File::Basename qw(basename);
use Net::Domain qw(hostfqdn);
use String::Format qw(stringf);
use Sys::Hostname qw(hostname);
use Devel::Symdump qw{};
$Shell::Base::RE_QUIT = '(?i)^\s*(exit|q(uit)?|logout)';
}
#####################################################################
### C L A S S G L O B A L S
#####################################################################
# Default configuration values
our %Defaults = (
prompt_fmt => '%{bold cyan}C%p%{white}C %!> ',
);
#####################################################################
### I N S T A N C E M E T H O D S
#####################################################################
### Shell initialization
sub init {
my ( $self, $args ) = @_;
# Merge defaults
%$args = ( %Defaults, %$args );
# Add instance vars
$self->{result_history} = [];
$self->{package} = 'main';
$self->{prompt_fmt} = $args->{prompt_fmt};
}
### METHOD: init_completions()
### Initialize the internal COMPLETIONS list.
sub init_completions {
my $self = shift;
$self->SUPER::init_completions;
$self->completions( map { ":$_" } $self->completions );
}
### Intro/outro message
sub intro { return qq{IPL: Interactive Perl Shell $VERSION} }
sub outro { "" }
### Prompt callback
sub prompt {
my $self = shift;
my $fmt = $self->{prompt_fmt};
my $prompt = stringf $fmt => {
'!' => $self->term->{history_length},
'$' => $$,
'0' => $self->progname,
'C' => sub { color($_[0]) },
'H' => hostfqdn,
'W' => basename(cwd),
'c' => ref($self),
'g' => scalar getgrgid($(),
'h' => hostname,
'p' => $self->{package},
'u' => scalar getpwuid($<),
'w' => cwd,
};
# Append a reset to clean up if there are color escapes in the prompt
$prompt .= color( 'reset' ) if $prompt =~ m{\e\[};
return "\n$prompt";
}
### METHOD: print( $output )
### Output method.
sub print {
my $self = shift;
my $output = join( "", @_ );
my $nlcount = $output =~ tr/\n/\n/;
if ( $nlcount >= $self->{ROWS} - 3 ) {
my $pager = $self->pager;
open my $P, "|$pager" or carp "Can't open $pager: $!";
CORE::print $P $output;
close $P;
} else {
CORE::print $output;
}
}
### METHOD: parseline( $input )
### Command-parsing method: parse the specified I<input>, run the specified
### command, and output the result.
sub parseline {
my ( $self, $input ) = @_;
if ( $input =~ m{^:(\S+.*)} ) {
return $self->SUPER::parseline( $1 );
}
else {
return ( "eval", {}, $input );
}
}
### METHOD: prettyPrint( $value )
### Return the given I<value> as a pretty-printed string.
sub prettyPrint {
my ( $self, $val ) = @_;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 1;
local $Data::Dumper::Pad = " ";
my $rval = Data::Dumper->Dumpxs( [$val] );
$rval =~ s{^\s+}{};
$rval = " => $rval ";
return $rval;
}
#####################################################################
### C O M M A N D M E T H O D S
#####################################################################
### METHOD: do_shellobj( undef )
### Implementation of the 'shellobj' command
sub do_shellobj {
my $self = shift;
return Data::Dumper->Dumpxs( [$self], [qw{self}] );
}
### METHOD: help_shellobj( undef )
### Callback for help on the 'shellobj' command.
sub help_shellobj {
return q{Show a dump of the IPL shell object itself.};
}
### METHOD: do_eval( @args )
### Implementation of the 'eval' command.
sub do_eval {
my ( $self, @args ) = @_;
my (
$input,
$res,
$rval,
);
$input = join( ' ', @args );
#$self->print( "[Evaluating '$input']\n" );
$res = eval qq{
package $self->{package};
no strict;
$input ;
};
# Show error message for errors
if ( $@ ) {
( $rval = $@ ) =~ s{at \S+ line \d+\..*}{};
}
# Dump everything else
else {
$rval = $self->prettyPrint( $res );
}
return $rval;
}
### METHOD: help_eval( undef )
### Provide help text for the 'eval' command.
sub help_eval {
return q{Evaluate the input as Perl and display the result.};
}
### METHOD: do_rl( $subcmd, @args )
### Implementation of the 'readline' command.
sub do_rl {
my ( $self, $subcmd, @args ) = @_;
my $rval = '';
if ( $subcmd =~ m{features} ) {
$rval = $self->prettyPrint( $self->term->Features );
}
elsif ( $subcmd =~ m{module} ) {
$rval = "Readline features provided by: " .
$self->term->ReadLine;
}
elsif ( $subcmd =~ m{completions} ) {
$rval = "Tab-completions: \n" .
$self->prettyPrint( [sort $self->completions] );
}
else {
$rval = $self->prettyPrint( $self->term );
}
return $rval;
}
### METHOD: help_rl( undef )
### Return help text for the 'rl' (readline) command.
sub help_rl {q{
rl [<subcommand>]
Provide information about the readline implementation used by the shell. The
<subcommand> can be one of:
features Dump the hash of features understood by the implementation.
module Show the name of the actual module that is providing readline
features.
completions Dump the list of tab-completions.
}}
### METHOD: do_perldoc( @args )
### Implementation of the 'perldoc' command.
sub do_perldoc {
my ( $self, @args ) = @_;
if ( @args ) {
system 'perldoc', @args;
return undef;
} else {
return "'perldoc' requires at least one argument.";
}
}
### METHOD: help_perldoc()
### Help method for the 'perldoc' command.
sub help_perldoc {q{
Run the 'perldoc' program.
}}
### METHOD: do_package( $newpkg )
### Change the package future evals will take place in.
sub do_package {
my ( $self, $package ) = @_;
if ( $package ) {
return "Invalid package name '$package'"
unless $package =~ m{^[a-z]\w+(?:::[a-z]\w+)*$}i;
$self->{package} = $package;
return "Set eval package to: '$package'.";
} else {
return "Current package is: '$self->{package}'.";
}
}
### METHOD: help_package()
### Help method for the 'package' command.
sub help_package {q{
package [<newpackage>]
Set the package future evaluations will occur in to 'newpackage' if the optional
argument is given, else just print the name of the current package.
}}
### METHOD: do_reload()
### Implementation of the 'reload' command.
sub do_reload {
my $self = shift;
if ( my $h = $self->histfile ) {
$self->term->WriteHistory( $h );
}
$self->print( "Re-execing shell...\n" );
local $ENV{IPL_RELOAD} = 1;
exec $0;
return "Hmmm... something funky happened.";
}
sub help_reload {q{
reload
Reload the shell.
}}
### METHOD: do_inspect( [$package] )
### Implementation of the 'inspect' command.
sub do_inspect {
my $self = shift;
my $package = shift || $self->{package};
my $inspector = new Devel::Symdump $package;
no strict;
my @out = ( "Symbol table for $package" );
my ( %scalars, %arrays, %hashes, %functions, %packages, %filehandles );
# Grab entries for each type from the symbol table for the inspected
# package.
%scalars = map { $_ => ${*{$_}{SCALAR}} } $inspector->scalars;
%arrays = map { $_ => *{$_}{ARRAY} } $inspector->arrays;
%hashes = map { $_ => *{$_}{HASH} } $inspector->hashes;
%functions = map { $_ => *{$_}{CODE} } $inspector->functions;
%packages = map { $_ => undef } $inspector->packages;
%filehandles = map { $_ => *{$_}{IO} } $inspector->filehandles;
# Build displays for each entry in the symbol table, sorted by type,
# prepending the sigil for each one. In part stolen from Matthew Simon
# Cavalletto's Term::ShellKit::Dev
foreach my $output (
[ 'Scalars', \%scalars, '$', ],
[ 'Arrays', \%arrays, '@'],
[ 'Hashes', \%hashes, '%'],
[ 'Subs', \%functions, 'sub '],
[ 'Packages', \%packages, '::'],
[ 'Filehandles', \%filehandles, '*'],
) {
next unless scalar keys %{$output->[1]};
push @out, "$output->[0]:", '-' x length($output->[0]);
foreach ( sort keys %{$output->[1]} ) {
if ( defined $output->[1]{$_} ) {
push @out, qq{ $output->[2]$_ = "$output->[1]{$_}"};
} else {
push @out, qq{ $output->[2]$_};
}
}
}
return join "\n", @out;
}
sub help_inspect {q{
inspect [<package>]
Inspect the contents of the specified package, or the current package if no
package is given.
}}
#####################################################################
### R U N T I M E P A C K A G E
#####################################################################
package ipl;
use strict;
BEGIN {
use Getopt::Long qw{GetOptions};
$GetOpt::Long::Bundling = 1;
}
if ( $0 eq __FILE__ ) {
my %opts = (
HISTFILE => glob("~/.ipl_history"),
RCFILES => [glob("~/.iplrc"), ".iplrc"],
);
IPL::Shell->new( %opts )->run;
}