##
##  OpenPKG Software Stack Generation
##  Copyright (c) 2010-2012 OpenPKG GmbH <http://openpkg.com/>
##
##  This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
##  All rights reserved. Licenses which grant limited permission to use,
##  copy, modify and distribute this software are available from the
##  OpenPKG GmbH.
##
##  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##

#   configuration
openpkg="${OPENPKG_PREFIX}/bin/openpkg"
curl="${OPENPKG_PREFIX}/bin/openpkg curl"
shtool="${OPENPKG_PREFIX}/lib/openpkg/shtool"

##
##  COMMAND LINE PARSING
##

str_usage="[-h|--help] [-v|--verbose <level>] [-o|--output <dir>] [-D|--define <name>=<value> ...] [-u|--unversioned] [-q|--query] <name>.stk"
arg_spec="1="
opt_spec="h.v:o:D+u.q."
opt_alias="h:help,v:verbose,o:output,D:define,u:unversioned,q:query"
opt_h=no
opt_v=4
opt_o="."
opt_D=""
opt_u=no
opt_q=no

#   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

##
##  HELPER FUNCTIONS
##

#   helper function for graceful termination
die () {
    echo "$0: ERROR: $*"
    exit 1
}

#   helper function for verbose messages
verbose () {
    lvl="$1"
    msg="$2"
    if [ $opt_v -ge $lvl ]; then
        case "$lvl" in
            1 ) str_prefix="**" ;;
            2 ) str_prefix="++" ;;
            3 ) str_prefix="--" ;;
            4 ) str_prefix=".." ;;
        esac
        echo "${str_prefix} ${msg}"
    fi
}

#   expand macros
macro_expand () {
    stk="$1"

    #   load all options and their default values
    options=""
    for option in `sed <$stk \
        -e "1,/^%options/d" -e '/^%/,$d' \
        -e 's/#.*$//' -e 's/^ *//' -e 's/ *$//' -e '/^ *$/d' \
        -e 's; *[^ ]*$;;'`; do
        options="$options $option"
    done
    eval `sed <$stk \
        -e "1,/^%options/d" -e '/^%/,$d' \
        -e 's/#.*$//' -e 's/^ *//' -e '/^ *$/d' \
        -e 's:^\([^ ]*\) *\([^ ]*\)$:option_\1="\2";:'`

    #   override options with defined values
    for define in $opt_D; do
        name=`echo "$define" | sed -e 's;=.*$;;'`
        value=`echo "$define" | sed -e 's;^.*=;;'`
        eval "exists=\"\${option_${name}}\""
        if [ ".$exists" = . ]; then
            die "definition of not-existing option \"$name\""
        fi
        eval "option_${name}=\"${value}\""
    done

    #   expand macros
    cmd="sed"
    for name in $options; do
        eval "value=\"\${option_${name}}\""
        cmd="$cmd -e 's;%{[?]$name:\\([^}]*\\)};\1;g'"
        cmd="$cmd -e 's;%{![?]$name:\\([^}]*\\)};;g'"
        cmd="$cmd -e 's;%{$name};$value;g'"
    done
    cmd="$cmd -e 's;%{[?][^:}]*:[^}]*};;g'"
    cmd="$cmd -e 's;%{![?][^:}]*:\([^}]*\)};\1;g'"
    eval "$cmd"
}

#   extract macro-expanded header
header () {
    stk="$1"
    hdr="$2"
    exists=`egrep "^${hdr}" $stk`
    if [ ".$exists" != . ]; then
        egrep "^${hdr}:" $stk | \
        sed -e 's;^[^:]*: *;;' -e 's/#.*$//' -e 's/^ *//' -e 's/ *$//' -e '/^ *$/d' | \
        macro_expand "$stk"
    fi
}

#   extract macro-expanded section
section () {
    stk="$1"
    sec="$2"
    exists=`egrep "^%${sec}" $stk`
    if [ ".$exists" != . ]; then
        sed <$stk \
            -e "1,/^%${sec}/d" -e '/^%/,$d' \
            -e 's/#.*$//' -e 's/^ *//' -e 's/ *$//' -e '/^ *$/d' | \
        macro_expand "$stk"
    fi
}

##
##  MAIN
##

#   determine stack file
stk="$1"
shift

