#! /bin/sh

#
# customize: manages the customized files of a host             (jh,20.06.2001)
#

#
#   customize: manages the customized files and links of a host
#   Copyright (C) 2000-2001  Jochen Hepp <jochen.hepp@gmx.de>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation in version 2 of the License.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#



# --------- definitions ---------

definitions () {
	customhome=""   # if customize directory is not $HOME/customize

	version='0.0.7'

	script="${0##*/}"

	if [ -z "$HOSTNAME" ]; then
		HOSTNAME="`hostname 2>/dev/null || uname -n`"
	fi
	host="${HOSTNAME%%.*}"
}



# --------- usage ---------

print_usage () {
	cat <<EOF
Usage: $script OPTION FILE ...

       -c  --common      backup file is on all hosts equal
       -s  --special     backup file is special on this host
       -d  --diff        display differences between file and customized file
       -ds --diffshort   like diff, but do not show the context
       -r  --restore     restore file from customize directory
       -rf --force       restore file but don't ask if overwriting

           --cmpall      compare all files in the custom directory
           --tarcommon   create tarball from the common files
           --untarcommon unpack tarball with the common files
           --tar         create tarball from the special files of this host
           --untar       unpack tarball with the special files for this host

       -V  --version     display version number
       -h  --help        display this help and exit
EOF
}



# --------- version ---------

print_version () {
	cat <<-EOF
		$script $version

		Copyright (C) 2000, 2001 Jochen Hepp
		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.

		Written by Jochen Hepp <jochen.hepp@gmx.de>.
EOF
}



# --------- mode ---------

switch_mode () { # mode, file ... global: $host
	mode="$1"   # global
	shift
	local command=""

	case "$mode" in
		-c)  mode="--common" ;;
		-s)  mode="--special" ;;
		-d)  mode="--diff" ;;
		-ds) mode="--diffshort" ;;
		-r)  mode="--restore" ;;
		-rf) mode="--force" ;;
	esac

	case "$mode" in
		--common|--special) command="backup_file" ;;
		--restore|--force)  command="restore_file" ;;
		--diff|--diffshort) command="diff_file" ;;
		*)
			echo "$script: unrecognized option \`$mode'" >&2
			echo "$script: Try \`$script --help' for more information." >&2
			exit 1
			;;
	esac

	while [ $# -gt 0 ]; do
		# absolute file names
		eval "$command" "$host" "`file_name \"$1\"`"
		shift
	done
}



# --------- backup to customize directory (common or special) ---------

backup_file () { # host, file, global: $mode, $customhome
	local host="$1"
	local file="$2"
	local rootdir
	local dir

	if [ ! -L "$file" ]; then
		if [ -d "$file" ]; then
			print_error "$file: is a directory"
			exit 10
		elif [ -e "$file" -a ! -f "$file" ]; then
			print_error "$file: not a file"
			exit 10
		elif [ ! -e "$file" ]; then
			print_error "$file: not found"
			exit 10
		fi
	fi

	if [ "$mode" != "--common" ]; then
		rootdir="$customhome/$host"
	else
		rootdir="$customhome/common"
		if [ -e "$customhome/$host$file" ]; then
			print_error "a special file for $host exists!" \
			            "so use: $script --special FILE"
			exit 5
		fi
	fi

	dir="$rootdir${file%/*}"

	if [ ! -d "$dir" ]; then
		echo "$script: create directory: $dir"
		mkdir -p "$dir" || \
		{ print_error "$dir: can't create directory"; \
		  exit 10; }
	fi

	if [ -L "$rootdir$file" -o -f "$rootdir$file" ]; then
		rescue_old_backup_file "$rootdir" "$file"
	fi

	print_mode "$file" "$rootdir$file"

	cp -pdf "$file" "$rootdir$file"
}



# --------- rescue old backup file ---------

rescue_old_backup_file () { # rootdir file, global: $customhome
	local rootdir="$1"
	local file="$2"
	local dir
	local rescue

	rescue="${rootdir}-old${file}_$(date +%y%m%d).old"

	dir="${rescue%/*}"

	if [ ! -d "$dir" ]; then
		echo "$script: create directory: $dir"
		mkdir -p "$dir" || \
		{ print_error "$dir: can't create directory"; \
		  exit 10; }
	fi

	if [ ! -e "$rescue" ]; then
		cp -pdf "$rootdir$file" "$rescue"
		echo "$script: old file saved"
	fi
}



# --------- restore file ---------

restore_file () { # host, file
	local host="$1"
	local file="$2"

	search_file "$host" "$file"  # -> $backup
	print_mode "$backup" "$file"

	if [ "$mode" = "--force" ]; then
		cp -pdf  "$backup" "$file"
	else
		cp -pdi  "$backup" "$file"
	fi
}



# --------- diff file ---------

