#!/bin/sh

# Sets up the system object tree. This tree overshadows the src objects,
# and local system modifications happen to objects in the tree, so the
# src objects need never be modified from what is shipped.


# In an attempt to allow for upgrades of the core objects of a moo, while
# still giving moo admins the power to make any changes the need to, mooix
# uses two classes of objects. The src (aka "dist") objects are provided as
# part of mooix, and get installed somewhere like /usr/share/mooix, and should
# only be  changed when mooix is upgraded. (To facilitate that, they are left
# off in limbo where nothing is likely to find them and modify them. They're
# also owned by root, and so mood will refuse to modify them.) The dest
# (aka "system") objects are derived from the src objects objects, and are
# created when mooix is installed, probably somewhere like /var/lib/mooix/,
# and are modified as the moo runs and is tewaked. They inherit their basic
# behavior from the src objects, but of course the moo admin is free to change
# that behavior in arbitrary ways.
#
# So the inheritance chain of an object -- take the system avatar
# for example -- will weave back and forth:
#       system avatar parent is dist avatar
#       dist avatar parent is system container
#       system container parent is dist ocntainer
#       dist container parent is system thing
#       system thing parent is dist thing

SRC=$1
DEST=$2
# This third parameter is name of a link between dest and src. Must be
# absolute.
SRCLINK=$3
# And this fourth parameter is the name of the link going the other day,
# src to dest. Must be absolute.
DESTLINK=$4
# Optional, make files owned by
OWNER=$5
# Optional, do not modify src tree at all if set.
DONTTOUCHSRC=$6

set -e
umask 022 # is assumed

if [ -z "$SRC" -o -z "$DEST" -o -z "$SRCLINK" -o -z "$DESTLINK" ]; then
	echo "Usage: splittree src dest srclink destlink" >&2
	exit 1
fi

# Given a symlink, returns the absolute sanitized path it points at.
# Works even if the link is currently broken.
linkpath () {
	export SRC
	export DESTLINK
	perl -e '
		use File::Spec;
		my $link=shift;
		my $path=readlink($link);
		my (undef,$linkdir,undef)=File::Spec->splitpath($link);
		my $abspath=File::Spec->rel2abs($path, $linkdir);
		my @elts=File::Spec->splitdir(File::Spec->canonpath($abspath));
		my @dest;
		foreach (@elts) {
		        if ($_ eq "..") {
		                pop @dest;
			}
		        else {
				push @dest, $_;
			}
		}
		my $dir=join("/", @dest);
		$dir="/$dir" unless $dir=~/^\//;
		$ENV{SRC}=File::Spec->canonpath($ENV{SRC});
		$dir=~s/^$ENV{SRC}/$ENV{DESTLINK}/;
		print "$dir\n";
	' $1
}

isa () {
	# This can fail if links are broken, which can happen, so ignore
	# those failures. Since we do a breadth-first object traversal
	# below, it happens to work when it needs to.
	perl -e '
		use Mooix::Thing;
		$o=Mooix::Thing->get(shift);
		$p=Mooix::Thing->get(shift);
		exit ! $o->isa($p)
	' $1 $2 2>/dev/null
}