#   support stand-alone query operation
if [ ".$opt_q" = .yes ]; then
    name=`header "$stk" "Name"`
    summary=`header "$stk" "Summary"`
    packager=`header "$stk" "Packager"`
    version=`header "$stk" "Version"`
    release=`header "$stk" "Release"`
    echo "name=\"$name\";"
    echo "summary=\"$summary\";"
    echo "packager=\"$packager\";"
    echo "version=\"$version\";"
    echo "release=\"$release\";"
    sed <$stk \
        -e "1,/^%options/d" -e '/^%/,$d' \
        -e 's/#.*$//' -e 's/^ *//' -e '/^ *$/d' \
        -e 's:^\([^ ]*\) *\([^ ]*\)$:\1="\2";:'
    exit 0
fi

#   determine temporary directory
tmpdir="${TMPDIR-/tmp}"

#   provide verbose header
verbose 1 "OpenPKG Software Stack Generation"

#   determine information
verbose 2 "determining software stack information"
name=`header "$stk" "Name"`
summary=`header "$stk" "Summary"`
packager=`header "$stk" "Packager"`
version=`header "$stk" "Version"`
release=`header "$stk" "Release"`
verbose 3 "Name:        $name"
verbose 3 "Summary:     $summary"
verbose 3 "Packager:    $packager"
verbose 3 "Version:     $version"
verbose 3 "Release:     $release"
if [ ".$opt_u" = .yes ]; then
    basename="$name"
else
    basename="$name-$version-$release"
fi

#   determine URLs
verbose 2 "determining software stack packages"
repo=`section "$stk" "repository"`
verbose 3 "repository:  [URL]  $repo"
$openpkg build -r "$repo" -Z -u -K -B \
    `section "$stk" "packages"` | \
    egrep "openpkg curl" | \
    sed -e 's;^.* \([^ ]*\) || exit.*$;\1;' \
        -e '/\/openpkg-[0-9].*\.src\.rpm/{h;s/\.src\.rpm$/.src.sh/;p;x}' \
    >$tmpdir/url.txt

#   provide header
verbose 2 "assembling software stack sources"

#   download source packages
if [ ! -d "$opt_o/$basename.src.d" ]; then
    verbose 3 "creating:    [DIR]  $opt_o/$basename.src.d"
    $shtool mkdir -p -m 755 "$opt_o/$basename.src.d"
else
    verbose 3 "reusing:     [DIR]  $opt_o/$basename.src.d"
fi
for url in `cat $tmpdir/url.txt`; do
    url_file=`echo $url | sed -e 's;^.*/\([^/]*\)$;\1;'`
    if [ ! -f "$opt_o/$basename.src.d/$url_file" ]; then
        verbose 3 "downloading: [FILE] $opt_o/$basename.src.d/$url_file"
        if [ $opt_v -ge 4 ]; then
            $curl '-#' -L -k -o $opt_o/$basename.src.d/$url_file $url
        else
            $curl -s -L -k -o $opt_o/$basename.src.d/$url_file $url
        fi
    else
        verbose 3 "reusing:     [FILE] $opt_o/$basename.src.d/$url_file"
    fi
done
for path in $opt_o/$basename.src.d/*.src.sh $opt_o/$basename.src.d/*.src.rpm; do
    if [ ! -f "$path" ]; then
        continue
    fi
    path_file=`echo $path | sed -e 's;^.*/\([^/]*\)$;\1;'`
    found=no
    for url in `cat $tmpdir/url.txt`; do
        url_file=`echo $url | sed -e 's;^.*/\([^/]*\)$;\1;'`
        if [ ".$url_file" = ".$path_file" ]; then
            found=yes
            break
        fi
    done
    if [ ".$found" = .no ]; then
        verbose 3 "removing:    [FILE] $path"
        rm -f "$path"
    fi
done
rm -f $tmpdir/url.txt

#   generate XML/RDF index for SRPM packages
verbose 3 "generating:  [FILE] $opt_o/$basename.src.d/00INDEX.rdf"
$openpkg index -r "OpenPKG/SRPM" \
    -o $opt_o/$basename.src.d/00INDEX.rdf \
    -i $opt_o/$basename.src.d

