From d1305ed102b067a0150e92e736308478e66bbe2d Mon Sep 17 00:00:00 2001
From: Tino Calancha <tino.calancha@gmail.com>
Date: Tue, 16 Jul 2019 19:08:03 +0200
Subject: [PATCH] Support Git/tree versions available in Centos 7

Git option -C requires version >= 1.8.5.  Add a wrapper
function, exec_git_commandm to run the Git commands.  If
the system Git supports -C option, then use it; otherwise,
emulate that option inside the wrapper.

The options --matchdirs/--ignore-case, used by cmd_find,
require tree version >= v1.7.0.  For lower versions, print a message
to stderr and run tree without the unsupported options.

See: https://bugzilla.redhat.com/show_bug.cgi?id=1639806
* src/password-store.sh (exec_git_command): New function.
(cmd_find): Use --matchdirs/--ignore-case options if they are
available; otherwise, warn user and run tree without the
unsupported options.

* tests/t0500-find.sh: Update find test to cover tree versions < 1.7.0.

* tests/t0300-reencryption.sh: Tweak test to pass on git v1.8.3.
---
 src/password-store.sh       | 57 +++++++++++++++++++++++++++----------
 tests/t0300-reencryption.sh | 16 +++++------
 tests/t0500-find.sh         | 42 ++++++++++++++++++---------
 3 files changed, 79 insertions(+), 36 deletions(-)

diff --git a/src/password-store.sh b/src/password-store.sh
index b99460c..0488c04 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -26,24 +26,40 @@ export GIT_CEILING_DIRECTORIES="$PREFIX/.."
 # BEGIN helper functions
 #
 
