#!/bin/bash

#######################################################################
#            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE              #
#                    Version 2, December 2004                         #
#                                                                     #
# Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>                    #
#                                                                     #
# Everyone is permitted to copy and distribute verbatim or modified   #
# copies of this license document, and changing it is allowed as long #
# as the name is changed.                                             #
#                                                                     #
#            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE              #
#   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION   #
#                                                                     #
#  0. You just DO WHAT THE FUCK YOU WANT TO.                          #
#######################################################################

# This is Wonko's script to clone a file/directory hierarchy.
# Later versions can probably be found here:
# http://www.wonkology.org/utils/clone0
# Send your questions and suggestions to wonko@wonkology.org.

# name and version of this script
ScriptName=${0##*/}
ScriptDate=2011-01-30

# make undefined variables throw an error
set -u

# pre-define command line options
    oDir=
   oSize=0
 oSparse=true
oVerbose=0
   oZero=

# parse command line arguments
while getopts ":dhs:Sv0" opt
do
	case $opt in
	d )
		oDir=true
		;;
	h )
		cat <<- FNORD
		$ScriptName version $ScriptDate
		Duplicate a file / directory hierarchy. Files are
		created as sparse files, not taking up real space.
		
		Usage: $ScriptName [-dhSv0] [-s size] src... dst
		
		Options:
		  -d      clone directory structure only, not files
		  -h      show this help
		  -s size copy files up to size as the are, and clip larger files
		  -S      do not create sparse files
		  -v      show directories being created
		  -vv     show files being created
		  -vvv    debug output
		  -0      clip files larger than size (option -s) to zero size
		
		Arguments:
		  src... one or more directories to clone
		  dst    destination directory (will be created)
		
		FNORD
		exit 0
		;;
	s )
		oSize=$OPTARG
		;;
	S )
		oSparse=
		;;
	v )
		(( oVerbose++ ))
		;;
	0 )
		oZero=true
		;;
	* )
		printf "Error: No such option %s!\n\n" $OPTARG
		$0 -h
		exit 2
	esac
done
shift $(( OPTIND-1 ))

# are there at least 2 arguments?
if (( $# < 2 ))
then
	printf "Error: Too few arguments given!\n\n"
	"$0" -h
	exit 1
fi

# get last argument
: $@
aDst=$_

# add -v to commands later?
verboseFiles=
verboseDirs=
(( oVerbose >= 1 )) &&
	verboseDirs=-v
(( oVerbose >= 2 )) &&
	verboseFiles=-v
(( oVerbose >= 3 )) &&
	set -xv

# loop through all but the last arguments
while (( $# > 1 ))
do
	# let aSrc hold $1
	aSrc=$1
	
	# change into source directory	
	cd "$aSrc" || exit $?
	
	# find all files (or directories only with option -d)
	find . ${oDir:+-type d} -print0 |
		while read -d $'\0' file
		do
			if [[ -d $file ]]
			then
				# $file is a directory, create it if necessary
				[[ -d "$aDst/$file" ]] ||
					mkdir $verboseDirs -p -- "$aDst/$file"
			else
				# create directory part of $file, if necessary
				[[ -d "$aDst/${file%/*}" ]] ||
					mkdir $verboseDirs -p -- "$aDst/${file%/*}"
				
				# copy file with size 0
				cp $verboseFiles --attributes-only -a -- "$file" "$aDst/$file"
				
				# for regular files, make the copy a sparse file of same size
				if [[ -f $file ]]
				then
					# change copy into sparse file of original file's size
					if [[ $oSparse ]]
					then
						# get file size
						size=$( ls -l "$file" | awk '{ print $5 }' )
						# copy file contents if option -s is given
						if (( oSize ))
						then
							if (( size < oSize )) || ! [[ $oZero ]]
							then
								head -c "$oSize" -- "$file" >> "$aDst/$file"
							fi
						fi
						# make a sparse file to fake real size
						(( size > oSize )) &&
							truncate -s $size -- "$aDst/$file"
					fi
				elif ! [[ -L $file ]] && (( oVerbose ))
				then
					printf "Special file '%s' created as regular file.\n" \
					       "$file"
				fi
			fi
		done
	
	shift
done
