##
##  sea -- Shell Execution Archive
##  Copyright (c) 2012 Ralf S. Engelschall <rse@engelschall.com>
##
##  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; either version 2 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
##  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
##  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
##
##  NOTICE: Given that you include this file verbatim into your own
##  source tree, you are justified in saying that it remains separate
##  from your package, and that this way you are simply just using GNU
##  shtool. So, in this situation, there is no requirement that your
##  package itself is licensed under the GNU General Public License in
##  order to take advantage of GNU shtool.
##

##
##  COMMAND LINE PARSING
##

str_usage="[-h|--help] [-o|--output <file>] <script> [<file-or-dir> ...]"
arg_spec="1+"
opt_spec="h.o:"
opt_alias="h:help,o:output"
opt_h=no
opt_o=""

#   parse argument specification string
eval `echo $arg_spec |\
      sed -e 's/^\([0-9]*\)\([+=]\)/arg_NUMS=\1; arg_MODE=\2/'`

#   parse option specification string
eval `echo $opt_spec |\
      sed -e 's/\([a-zA-Z0-9]\)\([.:+]\)/opt_MODE_\1=\2;/g'`

#   parse option alias string
eval `echo $opt_alias |\
      sed -e 's/-/_/g' -e 's/\([a-zA-Z0-9]\):\([^,]*\),*/opt_ALIAS_\2=\1;/g'`

#   interate over argument line
opt_PREV=''
while [ $# -gt 0 ]; do
    #   special option stops processing
    if [ ".$1" = ".--" ]; then
        shift
        break
    fi

    #   determine option and argument
    opt_ARG_OK=no
    if [ ".$opt_PREV" != . ]; then
        #   merge previous seen option with argument
        opt_OPT="$opt_PREV"
        opt_ARG="$1"
        opt_ARG_OK=yes
        opt_PREV=''
    else
        #   split argument into option and argument
        case "$1" in
            --[a-zA-Z0-9]*=*)
                eval `echo "x$1" |\
                      sed -e 's/^x--\([a-zA-Z0-9-]*\)=\(.*\)$/opt_OPT="\1";opt_ARG="\2"/'`
                opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'`
                eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}"
                ;;
            --[a-zA-Z0-9]*)
                opt_OPT=`echo "x$1" | cut -c4-`
                opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'`
                eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}"
                opt_ARG=''
                ;;
            -[a-zA-Z0-9]*)
                eval `echo "x$1" |\
                      sed -e 's/^x-\([a-zA-Z0-9]\)/opt_OPT="\1";/' \
                          -e 's/";\(.*\)$/"; opt_ARG="\1"/'`
                ;;
            -[a-zA-Z0-9])
                opt_OPT=`echo "x$1" | cut -c3-`
                opt_ARG=''
                ;;
            *)
                break
                ;;
        esac
    fi

    #   eat up option
    shift

    #   determine whether option needs an argument
    eval "opt_MODE=\$opt_MODE_${opt_OPT}"
    if [ ".$opt_ARG" = . ] && [ ".$opt_ARG_OK" != .yes ]; then
        if [ ".$opt_MODE" = ".:" ] || [ ".$opt_MODE" = ".+" ]; then
            opt_PREV="$opt_OPT"
            continue
        fi
    fi

    #   process option
    case $opt_MODE in
        '.' )
            #   boolean option
            eval "opt_${opt_OPT}=yes"
            ;;
        ':' )
            #   option with argument (multiple occurances override)
            eval "opt_${opt_OPT}=\"\$opt_ARG\""
            ;;
        '+' )
            #   option with argument (multiple occurances append)
            eval "opt_${opt_OPT}=\"\$opt_${opt_OPT} \$opt_ARG\""
            ;;
        * )
            echo "$0: ERROR: unknown option: \`$opt_OPT'" 1>&2
            exit 1
            ;;
    esac
done
if [ ".$opt_PREV" != . ]; then
    echo "$0: ERROR: missing argument to option \`$opt_PREV'" 1>&2
    exit 1
fi

#   process help option
if [ ".$opt_h" = .yes ]; then
    echo "Usage: $0 $str_usage"
    exit 0
fi

#   complain about incorrect number of arguments
case $arg_MODE in
    '=' )
        if [ $# -ne $arg_NUMS ]; then
            echo "$0: ERROR: invalid number of arguments (exactly $arg_NUMS expected)" 1>&2
            exit 1
        fi
        ;;
    '+' )
        if [ $# -lt $arg_NUMS ]; then
            echo "$0: ERROR: invalid number of arguments (at least $arg_NUMS expected)" 1>&2
            exit 1
        fi
        ;;
esac

##
##  MAIN
##

#   we required at least the control script
script="$1"
shift

#   determine output file
sea="$opt_o"
if [ ".$sea" = . ]; then
    sea=`echo "$script" | sed -e 's;\.[^.][^.]*$;;' -e 's;$;.sea;'`
fi

#   wrap into a self-extracting distribution file
tmpdir="${TMPDIR-/tmp}"
tmpfile="$tmpdir/sea.tmp.$$"
rm -f $tmpfile
set -o noclobber
(   sed <$0 \
        -e '1,/^____/d' \
        -e "s;@script@;$script;g"
    echo . | awk '{
        for (i = 0; i < 2048/8; i++) {
            printf("        ");
        }
    }'
) >$tmpfile
dd bs=2048 count=1 if=$tmpfile of=$sea 2>/dev/null
rm -f $tmpfile
tar cf - "$script" "$@" 2>/dev/null >>$sea

#   graceful termination
#   (before the shell discovers that we carry the wrapper with us below)
exit 0

##
##  WRAPPER SCRIPT
##

______________________________________________________________________________
#!/bin/sh
#![OpenPKG]
##
##  THIS IS AN AUTO-GENERATED SHELL-WRAPPER ARCHIVE
##

#   establish sane environment
PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin"
LC_CTYPE=C
export LC_CTYPE
umask 022

#   pass-through
if [ $# -eq 1 -a ".$1" = ".--tar" ]; then
    #   pass-through attached payload on stdout
    dd if="$0" bs=2048 skip=1 2>/dev/null
    rc=$?
else
    #   extract attached payload
    rm -rf "$0.d" >/dev/null 2>&1
    mkdir "$0.d" || exit $?
    dd if="$0" bs=2048 skip=1 2>/dev/null | \
    (cd "$0.d" && tar xf - 2>/dev/null)
    if [ ! -f "$0.d/@script@" ]; then
        echo "$0: ERROR: failed to unpack attached payload into directory \"$0.d\"" 1>&2
        rm -rf "$0.d" >/dev/null 2>&1 || true
        exit 1
    fi

    #   pass-through execution to control script
    (cd "$0.d" && exec sh "./@script@" ${1+"$@"})
    rc=$?

    #   cleanup
    rm -rf "$0.d" >/dev/null 2>&1 || true
fi

#   terminate gracefully
#   (before the shell discovers that we carry MBs of raw data with us below)
exit $rc

#   the payload tarball is appended in raw format directly to the end
#   of this script, just leaded by padding whitespaces which make sure
#   that the tarball data starts at the pre-defined offset of 2KB. This
#   allows us to unpack the tarball by just skipping the leading 2KB.

