Add alias, unalias, configfile
authorDavor Ocelic <docelic@crystallabs.io>
Sun, 05 May 2019 09:54:28 +0200
changeset 115 5ace587c3ea3
parent 114 35b7143830e8
child 116 6a0b1209dafa
Add alias, unalias, configfile - Add commands 'alias' and 'unalias'. Behavior is almost identical to shell. Aliases currently cannot override commands. - Add command 'configfile'. Allows loading / saving config files from command line.
shelldap
--- a/shelldap	Sat May 04 22:38:33 2019 +0200
+++ b/shelldap	Sun May 05 09:54:28 2019 +0200
@@ -145,7 +145,7 @@
 sizelimit errors, you'll likely need server configuration to raise the
 limits for paginated results.
 
-	--paginate 100
+  --paginate 100
 
 =back
 
@@ -240,6 +240,25 @@
 
 =over 4
 
+=head2 alias
+
+Define or display aliases.
+
+Without arguments, `alias' prints the list of aliases in the reusable
+form `alias NAME=VALUE' on standard output.
+
+Otherwise, an alias is defined for each NAME whose VALUE is given.
+A trailing space in VALUE causes the next word to be checked for
+alias substitution when the alias is expanded.
+
+    alias
+    alias ll=ls -al
+    alias ll
+    alias show=cat
+    alias cmd1=command 'arg with spaces'
+    alias cmd2='command '
+    alias cmd2=command 'with_arg '
+
 =head2 cat
 
 Print contents of LDAP entry to STDOUT in LDIF format.
@@ -257,6 +276,24 @@
     cat uid=mahlon,ou=People,dc=example,o=company
     cat uid=mahlon + userPassword
 
+=head2 configfile
+
+Load or save config file.
+
+If no config file is specified as argument to 'load', the default search list is:
+
+    $HOME/.shelldap.rc
+    /usr/local/etc/shelldap.conf
+    /etc/shelldap.conf
+
+If no config file is specified as argument to 'save', the default path is
+$HOME/.shelldap.rc.
+
+    configfile load
+    configfile load /path/to/config
+    configfile save
+    configfile save /path/to/config
+
 =head2 less
 
 Like 'cat', but use configured pager to paginate output.
@@ -404,8 +441,8 @@
 
 Create a new 'organizationalUnit' LDAP entry.
 
-	mkdir containername
-	mkdir ou=whatever
+  mkdir containername
+  mkdir ou=whatever
 
 =head2 move
 
@@ -433,6 +470,16 @@
     setenv debug 1
     export debug=1
 
+=head2 unalias
+
+Remove each NAME from the list of defined aliases.
+
+    alias ll=ls -al
+    alias
+    unalias ll
+    unalias ll ls
+    alias
+
 =head2 whoami
 
 Print current bind DN.
@@ -1451,11 +1498,11 @@
 
 # alias_or_command => [ real_command_name, completion_function ]
 #
-# 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.
 tie my %cmd_map, 'Tie::IxHash';
 %cmd_map = (
 	# Real commands:
+	'alias'   => [ undef ],
+	'configfile'=> [ undef ],
 	'whoami'  => [ undef ],
 	'pwd'     => [ undef ],
 	'list'    => [ undef, 'autocomplete_from_cwd' ],
@@ -1475,6 +1522,7 @@
 	#'help'    => [ undef ],
 	'mkdir'   => [ undef ],
 	'inspect' => [ undef, 'autocomplete_from_objectclasses_and_cwd' ],
+	'unalias' => [ undef ],
 
 	# Aliases:
 	'id'      => [ 'whoami' ],
@@ -1508,6 +1556,81 @@
 } 
 
 
+### Display or define aliases.
+###
+sub run_alias
+{
+	my $self  = shift;
+	my $cmd_alias = shift;
+
+	# If $cmd_alias is empty, user requested printing of known aliases
+	unless($cmd_alias) {
+		while(my($alias,$cmd_args) = each %{$conf->{alias}}) {
+			print "alias $alias=${\( join ' ', map { $_=~ /\s/ ? \"'$_'\" : $_} @{$cmd_args})}\n";
+		}
+		return
+
+	# If there is argument but without =, user wanted to print specific alias
+	} elsif($cmd_alias !~ /=/ and !@_) {
+		my $alias = $cmd_alias;
+		my $cmd_args = $conf->{alias}{$alias};
+		unless( $cmd_args) {
+			print "alias: $alias: not found\n";
+		} else {
+			print "alias $alias=${\( join ', ', map {\"'$_'\"} @{$cmd_args})}\n";
+		}
+		return
+
+	# There is argument with =, so the line is a new alias definition
+	} else {
+		my($alias, $command) = split /=/, $cmd_alias, 2;
+		$command = $cmd_map{$command}[0] if $cmd_map{$command} and $cmd_map{$command}[0];
+		$conf->{alias}{$alias} = [ $command, @_ ];
+	}
+}
+
+# Remove alias
+sub run_unalias
+{
+	my $self = shift;
+	for my $alias(@_) {
+		unless( $conf->{alias}{$alias}) {
+			print "alias: $alias: not found\n";
+		} else {
+		delete $conf->{alias}{$alias};
+		}
+	}
+	return
+}
+
+# Run aliased command when alias is entered
+sub catch_run {
+	my $self = shift;
+	my @cmdline;
+
+	unless( $conf->{alias}{$_[0]}) {
+		print $self->msg_unknown_cmd($_[0]);
+		return
+	}
+
+	my $done = 0;
+	while(my $arg = $_[0]) {
+		my @alias = @{$conf->{alias}{$arg} or last};
+
+		if($alias[-1] !~ s/\s+$//) {
+			$done++
+		}
+		push @cmdline, @alias;
+		shift;
+		last if $done;
+	}
+
+	push @cmdline, @_;
+
+	$self->run(@cmdline);
+}
+
+
 ### Display an entry as LDIF to the terminal.
 ###
 sub run_cat 
@@ -1520,6 +1643,29 @@
 }
 
 
+### Load or save config
+###
+sub run_configfile
+{
+	my $self     = shift;
+	my $action   = shift;
+	my $filepath = shift;
+
+	unless ( $action and $action =~ /^(?:load|save)$/) {
+		print "No action specified; use 'load' or 'save'.\n";
+		return;
+	}
+
+	if( $action eq 'load') {
+		main::load_config($filepath);
+		print "Loaded.\n";
+	} elsif( $action eq 'save') {
+		main::save_config($filepath);
+		print "Saved.\n";
+	}
+}
+
+
 ### Display an entry as LDIF to the terminal with external pagination.
 ###
 sub run_less
@@ -2087,10 +2233,8 @@
 			# strip the current base from the dn, if we're recursing and not in long mode
 			if ( $recurse ) {
 				$dn =~ s/,$base//oi;
-			}
-
 			# only show RDN unless -l was given
-			else {
+			} else {
 				$dn = canonical_dn( [shift(@{ldap_explode_dn($dn, casefold => 'none')})], casefold => 'none' )
 			}
 		}
@@ -2607,6 +2751,8 @@
 		delete $conf->{'configfile'}
 	}
 
+	$conf->{alias} ||= {};
+
 	return $conf;
 }
 
@@ -2616,7 +2762,7 @@
 ###
 sub save_config
 {
-	my $confpath = shift;
+	my $confpath = (shift) || "$ENV{'HOME'}/.shelldap.rc";
 
 	# This check is currently unnecessary because the comparison will always
 	# be true, but is left here for effect of least surprise in the future.