Hello. After two years of using pass and following the mailing list
I've finally found a way to add multi-clip. This is a feature
heavily inspired by Nicolas S. Dade's pwsafe. It started as an idea for
a feature request, then it was a proof of concept, and now, after a few
weeks of poking and prodding, I'm happy to share a full working alt
version of passmenu. I use it every day and I hope you will too.

Thank you milki and Jason and others for your past correspondence helping
to steer me in this direction. [rolling][loops]

First:
If you like the way passmenu currently behaves, then you wont notice
much of a change. In this version, I've preserved all single clip
behavior. 

This patch:
If you add my patch, you will gain the ability to select multiple
passwords and paste them in series. Meaning, after the first paste,
passmenu continues and lines up another. This is useful for pasting
logins and passwords. To do this, in dmenu press Ctrl-Enter instead of
Enter to continue selecting entries. Hit Enter on the last entry. Now
you can paste each entry once.

The magic line:  ## line #73
<code>xclip -l 1 -quiet -sel "$X_SELECTION" </code>
Here xclip waits for one X selection request and prints.
https://github.com/ampling/pass/blob/passmenu_multi-clip/contrib/dmenu/passmenu#L73

Multi-line vs Fist-line storage. 
Yes. I'm aware of both methods of storage. I had both in mind when
writing this. I found it easier to address first-line storage in
this patch, but I have an idea of how to add multi-line clips later.

Pro-tip  ## passmenu --type
I'm really liking xdotool. Try setting a keyboard shortcut for passmenu
--type, then select url, login and pass. Then, paste, paste, paste, enter.
Xdotool gets arround places which mess with the clipboard. I'm looking
at you firefox omnibar. Try it. It's simply the most constantly low
hassle authentication process I've yet tried.

What I've learned:
It's a bad idea for a pass or passmenu process to overlap with another
process of pass or passmenu. I needed a file lock for the clipboard. I
like flock as a file lock, but it wasn't default on all platforms. With
portability in mind, I went with mktemp -d.

I've done my best to go with sane defaults here. If you feel there's a
better way, please let me know.

In addition my the diff bellow, you can find my patch with full history
attached and on github.
https://github.com/ampling/pass/blob/passmenu_multi-clip/contrib/dmenu/passmenu


diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 7a9c517..68b4150 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -1,25 +1,98 @@
 #!/usr/bin/env bash
 
-shopt -s nullglob globstar
+shopt -s nullglob globstar failglob
+set -euo pipefail
 
+tool=${1:-} 
 typeit=0
-if [[ $1 == "--type" ]]; then
-       typeit=1
-       shift
+if [[ $tool == "--type" ]]; then
+  typeit=1
+  shift
 fi
 
-prefix=${PASSWORD_STORE_DIR-~/.password-store}
+X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
+CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}"
+prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
+
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
+password=( ${password:-""} )
+before=( "${before:-}" )
+userID="$(id -u $(whoami))"
+stalelock=( "${stalelock:-""}" )
+stalelock=( "$(find '/tmp' -maxdepth 1 -name "passmenulock."$userID".*" -user 
$(whoami) -print0  -quit)" ) &&
+cleanup=True || { true; cleanup=False; }
+
+_finish () {
+  [[ -n $before ]] &&
+  printf "$before" | base64 -d | xclip -sel "$X_SELECTION" -i
+  [[ True = "$cleanup" ]] &&
+  if compgen -G "/tmp/passmenulock.1000*" >/dev/null 2>&1 ;then
+    rmdir /tmp/passmenulock."$userID".* >/dev/null 2>&1
+  fi
+  exit
+}
+trap _finish EXIT
 
-password=$(printf '%s\n' "${password_files[@]}" | dmenu "$@")
+## Clearing stale Xclip loops avoids a posible race condition.
+if test -n "$stalelock" ;then
+  report=( "$(ps -u $(id -u $(whoami)) aux | grep "bash" | 
+  grep "passmenu" | grep -v "$$")" )
+  stalePID=( "$(printf $stalelock | 
+  sed -e "s/\/tmp\/passmenulock\.[0-9]\{1,6\}\?\..*\.//g")" )
+  if [[ "$report" == *"$stalePID"* ]] ;then
+    kill "$stalePID" || exit 1
+  else
+    rmdir /tmp/passmenulock.$userID.* >/dev/null 2>&1 || exit 1
+  fi
+fi
+
+## dmenu exits on KeyPress not KeyRelease.
+# It would be nice to send KeyRelease event to some dummy window.
+# psydocode: xdotool getwindowfocus; create dummy window;
+# exec dmenu; close dummy window; restore focus.
+for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do
+  passel+=("$password")
+done
 
 [[ -n $password ]] || exit
