[PATCH] Improve extensions support

HacKan hackan at gmail.com
Thu Mar 23 04:27:06 CET 2017


- Change command selection switch based on parameters by a more flexible
design using eval.
- Read user extensions first when executing a command, so even internal
commmands can be overridden.
- Add helper `override_function()` to enable extensions to override, but
use, internal functions. Overriden functions are renamed to
`pass_{function name}`.
---
 src/password-store.sh | 118 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 80 insertions(+), 38 deletions(-)

diff --git a/src/password-store.sh b/src/password-store.sh
index 2a9cad6..893ca66 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -142,6 +142,42 @@ check_sneaky_paths() {
 	done
 }
 
+SYSTEM_EXTENSION_DIR=""
+check_extension_and_load() {
+	check_sneaky_paths "$1"
+	local user_extension system_extension extension
+	[[ -n $SYSTEM_EXTENSION_DIR ]] && system_extension="$SYSTEM_EXTENSION_DIR/$1.bash"
+	[[ $PASSWORD_STORE_ENABLE_EXTENSIONS == true ]] && user_extension="$EXTENSIONS/$1.bash"
+	if [[ -n $user_extension && -f $user_extension && -x $user_extension ]]; then
+		verify_file "$user_extension"
+		extension="$user_extension"
+	elif [[ -n $system_extension && -f $system_extension && -x $system_extension ]]; then
+		extension="$system_extension"
+	else
+		return 1
+	fi
+	shift
+	# shellcheck source=/dev/null
+	source "$extension" "$@"
+	return 0
+}
+
+override_function() {
+	# Recieves a function name, and it renames it to pass_<function name>
+	# based on http://mivok.net/2009/09/20/bashfunctionoverrist.html
+	local func="$1"
+
+	if type "$func" > /dev/null 2>&1; then
+		local contents newname
+		contents=$(declare -f $func)
+		newname="pass_${func}${contents#$func}"
+		eval "$newname"
+		return $?
+	fi
+
+	return 1
+}
+
 in_array() {
 	local seeking="$1"; shift
 	local in=1
@@ -338,6 +374,8 @@ cmd_usage() {
 	
 	echo "More information may be found in the ${PROGRAM}(1) man page."
 }
+cmd_help() { cmd_usage "$@"; }
+cmd_h() { cmd_usage "$@"; }
 
 cmd_init() {
 	local opts id_path=""
@@ -500,6 +538,7 @@ cmd_insert() {
 	fi
 	git_add_file "$passfile" "Add given password for $path to store."
 }
+cmd_add() { cmd_insert "$@"; }
 
 cmd_edit() {
 	[[ $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND pass-name"
@@ -612,6 +651,8 @@ cmd_delete() {
 	fi
 	rmdir -p "${passfile%/*}" 2>/dev/null
 }
+cmd_remove() { cmd_delete "$@"; }
+cmd_rm() { cmd_delete "$@"; }
 
 cmd_copy_move() {
 	local opts move=1 force=0
@@ -667,6 +708,10 @@ cmd_copy_move() {
 		git_add_file "$new_path" "Copy ${1} to ${2}."
 	fi
 }
+cmd_rename() { cmd_copy_move "move" "$@"; }
+cmd_mv() { cmd_copy_move "move" "$@"; }
+cmd_cp() { cmd_copy_move "copy" "$@"; }
+cmd_copy() { cmd_copy_move "copy" "$@"; }
 
 cmd_git() {
 	set_git "$PREFIX/"
@@ -688,30 +733,26 @@ cmd_git() {
 	fi
 }
 
-cmd_extension_or_show() {
-	if ! cmd_extension "$@"; then
-		COMMAND="show"
-		cmd_show "$@"
-	fi
-}
+cmd_internal() {
+	local cmd counter=0
+	cmd="$COMMAND"
 
-SYSTEM_EXTENSION_DIR=""
-cmd_extension() {
-	check_sneaky_paths "$1"
-	local user_extension system_extension extension
-	[[ -n $SYSTEM_EXTENSION_DIR ]] && system_extension="$SYSTEM_EXTENSION_DIR/$1.bash"
-	[[ $PASSWORD_STORE_ENABLE_EXTENSIONS == true ]] && user_extension="$EXTENSIONS/$1.bash"
-	if [[ -n $user_extension && -f $user_extension && -x $user_extension ]]; then
-		verify_file "$user_extension"
-		extension="$user_extension"
-	elif [[ -n $system_extension && -f $system_extension && -x $system_extension ]]; then
-		extension="$system_extension"
-	else
-		return 1
+	# Remove dashes (up to 2)
+	while [[ "${cmd:0:1}" == "-" && $counter -lt 2 ]]; do
+		cmd="${cmd:1}"
+		let counter+=1
+	done
+
+	# Check if a function exists
+	if type "cmd_${cmd}" > /dev/null 2>&1; then
+		shift
+		eval '"cmd_${cmd}" "$@"'
+		# Internal commands don't handle well return values, but they either
+		# die (exit 1) or succeed, so fix return value to 0
+		return 0
 	fi
-	shift
-	source "$extension" "$@"
-	return 0
+
+	return 1
 }
 
 #
@@ -721,20 +762,21 @@ cmd_extension() {
 PROGRAM="${0##*/}"
 COMMAND="$1"
 
-case "$1" in
-	init) shift;			cmd_init "$@" ;;
-	help|--help) shift;		cmd_usage "$@" ;;
-	version|--version) shift;	cmd_version "$@" ;;
-	show|ls|list) shift;		cmd_show "$@" ;;
-	find|search) shift;		cmd_find "$@" ;;
-	grep) shift;			cmd_grep "$@" ;;
-	insert|add) shift;		cmd_insert "$@" ;;
-	edit) shift;			cmd_edit "$@" ;;
-	generate) shift;		cmd_generate "$@" ;;
-	delete|rm|remove) shift;	cmd_delete "$@" ;;
-	rename|mv) shift;		cmd_copy_move "move" "$@" ;;
-	copy|cp) shift;			cmd_copy_move "copy" "$@" ;;
-	git) shift;			cmd_git "$@" ;;
-	*)				cmd_extension_or_show "$@" ;;
-esac
+# Check if command is an extension
+check_extension_and_load "$@" && exit 0
+
+# Check if command is internal command
+#
+# Internal commands must begin with cmd_ and named like cmd_command
+# Multiname functions such as delete=remove=rm can be done like
+#  cmd_remove() { cmd_delete "$@"; }
+#  cmd_rm() { cmd_delete "$@"; }
+# Others like help=--help=-h are handled by cmd_internal() as long as a
+# function without the dashes but same name exists.
+cmd_internal "$@" && exit 0
+
+# Assume its an implicit show command
+COMMAND="show"
+check_extension_and_load show "$@" || cmd_show "$@"
+
 exit 0
-- 
2.11.0



More information about the Password-Store mailing list