# Take care of splitting the tree for an object, and all objects inside it.
# Recursive. $1 should be the name of the object, without $SRC or $DEST
# prefixed.
doobj () {
	local obj=$1
	
	if [ -z "$DONTTOUCHSRC" ]; then
		# Change all the links in the src object to point to dest
		# objects, using absolute paths. Note that the dest objects
		# may not exist yet.
		for link in $(find $SRC/$obj -maxdepth 1 -type l); do
			# It might be a directory, it it might be a broken link.
			# The link might point relative to a location in SRC,
			# or it might point (relative or absolute) to a
			# location in DEST.
			if [ ! -f $link ]; then
				linktarget=$(linkpath $link)
				base=$(echo $link | sed "s!$SRC/$obj!!")
				rm -f $SRC/$obj/$base
				ln -s $linktarget $SRC/$obj/$base
			fi
		done
	fi
	
	# Make the system object. I'd much rather just runmeth new, but
	# that requires a running mood that we can use, and that is
	# probably not a wise thing for this script to depend on. So I will
	# hack it -- create the object by hand.
	if [ ! -d $DEST/$obj ]; then
		NEW=1
		# Duplicate permissions.
		install -m $(find $SRC/$obj -maxdepth 0 -printf "%m") -d $DEST/$obj
	else
		NEW=0
	fi

	touch $DEST/$obj/.mooix
	if [ "$OWNER" ]; then
		chown $OWNER:$OWNER $DEST/$obj $DEST/$obj/.mooix
	fi
	
	# Special case for thinglists/thingsets: If the object is one of these,
	# copy the entire contents of the src object into the dest object, on
	# new installs only. That is because these objects don't inherit like
	# regular objects.
	if [ "$NEW" = 1 ] && 
		( isa $SRC/$obj $SRC/abstract/thingset || 
		  isa $SRC/$obj $SRC/abstract/thinglist ) && \
	   [ -z "`find $SRC/$obj -type d -mindepth 1`" ] && \
	   [ ! -e $SRC/$obj/newid ]; then
		FILES=$(find $SRC/$obj/ \( -type f -or -type l \) -maxdepth 1 \
				\( -name item\* -or -name watermark -or \
				-name dir_ok -or -name list -or -name owner \
				-or -name .list_lock \))
		if [ "$FILES" ]; then
			cp -R $FILES $DEST/$obj
		fi
		if [ "$OWNER" ]; then
			chown -R $OWNER:$OWNER $DEST/$obj
		fi
	fi
	
	# The parent is always the src object by the same name, unless
	# it's already set.
	if [ ! -e $DEST/$obj/parent ]; then
		rm -f $DEST/$obj/parent # maybe the link is broken, so remove..
		if [ "$OWNER" ]; then
			su $OWNER -c "ln -s $SRCLINK/$obj $DEST/$obj/parent"
		else	
			ln -s $SRCLINK/$obj $DEST/$obj/parent
		fi
	fi

	# If the src object has an owner field, propigate it to the child
	# (owner fields are not inherited normally)
	if [ -L $SRC/$obj/owner ]; then
		rm -f $DEST/$obj/owner
		cp -a $SRC/$obj/owner $DEST/$obj/owner
	fi
	
	# If any fields/dirs in the src oject are group writable,
	# replicate that over, on new installs only.
	if [ "$NEW" = 1 ]; then
		for thing in $(find $SRC/$obj -maxdepth 1 -not -type l -perm +g+w -printf "./%P\n"); do
			if [ -e $DEST/$obj/$thing ]; then 
				chmod g+w $DEST/$obj/$thing
			fi
		done
	fi

	# Recurse into sub-objects. Note that I have to handle thingset
	# and thinglist and kids first, if they are in this set.
	if [ -d $SRC/${obj}thingset/ ]; then
		doobj ${obj}thingset/
	fi
	if [ -d $SRC/${obj}thinglist/ ]; then
		doobj ${obj}thinglist/
	fi
	if [ -d $SRC/${obj}filterlist/ ]; then
		doobj ${obj}filterlist/
	fi
	# And to make sure they're done first, do abstract first too.
	if [ -d $SRC/${obj}abstract/ ]; then
		doobj ${obj}abstract/
	fi
	for subobj in $(find $SRC/$obj -type d -maxdepth 1 -mindepth 1 -printf "$obj%P/\n"); do
		if [ -e "$SRC/$subobj/.mooix" ] && \
		   [ "$subobj" != "${obj}thingset/" ] && \
		   [ "$subobj" != "${obj}thinglist/" ]; then
			doobj $subobj
		fi
	done
}

# Get the ball rolling.
doobj ""

# "Mommy, can I be a perl script when I grow up?"