#   generate deployment utility
verbose 3 "generating:  [FILE] $opt_o/$basename.sh"; \
section "$stk" "framework" >$tmpdir/framework.txt
section "$stk" "prolog" >$tmpdir/prolog.txt
section "$stk" "epilog" >$tmpdir/epilog.txt
HAVE_PROLOG=no; if [ ".`cat $tmpdir/prolog.txt`" != . ]; then HAVE_PROLOG=yes; fi
HAVE_EPILOG=no; if [ ".`cat $tmpdir/epilog.txt`" != . ]; then HAVE_EPILOG=yes; fi
$openpkg build -r "$repo" -Z -u -K \
    `section "$stk" "packages"` | \
grep -v "^#" | \
grep -v "^echo" | \
grep -v "^rm" | \
egrep -v "^(if|fi)" | \
grep -v "openpkg curl" | \
grep -v "openpkg rpm -Uvh" | \
sed -e 's;.*openpkg rpm ;;' \
    -e 's; || exit.*;;' \
    -e 's;--rebuild ;;' \
    -e 's;/.*/\([^/]*\.src\.rpm\);\1;' \
    -e 's;^\(.*\) \([^/]*\.src\.rpm\);\2 \1;' \
    -e "s;--define '\\([^ ]*\\) \\([^ ]*\\)';\\1=\\2;g" \
    -e 's;\.src\.rpm;;' \
    >$tmpdir/packages.txt
sed <$0 \
    -e '1,/^____/d' \
    -e "/^CFG_FRAMEWORK=\"/r $tmpdir/framework.txt" \
    -e "/^CFG_PACKAGES=\"/r $tmpdir/packages.txt" \
    -e "s;@HAVE_PROLOG@;$HAVE_PROLOG;" \
    -e "/%prolog/r $tmpdir/prolog.txt" \
    -e "s;@HAVE_EPILOG@;$HAVE_EPILOG;" \
    -e "/%epilog/r $tmpdir/epilog.txt" \
    -e "/^## SHTOOL ##/r $shtool" \
    -e "s;^\\(CFG_NAME=\"\\).*\\(\"\\);\\1$name\\2;" \
    -e "s;^\\(CFG_SUMMARY=\"\\).*\\(\"\\);\\1$summary\\2;" \
    -e "s;^\\(CFG_PACKAGER=\"\\).*\\(\"\\);\\1$packager\\2;" \
    -e "s;^\\(CFG_VERSION=\"\\).*\\(\"\\);\\1$version\\2;" \
    -e "s;^\\(CFG_RELEASE=\"\\).*\\(\"\\);\\1$release\\2;" \
    -e "s;^\\(CFG_UNVERSIONED=\"\\).*\\(\"\\);\\1$opt_u\\2;" \
    >$opt_o/$basename.sh

#   cleanup
rm -f $tmpdir/framework.txt
rm -f $tmpdir/packages.txt
rm -f $tmpdir/prolog.txt
rm -f $tmpdir/epilog.txt

#   exit gracefully
exit 0

##
##  DEPLOYMENT SCRIPT
##

______________________________________________________________________________
#!/bin/sh
#![OpenPKG]
##
##  OpenPKG Software Stack Deployment
##  Copyright (c) 2010-2012 OpenPKG GmbH <http://openpkg.com/>
##
##  This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
##  All rights reserved. Licenses which grant limited permission to use,
##  copy, modify and distribute this software are available from the
##  OpenPKG GmbH.
##
##  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##

##
##  COMMAND LINE PARSING
##

str_usage="[-h|--help] [-v|--verbose <level>] [-c|--cleanup <level>] [-D|--define <name>=<value> ...] [-s|--srcdir <dir>] [-o|--outdir <dir>] [-t|--tmpdir <dir>] [-S <sudo>]"
arg_spec="0="
opt_spec="h.v:c:D+s:o:t:S:"
opt_alias="h:help,v:verbose,c:cleanup,D:define,s:srcdir,o:outdir,t:tmpdir,S:sudo"
opt_h=no
opt_v=3
opt_c=1
opt_D=""
opt_s=""
opt_o=""
opt_t="${TMPDIR-/tmp}"
opt_S=""

#   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

#   helper function for verbose messages
verbose () {
    lvl="$1"
    msg="$2"
    if [ $opt_v -ge $lvl ]; then
        case "$lvl" in
            1 ) str_prefix="**" ;;
            2 ) str_prefix="++" ;;
            3 ) str_prefix="--" ;;
            4 ) str_prefix=".." ;;
        esac
        echo "${str_prefix} ${msg}"
    fi
}

