shelldap
changeset 109 d37c6197818f
parent 108 24340fdef276
child 110 dbceec51da98
--- a/shelldap	Mon Apr 29 21:57:57 2019 +0200
+++ b/shelldap	Tue Apr 30 00:43:52 2019 +0200
@@ -242,7 +242,9 @@
 
 =head2 cat
 
-Display LDIF dump of an entry. Globbing is supported. Specify either full DN, or a RDN.
+Print contents of LDAP entry to STDOUT in LDIF format.
+
+Globbing is supported. Specify either full DN, or a RDN.
 RDNs are local to the current search base ('cwd' in shell terms). If RDN is '.' or missing,
 it defaults to the current search base.
 You may additionally add a list of attributes to display (e.g. use '+' for operational
@@ -257,11 +259,13 @@
 
 =head2 less
 
-Like cat, but uses the configured pager to display output.
+Like 'cat', but use configured pager to paginate output.
 
 =head2 cd
 
-Change directory.  Translated to LDAP, this changes the current basedn.
+Change the working directory (LDAP search base).
+
+Translated to LDAP, this changes the current basedn.
 All commands after a 'cd' operate within the new basedn.
 
     cd                  change to 'home' (binddn if any, or basedn)
@@ -282,11 +286,17 @@
 
 =head2 clear
 
-Clear the screen.
+Clear the terminal screen.
+
+Clears screen similar to 'clear' or Ctrl+l on the shell command line.
+
+Ctrl+l alias is also supported.
 
 =head2 copy
 
-Copy an entry to a different DN path.  All copies are relative to the
+Copy an entry.
+
+All copies are relative to the
 current basedn unless a full DN is specified.  All attributes are
 copied and then an LDAP moddn() is performed.
 
@@ -298,7 +308,9 @@
 
 =head2 create
 
-Create an entry from scratch.  Arguments are space separated objectClass
+Create an entry.
+
+Arguments are space separated objectClass
 names.  Possible objectClasses are derived automatically from the
 server, and will tab-complete.
 
@@ -313,7 +325,9 @@
 
 =head2 delete
 
-Remove an entry from the directory.  Globbing is supported.
+Remove an entry.
+
+Globbing is supported.
 All deletes are sanity-prompted.  The -v flag prints the entries out
 for review before delete.
 
@@ -325,7 +339,9 @@
 
 =head2 edit
 
-Edit an entry in an external editor.  After the editor exits, the
+Edit an entry in an external editor.
+
+After the editor exits, the
 resulting LDIF is sanity checked, and changes are written to the LDAP
 directory.
 
@@ -335,11 +351,15 @@
 
 =head2 env
 
- Show values for various runtime variables.
+Print values of configurable shelldap variables.
+
+This is a subset of all variables configurable via shelldap config
+file and/or its command line options.
 
 =head2 grep
 
-Search for arbitrary LDAP filters, and return matching DN results.
+Search using LDAP filters and return matching DN results.
+
 The search string must be a valid LDAP filter.
 
     grep uid=mahlon
@@ -350,8 +370,9 @@
 
 =head2 inspect
 
-View schema information about a given entry, or a list of arbitrary
-objectClasses, along with the most common flags for the objectClass
+View schema and flags for an entry or objectClass.
+
+It also includes the most common flags for the objectClass
 attributes.
 
     inspect uid=mahlon
@@ -369,7 +390,9 @@
 
 =head2 list
 
-List entries for the current basedn.  Globbing is supported.
+List directory contents.
+
+Globbing is supported.
 
 aliased to: ls
 
@@ -391,19 +414,23 @@
 
 =head2 mkdir
 
-Creates a new 'organizationalUnit' entry.
-
-    mkdir containername
-    mkdir ou=whatever
+Create a new 'organizationalUnit' LDAP entry.
+
+	mkdir containername
+	mkdir ou=whatever
 
 =head2 move
 
-Move an entry to a different dn path.  Usage is identical to B<copy>.
+Move (rename) entry.
+
+Usage is identical to B<copy>.
 
 aliased to: mv
 
 =head2 passwd
 
+Change user password.
+
 If supported server side, change the password for a specified entry.
 The entry must have a 'userPassword' attribute.
 
@@ -411,22 +438,22 @@
 
 =head2 pwd
 
-Print the 'working directory' - aka, the current ldap basedn.
+Print name of current/working LDAP search base.
 
 =head2 setenv
 
-Modify various runtime variables normally set from the command line.
+Change or define shelldap variable.
 
     setenv debug 1
     export debug=1
 
 =head2 whoami
 
+Print current bind DN.
+
 Show current auth credentials.  Unless you specified a binddn, this
 will just show an anonymous bind.
 
-aliased to: id
-
 =back
 
 =head1 TODO
@@ -486,6 +513,7 @@
 use Algorithm::Diff qw//;
 use Carp 'confess';
 use POSIX qw//;
+use Tie::IxHash qw//;
 use base 'Term::Shell';
 
 my $conf = $main::conf;
@@ -494,6 +522,55 @@
 $SIG{'__DIE__'} = \&Carp::confess if $conf->{'debug'};
 
 
+
+########################################################################
+### Term::Shell Fixes
+########################################################################
+
+# Term::Shell function add_handlers() is implemented in an incorrect way.
+# We reimplement the method here to fix its problems.
+
+# In add_handlers, we split searching for aliases in a separate loop,
+# because otherwise not all aliases are registered before we look them
+# up.
+sub add_handlers
+{
+    my $o = shift;
+    for my $hnd (@_)
+    {
+        next unless $hnd =~ /^(run|help|smry|comp|catch|alias)_/o;
+        my $t = $1;
+        my $a = substr( $hnd, length($t) + 1 );
+ 
+        # Add on the prefix and suffix if the command is defined
+        if ( length $a )
+        {
+            substr( $a, 0, 0 ) = $o->cmd_prefix;
+            $a .= $o->cmd_suffix;
+        }
+        $o->{handlers}{$a}{$t} = $hnd;
+    }
+    for my $hnd (@_)
+    {
+        next unless $hnd =~ /^(run|help|smry|comp|catch|alias)_/o;
+        my $t = $1;
+        my $a = substr( $hnd, length($t) + 1 );
+
+        if ( $o->has_aliases($a) )
+        {
+            my @a = $o->get_aliases($a);
+            for my $alias (@a)
+            {
+                substr( $alias, 0, 0 ) = $o->cmd_prefix;
+                $alias .= $o->cmd_suffix;
+                $o->{handlers}{$alias}{$t} = $hnd;
+            }
+        }
+    }
+}
+
+
+
 ########################################################################
 ### U T I L I T Y   F U N C T I O N S
 ########################################################################
@@ -1379,9 +1456,10 @@
 #
 # It is not necessary to list all real commands here, but you can/should
 # do so to assign autocompleter function to them, and/or to define aliases.
-my %cmd_map = (
-	#'whoami'  => [ undef ],
-	#'pwd'     => [ undef ],
+tie my %cmd_map, 'Tie::IxHash';
+%cmd_map = (
+	'whoami'  => [ undef ],
+	'pwd'     => [ undef ],
 	'list'    => [ undef, 'comp_cwd' ],
 	'grep'    => [ undef, 'comp_cwd' ],
 	'edit'    => [ undef, 'comp_cwd' ],
@@ -1393,11 +1471,11 @@
 	'cd'      => [ undef, 'comp_cwd' ],
 	'create'  => [ undef, 'comp_create' ],
 	'setenv'  => [ undef, 'comp_setenv' ],
-	#'passwd'  => [ undef ],
-	##'clear'   => [ undef ],
-	#'env'     => [ undef ],
+	'passwd'  => [ undef ],
+	'clear'   => [ undef ],
+	'env'     => [ undef ],
 	#'help'    => [ undef ],
-	#'mkdir'   => [ undef ],
+	'mkdir'   => [ undef ],
 	'inspect' => [ undef, 'comp_inspect' ],
 
 	'id'      => [ 'whoami' ],
@@ -1415,11 +1493,6 @@
 );
 
 
-### Don't die on a newline, just no-op.
-###
-sub run_ { return; }
-
-
 ### Term::Shell hook.
 ### Write history for each command, print shell debug actions.
 ###
@@ -1928,28 +2001,6 @@
 }
 
 
-### Override internal help function with pod2usage output.
-###
-sub run_help 
-{
-	my $self = shift;
-	my $section = 'SHELL COMMANDS';
-
-	my $command = shift;
-	if( $command ) {
-		# If it is an alias, resolve to real name:
-		$command = $cmd_map{$command}[0] if $cmd_map{$command}[0];
-		$section .= "/$command"
-	}
-
-	return Pod::Usage::pod2usage(
-		-exitval  => 'NOEXIT',
-		-verbose  => 99,
-		-sections => $section
-	);
-}
-
-
 ### Generate and display a list of LDAP entries, relative to the current
 ### location the command was run from.
 ###
@@ -2352,25 +2403,46 @@
 
 ### Inject various autocomplete and alias routines into the symbol table.
 ###
-
-# setup alias subs
-#
-# Term::Shell has an alias_* feature, but
-# it seems to work about 90% of the time.
-# that last 10% is something of a mystery.
-#
 { no strict 'refs';
+	local $| = 1;
+	my %aliases;
+
 	while(my($cmd, $data) = each %cmd_map ) {
+		# If command is an alias, insert alias symbol.
 		if( $$data[0]) {
-			my $alias_sub = 'run_' . $cmd;
-			my $real_sub  = 'run_' . $$data[0];
-			*$alias_sub = \&$real_sub;
+			$aliases{$$data[0]} ||= [];
+			push @{$aliases{$$data[0]}}, $cmd;
+
+		} else {
+			## If completer is defined, set it.
+			#if( $$data[1]) {
+			#	my $comp_sub = "comp_$cmd";
+			#	*$comp_sub   = \&{$$data[1]}
+			#}
+
+			# Define help and summary functions for the command:
+			my $pod = ''; open my $io, '>', \$pod;
+			Pod::Usage::pod2usage( -exitval  => 'NOEXIT', -verbose  => 99, -sections => "SHELL COMMANDS/${\( quotemeta $cmd )}", -output => \*$io );
+			my @pod = split /\n/, $pod;
+			my $summary = $pod[1];
+			if($summary) {
+				$summary =~ s/^\s+//s;
+				$summary =~ s/\s+$//s;
+				$summary =~ s/\s+/ /s;
+			}
+			my $help = join "\n", @pod;
+
+			my $helpfunc = sub { "$help\n" };
+			*{"help_$cmd"} = \&$helpfunc;
+
+			my $summfunc = sub { $summary };
+			*{"smry_$cmd"} = \&$summfunc;
 		}
-
-		if( $$data[1]) {
-			my $comp_sub = "comp_$cmd";
-			*$comp_sub   = \&{$$data[1]}
-		}
+	}
+
+	while(my($cmd,$aliases) = each %aliases) {
+		my $aliasfunc = sub { @$aliases };
+		*{"alias_$cmd"} = \&$aliasfunc;
 	}
 }
 
@@ -2432,10 +2504,10 @@
 	'tls_key=s',
 	'tls', 'debug', 'version',
 	 help => sub {
-		Pod::Usage::pod2usage(
-			-verbose => 1,
-			-message => "\n$0 command line flags\n" . '-' x 65
-		);
+	  Pod::Usage::pod2usage(
+	  	-verbose => 1,
+	  	-message => "\n$0 command line flags\n" . '-' x 65
+	  );
 	}
 );