#!/bin/bash
##########################################################################
##                  mbackup                                             ##
## This shell script makes incremental backups of the MCTDH directory.  ##
## A full copy, created e.g with mdircp, must exist on the backup dir.  ##
## The script uses rsync.                                               ##
## HDM 12/03                                                            ##
##########################################################################

#-----------------------------------------------------------------------
#  Definitions :
#-----------------------------------------------------------------------
host=localhost # This would be better, but password is requested.
host=$HOSTNAME
LANG=en_US
minus40='----------------------------------------'
 star40='****************************************'
echo $minus40$minus40
type rsync > /dev/null  2>&1
if [ "$?" != 0  ] ; then
   echo "To run mbackup you must first install rsync."
   exit
fi

purp1='Purpose : Create incremental backups of the MCTDH directory.'
purp2='A full backup copy must exist on the backup dir. (use mdircp).'
purp3='Backup/Restore dir. If dir is not given, $MCTDH_DIR is assumed.'
usage='Usage   : mbackup [ -d -D -r -R -s -t -h ] [dir]'
help1=' -t txt : Add the text txt to log-file. Text must be quoted.'
help2=' -s     : Show Log file.'
hlp2a=' -S     : Show Log file, but skip list of modified files.'
hlp2b=' -M nam : Mask the name, i.e. store the backup under the name nam.'
help3=' -d num : Delete backup number num.'
help4=' -D num : Delete backup, counting from the end.'
help5='          I.e. -D1 deletes last backup.'
help6=' -r num : Restore backup number num.'
help7=' -R num : Restore backup counting number from the end.'
hlp7a=' -B dir : Restore backup to directory dir. (Requires -r or -R option)'
help8=' -n     : No action, show what would have been transferred.'
hlp8a=' -f     : Force. Ignore "There are no new files to backup!" message.'
help9=' -k     : Keep, do not delete files that do not exist on backup-dir.'
hlp9a='          This option applies only to restore jobs (-r|R).'
helph=' -h     : Print this help text.'

#############################################################################
## Set exclude