#   helper function for graceful termination
die () {
    echo "$0: ERROR: $*"
    exit 1
}

##
##  MAIN
##

#   the stack information
CFG_NAME=""
CFG_SUMMARY=""
CFG_PACKAGER=""
CFG_VERSION=""
CFG_RELEASE=""

#   the required OpenPKG framework parameters
CFG_FRAMEWORK="
"

#   the required OpenPKG packages
#   (in topological order and with build options resolved)
CFG_PACKAGES="
"

#   whether unversioned output directory should be used
CFG_UNVERSIONED=""

#   display information
verbose 1 "OpenPKG Software Stack Deployment"
verbose 2 "determining information"

#   parse OpenPKG framework parameters
params="
    prefix
    user group
    susr musr rusr nusr
    sgrp mgrp rgrp ngrp
    suid muid ruid nuid
    sgid mgid rgid ngid
    tag
"
for param in $params; do
    eval "CF_$param=''"
done
for opt in $CFG_FRAMEWORK; do
    case "$opt" in
        --[a-zA-Z0-9-]*=* )
            name=`echo "$opt" | sed -e 's;^--\([^=]*\)=.*$;\1;'`
            value=`echo "$opt" | sed -e 's;^--[^=]*=\(.*\)$;\1;'`
            eval "CF_$name='$value'"
            ;;
    esac
done
for define in $opt_D; do
    name=`echo "$define" | sed -e 's;=.*$;;'`
    value=`echo "$define" | sed -e 's;^.*=;;'`
    eval "CF_$name='$value'"
done
CFG_FRAMEWORK=''
for name in $params; do
    eval "value=\"\$CF_$name\""
    if [ ".$value" != . ]; then
        if [ ".$CFG_FRAMEWORK" = . ]; then
            CFG_FRAMEWORK="--$name=$value"
        else
            CFG_FRAMEWORK="$CFG_FRAMEWORK --$name=$value"
        fi
    fi
done
if [ ".$CF_musr" = . ]; then CF_musr="$CF_user";    fi
if [ ".$CF_mgrp" = . ]; then CF_mgrp="$CF_group";   fi
if [ ".$CF_rusr" = . ]; then CF_rusr="$CF_user-r";  fi
if [ ".$CF_rgrp" = . ]; then CF_rgrp="$CF_group-r"; fi
if [ ".$CF_nusr" = . ]; then CF_nusr="$CF_user-n";  fi
if [ ".$CF_ngrp" = . ]; then CF_ngrp="$CF_group-n"; fi
verbose 3 "name:     $CFG_NAME"
verbose 3 "summary:  $CFG_SUMMARY"
verbose 3 "packager: $CFG_PACKAGER"
verbose 3 "version:  $CFG_VERSION"
verbose 3 "release:  $CFG_RELEASE"

#   determine target parameters
if [ ".$CF_prefix" = . ]; then
    die "OpenPKG Framework parameter \"prefix\" required"
fi
prefix="$CF_prefix"
if [ ".$CF_tag" = . ]; then
    die "OpenPKG Framework parameter \"tag\" required"
fi
tag="$CF_tag"
shtool="$opt_t/$CFG_NAME-$CFG_VERSION-$CFG_RELEASE.shtool.sh"
sed <$0 -e '1,/^## SHTOOL ##.*/d' >$shtool
platform=`sh $shtool platform -n -L -S "" -C "+" -F '%<ap>-%<sp>'`
verbose 3 "platform: $platform"
verbose 3 "tag:      $tag"
verbose 3 "prefix:   $prefix"

#   determine operation directories
srcdir="$opt_s"
if [ ".$srcdir" = . ]; then
    srcdir="$CFG_NAME-$CFG_VERSION-$CFG_RELEASE.src.d"
fi
tmpdir="$opt_t/$CFG_NAME-$CFG_VERSION-$CFG_RELEASE.d"
if [ ! -d "$tmpdir" ]; then
    (   umask 077
        rm -rf "$tmpdir" >/dev/null 2>&1 || true
        sh $shtool mkdir -p -m 700 "$tmpdir" >/dev/null 2>&1
        if [ $? -ne 0 ]; then
            die "failed to create temporary directory: \"$tmpdir\""
        fi
    ) || exit $?
