openpkg/dev.pl

changeset 428
f880f219c566
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/openpkg/dev.pl	Tue Jul 31 12:23:42 2012 +0200
     1.3 @@ -0,0 +1,905 @@
     1.4 +##
     1.5 +##  openpkg dev -- OpenPKG Package Development Tool
     1.6 +##  Copyright (c) 2008-2012 OpenPKG GmbH <http://openpkg.com/>
     1.7 +##
     1.8 +##  This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
     1.9 +##  All rights reserved. Licenses which grant limited permission to use,
    1.10 +##  copy, modify and distribute this software are available from the
    1.11 +##  OpenPKG GmbH.
    1.12 +##
    1.13 +##  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
    1.14 +##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    1.15 +##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    1.16 +##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
    1.17 +##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.18 +##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.19 +##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    1.20 +##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    1.21 +##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    1.22 +##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    1.23 +##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    1.24 +##  SUCH DAMAGE.
    1.25 +##
    1.26 +
    1.27 +require 5;
    1.28 +
    1.29 +#   OpenPKG instance prefix and RPM
    1.30 +my $openpkg_prefix = $ENV{'OPENPKG_PREFIX'};
    1.31 +delete $ENV{'OPENPKG_PREFIX'};
    1.32 +
    1.33 +#   program identification
    1.34 +my $prog_name  = "dev";
    1.35 +my $prog_vers  = "20100111";
    1.36 +
    1.37 +#   home-brewn getopt(3) style option parser
    1.38 +sub getopts ($$@) {
    1.39 +    my ($opt_spec, $opts, @argv_orig) = @_;
    1.40 +    my (%optf) = map { m/(\w)/; $1 => $_ } $opt_spec =~ m/(\w:|\w)/g;
    1.41 +    my (@argv, $optarg);
    1.42 +
    1.43 +    foreach (@argv_orig) {
    1.44 +        if (@argv) {
    1.45 +            push @argv, $_;
    1.46 +        } elsif (defined $optarg) {
    1.47 +            if (exists $opts->{$optarg}) {
    1.48 +                $opts->{$optarg} .= " $_";
    1.49 +            } else {
    1.50 +                $opts->{$optarg} = $_;
    1.51 +            }
    1.52 +            $optarg = undef;
    1.53 +        } elsif (!/^[-]/) {
    1.54 +            push @argv, $_;
    1.55 +        } else {
    1.56 +            while (/^\-(\w)(.*)/) {
    1.57 +                if (exists $optf{$1}) {
    1.58 +                    if (length($optf{$1}) > 1) {
    1.59 +                        if ($2 ne '') {
    1.60 +                            if (exists $opts->{$1}) {
    1.61 +                                $opts->{$1} .= " $2";
    1.62 +                            } else {
    1.63 +                                $opts->{$1} = $2;
    1.64 +                            }
    1.65 +                        } else {
    1.66 +                            $optarg = $1;
    1.67 +                        }
    1.68 +                        last;
    1.69 +                    } else {
    1.70 +                        $opts->{$1} = 1;
    1.71 +                    }
    1.72 +                } else {
    1.73 +                    warn "openpkg:$prog_name:WARNING: unknown option $_\n";
    1.74 +                }
    1.75 +                $_ = "-$2";
    1.76 +            }
    1.77 +        }
    1.78 +    }
    1.79 +    if (defined $optarg) {
    1.80 +        warn "openpkg:$prog_name:WARNING: option $optarg requires an argument\n";
    1.81 +    }
    1.82 +    foreach my $opt (keys %optf) {
    1.83 +        if (not exists $opts->{$opt}) {
    1.84 +            $opts->{$opt} = (length($optf{$opt}) > 1 ? "" : 0);
    1.85 +        }
    1.86 +    }
    1.87 +    return @argv;
    1.88 +}
    1.89 +
    1.90 +#   determine reasonable temporary directory
    1.91 +my $tmpdir = ($ENV{"TMPDIR"} || "/tmp");
    1.92 +$tmpdir .= "/openpkg-$prog_name-$$";
    1.93 +my $rc = system("umask 022; $openpkg_prefix/lib/openpkg/shtool mkdir -f -p -m 755 $tmpdir || exit $?");
    1.94 +if ($rc != 0) {
    1.95 +    die "failed to create temporary directory: $tmpdir";
    1.96 +}
    1.97 +
    1.98 +#   parse command line options
    1.99 +my $opts = {};
   1.100 +@ARGV = getopts("h", $opts, @ARGV);
   1.101 +
   1.102 +#   usage sanity check and usage help
   1.103 +sub usage {
   1.104 +    my ($rc) = @_;
   1.105 +    my $usage =
   1.106 +        "openpkg:$prog_name:USAGE: openpkg dev <command> [<options>]\n";
   1.107 +    if ($rc == 0) {
   1.108 +        print STDOUT $usage;
   1.109 +    }
   1.110 +    else {
   1.111 +        print STDERR $usage;
   1.112 +    }
   1.113 +    exit($rc);
   1.114 +}
   1.115 +if ($opts->{"h"}) {
   1.116 +    usage(0);
   1.117 +}
   1.118 +if (@ARGV == 0) {
   1.119 +    usage(1);
   1.120 +}
   1.121 +
   1.122 +#   command map
   1.123 +my $map = {
   1.124 +    "unpack "            => { -alias => qr/^(up|ex)$/,   -key => "x" },
   1.125 +    "edit\n"             => { -alias => qr/^(ed|vi)$/,   -key => "v" },
   1.126 +    "build -s track\n"   => { -alias => qr/^(bt|tr)$/,   -key => "t" },
   1.127 +    "build -s fetch\n"   => { -alias => qr/^(bf|fe)$/,   -key => "f" },
   1.128 +    "build\n"            => { -alias => qr/^(ba|bu)$/,   -key => "b" },
   1.129 +    "build -s prep\n"    => { -alias => qr/^bp$/,        -key => "1" },
   1.130 +    "build -s compile\n" => { -alias => qr/^bc$/,        -key => "2" },
   1.131 +    "build -s install\n" => { -alias => qr/^bi$/,        -key => "3" },
   1.132 +    "build -s binary\n"  => { -alias => qr/^bb$/,        -key => "4" },
   1.133 +    "build -s source\n"  => { -alias => qr/^bs$/,        -key => "s" },
   1.134 +    "peek\n"             => { -alias => qr/^pe$/,        -key => "p" },
   1.135 +    "diff\n"             => { -alias => qr/^di$/,        -key => "d" },
   1.136 +    "install\n"          => { -alias => qr/^in(?:st)?$/, -key => "i" },
   1.137 +    "erase\n"            => { -alias => qr/^er$/,        -key => "e" },
   1.138 +    "lint\n"             => { -alias => qr/^li$/,        -key => "l" },
   1.139 +    "release "           => { -alias => qr/^rel?$/,      -key => "r" },
   1.140 +};
   1.141 +
   1.142 +#   dispatch command
   1.143 +my $cmd = shift(@ARGV);
   1.144 +foreach my $c (keys %{$map}) {
   1.145 +    my $a = $map->{$c}->{-alias};
   1.146 +    if ($cmd =~ $a) {
   1.147 +        $c =~ s/\n$//;
   1.148 +        my @args = split(/\s+/, $c);
   1.149 +        $cmd = shift(@args);
   1.150 +        unshift(@ARGV, @args);
   1.151 +        print STDOUT "\033[34m\$ opd $cmd " . join(" ", map {
   1.152 +            my $x = $_;
   1.153 +            $x =~ s/"/\\"/g;
   1.154 +            $x =~ s/^(.*[ \t].*)$/"$1"/;
   1.155 +            $x
   1.156 +        } @ARGV) . "\033[0m\n";
   1.157 +    }
   1.158 +}
   1.159 +my $func = "cmd_$cmd";
   1.160 +if (not defined(&$func)) {
   1.161 +    print STDERR "openpkg:$prog_name:ERROR: invalid command \"$cmd\"\n";
   1.162 +    usage(1);
   1.163 +}
   1.164 +my $rc = &$func($opts, @ARGV);
   1.165 +exit($rc);
   1.166 +
   1.167 +#   execute a shell command
   1.168 +sub run {
   1.169 +    my ($cmd) = @_;
   1.170 +    print STDOUT "\033[34m\$ $cmd\033[0m\n";
   1.171 +    my $rc = system($cmd);
   1.172 +    return $rc;
   1.173 +}
   1.174 +
   1.175 +#   determine path to package specification
   1.176 +sub specfile {
   1.177 +    my $specfile = (glob("*.spec"))[0];
   1.178 +    if (not defined $specfile) {
   1.179 +        $specfile = (glob("src/*.spec"))[0];
   1.180 +        if (not defined $specfile) {
   1.181 +            $specfile = (glob("*/*.spec"))[0];
   1.182 +            if (not defined $specfile) {
   1.183 +                print STDERR "openpkg:$prog_name:ERROR: unable to determine package specification file\n";
   1.184 +                exit(1);
   1.185 +            }
   1.186 +        }
   1.187 +    }
   1.188 +    return $specfile;
   1.189 +}
   1.190 +
   1.191 +#   command: interactive shell
   1.192 +sub cmd_shell ($@) {
   1.193 +    my ($gopts, @argv) = @_;
   1.194 +    my $rc = 0;
   1.195 +
   1.196 +    #   command line argument processing
   1.197 +    my $lopts = {};
   1.198 +    @argv = getopts("s", $lopts, @argv);
   1.199 +
   1.200 +    #   determine release
   1.201 +    my $release = `$openpkg_prefix/bin/openpkg release -F '%t'`;
   1.202 +    $release =~ s/\n$//s;
   1.203 +
   1.204 +    #   create dot files
   1.205 +    my $screenrc = "$tmpdir/dot.screenrc";
   1.206 +    my $bashsh   = "$tmpdir/bash.sh";
   1.207 +    my $bashrc   = "$tmpdir/dot.bashrc";
   1.208 +    my $inputrc  = "$tmpdir/dot.inputrc";
   1.209 +    unlink("$screenrc");
   1.210 +    unlink("$bashsh");
   1.211 +    unlink("$bashrc");
   1.212 +    unlink("$inputrc");
   1.213 +    my $title = "openpkg dev";
   1.214 +    $title .= '%? %{= rW} %n %{-}%?';
   1.215 +    $title .= ' %=%-w%{= rW}%50>%n %t%{-}%+w';
   1.216 +    my $screenrc_data = "";
   1.217 +    $screenrc_data .= "source \"$ENV{HOME}/.screenrc\"\n" if (-f "$ENV{HOME}/.screenrc");
   1.218 +    $screenrc_data .=
   1.219 +        "escape          ^Aa\n" .
   1.220 +        "startup_message off\n" .
   1.221 +        "vbell           off\n" .
   1.222 +        "defscrollback   10000\n" .
   1.223 +        "defmonitor      off\n" .
   1.224 +        "msgminwait      1\n" .
   1.225 +        "msgwait         1\n" .
   1.226 +        "hardstatus      alwayslastline \" $title \"\n" .
   1.227 +        "shell           $bashsh\n";
   1.228 +    my $bashsh_data =
   1.229 +        "##\n" .
   1.230 +        "##\  bash.sh -- \"openpkg dev shell\" GNU bash wrapper script\n" .
   1.231 +        "##\n" .
   1.232 +        "\n" .
   1.233 +        "OPENPKG_PREFIX=\"$openpkg_prefix\"\n" .
   1.234 +        "export OPENPKG_PREFIX\n" .
   1.235 +        "INPUTRC=\"$inputrc\"\n" .
   1.236 +        "export INPUTRC\n" .
   1.237 +        "SHELL=\"$openpkg_prefix/lib/openpkg/bash\"\n" .
   1.238 +        "export SHELL\n" .
   1.239 +        "eval `$openpkg_prefix/bin/openpkg rpm --eval 'T=\"\%{_tmppath}\"; S=\"\%{_specdir}\"; D=\"\%{_sourcedir}\"'`\n" .
   1.240 +        "export T S D\n" .
   1.241 +        "\$SHELL --rcfile $bashrc";
   1.242 +    my $bashrc_data =
   1.243 +        "##\n" .
   1.244 +        "##  dot.bashrc -- \"openpkg dev shell\" GNU bash configuration\n" .
   1.245 +        "##\n" .
   1.246 +        "\n" .
   1.247 +        "#   load user's standard bash run-command script\n" .
   1.248 +        "if [ -f ~/.bashrc ]; then\n" .
   1.249 +        "    . ~/.bashrc\n" .
   1.250 +        "fi\n" .
   1.251 +        "\n" .
   1.252 +        "#   configure a special command-line prompt\n" .
   1.253 +        "if [ \".\$TERM\" = .screen ]; then\n" .
   1.254 +        "    PS1=\"\\\\u@\\\\h:\\\\w [P=\\\\e[1m\\\${OPENPKG_PREFIX}\\\\e[0m]\\n\\\\\\\$ \\\\ek\\\\W\\\\e\\\\\\\\\"\n" .
   1.255 +        "else\n" .
   1.256 +        "    PS1=\"\\\\u@\\\\h:\\\\w [P=\\\\e[1m\\\${OPENPKG_PREFIX}\\\\e[0m]\\n\\\\\\\$ \"\n" .
   1.257 +        "fi\n" .
   1.258 +        "alias openpkg=\"\\\${OPENPKG_PREFIX}/bin/openpkg\"\n" .
   1.259 +        "alias opd=\"\\\${OPENPKG_PREFIX}/bin/openpkg dev\"\n" .
   1.260 +        "\n";
   1.261 +    my $inputrc_data =
   1.262 +        "##\n" .
   1.263 +        "##  dot.inputrc -- \"openpkg dev shell\" GNU readline configuration\n" .
   1.264 +        "##\n" .
   1.265 +        "\n";
   1.266 +    foreach my $c (keys %{$map}) {
   1.267 +        my $k = $map->{$c}->{-key};
   1.268 +        $c =~ s/\n/\\n/sg;
   1.269 +        $inputrc_data .= "\"\\e$k\": \"opd $c\"\n";
   1.270 +    }
   1.271 +    $inputrc_data .= "\n";
   1.272 +    open(SCREENRC, ">$screenrc");
   1.273 +    print(SCREENRC $screenrc_data);
   1.274 +    close(SCREENRC);
   1.275 +    open(BASHSH, ">$bashsh");
   1.276 +    print(BASHSH $bashsh_data);
   1.277 +    close(BASHSH);
   1.278 +    system("chmod a+x $bashsh");
   1.279 +    open(BASHRC, ">$bashrc");
   1.280 +    print(BASHRC $bashrc_data);
   1.281 +    close(BASHRC);
   1.282 +    open(INPUTRC, ">$inputrc");
   1.283 +    print(INPUTRC $inputrc_data);
   1.284 +    close(INPUTRC);
   1.285 +
   1.286 +    #   run interactive shell
   1.287 +    print STDOUT "\033[34m++ entering OpenPKG $release development shell\033[0m\n";
   1.288 +    my $rc;
   1.289 +    if ($lopts->{"s"}) {
   1.290 +        $rc = system("screen -c $screenrc -S 'openpkg-dev-shell' -d -R");
   1.291 +    }
   1.292 +    else {
   1.293 +        $rc = system("$bashsh");
   1.294 +    }
   1.295 +
   1.296 +    #   cleanup
   1.297 +    unlink("$tmpdir/dot.screenrc");
   1.298 +    unlink("$tmpdir/bash.sh");
   1.299 +    unlink("$tmpdir/dot.bashrc");
   1.300 +    unlink("$tmpdir/dot.inputrc");
   1.301 +    unlink("$tmpdir");
   1.302 +
   1.303 +    return $rc;
   1.304 +}
   1.305 +
   1.306 +#   command: unpack source RPM
   1.307 +sub cmd_unpack ($@) {
   1.308 +    my ($gopts, @argv) = @_;
   1.309 +    my $rc = 0;
   1.310 +
   1.311 +    #   command line argument processing
   1.312 +    my $lopts = {};
   1.313 +    @argv = getopts("l:b:sd", $lopts, @argv);
   1.314 +    if ($lopts->{"l"} eq "") {
   1.315 +        $lopts->{"l"} = "structured";
   1.316 +    }
   1.317 +    if ($lopts->{"l"} !~ m/^(global|local|simple|structured|distributed)$/) {
   1.318 +        die sprintf("invalid layout type \"%s\"", $lopts->{"l"});
   1.319 +    }
   1.320 +    if (@argv != 1) {
   1.321 +        die sprintf("exactly one SRPM has to be given");
   1.322 +    }
   1.323 +    my $srpm = $argv[0];
   1.324 +    if (not -f $srpm) {
   1.325 +        die sprintf("SRPM \"%s\" has to be a regular file", $srpm);
   1.326 +    }
   1.327 +    my $name = `$openpkg_prefix/bin/openpkg rpm -qp --qf '\%{NAME}' $srpm 2>/dev/null || true`;
   1.328 +    $name =~ s/\r?\n$//s;
   1.329 +    if ($name eq '') {
   1.330 +        die sprintf("unable to determine package name from SRPM \"%s\"", $srpm);
   1.331 +    }
   1.332 +    if ($lopts->{"b"} eq "") {
   1.333 +        $lopts->{"b"} = $srpm;
   1.334 +        if ($lopts->{"b"} =~ m/\.src\.rpm$/) {
   1.335 +            $lopts->{"b"} =~ s/\.src\.rpm$//;
   1.336 +        }
   1.337 +        else {
   1.338 +            my $subdir = `$openpkg_prefix/bin/openpkg rpm -qp --qf '\%{NAME}-\%{VERSION}-\%{RELEASE}' $srpm 2>/dev/null || true`;
   1.339 +            $lopts->{"b"} = $subdir;
   1.340 +        }
   1.341 +    }
   1.342 +
   1.343 +    #   determine result directory
   1.344 +    my $basedir = $lopts->{"b"};
   1.345 +    my ($macrosfile, $specdir, $sourcedir);
   1.346 +    if ($lopts->{"l"} eq "global") {
   1.347 +        $macrosdir  = "$openpkg_prefix/etc/openpkg";
   1.348 +        $macrosfile = "$openpkg_prefix/etc/openpkg/rpmmacros";
   1.349 +        $specdir    = "$openpkg_prefix/RPM/SRC/$name";
   1.350 +        $sourcedir  = "$openpkg_prefix/RPM/SRC/$name";
   1.351 +        $builddir   = "$openpkg_prefix/RPM/TMP";
   1.352 +        $tmpdir     = "$openpkg_prefix/RPM/TMP";
   1.353 +        $binrpmdir  = "$openpkg_prefix/RPM/PKG";
   1.354 +        $srcrpmdir  = "$openpkg_prefix/RPM/PKG";
   1.355 +    }
   1.356 +    elsif ($lopts->{"l"} eq "local") {
   1.357 +        $macrosdir  = "$basedir/.openpkg";
   1.358 +        $macrosfile = "$basedir/.openpkg/rpmmacros";
   1.359 +        $specdir    = "$basedir";
   1.360 +        $sourcedir  = "$basedir";
   1.361 +        $builddir   = "$basedir";
   1.362 +        $tmpdir     = "$basedir";
   1.363 +        $binrpmdir  = "$basedir";
   1.364 +        $srcrpmdir  = "$basedir";
   1.365 +    }
   1.366 +    elsif ($lopts->{"l"} eq "simple") {
   1.367 +        $macrosdir  = "$basedir/.openpkg";
   1.368 +        $macrosfile = "$basedir/.openpkg/rpmmacros";
   1.369 +        $specdir    = "$basedir";
   1.370 +        $sourcedir  = "$basedir";
   1.371 +        $builddir   = "$tmpdir";
   1.372 +        $tmpdir     = "$tmpdir";
   1.373 +        $binrpmdir  = "$basedir/..";
   1.374 +        $srcrpmdir  = "$basedir/..";
   1.375 +    }
   1.376 +    elsif ($lopts->{"l"} eq "structured") {
   1.377 +        $macrosdir  = "$basedir/.openpkg";
   1.378 +        $macrosfile = "$basedir/.openpkg/rpmmacros";
   1.379 +        $specdir    = "$basedir/src";
   1.380 +        $sourcedir  = "$basedir/dst";
   1.381 +        $builddir   = "$basedir/tmp";
   1.382 +        $tmpdir     = "$basedir/tmp";
   1.383 +        $binrpmdir  = "$basedir/pkg/bin";
   1.384 +        $srcrpmdir  = "$basedir/pkg/src";
   1.385 +    }
   1.386 +    elsif ($lopts->{"l"} eq "distributed") {
   1.387 +        $macrosdir  = "$basedir/.openpkg";
   1.388 +        $macrosfile = "$basedir/.openpkg/rpmmacros";
   1.389 +        $specdir    = "$basedir/src/$name";
   1.390 +        $sourcedir  = "$basedir/dst/$name";
   1.391 +        $builddir   = "$basedir/tmp";
   1.392 +        $tmpdir     = "$basedir/tmp";
   1.393 +        $binrpmdir  = "$basedir/pkg/bin";
   1.394 +        $srcrpmdir  = "$basedir/pkg/src";
   1.395 +    }
   1.396 +
   1.397 +    #   create still missing directories
   1.398 +    foreach my $dir ($macrosdir, $specdir, $sourcedir, $builddir, $tmpdir, $binrpmdir, $srcrpmdir) {
   1.399 +        if (not -d $dir) {
   1.400 +            print STDOUT "openpkg:$prog_name: creating directory \"$dir\"\n";
   1.401 +            system("$openpkg_prefix/lib/openpkg/shtool mkdir -f -p -m 755 $dir");
   1.402 +        }
   1.403 +    }
   1.404 +
   1.405 +    #   unpack SRPM
   1.406 +    print STDOUT "openpkg:$prog_name: unpacking source: \"$srpm\"\n";
   1.407 +    print STDOUT "openpkg:$prog_name: unpacking target: \"$specdir\"\n";
   1.408 +    print STDOUT "openpkg:$prog_name: unpacking target: \"$sourcedir\"\n";
   1.409 +    my $abs_specdir = $specdir;
   1.410 +    my $abs_sourcedir = $sourcedir;
   1.411 +    my $pwd = `pwd`; $pwd =~ s/\r?\n$//s;
   1.412 +    $abs_specdir   = "$pwd/$abs_specdir"   if ($abs_specdir   !~ m/^\//);
   1.413 +    $abs_sourcedir = "$pwd/$abs_sourcedir" if ($abs_sourcedir !~ m/^\//);
   1.414 +    my $rc = system(
   1.415 +        "$openpkg_prefix/bin/openpkg" .
   1.416 +        " --keep-privileges" .
   1.417 +        " rpm" .
   1.418 +        " -i" .
   1.419 +        " --define '_specdir $abs_specdir'" .
   1.420 +        " --define '_sourcedir $abs_sourcedir'" .
   1.421 +        " $srpm"
   1.422 +    );
   1.423 +
   1.424 +    #   fix location of files
   1.425 +    if (not -f "$specdir/$name.spec") {
   1.426 +        die sprintf("failed to install package \"%s\": file \"%s\" not found\n", $srpm, "$specdir/$name.spec");
   1.427 +    }
   1.428 +    open(SPEC, "<$specdir/$name.spec");
   1.429 +    my $spec = ""; { local $/; $spec = <SPEC>; }
   1.430 +    close(SPEC);
   1.431 +    my $src = {};
   1.432 +    $spec =~ s/^(?:Source|Patch)\d+:\s+(\S+)/$src->{$1} = 1, ''/mgei;
   1.433 +    foreach my $file (keys %{$src}) {
   1.434 +        if ($file !~ m/^(https?|ftp):\/\//) {
   1.435 +            if (not -f "$specdir/$file" and -f "$sourcedir/$file") {
   1.436 +                system("mv $sourcedir/$file $specdir/$file");
   1.437 +            }
   1.438 +        }
   1.439 +    }
   1.440 +
   1.441 +    #   create .openpkg/rpmmacros file
   1.442 +    if (not -f $macrosfile) {
   1.443 +        print STDOUT "openpkg:$prog_name: creating file: \"$macrosfile\"\n";
   1.444 +        my $rpmmacros =
   1.445 +            "##\n" .
   1.446 +            "##  .openpkg/rpmmacros -- local OpenPKG RPM macro definitions\n" .
   1.447 +            "##\n" .
   1.448 +            "\n" .
   1.449 +            "\%openpkg_layout" .
   1.450 +            " macrosfile=\%{macrosfile}" .
   1.451 +            " layout=" . $lopts->{"l"} .
   1.452 +            " shared=" . ($lopts->{"s"} ? "yes" : "no") .
   1.453 +            " debug=" . ($lopts->{"d"} ? "yes" : "no") .
   1.454 +            "\n" .
   1.455 +            "\n";
   1.456 +        open(MACROS, ">$macrosfile");
   1.457 +        print(MACROS $rpmmacros);
   1.458 +        close(MACROS);
   1.459 +    }
   1.460 +
   1.461 +    return $rc;
   1.462 +}
   1.463 +
   1.464 +#   command: edit package specification
   1.465 +sub cmd_edit ($@) {
   1.466 +    my ($gopts, @argv) = @_;
   1.467 +    my $rc = 0;
   1.468 +
   1.469 +    #   run editor
   1.470 +    my $editor = ($ENV{"EDITOR"} || "vi");
   1.471 +    my $specfile = ($argv[0] || specfile());
   1.472 +    run("$editor $specfile");
   1.473 +
   1.474 +    return $rc;
   1.475 +}
   1.476 +
   1.477 +#   command: build package
   1.478 +sub cmd_build ($@) {
   1.479 +    my ($gopts, @argv) = @_;
   1.480 +    my $rc = 0;
   1.481 +
   1.482 +    #   command line argument processing
   1.483 +    my $lopts = {};
   1.484 +    @argv = getopts("s:D:w:", $lopts, @argv);
   1.485 +    if ($lopts->{"s"} eq '') {
   1.486 +        $lopts->{"s"} = "all";
   1.487 +    }
   1.488 +    if ($lopts->{"s"} !~ m/^(track|fetch|prep|compile|install|binary|source|all)$/) {
   1.489 +        die "invalid step";
   1.490 +    }
   1.491 +
   1.492 +    #   assembly defines
   1.493 +    my $defs = "";
   1.494 +    if ($lopts->{"D"}) {
   1.495 +        foreach my $def (split(/\s+/, $lopts->{"D"})) {
   1.496 +            if ($def =~ m/^([^=]+)=(.+)$/) {
   1.497 +                $defs .= "--define '$1 $2' ";
   1.498 +            }
   1.499 +            else {
   1.500 +                $defs .= "--define '$def yes' ";
   1.501 +            }
   1.502 +        }
   1.503 +    }
   1.504 +    if ($lopts->{"w"}) {
   1.505 +        foreach my $def (split(/\s+/, $lopts->{"w"})) {
   1.506 +            $defs .= "--with $def ";
   1.507 +        }
   1.508 +    }
   1.509 +
   1.510 +    #   run build command
   1.511 +    my $specfile = ($argv[0] || specfile());
   1.512 +    if ($lopts->{"s"} eq 'track') {
   1.513 +        run("$openpkg_prefix/bin/openpkg rpm -bt $defs$specfile");
   1.514 +    }
   1.515 +    elsif ($lopts->{"s"} eq 'fetch') {
   1.516 +        run("$openpkg_prefix/bin/openpkg rpm -bf $defs$specfile");
   1.517 +    }
   1.518 +    elsif ($lopts->{"s"} eq 'prep') {
   1.519 +        run("$openpkg_prefix/bin/openpkg rpm -bp $defs$specfile");
   1.520 +    }
   1.521 +    elsif ($lopts->{"s"} eq 'compile') {
   1.522 +        run("$openpkg_prefix/bin/openpkg rpm -bc --short-circuit $defs$specfile");
   1.523 +    }
   1.524 +    elsif ($lopts->{"s"} eq 'install') {
   1.525 +        run("$openpkg_prefix/bin/openpkg rpm -bi --short-circuit $defs$specfile");
   1.526 +    }
   1.527 +    elsif ($lopts->{"s"} eq 'binary') {
   1.528 +        run("$openpkg_prefix/bin/openpkg rpm -bb --short-circuit $defs$specfile");
   1.529 +    }
   1.530 +    elsif ($lopts->{"s"} eq 'source') {
   1.531 +        run("$openpkg_prefix/bin/openpkg rpm -bs $defs$specfile");
   1.532 +    }
   1.533 +    elsif ($lopts->{"s"} eq 'all') {
   1.534 +        run("$openpkg_prefix/bin/openpkg rpm -ba $defs$specfile");
   1.535 +    }
   1.536 +
   1.537 +    return $rc;
   1.538 +}
   1.539 +
   1.540 +#   command: peek into package
   1.541 +sub cmd_peek ($@) {
   1.542 +    my ($gopts, @argv) = @_;
   1.543 +    my $rc = 0;
   1.544 +
   1.545 +    #   run query command
   1.546 +    my $template = `$openpkg_prefix/bin/openpkg rpm --eval '%{_rpmdir}/%{_rpmfilename}'`;
   1.547 +    $template =~ s/\n$//s;
   1.548 +    my $specfile = specfile();
   1.549 +    my $rpm = `$openpkg_prefix/bin/openpkg rpm -q --specfile '$specfile' --qf 'XXX$template'`;
   1.550 +    $rpm =~ s/^XXX//s;
   1.551 +    $rpm =~ s/\n$//s;
   1.552 +
   1.553 +    #   determine files
   1.554 +    print STDOUT "\033[34m++ determining configuration files\033[0m\n";
   1.555 +    my @cfgfiles = split(/\n/, `$openpkg_prefix/bin/openpkg rpm -qplc $rpm`);
   1.556 +    print STDOUT "\033[34m++ determining documentation files\033[0m\n";
   1.557 +    my @docfiles = split(/\n/, `$openpkg_prefix/bin/openpkg rpm -qpld $rpm`);
   1.558 +    print STDOUT "\033[34m++ determining all file information\033[0m\n";
   1.559 +    my @allfiles = split(/\n/, `$openpkg_prefix/bin/openpkg rpm -qplv $rpm`);
   1.560 +
   1.561 +    #   create package file listing
   1.562 +    foreach my $line (@allfiles) {
   1.563 +        $prefix = "";
   1.564 +        foreach my $docfile (@docfiles) {
   1.565 +            if ($line =~ m/\s+$docfile\b/s) {
   1.566 +                $prefix .= "D";
   1.567 +                last;
   1.568 +            }
   1.569 +        }
   1.570 +        foreach my $cfgfile (@cfgfiles) {
   1.571 +            if ($line =~ m/\s+$cfgfile\b/s) {
   1.572 +                $prefix .= "C";
   1.573 +                last;
   1.574 +            }
   1.575 +        }
   1.576 +        $prefix .= "--";
   1.577 +        $prefix = substr($prefix, 0, 2);
   1.578 +        if ($line =~ m/^d/) {
   1.579 +            $line =~ s/(\s+\/\S+)/\033[34m$1\033[0m/s;
   1.580 +        }
   1.581 +        print "$prefix $line\n";
   1.582 +    }
   1.583 +
   1.584 +    return $rc;
   1.585 +}
   1.586 +
   1.587 +#   command: show modifications via VCS
   1.588 +sub cmd_diff ($@) {
   1.589 +    my ($gopts, @argv) = @_;
   1.590 +    my $rc = 0;
   1.591 +
   1.592 +    my $vcs = "";
   1.593 +    my $cmd = "";
   1.594 +    if (-d "CVS") {
   1.595 +        $vcs = "CVS";
   1.596 +        $cmd = "cvs diff -u3";
   1.597 +    }
   1.598 +    elsif (-d ".svn") {
   1.599 +        $vcs = "Subversion";
   1.600 +        $cmd = "svn diff";
   1.601 +    }
   1.602 +    elsif (-d "_MTN"          or -d ".mtn" or
   1.603 +           -d "../_MTN"       or -d "../.mtn" or
   1.604 +           -d "../../_MTN"    or -d "../../.mtn" or
   1.605 +           -d "../../../_MTN" or -d "../../../.mtn") {
   1.606 +        $vcs = "Monotone";
   1.607 +        $cmd = "mtn diff .";
   1.608 +    }
   1.609 +    elsif (-d ".git" or
   1.610 +           -d "../.git" or
   1.611 +           -d "../../.git" or
   1.612 +           -d "../../../.git") {
   1.613 +        $vcs = "Git";
   1.614 +        $cmd = "git diff . ";
   1.615 +    }
   1.616 +    elsif (-d ".hg" or
   1.617 +           -d "../.hg" or
   1.618 +           -d "../../.hg" or
   1.619 +           -d "../../../.hg") {
   1.620 +        $vcs = "Mercurial";
   1.621 +        $cmd = "hg diff ."
   1.622 +    }
   1.623 +    else {
   1.624 +        $vcs = "OSSP svs";
   1.625 +        $cmd = "svs diff";
   1.626 +    }
   1.627 +    print STDOUT "\033[34m++ modifications as known to underlying $vcs VCS\033[0m\n";
   1.628 +    run($cmd);
   1.629 +}
   1.630 +
   1.631 +#   command: install package
   1.632 +sub cmd_install ($@) {
   1.633 +    my ($gopts, @argv) = @_;
   1.634 +    my $rc = 0;
   1.635 +
   1.636 +    #   command line argument processing
   1.637 +    my $lopts = {};
   1.638 +    @argv = getopts("fnos", $lopts, @argv);
   1.639 +
   1.640 +    #   run install command
   1.641 +    my $template = `$openpkg_prefix/bin/openpkg rpm --eval '%{_rpmdir}/%{_rpmfilename}'`;
   1.642 +    $template =~ s/\n$//s;
   1.643 +    my $specfile = specfile();
   1.644 +    my $rpm = `$openpkg_prefix/bin/openpkg rpm -q --specfile '$specfile' --qf 'XXX$template'`;
   1.645 +    $rpm =~ s/^XXX//s;
   1.646 +    $rpm =~ s/\n$//s;
   1.647 +    chdir("/");
   1.648 +    run(($lopts->{"s"} ? "sudo " : "") .
   1.649 +        "$openpkg_prefix/bin/openpkg rpm -Uvh"
   1.650 +        . ($lopts->{"f"} ? " --force" : "")
   1.651 +        . ($lopts->{"n"} ? " --nodeps" : "")
   1.652 +        . ($lopts->{"o"} ? " --oldpackage" : "")
   1.653 +        . " $rpm");
   1.654 +
   1.655 +    return $rc;
   1.656 +}
   1.657 +
   1.658 +#   command: erase package
   1.659 +sub cmd_erase ($@) {
   1.660 +    my ($gopts, @argv) = @_;
   1.661 +    my $rc = 0;
   1.662 +
   1.663 +    #   command line argument processing
   1.664 +    my $lopts = {};
   1.665 +    @argv = getopts("fnas", $lopts, @argv);
   1.666 +
   1.667 +    #   run erase command
   1.668 +    my $specfile = specfile();
   1.669 +    my $name = `$openpkg_prefix/bin/openpkg rpm -q --specfile $specfile --qf '%{NAME}'`;
   1.670 +    $name =~ s/\n$//s;
   1.671 +    chdir("/");
   1.672 +    run(($lopts->{"s"} ? "sudo " : "") .
   1.673 +        "$openpkg_prefix/bin/openpkg rpm -e"
   1.674 +        . ($lopts->{"f"} ? " --force" : "")
   1.675 +        . ($lopts->{"n"} ? " --nodeps" : "")
   1.676 +        . ($lopts->{"a"} ? " --allmatches" : "")
   1.677 +        . " $name");
   1.678 +
   1.679 +    return $rc;
   1.680 +}
   1.681 +
   1.682 +#   command: lint package
   1.683 +sub cmd_lint ($@) {
   1.684 +    my ($gopts, @argv) = @_;
   1.685 +    my $rc = 0;
   1.686 +
   1.687 +    #   command line argument processing
   1.688 +    my $lopts = {};
   1.689 +    @argv = getopts("vb", $lopts, @argv);
   1.690 +
   1.691 +    #   run source linting commands
   1.692 +    my $specfile = specfile();
   1.693 +    my $fslfile = $specfile;
   1.694 +    $fslfile =~ s/([^\/]+)\.spec$/fsl.$1/s;
   1.695 +    my $rcfile = $specfile;
   1.696 +    $rcfile =~ s/([^\/]+)\.spec$/rc.$1/s;
   1.697 +    my $name = `$openpkg_prefix/bin/openpkg rpm -q --specfile $specfile --qf '%{NAME}'`;
   1.698 +    $name =~ s/\n$//s;
   1.699 +    my $template = `$openpkg_prefix/bin/openpkg rpm --eval '%{_rpmdir}/%{_rpmfilename}'`;
   1.700 +    $template =~ s/\n$//s;
   1.701 +    my $rpm = `$openpkg_prefix/bin/openpkg rpm -q --specfile '$specfile' --qf 'XXX$template'`;
   1.702 +    $rpm =~ s/^XXX//s;
   1.703 +    $rpm =~ s/\n$//s;
   1.704 +    my $rc = 0;
   1.705 +    $rc += run("$openpkg_prefix/bin/openpkg lint-spec" . ($lopts->{"v"} ? " --verbose" : "") . " $specfile");
   1.706 +    $rc += run("$openpkg_prefix/bin/openpkg lint-fsl"  . ($lopts->{"v"} ? " --verbose" : "") . " $fslfile") if (-f $fslfile);
   1.707 +    $rc += run("$openpkg_prefix/bin/openpkg lint-rc"   . ($lopts->{"v"} ? " --verbose" : "") . " $rcfile")  if (-f $rcfile);
   1.708 +
   1.709 +    #   optionally run binary linting command
   1.710 +    run("$openpkg_prefix/bin/openpkg lint-rpm" . ($lopts->{"v"} ? " --verbose" : "") . " $rpm") if (-f $rpm and not $lopts->{"b"});
   1.711 +
   1.712 +    return $rc;
   1.713 +}
   1.714 +
   1.715 +#   command: release package
   1.716 +sub cmd_release ($@) {
   1.717 +    my ($gopts, @argv) = @_;
   1.718 +    my $rc = 0;
   1.719 +
   1.720 +    #   command line argument processing
   1.721 +    my $lopts = {};
   1.722 +    @argv = getopts("nm:f", $lopts, @argv);
   1.723 +
   1.724 +    #   implicit linting
   1.725 +    if (not $lopts->{"f"}) {
   1.726 +        my $rc = cmd_lint($gopts);
   1.727 +        if ($rc != 0 and !$lopts->{"f"}) {
   1.728 +            return $rc;
   1.729 +        }
   1.730 +    }
   1.731 +
   1.732 +    #   sanity check environment
   1.733 +    my $cmd = $ENV{"OPENPKG_DEV_RELEASE"}
   1.734 +        || `$openpkg_prefix/bin/openpkg rpm --eval '%{openpkg_dev_release}' 2>/dev/null || true` || "";
   1.735 +    $cmd =~ s/\n$//s;
   1.736 +    if ($cmd eq "") {
   1.737 +        print STDERR "openpkg:$prog_name:ERROR: no \$OPENPKG_DEV_RELEASE command defined\n";
   1.738 +        exit(1);
   1.739 +    }
   1.740 +
   1.741 +    #
   1.742 +    #   determine package information
   1.743 +    #
   1.744 +
   1.745 +    print STDOUT "\033[34m++ determining package information\033[0m\n";
   1.746 +    my $info = {};
   1.747 +    $info->{"openpkg-prefix"} = $openpkg_prefix;
   1.748 +    $info->{"option-force"} = $lopts->{"f"} ? "yes" : "no";
   1.749 +
   1.750 +    print STDOUT "-- determining package name/version/release\n";
   1.751 +    my $specfile = specfile();
   1.752 +    my $result = `$openpkg_prefix/bin/openpkg rpm -q --specfile $specfile --qf '::%{NAME}::%{VERSION}::%{RELEASE}'`;
   1.753 +    $result =~ s/\n$//s;
   1.754 +    $result =~ s/^:://s;
   1.755 +    my @result = split(/::/, $result);
   1.756 +    $info->{"package-name"}    = $result[0];
   1.757 +    $info->{"package-version"} = $result[1];
   1.758 +    $info->{"package-release"} = $result[2];
   1.759 +
   1.760 +    print STDOUT "-- determining package source and distribution location\n";
   1.761 +    my $defines = "--define '\%name " . $info->{"package-name"} . "'";
   1.762 +    $defines .= " --define '\%version " . $info->{"package-version"} . "'";
   1.763 +    $defines .= " --define '\%release " . $info->{"package-release"} . "'";
   1.764 +    my $result = `$openpkg_prefix/bin/openpkg rpm $defines --eval '%{_specdir}::%{_sourcedir}'`;
   1.765 +    $result =~ s/\n$//s;
   1.766 +    @result = split(/::/, $result);
   1.767 +    $info->{"spec-dir"}   = $result[0];
   1.768 +    $info->{"source-dir"} = $result[1];
   1.769 +
   1.770 +    print STDOUT "-- determining package source and binary RPM files\n";
   1.771 +    my $template = `$openpkg_prefix/bin/openpkg rpm --eval '%{_rpmdir}/%{l_binrpmfilename}::%{_srcrpmdir}/%{l_srcrpmfilename}'`;
   1.772 +    $template =~ s/\n$//s;
   1.773 +    $result = `$openpkg_prefix/bin/openpkg rpm -q --specfile $specfile --qf '::$template'`;
   1.774 +    $result =~ s/\n$//s;
   1.775 +    $result =~ s/^:://s;
   1.776 +    @result = split(/::/, $result);
   1.777 +    $info->{"binary-rpm-file"} = $result[0];
   1.778 +    $info->{"source-rpm-file"} = $result[1];
   1.779 +    $info->{"package-version-old"} = "";
   1.780 +    $info->{"package-release-old"} = "";
   1.781 +    $info->{"vcs"} = "none";
   1.782 +
   1.783 +    print STDOUT "-- determining previous package version/release\n";
   1.784 +    my $vcs = "";
   1.785 +    my $diff = "";
   1.786 +    my $content_old = "";
   1.787 +    my $specdir  = $info->{"spec-dir"};
   1.788 +    my $specfile = $info->{"package-name"} . ".spec";
   1.789 +    if (-f "$specdir/$specfile") {
   1.790 +        open(FP, "<$specdir/$specfile");
   1.791 +        $content_new .= $_ while (<FP>);
   1.792 +        close(FP);
   1.793 +        if ($content_new =~ m/\n\%No(?:Source|Patch)\s+/s) {
   1.794 +            $info->{"source-rpm-file"} =~ s/\.src\.rpm$/.nosrc.rpm/s;
   1.795 +        }
   1.796 +        if (-d "$specdir/CVS") {
   1.797 +            #   package is version controlled via CVS
   1.798 +            $vcs = "cvs";
   1.799 +            $diff = `(cd $specdir && cvs diff -u3 $specfile) 2>/dev/null || true`;
   1.800 +        }
   1.801 +        elsif (-d "$specdir/.svn") {
   1.802 +            #   package is version controlled via Subverson (SVN)
   1.803 +            $vcs = "svn";
   1.804 +            $diff = `(cd $specdir && svn diff $specfile) 2>/dev/null || true`;
   1.805 +        }
   1.806 +        elsif (-d "$specdir/_MTN"          or -d "$specdir/.mtn" or
   1.807 +               -d "$specdir/../_MTN"       or -d "$specdir/../.mtn" or
   1.808 +               -d "$specdir/../../_MTN"    or -d "$specdir/../../.mtn" or
   1.809 +               -d "$specdir/../../../_MTN" or -d "$specdir/../../../.mtn") {
   1.810 +            #   package is version controlled via Monotone (MTN)
   1.811 +            $vcs = "mtn";
   1.812 +            $diff = `(cd $specdir && mtn diff $specfile) 2>/dev/null || true`;
   1.813 +            $content_old = `(cd $specdir && mtn automate get_file_of -r h: $specfile) 2>/dev/null || true`;
   1.814 +        }
   1.815 +        elsif (-d "$specdir/.git" or
   1.816 +               -d "$specdir/../.git" or
   1.817 +               -d "$specdir/../../.git" or
   1.818 +               -d "$specdir/../../../.git") {
   1.819 +            #   package is version controlled via Git
   1.820 +            $vcs = "git";
   1.821 +            $diff = `(cd $specdir && git diff $specfile) 2>/dev/null || true`;
   1.822 +        }
   1.823 +        elsif (-d "$specdir/.hg" or
   1.824 +               -d "$specdir/../.hg" or
   1.825 +               -d "$specdir/../../.hg" or
   1.826 +               -d "$specdir/../../../.hg") {
   1.827 +            #   package is version controlled via Mercurial (Hg)
   1.828 +            $vcs = "hg";
   1.829 +            $diff = `(cd $specdir && hg diff $specfile) 2>/dev/null || true`;
   1.830 +        }
   1.831 +        elsif (-f "$specdir/$specfile.orig") {
   1.832 +            #   package is patched via OSSP svs
   1.833 +            $vcs = "svs";
   1.834 +            $diff = `(cd $specdir && svs diff $specfile) 2>/dev/null || true`;
   1.835 +        }
   1.836 +        if ($vcs ne '') {
   1.837 +            $info->{"vcs"} = $vcs;
   1.838 +        }
   1.839 +        if ($content_old ne '') {
   1.840 +            my $define = {};
   1.841 +            $content_old =~ s/\n\%define[ \t]+([^ \t\n]+)[ \t]+([^ \t\n]+)/$define->{$1} = $2, ''/sge;
   1.842 +            $content_old =~ s/\n([^ \t\n]+):[ \t]+([^ \t\n]+)/$define->{lc($1)} = $2, ''/sge;
   1.843 +            sub resolve {
   1.844 +                my ($define, $name) = @_;
   1.845 +                my $value = $define->{$name};
   1.846 +                $value = "" if (not defined $value);
   1.847 +                $value =~ s/\%\{(.+?)\}/resolve($define, $1)/sge;
   1.848 +                return $value;
   1.849 +            }
   1.850 +            $info->{"package-version-old"} = resolve($define, "version");
   1.851 +            $info->{"package-release-old"} = resolve($define, "release");
   1.852 +        }
   1.853 +        if ((   $info->{"package-version-old"} eq ''
   1.854 +             or $info->{"package-release-old"} eq '')
   1.855 +            and $diff ne ''                          ) {
   1.856 +            if ($info->{"package-version-old"} eq '' and $diff =~ m/\n-Version:\s+(\S+)/s) {
   1.857 +                $info->{"package-version-old"} = $1;
   1.858 +            }
   1.859 +            if ($info->{"package-release-old"} eq '' and $diff =~ m/\n-Release:\s+(\S+)/s) {
   1.860 +                $info->{"package-release-old"} = $1;
   1.861 +            }
   1.862 +        }
   1.863 +    }
   1.864 +
   1.865 +    print STDOUT "-- determining commit message\n";
   1.866 +    $info->{"commit-message"} = ($lopts->{"m"} || "");
   1.867 +    if ($info->{"commit-message"} eq "") {
   1.868 +        if ($info->{"package-version-old"} ne $info->{"package-version"}) {
   1.869 +            #   new version
   1.870 +            $info->{"commit-message"} = "upgrading package: " .
   1.871 +                $info->{"package-name"} . " " .
   1.872 +                $info->{"package-version-old"} . " -> " .
   1.873 +                $info->{"package-version"};
   1.874 +        }
   1.875 +        elsif (!$lopts->{"f"}) {
   1.876 +            print STDERR "openpkg:$prog_name:ERROR: package version not changed -- you have to manually provide a commit message\n";
   1.877 +            exit(1);
   1.878 +        }
   1.879 +    }
   1.880 +
   1.881 +    #   run external command
   1.882 +    print STDOUT "\033[34m++ executing openpkg development release program\033[0m\n";
   1.883 +    $cmd .= " %{openpkg-prefix}";
   1.884 +    $cmd .= " %{spec-dir} %{source-dir} %{binary-rpm-file} %{source-rpm-file}";
   1.885 +    $cmd .= " %{package-name} %{package-version} %{package-release} %{package-version-old} %{package-release-old}";
   1.886 +    $cmd .= " %{commit-message} %{vcs}";
   1.887 +    $cmd .= " %{option-force}";
   1.888 +    $cmd =~ s/(\%\{([a-z][a-z-]+)\})/&subst($info, $1, $2)/sge;
   1.889 +    sub subst {
   1.890 +        my ($info, $text, $name) = @_;
   1.891 +        if (exists($info->{$name})) {
   1.892 +            $text = $info->{$name};
   1.893 +            $text =~ s/'/\\'/sg;
   1.894 +            $text = "'$text'";
   1.895 +        }
   1.896 +    }
   1.897 +    if ($lopts->{"n"}) {
   1.898 +        print STDOUT "-- DRY RUN:\n";
   1.899 +        print STDOUT "-- $cmd\n";
   1.900 +        $rc = 0;
   1.901 +    }
   1.902 +    else {
   1.903 +        $rc = system($cmd);
   1.904 +    }
   1.905 +
   1.906 +    return $rc;
   1.907 +}
   1.908 +

mercurial