use DXVars;
use DXDebug;
use DXM;
+use DXLog;
+use DXLogPrint;
+use CmdAlias;
+use FileHandle;
use Carp;
use strict;
-use vars qw(%Cache %cmd_cache);
+use vars qw(%Cache %cmd_cache $errstr %aliases);
%Cache = (); # cache of dynamically loaded routine's mod times
%cmd_cache = (); # cache of short names
+$errstr = (); # error string from eval
+%aliases = (); # aliases for (parts of) commands
#
# obtain a new connection this is derived from dxchannel
sub start
{
- my ($self, $line) = @_;
+ my ($self, $line, $sort) = @_;
my $user = $self->{user};
my $call = $self->{call};
my $name = $user->{name};
-
+
$self->{name} = $name ? $name : $call;
- $self->msg('l2',$self->{name});
+ $self->send($self->msg('l2',$self->{name}));
$self->send_file($main::motd) if (-e $main::motd);
- $self->msg('pr', $call);
$self->state('prompt'); # a bit of room for further expansion, passwords etc
$self->{priv} = $user->priv;
+ $self->{lang} = $user->lang;
+ $self->{pagelth} = 20;
$self->{priv} = 0 if $line =~ /^(ax|te)/; # set the connection priv to 0 - can be upgraded later
$self->{consort} = $line; # save the connection type
# set some necessary flags on the user if they are connecting
$self->{wwv} = $self->{talk} = $self->{ann} = $self->{here} = $self->{dx} = 1;
- $self->prompt() if $self->{state} =~ /^prompt/o;
+# $self->prompt() if $self->{state} =~ /^prompt/o;
# add yourself to the database
my $node = DXNode->get($main::mycall) or die "$main::mycall not allocated in DXNode database";
# issue a pc16 to everybody interested
my $nchan = DXChannel->get($main::mycall);
- my $pc16 = $nchan->pc16($cuser);
- DXProt::broadcast_ak1a($pc16);
+ my @pc16 = DXProt::pc16($nchan, $cuser);
+ DXProt::broadcast_ak1a(@pc16);
+ Log('DXCommand', "$call connected");
+
+ # send prompts and things
+ my $info = DXCluster::cluster();
+ $self->send("Cluster:$info");
+ $self->send($self->msg('pr', $call));
}
#
# This is the normal command prompt driver
#
+
sub normal
{
- my $self = shift;
- my $user = $self->{user};
- my $call = $self->{call};
- my $cmdline = shift;
+ my $self = shift;
+ my $cmdline = shift;
+ my @ans;
+
+ # remove leading and trailing spaces
+ $cmdline =~ s/^\s*(.*)\s*$/$1/;
+
+ if ($self->{state} eq 'page') {
+ my $i = $self->{pagelth};
+ my $ref = $self->{pagedata};
+ my $tot = @$ref;
+
+ # abort if we get a line starting in with a
+ if ($cmdline =~ /^a/io) {
+ undef $ref;
+ $i = 0;
+ }
+
+ # send a tranche of data
+ while ($i-- > 0 && @$ref) {
+ my $line = shift @$ref;
+ $line =~ s/\s+$//o; # why am having to do this?
+ $self->send($line);
+ }
- # strip out //
- $cmdline =~ s|//|/|og;
-
- # split the command line up into parts, the first part is the command
- my ($cmd, $args) = $cmdline =~ /^([\w\/]+)\s*(.*)/o;
+ # reset state if none or else chuck out an intermediate prompt
+ if ($ref && @$ref) {
+ $tot -= $self->{pagelth};
+ $self->send($self->msg('page', $tot));
+ } else {
+ $self->state('prompt');
+ }
+ } else {
+ @ans = run_cmd($self, $cmdline) if length $cmdline;
+
+ if ($self->{pagelth} && @ans > $self->{pagelth}) {
+ my $i;
+ for ($i = $self->{pagelth}; $i-- > 0; ) {
+ my $line = shift @ans;
+ $line =~ s/\s+$//o; # why am having to do this?
+ $self->send($line);
+ }
+ $self->{pagedata} = \@ans;
+ $self->state('page');
+ $self->send($self->msg('page', scalar @ans));
+ } else {
+ for (@ans) {
+ s/\s+$//o; # why ?????????
+ $self->send($_);
+ }
+ }
+ }
+
+ # send a prompt only if we are in a prompt state
+ $self->prompt() if $self->{state} =~ /^prompt/o;
+}
- if ($cmd) {
-
- my ($path, $fcmd);
-
- # first expand out the entry to a command
- ($path, $fcmd) = search($main::localcmd, $cmd, "pl");
- ($path, $fcmd) = search($main::cmd, $cmd, "pl") if !$path || !$fcmd;
+#
+# this is the thing that runs the command, it is done like this for the
+# benefit of remote command execution
+#
+
+sub run_cmd
+{
+ my $self = shift;
+ my $user = $self->{user};
+ my $call = $self->{call};
+ my $cmdline = shift;
+ my @ans;
+
+ if ($self->{func}) {
+ my $c = qq{ \@ans = $self->{func}(\$self, \$cmdline) };
+ dbg('eval', "stored func cmd = $c\n");
+ eval $c;
+ if ($@) {
+ return (1, "Syserr: Eval err $errstr on stored func $self->{func}");
+ }
+ } else {
+
+ # strip out //
+ $cmdline =~ s|//|/|og;
+
+ # split the command line up into parts, the first part is the command
+ my ($cmd, $args) = $cmdline =~ /^([\w\/]+)\s*(.*)/o;
+
+ if ($cmd) {
+
+ my ($path, $fcmd);
+
+ # alias it if possible
+ my $acmd = CmdAlias::get_cmd($cmd);
+ if ($acmd) {
+ ($cmd, $args) = "$acmd $args" =~ /^([\w\/]+)\s*(.*)/o;
+ }
+
+ # first expand out the entry to a command
+ ($path, $fcmd) = search($main::localcmd, $cmd, "pl");
+ ($path, $fcmd) = search($main::cmd, $cmd, "pl") if !$path || !$fcmd;
+
+ my $package = find_cmd_name($path, $fcmd);
+ @ans = (0) if !$package ;
+
+ if ($package) {
+ my $c = qq{ \@ans = $package(\$self, \$args) };
+ dbg('eval', "cluster cmd = $c\n");
+ eval $c;
+ if ($@) {
+ @ans = (0, "Syserr: Eval err cached $package\n$@");
+ }
+ }
+ }
+ }
- my @ans = $self->eval_file($path, $fcmd, $args) if $path && $fcmd;
-# @ans = $self->eval_file($main::cmd, $cmd, $args) if !$ans[0];
if ($ans[0]) {
- shift @ans;
- $self->send(@ans) if @ans > 0;
+ shift @ans;
} else {
- shift @ans;
- if (@ans > 0) {
- $self->msg('e2', @ans);
- } else {
- $self->msg('e1');
- }
+ shift @ans;
+ if (@ans > 0) {
+ unshift @ans, $self->msg('e2');
+ } else {
+ @ans = $self->msg('e1');
+ }
}
- } else {
- $self->msg('e1');
- }
-
- # send a prompt only if we are in a prompt state
- $self->prompt() if $self->{state} =~ /^prompt/o;
+ return (@ans);
}
#
# issue a pc17 to everybody interested
my $nchan = DXChannel->get($main::mycall);
- my $pc17 = $nchan->pc17($ref);
+ my $pc17 = $nchan->pc17($self);
DXProt::broadcast_ak1a($pc17);
-
+
+ Log('DXCommand', "$call disconnected");
$ref->del() if $ref;
}
{
my $self = shift;
my $call = $self->{call};
- DXChannel::msg($self, 'pr', $call);
+ $self->send($self->msg('pr', $call));
+ #DXChannel::msg($self, 'pr', $call);
}
# broadcast a message to all users [except those mentioned after buffer]
my ($apath, $acmd) = split ',', $cmd_cache{$short_cmd};
if ($apath && $acmd) {
dbg('command', "cached $short_cmd = ($apath, $acmd)\n");
- return ($apath, $acmd) if $apath;
+ return ($apath, $acmd);
}
# if not guess
my $curdir = $path;
my $p;
my $i;
+ my @lparts;
for ($i = 0; $i < @parts; $i++) {
my $p = $parts[$i];
$curdir .= "/$l";
last;
}
- } else { # we are dealing with commands
- next if !$l =~ /\.$suffix$/; # only look for .$suffix files
+ } else { # we are dealing with commands
+ @lparts = split /\./, $l;
+ next if $lparts[$#lparts] ne $suffix; # only look for .$suffix files
if ($p eq substr($l, 0, length $p)) {
- $l =~ s/\.$suffix$//; # remove the suffix
- chop $dirfn; # remove trailing /
- $cmd_cache{"$short_cmd"} = join(',', ($path, "$dirfn/$l")); # cache it
- dbg('command', "got path: $path cmd: $dirfn/$l\n");
- return ($path, "$dirfn/$l");
+ pop @lparts; # remove the suffix
+ $l = join '.', @lparts;
+# chop $dirfn; # remove trailing /
+ $cmd_cache{"$short_cmd"} = join(',', ($path, "$dirfn$l")); # cache it
+ dbg('command', "got path: $path cmd: $dirfn$l\n");
+ return ($path, "$dirfn$l");
}
}
}
$string =~ s|/(\d)|sprintf("/_%2x",unpack("C",$1))|eg;
#Dress it up as a real package name
- $string =~ s|/|_|g;
+ $string =~ s/\//_/og;
return "Emb_" . $string;
}
my ($stem, $leaf);
no strict 'refs';
- $pkg = "DXChannel::$pkg\::"; # expand to full symbol table name
+ $pkg = "DXCommandmode::$pkg\::"; # expand to full symbol table name
($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
-
- my $stem_symtab = *{$stem}{HASH};
-
- delete $stem_symtab->{$leaf};
+
+ if ($stem && $leaf) {
+ my $stem_symtab = *{$stem}{HASH};
+ delete $stem_symtab->{$leaf};
+ }
}
-sub eval_file {
- my $self = shift;
+# find a cmd reference
+# this is really for use in user written stubs
+#
+# use the result as a symbolic reference:-
+#
+# no strict 'refs';
+# @out = &$r($self, $line);
+#
+sub find_cmd_ref
+{
+ my $cmd = shift;
+ my $r;
+
+ if ($cmd) {
+
+ # first expand out the entry to a command
+ my ($path, $fcmd) = search($main::localcmd, $cmd, "pl");
+ ($path, $fcmd) = search($main::cmd, $cmd, "pl") if !$path || !$fcmd;
+
+ # make sure it is loaded
+ $r = find_cmd_name($path, $fcmd);
+ }
+ return $r;
+}
+
+#
+# this bit of magic finds a command in the offered directory
+sub find_cmd_name {
my $path = shift;
my $cmdname = shift;
my $package = valid_package_name($cmdname);
my $mtime = -M $filename;
# return if we can't find it
- return (0, DXM::msg('e1')) if !defined $mtime;
+ $errstr = undef;
+ if (undef $mtime) {
+ $errstr = DXM::msg('e1');
+ return undef;
+ }
if(defined $Cache{$package}{mtime} && $Cache{$package}{mtime } <= $mtime) {
#we have compiled this subroutine already,
#print STDERR "already compiled $package->handler\n";
;
} else {
- local *FH;
- if (!open FH, $filename) {
- return (0, "Syserr: can't open '$filename' $!");
+ my $fh = new FileHandle;
+ if (!open $fh, $filename) {
+ $errstr = "Syserr: can't open '$filename' $!";
+ return undef;
};
- local($/) = undef;
- my $sub = <FH>;
- close FH;
+ local $/ = undef;
+ my $sub = <$fh>;
+ close $fh;
#wrap the code into a subroutine inside our unique package
- my $eval = qq{package DXChannel; sub $package { $sub; }};
+ my $eval = qq{
+ sub $package
+ {
+ $sub
+ } };
+
if (isdbg('eval')) {
my @list = split /\n/, $eval;
my $line;
- foreach (@list) {
+ for (@list) {
dbg('eval', $_, "\n");
}
}
- #print "eval $eval\n";
+
{
#hide our variables within this block
my($filename,$mtime,$package,$sub);
eval $eval;
}
+
if ($@) {
+ print "\$\@ = $@";
+ $errstr = $@;
delete_package($package);
- return (0, "Syserr: Eval err $@ on $package");
+ } else {
+ #cache it unless we're cleaning out each time
+ $Cache{$package}{mtime} = $mtime;
}
-
- #cache it unless we're cleaning out each time
- $Cache{$package}{mtime} = $mtime;
}
- my @r;
- my $c = qq{ \@r = \$self->$package(\@_); };
- dbg('eval', "cluster cmd = $c\n");
- eval $c; ;
- if ($@) {
- delete_package($package);
- return (0, "Syserr: Eval err $@ on cached $package");
- }
-
- #take a look if you want
#print Devel::Symdump->rnew($package)->as_string, $/;
- return @r;
+ $package = "DXCommandmode::$package" if $package;
+ $package = undef if $errstr;
+ return $package;
}
1;