#! /bin/sh
# Copyright (c) Feb 2000, by Denys Duchier, Universitaet des Saarlandes
#
# ozinstall is a shell script to install files and directories.  It is
# hopefully written in pure sh and thus should be maximally portable.
# It assumes the existence of cp, mkdir, chmod, and chgrp, and further
# assumes that cp supports the -f flag (force).  ozinstall makes it
# unnecessary to fight incompatible install programs and covers
# concisely all the cases useful for Mozart packages.  An `install'
# script must in any case be included in any distributed package to
# permit installation on machines which do not have an install program.
# We might as well always use ozinstall.
#
# One feature of ozinstall is that it will create the destination
# directory structure.  This greatly simplifies Makefiles since no
# rules are necessary to create installation directories.
#
# SYNOPSIS
#
# ozinstall OPTIONS -d DIR
#
#	if directory DIR is not present, it is installed
#
# ozinstall OPTIONS [-f|-x|-M] SRCFILE DSTFILE
# ozinstall OPTIONS [-f|-x|-M] SRCFILE DSTDIR/
# ozinstall OPTIONS [-f|-x|-M] SRCFILE -d DSTDIR
#
#	SRCFILE is installed as DSTFILE.  If the directory of
#	DSTFILE does not exist yet, it is also installed. -f
#	installs a regular data file, -x installs an executable
#	file, and -M decides which of -f or -x to use based on
#	whether the file to install is executable or not.
#
# OPTIONS
#
# -g GROUP	set group	(--group)
# -m MODE	set mode	(--mode)
#
# ENVIRONMENT VARIABLES
#
# OZSKEL_MODE_DIR	default mode for directories
# OZSKEL_MODE_FILE	default mode for data files
# OZSKEL_MODE_BIN	default mode for executable files
# OZSKEL_GROUP		default group

PGM="ozinstall"

defaultIFS=' 	
'
IFS="${IFS:-${defaultIFS}}"

explode() {
    tmp=`echo "$1" | sed -e 's@/@%@g' -e 's@^%@/@'`
    echo "$tmp%O_Z_S_K_E_L"
}

basename() {
    tmp=`explode "$1"`
    last=
    oIFS="$IFS"
    IFS='%'
    for x in $tmp; do
	if test "$x" = O_Z_S_K_E_L; then break;
	else last="$x"; fi
    done
    IFS="$oIFS"
    echo "$last"
}

dirname() {
    tmp=`explode "$1"`
    sofar=
    last=
    oIFS="$IFS"
    IFS='%'
    for x in $tmp; do
	if test "$x" = O_Z_S_K_E_L; then break;
	else
	    sofar="$sofar${sofar:+/}$last"
	    last="$x"
	fi
    done
    IFS="$oIFS"
    echo "$sofar"
}

isslashed() {
    tmp=`basename "$1"`
    test -z "$tmp"
}

VERBOSE=

trace() {
    test -n "$VERBOSE" && echo "$1" 1>&2
}

error() {
    echo "$PGM: $1" 1>&2
    exit 1
}

: ${OZSKEL_MODE_DIR:=775}
: ${OZSKEL_MODE_FILE:=444}
: ${OZSKEL_MODE_BIN:=555}

OZSKEL_GROUP=
OZSKEL_MODE=

USE_MODE=
USE_GROUP=

installfix() {
    if test -n "$USE_GROUP"; then
	trace "chgrp($USE_GROUP,$1)"
	chgrp "$USE_GROUP" "$1" || error "chgrp($USE_GROUP,$1) failed"
    fi
    if test -n "$USE_MODE"; then
	trace "chmod($USE_MODE,$1)"
	chmod "$USE_MODE"  "$1" || error "chmod($USE_MODE,$1) failed"
    fi
}

installdir() {
    test -n "$1" || error "installdir($1), empty name"
    if test -r "$1"; then
	test -d "$1" || error "installdir($1), file exists"
    else
	tmp=`explode "$1"`
	oIFS="$IFS"
	IFS='%'
	sofar=
	for x in $tmp; do
	    if test "$x" = O_Z_S_K_E_L; then break;
	    else
		sofar="$sofar${sofar:+/}$x"
		test -d "$sofar" && continue
		trace "mkdir($sofar)"
		mkdir "$sofar" || error "mkdir($1) failed"
		installfix "$sofar"
	    fi
	done
	IFS="$oIFS"
    fi
}

