#! /bin/bash -

# H.A.Trujillo, 7 Jan 15
#
# Last Change:     12 Jun 2019
# Version: 1.5.4c

# Adjust as needed  
BakVol=MyDrive_bak
FYname="$(hostname -s)"		# eg: fy10123a

esc=$'\e['
ul="${esc}4m"
pl="${esc}m"


case "$1" in 
    -p)
	PrintPath=1
	shift
	;;

    -h|"")
	scriptname=`basename $0`
	sn_ul="${ul}${scriptname}${pl}"
	tick=$'\x60'	# backtick  ( ` )
	cat <<- EOT

	  ${esc}32m$scriptname:$pl
	              Searches for unique versions of a given file on a
	              Time-Machine backup drive.
	      
	      $sn_ul searches through a Time-Machine backup for versions of the
	      file specified on the command line, and prints the date of each
	      version.  Once the user selects a backup date, the path to that
	      version of file is copied into the clipboard so the file may be
	      inspected.  After the desired version is found, the folder
	      containing the file may be opened, allowing the file to be dragged
	      where desired.

	      Wildcards may be specified as "*".

	  Usage:
	      $scriptname  ${ul}path${pl}       prints a menu containing versions of the file
	      $scriptname  -p ${ul}path${pl}    prints a wildcard-containing path to all matches
	      $scriptname  -l         use MobileBackups volume (local)
	      $scriptname  -h         print this message

	              ( ${ul}path${pl} = path to current version of the file)

	  Caveats:
	      ${ul}\$BakVol${pl} will need to be set for a given backup partition.

	      Using ${ul}${tick}pbpaste${tick}${pl} after a csh prompt sometimes glitches, but it works 
	      fine in bash.
	  
	EOT
	exit 1
	;;
esac

#   PrintPath():  create a grep-able path	#{{{
#   Potential bugs:	
#	  - if a file is not in "Latest" directory, it won't show up.
#	  - if the HD name ($HDname) has changed, only hits to the most
#	    recent will show up.
PrintPath() {
    if [ $PrintPath ] ; then
	if [ -e $BakPath1/Latest/$BakPath2 ] 
	then
	    if [ -d $file ] ; then ls_opt="-d" ; fi
	    echo "copying into clipboard:"
	    printf "   "
	    ls $ls_opt $BakPath1/Latest/$BakPath2 \
		| sed 's,/Latest/,/*/,' \
		| tee /dev/stderr | pbcopy
	    stat=0
	else
	    echo "  backup for file ${ul}${BakPath2:2}${pl} doesn't exist"
	    stat=1
	fi
	exit $stat
    fi
} #}}}

if ! ls -d /Volumes/$BakVol >/dev/null 2>&1 ; then
    echo "    /Volumes/$BakVol not mounted.  Exiting."
    exit 1
fi

file="$1"
# This may not be constant on backup-drive, so use wildcard instead:
# HDname=$(ls -l /Volumes | grep '> /$' | tr -s " " | cut -f9 -d" ")
HDname='*'			# eg: Macintosh HD, ...

if [ ! -e $file ] ; then
    printf "  file ${ul}${file}${pl} doesn't exist.   Continue? [n]  "
    read
    if [ "$REPLY" != "y" ]; then  
    exit 1
    fi
fi

# Expand relative paths.
# 	gyration through 'cd' to clean up "../foo" syntax
if [[ ! ${file}_ =~ ^/ ]] ; then 
  tmp="`pwd`/$file"
  cd "${tmp%/*}"
  file=`pwd`/${file##*/}
  cd - > /dev/null
fi

# path-to-bakfile = $BakPath1/_backup_branch_/$BakPath2
BakPath1=/Volumes/$BakVol/Backups.backupdb/$FYname
#	wildcard of $HDname doesn't work when $BakPath2 is quoted,
#	so must explicitly write out in places
BakPath2=$HDname/"$file"	

if [ $PrintPath ] ; then PrintPath ; fi

depth=$(echo $BakPath1/x/$BakPath2 | tr " "/ _" " | wc -w)


# generates a list of paths to unique revisions
function awkfilter {
    sort -s -t/ -k$depth \
	| awk '
	   {testdate = $6$7$8
	    if (testdate == lastdate) {next}	# short-circuit if file is unchanged
	    split( $9, dirdate, "/")
	    print dirdate[6]			# else print TM backup-branch
	    lastdate = testdate
	   }'
    }


printf "\n   This may take a minute... \\015"
if [ -d "$file" ] ; then suffix='/*' ; fi
dirlist=( $(ls -l $BakPath1/*/$HDname/"${file}"${suffix}  | awkfilter | sort | uniq) )
if [ "$dirlist" == "" ] ; then exit ; fi


# Now that we know we're actually going to run, ensure that we
# go away after five minutes if user forgets to quit.
{ sleep 300 ; kill $$ 2> /dev/null ; } &
    #	bug: the "sleep" doesn't get killed (but it will timeout by itself)
trap  "kill $!"  EXIT


tput setaf 6	# next line is cyan
    printf "   ‘o’ to open enclosing folder;  ‘q’ to quit\n\n"
tput sgr0

PS3="choose a version: "
select version in ${dirlist[@]%-*} ; do
    case _$REPLY in
	_q) exit	;;	# quit silently
	_o) break	;;	# exit loop and open enclosing directory
	_*)
	    if [ -z $version ] ; then continue  ; fi   # if $REPLY is out of range
    let REPLY--
    echo "$BakPath1/${dirlist[$REPLY]}/$BakPath2" | tr \\n " " | pbcopy
    echo "    path to ${ul}$BakVol/.../${dirlist[$REPLY]}/.../${file##/*/}${pl} pasted into clipboard"
    Reply=$REPLY
	    ;;
    esac
done


# Open the enclosing folder
echo ""

open $BakPath1/${dirlist[$Reply]}/$HDname/"${file%/*}"

exit 0


#______________________________

#Version History:

# v1.2	2015-04-09
#	No longer prints duplicate listings when >1 file matches a wildcard.
# v1.3  2016-06-08
#	-p flag  (output wildcard-containing path to *all* backup files)
# v1.4  2016-06-25
#	Can now also search for directories.  sanity-check for bad filename.
# v1.4a 2016-08-10
# 	Tweaking of help-text
# v1.5	2017-04-04
# 	Perhaps now works when file/directory name contains a space?
# 	Exits gracefully if no matches.
# 1.5.1	2017-06-01
# 	more elegant exit from loop (q/o vs ^C/^D)
# 1.5.3 ??
# 	local backup folder
# 1.5.4	2019-06-08
# 	times out after five minutes


#vim: tw=0 foldmethod=marker
