#
# tbackup, a shell script to simplify making backups of any kind
#
#	Author	: K. Drenth
#	Date	: Mon Jan 24 08:53:34 PST 1994
#
#	Version : 1.0
#	Revision: Mon Jan 24 08:53:34 PST 1994
#	Descr.	: Initial version
#
VERSION="1.0"
PATH=/usr/bin/menu:$PATH;	export PATH
UXXOUT=/tmp/$$;			export UXXOUT			
# Global variable declarations
BUPTYPE=0
OVERWRI=1
CPIOLVL=v
BLKSIZE=32768
FINDLVL=""
UNATTEN=0
RD_BACK=0
TAPEDEV=""
TAPE_OK=1
TOCSKIP=0

usage()
{
	echo '
USAGE:

tbackup -afruvCFLt [specs]

	-v		Select Volume Backup
	-f		Select File Backup
	-a		Select Append Mode
	-r		Enable ReadBack by default
	-u		Run Unattended
	-t device	specify backup device without /dev/rmt
Note !			must be a non-rewind device !!
	-C blocksize	Specify cpio block size
	-F option	Specify extra find options
	-L option	Specify extra cpio options
'
	exit 0
}

#
# Return an indexed parameter from input parameter list
#
index_parm()
{
	I=$1
	shift
	J=1
	for PARM in "$@"
	do
		if [ $I -eq $J ]
		then
			echo "`DeWild "$PARM"`"
			return
		fi
		J=`expr $J + 1`
	done
}

#
# Ask user for type of backup
#
ask_vol_or_files()
{
	UserSelectSingle -h"Select Type of Backup"\
			   "File(s)" "Volume(s)" "Quit"

	case `AssignUSS` in
		'File(s)'	)	BUPTYPE=1;;
		'Volume(s)'	)	BUPTYPE=2;;
		*		)	exit    1;;
	esac
}

#
# Ask user for backup mode
#
ask_append_or_overwrite()
{
	UserSelectSingle -h"Select Backup mode" -d$OVERWRI	\
		           "Append" "Overwrite"

	case `AssignUSS` in
		'Append'	)	OVERWRI=0;;
		'Overwrite'	)	OVERWRI=1;;
		*		)	exit    1;;
	esac
}

#
# Ask user for backup verification
# 
ask_readback()
{
	UserSelectSingle -h"Readback after Backup" -d$RD_BACK	\
		           "No" "Yes"

	case `AssignUSS` in
		'No'		)	RD_BACK=0;;
		'Yes'		)	RD_BACK=1;;
		*		)	exit    1;;
	esac
}

#
# Ask user for backup device if none is specified yet
#
ask_tapedev()
{
	if [ "$TAPEDEV" = "" ]
	then
		TAPEDRIVES=`/bin/ls /dev/rmt | grep -v 0`

		UserSelectSingle -h "Select Tapedrive" $TAPEDRIVES

		TAPEDEV=`AssignUSS`
		if [ "$TAPEDEV" = "EsCaPeD" ]
		then
			exit 1
		fi
		clear
	fi

# try to test if the tapedrive is ready and on-line
	STATUS=`mt -t /dev/rmt/$TAPEDEV status | grep er=`

	if [ "$STATUS" != "er=0" ]
	then
		UserSelectSingle -h"Tapedrive not OK $STATUS, continue ?" No Yes

		NOYES=`AssignUSS`
		if [ "$NOYES" = "No" ] || [ "$NOYES" = "EsCaPeD" ]
		then
			exit 1
		fi
		TAPE_OK=0
	else
		TAPE_OK=1
	fi
	sleep 1
}
		
#
# test if tape is already on End Of Media
#
tape_is_EOM()
{
	EOM=0
	DDSTAT=""
	MTSTAT=""
# try to read one tape sector
	DDSTAT=`dd if=/dev/rmt/$TAPEDEV of=/dev/null bs=512 count=1 2>&1 |\
		awk '{ print $1;\
		       exit; }'` 
	if [ "$DDSTAT" != "1+0" ]
	then
# read failed try to obtain tape drive status to see if we're on a FileMark
		MTSTAT=`mt -t /dev/rmt/$TAPEDEV status | grep FM`
		if [ "$MTSTAT" = "" ]
		then
			echo "\n\007Probably Tape Error, Status:\n\
			      `mt -t /dev/rmt/$TAPEDEV status`"
			exit 2
		else
			EOM=1
		fi
	else
# backspace the just read sector
		mt -t /dev/rmt/$TAPEDEV bsr 1
	fi

	return $EOM
}