installfile() {
    test -d "$2" && error "installfile($1,$2), destination is directory"
    tmp=`dirname "$2"`
    test -d "$2" && error "installfile($1,$2), destination's directory non-existent"
    trace "cp($1,$2)"
    cp -f "$1" "$2" || error "cp($1,$2) failed"
    installfix "$2"
}

USER_MODE=
USER_GROUP=
USER_SRC=
USER_DST=
USER_ACTION=
USER_DIR=

VAR=
OPT=
VAL=
for ARG
do
    # grab argument for preceding option
    if test -n "$VAR"; then
	eval "$VAR=\$ARG"
	VAR=
	OPT=
	continue
    fi
    case "$ARG" in
    -*=*) VAL=`echo "$ARG" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
       *) VAL= ;;
    esac
    case "$ARG" in
    -m=*|-mode=*|--mode=*)
	USER_MODE="$VAL"
    ;;
    -m|--mode)
	OPT="$ARG"
	VAR=USER_MODE
    ;;
    -g=*|-group=*|--group=*)
	USER_GROUP="$VAL"
    ;;
    -g|-group|--group)
	OPT="$ARG"
	VAR=USER_GROUP
    ;;
    -v|--verbose)
	VERBOSE=yes
    ;;
    -f|-x|-M)
	test -z "$USER_ACTION" || error "only one of -f,-x,-M is allowed"
	USER_ACTION="$ARG"
    ;;
    -d)
	test -z "$USER_DIR" || error "-d supplied twice"
	OPT="$ARG"
	VAR=USER_DIR
    ;;
    -*)
	error "invalid option $ARG"
    ;;
    *)
	if test -n "$USER_DST"; then
	    error "too may arguments"
	elif test -n "$USER_SRC"; then
	    USER_DST="$ARG"
	else
	    USER_SRC="$ARG"
	fi
    ;;
    esac
done

# check if we were expecting to grab an option's argument
test -z "$VAR" || error "missing argument for $OPT"

# we cannot allow both `-d DIR' and `DSTFILE'
if test -n "$USER_DST" && test -n "$USER_DIR"; then
    error "it is illegal to supply both -d dir and destination arg"
fi

# for convenience strip off the slash in `-d DIR/'
if test -n "$USER_DIR" && isslashed "$USER_DIR"; then
    USER_DIR=`dirname "$USER_DIR"`
fi

# if no action specified: if SRC was supplied, then assume `-f'
# else if `-d DIR' was supplied, use `-d', else its an error.
if test -z "$USER_ACTION"; then
    if test -n "$USER_SRC"; then
	USER_ACTION="-f"
    elif test -n "$USER_DIR"; then
	USER_ACTION="-d"
    else
	error "too few arguments"
    fi
fi

if test -n "$USER_SRC"; then
    test -r "$USER_SRC" || error "source file non-existent"
fi

case "$USER_ACTION" in
    -f|-x|-M)
	test -n "$USER_SRC" || error "source file argument missing"
	if test -n "$USER_DIR"; then
	    tmp=`basename "$USER_SRC"`
	    USER_DST="$USER_DIR/$tmp"
	elif test -n "$USER_DST"; then
	    if isslashed "$USER_DST"; then
		tmp=`basename "$USER_SRC"`
		USER_DST="${USER_DST}$tmp"
	    fi
	else
	    error "no destination specified"
	fi
	USE_MODE="${OZSKEL_MODE_DIR}"
	USE_GROUP="$USER_GROUP"
	tmp=`dirname "$USER_DST"`
	test -n "$tmp" && installdir "$tmp"
	if test -z "$USER_MODE"; then
	    case "$USER_ACTION" in
	    -f) USER_MODE="$OZSKEL_MODE_FILE" ;;
	    -x) USER_MODE="$OZSKEL_MODE_BIN"  ;;
	    -M) if test -x "$USER_SRC"; then
		    USER_MODE="$OZSKEL_MODE_BIN"
		else
		    USER_MODE="$OZSKEL_MODE_FILE"
		fi
	     ;;
	    esac
	fi
	USE_MODE="${USER_MODE}"
	installfile "$USER_SRC" "$USER_DST"
    ;;
    -d)
	USE_MODE="${USER_MODE:-$OZSKEL_MODE_DIR}"
	USE_GROUP="$USER_GROUP"
	installdir "$USER_DIR"
    ;;
esac

exit 0