excl='--exclude mctdhform.ps          --exclude .nfs*
      --include doc/guide/guide.tex   --include doc/guide/figures/*.ps
      --include doc/examples/*        --include doc/guide/guide.bbl
      --include bin/python/
      --include vcham_inputs/*.log    --include inputs/optcntrl/*.inp.0
      --exclude *.flc                 --exclude install/*.inc.bak
      --exclude mctdhall.prj          --exclude .mctdhall.prcs_aux
      --exclude *mctdhall*            --exclude mctdhall.log
      --exclude .addsurf*             --exclude addsurf.log
      --exclude [a-zA-Z]*.[0-9][0-9]  --exclude [a-zA-Z]*.[0-9]
      --exclude *_bk[0-9]*            --exclude *_md[0-9]*
      --exclude .[a-zA-Z]*            --exclude *.pdf
      --exclude *.ps                  --exclude *.dvi
      --exclude *.log                 --exclude *.aux
      --exclude *.toc                 --exclude *.bbl
      --exclude *.blg                 --exclude *~
      --exclude *.pyc
      --exclude #*#                   --exclude .#*
      --exclude source/*.orig/        --exclude save-*
      --exclude source_*/             --exclude source.*/
      --exclude doc/guide/guid*       --exclude guide.b*
      --exclude guide.lo*             --exclude guide.i*
      --exclude bin/binary/           --exclude CompDir*/
      --exclude object/               --exclude obsolete/
      --exclude source/SFILES         --exclude source/SUBR
      --exclude install/depend*       --exclude paths.inc
      --exclude source/TAGS           --exclude source/tags
      --exclude source/TYPE_DATABASE* --exclude source/**/*.orig
      --exclude install/compile.cnf   --exclude install/platform.cnf
      --exclude install/platform.cnf.csh   --exclude addsurf.prj
      --exclude tags  --exclude Doxyfile   --exclude mctdh8?.kde*
      --exclude install/MCTDH_client  --exclude *_mbk[0-9]*
      --exclude doc/guide/figures/mctdhform.ps
      --exclude doc/examples/.svn/*   --exclude /.svn/*
      --exclude /.svn/                --exclude install/MCTDH_IFORT_*
      --exclude elk_comp/             --exclude install/Makefile_*'

#############################################################################

# --- Check $MCTDH_BACKUP
if [ -z "$MCTDH_BACKUP" ] ; then
   help=1
   echo " You must first set the environment variable MCTDH_BACKUP"
   echo " This is most conveniently done in .mctdhrc"
fi

# --- Find the argument dir (last argument).
it=1
for i in "$@" ; do
   i1=$i
   if [ -z "${it#-[tdDrR]}" ]; then unset i1   # all options with arguments
   elif [ -z "${i##-*}" ];     then unset i1 ; fi
   it=$i
done
dir=${i1:-$MCTDH_DIR}

# --- Set file names and determine the total number of backups.

if [ -z "$dir" ]; then dir=$MCTDH_DIR ; fi
if [ -z "$dir" ] ; then
   echo " MCTDH_DIR is empty!"
   exit
elif [ ! -d "$dir" ]; then
   echo "The directory dir = $dir does not exist!"
   exit
fi
ic=0
oldpwd=`pwd`
cd $dir
dir=`pwd`
dirrst=$dir
mdir=`basename $dir`
bdir=$MCTDH_BACKUP/$mdir
last=${bdir}_last
log=${bdir}_log
nrtot=0
if [ -f $last ]; then
   nrtot=`cat $last | wc -l `
fi

delete='--delete'

# --- This is to check that -d, -D, -r -R have arguments (e.g. -R1).
echo " $@" | grep -i ' -d'  > /dev/null && dlbk=1
echo " $@" | grep -i ' -r'  > /dev/null && restore=1


#-----------------------------------------------------------------------
#  Read the options (if any).
#-----------------------------------------------------------------------

while getopts ":hsSknft:M:d:D:r:R:B:" opt; do
    case $opt in
    h  ) help=1 ;;
    t  ) ic=$(($ic+1))
         txt[ic]="$OPTARG" ;;
    s  ) show=1 ;;
    S  ) show=2 ;;
    M  ) Mdir="$OPTARG"; bdir=$MCTDH_BACKUP/$Mdir
         last=${bdir}_last ; log=${bdir}_log ; nrtot=0
         if [ ! -f $last ]; then echo "$last does not exist!"; exit; fi
         nrtot=`cat $last | wc -l` ;;
    d  ) rmbk=1
         unset ok
         echo $OPTARG | egrep '^[0-9]*$' &> /dev/null  && ok=1
         if [ -z "$OPTARG" ] || [ -z "$ok" ] ; then
            echo " A positive integer must follow -d, not: $OPTARG "
            exit
         fi
         if [ -n "$ndel" ] ; then
            echo " No more than one -d or -D option allowed."
            exit
         else
            ndel=$OPTARG
         fi ;;
    D  ) rmbk=1
         unset ok
         echo $OPTARG | egrep '^[0-9]*$' &> /dev/null && ok=1
         if [ -z "$OPTARG" ] || [ -z "$ok" ] ; then
            echo " A positive integer must follow -D, not: $OPTARG "
            exit
         fi
         if [ -n "$ndel" ] ; then
            echo " No more than one -d or -D option allowed."
            exit
         else
            ndel=$(($nrtot+1-$OPTARG))
         fi ;;
    r  ) rst=1
         unset ok
         echo $OPTARG | egrep '^[0-9]*$' &> /dev/null && ok=1
         if [ -z "$OPTARG" ] || [ -z "$ok" ] ; then
            echo " A positive integer must follow -r, not: $OPTARG "
            exit
         fi
         if [ -n "$nrst" ] ; then
            echo " No more than one -r or -R option allowed."
            exit
         else
            nrst=$OPTARG
         fi ;;
    R  ) rst=1
         unset ok
         echo $OPTARG | egrep '^[0-9]*$' &> /dev/null && ok=1
         if [ -z "$OPTARG" ] || [ -z "$ok" ] ; then
            echo " A positive integer must follow -R, not: $OPTARG "
            exit
         fi
         if [ -n "$nrst" ] ; then
            echo " No more than one -r or -R option allowed."
            exit
         else
            nrst=$(($nrtot+1-$OPTARG))
         fi ;;
     B ) dirrst=$OPTARG ;;
     n ) noaction='-n' ;;
     f ) force=1 ;;
     k ) unset delete ;;
    \? ) echo ' Unkown option!'
         echo $usage
         echo ' -h provides a help text.'
         exit 1 ;;
  esac
done
shift $(($OPTIND - 1))
cd $oldpwd
nrtot=`echo $nrtot`   # removing blanks

if [ -n "$help" ] ; then
    echo "$purp1"
    echo "           $purp2"
    echo "           $purp3"
    echo "$usage"
    echo "$help1"
    echo "$help2"
    echo "$hlp2a"
    echo "$hlp2b"
    echo "$help3"
    echo "$help4"$help5
    echo "$help6"
    echo "$help7"
    echo "$hlp7a"
    echo "$help8"
    echo "$hlp8a"
    echo "$help9"
    echo "$hlp9a"
    echo "$helph"
    echo ' '
    echo 'Do not put a blank between option-symbol and option-argument.'
    echo 'E.g. use "-R2 -B/work/test" rather than "-R 2 -B /work/test".'
    echo 'For restoring single files of group of files use mdircp.'
    echo ' '
    echo "  The MCTDH_DIR   is: $dir"
    echo "  The full backup is: $bdir"
    if [ -f $last ] ; then
      echo -n "  The last backup is: "; tail -n 1 $last
      echo "  This last backup the ${nrtot}-th backup."
    fi
    echo $minus40$minus40
    exit
fi

echo -n "--- Start mbackup ---  "; date
echo ' '

#-- Check for correct input.

if [ -n "$dlbk" ] && [ -z "$rmbk" ]; then
   echo " A number must follow -d or -D option!"
   exit
fi
if [ -n "$restore" ] && [ -z "$rst" ]; then
   echo " A number must follow -r or -R option!"
   exit
fi

if [ -n "$rmbk" ]; then
   if [ $ndel -lt 1 ] || [ $ndel -gt $nrtot ]; then
      echo " There is no such backup (-d -D option)"
      echo " Total number of backup files: $nrtot ,  (1...$nrtot)."
      echo " You requested file No. $ndel"
      echo " Log file is: $log"
      echo " I show the tail of it:"
      echo " "
      tail -n 25 ${bdir}_log
      echo " "
      exit
   fi
fi

if [ -n "$rst" ]; then
   if [ $nrst -lt 1 ] || [ $nrst -gt $nrtot ]; then
      echo " There is no such backup (-r -R option)"
      echo " Total number of backup files: $nrtot ,  (1...$nrtot)."
      echo " You requested file No. $nrst"
      echo " Log file is: $log"
      echo " I show the tail of it:"
      echo " "
      tail -n 25 $log
      echo " "
      exit
   fi
fi

#-- Check if bdir  exist.
if [ ! -d $MCTDH_BACKUP ] ; then
   echo "The backup directory $MCTDH_BACKUP does not exist!"
   echo "Check the environment variable MCTDH_BACKUP."
   echo "Check for write permissions."
   exit
fi
if [ -z "$bdir" ] ; then
   echo "The name for the backup dir is empty!"
   exit
elif [ ! -d $bdir ] ; then
   echo "The full backup  $bdir does not exist!"
   echo "Run mdircp to create a full backup."
   exit
fi

#--------------------------------------------------------------------------
# Show Log file. (-s option).
#--------------------------------------------------------------------------

if [ "$show" = 1 ]; then
   less +G $log
   exit
fi
if [ "$show" = 2 ]; then
   cat $log | egrep -v '(/|^ProjectHeader|^changelog)' | less +G
   exit
fi


#--------------------------------------------------------------------------
# Write file info to screen.
#--------------------------------------------------------------------------

cd $MCTDH_BACKUP

# --- Determine $ext, $lastbk, and set mext, bext, bklast, and li1

ext=`date '+%y%b%d_%H.%M.%S' `
if [ -e ${mdir}_$ext ]; then ext=${ext}-$$ ; fi   # for safety
mext=${mdir}_$ext
bext=${bdir}_$ext
lastbk=`tail -n 1 ${mdir}_last `
bklast=$MCTDH_BACKUP/$lastbk
li1="$MCTDH_BACKUP/rsync_$mdir"

# --- Write file names to screen.

echo "  The MCTDH_DIR   is: $dir"
echo "  The full backup is: $bdir"
echo "  The Log file    is: $log"
nn=`echo "$bklast" | wc -c `
if [ $nn -lt 58 ]; then
   echo "  The last backup is: $bklast"
else
   echo "  The last backup is:"
   echo ' ' $bklast
fi
if [ -z "$rmbk$rst" ]; then
   nn=`echo "$bext" | wc -c `
   if [ $nn -lt 58 ]; then
      echo "  The new  backup is: $bext"
   else
      echo "  The new  backup is:"
      echo ' ' $bext
   fi
   nn=`echo "$li1" | wc -c `
   if [ $nn -lt 58 ]; then
      echo "  The rsync log   is: $li1"
   else
      echo "  The rsync log   is:"
      echo ' ' $li1
   fi
fi
echo "  There are $nrtot backups."
echo ' '

#--------------------------------------------------------------------------
# Restore a backup (-r or -R option).
#--------------------------------------------------------------------------
if [ -n "$rst" ]; then
   bext=$MCTDH_BACKUP/`cat ${bdir}_last | tr '\n' ' ' | cut -d' ' -f$nrst `
   nn=`echo "$bext" | wc -c `
   day=`date '+%d' `
   sfx="_mbk$day"
   if [ $nn -lt 58 ]; then
      echo "  Restoring ${nrst}-th backup: $bext"
   else
      echo "  Restoring ${nrst}-th backup:"
      echo ' ' $bext
   fi
   if [ "$dir" = "$dirrst" ]; then
      echo "  Restoring to original location $dir"
   else
      echo "  Restoring to directory $dirrst"
      if [ -f "$dirrst" ]; then
         echo "$dirrst is a regular file! STOP!"
         exit
      elif [ -d "$dirrst" ]; then
         :
      else
         mkdir $dirrst
         echo "Made directory $dirrst"
      fi
   fi
   echo "  The files to be overwriten will be saved with suffix $sfx "
   if [ -z "$noaction" ]; then
   echo -n "Do you really want to restore, i.e. overwrite files? Type (y|n): "
      read answr
      if [ "$answr" = y ] || [ "$answr" = "yes" ] ; then
         :
      else
         echo "Restore aborted!"
         exit
      fi
   fi
   nn=`echo "$li1" | wc -c `
   if [ $nn -lt 58 ]; then
      echo "  The rsync log   is: $li1"
   else
      echo "  The rsync log   is:"
      echo ' ' $li1
   fi

   if [ -f $li1 ]; then  mv -f $li1 ${li1}_old ; fi
   date > $li1

   opt="$noaction $delete -rlpgcvvv -b --suffix=$sfx"
#  echo "  Rsync-Options: $opt"

#   ----------   RSYNC   ----------
   set -f  # Do not expand shell wildcards (in $excl)
   rsync $opt -e "ssh -x" $excl $bext/ $host:$dirrst/ \
      | fgrep -v recv_  | fgrep -v make_  >> $li1   2>&1
   set +f

   date >> $li1
   echo ' '
   echo "--- Done: mbackup; Restore no. $nrst--- "
   date
   echo ' '
   if [ -n "$noaction" ]; then
      echo 'Nothing has been done (-n option).'
   fi
   echo 'These are the files sent:'
#   egrep '^send_files\(' "$li1" | cut -d, -f2 | tr ')' ' '
#   egrep '^recv mapped ' $li1 | cut -d' ' -f3
   egrep '^send_files mapped ' $li1 | cut -d' ' -f3
   echo 'These are the files backed up:'
   egrep '^backed up ' "$li1" | cut -d' ' -f3
   echo 'These are the files deleted:'
   egrep '^deleting ' "$li1" | cut -d' ' -f2 | egrep -v '^in$'
   tail $li1 | fgrep terminated
   tail $li1 | fgrep EOF
   echo "$star40$star40"
   exit
fi


#--------------------------------------------------------------------------
# Remove a backup (-d or -D option).
#--------------------------------------------------------------------------

if [ -n "$rmbk" ] ; then
   cd $MCTDH_BACKUP
   thisbk=`cat $last | tr '\n' ' ' | cut -d' ' -f$ndel`

   if [ $ndel -eq 1 ]; then
      echo "You want to remove the very first backup."
      echo "This implies that you will remove all $thisbk backups!"
      echo -n "Do you really want to do this? Type (y|n): "
      read answ
      if [ "$answ" = y ] || [ "$answ" = "yes" ] ; then
         if [ -d "$thisbk" ]; then
            rm -rf ${thisbk}*
            rm -f rsync_$thisbk
         else
            echo "There is no directory $thisbk"
         fi
      fi
      exit
   fi

   echo "  This is an excerpt of the Log file."
   echo "----------$minus40"
   cat $log | sed -ne "/^${ndel})(#)  /,/------------/p"
   echo ' '
   echo " Shall I remove this ${ndel}-th backup?"
   echo " $thisbk"
   echo -n " Type (y|n): "
   read answ
   if [ "$answ" = y ] || [ "$answ" = "yes" ] ; then
      rm -rf $thisbk
      cat $log | sed -e "/^${ndel})(#)  /,/------------/d" > new
      cp -f new $log
      cat $last | sed -e "/^${thisbk}$/d" > new
      cp -f new $last
      i=$(($ndel+1))
      while [ $ndel -lt $nrtot ]; do
         cat $log | sed -e "s/^${i})(#)  /${ndel})(#)  /" > new
         cp -f new $log
         ndel=$i
         i=$(($i+1))
      done
      rm -f new
      echo ' These are the last 35 lines of the (new) Log file:'
      echo ' '
      tail -n 35 $log
   else
      echo "$star40$star40"
   fi
   exit
fi

#--------------------------------------------------------------------------
# Make a new backup.
#--------------------------------------------------------------------------

if [ -z "$force" ] ; then
   newfiles=`find $dir -type f -newer $last`
   if [ -z "$newfiles" ] ; then
      echo "There are no new files to backup!"
      exit
   fi
fi

cd $MCTDH_BACKUP

# --- Making the hard links.

if [ -f $li1 ]; then  mv -f $li1 ${li1}_old ; fi
date > $li1
if [ -z "$noaction" ]; then
   echo " Creating dir:"
   echo " $mext"
   echo " and hard link it to:"
   echo " $lastbk"
fi

cp -al $lastbk $mext  # making hard links. We are on $MCTDH_BACKUP.

opt="$noaction -acvvv --delete"
#echo " Rsync-Options: $opt"

#   ----------   RSYNC   ----------
set -f  # Do not expand shell wildcards (in $excl)
rsync $opt -e "ssh -x" $excl $dir/ $host:$bext/ \
      | fgrep -v recv_  | fgrep -v make_  >> $li1
set +f
date >> $li1

# --- Check if there are back-uped files. Otherwise rm the last backup.
test=`egrep '^send_files\(' $li1 `
if [ -z "$test" ] || [ -n "$noaction" ]; then
   echo ' '
   if [ -n "$noaction" ]; then
      echo " Nothing has been done (-n option)."
      rm -rf $bext
      echo "$star40$star40"
   else
      echo " No changed files. I remove the last backup."
      rm -rf $bext
      echo " removed: $bext"
      echo "$star40$star40"
      exit
   fi
else

# --- append to last and log files.
   echo $mext >> $last
   nrtot=`cat $last | wc -l`
   dt=`date`
   echo ${nrtot}")(#)  $dt" >> $log
   echo "$mext"             >> $log
fi

echo ' '
echo -n "--- Done: mbackup no. $nrtot ---  "; date
echo ' '
if [ -z "$noaction" ]; then
   echo 'These are the files sent:'
#   egrep '^send_files\(' "$li1" | cut -d, -f2 | tr ')' ' ' | tee -a $log
#   egrep '^recv mapped ' $li1 | cut -d' ' -f3 | tee -a $log
   egrep '^send_files mapped ' $li1 | cut -d' ' -f3 | tee -a $log
   j=1
   while [ $j -le $ic ]; do
      echo "${txt[$j]}" >> $log
      j=$(($j+1))
   done
   echo "--------------------$minus40" >> $log
else
   echo 'These are the files that would have been sent:'
#   egrep '^send_files\(' "$li1" | cut -d, -f2 | tr ')' ' '
#   egrep '^recv mapped ' $li1 | cut -d' ' -f3
   egrep '^send_files mapped ' $li1 | cut -d' ' -f3
fi
tail $li1 | fgrep terminated
tail $li1 | fgrep EOF
echo "$star40$star40"

exit

#############################################################################