fi
outdir="$opt_o"
if [ ".$outdir" = . ]; then
    if [ ".$CFG_UNVERSIONED" = .yes ]; then
        outdir="$CFG_NAME.$platform-$tag.d"
    else
        outdir="$CFG_NAME-$CFG_VERSION-$CFG_RELEASE.$platform-$tag.d"
    fi
fi
if [ ! -d "$outdir" ]; then
    sh $shtool mkdir -p -m 755 "$outdir" >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        die "failed to create output directory: \"$outdir\""
    fi
fi
verbose 3 "srcdir:   $srcdir"
verbose 3 "outdir:   $outdir"
verbose 3 "tmpdir:   $tmpdir"

#   execute prolog script
if [ ".@HAVE_PROLOG@" = .yes ]; then
    verbose 2 "execute prolog script"
    cat >$tmpdir/prolog.sh <<EOT
#!/bin/sh
#![OpenPKG]
# %prolog
EOT
    $opt_S sh $tmpdir/prolog.sh
    rm -f $tmpdir/prolog.sh
fi

if [ $opt_c -le 1 -a -x "$prefix/bin/openpkg" ]; then
    #   optionally fresh-up OpenPKG instance
    pkgs=`$prefix/bin/openpkg rpm -qa | egrep -v '^(openpkg-[0-9]|gpg-pubkey-)'`
    if [ ".$pkgs" != . ]; then
        verbose 2 "uninstall already installed OpenPKG packages"
        for pkg in $pkgs; do
            verbose 3 "REMOVE: $pkg @ $prefix"
        done
        $opt_S $prefix/bin/openpkg rpm -e $pkgs || \
            die "failed to uninstall already installed OpenPKG packages"
    fi
fi
if [ $opt_c -le 0 -a -x "$prefix/bin/openpkg" ]; then
    #   optionally destroy OpenPKG instance
    verbose 2 "destroy existing OpenPKG framework"
    $opt_S $prefix/bin/openpkg rpm -e openpkg || \
        die "failed to destroy existing OpenPKG framework"
fi

#   check whether an already existing OpenPKG instance can be reused
if [ -x "$prefix/bin/openpkg" ]; then
    verbose 2 "checking compatibility of existing OpenPKG framework"
    for name in $params; do
        case "$name" in
            user|group ) continue ;;
        esac
        eval "value_required=\"\$CF_$name\""
        if [ ".$value_required" != . ]; then
            value_existing=`$prefix/bin/openpkg rpm --eval "%{l_${name}}"`
            if [ ".$value_existing" != ".$value_required" ]; then
                die "conflict on parameter \"$name\": value required: \"$value_required\", value existing: \"$value_existing\""
            fi
        fi
    done
fi

