#!/bin/sh
##
##  rpmtool -- RPM Auxiliary Tool
##  Copyright (c) 2000-2003 Cable & Wireless Deutschland GmbH
##  Copyright (c) 2000-2003 The OpenPKG Project <http://www.openpkg.org/>
##  Copyright (c) 2000-2003 Ralf S. Engelschall <rse@engelschall.com>
##
##  Permission to use, copy, modify, and distribute this software for
##  any purpose with or without fee is hereby granted, provided that
##  the above copyright notice and this permission notice appear in all
##  copies.
##
##  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.
##

prog_name="rpmtool"
prog_vers="1.0.2"
prog_date="03-Aug-2001"

if [ $# -eq 0 ]; then
    echo "$0:Error: invalid command line" 1>&2
    echo "$0:Hint:  run \`$0 -h' for usage" 1>&2
    exit 1
fi
if [ ".$1" = ".-h" -o ".$1" = ".--help" ]; then
    echo "This is $prog_name, version $prog_vers ($prog_date)"
    echo "Copyright (c) 2000-2003 Ralf S. Engelschall <rse@engelschall.com>"
    echo ''
    echo "Usage: $prog_name [<options>] [<cmd-name> [<cmd-options>] [<cmd-args>]]" 
    echo ''
    echo 'Available global <options>:'
    echo '  -d, --debug     enable internal debugging'
    echo '  -v, --version   display version information'
    echo '  -h, --help      display usage help page (this one)'
    echo ''
    echo 'Available <cmd-name> [<cmd-options>] [<cmd-args>]:'
    echo '  platform' 
    echo '  flags  [-m] [-c] [-O] <tool>'
    echo '  files  [-v] [-o <outfile>] [-r <buildroot>] [<filelist>]'
    echo '  user   [-c] [-d] [-p <passwd>] [-n <realname>] [-d <homedir>]'
    echo '         [-s <shell>] [-u <min-uid>] <username>'
    echo '  group  [-c] [-d] <groupname> <min-gid> [<username> ...]'
    echo '  signal [-v] [-t] [-n] [-c] [-d <delay>] [-p <pid>] [-m <pattern>] <sig> [<sig> ...]'
    echo '  config [-v] [-s] [-a] [-r] [-b <ext>] [-p <tagprefix>] [-t <tagname>] [-i <tagid>] <file>'
    echo '  msg    [-b] [-t <type>]'
    echo ''
    exit 0
fi
if [ ".$1" = ".-v" -o ".$1" = ."--version" ]; then
    echo "$prog_name $prog_vers ($prog_date)"
    exit 0
fi
if [ ".$1" = ".-d" -o ".$1" = ."--debug" ]; then
    shift
    set -x
fi

tool="$1"
shift
arg_spec=""
opt_spec=""
gen_tmpfile=no

#   configure tool specific option parsing
case $tool in
    platform )
        str_usage=""
        arg_spec="0="
        opt_spec=""
        ;;
    flags )
        str_usage="[-m] [-c] [-O] <tool>"
        arg_spec="1="
        opt_spec="m.c.O."
        opt_m=no
        opt_c=no
        opt_O=no
        ;;
    files )
        gen_tmpfile=yes
        str_usage="[-v] [-o <outfile>] [-r <buildroot>] [<filelist>]"
        arg_spec="0+"
        opt_spec="v.o:r:"
        opt_v=no
        opt_o=''
        opt_r=''
        ;;
    user )
        str_usage="[-a] [-r] [-p <passwd>] [-n <realname>] [-d <homedir>] [-s <shell>] [-u <min-uid>] <username>"
        arg_spec="1="
        opt_spec="a.r.p:n:d:s:u:"
        opt_a=no
        opt_r=no
        opt_p=''
        opt_n=''
        opt_d=''
        opt_s=''
        opt_u=1000
        ;;
    group )
        str_usage="[-a] [-r] [-g <min-gid>] <groupname> [<username> ...]"
        arg_spec="1+"
        opt_spec="a.r.g:"
        opt_a=no
        opt_r=no
        opt_g=1000
        ;;
    signal )
        str_usage="[-v] [-t] [-n] [-c] [-d <delay>] [-p <pid>] [-m <pattern>] <sig> [<sig> ...]"
        arg_spec="1+"
        opt_spec="v.t.n.c.d:p:m:"
        opt_v=no
        opt_t=no
        opt_n=no
        opt_c=no
        opt_d=1
        opt_p=""
        opt_m=""
        ;;
    config )
        str_usage="[-v] [-s] [-a] [-r] [-b <ext>] [-p <tagprefix>] [-t <tagname>] [-i <tagid>] <file>"
        arg_spec="1="
        opt_spec="v.s.a.r.b:p:t:i:c:"
        opt_v=no
        opt_s=no
        opt_a=no
        opt_r=no
        opt_b=""
        opt_p="#"
        opt_t="OpenPKG"
        opt_i=""
        gen_tmpfile=yes
        ;;
    msg )
        str_usage="[-b] [-t <type>]"
        arg_spec="0="
        opt_spec="b."
        opt_spec="b.t:"
        opt_b=no
        opt_t=info
        ;;
    -* )
        echo "$prog_name:Error: unknown option \`$tool'" 2>&1
        echo "$prog_name:Hint:  run \`$0 -h' for usage" 2>&1
        exit 1
        ;;
    * )
        echo "$prog_name:Error: unknown command \`$tool'" 2>&1
        echo "$prog_name:Hint:  run \`$0 -h' for usage" 2>&1
        exit 1
        ;;