+# It would be nice to first somehow test if string exists.
+before="$(xclip -sel "$X_SELECTION" -o 2>/dev/null | base64)" || true
+
+umask 077
+( mktemp -d "/tmp/passmenulock."$userID".XXXXXXXXXX"."$$" >/dev/null 2>&1 && 
cleanup=True ||
+  { echo >&2 ":: Unable to make a filelock."; exit 1; } )
 
 if [[ $typeit -eq 0 ]]; then
-       pass show -c "$password" 2>/dev/null
+  if [ ${#passel[@]} -gt "1" ]; then
+    round=0
+    for entry in "${passel[@]}"; do
+      printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
+      pass show "$entry" | sed '1!d' | tr -d '\n' |
+      xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1
+      round=`expr $round + 1`
+    done
+  else
+    printf '%s\n' "Sending "$password" via "$X_SELECTION""
+    pass show "$password" | sed '1!d' | tr -d '\n' |
+    xclip -sel "$X_SELECTION" -i && sleep "$CLIP_TIME" 
+  fi
 else
-       pass show "$password" | { read -r pass; printf %s "$pass"; } |
-               xdotool type --clearmodifiers --file -
+  command -v xdotool >/dev/null 2>&1 || 
+  { echo >&2 "e: cannot find xdotool."; exit 1; }
+  if [ ${#passel[@]} -gt "1" ]; then
+    round=0
+    for entry in "${passel[@]}"; do
+      printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
+      printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1
+      pass show "$entry" | sed '1!d' | tr -d '\n' | 
+      xdotool type --clearmodifiers --file -
+      round=`expr $round + 1`
+    done
+  else
+    pass show "$password" | sed '1!d' | tr -d '\n' |
+    xdotool type --clearmodifiers --file -
+  fi
 fi
+exit



-- 
At your service,
mitfree
http://ampling.com

From 1d2e5d9d0b3e46e1ce72e4151193eb648d668176 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Fri, 8 Apr 2016 23:38:57 -0400
Subject: [PATCH 01/11] Adding xclip loop to select multiple pass entries.

---
 contrib/dmenu/passmenu | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 7a9c517..b80399c 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -4,22 +4,27 @@ shopt -s nullglob globstar
 
 typeit=0
 if [[ $1 == "--type" ]]; then
-	typeit=1
-	shift
+    typeit=1
+    shift
 fi
 
+X_SELECTION="$PASSWORD_STORE_X_SELECTION"
 prefix=${PASSWORD_STORE_DIR-~/.password-store}
+
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
-password=$(printf '%s\n' "${password_files[@]}" | dmenu "$@")
+if [[ $typeit -eq 0 ]]; then
+    for password in $(printf '%s\n' "${password_files[@]}" | 
+	dmenu -f "$@"); do
+	pass show $password | 
+	    xclip -l 1 -quiet -sel $X_SELECTION
+    done
 
 [[ -n $password ]] || exit
 
-if [[ $typeit -eq 0 ]]; then
-	pass show -c "$password" 2>/dev/null
 else
-	pass show "$password" | { read -r pass; printf %s "$pass"; } |
-		xdotool type --clearmodifiers --file -
+    pass show "$password" | { read -r pass; printf %s "$pass"; } |
+	xdotool type --clearmodifiers --file -
 fi
-- 
2.8.0


From 4b003d9bbd9b79d71250aeee029468806bf16e09 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Sat, 9 Apr 2016 00:46:26 -0400
Subject: [PATCH 02/11] Add condition to skip xclip loop on 1 selection.

---
 contrib/dmenu/passmenu | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index b80399c..ddfb07f 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -17,13 +17,20 @@ password_files=( "${password_files[@]%.gpg}" )
 
 if [[ $typeit -eq 0 ]]; then
     for password in $(printf '%s\n' "${password_files[@]}" | 
-	dmenu -f "$@"); do
-	pass show $password | 
-	    xclip -l 1 -quiet -sel $X_SELECTION
+	dmenu -f "$@");  do
+	lot+=($password)
     done
 
-[[ -n $password ]] || exit
+    [[ -n $password ]] || exit
 
+    if [ ${#lot[@]} -gt "0" ]; then
+	for entry in "${lot[@]}";  do
+	      pass show $entry | awk 'NR==1' | 
+	      xclip -l 1 -quiet -sel $X_SELECTION
+	done
+    else
+	pass show -c "$password" 2>/dev/null
+    fi
 else
     pass show "$password" | { read -r pass; printf %s "$pass"; } |
 	xdotool type --clearmodifiers --file -
-- 
2.8.0


From 1e52e9fc165961a94bc834d5fd58ee2669eaae1d Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Thu, 14 Apr 2016 02:03:17 -0400
Subject: [PATCH 03/11] Restores clipboard to previous state.

---
 contrib/dmenu/passmenu | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index ddfb07f..789d0de 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -15,22 +15,24 @@ password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
-if [[ $typeit -eq 0 ]]; then
-    for password in $(printf '%s\n' "${password_files[@]}" | 
-	dmenu -f "$@");  do
+for password in $(printf '%s\n' "${password_files[@]}" | 
+    dmenu -f "$@"); do
 	lot+=($password)
-    done
+done
 
-    [[ -n $password ]] || exit
+[[ -n $password ]] || exit
 
-    if [ ${#lot[@]} -gt "0" ]; then
-	for entry in "${lot[@]}";  do
-	      pass show $entry | awk 'NR==1' | 
-	      xclip -l 1 -quiet -sel $X_SELECTION
+if [[ $typeit -eq 0 ]]; then
+    xclip -sel clip -o | xclip -sel sec -i
+    if [ ${#lot[@]} -gt "1" ]; then
+	for entry in "${lot[@]}"; do
+	    pass show $entry | awk 'NR==1' | 
+	    xclip -l 1 -quiet -sel $X_SELECTION &>/dev/null
 	done
+    xclip -sel sec -o | xclip -sel clip -i
     else
-	pass show -c "$password" 2>/dev/null
-    fi
+	pass show -c "$password" &>/dev/null
+    fi 
 else
     pass show "$password" | { read -r pass; printf %s "$pass"; } |
 	xdotool type --clearmodifiers --file -
-- 
2.8.0


From 24d3194a119c4503d471d09159d11f3d4c12eda3 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Fri, 22 Apr 2016 14:44:51 -0400
Subject: [PATCH 04/11] Clears secondary clipboard after use.

---
 contrib/dmenu/passmenu | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 789d0de..84b4ae4 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -8,13 +8,14 @@ if [[ $1 == "--type" ]]; then
     shift
 fi
 
-X_SELECTION="$PASSWORD_STORE_X_SELECTION"
-prefix=${PASSWORD_STORE_DIR-~/.password-store}
+X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
+prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
 
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
+## Press Ctrl-Return to continue selecting entries.
 for password in $(printf '%s\n' "${password_files[@]}" | 
     dmenu -f "$@"); do
 	lot+=($password)
@@ -23,15 +24,19 @@ done
 [[ -n $password ]] || exit
 
 if [[ $typeit -eq 0 ]]; then
-    xclip -sel clip -o | xclip -sel sec -i
+    xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
     if [ ${#lot[@]} -gt "1" ]; then
+        x=0
 	for entry in "${lot[@]}"; do
-	    pass show $entry | awk 'NR==1' | 
-	    xclip -l 1 -quiet -sel $X_SELECTION &>/dev/null
+	    printf '%s\n' "Sending "${lot[$x]}" via "$X_SELECTION""
+	    pass show $entry | head -n 1 | tr -d '\n' |
+	    xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null
+	    x=`expr $x + 1`
 	done
-    xclip -sel sec -o | xclip -sel clip -i
+    xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
+    printf "" | xclip -sel sec -i 
     else
-	pass show -c "$password" &>/dev/null
+	pass -c "$password" 2>/dev/null
     fi 
 else
     pass show "$password" | { read -r pass; printf %s "$pass"; } |
-- 
2.8.0


From 8e25eeeab68810ec295fb5d8cb2b4412bcd529f2 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Sat, 23 Apr 2016 02:36:55 -0400
Subject: [PATCH 05/11] Limits use of secondary clip to multi selections.

---
 contrib/dmenu/passmenu | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 84b4ae4..a784481 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -24,17 +24,17 @@ done
 [[ -n $password ]] || exit
 
 if [[ $typeit -eq 0 ]]; then
-    xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
     if [ ${#lot[@]} -gt "1" ]; then
         x=0
+	xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
 	for entry in "${lot[@]}"; do
 	    printf '%s\n' "Sending "${lot[$x]}" via "$X_SELECTION""
 	    pass show $entry | head -n 1 | tr -d '\n' |
 	    xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null
 	    x=`expr $x + 1`
 	done
-    xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
-    printf "" | xclip -sel sec -i 
+	xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
+	printf "" | xclip -sel sec -i
     else
 	pass -c "$password" 2>/dev/null
     fi 
-- 
2.8.0


From 20d3a8a8c55dfb31fa9cdc191b156f7d98ac3a53 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Sat, 23 Apr 2016 17:12:29 -0400
Subject: [PATCH 06/11] Changes variables and comments for readability.

---
 contrib/dmenu/passmenu | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index a784481..45eb66f 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -8,30 +8,31 @@ if [[ $1 == "--type" ]]; then
     shift
 fi
 
-X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
+X_SELECTION="${PASSWORD_STORE_X_SELECTION:-primary}"
 prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
 
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
-## Press Ctrl-Return to continue selecting entries.
+## dmenu exits on KeyPress.  
+## We should send KeyRelease event to some dummy window.
 for password in $(printf '%s\n' "${password_files[@]}" | 
     dmenu -f "$@"); do
-	lot+=($password)
+	passel+=($password)
 done
 
 [[ -n $password ]] || exit
 
 if [[ $typeit -eq 0 ]]; then
-    if [ ${#lot[@]} -gt "1" ]; then
-        x=0
+    if [ ${#passel[@]} -gt "1" ]; then
+        round=0
 	xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
-	for entry in "${lot[@]}"; do
-	    printf '%s\n' "Sending "${lot[$x]}" via "$X_SELECTION""
+	for entry in "${passel[@]}"; do
+	    printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
 	    pass show $entry | head -n 1 | tr -d '\n' |
 	    xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null
-	    x=`expr $x + 1`
+	    round=`expr $round + 1`
 	done
 	xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
 	printf "" | xclip -sel sec -i
-- 
2.8.0


From 499a2b10739514860744c0179f00a551bd34cd12 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Sat, 23 Apr 2016 20:22:08 -0400
Subject: [PATCH 07/11] Adding multi password selection to option --type.

---
 contrib/dmenu/passmenu | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 45eb66f..1b0339f 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -40,6 +40,20 @@ if [[ $typeit -eq 0 ]]; then
 	pass -c "$password" 2>/dev/null
     fi 
 else
-    pass show "$password" | { read -r pass; printf %s "$pass"; } |
-	xdotool type --clearmodifiers --file -
+    if [ ${#passel[@]} -gt "1" ]; then
+        round=0
+	xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
+	for entry in "${passel[@]}"; do
+	    printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
+	    printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null
+	    pass show "$entry" | { read -r pass; printf %s "$pass"; } |
+		xdotool type --clearmodifiers --file -
+	    round=`expr $round + 1`
+	done
+	xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
+	printf "" | xclip -sel sec -i
+    else
+	pass show "$password" | { read -r pass; printf %s "$pass"; } |
+	    xdotool type --clearmodifiers --file -
+    fi
 fi
-- 
2.8.0


From 67dacee2237bf12dfa0f6e14806971ad2daab7ea Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Wed, 27 Apr 2016 20:29:07 -0400
Subject: [PATCH 08/11] Locks script and attemps to clear old loop.

---
 contrib/dmenu/passmenu | 91 ++++++++++++++++++++++++++++----------------------
 1 file changed, 52 insertions(+), 39 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 1b0339f..e842f13 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -4,56 +4,69 @@ shopt -s nullglob globstar
 
 typeit=0
 if [[ $1 == "--type" ]]; then
-    typeit=1
-    shift
+  typeit=1
+  shift
 fi
 
-X_SELECTION="${PASSWORD_STORE_X_SELECTION:-primary}"
+X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
 prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
 
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
-## dmenu exits on KeyPress.  
-## We should send KeyRelease event to some dummy window.
-for password in $(printf '%s\n' "${password_files[@]}" | 
-    dmenu -f "$@"); do
-	passel+=($password)
+## Preexisting xclip loops can cause unanticipated behavior.
+exec {lock_fd}>/tmp/passmenulock || exit 1
+flock -n "$lock_fd" || 
+  { xclip -sel "$X_SELECTION" -o >/dev/null 2>&1 
+    flock -n "$lock_fd" ||
+    printf '%s\n' 'Already running' >&2
+    exit 1
+  }
+
+## dmenu exits on KeyPress not KeyRelease.
+## It would be nice to send KeyRelease event to some dummy window.
+for password in $(printf '%s\n' "${password_files[@]}" |
+  dmenu -f "$@"); do
+  passel+=($password)
 done
 
 [[ -n $password ]] || exit
-
+ 
 if [[ $typeit -eq 0 ]]; then
-    if [ ${#passel[@]} -gt "1" ]; then
-        round=0
-	xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
-	for entry in "${passel[@]}"; do
-	    printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
-	    pass show $entry | head -n 1 | tr -d '\n' |
-	    xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null
-	    round=`expr $round + 1`
-	done
-	xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
-	printf "" | xclip -sel sec -i
-    else
-	pass -c "$password" 2>/dev/null
-    fi 
+  if [ ${#passel[@]} -gt "1" ]; then
+    xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
+    round=0
+    for entry in "${passel[@]}"; do
+      printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
+      pass show $entry | sed '1!d' | tr -d '\n' |
+      xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1
+      ~/bin/beeponce
+      round=`expr $round + 1`
+    done
+    xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
+  else
+    pass -c "$password" 2>/dev/null
+  fi
 else
-    if [ ${#passel[@]} -gt "1" ]; then
-        round=0
-	xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
-	for entry in "${passel[@]}"; do
-	    printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
-	    printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null
-	    pass show "$entry" | { read -r pass; printf %s "$pass"; } |
-		xdotool type --clearmodifiers --file -
-	    round=`expr $round + 1`
-	done
-	xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
-	printf "" | xclip -sel sec -i
-    else
-	pass show "$password" | { read -r pass; printf %s "$pass"; } |
-	    xdotool type --clearmodifiers --file -
-    fi
+  command -v xdotool >/dev/null 2>&1 ||
+  { echo >&2 "e: cannot find xdotool."; exit 1; }
+  if [ ${#passel[@]} -gt "1" ]; then
+    xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
+    round=0
+    for entry in "${passel[@]}"; do
+      printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
+      printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1
+      ~/bin/beeponce
+      pass show "$entry" | sed '1!d' | tr -d '\n' | 
+      xdotool type --clearmodifiers --file -
+      round=`expr $round + 1`
+    done
+    xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
+  else
+    pass show "$password" | sed '1!d' | tr -d '\n' |
+    xdotool type --clearmodifiers --file -
+  fi
 fi
+printf "" | xclip -sel sec -i
+flock -u "$lock_fd"
-- 
2.8.0


From 5cee86eb671b0db6229bcb490ca1762ae2eb8d76 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Sun, 1 May 2016 17:49:36 -0400
Subject: [PATCH 09/11] A portable process lock avoids unanticipated behavior.

---
 contrib/dmenu/passmenu | 68 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 49 insertions(+), 19 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index e842f13..021c48f 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -1,6 +1,7 @@
 #!/usr/bin/env bash
 
 shopt -s nullglob globstar
+set -o errexit
 
 typeit=0
 if [[ $1 == "--type" ]]; then
@@ -9,64 +10,93 @@ if [[ $1 == "--type" ]]; then
 fi
 
 X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
+CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}"
 prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
 
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
-## Preexisting xclip loops can cause unanticipated behavior.
-exec {lock_fd}>/tmp/passmenulock || exit 1
-flock -n "$lock_fd" || 
-  { xclip -sel "$X_SELECTION" -o >/dev/null 2>&1 
-    flock -n "$lock_fd" ||
-    printf '%s\n' 'Already running' >&2
-    exit 1
+_finish () {
+  [[ 1 = "$remove" ]]
+    rmdir /tmp/passmenu."$uid".* 2>/dev/null
+    ## kill more child processes here.
+    # echo $(ps -o pgid=$$ | grep -o [0-9]*)
+    printf "$buffer" | xclip -sel "$X_SELECTION" -i
+  exit
   }
+trap _finish EXIT
+
+_fire () {
+  stale="$(find /tmp/passmenu."$uid".* -print -quit | 
+  sed -e "s/\/tmp\/passmenu\.[0-9]\{1,6\}\?\..*\.//g")"
+  report="$(ps -u $(id -u $(whoami)) aux | grep "bash" | 
+  grep "passmenu" | grep -v "$$")"
+  if [[ "$report" == *"passmenu"* ]] ;then 
+    kill "$stale" && rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1
+  else
+    rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1
+  fi
+}
+
+## Clearing stale Xclip loops avoids a posible race condition.
+uid="$(id -u $(whoami))"
+remove=0
+if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then
+  question=$(printf 'yes\nno'|
+  dmenu -nf red -f -p "The clipboard is locked by another process. Kill it with fire?")
+  if [ "$question" = 'yes' ] ;then
+    _fire
+  else
+    exit 0
+  fi
+fi
 
 ## dmenu exits on KeyPress not KeyRelease.
 ## It would be nice to send KeyRelease event to some dummy window.
+## psydocode: xdotool getwindowfocus; create dummy window;
+## exec dmenu; close dummy window; restore focus.
 for password in $(printf '%s\n' "${password_files[@]}" |
   dmenu -f "$@"); do
-  passel+=($password)
+  passel+=("$password")
 done
 
 [[ -n $password ]] || exit
- 
+
+umask 077
+mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 || exit 1  
+remove=1
+
+buffer="$(xclip -sel "$X_SELECTION" -o)"
 if [[ $typeit -eq 0 ]]; then
   if [ ${#passel[@]} -gt "1" ]; then
-    xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
     round=0
     for entry in "${passel[@]}"; do
       printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
-      pass show $entry | sed '1!d' | tr -d '\n' |
+      pass show "$entry" | sed '1!d' | tr -d '\n' |
       xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1
-      ~/bin/beeponce
       round=`expr $round + 1`
     done
-    xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
   else
-    pass -c "$password" 2>/dev/null
+    pass show "$password" | sed '1!d' | tr -d '\n' |
+    xclip -sel "$X_SELECTION" -i 
+    sleep "$CLIP_TIME"
   fi
 else
   command -v xdotool >/dev/null 2>&1 ||
   { echo >&2 "e: cannot find xdotool."; exit 1; }
   if [ ${#passel[@]} -gt "1" ]; then
-    xclip -sel "$X_SELECTION" -o | xclip -sel sec -i
     round=0
     for entry in "${passel[@]}"; do
       printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION""
       printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1
-      ~/bin/beeponce
       pass show "$entry" | sed '1!d' | tr -d '\n' | 
       xdotool type --clearmodifiers --file -
       round=`expr $round + 1`
     done
-    xclip -sel sec -o | xclip -sel "$X_SELECTION" -i
   else
     pass show "$password" | sed '1!d' | tr -d '\n' |
     xdotool type --clearmodifiers --file -
   fi
 fi
-printf "" | xclip -sel sec -i
-flock -u "$lock_fd"
+exit
-- 
2.8.0


From f8b10ed59edbb293e3728f29fc087ddcf316cc3c Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Fri, 6 May 2016 11:58:34 -0400
Subject: [PATCH 10/11] Adding strict mode.

---
 contrib/dmenu/passmenu | 58 ++++++++++++++++++++++----------------------------
 1 file changed, 26 insertions(+), 32 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 021c48f..e6e2cfc 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -1,10 +1,11 @@
 #!/usr/bin/env bash
 
-shopt -s nullglob globstar
-set -o errexit
+shopt -s nullglob globstar failglob
+set -euo pipefail
 
+tool=${1:-} 
 typeit=0
-if [[ $1 == "--type" ]]; then
+if [[ $tool == "--type" ]]; then
   typeit=1
   shift
 fi
@@ -18,56 +19,49 @@ password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
 
 _finish () {
-  [[ 1 = "$remove" ]]
-    rmdir /tmp/passmenu."$uid".* 2>/dev/null
-    ## kill more child processes here.
-    # echo $(ps -o pgid=$$ | grep -o [0-9]*)
-    printf "$buffer" | xclip -sel "$X_SELECTION" -i
+  [[ -n $before ]] &&
+  printf "$before" | xclip -sel "$X_SELECTION" -i
+  [[ True = "$remove" ]] &&
+  if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then
+    rmdir /tmp/passmenu."$uid".* >/dev/null 2>&1
+  fi
   exit
-  }
+}
 trap _finish EXIT
 
-_fire () {
+## Clearing stale Xclip loops avoids a posible race condition.
+uid="$(id -u $(whoami))"
+remove=False
+if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then
   stale="$(find /tmp/passmenu."$uid".* -print -quit | 
   sed -e "s/\/tmp\/passmenu\.[0-9]\{1,6\}\?\..*\.//g")"
   report="$(ps -u $(id -u $(whoami)) aux | grep "bash" | 
   grep "passmenu" | grep -v "$$")"
-  if [[ "$report" == *"passmenu"* ]] ;then 
+  if [[ "$report" == *"$stale"* ]] ;then 
     kill "$stale" && rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1
   else
     rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1
   fi
-}
-
-## Clearing stale Xclip loops avoids a posible race condition.
-uid="$(id -u $(whoami))"
-remove=0
-if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then
-  question=$(printf 'yes\nno'|
-  dmenu -nf red -f -p "The clipboard is locked by another process. Kill it with fire?")
-  if [ "$question" = 'yes' ] ;then
-    _fire
-  else
-    exit 0
-  fi
 fi
 
 ## dmenu exits on KeyPress not KeyRelease.
 ## It would be nice to send KeyRelease event to some dummy window.
 ## psydocode: xdotool getwindowfocus; create dummy window;
 ## exec dmenu; close dummy window; restore focus.
-for password in $(printf '%s\n' "${password_files[@]}" |
-  dmenu -f "$@"); do
+password=${password:-}
+for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do
   passel+=("$password")
 done
 
+before=${before:-}
 [[ -n $password ]] || exit
+before="$(xclip -sel "$X_SELECTION" -o >/dev/null 2>&1)" || true
 
 umask 077
-mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 || exit 1  
-remove=1
+mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 ||
+{ echo >&2 ":: Unable to make a filelock."; exit 1; }
+remove=True
 
-buffer="$(xclip -sel "$X_SELECTION" -o)"
 if [[ $typeit -eq 0 ]]; then
   if [ ${#passel[@]} -gt "1" ]; then
     round=0
@@ -78,12 +72,12 @@ if [[ $typeit -eq 0 ]]; then
       round=`expr $round + 1`
     done
   else
+    printf '%s\n' "Sending "$password" via "$X_SELECTION""
     pass show "$password" | sed '1!d' | tr -d '\n' |
-    xclip -sel "$X_SELECTION" -i 
-    sleep "$CLIP_TIME"
+    xclip -sel "$X_SELECTION" -i && sleep "$CLIP_TIME" 
   fi
 else
-  command -v xdotool >/dev/null 2>&1 ||
+  command -v xdotool >/dev/null 2>&1 || 
   { echo >&2 "e: cannot find xdotool."; exit 1; }
   if [ ${#passel[@]} -gt "1" ]; then
     round=0
-- 
2.8.0


From 90a3613d854085e1fe1416714729aa24e2013cf3 Mon Sep 17 00:00:00 2001
From: ampling <[email protected]>
Date: Sat, 7 May 2016 06:14:50 -0400
Subject: [PATCH 11/11] Improved filelock, and more appropriate variable names.

---
 contrib/dmenu/passmenu | 48 +++++++++++++++++++++++++-----------------------
 1 file changed, 25 insertions(+), 23 deletions(-)

diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index e6e2cfc..68b4150 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -17,50 +17,52 @@ prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
 password_files=( "$prefix"/**/*.gpg )
 password_files=( "${password_files[@]#"$prefix"/}" )
 password_files=( "${password_files[@]%.gpg}" )
+password=( ${password:-""} )
+before=( "${before:-}" )
+userID="$(id -u $(whoami))"
+stalelock=( "${stalelock:-""}" )
+stalelock=( "$(find '/tmp' -maxdepth 1 -name "passmenulock."$userID".*" -user $(whoami) -print0  -quit)" ) &&
+cleanup=True || { true; cleanup=False; }
 
 _finish () {
   [[ -n $before ]] &&
-  printf "$before" | xclip -sel "$X_SELECTION" -i
-  [[ True = "$remove" ]] &&
-  if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then
-    rmdir /tmp/passmenu."$uid".* >/dev/null 2>&1
+  printf "$before" | base64 -d | xclip -sel "$X_SELECTION" -i
+  [[ True = "$cleanup" ]] &&
+  if compgen -G "/tmp/passmenulock.1000*" >/dev/null 2>&1 ;then
+    rmdir /tmp/passmenulock."$userID".* >/dev/null 2>&1
   fi
   exit
 }
 trap _finish EXIT
 
 ## Clearing stale Xclip loops avoids a posible race condition.
-uid="$(id -u $(whoami))"
-remove=False
-if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then
-  stale="$(find /tmp/passmenu."$uid".* -print -quit | 
-  sed -e "s/\/tmp\/passmenu\.[0-9]\{1,6\}\?\..*\.//g")"
-  report="$(ps -u $(id -u $(whoami)) aux | grep "bash" | 
-  grep "passmenu" | grep -v "$$")"
-  if [[ "$report" == *"$stale"* ]] ;then 
-    kill "$stale" && rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1
+if test -n "$stalelock" ;then
+  report=( "$(ps -u $(id -u $(whoami)) aux | grep "bash" | 
+  grep "passmenu" | grep -v "$$")" )
+  stalePID=( "$(printf $stalelock | 
+  sed -e "s/\/tmp\/passmenulock\.[0-9]\{1,6\}\?\..*\.//g")" )
+  if [[ "$report" == *"$stalePID"* ]] ;then
+    kill "$stalePID" || exit 1
   else
-    rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1
+    rmdir /tmp/passmenulock.$userID.* >/dev/null 2>&1 || exit 1
   fi
 fi
 
 ## dmenu exits on KeyPress not KeyRelease.
-## It would be nice to send KeyRelease event to some dummy window.
-## psydocode: xdotool getwindowfocus; create dummy window;
-## exec dmenu; close dummy window; restore focus.
-password=${password:-}
+# It would be nice to send KeyRelease event to some dummy window.
+# psydocode: xdotool getwindowfocus; create dummy window;
+# exec dmenu; close dummy window; restore focus.
 for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do
   passel+=("$password")
 done
 
-before=${before:-}
 [[ -n $password ]] || exit
-before="$(xclip -sel "$X_SELECTION" -o >/dev/null 2>&1)" || true
+# It would be nice to first somehow test if string exists.
+before="$(xclip -sel "$X_SELECTION" -o 2>/dev/null | base64)" || true
 
 umask 077
-mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 ||
-{ echo >&2 ":: Unable to make a filelock."; exit 1; }
-remove=True
+( mktemp -d "/tmp/passmenulock."$userID".XXXXXXXXXX"."$$" >/dev/null 2>&1 && cleanup=True ||
+  { echo >&2 ":: Unable to make a filelock."; exit 1; } )
 
 if [[ $typeit -eq 0 ]]; then
   if [ ${#passel[@]} -gt "1" ]; then
-- 
2.8.0

Attachment: signature.asc
Description: signature

_______________________________________________
Password-Store mailing list
[email protected]
http://lists.zx2c4.com/mailman/listinfo/password-store

Reply via email to