#
# Skip tape to end of media to append data to it
#
tape_append()
{
	if [ $TAPE_OK -eq 1 ]
	then
		echo "Skipping to end of data on /dev/rmt/$TAPEDEV...\c"
		mt -t /dev/rmt/$TAPEDEV append
		echo "done"
		if [ $? -ne 0 ]
		then
			exit $?
		fi
	else
		return 1
	fi
}

#
# space the tape $1 files forward
#
tape_space()
{
	if [ $TAPE_OK -eq 1 ]
	then
		echo "Skipping $1 datasets on  /dev/rmt/$TAPEDEV...\c"
		mt -t /dev/rmt/$TAPEDEV fsf $1
		echo "done"
		if [ $? -ne 0 ]
		then
			exit $?
		fi
	else
		return 1
	fi
}

#
# rewind the tape and skip $TOCSKIP number of files forward if applicable
#
tape_rewind()
{
	if [ $TAPE_OK -eq 1 ]
	then
		echo "Rewinding /dev/rmt/$TAPEDEV...\c"
		mt -t /dev/rmt/$TAPEDEV rewind
		if [ $? -ne 0 ]
		then
			exit $?
		fi

		sleep 1
# space the tape forward if applicable
		if [ $TOCSKIP -ge 1 ]
		then
			tape_space $TOCSKIP
		else
			echo "done"
		fi
	else
		return 1
	fi
}

#
# find existing number of archives and place the number in TOCSKIP
#
tape_find_skip()
{
	TOCSKIP=0
	CPIO_OK=0
	tape_rewind
	if [ $OVERWRI -eq 0 ]
	then
		echo "Looking for existing archives...\c"
		I=0
		while [ $CPIO_OK -eq 0 ]
		do
# are we at EOM ?
			tape_is_EOM
			CPIO_OK=$?
			if [ $CPIO_OK -eq 0 ]
			then
# the data on the tape must be a TOC, so read it
				rm /tmp/tbackup.contents		       \
						         2>/dev/null >/dev/null
				cpio -icC $BLKSIZE		  	       \
				     -I/dev/rmt/$TAPEDEV 2>/dev/null
				echo "\nArchive #$I contains:\c"
				cat /tmp/tbackup.contents
# determine the number of tape files contained in this archive
				NARCHIVES=`wc -l</tmp/tbackup.contents`
				rm /tmp/tbackup.contents 
				NARCHIVES=`expr $NARCHIVES - 3`
				TOCSKIP=`expr $TOCSKIP + $NARCHIVES`
				I=`expr $I + 1`
				echo "Skipping to the next TOC...\c"
				mt -t /dev/rmt/$TAPEDEV fsf $NARCHIVES
				echo "done"
			fi
		done
		if [ $TOCSKIP -eq 0 ]
		then
			echo "done"
		fi
		tape_append
	fi
}

#
# verify the written data, Note ! dd is used to really read the tape
#
tape_readback()
{
	if [ $TAPE_OK -eq 1 ]
	then
		echo "Reading back from /dev/rmt/$TAPEDEV"
		dd if=/dev/rmt/$TAPEDEV files=1 bs=$BLKSIZE |		\
		cpio -ic"${CPIOLVL}"tC $BLKSIZE 2>&1 |			\
		grep -v ' blocks$'
	else
		return 1
	fi
}

#
# backup the files specified in /tmp/$$tbackup.tmp
#
tape_backup()
{
	if [ $TAPE_OK -eq 1 ]
	then
		echo "Backing up to /dev/rmt/$TAPEDEV"
		cat /tmp/$$tbackup.tmp |				 \
		cpio -oc"${CPIOLVL}"C $BLKSIZE -O/dev/rmt/$TAPEDEV 2>&1 |\
		grep -v ' blocks$'
		if [ $? -ne 0 ]
		then
			exit $?
		fi
	else
		return 1
	fi
}