esac

#   tool information
toolcmd="$0 $tool"
toolcmdhelp="$prog_name $tool"
msgprefix="$prog_name:$tool"

#   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 h.$opt_spec |\
      sed -e 's/\([a-zA-Z0-9]\)\([.:+]\)/opt_MODE_\1=\2;/g'`

#   iterate 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";/' \
                          -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" = . -a ".$opt_ARG_OK" != .yes ]; then
        if [ ".$opt_MODE" = ".:" -o ".$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 "$msgprefix:Error: unknown option: \`-$opt_OPT'" 1>&2
            echo "$msgprefix:Hint:  run \`$toolcmdhelp -h' or \`man $prog_name' for details" 1>&2
            exit 1
            ;;
    esac
done
if [ ".$opt_PREV" != . ]; then
    echo "$msgprefix:Error: missing argument to option \`-$opt_PREV'" 1>&2
    echo "$msgprefix:Hint:  run \`$toolcmdhelp -h' or \`man $prog_name' for details" 1>&2
    exit 1
fi

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

#   complain about incorrect number of arguments
case $arg_MODE in
    '=' )
        if [ $# -ne $arg_NUMS ]; then
            echo "$msgprefix:Error: invalid number of arguments (exactly $arg_NUMS expected)" 1>&2
            echo "$msgprefix:Hint:  run \`$toolcmd -h' or \`man $prog_name' for details" 1>&2
            exit 1
        fi
        ;;
    '+' )
        if [ $# -lt $arg_NUMS ]; then
            echo "$msgprefix:Error: invalid number of arguments (at least $arg_NUMS expected)" 1>&2
            echo "$msgprefix:Hint:  run \`$toolcmd -h' or \`man $prog_name' for details" 1>&2
            exit 1
        fi
        ;;
esac

#   establish a temporary file on request
if [ ".$gen_tmpfile" = .yes ]; then
    if [ ".$TMPDIR" != . ]; then
        tmpdir="$TMPDIR"
    elif [ ".$TEMPDIR" != . ]; then
        tmpdir="$TEMPDIR"
    else
        tmpdir="/tmp"
    fi
    tmpfile="$tmpdir/.$prog_name.$$"
    rm -f $tmpfile >/dev/null 2>&1
    touch $tmpfile
fi

#   provide a few useful variables
NL="
"
TAB="	"
DIFS=" ${TAB}${NL}"

#   determine platform information
platform_machine=`(uname -m) 2>/dev/null` ||\
platform_machine=`(uname -p) 2>/dev/null` ||\
platform_machine='unknown'
if [ ".$platform_machine" = ".Power Macintosh" ]; then      
    platform_machine=`(uname -p) 2>/dev/null`
fi
platform_release=`(uname -r) 2>/dev/null` ||\
platform_release='unknown'
platform_system=`(uname -s) 2>/dev/null` ||\
platform_system='unknown'
platform_version=`(uname -v) 2>/dev/null` ||\
platform_version='unknown'
platform="${platform_machine}:${platform_system}:${platform_release}:${platform_version}"

#   dispatch into tools
case $tool in
    platform )
        #    provide unique platform id
        m=`echo "$platform_machine" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
        s=`echo "$platform_system"  | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
        r=`echo "$platform_release" | sed -e 's;-.*$;;'`
        echo "${m}-${s}${r}"
        ;;

    flags )
        tool="$1"
        if [ ".$opt_m" = .yes ]; then
            isgmake=no
            if [ ".`$tool -v -f /dev/null 2>/dev/null | grep 'GNU Make'`" != . ]; then
                isgmake=yes
            fi
            mflags=''
            if [ ".$isgmake" = .yes ]; then
                mflags="--no-print-directory"
            fi
            if [ ".$opt_O" = .yes ]; then
                case $platform in
                    *:SunOS:5.*:* | *:OSF1:*:* )
                        if [ ".$isgmake" = .yes ]; then
                            n=`(/bin/uname -X) 2>/dev/null | grep -i NumCPU | awk '{ print $3; }'`
                            if [ ".$n" != . ]; then
                                if [ $n -gt 1 ]; then
                                    n=`expr $n \* 2`
                                    mflags="$mflags -j$n"
                                fi
                            fi
                        fi
                        ;;
                    *:FreeBSD:4.*:* )
                        n=`/sbin/sysctl hw.ncpu | awk '{ print $2; }'`
                        if [ ".$n" != . ]; then
                            if [ $n -gt 1 ]; then
                                n=`expr $n \* 2`
                                mflags="$mflags -j$n"
                                if [ ".$isgmake" = .no ]; then
                                    mflags="$mflags -B"
                                fi
                            fi
                        fi
                        ;;
                    *:NetBSD:1.5*:* )
                        n=`/sbin/sysctl hw.ncpu | awk '{ print $3; }'`
                        if [ ".$n" != . ]; then
                            if [ $n -gt 1 ]; then
                                n=`expr $n \* 2`
                                mflags="$mflags -j$n"
                                if [ ".$isgmake" = .no ]; then
                                    mflags="$mflags -B"
                                fi
                            fi
                        fi
                        ;;
                    *:Linux:*:* )
                        n=`cat /proc/cpuinfo | grep processor | wc -l | awk '{ print $1; }'`
                        if [ ".$n" != . ]; then
                            if [ $n -gt 1 ]; then
                                n=`expr $n \* 2`
                                mflags="$mflags -j$n"
                            fi
                        fi
                        ;;
                    *:HP-UX:*:* )
                        n=1 # FIXME
                        ;;
                    *:IRIX64:6.*:* )
                        if [ ".$isgmake" = .yes ]; then
                            n=`/usr/sbin/sysconf | awk '/NUM_PROCESSORS/ { print $2; }'`
                            if [ ".$n" != . ]; then
                                if [ $n -gt 1 ]; then
                                    n=`expr $n \* 2`
                                    mflags="$mflags -j$n"
                                fi
                            fi
                        fi
                        ;;
                    *:Darwin:*:* )
                        n=`/usr/sbin/sysctl hw.ncpu | awk '{ print $3; }'`
                        if [ ".$n" != . ]; then
                            if [ $n -gt 1 ]; then
                                n=`expr $n \* 2`
                                mflags="$mflags -j$n"
                                if [ ".$isgmake" = .no ]; then
                                    mflags="$mflags -B"
                                fi
                            fi
                        fi
                        ;;
                esac
            fi
            echo "$mflags"
        elif [ ".$opt_c" = .yes ]; then
            isgcc=no
            if [ ".`$tool --version 2>/dev/null | egrep '(2.[7-9]|3.[0-3])'`" != . ]; then
                isgcc=yes
            fi
            cflags=''
            if [ ".$opt_O" = .yes ]; then
                if [ ".$isgcc" = .yes ]; then
                    cflags="-O2 -pipe"
                else
                    cflags="-O"
                fi
            fi
            echo "$cflags"
        fi
        ;;
 
     files )
        #   if a Perl interpreter is available, we perform the operation
        #   with it because Perl is a magnitude (factor 10!) faster than
        #   what we can do here in Bourne-Shell.
        perl=''
        for dir in `echo $PATH | sed -e 's/:/ /g'` .; do
            [ ".$dir" = .. -o ".$dir" = . ] && continue
            for tool in perl5 perl miniperl; do
                if [ -f "$dir/$tool" ]; then
                    perl="$dir/$tool"
                    break
                fi
            done
            if [ ".$perl" != . ]; then
                break
            fi
        done
        if [ ".$perl" != . ]; then
            cat >$tmpfile <<'EOT'
            ##
            ##  PERL IMPLEMENTATION (FAST)
            ##

            my $opt_v = 0;
            my $opt_o = '';
            my $opt_r = '';
            while ($ARGV[0] =~ m|^-(.)(.*)$|) {
                my ($opt, $arg) = ($1, $2);
                shift(@ARGV);
                unshift(@ARGV, $arg)  if ($arg ne '');
                $opt_v = 1            if ($opt eq 'v');
                $opt_o = shift(@ARGV) if ($opt eq 'o');
                $opt_r = shift(@ARGV) if ($opt eq 'r');
            }

            #   remember the build root in a reasonable short variable ;)
            my $br = "$opt_r";

            #   read input file list
            my @L = ();
            if ($#ARGV == -1 or ($#ARGV == 0 and $ARGV[0] eq "-")) { 
                #    read file list from stdin
                while (<STDIN>) {
                    s/\n$//s;
                    push(@L, $_);
                }
            }
            else {
                #    read file list from argument line
                @L = @ARGV;
            }

            #   PASS 1: PREPARATION AND SYNTACTICAL EXPANSION
            if ($opt_v == 1) {
                print STDERR "rpmtool:files: pass 1 (preparation and syntactical expansions)\n";
            }
            my @T = ();
            my $l;
            foreach $l (@L) {
                #   replace tabs with spaces, reduce multiple spaces to single
                #   spaces, and remove leading and trailing spaces
                $l =~ s|[ \t\n]+| |sg;
                $l =~ s|^[ \t]+||sg;
                $l =~ s|[ \t]+$||sg;

                #   move tags to end of entry for easier parsing
                1 while ($l =~ s|^(%\S+) (.+)|$2 $1|);

                #   use `:' as a dummy path for tag-only entries (like `%defattr') 
                #   to avoid special cases in the later processing
                if ($l =~ m|^%.+|) {
                    $l = ": $l";
                }

                #   split entry into path and optional tags
                my ($p, $t) = ($l =~ m|^(\S+)(.*)$|);

                #   expand `{foo,bar,quux}' constructs in path (silent
                #   assumtion to make life easier is that the constructs
                #   occurs only once in a path!)
                if ($p =~ m|^(.*)\{([^\}]+)\}(.*)$|) {
                    #   split path into prolog, the set construct, and the epilog
                    my ($pro, $set, $epi) = ($1, $2, $3);

                    #   repeat prolog and epilog for all elements in set
                    my $x;
                    foreach $x (split(/,/, $set)) {
                        push(@T, "${pro}${x}${epi}${t}");
                    }
                }
                else {
                    #   else just take over the entry as is
                    push(@T, $l);
                }
            }
            @L = @T;

            #   PASS 2: FILESYSTEM-BASED EXPANSIONS
            if ($opt_v == 1) {
                print STDERR "rpmtool:files: pass 2 (filesystem-based expansions)\n";
            }
            @T = ();
            foreach $l (@L) {
                #   split entry into path and optional tags
                my ($p, $t) = ($l =~ m|^(\S*)(.*)$|);

                #   expand...
                if (-d "$br$p" and $t !~ m|.*%dir.*|) {
                    #   expand path because it is not tagged with %dir
                    my @X = `cd "$br$p" && find . -print`;
                    my $x;
                    foreach $x (@X) {
                        $x =~ s|\n$||s;
                        $x =~ s|^\.|$p|s;
                        push(@T, "${x}${t}");
                    }
                }
                else {
                    #   expand path wildcards (`*' and `[..]')
                    #   (if not wildcards are present, this doesn't harm)
                    my @X = glob("$br$p");
                    my $x;
                    foreach $x (@X) {
                        my $brqm = quotemeta($br);
                        $x =~ s|^$brqm||s;
                        push(@T, "${x}${t}");
                    }
                }
            }
            @L = @T;

            #   PASS 3: DUPLICATION REMOVAL AND CLEANUP
            if ($opt_v == 1) {
                print STDERR "rpmtool:files: pass 3 (duplication removal and cleanup)\n";
            }
            @T = ();
            foreach $l (@L) {
                #   split entry into path and optional tags
                my ($p, $t) = ($l =~ m|^(\S*)(.*)$|);

                #   add %dir tag if entry is a directory, but still not
                #   tagged as such (else RPM would again expand it recursively)
                if (-d "$br$p") {
                    if ($t !~ m|%dir|) {
                        $t .= " %dir";
                    }
                }

                #   remove duplicate entries in already processed part
                #   (but make sure we keep the entries with the dummy tags)
                if ($p ne ":") {
                    my @T2 = ();
                    foreach $t (@T) {
                        my $pqm = quotemeta($p);
                        next if ($t =~ m|^$pqm | or $t eq $p);
                        push(@T2, $t);
                    }
                    @T = @T2;
                }

                #   keep entry only if no %not tag is present
                #   (else all entries are removed)
                if ($t !~ m|.*%not.*|) {
                    push(@T, "${p}${t}");
                }
            }
            @L = @T;

            #   write out new file list
            @T = ();
            foreach $l (@L) {
                $l =~ s|^(\S+) (.*)$|$2 $1|sg;
                $l =~ s|^\s+||s;
                $l =~ s|\s+$||s;
                $l =~ s|\s+:$||s;
                push(@T, $l);
            }
            @L = @T;
            if ($opt_o eq '' or $opt_o eq '-') {
                print STDOUT join("\n", @L)."\n";
            }
            else {
                open(FP, ">$opt_o"); 
                print FP join("\n", @L)."\n";
                close(FP);
            }