+# If the system Git doesn't support the -C option, then emulate
+# it with pushd/popd.
+exec_git_command() {
+    if git --help | grep -q '\[-C <path>\]'; then
+        git -C "$INNER_GIT_DIR" "$@"
+        return $?
+    fi
+    # Emulate Git -C option.
+    pushd "$INNER_GIT_DIR" > /dev/null
+    git "$@"
+    local result=$?
+    popd > /dev/null
+    return $result
+}
+
+
 set_git() {
 	INNER_GIT_DIR="${1%/*}"
 	while [[ ! -d $INNER_GIT_DIR && ${INNER_GIT_DIR%/*}/ == "${PREFIX%/}/"* ]]; do
 		INNER_GIT_DIR="${INNER_GIT_DIR%/*}"
 	done
-	[[ $(git -C "$INNER_GIT_DIR" rev-parse --is-inside-work-tree 2>/dev/null) == true ]] || INNER_GIT_DIR=""
+	[[ $(exec_git_command rev-parse --is-inside-work-tree 2>/dev/null) == true ]] || INNER_GIT_DIR=""
 }
 git_add_file() {
 	[[ -n $INNER_GIT_DIR ]] || return
-	git -C "$INNER_GIT_DIR" add "$1" || return
-	[[ -n $(git -C "$INNER_GIT_DIR" status --porcelain "$1") ]] || return
+	exec_git_command add "$1" || return
+	[[ -n $(exec_git_command status --porcelain "$1") ]] || return
 	git_commit "$2"
 }
 git_commit() {
 	local sign=""
 	[[ -n $INNER_GIT_DIR ]] || return
-	[[ $(git -C "$INNER_GIT_DIR" config --bool --get pass.signcommits) == "true" ]] && sign="-S"
-	git -C "$INNER_GIT_DIR" commit $sign -m "$1"
+	[[ $(exec_git_command config --bool --get pass.signcommits) == "true" ]] && sign="-S"
+	exec_git_command commit $sign -m "$1"
 }
 yesno() {
 	[[ -t 0 ]] || return 0
@@ -336,7 +352,7 @@ cmd_init() {
 		[[ ! -f "$gpg_id" ]] && die "Error: $gpg_id does not exist and so cannot be removed."
 		rm -v -f "$gpg_id" || exit 1
 		if [[ -n $INNER_GIT_DIR ]]; then
-			git -C "$INNER_GIT_DIR" rm -qr "$gpg_id"
+			exec_git_command rm -qr "$gpg_id"
 			git_commit "Deinitialize ${gpg_id}${id_path:+ ($id_path)}."
 		fi
 		rmdir -p "${gpg_id%/*}" 2>/dev/null
@@ -411,7 +427,18 @@ cmd_find() {
 	[[ $# -eq 0 ]] && die "Usage: $PROGRAM $COMMAND pass-names..."
 	IFS="," eval 'echo "Search Terms: $*"'
 	local terms="*$(printf '%s*|*' "$@")"
-	tree -C -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
+    local tree_version=$(tree --version | sed 's/tree \([^[:space:]]\+\).*/\1/g')
+    # Use --matchdirs and --ignore-case options only when available.
+    if tree --help |& grep -q -e --matchdirs; then
+	    tree -C -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
+    else
+        cat >&2 <<-_EOF
+        Your tree version ($tree_version) doesn't support --ignore-case/--matchdirs options.
+        Upgrade tree to version >= 1.7.0 if you wish to use them.
+
+_EOF
+        tree -C -l --noreport -P "${terms%|*}" --prune "$PREFIX" | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
+    fi
 }
 
 cmd_grep() {
@@ -584,7 +611,7 @@ cmd_delete() {
 	rm $recursive -f -v "$passfile"
 	set_git "$passfile"
 	if [[ -n $INNER_GIT_DIR && ! -e $passfile ]]; then
-		git -C "$INNER_GIT_DIR" rm -qr "$passfile"
+		exec_git_command rm -qr "$passfile"
 		set_git "$passfile"
 		git_commit "Remove $path from store."
 	fi
@@ -628,15 +655,15 @@ cmd_copy_move() {
 
 		set_git "$new_path"
 		if [[ -n $INNER_GIT_DIR && ! -e $old_path ]]; then
-			git -C "$INNER_GIT_DIR" rm -qr "$old_path" 2>/dev/null
+			exec_git_command rm -qr "$old_path" 2>/dev/null
 			set_git "$new_path"
 			git_add_file "$new_path" "Rename ${1} to ${2}."
 		fi
 		set_git "$old_path"
 		if [[ -n $INNER_GIT_DIR && ! -e $old_path ]]; then
-			git -C "$INNER_GIT_DIR" rm -qr "$old_path" 2>/dev/null
+			exec_git_command rm -qr "$old_path" 2>/dev/null
 			set_git "$old_path"
-			[[ -n $(git -C "$INNER_GIT_DIR" status --porcelain "$old_path") ]] && git_commit "Remove ${1}."
+			[[ -n $(exec_git_command status --porcelain "$old_path") ]] && git_commit "Remove ${1}."
 		fi
 		rmdir -p "$old_dir" 2>/dev/null
 	else
@@ -650,17 +677,17 @@ cmd_git() {
 	set_git "$PREFIX/"
 	if [[ $1 == "init" ]]; then
 		INNER_GIT_DIR="$PREFIX"
-		git -C "$INNER_GIT_DIR" "$@" || exit 1
+		exec_git_command "$@" || exit 1
 		git_add_file "$PREFIX" "Add current contents of password store."
 
 		echo '*.gpg diff=gpg' > "$PREFIX/.gitattributes"
 		git_add_file .gitattributes "Configure git repository for gpg file diff."
-		git -C "$INNER_GIT_DIR" config --local diff.gpg.binary true
-		git -C "$INNER_GIT_DIR" config --local diff.gpg.textconv "$GPG -d ${GPG_OPTS[*]}"
+		exec_git_command config --local diff.gpg.binary true
+		exec_git_command config --local diff.gpg.textconv "$GPG -d ${GPG_OPTS[*]}"
 	elif [[ -n $INNER_GIT_DIR ]]; then
 		tmpdir nowarn #Defines $SECURE_TMPDIR. We don't warn, because at most, this only copies encrypted files.
 		export TMPDIR="$SECURE_TMPDIR"
-		git -C "$INNER_GIT_DIR" "$@"
+		exec_git_command "$@"
 	else
 		die "Error: the password store is not a git repository. Try \"$PROGRAM git init\"."
 	fi
diff --git a/tests/t0300-reencryption.sh b/tests/t0300-reencryption.sh
index 3c88987..72029db 100755
--- a/tests/t0300-reencryption.sh
+++ b/tests/t0300-reencryption.sh
@@ -87,14 +87,14 @@ test_expect_success 'Reencryption subfolder multiple keys, move, deinit' '
 	[[ $(canonicalize_gpg_keys $KEY3 $KEY4 $KEY2) == "$(gpg_keys_from_encrypted_file "$PASSWORD_STORE_DIR/anotherfolder2/anotherfolder/cred1.gpg")" ]]
 '
 
-test_expect_success 'Reencryption skips links' '
-	ln -s "$PASSWORD_STORE_DIR/folder/cred1.gpg" "$PASSWORD_STORE_DIR/folder/linked_cred.gpg" &&
-	[[ -L $PASSWORD_STORE_DIR/folder/linked_cred.gpg ]] &&
-	git add "$PASSWORD_STORE_DIR/folder/linked_cred.gpg" &&
-	git commit "$PASSWORD_STORE_DIR/folder/linked_cred.gpg" -m "Added linked cred" &&
-	"$PASS" init -p folder $KEY3 &&
-	[[ -L $PASSWORD_STORE_DIR/folder/linked_cred.gpg ]]
-'
+ test_expect_success 'Reencryption skips links' '
+ 	ln -s "$PASSWORD_STORE_DIR/folder/cred1.gpg" "$PASSWORD_STORE_DIR/folder/linked_cred.gpg" &&
+ 	[[ -L $PASSWORD_STORE_DIR/folder/linked_cred.gpg ]] &&
+ 	git add "folder/linked_cred.gpg" &&
+ 	git commit -m "Added linked cred" &&
+ 	"$PASS" init -p folder $KEY3 &&
+ 	[[ -L $PASSWORD_STORE_DIR/folder/linked_cred.gpg ]]
+ '
 
 #TODO: test with more varieties of move and copy!
 
diff --git a/tests/t0500-find.sh b/tests/t0500-find.sh
index 3cf6815..936c115 100755
--- a/tests/t0500-find.sh
+++ b/tests/t0500-find.sh
@@ -1,18 +1,34 @@
 #!/usr/bin/env bash
 
-test_description='Find check'
-cd "$(dirname "$0")"
-. ./setup.sh
 
-test_expect_success 'Make sure find resolves correct files' '
-	"$PASS" init $KEY1 &&
-	"$PASS" generate Something/neat 19 &&
-	"$PASS" generate Anotherthing/okay 38 &&
-	"$PASS" generate Fish 12 &&
-	"$PASS" generate Fishthings 122 &&
-	"$PASS" generate Fishies/stuff 21 &&
-	"$PASS" generate Fishies/otherstuff 1234 &&
-	[[ $("$PASS" find fish | sed "s/^[ \`|-]*//g;s/$(printf \\x1b)\\[[0-9;]*[a-zA-Z]//g" | tr "\\n" -) == "Search Terms: fish-Fish-Fishies-otherstuff-stuff-Fishthings-" ]]
-'
+    test_description='Find check'
+    cd "$(dirname "$0")"
+    . ./setup.sh
+
+if tree --help 2>&1 | grep -q -e --ignore-case -e --matchdirs; then
+        test_expect_success 'Make sure find resolves correct files' '
+            "$PASS" init $KEY1 &&
+            "$PASS" generate Something/neat 19 &&
+            "$PASS" generate Anotherthing/okay 38 &&
+            "$PASS" generate Fish 12 &&
+            "$PASS" generate Fishthings 122 &&
+            "$PASS" generate Fishies/stuff 21 &&
+            "$PASS" generate Fishies/otherstuff 1234 &&
+            [[ $("$PASS" find fish | sed "s/^[ \`|-]*//g;s/$(printf \\x1b)\\[[0-9;]*[a-zA-Z]//g" | tr "\\n" -) == "Search Terms: fish-Fish-Fishies-otherstuff-stuff-Fishthings-" ]]
+        '
+else
+    # Redirect stderr to /dev/null to drop the warning message
+        test_expect_success 'Make sure find resolves correct files' '
+            "$PASS" init $KEY1 &&
+            "$PASS" generate Something/neat 19 &&
+            "$PASS" generate Anotherthing/okay 38 &&
+            "$PASS" generate Fish 12 &&
+            "$PASS" generate myfish 12 &&
+            "$PASS" generate Fishthings 122 &&
+            "$PASS" generate Fishies/stuff 21 &&
+            "$PASS" generate Fishies/otherstuff 1234 &&
+            [[ $("$PASS" find fish 2>/dev/null | sed "s/^[ \`|-]*//g;s/$(printf \\x1b)\\[[0-9;]*[a-zA-Z]//g" | tr "\\n" -) == "Search Terms: fish-myfish-" ]]
+        '
+fi
 
 test_done
-- 
2.20.1

