#!/bin/sh
# Copyright (C) 2015  Etersoft
# Copyright (C) 2015  Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# Saved meta info for the current dir with use metastore and pax

export LANG=C

DESCR="eterattrstore - save&restore attrs and special files (c) Etersoft 2015"

print_message()
{
	local DESC="$1"
	shift
	echo "$DESC in $(basename "$0"): $*"
}

# Print error message and stop the program
fatal()
{
	print_message Error "$@" >&2
	exit 1
}

# CHECKME: the same like estrlist has ?
# Note: used egrep! write '[0-9]+(first|two)', not '[0-9]\+...'
rhas()
{
	echo "$1" | grep -E -q -- "$2"
}

PAXFILE=.eterpack.special.files.pax
METAFILE=.metadata
FOLLOWDIR=.eterpack.follow
# with --metadir, metafiles are kept in a separate dir (eterpack keeps them out of the source tree)
METADIR=''
# file with the list of followed symlinks to dirs (relative paths, one per line)
FOLLOWLIST=''

set_metadir()
{
	METADIR="$1"
	PAXFILE="$METADIR/$PAXFILE"
	METAFILE="$METADIR/$METAFILE"
	FOLLOWDIR="$METADIR/$FOLLOWDIR"
}

print_followed()
{
	[ -n "$FOLLOWLIST" ] && [ -s "$FOLLOWLIST" ] && cat "$FOLLOWLIST"
	return 0
}

# special files in the current dir and inside the followed dirs (but not the followed links itself)
list_special_files()
{
	local link
	find . -depth "(" -type b -or -type c -or -type p -or -type l ")" -printf "%P\n"
	print_followed | while read -r link ; do
		find "$link/" -depth -mindepth 1 "(" -type b -or -type c -or -type p -or -type l ")" -printf "%p\n"
	done
}

# the followed links are packed as dirs, so do not save them as symlinks
skip_followed()
{
	if [ -n "$FOLLOWLIST" ] && [ -s "$FOLLOWLIST" ] ; then
		grep -v -x -F -f "$FOLLOWLIST"
	else
		cat
	fi
	return 0
}

save_attrs()
{
	local res=0

	# save special files
	# Note: sv4cpio has no ustar limits (100 chars for a link target, about 255 chars for a path)
	list_special_files | skip_followed | pax -wd -x sv4cpio -f "$PAXFILE" || res=1
	[ -s "$PAXFILE" ] || rm -f "$PAXFILE" 2>/dev/null

	# save attrs in .metafile
	metastore -s -f "$METAFILE" || res=1
	[ -s "$METAFILE" ] || rm -f "$METAFILE" 2>/dev/null

	# metastore does not follow symlinks (it stores the real paths), so save every followed dir separately
	local link n=0
	if [ -n "$(print_followed)" ] ; then
		mkdir -p "$FOLLOWDIR" || return 1
		while read -r link ; do
			n=$((n+1))
			printf '%s\n' "$link" >"$FOLLOWDIR/$n.path" || res=1
			(cd "$link/" && metastore -s -f "$FOLLOWDIR/$n.metadata") || res=1
		done <"$FOLLOWLIST"
	fi

	return $res
}

remove_attrs()
{
	rm -f "$PAXFILE" 2>/dev/null
	rm -f "$METAFILE" 2>/dev/null
	rm -rf "$FOLLOWDIR" 2>/dev/null
	[ -n "$METADIR" ] && rmdir "$METADIR" 2>/dev/null
	return 0
}

print_metafiles()
{
	[ -s "$PAXFILE" ] && echo "$PAXFILE"
	[ -s "$METAFILE" ] && echo "$METAFILE"
	return 0
}

compare_attrs()
{
	# TODO:
	#[ -s "$PAXFILE" ] && echo "$PAXFILE"
	if [ -s "$METAFILE" ] ; then
		metastore -c -f "$METAFILE"
	fi
	return 0
}

restore_attrs()
{
	local pathfile link
	# always uses local .metastore for all subdirs
	# pax detects the format itself (sv4cpio or ustar in old backups)
	[ -s "$PAXFILE" ] && pax -r -f "$PAXFILE"
	[ -s "$METAFILE" ] && metastore -a -f "$METAFILE"
	if [ -d "$FOLLOWDIR" ] ; then
		for pathfile in "$FOLLOWDIR"/*.path ; do
			[ -s "$pathfile" ] || continue
			read -r link <"$pathfile"
			[ -s "${pathfile%.path}.metadata" ] || continue
			(cd "./$link/" && metastore -a -f "${pathfile%.path}.metadata")
		done
	fi
	return 0
}

COMMAND=$1
shift

if [ "$1" = "--metadir" ] ; then
	[ -n "$2" ] || fatal "--metadir requires a dir"
	set_metadir "$2"
	shift 2
fi

if [ "$1" = "--follow" ] ; then
	[ -n "$2" ] || fatal "--follow requires a file"
	FOLLOWLIST="$2"
	shift 2
fi

# epm assure pax
# epm assure metastore

case $COMMAND in
	save)
		save_attrs
		;;
	restore)
		restore_attrs
		;;
	compare)
		compare_attrs
		;;
	metafiles)
		print_metafiles
		;;
	clear)
		remove_attrs
		;;
	-h|--help)
		echo $DESCR
		echo "Run as $0 command [--metadir DIR] [--follow FILE]"

		echo
		echo "Commands:"
		echo "	save       save attrs and spec. files"
		echo "	restore    restore attrs and spec. files"
		echo "	compare    show differences between stored and real metadata"
		echo "	metafiles  print list of created files with metainfo"
		echo "	clear      remove files with metainfo"
		echo
		echo "Options:"
		echo "	--metadir DIR  keep metainfo in DIR (absolute path) instead of the current dir"
		echo "	--follow FILE  also save the dirs behind the symlinks listed in FILE (save only)"
		;;
	*)
		echo "$DESCR" >&2
		echo "Run with -h or --help for help" >&2
		exit 1
	;;
esac