#   install or upgrade all packages
verbose 2 "install or upgrade all OpenPKG packages"
DIFS="$IFS"
OIFS="$IFS"; IFS="
"
for spec in $CFG_PACKAGES; do
    #   determine information
    pkg=`echo "$spec"    | sed -e 's;^\([^ ]*\).*$;\1;'`
    opts=`echo "$spec"   | sed -e 's;^[^ ]*;;' -e 's;^ *;;'`
    name=`echo "$pkg"    | sed -e 's;^\(.*\)-[^-][^-]*-[^-][^-]*$;\1;'`
    version=`echo "$pkg" | sed -e 's;^.*-\([^-][^-]*\)-[^-][^-]*$;\1;'`
    release=`echo "$pkg" | sed -e 's;^.*-[^-][^-]*-\([^-][^-]*\)$;\1;'`

    if [ ".$name" = .openpkg ]; then
        #
        #   OpenPKG Framework
        #
        if [   ! -f "$outdir/$pkg.$platform-$tag.sh" \
            -o ! -f "$outdir/$pkg.$platform-$tag.rpm" ]; then
            #   build (bootstrap & regular)
            verbose 3 "BOOTSTRAP-BUILD: $srcdir/$pkg.src.sh"
            verbose 4 "options: $CFG_FRAMEWORK"
            if [ ! -f "$srcdir/$pkg.src.sh" ]; then
                die "source shell package not existing: $srcdir/$pkg.src.sh"
            fi
            (   TMPDIR="$tmpdir"
                srcdir="`cd $srcdir && pwd`"
                cd "$outdir" || exit $?
                cmd="sh \"$srcdir/$pkg.src.sh\" $CFG_FRAMEWORK"
                if [ $opt_v -lt 4 ]; then cmd="$cmd >/dev/null 2>&1"; fi
                eval "$cmd" || \
                    die "failed to build package \"$srcdir/$pkg.src.sh\""
                rm -f $pkg.src.rpm
            ) || exit $?
        fi
        if [ ! -x "$prefix/bin/openpkg" ]; then
            #   install (bootstrap)
            if [ ! -f "$outdir/$pkg.$platform-$tag.sh" ]; then
                die "binary shell package not existing: $outdir/$pkg.$platform-$tag.sh"
            fi
            verbose 3 "BOOTSTRAP-INSTALL: $outdir/$pkg.$platform-$tag.sh"
            cmd="$opt_S sh \"$outdir/$pkg.$platform-$tag.sh\""
            if [ $opt_v -lt 4 ]; then cmd="$cmd >/dev/null 2>&1"; fi
            eval "$cmd" || \
                die "failed to install package \"$outdir/$pkg.$platform-$tag.sh\""
        else
            check=`$prefix/bin/openpkg rpm -q openpkg 2>/dev/null`
            if [ ".$check" != ".$pkg" ]; then
                #   upgrade (regular)
                if [ ! -f "$outdir/$pkg.$platform-$tag.rpm" ]; then
                    die "binary RPM package not existing: $outdir/$pkg.$platform-$tag.rpm"
                fi
                verbose 3 "UPGRADE: $outdir/$pkg.$platform-$tag.rpm"
                rpm_opt="-U"; if [ $opt_v -ge 4 ]; then rpm_opt="${rpm_opt}vh"; fi
                $opt_S $prefix/bin/openpkg rpm $rpm_opt \
                    $outdir/$pkg.$platform-$tag.rpm || \
                    die "failed to upgrade package: $outdir/$pkg.$platform-$tag.rpm"
            else
                #   keep as-is
                verbose 3 "KEEP AS-IS: $pkg @ $prefix"
            fi
        fi
    else
        #
        #   OpenPKG Packages
        #
        if [ ! -x "$prefix/bin/openpkg" ]; then
            #   usually will not happen because of correctly
            #   generated topologically correct package list
            die "command \"$prefix/bin/openpkg\" has to exit for building a regular OpenPKG package" 1>&2
        fi
        if [ ! -f $outdir/$pkg.$platform-$tag.rpm ]; then
            #    build (regular)
            if [ ! -f "$srcdir/$pkg.src.rpm" ]; then
                die "source RPM package not existing: $srcdir/$pkg.src.rpm"
            fi
            verbose 3 "BUILD: $srcdir/$pkg.src.rpm"
            if [ ".$opts" != . ]; then
                verbose 4 "options: $opts"
            fi
            rm -rf "$tmpdir/$pkg" >/dev/null 2>&1 || true
            sh $shtool mkdir -p -m 755 "$tmpdir/$pkg" >/dev/null 2>&1
            cmd="$prefix/bin/openpkg --keep-privileges"
            cmd="$cmd rpm --rebuild"
            cmd="$cmd --define \"_specdir   $tmpdir/$pkg\""
            cmd="$cmd --define \"_sourcedir $tmpdir/$pkg\""
            cmd="$cmd --define \"_builddir  $tmpdir/$pkg\""
            cmd="$cmd --define \"_tmppath   $tmpdir/$pkg\""
            cmd="$cmd --define \"_srcrpmdir $tmpdir/$pkg\""
            cmd="$cmd --define \"_rpmdir    $outdir\""
            OIFS2="$IFS"; IFS="$DIFS"
            for opt in $opts; do
                opt_name=`echo "$opt" | sed -e 's;^\([^=]*\)=.*$;\1;'`
                opt_value=`echo "$opt" | sed -e 's;^[^=]*=\(.*\)$;\1;'`
                cmd="$cmd --define \"$opt_name $opt_value\""
            done
            IFS="$OIFS2"
            cmd="$cmd $srcdir/$pkg.src.rpm"
            if [ $opt_v -lt 4 ]; then cmd="$cmd >/dev/null 2>&1"; fi
            eval "$cmd" || \
                die "failed to build package \"$srcdir/$pkg.src.rpm\""
        fi
        check=`$prefix/bin/openpkg rpm -q "$name" 2>/dev/null`
        if [ ".$check" = ".package $name is not installed" ]; then
            #   install (regular)
            if [ ! -f "$outdir/$pkg.$platform-$tag.rpm" ]; then
                die "binary RPM package not existing: $outdir/$pkg.$platform-$tag.rpm"
            fi
            verbose 3 "INSTALL: $outdir/$pkg.$platform-$tag.rpm"
            rpm_opt="-i"; if [ $opt_v -ge 4 ]; then rpm_opt="${rpm_opt}vh"; fi
            $opt_S $prefix/bin/openpkg rpm $rpm_opt \
                $outdir/$pkg.$platform-$tag.rpm || \
                die "failed to install package: $outdir/$pkg.$platform-$tag.rpm"
        elif [ ".$check" != ".$pkg" ]; then
            #   upgrade (regular)
            if [ ! -f "$outdir/$pkg.$platform-$tag.rpm" ]; then
                die "binary RPM package not existing: $outdir/$pkg.$platform-$tag.rpm"
            fi
            verbose 3 "UPGRADE: $outdir/$pkg.$platform-$tag.rpm"
            rpm_opt="-U"; if [ $opt_v -ge 4 ]; then rpm_opt="${rpm_opt}vh"; fi
            $opt_S $prefix/bin/openpkg rpm $rpm_opt \
                $outdir/$pkg.$platform-$tag.rpm || \
                die "failed to upgrade package: $outdir/$pkg.$platform-$tag.rpm"
        else
            #   keep as-is
            verbose 3 "KEEP AS-IS: $pkg @ $prefix"
        fi
    fi
