Bladeren bron

new package: openpkg-tool 20021126 (OpenPKG Tool)

Ralf S. Engelschall 23 jaren geleden
bovenliggende
commit
013ce702bc

+ 4 - 0
openpkg-tool/Makefile

@@ -0,0 +1,4 @@
+
+openpkg.1: openpkg.pod
+	pod2man --section=1 --quotes=none --release="openpkg-tool" --center="OpenPKG Maintainance" openpkg.pod >openpkg.1
+

File diff suppressed because it is too large
+ 1291 - 0
openpkg-tool/openpkg-build.pl


+ 866 - 0
openpkg-tool/openpkg-index.pl

@@ -0,0 +1,866 @@
+##
+##  openpkg-index -- create index from spec files
+##
+##  Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
+##  Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
+##  Copyright (c) 2000-2002 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.
+##
+
+require 5;
+
+use strict;
+
+use Getopt::Std;
+getopts('r:p:C:o:ci');
+use vars qw/$opt_r $opt_p $opt_C $opt_o $opt_c $opt_i/;
+
+use FileHandle;
+use DirHandle;
+
+my $RPM = 'rpm';
+my $R2C = 'rpm2cpio';
+my $BZ  = 'bzip2 -9';
+
+#########################################################################
+
+#
+# escape XML special characters for output in RDF file
+#
+# remove trailing whitespace
+# remove common leading whitespace
+#
+sub e ($) {
+    my($s) = @_;
+    my($i);
+
+    $s =~ s/\n+$//sg;
+    $s =~ s/\s+$//mg;
+
+    $i = undef;
+    while ($s =~ /^(\s+)/mg) {
+        $i = $1 if !defined $i || length($1) < length($i);
+    }
+
+    $s =~ s/^\Q$i\E//mg if defined $i;
+    $s =~ s/&/&amp;/sg;
+    $s =~ s/</&lt;/sg;
+    $s =~ s/>/&gt;/sg;
+
+    return $s;
+}
+
+sub commasep ($$) {
+    my($k,$v) = @_;
+
+    if ($k =~ /^(PreReq|BuildPreReq|Provides|Conflicts)$/) {
+        return split(/\s*,\s*/, $v);
+    }
+
+    return $v;
+}
+
+sub vsub ($$) {
+    my($var,$v) = @_;
+
+    $v =~ s/\%\{([^}]+)\}/exists $var->{$1} ? $var->{$1} : '%{'.$1.'}'/emg;
+
+    return $v;
+}
+
+sub upn ($) {
+    my($t) = @_;
+    my(@tok) = $t =~ /(\(|\)|\&\&|\|\||\!|\S+)/g;
+    my(@out,$op,$o);
+    my(@save);
+
+    $op = [];
+    foreach (@tok) {
+        if ($_ eq '(') {
+            push @save, $op;
+            $op = [];
+        } elsif ($_ eq ')') {
+            die "FATAL: unresolved operators in: @tok\n" if @$op;
+            $op = pop @save
+                or die "FATAL: parenthesis stack underflow in: @tok\n";
+            while ($o = pop @$op) {
+                push @out, $o->[0];
+                last if $o->[1];
+            }
+        } elsif ($_ eq '&&') {
+            push @$op, [ '+', 1 ] ;
+        } elsif ($_ eq '||') {
+            push @$op, [ '|', 1 ] ;
+        } elsif ($_ eq '!') {
+            push @$op, [ '!', 0 ];
+        } elsif (/^\%\{(\S*?)\}$/) {
+            push @out, $1;
+            while ($o = pop @$op) {
+                push @out, $o->[0];
+                last if $o->[1]; # binop
+            }
+        }
+    }
+
+    return join (' ',@out);
+}
+
+#
+# deduce external variables from description
+#
+sub find_options ($) {
+    my($descr) = @_;
+    my(%evar);
+
+    %evar = map {
+        $1 => '%{'.$1.'}'
+    } $descr =~ /--define\s*'(\S+)\s*\%\{\1\}'/;
+
+    return \%evar;
+}
+
+#
+# translate default section from spec-file
+# into a hash
+# %if/%ifdef/%define... are translated to #if/#ifdef/#define
+#
+# #defines are interpolated (correct ?)
+#
+# #if/#ifdef/... sections are stripped
+# result is the same as if all conditions evaluate false (!)
+#
+# all attributes are of the form key: value
+# repeated attributes are coalesced into a list
+#
+sub package2data ($$) {
+    my($s,$evar) = @_;
+    my(%var);
+    my(@term, $term);
+    my(%attr);
+    my($l, $v, $cond, $d, $p);
+    my($re,@defs);
+
+    # combine multilines
+    $s =~ s/\\\n/ /sg;
+
+    #
+    # map conditional variable macros
+    #
+    $s =~ s/^#\{\!\?([^:]*):\s*%(.*?)\s*\}\s*$/#ifndef $1\n#$2\n#endif/mg;
+    $s =~ s/^#\{\!\?([^:]*):\s*(.*?)\s*\}\s*$/#ifndef $1\n$2\n#endif/mg;
+
+    #
+    # guess more external parameters by scanning for "default" sections.
+    #
+    $re = '^\#ifndef\s+[\w\_]+\s*\n((?:\#define\s+[\w\_]+\s.*\n)+)\#endif\n';
+    @defs = $s =~ /$re/gm;
+    foreach (@defs) {
+        while (/^\#define\s+([\w\_]+)\s(.*?)\s*$/mg) {
+            $evar->{$1} = '%{'.$1.'}';
+        }
+    }
+    $s =~ s/$re//gm;
+
+    #
+    # add everything looking like a with_ variable
+    #
+    $re = '%{(with\_[\w\_]+)}';
+    @defs = $s =~ /$re/gm;
+    foreach (@defs) {
+        $evar->{$1} = '%{'.$1.'}';
+    }
+
+
+    #
+    # extract all conditional sections
+    #
+    @term = ();
+    %var  = ();
+    $cond = '';
+    foreach $l (split(/\n/, $s)) {
+        $v = vsub(\%var,$l);
+
+        if (($p) = $v =~ /^\#if\s+(.*?)\s*$/) {
+            #
+            # normalize #if expressions
+            # "%{variable}" == "yes"
+            # "%{variable}" == "no"
+            # operators ! && ||
+            #
+            $term = '';
+            while ($p =~ /(!=)|(\!|\|\||\&\&|\(|\))|"\%\{([^}]+)\}"\s*==\s*"(yes|no)"|(\S+)/g) {
+                if (defined $1) {
+                    warn "WARNING: unknown token '$1':\n< $l\n> $v\n";
+                } elsif (defined $5) {
+                    warn "WARNING: unknown token '$5':\n< $l\n> $v\n";
+                } elsif (defined $2) {
+                    $term .= " $2 ";
+                } elsif (exists $evar->{$3}) {
+                    $term .= ($4 eq 'no' ? '! ' : '').vsub($evar,'%{'.$3.'}');
+                } else {
+                    warn "WARNING: unknown conditional '$2':\n< $l\n> $v\n";
+                }
+            }
+
+            #
+            # join with previous conditions for this #if/#endif block
+            #
+            if ($term ne '') {
+                push @term, "( $term )";
+                $cond = join(' && ', grep { $_ ne '' } @term).'';
+            } else {
+                push @term, '';
+            }
+        } elsif ($v =~ /^\#else\s*$/) {
+            #
+            # reverse last condition
+            #
+            if (@term) {
+                $term[-1] = ' ! '.$term[-1];
+                $cond = join(' && ', grep { $_ ne '' } @term).'';
+            } else {
+                die "FATAL: else without if\n";
+            }
+        } elsif ($v =~ /^\#endif\s*$/) {
+            #
+            # unwind last #if expression
+            #
+            pop @term;
+            $cond = join(' && ', grep { $_ ne '' } @term).'';
+
+        } elsif ($v =~ /^\#(?:define)\s*(\S+)\s*(.*?)\s*$/) {
+
+            #
+            # define conditional variables
+            # truth-value becomes current condition
+            #
+            # define internal variables
+            # -> store for subsequent substitution
+            #
+            if (exists $evar->{$1}) {
+                if ($2 eq 'yes') {
+                    $evar->{$1} = "( \%\{$1\} || ( $cond ) )";
+                } elsif ($2 eq 'no') {
+                    $evar->{$1} = "( %\{$1\} && ! ( $cond ) )";
+                } else {
+                    warn "WARNING: logic too complex for '$1':\n< $l\n> $v\n";
+                }
+            } else {
+                $var{$1} = $2;
+            }
+        } elsif ($v =~ /^\s*([^\#]\S*)\s*:\s*(.*?)\s*$/) {
+
+            #
+            # store attribute=value for current condition
+            #
+            push @{$attr{$1}->{$cond}}, commasep($1,$2);
+        }
+    }
+
+    return \%attr;
+}
+
+#
+# split spec file into sections starting with a %word
+#
+# concatenate extended lines
+# strip comment lines
+# map %command to #command
+# split sections
+#
+# return package2data from default section.
+#
+sub spec2data ($) {
+    my($s) = @_;
+    my(%map);
+    my($a,$o);
+
+    # remove comments
+    $s =~ s/^\s*#.*?\n//mg;
+
+    # map commands
+    $s =~ s/^%(ifdef|ifndef|if|define|else|endif|\{)/#$1/mg;
+
+    # split sections
+    foreach (split(/^(?=%\w+\s*\n)/m, $s)) {
+        if (/^%(\w+)\s*\n/) {
+            $map{$1} .= $';
+        } else {
+            $map{'*'} .= $_;
+        }
+    }
+
+    $o = find_options($map{'description'});
+    $a = package2data($map{'*'}, $o);
+    if (exists $map{'description'}) {
+        $a->{'Description'} = { '' => [ $map{'description'} ] };
+    }
+
+    return $a;
+}
+
+##########################################################################
+
+#
+# start of XML file
+#
+sub xml_head ($$) {
+    my($fh,$res) = @_;
+    print $fh <<EOFEOF;
+<?xml version="1.0" encoding="iso-8859-1"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns="http://www.openpkg.org/xml-rdf-index/0.9">
+  <Repository rdf:resource="$res">
+EOFEOF
+}
+
+#
+# end of XML file, corresponds with start tags
+#
+sub xml_foot ($) {
+    my($fh) = @_;
+    print $fh <<EOFEOF;
+  </Repository>
+</rdf:RDF>
+EOFEOF
+}
+
+sub n($$) {
+    my($a,$k) = @_;
+    return unless $a->{$k};
+    return unless $a->{$k}->{''};
+    return $a->{$k}->{''}->[0];
+}
+
+#
+# send out $a->{$k} as text-style tag
+#
+sub xml_text ($$$;$) {
+    my($i,$a,$k,$tag) = @_;
+    my($out);
+    return "" unless exists $a->{$k};
+    $tag = $k unless defined $tag;
+    $i = ' ' x $i;
+
+    $out = e(n($a,$k));
+
+    return if $out eq '';
+
+    return "$i<$tag>\n$out\n$i</$tag>\n";
+}
+
+#
+# send out @{$a->{$k}} as body of an XML tag
+# $k is the name of the tag unless overridden by $tag
+# $i denotes the depth of indentation to form nicely
+# looking files.
+#
+# all data from the list is flattened into a single
+# body, separated by LF and escaped for XML metachars.
+#
+sub xml_tag ($$$;$) {
+    my($i,$a,$k,$tag) = @_;
+    my($out,$cond,$upn);
+    return "" unless exists $a->{$k};
+    $tag = $k unless defined $tag;
+    $out = '';
+    $i = ' ' x $i;
+
+    foreach $cond (sort keys %{$a->{$k}}) {
+        $upn = e(upn($cond));
+        $out .= $i.
+            ($cond ne '' ?  "<$tag cond=\"$upn\">" : "<$tag>").
+            join("\n", map { e($_) } @{$a->{$k}->{$cond}}).
+            "</$tag>\n";
+    }
+    
+    return $out;
+}
+
+#
+# send out @{$a->{$k}} as a rdf:bag
+# $k is the name of the outer tag unless overriden by $tag
+# $i denotes the depth of indentation, inner tags are indented
+# 2 or 4 more character positions.
+#
+sub xml_bag ($$$;$) {
+    my($i,$a,$k,$tag) = @_;
+    my($out,$cond,$upn);
+    return "" unless exists $a->{$k};
+    $tag = $k unless defined $tag;
+    $out = '';
+    $i = ' ' x $i;
+
+    foreach $cond (sort keys %{$a->{$k}}) {
+        $upn = e(upn($cond));
+        $out .= $i.
+                ($cond ne '' ? "<$tag cond=\"$upn\">\n" : "<$tag>\n").
+                "$i  <rdf:bag>\n".
+                join("",
+                     map { "$i    <rdf:li>".e($_)."</rdf:li>\n" }
+                     @{$a->{$k}->{$cond}}).
+                "$i  </rdf:bag>\n".
+                "$i</$tag>\n";
+    }
+
+    return $out;
+}
+
+#
+# send out reference to another RDF
+#
+sub xml_reference ($$$) {
+    my($fh, $res, $href) = @_;
+
+    print $fh <<EOFEOF;
+    <Repository rdf:resource="$res" href="$href"/>
+EOFEOF
+}
+
+#
+# translate attributes from %$a as generated by package2data
+# into XML and write to file $fh
+#
+sub xml_record ($$$) {
+    my($fh, $a, $href) = @_;
+    my($maj,$min,$rel,$about);
+
+    $about =
+        n($a,'Name').'-'.
+        n($a,'Version').'-'.
+        n($a,'Release');
+
+    unless (defined $href) {
+
+        # guess location from Information in Specfile
+
+        $href = "$about.src.rpm";
+        ($maj,$min,$rel) = n($a,'Release') =~ /^(\d+)\.(\d+)\.(\d+)/;
+
+        if (defined $min) {
+            if ($maj > 1 || ($maj == 1 && $min > 0)) {
+                # 1.1 or later
+                if (n($a,'Distribution') =~ /\[PLUS\]/) {
+                    $href = 'PLUS/'.$href;
+                }
+            }
+            if ($maj > 1 || ($maj == 1 && $min >= 0)) {
+                # 1.0 or later 
+                if ($rel > 0) {
+                    $href = 'UPD/'.$href;
+                }
+            }
+        } else {
+            # current
+        }
+
+    }
+    
+    print $fh <<EOFEOF;
+    <rdf:Description about="$about" href="$href">
+EOFEOF
+
+    # fake Source attribute from Source\d attribtutes
+    # XXX only default conditional
+    $a->{'Source'} = { '' => [
+        map {
+            s/\Q%{name}\E/n($a,'Name')/esg;
+            s/\Q%{version}\E/n($a,'Version')/esg;
+            s/\Q%{release}\E/n($a,'Release')/esg;
+            s/.*\///;
+            $_;
+        }
+        map {
+            $a->{$_}->{''} ? @{$a->{$_}->{''}} : ()
+        }
+        sort {
+            my($x) = $a =~ /^(\d*)$/;
+            my($y) = $b =~ /^(\d*)$/;
+            return $x <=> $y;
+        }
+        grep {
+            /^Source\d*$/
+        } keys %$a
+    ]};
+    delete $a->{'Source'} unless @{$a->{'Source'}->{''}};
+
+    print $fh
+        xml_tag(6, $a, 'Name'),
+        xml_tag(6, $a, 'Version'),
+        xml_tag(6, $a, 'Release'),
+        xml_tag(6, $a, 'Distribution'),
+        xml_tag(6, $a, 'Group'),
+        xml_tag(6, $a, 'License'),
+        xml_tag(6, $a, 'Packager'),
+        xml_tag(6, $a, 'Summary'),
+        xml_tag(6, $a, 'URL'),
+        xml_tag(6, $a, 'Vendor'),
+        xml_tag(6, $a, 'SourceRPM'),
+        xml_tag(6, $a, 'Arch'),
+        xml_tag(6, $a, 'Os'),
+        xml_tag(6, $a, 'BuildRoot'),
+        xml_tag(6, $a, 'BuildHost'),
+        xml_tag(6, $a, 'BuildSystem'),
+        xml_tag(6, $a, 'BuildTime'),
+        xml_tag(6, $a, 'Relocations'),
+        xml_tag(6, $a, 'Size'),
+        xml_tag(6, $a, 'Prefixes'),
+        xml_tag(6, $a, 'Platform'),
+        xml_tag(6, $a, 'SigSize'),
+        xml_tag(6, $a, 'SigMD5'),
+        xml_tag(6, $a, 'SigPGP'),
+        xml_tag(6, $a, 'SigGPG'),
+        xml_bag(6, $a, 'BuildPreReq'),
+        xml_bag(6, $a, 'PreReq'),
+        xml_bag(6, $a, 'Provides'),
+        xml_bag(6, $a, 'Conflicts'),
+        xml_bag(6, $a, 'Source'),
+        xml_bag(6, $a, 'Filenames'),
+        xml_text(6, $a, 'Description');
+
+    print $fh <<EOFEOF;
+    </rdf:Description>
+EOFEOF
+}
+
+#####################################################################
+
+sub rpm2spec ($) {
+    my($fn) = @_;
+    my($pipe) = new FileHandle "$R2C '$fn' |"
+        or die "FATAL: cannot read '$fn' ($!)\n";
+    my($buf,@hdr,$n,$m,$name,$step);
+    my($spec);
+
+    while (read($pipe,$buf,110) == 110) {
+        @hdr = unpack('a6a8a8a8a8a8a8a8a8a8a8a8a8a8',$buf);
+        $n = hex($hdr[12]);      # filename length
+        $m = int(($n+5)/4)*4-2;  # filename size (padded)
+        last unless read($pipe,$buf,$m) == $m;
+        $name = substr($buf,0,$n-1);
+        $n = hex($hdr[7]);       # file length
+        $m = int(($n+3)/4)*4;    # file size (padded)
+        if ($name !~ /.spec$/) {
+            while ($m > 0) {
+                $step = $m > 8192 ? 8192 : $m;
+                last unless read($pipe,$buf,$step);
+                $m -= length($buf);
+            }
+        } else {
+            if (read($pipe,$buf,$n) == $n) {
+                $spec = $buf;
+            }
+            last;
+        }
+    }
+    $pipe->close;
+
+    return $spec;
+}
+
+#####################################################################
+
+sub rpm2data ($$) {
+    my($fn,$platform) = @_;
+    my($q,$pipe,%a);
+    my($t,$v);
+
+    $q = <<EOFEOF;
+Name %{Name}
+Version %{Version}
+Release %{Release}
+URL %{URL}
+Summary %{Summary}
+Copyright %{Copyright}
+License %{License}
+Distribution %{Distribution}
+Vendor %{Vendor}
+Group %{Group}
+Packager %{Packager}
+Prefixes %{Prefixes}
+BuildRoot %{BuildRoot}
+BuildHost %{BuildHost}
+BuildTime %{BuildTime}
+Arch %{Arch}
+Os %{Os}
+Size %{Size}
+SigSize %{SigSize}
+SigMD5 %{SigMD5}
+SigPGP %{SigPGP}
+SigGPG %{SigGPG}
+SourceRPM %{SourceRPM}
+[Patch %{Patch}
+]
+[Source %{Source}
+]
+[Filenames %{Filenames}
+]
+[Conflicts %{CONFLICTNAME} %|CONFLICTFLAGS?{%{CONFLICTFLAGS:depflags} %{CONFLICTVERSION}}:{}|
+]
+[PreReq %{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|
+]
+[Provides %{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|
+]
+Description %{Description}
+EOFEOF
+
+    $pipe = new FileHandle "$RPM -qp --qf '$q' '$fn' |"
+        or die "FATAL: cannot read '$fn' ($!)\n";
+    while (<$pipe>) {
+        if (/^(\S+)\s+(.*?)\s*$/) {
+            $t = $1;
+            $v = $2;
+        } elsif (/^(\s+.+?)\s*$/) {
+            next unless defined $t;
+            $v = $1;
+        } else {
+            $t = undef;
+            next;
+        }
+
+        if (exists $a{$t}) {
+            $a{$t} .= "\n$v";
+        } else {
+            $a{$t} = $v;
+        }
+    }
+    $pipe->close;
+
+    %a = map { $_ => $a{$_} }
+         grep { $a{$_} ne '(none)' }
+         keys %a;
+    if ($a{'Relocations'} eq '(non relocatable)') {
+        delete $a{'Relocations'};
+    }
+    if ($a{'SigMD5'} eq '(unknown type)') {
+        delete $a{'SigMD5'};
+    }
+    $a{'Platform'} = "$a{'Arch'}-$platform-$a{'Os'}";
+    $a{'PreReq'} =~ s/^rpmlib\(.*$//mg;
+    $a{'Description'} = [ $a{'Description'} ];
+
+    return { map {
+        $_ => { '' => (ref $a{$_} ? $a{$_} : [ split(/\n+/, $a{$_}) ]) }
+    } keys %a };
+}
+
+#####################################################################
+
+sub getindex ($) {
+    my($dir) = @_;
+    my(@idx) = sort { -M $a <=> -M $b; }
+               grep { -f $_ }
+                ( <$dir/00INDEX.rdf>, <$dir/00INDEX.rdf.*> );
+
+    return unless @idx;
+    return $idx[0];
+}
+
+sub list_specdir ($) {
+    my($dir) = @_;
+    my($dh,$d,$path);
+    my(@list);
+
+    $dh = new DirHandle($dir);
+    while ($d = $dh->read) {
+        next if $d =~ /^\./;
+        $path = "$dir/$d/$d.spec";
+        push @list, $path if -f $path;
+    }
+
+    return \@list;
+}
+
+sub list_rpmdir ($) {
+    my($dir) = @_;
+    my($dh,$d,$path);
+    my(@list,$idx,$sub);
+
+    $dh = new DirHandle($dir);
+    while ($d = $dh->read) {
+        next if $d =~ /^\./;
+        $path = "$dir/$d";
+        if (-d $path) {
+            $idx = getindex($path);
+            if (defined $idx) {
+                push @list, $idx;
+            } else {
+                $sub = list_rpmdir($path);
+                push @list, @$sub;
+                undef $sub;
+            }
+        } else {
+            next unless $d =~ /\.rpm$/ && -f $path;
+            push @list, $path;
+        }
+    }
+
+    return \@list;
+}
+
+#####################################################################
+
+sub readfile ($) {
+    my($fn) = @_;
+    my($fh) = new FileHandle "< $fn"
+        or die "FATAL: cannot read '$fn' ($!)\n";
+    my(@l) = <$fh>;
+    $fh->close;
+    return join('',@l);
+}
+
+sub relpath ($$) {
+    my($prefix,$path) = @_;
+    $path =~ s/^\Q$prefix\E\///s;
+    return $path;
+}
+
+sub dirname ($) {
+    my($path) = @_;
+    $path =~ s/\/[^\/]*$//s;
+    return $path.'/';
+}
+
+sub getresource ($) {
+    my($fn) = @_;
+    my($fh, $buf);
+
+    if ($fn =~ /\.bz2$/) {
+        $fh = new FileHandle "$BZ -dc $fn |"
+            or die "FATAL: cannot read '$fn' ($!)\n";
+    } else {
+        $fh = new FileHandle "< $fn"
+            or die "FATAL: cannot read '$fn' ($!)\n";
+    }
+    $fh->read($buf, 1024);
+    $fh->close;
+
+    if ($buf =~ /<Repository.*?rdf:resource="([^"]+)"/) {
+        return $1;
+    }
+
+    return undef;
+}
+
+#####################################################################
+
+sub write_index ($$$$$$) {
+    my($fh,$prefix,$resource,$platform,$list,$cache) = @_;
+    my($a,$h,$r,$spec);
+    my($mtime);
+
+    foreach (@$list) {
+        $a = undef;
+        $h = undef;
+        $r = undef;
+        if (/\.spec$/) {
+            $spec = readfile($_);
+            $a    = spec2data($spec);
+        } elsif (/([^\/]+\.src\.rpm)$/) {
+            $h    = relpath($prefix, $_);
+            if ($cache) {
+                $mtime = (stat $_)[9];
+                if (exists $cache->{"M$_"} &&
+                    $cache->{"M$_"} == $mtime) {
+                    $spec = $cache->{"S$_"};
+                } else {
+                    $spec = rpm2spec($_);
+                    $cache->{"S$_"} = $spec;
+                    $cache->{"M$_"} = $mtime;
+                }
+            } else {
+                $spec = rpm2spec($_);
+            }
+            $a    = spec2data($spec);
+        } elsif (/([^\/]+\.rpm)$/) {
+            $h    = relpath($prefix, $_);
+            $a    = rpm2data($_, $platform);
+        } elsif (/([^\/]+\.rdf[^\/]*)$/) {
+            $h    = relpath($prefix, $_);
+            $r    = getresource($_) || $resource.dirname($h);
+        }
+
+        if ($a) {
+            xml_record($fh, $a, $h);
+        } elsif ($r) {
+            xml_reference($fh, $r, $h);
+        } else {
+            warn "ERROR: cannot process $_\n";
+        }
+    }
+}
+
+#####################################################################
+
+my($prefix,$list,$fh,%cache,$tmpo);
+
+if ($#ARGV < 0) {
+    print "openpkg:index:USAGE: $0 [-r resource] [-p platform] [-C cache.db] [-o index.rdf] [-c] [-i] dir ...\n";
+    die "\n";
+}
+
+if ($opt_C) {
+    require DB_File;
+    tie %cache, 'DB_File', $opt_C, O_CREAT|O_RDWR, 0666, $DB_File::DB_HASH
+        or die "FATAL: cannot tie cache '$opt_C' ($!)\n";
+}
+
+$opt_r = 'OpenPKG-CURRENT/Source/' unless defined $opt_r;
+$opt_p = 'unknown' unless defined $opt_p;
+
+if (defined $opt_o) {
+    $tmpo = $opt_o . '.tmp';
+    if ($opt_c) {
+        $fh = new FileHandle "| $BZ -c > '$tmpo'"
+            or die "FATAL: cannot write '$tmpo' ($!)\n";
+    } else {
+        $fh = new FileHandle "> $tmpo"
+            or die "FATAL: cannot write '$tmpo' ($!)\n";
+    }
+} else {
+    if ($opt_c) {
+        $fh = new FileHandle "| $BZ -c"
+            or die "FATAL: cannot write to stdout ($!)\n";
+    } else {
+        $fh = new FileHandle ">&=1"
+            or die "FATAL: cannot write to stdout ($!)\n";
+    }
+}
+
+xml_head($fh, $opt_r);
+foreach $prefix (@ARGV) {
+    die "FATAL: $prefix is not a directory\n" unless -d $prefix;
+    if ($opt_i) {
+        $list = list_rpmdir($prefix);
+    } else {
+        $list = list_specdir($prefix);
+    }
+    write_index($fh, $prefix, $opt_r, $opt_p, $list, $opt_C ? \%cache : undef);
+}
+xml_foot($fh);
+
+$fh->close
+    or die "FATAL: write error on output ($!)\n";
+
+if (defined $tmpo) {
+    rename $tmpo,$opt_o
+        or die "FATAL: cannot rename $tmpo to $opt_o ($!)\n";
+}
+

+ 89 - 0
openpkg-tool/openpkg-tool.spec

@@ -0,0 +1,89 @@
+##
+##  openpkg-tool.spec -- OpenPKG RPM Specification
+##  Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
+##  Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
+##  Copyright (c) 2000-2002 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.
+##
+
+#   package information
+Name:         openpkg-tool
+Summary:      OpenPKG Tool
+URL:          http://www.openpkg.org/
+Vendor:       The OpenPKG Project
+Packager:     The OpenPKG Project
+Distribution: OpenPKG [EVAL]
+Group:        Bootstrapping
+License:      GPL
+Version:      20021126
+Release:      20021126
+
+#   list of sources
+Source0:      openpkg.sh
+Source1:      openpkg-index.pl
+Source2:      openpkg-build.pl
+Source3:      openpkg.pod
+Source4:      openpkg.1
+Source5:      Makefile
+
+#   build information
+Prefix:       %{l_prefix}
+BuildRoot:    %{l_buildroot}
+BuildPreReq:  OpenPKG, openpkg >= 20020206
+PreReq:       OpenPKG, openpkg >= 20020206
+AutoReq:      no
+AutoReqProv:  no
+
+%description
+    The OpenPKG tool is a helper utility for managing an OpenPKG instance.
+
+%prep
+
+%build
+
+%install
+    rm -rf $RPM_BUILD_ROOT
+    %{l_shtool} mkdir -f -p -m 755 \
+        $RPM_BUILD_ROOT%{l_prefix}/bin \
+        $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg \
+        $RPM_BUILD_ROOT%{l_prefix}/man/man1
+    %{l_shtool} install -c -m 755 \
+        -e 's;@l_prefix@;%{l_prefix};g' \
+        -e 's;@version@;%{version};g' \
+        %{SOURCE openpkg.sh} $RPM_BUILD_ROOT%{l_prefix}/bin/openpkg
+    %{l_shtool} install -c -m 644 \
+        -e 's;@l_prefix@;%{l_prefix};g' \
+        -e 's;@version@;%{version};g' \
+        %{SOURCE openpkg.1} $RPM_BUILD_ROOT%{l_prefix}/man/man1/
+    %{l_shtool} install -c -m 755 \
+        %{SOURCE openpkg-index.pl} \
+        $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/
+    %{l_shtool} install -c -m 755 \
+        %{SOURCE openpkg-build.pl} \
+        $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/
+    %{l_rpmtool} files -v -ofiles -r$RPM_BUILD_ROOT \
+        %{l_files_std} \
+        '%not %dir %{l_prefix}/lib/openpkg'
+
+%files -f files
+
+%clean
+    rm -rf $RPM_BUILD_ROOT
+

+ 327 - 0
openpkg-tool/openpkg.1

@@ -0,0 +1,327 @@
+.\" Automatically generated by Pod::Man v1.34, Pod::Parser v1.13
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  | will give a
+.\" real vertical bar.  \*(C+ will give a nicer C++.  Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available.  \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` 
+.    ds C' 
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.\"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "OPENPKG 1"
+.TH OPENPKG 1 "2002-11-26" "openpkg-tool" "OpenPKG Maintainance"
+.SH "NAME"
+\&\fBopenpkg\fR \- \fBOpenPKG\fR maintainance utility
+.SH "VERSION"
+.IX Header "VERSION"
+openpkg-tool \f(CW@version\fR@
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBopenpkg\fR 
+\&\fBindex\fR 
+[\fB\-r\fR \fIresource\fR] 
+[\fB\-p\fR \fIplatform\fR]
+[\fB\-C\fR \fIcache.db\fR] 
+[\fB\-o\fR \fIindex.rdf\fR] 
+[\fB\-c\fR] 
+[\fB\-i\fR] 
+\&\fIdir\fR ...
+.PP
+\&\fBopenpkg\fR 
+\&\fBbuild\fR 
+[\fB\-R\fR \fIrpm\fR]
+[\fB\-r\fR \fIrepository\fR]
+[\fB\-f\fR \fIindex.rdf\fR]
+[\fB\-u\fR] 
+[\fB\-U\fR] 
+[\fB\-z\fR]
+[\fB\-Z\fR]
+[\fB\-i\fR]
+[\fB\-q\fR]
+[\fB\-P\fR \fIpriv-cmd\fR]
+[\fB\-N\fR \fInon-priv-cmd\fR]
+[\fB\-p\fR \fIplatform\fR]
+[\fB\-D\fR\fIvar\fR=\fIval\fR ...]
+[\fB\-E\fR \fIname\fR ...]
+([\fB\-a\fR] [\fB\-A\fR] | \fIpatternlist\fR)
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\fBopenpkg\fR is a frontend utility for maintaining an \fBOpenPKG\fR instance.
+It currenty provides indexing of \s-1RPM\s0 files (\fBopenpkg index\fR) and
+automated recursive from-scratch installation and updating of existing
+\&\s-1RPM\s0 packages (\fBopenpkg build\fR).
+.SH "COMMANDS"
+.IX Header "COMMANDS"
+.Sh "\s-1OPENPKG\s0 \s-1INDEX\s0"
+.IX Subsection "OPENPKG INDEX"
+\&\fBopenpkg-index\fR creates an \s-1XML/RDF\s0 based resource index for \s-1RPM\s0
+\&\fI.spec\fR files in a source tree or from an \s-1RPM\s0 package repository. The
+index holds enough information to support an automated build process by
+\&\fBopenpkg build\fR.
+.PP
+The following command line options exist:
+.IP "\fB\-r\fR \fIresource\fR" 4
+.IX Item "-r resource"
+The name of the resource stored in the index. The default is
+"\f(CW\*(C`OpenPKG\-CURRENT/Source/\*(C'\fR".
+.IP "\fB\-p\fR \fIplatform\fR" 4
+.IX Item "-p platform"
+\&\fBopenpkg index\fR adds a platform attribute for binary RPMs. The
+attribute is built as \fI%{arch}\fR\f(CW\*(C`\-\*(C'\fR\fIplatform\fR\f(CW\*(C`\-\*(C'\fR\fI%{os}\fR where
+\&\fI%{arch}\fR and \fI%{os}\fR are taken from the \s-1RPM\s0 header and \fIplatform\fR is
+the value of the \fB\-p\fR option. The default value is "\f(CW\*(C`unkown\*(C'\fR". This
+must be used to distinguish between platforms that support the same
+Architecture and \s-1OS\s0 name like various Linux distributions.
+.IP "\fB\-C\fR \fIcache.db\fR" 4
+.IX Item "-C cache.db"
+Cache all \fI.spec\fR files into this Berkeley-DB file when indexing source
+RPMs. The cache is refreshed automatically when the source RPMs are more
+recent than the cache entry.
+.IP "\fB\-o\fR \fIindex.rdf\fR" 4
+.IX Item "-o index.rdf"
+Name of the output \s-1XML/RDF\s0 file, default is to write to \fIstdout\fR.
+.IP "\fB\-c\fR" 4
+.IX Item "-c"
+Compress output with \f(CW\*(C`bzip2\*(C'\fR. Use the \fB\-o\fR option to specify a \fI.bz2\fR
+suffix.
+.IP "\fB\-i\fR" 4
+.IX Item "-i"
+The specified directories are \s-1RPM\s0 repositories. Build index over
+all \fI.rpm\fR files in these directories and all subdirectories.
+If a subdirectory already contains a \f(CW\*(C`00INDEX.rdf\*(C'\fR or \f(CW\*(C`00INDEX.rdf.*\*(C'\fR
+file then skip scanning the subdirectory, instead add a reference
+to the index file into the new index.
+.Sp
+Without this option the directories are source trees with a subdirectory
+per package and a \fIpackage\fR\f(CW\*(C`.spec\*(C'\fR file inside each subdirectory.
+.Sh "\s-1OPENPKG\s0 \s-1BUILD\s0"
+.IX Subsection "OPENPKG BUILD"
+\&\fBopenpkg build\fR writes a shell script to standard output that installs
+or upgrades software packages including all dependencies. Packages that
+are upgraded automatically trigger rebuilds of all packages that depend
+on the upgraded package (\*(L"reverse dependencies\*(R"). The dependency
+information is read from an index generated by \fBopenpkg index\fR.
+.PP
+The following command line options exist:
+.IP "\fB\-R\fR \fIrpm\fR" 4
+.IX Item "-R rpm"
+Specify a path to the installed \fBOpenPKG\fR \f(CW\*(C`rpm\*(C'\fR executable. Several
+other internal paths are deduced from the \fIrpm\fR path, so this should be
+something like \fI%{l_prefix}\fR\f(CW\*(C`/bin/rpm\*(C'\fR.
+.IP "\fB\-r\fR \fIrepository\fR" 4
+.IX Item "-r repository"
+Specify a path to an \s-1RPM\s0 repository, this can be a \s-1URL\s0 or a directory
+path. The name of the package file is appended to this path.
+The default is to use a \s-1URL\s0 pointing to the \fBOpenPKG\fR \s-1FTP\s0 server.
+.IP "\fB\-f\fR \fIindex.rdf\fR" 4
+.IX Item "-f index.rdf"
+Specify a path to the primary \s-1XML/RDF\s0 index, this can be a \s-1URL\s0 or a
+file path. If the index contains references to aother indexes these are
+included automatically. The default is to use a \s-1URL\s0 pointing to the
+\&\fBOpenPKG\fR \s-1FTP\s0 server for the \fBOpenPKG\fR release you are using.
+.IP "\fB\-u\fR" 4
+.IX Item "-u"
+The generated script will ignore binary RPMs that are stored on
+your system. Instead it will either fetch binary RPMs or rebuild
+from source RPMs fetched from the repository.
+.IP "\fB\-U\fR" 4
+.IX Item "-U"
+The generated script will try to upgrade all selected packages
+including their dependencies to the most recent version.
+.IP "\fB\-z\fR" 4
+.IX Item "-z"
+The generated script will rebuild all selected packages
+including their dependencies even when the most recent version
+is already installed.
+.IP "\fB\-Z\fR" 4
+.IX Item "-Z"
+\&\fBopenpkg build\fR ignores a installed packages, the
+script will rebuild all selected packages from scratch. 
+Note that this doesn't work together with the \fB\-a\fR option.
+.IP "\fB\-i\fR" 4
+.IX Item "-i"
+The generated script will ignore errors. However, if a build
+phase fails the install phase is still skipped.
+.IP "\fB\-q\fR" 4
+.IX Item "-q"
+Ignore all reverse dependencies.
+\&\fI\s-1ATTENTION:\s0 this might break already installed packages!\fR
+.IP "\fB\-P\fR \fIpriv-cmd\fR" 4
+.IX Item "-P priv-cmd"
+Command prefix to use for install commands that require elevated
+privileges. The most common tool for this is \fIsudo\fR\|(1).
+.IP "\fB\-N\fR \fInon-priv-cmd\fR" 4
+.IX Item "-N non-priv-cmd"
+Command prefix to use for install commands that do not require elevated
+privileges. The most common tool for this is \fIsudo\fR\|(1).
+.IP "\fB\-p\fR \fIplatform\fR" 4
+.IX Item "-p platform"
+The platform string that is matched against the index for binary
+packages. Default is to use the \fI%{_target_platform}\fR variable.
+.IP "\fB\-D\fR\fIvar\fR=\fIval\fR" 4
+.IX Item "-Dvar=val"
+Specify configuration options for all selected packages. This can be
+either \fB\-D\fR\f(CW\*(C`with_\*(C'\fR\fIxxx\fR\f(CW\*(C`=\*(C'\fR\fIyyy\fR or just \fB\-D\fR\f(CW\*(C`with_\*(C'\fR\fIxxx\fR, the
+latter is equivalent to a \fB\-D\fR\f(CW\*(C`with_\*(C'\fR\fIxxx\fR\f(CW\*(C`=\*(C'\fR\f(CW\*(C`yes\*(C'\fR. The parameters
+are matched against selected packages that are already installed. If
+they do indicate a change the package is rebuild. There can be multiple
+\&\fB\-D\fR options.
+.IP "\fB\-E\fR \fIname\fR" 4
+.IX Item "-E name"
+Ignore a package with the specified \fIname\fR. This can be used to avoid
+upgrading to a broken package in the repository. There can be multiple
+\&\fB\-E\fR options.
+.IP "\fB\-a\fR" 4
+.IX Item "-a"
+Select all installed packages. Do not specify a pattern list together
+with the \fB\-a\fR option.
+.IP "\fB\-A\fR" 4
+.IX Item "-A"
+Select all packages in the repository. Do not specify a pattern list together
+with the \fB\-a\fR option.
+.SH "CONFIGURATION"
+.IX Header "CONFIGURATION"
+\&\fBopenpkg build\fR reads the configuration file \fI$HOME/.openpkg/build\fR.
+The file lists default options, one option per line and section tags
+of the form \f(CW\*(C`[\*(C'\fR\fIprefix\fR\f(CW\*(C`]\*(C'\fR. Options following such a tag are only
+evaluated if the selected \s-1RPM\s0 path matches the prefix so that you can
+define default options for multiple \fBOpenPKG\fR hierarchies.
+.SH "CAVEATS"
+.IX Header "CAVEATS"
+Parallel execution of \fBopenpkg build\fR causes undefined effects.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fIrpm\fR\|(1), \fIsudo\fR\|(1)
+.SH "HISTORY"
+.IX Header "HISTORY"
+The \fBopenpkg index\fR and \fBopenpkg build\fR command
+was invented in November 2002 by \fIMichael van Elst\fR
+<mlelstv@dev.de.cw.net> under contract with \fICable & Wireless
+Germany\fR <http://www.cw.com/de> for use inside the \fBOpenPKG\fR
+project <http://www.openpkg.org/>.
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+.Vb 2
+\& Michael van Elst
+\& mlelstv@dev.de.cw.net
+.Ve

+ 265 - 0
openpkg-tool/openpkg.pod

@@ -0,0 +1,265 @@
+##
+##  openpkg.pod -- OpenPKG maintainance utility (frontend manual page)
+##
+##  Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
+##  Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
+##  Copyright (c) 2000-2002 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.
+##
+
+=pod
+
+=head1 NAME
+
+B<openpkg> - B<OpenPKG> maintainance utility
+
+=head1 VERSION
+
+openpkg-tool @version@
+
+=head1 SYNOPSIS
+
+B<openpkg> 
+B<index> 
+[B<-r> I<resource>] 
+[B<-p> I<platform>]
+[B<-C> I<cache.db>] 
+[B<-o> I<index.rdf>] 
+[B<-c>] 
+[B<-i>] 
+I<dir> ...
+
+B<openpkg> 
+B<build> 
+[B<-R> I<rpm>]
+[B<-r> I<repository>]
+[B<-f> I<index.rdf>]
+[B<-u>] 
+[B<-U>] 
+[B<-z>]
+[B<-Z>]
+[B<-i>]
+[B<-q>]
+[B<-P> I<priv-cmd>]
+[B<-N> I<non-priv-cmd>]
+[B<-p> I<platform>]
+[B<-D>I<var>=I<val> ...]
+[B<-E> I<name> ...]
+([B<-a>] [B<-A>] | I<patternlist>)
+
+=head1 DESCRIPTION
+
+B<openpkg> is a frontend utility for maintaining an B<OpenPKG> instance.
+It currenty provides indexing of RPM files (B<openpkg index>) and
+automated recursive from-scratch installation and updating of existing
+RPM packages (B<openpkg build>).
+
+=head1 COMMANDS
+
+=head2 OPENPKG INDEX
+
+B<openpkg-index> creates an XML/RDF based resource index for RPM
+F<.spec> files in a source tree or from an RPM package repository. The
+index holds enough information to support an automated build process by
+B<openpkg build>.
+
+The following command line options exist:
+
+=over 4
+
+=item B<-r> I<resource>
+
+The name of the resource stored in the index. The default is
+"C<OpenPKG-CURRENT/Source/>".
+
+=item B<-p> I<platform>
+
+B<openpkg index> adds a platform attribute for binary RPMs. The
+attribute is built as I<%{arch}>C<->I<platform>C<->I<%{os}> where
+I<%{arch}> and I<%{os}> are taken from the RPM header and I<platform> is
+the value of the B<-p> option. The default value is "C<unkown>". This
+must be used to distinguish between platforms that support the same
+Architecture and OS name like various Linux distributions.
+
+=item B<-C> I<cache.db>
+
+Cache all F<.spec> files into this Berkeley-DB file when indexing source
+RPMs. The cache is refreshed automatically when the source RPMs are more
+recent than the cache entry.
+
+=item B<-o> I<index.rdf>
+
+Name of the output XML/RDF file, default is to write to F<stdout>.
+
+=item B<-c>
+
+Compress output with C<bzip2>. Use the B<-o> option to specify a F<.bz2>
+suffix.
+
+=item B<-i>
+
+The specified directories are RPM repositories. Build index over
+all F<.rpm> files in these directories and all subdirectories.
+If a subdirectory already contains a C<00INDEX.rdf> or C<00INDEX.rdf.*>
+file then skip scanning the subdirectory, instead add a reference
+to the index file into the new index.
+
+Without this option the directories are source trees with a subdirectory
+per package and a I<package>C<.spec> file inside each subdirectory.
+
+=back
+
+=head2 OPENPKG BUILD
+
+B<openpkg build> writes a shell script to standard output that installs
+or upgrades software packages including all dependencies. Packages that
+are upgraded automatically trigger rebuilds of all packages that depend
+on the upgraded package ("reverse dependencies"). The dependency
+information is read from an index generated by B<openpkg index>.
+
+The following command line options exist:
+
+=over 4
+
+=item B<-R> I<rpm>
+
+Specify a path to the installed B<OpenPKG> C<rpm> executable. Several
+other internal paths are deduced from the I<rpm> path, so this should be
+something like I<%{l_prefix}>C</bin/rpm>.
+
+=item B<-r> I<repository>
+
+Specify a path to an RPM repository, this can be a URL or a directory
+path. The name of the package file is appended to this path.
+The default is to use a URL pointing to the B<OpenPKG> FTP server.
+
+=item B<-f> I<index.rdf>
+
+Specify a path to the primary XML/RDF index, this can be a URL or a
+file path. If the index contains references to aother indexes these are
+included automatically. The default is to use a URL pointing to the
+B<OpenPKG> FTP server for the B<OpenPKG> release you are using.
+
+=item B<-u>
+
+The generated script will ignore binary RPMs that are stored on
+your system. Instead it will either fetch binary RPMs or rebuild
+from source RPMs fetched from the repository.
+
+=item B<-U>
+
+The generated script will try to upgrade all selected packages
+including their dependencies to the most recent version.
+
+=item B<-z>
+
+The generated script will rebuild all selected packages
+including their dependencies even when the most recent version
+is already installed.
+
+=item B<-Z>
+
+B<openpkg build> ignores a installed packages, the
+script will rebuild all selected packages from scratch. 
+Note that this doesn't work together with the B<-a> option.
+
+=item B<-i>
+
+The generated script will ignore errors. However, if a build
+phase fails the install phase is still skipped.
+
+=item B<-q>
+
+Ignore all reverse dependencies.
+I<ATTENTION: this might break already installed packages!>
+
+=item B<-P> I<priv-cmd>
+
+Command prefix to use for install commands that require elevated
+privileges. The most common tool for this is sudo(1).
+
+=item B<-N> I<non-priv-cmd>
+
+Command prefix to use for install commands that do not require elevated
+privileges. The most common tool for this is sudo(1).
+
+=item B<-p> I<platform>
+
+The platform string that is matched against the index for binary
+packages. Default is to use the I<%{_target_platform}> variable.
+
+=item B<-D>I<var>=I<val>
+
+Specify configuration options for all selected packages. This can be
+either B<-D>C<with_>I<xxx>C<=>I<yyy> or just B<-D>C<with_>I<xxx>, the
+latter is equivalent to a B<-D>C<with_>I<xxx>C<=>C<yes>. The parameters
+are matched against selected packages that are already installed. If
+they do indicate a change the package is rebuild. There can be multiple
+B<-D> options.
+
+=item B<-E> I<name>
+
+Ignore a package with the specified I<name>. This can be used to avoid
+upgrading to a broken package in the repository. There can be multiple
+B<-E> options.
+
+=item B<-a>
+
+Select all installed packages. Do not specify a pattern list together
+with the B<-a> option.
+
+=item B<-A>
+
+Select all packages in the repository. Do not specify a pattern list together
+with the B<-a> option.
+
+=back
+
+=head1 CONFIGURATION
+
+B<openpkg build> reads the configuration file I<$HOME/.openpkg/build>.
+The file lists default options, one option per line and section tags
+of the form C<[>I<prefix>C<]>. Options following such a tag are only
+evaluated if the selected RPM path matches the prefix so that you can
+define default options for multiple B<OpenPKG> hierarchies.
+
+=head1 CAVEATS
+
+Parallel execution of B<openpkg build> causes undefined effects.
+
+=head1 SEE ALSO
+
+rpm(1), sudo(1)
+
+=head1 HISTORY
+
+The B<openpkg index> and B<openpkg build> command
+was invented in November 2002 by I<Michael van Elst>
+E<lt>mlelstv@dev.de.cw.netE<gt> under contract with I<Cable & Wireless
+Germany> E<lt>http://www.cw.com/deE<gt> for use inside the B<OpenPKG>
+project E<lt>http://www.openpkg.org/E<gt>.
+
+=head1 AUTHORS
+
+ Michael van Elst
+ mlelstv@dev.de.cw.net
+
+=cut
+

+ 90 - 0
openpkg-tool/openpkg.sh

@@ -0,0 +1,90 @@
+#!@l_prefix@/lib/openpkg/bash
+##
+##  openpkg.sh -- OpenPKG maintainance utility (frontend)
+##
+##  Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
+##  Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
+##  Copyright (c) 2000-2002 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.
+##
+
+#   program information
+progname="openpkg"
+progvers="@version@"
+l_prefix="@l_prefix@"
+
+#   try to determine Perl interpreter
+perl=""
+for dir in $l_prefix/bin `echo $PATH | sed -e 's;:; ;g'` /bin /usr/bin /usr/local/bin; do
+    if [ -f "$dir/perl" ]; then
+        perl="$dir/perl"
+        break
+    fi
+done
+
+#   command line option parsing
+if [ $# -eq 0 ]; then
+    echo "$progname:USAGE: $progname index|build [options]"
+    exit 0
+fi
+while [ ".$1" != . ]; do
+    case "$1" in
+        -h ) 
+            echo "$progname:USAGE: $progname index|build [options]"
+            exit 0
+            ;;
+        -v ) 
+            echo "$progname $progvers (OpenPKG instance: $l_prefix)"
+            exit 0
+            ;;
+        -* ) 
+            echo "$progname:ERROR: invalid option \"$1\"" 1>&2
+            exit 1 
+            ;;
+        *  ) 
+            break 
+            ;;
+    esac
+done
+
+#   command dispatching
+case "$1" in
+    index ) 
+        if [ ".$perl" = . ]; then
+            echo "$progname:ERROR: \"index\" command requires a Perl interpreter" 1>&2
+            exit 1
+        fi
+        shift
+        exec $perl ${l_prefix}/lib/openpkg/openpkg-index.pl ${1+"$@"}
+        ;;
+    build ) 
+        if [ ".$perl" = . ]; then
+            echo "$progname:ERROR: \"build\" command requires a Perl interpreter" 1>&2
+            exit 1
+        fi
+        shift
+        exec $perl ${l_prefix}/lib/openpkg/openpkg-build.pl ${1+"$@"}
+        ;;
+    * ) 
+        echo "$progname:ERROR: invalid command \"$1\"" 1>&2
+        exit 1 
+        ;;
+esac
+