diff_file () { # host, file
	local host="$1"
	local file="$2"

	search_file "$host" "$file"  # -> $backup
	print_mode "$backup" "$file"

	if [ -L "$backup" -o -L "$file" ]; then
		ls -l "$backup" "$file" | \
		awk '{ print $9, $10, $11; }'
	elif [ "$mode" = "--diffshort" ]; then
		diff -U 0 "$backup" "$file"
	else
		diff -u   "$backup" "$file"
	fi
}



# --------- search file ... (first special, than common) ---------

search_file () { # host, file, global: $customhome   -> $backup
	local host="$1"
	local file="$2"
	local subdir

	for subdir in "$host" common; do
		backup="$customhome/$subdir$file"
		if [ -L "$backup" -o -f "$backup" ]; then
			break
		fi
		backup=""
	done

	if [ "$backup" = "" ]; then
		print_error "$file: has never been customized"
		exit 10
	fi
}



# --------- print_mode ---------

print_mode () { # from, to # global: $script, $mode
	echo "$script: ${mode#--}"
	echo "           $1"
	echo "           $2"
}



# --------- compare all files in customhome (special or common) ---------

cmp_all_files () { # host, global: $customhome
	local host="$1"

	for subdir in common "$host"; do
		if [ ! -d "$customhome/$subdir" ]; then
			print_error "$customhome/$subdir: directory not found"
		else
			(cd "$customhome/$subdir"; find . -type f -o -type l) | \
			if [ "$subdir" = "common" ]; then
				while read file; do
					file="${file#.}"
					if [ ! -e "$customhome/$host$file" ]; then
						cmp_file "$customhome/common" "$file"
					fi
				done
			else
				while read file; do
					cmp_file "$customhome/$subdir" "${file#.}"
				done
			fi
		fi
	done
}



# --------- compare file with file in directory ---------

cmp_file () { # rootdir file
	local rootdir="$1"
	local file="$2"

	if [ -L "$file" ]; then
		if [ ! -e "$file" ]; then
			echo "dead ln: $file"
		elif [ ! -L "$rootdir$file" ]; then
			echo "is link: $file"
		fi
	else
		if [ ! -e "$file" ]; then
			echo "missing: $file"
		elif [ ! -f "$file" ]; then
			echo "no file: $file"
		elif [ -L "$rootdir$file" ]; then
			echo "is file: $file"
		else
			if ! cmp -s "$file" "$rootdir$file"; then
				if [ "$file" -nt "$rootdir$file" ]; then
					echo "newer:   $file"
				elif [ "$file" -ot "$rootdir$file" ]; then
					echo "older:   $file"
				else
					echo "differs: $file"
				fi
			fi
		fi
	fi
}



# --------- tar customize subdirectory ---------

tar_customize () { # subdirectory, global: $script
	if [ ! -d "$customhome/$1" ]; then
		print_error "$customhome/$1: directory not found"
		exit 10
	fi

	( umask 077 && \
	  tar -C "$customhome/.." \
	      -czf "$script-$1.tar.gz" "${customhome##*/}/$1" )
}



# --------- untar customize subdirectory ---------

untar_customize () { # subdirectory, global: $script
	if [ -d "$customhome/$1" ]; then
		print_error "$customhome/$1: directory does already exist"
		exit 10
	fi

	tar -C "$customhome/.." -xzf "$script-$1.tar.gz"
}



# --------- file_name (get absolute file name) ---------

file_name () { # filename
	local file="$PWD/$1"

	# absolute filename?
	case "$1" in
		/*)
			file="$1"
			;;
	esac
	echo "$file"
}



# --------- print error (multiline) ---------

print_error () { # text1, text2, ..., global: $script
	local error_string

	error_string="$script: ERROR: "
	echo "$error_string$1" >&2

	# error_string=${error_string//?/ }
	error_string='                  '
	shift
	while [ "$1" ]; do
		echo "$error_string$1" >&2
		shift
	done
}



# --------- main ---------

# main {

	umask 022
	definitions

	if [ $# -eq 0 ]; then
		print_usage >&2
		exit 1
	fi

	case "$1" in
		--version|-V)
			print_version
			exit 0
			;;
		--help|-h)
			print_usage
			exit 0
			;;
	esac

	if [ ! -d "${customhome:=$HOME/customize}" ]; then
		print_error "$customhome: custom directory not found" \
		            "please create it or adjust variable customhome in $script"
		exit 50
	fi

	case "$1" in
		--cmpall)
			cmp_all_files "$host"
			;;
		--tarcommon)
			tar_customize "common"
			;;
		--untarcommon)
			untar_customize "common"
			;;
		--tar)
			tar_customize "$host"
			;;
		--untar)
			untar_customize "$host"
			;;
		*)
			switch_mode "$@"
			;;
	esac

	exit 0
# }


# --- end ---