#
# do all the things nessesary for a file backup session
#
file_backup()
{
	clear
	echo "Initiating File Backup.."
# extract the directory- and filespecs  from the incoming parameters
	ADIR=1
	for PARM in "$@"
	do
		if [ $ADIR -eq 1 ]
		then
		 	DIRS="$DIRS $PARM"
			ADIR=0
		else
			FILS="$FILS $PARM"
			ADIR=1
		fi
	done

# setup defaults in case of no input 
	if [ "$DIRS" = "" ]
	then
		DIRS=" ./"
	fi
	if [ "$FILS" = "" ]
	then
		FILS=" *"
	fi

# skip leading space due to extraction
	DIRS=`CopyString "$DIRS" 1 255`
	FILS=`CopyString "$FILS" 1 255`

	if [ $UNATTEN -eq 0 ]
	then
# let user edit input parameters
		SAVIFS=$IFS
		IFS="^	\
"
		UserEnterValue	-h"Enter Backup Specifications"	-w55	\
				"Directoryspec(s)"		"$DIRS"	\
				"Filespec(s)"			"$FILS"
		IFS=$SAVIFS
		DIRS="`AssignUEV 1`"
		FILS="`AssignUEV 2`"

		ask_append_or_overwrite

		ask_readback

		clear
	fi

# do some stripping and counting on the dir- and filespecs
	 DIRS=`DeWild "$DIRS"`
	NDIRS=0
	for DIR in $DIRS
	do
		NDIRS=`expr $NDIRS + 1`
	done
	NFILS=0
	 FILS=`DeWild "$FILS"`
	for FIL in $FILS 
	do
		NFILS=`expr $NFILS + 1`
	done

	ask_tapedev

	tape_find_skip

# start building the TOC
	TMPLVL=$CPIOLVL
	CPIOLVL=V
	echo "Creating table of contents...\c"
	echo "\nFile Backup by $USER" >/tmp/tbackup.contents 
	echo "backed up at `date`\n"   >>/tmp/tbackup.contents 
	ID=1
	IF=1
	while [ $ID -le $NDIRS ]
	do
		DIR=`index_parm $ID $DIRS`
		ID=`expr $ID + 1`
		SAVDIR=`pwd`
		cd $DIR
		FIL=`index_parm $IF $FILS`
		IF=`expr $IF + 1`
		FND="$DIR	"
		if [ `SubString "'" "$FIL"` -eq 0 ]
		then
			FND="$FND `CopyString "$FIL" 1 255`" 
			FIL=`index_parm $IF $FILS`
			IF=`expr $IF + 1`
			QPOS=`SubString "'" "$FIL"`
			while [ $QPOS -eq 65535 ]
			do
				FND="$FND $FIL"
				FIL=`index_parm $IF $FILS`
				QPOS=`SubString "'" "$FIL"`
				IF=`expr $IF + 1`
			done
			FIL=`CopyString "$FIL" 0 $QPOS`
		fi
		FND="$FND $FIL"
		if [ $ID -gt $NDIRS ] && [ $IF -le $NFILS ]
		then
			while [ $IF -le $NFILS ]
			do
				FND="$FND `index_parm $IF $FILS`"
				IF=`expr $IF + 1`
			done
		fi
		echo "$FND">>/tmp/tbackup.contents
		cd $SAVDIR
	done
	echo "done"
	cat /tmp/tbackup.contents
	echo
	echo /tmp/tbackup.contents >/tmp/$$tbackup.tmp
	tape_backup
	if [ $RD_BACK -eq 1 ]
	then
		tape_rewind
		tape_readback
	fi
	TOCSKIP=`expr $TOCSKIP + 1`
	CPIOLVL=$TMPLVL
	rm  /tmp/tbackup.contents

# start the actual backup session
	ID=1
	IF=1
	while [ $ID -le $NDIRS ]
	do
		DIR=`index_parm $ID $DIRS`
		echo "Building file list for $DIR...\c"
		ID=`expr $ID + 1`
		SAVDIR=`pwd`
		cd $DIR
		FIL=`index_parm $IF $FILS`
		IF=`expr $IF + 1`
		if [ `SubString "@" "$FIL"` -eq 0 ]
		then
			FIL=`CopyString "$FIL" 1 255`
			echo "using contents of $FIL..."
			FLS=`cat $FIL`
			FLS=`DeWild "$FLS"`
			FND="\( -name DuMmYnAmE"  
			for FIL in $FLS
			do
				FND="$FND -o -name $FIL"
			done
		else
			FND="\( -name" 
			if [ `SubString "'" "$FIL"` -eq 0 ]
			then
				FND="$FND `CopyString "$FIL" 1 255` -o -name" 
				FIL=`index_parm $IF $FILS`
				IF=`expr $IF + 1`
				QPOS=`SubString "'" "$FIL"`
				while [ $QPOS -eq 65535 ]
				do
					FND="$FND $FIL -o -name"
					FIL=`index_parm $IF $FILS`
					QPOS=`SubString "'" "$FIL"`
					IF=`expr $IF + 1`
				done
				FIL=`CopyString "$FIL" 0 $QPOS`
			fi
			FND="$FND $FIL"
			if [ $ID -gt $NDIRS ] && [ $IF -le $NFILS ]
			then
				while [ $IF -le $NFILS ]
				do
					FIL=`index_parm $IF $FILS`
					IF=`expr $IF + 1`
					FND="$FND -o -name $FIL"
				done
			fi
		fi
		FND="$FND \)"
# let find create a backup specification file
		eval find ./ $FND -print 2>/dev/null >/tmp/$$tbackup.tmp
		echo "done"
		
		tape_append
		tape_backup
		if [ $RD_BACK -eq 1 ]
		then
			tape_rewind
			tape_readback
		fi
		TOCSKIP=`expr $TOCSKIP + 1`
		rm /tmp/$$tbackup.tmp
		cd $SAVDIR
	done

	TOCSKIP=0
	tape_rewind
}