EOT
            cmd="$perl $tmpfile";
            if [ ".$opt_v" = .yes ]; then
                cmd="$cmd -v"
            fi
            if [ ".$opt_o" != . ]; then
                cmd="$cmd -o \"$opt_o\""
            fi
            if [ ".$opt_r" != . ]; then
                cmd="$cmd -r \"$opt_r\""
            fi
            for arg
            do
                cmd="$cmd \"$arg\""
            done
            eval "$cmd"
        else
            ##
            ##  BOURNE-SHELL IMPLEMENTATION (PORTABLE)
            ##

            #   remember the build root in a reasonable short variable ;)
            br="$opt_r"
     
            #   make sure filename expansion is disabled per default
            set -f
     
            #    read input file list
            L=''
            if [ $# -eq 0 ] || [ $# -eq 1 -a ".$1" = ".-" ]; then
                #    read file list from stdin
                L=`cat`
            else
                #    read file list from argument line
                for arg
                do
                    L="$L$NL$arg"
                done
            fi
     
            #   PASS 1: PREPARATION AND SYNTACTICAL EXPANSION
            if [ ".$opt_v" = .yes ]; then
                echo "rpmtool:files: pass 1 (preparation and syntactical expansions)" 1>&2
            fi
            T=''
            OIFS="$IFS"; IFS="$NL"
            for l in $L; do
                [ ".$l" = . ] && continue
     
                #   replace tabs with spaces, reduce multiple spaces to single
                #   spaces, and remove leading and trailing spaces
                l=`echo "$l" | sed -e "s;${TAB}; ;g" -e 's;  *; ;g' -e 's;^ *;;' -e 's; *$;;'`
     
                #   move tags to end of entry for easier parsing
                while [ ".`echo \"$l\" | grep '^%[^ ]* .*'`" != . ]; do
                    l=`echo "$l" | sed -e 's;^\(%[^ ]*\) \(.*\);\2 \1;'`
                done
     
                #   use `:' as a dummy path for tag-only entries (like `%defattr') 
                #   to avoid special cases in the later processing
                if [ ".`echo \"$l\" | grep '^%.*'`" != . ]; then
                    l=": $l"
                fi
     
                #   split entry into path and optional tags
                eval `echo ".$l" | sed -e 's;^\.\([^ ]*\)\(.*\)$;p="\1" t="\2";'`
     
                #   expand `{foo,bar,quux}' constructs in path (silent
                #   assumtion to make life easier is that the constructs
                #   occurs only once in a path!)
                if [ ".`echo \".$p\" | grep '^\..*{[^}]*}.*$'`" != . ]; then
     
                    #   split path into prolog, the set construct, and the epilog
                    eval `echo ".$p" | sed -e 's;^\.\(.*\){\([^}]*\)}\(.*\)$;pro="\1" set="\2" epi="\3";'`
     
                    #   repeat prolog and epilog for all elements in set
                    OIFS2="$IFS"; IFS=","
                    for x in $set; do
                        T="$T$NL${pro}${x}${epi}${t}"
                    done
                    IFS="$OIFS2"
                else
                    #   else just take over the entry as is
                    T="$T$NL$l"
                fi
            done
            L="$T"
            IFS="$OIFS"
     
            #   PASS 2: FILESYSTEM-BASED EXPANSIONS
            if [ ".$opt_v" = .yes ]; then
                echo "rpmtool:files: pass 2 (filesystem-based expansions)" 1>&2
            fi
            T=''
            OIFS="$IFS"; IFS="$NL"
            for l in $L; do
                [ ".$l" = . ] && continue
     
                #   split entry into path and optional tags
                eval `echo ".$l" | sed -e 's;^\.\([^ ]*\)\(.*\)$;p="\1" t="\2";'`
     
                #   expand...
                if [ -d "$br$p" -a ".`expr \".$t\" : '\..*%dir.*'`" = .0 ]; then
                    #   expand path because it is not tagged with %dir
                    OIFS2="$IFS"; IFS="$DIFS"
                    for x in `cd "$br$p" && find . -print | sed -e "s;^\\.;$p;"`; do
                        T="$T$NL${x}${t}"
                    done
                    IFS="$OIFS2"
                else
                    #   expand path wildcards (`*' and `[..]')
                    #   (if not wildcards are present, this doesn't harm)
                    OIFS2="$IFS"; IFS="$DIFS"
                    for x in `(set +f; echo $br$p) | sed -e "s;^$br;;" -e "s; $br; ;g"`; do
                        T="$T$NL${x}${t}"
                    done
                    IFS="$OIFS2"
                fi
            done
            IFS="$OIFS"
            L="$T"
     
            #   PASS 3: DUPLICATION REMOVAL AND CLEANUP
            if [ ".$opt_v" = .yes ]; then
                echo "rpmtool:files: pass 3 (duplication removal and cleanup)" 1>&2
            fi
            T=''
            OIFS="$IFS"; IFS="$NL"
            for l in $L; do
                [ ".$l" = . ] && continue
     
                #   split entry into path and optional tags
                eval `echo ".$l" | sed -e 's;^\.\([^ ]*\)\(.*\)$;p="\1" t="\2";'`
     
                #   add %dir tag if entry is a directory, but still not
                #   tagged as such (else RPM would again expand it recursively)
                if [ -d "$br$p" ]; then
                    if [ ".`expr \".$t\" : '\..*%dir.*'`" = .0 ]; then
                        t="$t %dir"
                    fi
                fi
     
                #   remove duplicate entries in already processed part
                #   (but make sure we keep the entries with the dummy tags)
                if [ ".$p" != ".:" ]; then
                    T=`echo "$T" | grep -v "^$p " | grep -v "^$p\$"`
                fi
     
                #   keep entry only if no %not tag is present
                #   (else all entries are removed)
                if [ ".`expr \".$t\" : '\..*%not.*'`" = .0 ]; then
                    T="$T$NL${p}${t}"
                fi
            done
            IFS="$OIFS"
            L="$T"
     
            #   write out new file list
            if [ ".$opt_o" = . -o ".$opt_o" = ".-" ]; then
                outcmd="cat"
            else
                outcmd="cat > $opt_o"
            fi
            echo "$L" |\
            sed -e '/^$/d' \
                -e 's;^\([^ ]*\) *\(.*\)$;\2 \1;g' \
                -e 's;^ *;;' \
                -e 's; *$;;' \
                -e 's; *:$;;' | eval $outcmd
        fi
        ;;
 
    user )
        #   calling convention
        if [ ".$opt_a" = .no -a ".$opt_r" = .no ]; then
            echo "$msgprefix:Error: either -a or -r has to be given" 1>&2
            exit 1
        fi
        if [ ".$opt_a" = .yes -a ".$opt_r" = .yes ]; then
            echo "$msgprefix:Error: only -a or -r can be given" 1>&2
            exit 1
        fi
        user=$1

        #   default values
        if [ ".$opt_n" = . ]; then
            opt_n="$user"
        fi
        if [ ".$opt_s" = . ]; then
            opt_s=`which false 2>/dev/null`
            if [ ".$opt_s" = . ]; then
                opt_s="/bin/false"
            fi
        fi
        if [ ".$opt_d" = . ]; then
            opt_d="/nowhere"
        fi

        #   add user
        if [ ".$opt_a" = .yes ]; then
            case "$platform" in
                *:Darwin:*:* )
                    exists=`(cat /etc/passwd; nidump passwd .) 2>/dev/null | grep "^$user:"`
                    ;;
                * )
                    exists=`grep "^$user:" /etc/passwd`
                    ;;
            esac
            if [ ".$exists" != . ]; then
                echo "$msgprefix:Error: user $user already exists" 1>&2
                exit 1
            fi

            #   seek for next free uid
            uid=$opt_u
            ok=0
            while [ ".$ok" = .0 ]; do
                case "$platform" in
                    *:Darwin:*:* )
                        u_exists=`(cat /etc/passwd; nidump passwd .) 2>/dev/null | grep "^[^:]*:[^:]*:$uid:"`
                        g_exists=`(cat /etc/group;  nidump group  .) 2>/dev/null | grep "^[^:]*:[^:]*:$uid:"`
                        ;;
                    * )
                        u_exists=`grep "^[^:]*:[^:]*:$uid:" /etc/passwd`
                        g_exists=`grep "^[^:]*:[^:]*:$uid:" /etc/group`
                        ;;
                esac
                if [ ".$u_exists" = . -a ".$g_exists" = . ]; then
                    ok=1
                    break
                fi
                uid=`expr $uid + 1`
            done

            #   add entry to passwd database
            case "$platform" in
                *:FreeBSD:[456].*:* | *:NetBSD:1.*:* )
                    echo "${user}:*:${uid}:${uid}::0:0:${opt_n}:${opt_d}:${opt_s}" >>/etc/master.passwd
                    (PATH="$PATH:/usr/sbin"; pwd_mkdb -p /etc/master.passwd)
                    ;;
                *:Linux:2.*:* )
                    echo "${user}:*:${uid}:${uid}::${opt_n}:${opt_d}:${opt_s}" >>/etc/passwd
                    (PATH="$PATH:/usr/sbin"; pwconv)
                    ;;
                *:SunOS:5.*:* )
                    echo "${user}:*:${uid}:${uid}:${opt_n}:${opt_d}:${opt_s}" >>/etc/passwd
                    (PATH="$PATH:/usr/sbin"; pwconv)
                    ;;
                *:OSF1:5.*:* )
                    echo "${user}:*:${uid}:${uid}:${opt_n}:${opt_d}:${opt_s}" >>/etc/passwd
                    (PATH="$PATH:/usr/sbin"; mkpasswd /etc/passwd)
                    ;;
                *:HP-UX:*:* )
                    echo "${user}:*:${uid}:${uid}:${opt_n}:${opt_d}:${opt_s}" >>/etc/passwd
                    ;;
                *:IRIX64:6.*:* )
                    echo "${user}:*:${uid}:${uid}:${opt_n}:${opt_d}:${opt_s}" >>/etc/passwd
                    (PATH="$PATH:/usr/sbin"; pwconv)
                    ;;
                *:Darwin:*:* )
                    niutil -create     . "/users/${user}"
                    niutil -createprop . "/users/${user}" passwd   "*"
                    niutil -createprop . "/users/${user}" uid      "${uid}"
                    niutil -createprop . "/users/${user}" gid      "${uid}"
                    niutil -createprop . "/users/${user}" realname "${opt_n}"
                    niutil -createprop . "/users/${user}" home     "${opt_d}"
                    niutil -createprop . "/users/${user}" shell    "${opt_s}"
                    ;;
            esac

            #   add corresponding entry to group database
            case "$platform" in
                *:Darwin:*:* )
                    niutil -create     . "/groups/${user}"
                    niutil -createprop . "/groups/${user}" gid   "${uid}"
                    niutil -createprop . "/groups/${user}" users "${user}"
                    ;;
                * )
                    echo "${user}:*:${uid}:${user}" >>/etc/group
                    ;;
            esac
 
        #   remove user
        elif [ ".$opt_r" = .yes ]; then
            case "$platform" in
                *:Darwin:*:* )
                    exists=`(cat /etc/passwd; nidump passwd .) 2>/dev/null | grep "^$user:"`
                    ;;
                * )
                    exists=`grep "^$user:" /etc/passwd`
                    ;;
            esac
            if [ ".$exists" = . ]; then
                echo "$msgprefix:Error: user $user does not exist" 1>&2
                exit 1
            fi
 
            #   remove entry from passwd database
            case "$platform" in
                *:FreeBSD:[456].*:* | *:NetBSD:1.*:* )
                    cp /etc/master.passwd /etc/master.passwd.old
                    grep -v "^${user}:" /etc/master.passwd.old >/etc/master.passwd
                    (PATH="$PATH:/usr/sbin"; pwd_mkdb -p /etc/master.passwd)
                    rm -f /etc/master.passwd.old
                    ;;
                *:Linux:2.*:* | *:SunOS:5.*:* )
                    cp /etc/passwd /etc/passwd.old
                    grep -v "^${user}:" /etc/passwd.old >/etc/passwd
                    (PATH="$PATH:/usr/sbin"; pwconv)
                    rm -f /etc/passwd.old
                    ;;
                *:OSF1:5.*:* )
                    cp /etc/passwd /etc/passwd.old
                    grep -v "^${user}:" /etc/passwd.old >/etc/passwd
                    (PATH="$PATH:/usr/sbin"; mkpasswd /etc/passwd)
                    rm -f /etc/passwd.old
                    ;;
                *:HP-UX:*:* )
                    cp /etc/passwd /etc/passwd.old
                    grep -v "^${user}:" /etc/passwd.old >/etc/passwd
                    rm -f /etc/passwd.old
                    ;;
                *:IRIX64:6.*:* )
                    cp /etc/passwd /etc/passwd.old
                    grep -v "^${user}:" /etc/passwd.old >/etc/passwd
                    (PATH="$PATH:/usr/sbin"; pwconv)
                    rm -f /etc/passwd.old
                    ;;
                *:Darwin:*:* )
                    niutil -destroy . "/users/${user}"
                    ;;
            esac

            #   remove corresponding entry from group database
            case "$platform" in
                *:Darwin:*:* )
                    niutil -destroy . "/groups/${user}"
                    ;;
                * )
                    cp /etc/group /etc/group.old
                    grep -v "^${user}:" /etc/group.old >/etc/group
                    rm -f /etc/group.old
                    ;;
            esac
        fi
        ;;
 
    group )
        if [ ".$opt_a" = .no -a ".$opt_r" = .no ]; then
            echo "$msgprefix:Error: either -a or -r has to be given" 1>&2
            exit 1
        fi
        if [ ".$opt_a" = .yes -a ".$opt_r" = .yes ]; then
            echo "$msgprefix:Error: only -a or -r can be given" 1>&2
            exit 1
        fi
        group=$1

        #   add group
        if [ ".$opt_a" = .yes ]; then
            case "$platform" in
                *:Darwin:*:* )
                    exists=`(cat /etc/group; nidump group .) 2>/dev/null | grep "^$user:"`
                    ;;
                * )
                    exists=`grep "^$group:" /etc/group`
                    ;;
            esac
            if [ ".$exists" != . ]; then
                echo "$msgprefix:Error: group $group already exists" 1>&2
                exit 1
            fi

            #   seek for next free gid
            gid=$opt_g
            ok=0
            while [ ".$ok" = .0 ]; do
                case "$platform" in
                    *:Darwin:*:* )
                        exists=`(cat /etc/group; nidump group .) 2>/dev/null | grep "^[^:]*:[^:]*:$gid:"`
                        ;;
                    * )
                        exists=`grep "^[^:]*:[^:]*:$gid:" /etc/group`
                        ;;
                esac
                if [ ".$exists" = . ]; then
                    ok=1
                    break
                fi
                gid=`expr $gid + 1`
            done

            #   add corresponding entry to group database
            case "$platform" in
                *:Darwin:*:* )
                    niutil -create     . "/groups/${group}"
                    niutil -createprop . "/groups/${group}" gid   "${gid}"
                    niutil -createprop . "/groups/${group}" users "${group}"
                    ;;
                * )
                    echo "${group}:*:${gid}:$*" >>/etc/group
                    ;;
            esac

        #   remove group
        elif [ ".$opt_r" = .yes ]; then
            case "$platform" in
                *:Darwin:*:* )
                    exists=`(cat /etc/group; nidump group .) 2>/dev/null | grep "^$group:"`
                    ;;
                * )
                    exists=`grep "^$group:" /etc/group`
                    ;;
            esac
            if [ ".$exists" = . ]; then
                echo "$msgprefix:Error: group $group does not exist" 1>&2
                exit 1
            fi

            #   remove entry from group database
            case "$platform" in
                *:Darwin:*:* )
                    niutil -destroy . "/groups/${group}"
                    ;;
                * )
                    cp /etc/group /etc/group.old
                    grep -v "^${group}:" /etc/group.old >/etc/group
                    rm -f /etc/group.old
                    ;;
            esac
        fi
        ;;

    signal )
        if [ ".$opt_p" = . -a ".$opt_m" = . ]; then
            echo "$msgprefix:Error: either option -p or -m has to be specified" 1>&2
            exit 1
        fi
        case "$platform" in
            *:FreeBSD:*:* | *:NetBSD:*:* )
                cmd0="ps -ax -o pid" 
                cmd1="ps -ax -o command" 
                cmd2="ps -ax -o pid,command" 
                cmd3="ps -ax -o pid,ppid" 
                ;;
            *:Linux:*:* | *:OSF1:5.*:* )
                cmd0="ps axo pid" 
                cmd1="ps axo cmd" 
                cmd2="ps axo pid,cmd" 
                cmd3="ps axo pid,ppid" 
                ;;
            *:SunOS:5.*:* )
                cmd0="ps -ef -o pid" 
                cmd1="ps -ef -o args" 
                cmd2="ps -ef -o pid,args" 
                cmd3="ps -ef -o pid,ppid" 
                ;;
            *:HP-UX:*:* )
                cmd0="ps -ef" # FIXME 
                cmd1="ps -ef" # FIXME
                cmd2="ps -ef" # FIXME
                cmd3="ps -ef" # FIXME
                ;;
            *:IRIX64:6.*:* )
                cmd0="ps -ef -o pid" 
                cmd1="ps -ef -o args" 
                cmd2="ps -ef -o pid,args" 
                cmd3="ps -ef -o pid,ppid" 
                ;;
            *:Darwin:*:* )
                cmd0="ps -ax -opid"
                cmd1="ps -ax -ocommand"
                cmd2="ps -ax -opid,command"
                cmd3="ps -ax -opid,ppid"
        esac
        #   try all signals in order   
        i=$#
        for sig in "$@"; do
            #   check whether program is still running/active
            active=""
            if [ ".$opt_p" != . ]; then
                active=`$cmd0 | grep "$opt_p" | grep -v grep | grep -v rpmtool`
            elif [ ".$opt_m" != . ]; then
                active=`$cmd1 | grep "$opt_m" | grep -v grep | grep -v rpmtool`
            fi
            if [ ".$active" = . ]; then
                break
            fi

            #  send signal to program
            if [ ".$opt_p" != . ]; then
                pids="$opt_p"
            elif [ ".$opt_m" != . ]; then
                pids=`$cmd2 | sed -e "s;^[ ${TAB}]*;;" | egrep "[0-9]* .*$opt_m" |\
                      grep -v grep | grep -v rpmtool |\
                      awk '{ printf("%s\n", $1); }'`
            fi
            for pid in $pids; do
                if [ ".$opt_v" = .yes ]; then
                    echo "sending $sig signal to process $pid"
                fi
                if [ ".$opt_t" = .yes ]; then
                    echo "kill -$sig $pid"
                fi
                if [ ".$opt_n" = .no ]; then
                    eval "kill -$sig $pid"
                fi
            done

            #  optionally send signal to childs of program
            if [ ".$opt_c" = .yes ]; then
                for pid in $pids; do
                    cpids=`$cmd3 | sed -e "s;^[ ${TAB}]*;;" | egrep "[0-9]* $pid" |\
                           grep -v grep | grep -v rpmtool |\
                           awk '{ printf("%s\n", $1); }'`
                    for cpid in $cpids; do
                        if [ ".$opt_v" = .yes ]; then
                            echo "sending $sig signal to child process $cpid"
                        fi
                        if [ ".$opt_t" = .yes ]; then
                            echo "kill -$sig $cpid"
                        fi
                        if [ ".$opt_n" = .no ]; then
                            eval "kill -$sig $cpid >/dev/null 2>&1"
                        fi
                    done
                done
            fi
         
            #   perform optional delay
            i=`expr $i - 1`
            if [ $i -gt 0 ]; then
                if [ ".$opt_d" != . -a ".$opt_d" != . ]; then
                    sleep $opt_d
                fi
            fi
        done
        ;;

    config )
        #   usage consistency
        if [ ".$opt_a" = .no -a ".$opt_r" = .no ]; then
            echo "$msgprefix:Error: either option -a or -r has to be specified" 1>&2
            exit 1
        fi
        configfile="$1"

        #   determine block markers
        block_start="${opt_p}<${opt_t}"
        if [ ".$opt_i" != . ]; then
            block_start="${block_start} id=\"${opt_i}\""
        fi
        block_start="${block_start}>"
        block_end="${opt_p}</${opt_t}>"

        #   slash-escaped versions of block markers (for sed(3) call)
        block_start_esc=`echo "$block_start" | sed -e 's;/;\\\\/;g'`
        block_end_esc=`echo "$block_end" | sed -e 's;/;\\\\/;g'`

        #   determine backup extension
        case "X$opt_b" in
            X   ) ext=".bak"    ;;
            X.* ) ext="$opt_b"  ;;
            X*  ) ext=".$opt_b" ;;
        esac

        #   check for block in config file
        if [ -f $configfile ]; then
            check=`grep "^${block_start}" $configfile`
        else
            touch $configfile
            check=""
        fi

        #   add entry
        if [ ".$opt_a" = .yes ]; then
            if [ ".$check" != . ]; then
                if [ ".$opt_s" = .yes ]; then
                    exit 0
                else
                    echo "$msgprefix:Error: config entry already exists" 1>&2
                    exit 1
                fi
            fi
            cp $configfile $configfile$ext
            echo "${block_start}" >$tmpfile
            cat >>$tmpfile
            echo "${block_end}" >>$tmpfile
            cat $tmpfile >>$configfile

        #   remove entry
        elif [ ".$opt_r" = .yes ]; then
            if [ ".$check" = . ]; then
                if [ ".$opt_s" = .yes ]; then
                    exit 0
                else
                    echo "$msgprefix:Error: config entry does not exist" 1>&2
                    exit 1
                fi
            fi
            cp $configfile $configfile$ext
            sed -e "/^${block_start_esc}/,/^${block_end_esc}/d" \
                <$configfile$ext >$configfile
        fi

        #   verbosity
        if [ ".$opt_v" = .yes ]; then
            (diff -u1 $configfile$ext $configfile >$tmpfile) 2>/dev/null
            if [ ".`cat $tmpfile`" = . ]; then
                (diff -C1 $configfile$ext $configfile >$tmpfile) 2>/dev/null
                if [ ".`cat $tmpfile`" = . ]; then
                    if [ ".$opt_s" = .no ]; then
                        echo "$msgprefix:Warning: unable to show difference for config file \`$configfile'" 1>&2
                    fi
                fi
            fi
            echo "editing $configfile:"
            cat $tmpfile
        fi

        #   optionally remove backup file
        if [ ".$opt_b" = . ]; then
            rm -f $configfile$ext
        fi
        ;;

    msg )
        #   optionally beep before message
        if [ ".$opt_b" = .yes ]; then
            echo . | awk '{ printf("%c", 7); }'
        fi
        title=""
        case $opt_t in
            info   ) title="-------" ;;
            notice ) title="Notice-" ;;
            warn   ) title="Warning" ;;
            error  ) title="Error--" ;;
            *      ) echo "$msgprefix:Error: invalid message type \`$opt_t'" 1>&2; exit 1 ;;
        esac
        
        #   display message
        echo . | awk '{ printf("\r"); }'
        echo "+----------------------------------${title}------------------------------------+"
        expand -8 | sed -e 's; *$;;' | awk '{ printf("| %-75s |\n", substr($0, 0, 75)); }'
        echo "+-----------------------------------------------------------------------------+"
        ;;
esac

#   cleanup
if [ ".$gen_tmpfile" = .yes ]; then
    rm -f $tmpfile >/dev/null 2>&1
fi

#   die gracefully ;)
exit 0

