--- /dev/null
+#!/usr/bin/perl
+#
+# This module impliments the user facing command mode for a dx cluster
+#
+# Copyright (c) 1998 Dirk Koopman G1TLH
+#
+# $Id$
+#
+
+package DXCommandmode;
+
+use DXUtil;
+use DXChannel;
+use DXUser;
+use DXM;
+use DXVars;
+
+$last_dir_mtime = 0; # the last time one of the cmd dirs was modified
+@cmd = undef; # a list of commands+path pairs (in alphabetical order)
+
+# this is how a a connection starts, you get a hello message and the motd with
+# possibly some other messages asking you to set various things up if you are
+# new (or nearly new and slacking) user.
+
+sub user_start
+{
+ my $self = shift;
+ my $user = $self->{user};
+ my $call = $self->{call};
+ my $name = $self->{name};
+ $name = $call if !defined $name;
+ $self->{normal} = \&user_normal; # rfu for now
+ $self->{finish} = \&user_finish;
+ $self->msg('l2',$name);
+ $self->send_file($main::motd) if (-e $main::motd);
+ $self->msg('pr', $call);
+ $self->{state} = 10; # a bit of room for further expansion, passwords etc
+ $self->{priv} = 0; # set the connection priv to 0 - can be upgraded later
+}
+
+#
+# This is the normal command prompt driver
+#
+sub user_normal
+{
+ my $self = shift;
+ my $user = $self->{user};
+ my $call = $self->{call};
+ my $cmd = shift;
+
+ # read in the list of valid commands, note that the commands themselves are cached elsewhere
+ scan_cmd_dirs if (!defined %cmd);
+
+ # strip out any nasty characters like $@%&|. and double // etc.
+ $cmd =~ s/[\%\@\$\&\|\.\`\~]//og;
+ $cmd =~ s|//|/|og;
+
+ # split the command up into parts
+ my @parts = split |[/\b]+|, $cmd;
+
+ # first expand out the entry to a command, note that I will accept
+ # anything in any case with any (reasonable) seperator
+ $self->prompt();
+}
+
+#
+# This is called from inside the main cluster processing loop and is used
+# for despatching commands that are doing some long processing job
+#
+sub user_process
+{
+
+}
+
+#
+# finish up a user context
+#
+sub user_finish
+{
+
+}
+
+#
+# short cut to output a prompt
+#
+
+sub prompt
+{
+ my $self = shift;
+ my $call = $self->{call};
+ $self->msg('pr', $call);
+}
+
+#
+# scan the command directories to see if things have changed
+#
+# If they have remake the command list
+#
+# There are two command directories a) the standard one and b) the local one
+# The local one overides the standard one
+#
+
+sub scan_cmd_dirs
+{
+ my $self = shift;
+
+
+}
+
+#
+# the persistant execution of things from the command directories
+#
+#
+# This allows perl programs to call functions dynamically
+#
+# This has been nicked directly from the perlembed pages
+#
+
+#require Devel::Symdump;
+use strict;
+use vars '%Cache';
+
+sub valid_package_name {
+ my($string) = @_;
+ $string =~ s/([^A-Za-z0-9\/])/sprintf("_%2x",unpack("C",$1))/eg;
+
+ #second pass only for words starting with a digit
+ $string =~ s|/(\d)|sprintf("/_%2x",unpack("C",$1))|eg;
+
+ #Dress it up as a real package name
+ $string =~ s|/|::|g;
+ return "DXEmbed" . $string;
+}
+
+#borrowed from Safe.pm
+sub delete_package {
+ my $pkg = shift;
+ my ($stem, $leaf);
+
+ no strict 'refs';
+ $pkg = "main::$pkg\::"; # expand to full symbol table name
+ ($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
+
+ my $stem_symtab = *{$stem}{HASH};
+
+ delete $stem_symtab->{$leaf};
+}
+
+sub eval_file {
+ my($self, $path, $cmdname) = @_;
+ my $package = valid_package_name($cmdname);
+ my $filename = "$path/$cmdname";
+ my $mtime = -m $filename;
+ my @r;
+
+ if(defined $Cache{$package}{mtime} && $Cache{$package}{mtime } <= $mtime) {
+ #we have compiled this subroutine already,
+ #it has not been updated on disk, nothing left to do
+ #print STDERR "already compiled $package->handler\n";
+ ;
+ } else {
+ local *FH;
+ open FH, $filename or die "open '$filename' $!";
+ local($/) = undef;
+ my $sub = <FH>;
+ close FH;
+
+ #wrap the code into a subroutine inside our unique package
+ my $eval = qq{package $package; sub handler { $sub; }};
+ {
+ #hide our variables within this block
+ my($filename,$mtime,$package,$sub);
+ eval $eval;
+ }
+ if ($@) {
+ $self->send("Eval err $@ on $package");
+ delete_package($package);
+ return undef;
+ }
+
+ #cache it unless we're cleaning out each time
+ $Cache{$package}{mtime} = $mtime unless $delete;
+ }
+
+ @r = eval {$package->handler;};
+ if ($@) {
+ $self->send("Eval err $@ on cached $package");
+ delete_package($package);
+ return undef;
+ }
+
+ #take a look if you want
+ #print Devel::Symdump->rnew($package)->as_string, $/;
+ return @r;
+}
+
+1;
+__END__