#
# do all the things nessesary for a volume backup session
#
volume_backup()
{
	clear
	echo "Initiating Volume Backup..."
	if [ $UNATTEN -eq 0 ]
	then
# let the user choose filesystems, enable only mounted filesystems
		FILSYS=`/etc/mount | grep -v /tmp | awk '{ print $3}'`
		for FS in $FILSYS
		do
			SYSFIL="$SYSFIL $FS"
			for DFS in $@
			do
				if [ "$FS" = "$DFS" ]
				then
# append a + to default set it selected if the filesystem is an incoming parm
					SYSFIL="$SYSFIL+"
				fi
			done
		done

		UserSelectMultiple -h"Select FileSystem(s)" $SYSFIL

		FILSYS=`AssignUSM`
		if [ "$FILSYS" = "EsCaPeD" ]
		then
			exit 1	
		fi
		
		ask_append_or_overwrite

		ask_readback

		clear
	else
		FILSYS=$@
	fi

	ask_tapedev

	tape_find_skip

# start building the TOC
	TMPLVL=$CPIOLVL
	CPIOLVL=V
	echo "Creating table of contents...\c"
	echo "\nVolume Backup by $USER" >/tmp/tbackup.contents 
	echo "backed up at `date`\n"   >>/tmp/tbackup.contents 
	for FS in $FILSYS
	do
		echo "$FS" >> /tmp/tbackup.contents
	done
	echo "done"
	cat /tmp/tbackup.contents
	echo
	echo /tmp/tbackup.contents >/tmp/$$tbackup.tmp
	tape_backup
	if [ $RD_BACK -eq 1 ]
	then
		tape_rewind
		tape_readback
	fi
	TOCSKIP=`expr $TOCSKIP + 1`
	CPIOLVL=$TMPLVL
	rm  /tmp/tbackup.contents

# start the actual backup session
	for FS in $FILSYS
	do
		echo "Building file list for $FS...\c"
		CURDIR=`pwd`
		cd $FS
		find .							\
		     $FINDLVL -mount -depth -print			\
		     2>/dev/null >/tmp/$$tbackup.tmp
		echo "done"
		
		tape_append
		tape_backup
		if [ $RD_BACK -eq 1 ]
		then
			tape_rewind
			tape_readback
		fi
		TOCSKIP=`expr $TOCSKIP + 1`
		rm /tmp/$$tbackup.tmp
		cd $CURDIR
	done

	TOCSKIP=0
	tape_rewind
}

#
# Start of main program
#
clear
echo "TBackup Version $VERSION (c) Stichting ZCS"
while getopts afruvC:F:L:t: c
do
	case $c in
	a	) 	OVERWRI=0;;
	f	) 	BUPTYPE=1;;
	r	)	RD_BACK=1;;
	u	)	UNATTEN=1;;
	v	) 	BUPTYPE=2;;
	C	)	BLKSIZE=$OPTARG;;
	F	)	FINDLVL=$OPTARG;;
	L	)	CPIOLVL=$OPTARG;;
	t	)	TAPEDEV=$OPTARG;;
	*	)	usage;;
	esac
done
shift `expr $OPTIND - 1`

if [ $BUPTYPE -eq 0 ]
then
	ask_vol_or_files
fi

case $BUPTYPE in
1	)	file_backup "$@";;
2	)	volume_backup "$@";;
esac