done
IFS="$OIFS"

#   optionally remove residue packages from binary RPM package directory
if [ $opt_c -lt 3 ]; then
    verbose 2 "cleanup target directory from residue packages"
    for pkg in $outdir/*.sh $outdir/*.rpm; do
        if [ ! -f "$pkg" ]; then
            continue
        fi
        pkg_base=`echo "$pkg" | sed -e 's;^.*/\([^/][^/]*-[0-9][^/-]*-[0-9][0-9.]*\)\.[^/][^/]*$;\1;'`
        erase=yes
        for spec in $CFG_PACKAGES; do
            spec_base=`echo "$spec" | sed -e 's;^\(.*-[^-][^-]*-[^-][^-]*\).*$;\1;'`
            if [ ".$pkg_base" = ".$spec_base" ]; then
                erase=no
                break
            fi
        done
        if [ ".$erase" = .yes ]; then
            verbose 3 "REMOVE: $pkg"
            rm -f "$pkg"
        fi
    done
fi

#   optionally remove residue packages from OpenPKG instance
if [ $opt_c -lt 3 ]; then
    verbose 2 "cleanup OpenPKG instance from residue packages"
    for pkg1 in `$prefix/bin/openpkg rpm -qa | egrep -v '^(openpkg-[0-9]|gpg-pubkey-)'`; do
        name1=`echo "$pkg1" | sed -e 's;^\(.*\)-[^-][^-]*-[^-][^-]*$;\1;'`
        erase=yes
        for spec in $CFG_PACKAGES; do
            name2=`echo "$spec" | sed -e 's;^\(.*\)-[^-][^-]*-[^-][^-]*.*$;\1;'`
            if [ ".$name1" = ".$name2" ]; then
                erase=no
                break
            fi
        done
        if [ ".$erase" = .yes ]; then
            verbose 3 "REMOVE: pkg1 @ $prefix"
            $opt_S $prefix/bin/openpkg rpm -e "$pkg1" || \
                die "failed to erase residue package \"$pkg1\""
        fi
    done
fi

#   execute epilog script
if [ ".@HAVE_EPILOG@" = .yes ]; then
    verbose 2 "execute epilog script"
    cat >$tmpdir/epilog.sh <<EOT
#!/bin/sh
#![OpenPKG]
# %epilog
EOT
    $opt_S sh $tmpdir/epilog.sh
    rm -f $tmpdir/epilog.sh
fi

#   cleanup
rm -f  $shtool >/dev/null 3>&1 || true
rm -rf $tmpdir >/dev/null 2>&1 || true

exit 0

## SHTOOL ###################################################################

