#!/bin/ksh ###################################################################### # Program: tls - time ls # Purpose: Looks on a backup server and lists versions of files # available. # Arguments: files to list # Author: Perette Barella #--------------------------------------------------------------------- ###################################################################### # Function: usage # Purpose: Displays the usage of this command. # Author: Perette Barella #--------------------------------------------------------------------- function usage { print "Usage: $arg0" exit 1 } ##### End of function usage ##### ###################################################################### # Function: parse_arguments # Purpose: Parses the command line arguments # Arguments: The command line arguments. # Returns: Number of arguments parsed. # Author: Perette Barella #--------------------------------------------------------------------- function parse_arguments { typeset option INTERACTIVE=false while getopts 'i?' option do case "$option" in i) INTERACTIVE=true ;; *) usage ;; esac done typeset status=0 return $((OPTIND - 1)) } ##### End of function parse_arguments ##### ###################################################################### # Function: get_file_path # Purpose: Returns the file path relative to current user's home. # Arguments: $1 - the file to find the path to # Returns: 0. # Author: Perette Barella #--------------------------------------------------------------------- function get_file_path { typeset file="$1" typeset path=$(dirname "$file") typeset fulldir=$(cd "$path"; pwd) typeset filename=$(basename "$file") print -- "$fulldir/$filename" | sed -e "s&$HOME/&&" return 0 } ##### End of function get_file_path ##### ###################################################################### # Function: list_dates # Purpose: Gets available backup dates for a file. # When a file hasn't changed between two or more dates, # only the earliest date is listed. If the file is changed # and subsequently reverts to an earlier version, the # reverting version is listed again. # Arguments: $1 - the file to get data for # Returns: List of dates and server data. # Author: Perette Barella #--------------------------------------------------------------------- function list_dates { typeset status=0 file="$1" typeset cksum1 cksum2 lastcksum1="x" lastcksum2="x" for server in pmb1566@barella.org perette@sue.dnsalias.net do print -n "$server: " 1>&2 ssh -n "$server" "cksum Backups/$USER*/*/$file" | awk "{print \$1 \" \" \$2 \" $server \" \$3}" done | sort -k 4,1 | while read cksum1 cksum2 server filename do if [ "$cksum1" != "$lastcksum1" -o "$cksum2" != "$lastcksum2" ] then date=$(print -- "$filename" | cut -d/ -f3) print "$date $server" lastcksum1="$cksum1" lastcksum2="$cksum2" fi done return $status } ##### End of function list_dates ##### ###################################################################### # Function: present_file_dates # Purpose: List the dates at which a file is available on the # backup server. # Arguments: $1 - the filename # $2 - the file containing backup availability data # Returns: # Author: Perette Barella #--------------------------------------------------------------------- function present_file_dates { typeset status=0 file="$1" datelist="$2" if [ -s "$datelist" ] then print "$file:" awk '{print $1}' < "$datelist" | sed 's/^/ /' if [ -e "$HOME/$file" ] then ls -l "$HOME/$file" else print "No local file." fi else print "$file: No archival files found." status=1 fi return $status } ##### End of function present_file_dates ##### ###################################################################### # Function: retrieve_file # Purpose: # Arguments: # Returns: # Author: Perette Barella #--------------------------------------------------------------------- function retrieve_file { typeset status=0 file="$1" datelist="$2" date="$3" dest="$4" typeset date server [ ${#date} -eq 10 ] && date="$date.0" grep "^$date " "$datelist" | read date server if [ "$date" = "" ] then print "$date: date not available." return 1 fi print -n -- "$server: " scp -p "$server:Backups/$USER*/$date/$file" "$dest" return $? } ##### End of function retrieve_file ##### ###################################################################### # Function: perform_diff # Purpose: Compares a date and local or two dated versions of a file. # Author: Perette Barella #--------------------------------------------------------------------- function perform_diff { typeset status=0 file="$1" datelist="$2" date1="$3" date2="$4" typeset file1=/var/tmp/$arg0.diff.1.$$ typeset file2=/var/tmp/$arg0.diff.2.$$ if [ $# -lt 3 -o $# -gt 4 ] then print "Usage: diff [date]" print "With one date, compares to local version." return 1 fi if ! retrieve_file "$file" "$datelist" "$date1" "$file1" then status=1 elif [ $# -eq 3 ] then cp "$HOME/$file" "$file2" status=$? else retrieve_file "$file" "$datelist" "$date2" "$file2" status=$? fi if [ $status -eq 0 ] then diff "$file1" "$file2" status=$? [ $status -eq 0 ] && echo "Files are the same." fi rm -f "$file1" "$file2" return $status } ##### End of function perform_diff ##### ###################################################################### # Function: perform_vi # Purpose: Gets a file and edits it read-only. # Author: Perette Barella #--------------------------------------------------------------------- function perform_vi { typeset status=0 file="$1" datelist="$2" date="$3" typeset dest=/var/tmp/$arg0.vi.$$ if [ $# -lt 3 -o $# -gt 3 ] then print "Usage: vi " print "Views the specified version of the file (read-only)." return 1 fi if ! retrieve_file "$file" "$datelist" "$date" "$dest" then status=1 else ${EDITOR:-view} "$dest" fi rm -f "$dest" return $status } ##### End of function perform_vi ##### ###################################################################### # Function: perform_get # Purpose: Retrieves a dated file version to a local file. # Author: Perette Barella #--------------------------------------------------------------------- function perform_get { typeset file="$1" datelist="$2" date="$3" dest="$4" if [ $# -lt 3 -o $# -gt 4 ] then print "Usage: get [local file]" print "Copy date to local file. If local file is not specified, overwrites existing." return 1 fi [ $# -eq 3 ] && dest="$HOME/$file" retrieve_file "$file" "$datelist" "$date" "$dest" return $? } ##### End of function perform_get ##### ###################################################################### # Function: do_cli # Purpose: Implements a rudimentary command line for doing stuff # after seeing the available versions of a file. # Author: Perette Barella #--------------------------------------------------------------------- function do_cli { typeset status=0 file="$1" datelist="$2" typeset command="" options while [ "$command" != "quit" ] do print -n "tls $file> " read command options || command="quit" case "$command" in quit) : ;; diff) perform_diff "$file" "$datelist" $options ;; get) perform_get "$file" "$datelist" $options ;; vi|view) perform_vi "$file" "$datelist" $options ;; ls) present_file_dates "$file" "$datelist" ;; !) if [ "$options" = "" ] then ${SHELL:-/bin/sh} else ${SHELL:-/bin/sh} -c "$options" fi ;; *) print -- "$command: Unknown command." ;; esac done return $status } ##### End of function do_cli ##### ##### Start of main ##### arg0=$(basename $0) dir0=$(dirname $0) [ "$dir0" = "." ] && dir0="$(pwd)" BASE=$(dirname $dir0) parse_arguments "$@" shift $? if [ $# -eq 0 ] then set -- * fi filelist="/var/tmp/$arg0.$$.tmp" for file in "$@" do fullfile=$(get_file_path "$file") list_dates "$fullfile" > "$filelist" present_file_dates "$fullfile" "$filelist" $INTERACTIVE && [ -s "$filelist" ] && do_cli "$fullfile" "$filelist" rm "$filelist" done ##### End of main #####