aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--Makefile.PL16
-rwxr-xr-xgenbasefiles78
-rw-r--r--rpmtools.pm347
-rw-r--r--rpmtools.spec33
-rw-r--r--rpmtools.xs429
6 files changed, 894 insertions, 12 deletions
diff --git a/Makefile b/Makefile
index 0c566ed..5894726 100644
--- a/Makefile
+++ b/Makefile
@@ -10,11 +10,14 @@ CFLAGS = -Wall -g
LIBRPM = -lrpm -ldb1 -lz -lbz2 -I/usr/include/rpm -lpopt
all: $(ALL)
+ perl Makefile.PL
+ $(MAKE) -f Makefile_core $@
install: $(ALL)
install -d $(PREFIX)/usr/bin
install -s $(ALL) $(PREFIX)/usr/bin
install genhdlist_cz2 genhdlists genfilelist packdrake $(PREFIX)/usr/bin
+ $(MAKE) -f Makefile_core $@
$(FROMCC): %: %.cc
$(CXX) $(CFLAGS) $< $(LIBRPM) -o $@
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..9e62f5e
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,16 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+my $libs = ' -lrpm -ldb1 -lz';
+
+WriteMakefile(
+ 'NAME' => 'rpmtools',
+ 'OPTIMIZE' => '-O3 -fomit-frame-pointer -fno-exceptions -fno-rtti -pipe -s -mpentium -mcpu=pentium -march=pentium -ffast-math -fexpensive-optimizations',
+ 'MAKEFILE' => 'Makefile_core',
+ 'OBJECT' => 'rpmtools.o',
+ 'VERSION_FROM' => 'rpmtools.pm',
+ 'LIBS' => [$libs], # e.g., '-lm'
+ 'INC' => '-I/usr/include/rpm', # e.g., '-I/usr/include/other'
+);
+
diff --git a/genbasefiles b/genbasefiles
new file mode 100755
index 0000000..5a68704
--- /dev/null
+++ b/genbasefiles
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+#- Copyright (C) 1999 MandrakeSoft (fpons@mandrakesoft.com)
+#-
+#- This program is free software; you can redistribute it and/or modify
+#- it under the terms of the GNU General Public License as published by
+#- the Free Software Foundation; either version 2, or (at your option)
+#- any later version.
+#-
+#- This program is distributed in the hope that it will be useful,
+#- but WITHOUT ANY WARRANTY; without even the implied warranty of
+#- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#- GNU General Public License for more details.
+#-
+#- You should have received a copy of the GNU General Public License
+#- along with this program; if not, write to the Free Software
+#- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+use strict qw(subs vars refs);
+use rpmtools;
+
+sub main {
+ my ($output_dir, @files) = @_;
+ my $params = new rpmtools;
+
+ -d $output_dir or die "usage: gendepslist <output_dir> <hdlist files|rpm files>\n";
+
+ #- this version try to use an existing profiles file to reduce
+ #- number of pass of parsing hdlist.
+ if (-r "$output_dir/provides") {
+ print STDERR "using existing $output_dir/provides file\n";
+ open F, "$output_dir/provides";
+ $params->read_provides_files(\*F);
+ close F;
+ }
+
+ #- now, try to build dependancy, but incrementally only.
+ foreach (@files) {
+ print STDERR "reading $_\n";
+ /\.rpm$/ ? $params->read_rpms($_) : $params->read_hdlists($_);
+ $params->compute_depslist();
+ }
+
+ my @unresolved = $params->get_unresolved_provides_files();
+ if (@unresolved > 0) {
+ foreach (@unresolved) {
+ print STDERR "found requires on file not yet found [$_], forcing a second pass\n";
+ }
+
+ #- cleaning.
+ $params->keep_only_cleaned_provides_files();
+
+ foreach (@files) {
+ print STDERR "reading (second pass) $_\n";
+ /\.rpm$/ ? $params->read_rpms($_) : $params->read_hdlists($_);
+ $params->compute_depslist();
+ }
+ }
+
+ #- work finished, so write results:
+ #- $output_dir/depslist.ordered
+ #- $output_dir/provides
+ #- $output_dir/compss
+ print STDERR "writing $output_dir/depslist.ordered\n";
+ open F, ">$output_dir/depslist.ordered" or die "unable to write depslist file $output_dir/depslist.ordered\n";
+ $params->write_depslist(\*F);
+ close F;
+ print STDERR "writing $output_dir/provides\n";
+ open F, ">$output_dir/provides" or die "unable to write provides file $output_dir/provides\n";
+ $params->write_provides(\*F);
+ close F;
+ print STDERR "writing $output_dir/compss\n";
+ open F, ">$output_dir/compss" or die "unable to write compss file $output_dir/compss";
+ $params->write_compss(\*F);
+ close F;
+}
+
+main(@ARGV);
diff --git a/rpmtools.pm b/rpmtools.pm
new file mode 100644
index 0000000..a892ea3
--- /dev/null
+++ b/rpmtools.pm
@@ -0,0 +1,347 @@
+package rpmtools;
+
+use strict;
+use vars qw($VERSION @ISA);
+
+require DynaLoader;
+
+@ISA = qw(DynaLoader);
+$VERSION = '0.01';
+
+bootstrap rpmtools $VERSION;
+
+#- build an empty params struct that can be used to compute dependancies.
+sub new {
+ bless {
+ use_base_flag => 0,
+ flags => [ qw(name version release size arch group requires provides) ],
+ info => {},
+ depslist => [],
+ provides => {},
+ };
+}
+
+#- read one or more hdlist files, use packdrake for decompression.
+sub read_hdlists {
+ my ($params, @hdlists) = @_;
+
+ local *F;
+ open F, "packdrake -c ". join (' ', @hdlists) ." |";
+ rpmtools::_parse_(fileno *F, $params->{flags}, $params->{info}, $params->{provides});
+ close F;
+ 1;
+}
+
+#- read one or more rpm files.
+sub read_rpms {
+ my ($params, @rpms) = @_;
+
+ foreach (@rpms) {
+ rpmtools::_parse_($_, $params->{flags}, $params->{info}, $params->{provides});
+ }
+ 1;
+}
+
+#- compute dependancies, result in stored in info values of params.
+#- operations are incremental, it is possible to read just one hdlist, compute
+#- dependancies and read another hdlist, and again.
+sub compute_depslist {
+ my ($params) = @_;
+
+ #- avoid recomputing already present infos, take care not to modify
+ #- existing entries, as the array here is used instead of values of infos.
+ my @info = grep { ! exists $_->{id} } values %{$params->{info}};
+
+ #- speed up the search by giving a provide from all packages.
+ foreach (@info) {
+ push @{$params->{provides}{$_->{name}} ||= []}, $_->{name};
+ }
+
+ #- search for entries in provides, if such entries are found,
+ #- another pass has to be done. TODO.
+
+ #- take into account in which hdlist a package has been found.
+ #- this can be done by an incremental take into account generation
+ #- of depslist.ordered part corresponding to the hdlist.
+ #- compute closed requires, do not take into account choices.
+ foreach (@info) {
+ my %required_packages;
+ my @required_packages;
+ my %requires; @requires{@{$_->{requires} || []}} = ();
+ my @requires = keys %requires;
+
+ while (my $req = shift @requires) {
+ ref $req or $req = $params->{provides}{$req} || ($req =~ /rpmlib\(/ ? [] :
+ [ ($req !~ /NOTFOUND_/ && "NOTFOUND_") . $req ]);
+ if (@$req > 1) {
+ #- this is a choice, no closure need to be done here.
+ exists $requires{$req} or push @required_packages, $req;
+ $requires{$req} = undef;
+ } else {
+ #- this could be nothing if the provides is a file not found.
+ #- and this has been fixed above.
+ foreach (@$req) {
+ my $info = $params->{info}{$_}; $info or next;
+ $required_packages{$_} = undef;
+ if ($info->{deps} && !$info->{requires}) {
+ #- the package has been read from an ordered depslist file, and need
+ #- to rebuild its requires tags, so it can safely be used here.
+ my @rebuild_requires;
+ foreach (split /\s+/, $info->{deps}) {
+ if (/\|/) {
+ push @rebuild_requires, [ map { $params->{depslist}[$_]{name} || $_ } split /\|/, $_ ];
+ } else {
+ push @rebuild_requires, $params->{depslist}[$_]{name} || $_;
+ }
+ }
+ $info->{requires} = \@rebuild_requires;
+ }
+ foreach (@{$info->{requires} || []}) {
+ unless (exists $requires{$_}) {
+ $requires{$_} = undef;
+ push @requires, $_;
+ }
+ }
+ }
+ }
+ }
+ unshift @required_packages, keys %required_packages;
+
+ delete $_->{requires}; #- affecting it directly make perl crazy, oops for rpmtools. TODO
+ $_->{requires} = \@required_packages;
+ }
+
+ #- sort packages, expand choices and closure again.
+ my %ordered;
+ foreach (@info) {
+ my %requires;
+ my @requires = ($_->{name});
+ while (my $dep = shift @requires) {
+ foreach (@{$params->{info}{$dep} && $params->{info}{$dep}{requires} || []}) {
+ if (ref $_) {
+ foreach (@$_) {
+ unless (exists $requires{$_}) {
+ $requires{$_} = undef;
+ push @requires, $_;
+ }
+ }
+ } else {
+ unless (exists $requires{$_}) {
+ $requires{$_} = undef;
+ push @requires, $_;
+ }
+ }
+ }
+ }
+
+ if ($_->{name} eq 'basesystem') {
+ foreach (keys %requires) {
+ $ordered{$_} += 10001;
+ }
+ } else {
+ foreach (keys %requires) {
+ ++$ordered{$_};
+ }
+ }
+ }
+ #- setup, filesystem and basesystem should be at the beginning.
+ @ordered{qw(setup filesystem basesystem)} = (30000, 20000, 10000);
+
+ #- compute base flag, consists of packages which are required without
+ #- choices of basesystem and are ALWAYS installed. these packages can
+ #- safely be removed from requires of others packages.
+ foreach (@{$params->{info}{basesystem}{requires}}) {
+ ref $_ or $params->{info}{$_} and $params->{info}{$_}{base} = undef;
+ }
+
+ #- give an id to each packages, start from number of package already
+ #- registered in depslist.
+ my $global_id = scalar @{$params->{depslist}};
+ foreach (sort { $ordered{$b->{name}} <=> $ordered{$a->{name}} } @info) {
+ $_->{id} = $global_id++;
+ }
+
+ #- recompute requires to use packages id, drop any base packages or
+ #- reference of a package to itself.
+ foreach my $pkg (sort { $a->{id} <=> $b->{id} } @info) {
+ my %requires_id;
+ my @requires_id;
+ foreach (@{$pkg->{requires}}) {
+ if (ref $_) {
+ #- all choices are grouped together at the end of requires,
+ #- this allow computation of dropable choices.
+ my @choices_id;
+ my $to_drop;
+ foreach (@$_) {
+ my ($id, $base) = $params->{info}{$_} ? ($params->{info}{$_}{id},
+ $params->{use_base_flag} && exists $params->{info}{$_}{base}) : ($_, 0);
+ $to_drop ||= $id == $pkg->{id} || $requires_id{$id} || $base;
+ push @choices_id, $id;
+ }
+ $to_drop or push @requires_id, \@choices_id;
+ } else {
+ my ($id, $base) = $params->{info}{$_} ? ($params->{info}{$_}{id},
+ $params->{use_base_flag} && exists $params->{info}{$_}{base}) : ($_, 0);
+ $requires_id{$id} = $_;
+ $id == $pkg->{id} or $base or push @requires_id, $id;
+ }
+ }
+ #- cannot remove requires values as they are necessary for closure on incremental job.
+ $pkg->{deps} = join(' ', map { join '|', @{ref $_ ? $_ : [$_]} } @requires_id);
+ $pkg->{name} eq 'basesystem' and $params->{use_base_flag} = 1;
+ push @{$params->{depslist}}, $pkg;
+ }
+ 1;
+}
+
+#- read depslist.ordered file, as if it was computed internally.
+sub read_depslist {
+ my ($params, $FILE) = @_;
+ my $global_id = scalar @{$params->{depslist}};
+
+ foreach (<$FILE>) {
+ chomp; /^\s*#/ and next;
+ my ($name, $version, $release, $size, $deps) = /^(\S*)-([^-\s]+)-([^-\s]+)\s+(\d+)\s+(.*)/;
+
+ #- store values here according to it.
+ push @{$params->{depslist}}, $params->{info}{$name} = {
+ name => $name,
+ version => $version,
+ release => $release,
+ size => $size,
+ deps => $deps,
+ id => $global_id++,
+ };
+ }
+
+ #- compute base flag, consists of packages which are required without
+ #- choices of basesystem and are ALWAYS installed. these packages can
+ #- safely be removed from requires of others packages.
+ if ($params->{info}{basesystem} && ! exists $params->{info}{basesystem}{base}) {
+ my @requires_id;
+ foreach (split /\s+/, $params->{info}{basesystem}{deps}) {
+ /\|/ or push @requires_id, $_;
+ }
+ foreach (@requires_id) {
+ $params->{depslist}[$_] and $params->{depslist}[$_]{base} = undef;
+ }
+ $params->{info}{basesystem}{base} = undef; #- make sure.
+ $params->{use_base_flag} = 1;
+ }
+ 1;
+}
+
+#- write depslist.ordered file according to info in params.
+sub write_depslist {
+ my ($params, $FILE, $min, $max) = @_;
+
+ foreach (grep { (! defined $min || $_->{id} >= $min) && (! defined $max || $_->{id} <= $max) }
+ sort { $a->{id} <=> $b->{id} } values %{$params->{info}}) {
+ printf $FILE "%s-%s-%s %s %s\n", $_->{name}, $_->{version}, $_->{release}, $_->{size}, $_->{deps};
+ }
+ 1;
+}
+
+#- fill params provides with files that can be used, it use the format for
+#- a provides file.
+sub read_provides_files {
+ my ($params, $FILE) = @_;
+
+ foreach (<$FILE>) {
+ chomp;
+ my ($k, @v) = split ':';
+ $k =~ /^\// and $params->{provides}{$k} ||= undef;
+ }
+ 1;
+}
+
+#- check if there has been a problem with reading hdlists or rpms
+#- to resolve provides on files.
+#- this is done by checking whether there exists a keys in provides
+#- hash where to value is null (and the key is a file).
+#- give the result as output.
+sub get_unresolved_provides_files {
+ my ($params) = @_;
+ my ($k, $v, @unresolved);
+
+ while (($k, $v) = each %{$params->{provides}}) {
+ $k =~ /^\// && ! defined $v and push @unresolved, $k;
+ }
+ @unresolved;
+}
+
+#- clean everything on provides but keep the files key entry on undef.
+#- this is necessary to try a second pass.
+sub keep_only_cleaned_provides_files {
+ my ($params) = @_;
+
+ foreach (keys %{$params->{provides}}) {
+ /^\// ? $params->{provides}{$_} = undef : delete $params->{provides}{$_};
+ }
+
+ #- clean everything else at this point.
+ $params->{use_base_flag} = 0;
+ $params->{info} = {};
+ $params->{depslist} = [];
+}
+
+#- read provides, first is key, after values.
+sub read_provides {
+ my ($params, $FILE) = @_;
+
+ foreach (<$FILE>) {
+ chomp;
+ my ($k, @v) = split ':';
+ $params->{provides}{$k} = \@v;
+ }
+}
+
+#- write provides, first is key, after values.
+sub write_provides {
+ my ($params, $FILE) = @_;
+ my ($k, $v);
+
+ while (($k, $v) = each %{$params->{provides}}) {
+ printf $FILE "%s\n", join ':', $k, @{$v || []};
+ }
+}
+
+#- read compss, look at DrakX for more info.
+sub read_compss {
+ my ($params, $FILE) = @_;
+ my $p;
+
+ foreach (<$FILE>) {
+ /^\s*$/ || /^#/ and next;
+ s/#.*//;
+
+ if (/^(\S.*)/) {
+ $p = $1;
+ } else {
+ /(\S+)/;
+ $params->{info}{$1} and $params->{info}{$1}{group} = $p;
+ }
+ }
+ 1;
+}
+
+#- write compss.
+sub write_compss {
+ my ($params, $FILE) = @_;
+ my %p;
+
+ foreach (values %{$params->{info}}) {
+ $_->{group} or next;
+ push @{$p{$_->{group}} ||= []}, $_->{name};
+ }
+ foreach (sort keys %p) {
+ print $FILE $_, "\n";
+ foreach (@{$p{$_}}) {
+ print $FILE "\t", $_, "\n";
+ }
+ print $FILE "\n";
+ }
+ 1;
+}
+
+1;
diff --git a/rpmtools.spec b/rpmtools.spec
index 4a49d2b..5857f03 100644
--- a/rpmtools.spec
+++ b/rpmtools.spec
@@ -1,5 +1,5 @@
%define name rpmtools
-%define release 1mdk
+%define release 2mdk
# do not modify here, see Makefile in the CVS
%define version 1.2
@@ -72,26 +72,35 @@ rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
-/usr/bin/packdrake
-/usr/bin/parsehdlist
-/usr/bin/rpm2header
-/usr/bin/genhdlist_cz2
+%{_bindir}/packdrake
+%{_bindir}/parsehdlist
+%{_bindir}/rpm2header
+%{_bindir}/genhdlist_cz2
+%dir %{perl_sitearch}/auto/rpmtools
+%{perl_sitearch}/auto/rpmtools/rpmtools.so
+%{perl_sitearch}/rpmtools.pm
%files devel
%defattr(-,root,root)
-/usr/bin/genhdlists
-/usr/bin/genfilelist
+%{_bindir}/genhdlists
+%{_bindir}/genbasefiles
+%{_bindir}/genfilelist
%files compat
%defattr(-,root,root)
-/usr/bin/gendepslist2
-/usr/bin/hdlist2prereq
-/usr/bin/hdlist2groups
-/usr/bin/hdlist2files
-/usr/bin/hdlist2names
+%{_bindir}/gendepslist2
+%{_bindir}/hdlist2prereq
+%{_bindir}/hdlist2groups
+%{_bindir}/hdlist2files
+%{_bindir}/hdlist2names
%changelog
+* Fri Aug 25 2000 François Pons <fpons@mandrakesoft.com> 1.2-2mdk
+- added rpmtools perl module.
+- added genbasefiles to build compss, depslist.ordered and provides files
+ in one (or two) pass.
+
* Wed Aug 23 2000 François Pons <fpons@mandrakesoft.com> 1.2-1mdk
- 1.2 of rpmtools.
- new tools packdrake and parsehdlist.
diff --git a/rpmtools.xs b/rpmtools.xs
new file mode 100644
index 0000000..b7621e0
--- /dev/null
+++ b/rpmtools.xs
@@ -0,0 +1,429 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#undef Fflush
+#undef Mkdir
+#undef Stat
+#include <rpm/rpmlib.h>
+#include <rpm/header.h>
+
+char *get_name(Header header, int_32 tag) {
+ int_32 type, count;
+ char *name;
+
+ headerGetEntry(header, tag, &type, (void **) &name, &count);
+ return name;
+}
+
+int get_int(Header header, int_32 tag) {
+ int_32 type, count;
+ int *i;
+
+ headerGetEntry(header, tag, &type, (void **) &i, &count);
+ return *i;
+}
+
+HV* get_info(Header header) {
+ HV* info = newHV();
+ char *name = get_name(header, RPMTAG_NAME);
+ STRLEN len = strlen(name);
+
+ if (info != 0) {
+ SV* sv_name = newSVpv(name, len);
+ SV* sv_version = newSVpv(get_name(header, RPMTAG_VERSION), 0);
+ SV* sv_release = newSVpv(get_name(header, RPMTAG_RELEASE), 0);
+
+ hv_store(info, "name", 4, sv_name, 0);
+ hv_store(info, "version", 7, sv_version, 0);
+ hv_store(info, "release", 7, sv_release, 0);
+ }
+
+ return info;
+}
+
+SV *get_table_sense(Header header, int_32 tag_name, int_32 tag_flags, int_32 tag_version, HV* iprovides) {
+ AV* table_sense;
+ int_32 type, count;
+ char **list;
+ int_32 *flags;
+ char **list_evr;
+ int i;
+
+ char buff[4096];
+ char *p;
+ int len;
+
+ headerGetEntry(header, tag_name, &type, (void **) &list, &count);
+ if (tag_flags) headerGetEntry(header, tag_flags, &type, (void **) &flags, &count);
+ else flags = 0;
+ if (tag_version) headerGetEntry(header, tag_version, &type, (void **) &list_evr, &count);
+ else list_evr = 0;
+
+ if (list) {
+ table_sense = newAV();
+ if (!table_sense) return &PL_sv_undef;
+
+ for(i = 0; i < count; i++) {
+ len = strlen(list[i]); if (len >= sizeof(buff)) continue;
+ memcpy(p = buff, list[i], len + 1); p+= len;
+
+ if (flags) {
+ if (flags[i] & RPMSENSE_PREREQ) {
+ if (p - buff + 3 >= sizeof(buff)) continue;
+ memcpy(p, "[*]", 4); p += 3;
+ }
+ if (list_evr) {
+ if (list_evr[i]) {
+ len = strlen(list_evr[i]);
+ if (len > 0) {
+ if (p - buff + 6 + len >= sizeof(buff)) continue;
+ *p++ = '[';
+ if (flags[i] & RPMSENSE_LESS) *p++ = '<';
+ if (flags[i] & RPMSENSE_GREATER) *p++ = '>';
+ if (flags[i] & RPMSENSE_EQUAL) *p++ = '=';
+ if ((flags[i] & (RPMSENSE_LESS|RPMSENSE_EQUAL|RPMSENSE_GREATER)) == RPMSENSE_EQUAL) *p++ = '=';
+ *p++ = ' ';
+ memcpy(p, list_evr[i], len); p+= len;
+ *p++ = ']';
+ *p = '\0';
+ }
+ }
+ }
+ }
+
+ /* for getting provides about required files */
+ if (iprovides && buff[0] == '/')
+ hv_fetch(iprovides, buff, p - buff, 1);
+
+ av_push(table_sense, newSVpv(buff, p - buff));
+ }
+
+ return newRV_noinc((SV*)table_sense);
+ }
+ return &PL_sv_undef;
+}
+
+#define HDFLAGS_NAME 0x00000001
+#define HDFLAGS_VERSION 0x00000002
+#define HDFLAGS_RELEASE 0x00000004
+#define HDFLAGS_ARCH 0x00000008
+#define HDFLAGS_GROUP 0x00000010
+#define HDFLAGS_SIZE 0x00000020
+#define HDFLAGS_SENSE 0x00080000
+#define HDFLAGS_REQUIRES 0x00100000
+#define HDFLAGS_PROVIDES 0x00200000
+#define HDFLAGS_OBSOLETES 0x00400000
+#define HDFLAGS_CONFLICTS 0x00800000
+#define HDFLAGS_FILES 0x01000000
+#define HDFLAGS_DIRSIND 0x02000000
+#define HDFLAGS_FILESIND 0x04000000
+
+
+MODULE = rpmtools PACKAGE = rpmtools
+
+
+int
+get_packages_installed(prefix, packages, lnames)
+ char* prefix
+ SV* packages
+ SV* lnames
+ PREINIT:
+ int count = 0;
+ CODE:
+ if (SvROK(packages) && SvTYPE(SvRV(packages)) == SVt_PVAV &&
+ SvROK(lnames) && SvTYPE(SvRV(lnames)) == SVt_PVAV) {
+ AV* pkgs = (AV*)SvRV(packages);
+ AV* names = (AV*)SvRV(lnames);
+ HV* info;
+ SV** isv;
+ rpmdb db;
+ dbiIndexSet matches;
+ int num, i, j, rc, len;
+ char *name;
+ Header header;
+
+ if (rpmReadConfigFiles(NULL, NULL) == 0) {
+ if (rpmdbOpen(prefix, &db, O_RDONLY, 0644) == 0) {
+ len = av_len(names);
+ for (j = 0; j <= len; ++j) {
+ isv = av_fetch(names, j, 0);
+ name = SvPV_nolen(*isv);
+ rc = rpmdbFindPackage(db, name, &matches);
+ if (rc == 0) {
+ count += matches.count;
+ for (i = 0; i < matches.count; ++i) {
+ header = rpmdbGetRecord(db, matches.recs[i].recOffset);
+ info = get_info(header);
+
+ if (info != 0) av_push(pkgs, newRV_noinc((SV*)info));
+
+ headerFree(header);
+ }
+ }
+ }
+ rpmdbClose(db);
+ } else croak("unable to open database");
+ } else croak("cannot read rpm config files");
+ } else croak("bad arguments list");
+ RETVAL = count;
+ OUTPUT:
+ RETVAL
+
+
+int
+get_all_packages_installed(prefix, packages)
+ char* prefix
+ SV* packages
+ PREINIT:
+ int count = 0;
+ CODE:
+ if (SvROK(packages) && SvTYPE(SvRV(packages)) == SVt_PVAV) {
+ AV* pkgs = (AV*)SvRV(packages);
+ HV* info;
+ rpmdb db;
+ int num;
+ Header header;
+
+ if (rpmReadConfigFiles(NULL, NULL) == 0) {
+ if (rpmdbOpen(prefix, &db, O_RDONLY, 0644) == 0) {
+ num = rpmdbFirstRecNum(db);
+ while (num > 0) {
+ header = rpmdbGetRecord(db, num);
+ info = get_info(header);
+
+ if (info != 0) av_push(pkgs, newRV_noinc((SV*)info));
+
+ headerFree(header);
+ num = rpmdbNextRecNum(db, num);
+ ++count;
+ }
+ rpmdbClose(db);
+ } else croak("unable to open database");
+ } else croak("cannot read rpm config files");
+ } else croak("bad arguments list");
+ RETVAL = count;
+ OUTPUT:
+ RETVAL
+
+
+void
+_parse_(fileno_or_rpmfile, flag, info, ...)
+ SV* fileno_or_rpmfile
+ SV* flag
+ SV* info
+ PREINIT:
+ SV* provides = &PL_sv_undef;
+ CODE:
+ if (items > 3)
+ provides = ST(3);
+ if (SvROK(flag) && SvROK(info) && (provides == &PL_sv_undef || SvROK(provides))) {
+ FD_t fd;
+ int fd_is_hdlist;
+ Header header;
+ int_32 type, count;
+ char **list;
+ int_32 *flags;
+
+ int bflag = 0;
+ AV* iflag;
+ HV* iinfo;
+ HV* iprovides;
+ SV** ret;
+ I32 flag_len;
+ STRLEN len;
+ char* str;
+ int i;
+
+ if (SvIOK(fileno_or_rpmfile)) {
+ fd = fdDup(SvIV(fileno_or_rpmfile));
+ fd_is_hdlist = 1;
+ } else {
+ fd = fdOpen(SvPV_nolen(fileno_or_rpmfile), O_RDONLY, 0666);
+ if (fd < 0) croak("unable to open rpm file %s", SvPV_nolen(fileno_or_rpmfile));
+ fd_is_hdlist = 0;
+ }
+
+ if ((SvTYPE(SvRV(flag)) != SVt_PVAV) ||
+ (SvTYPE(SvRV(info)) != SVt_PVHV) ||
+ provides != &PL_sv_undef && (SvTYPE(SvRV(provides)) != SVt_PVHV))
+ croak("bad arguments list");
+
+ iflag = (AV*)SvRV(flag);
+ iinfo = (HV*)SvRV(info);
+ iprovides = (HV*)(provides != &PL_sv_undef ? SvRV(provides) : 0);
+
+ /* examine flag and set up iflag, which is faster to fecth out */
+ flag_len = av_len(iflag);
+ for (i = 0; i <= flag_len; ++i) {
+ ret = av_fetch(iflag, i, 0); if (!ret) continue;
+ str = SvPV(*ret, len);
+
+ switch (len) {
+ case 4:
+ if (!strncmp(str, "name", 4)) bflag |= HDFLAGS_NAME;
+ else if (!strncmp(str, "arch", 4)) bflag |= HDFLAGS_ARCH;
+ else if (!strncmp(str, "size", 4)) bflag |= HDFLAGS_SIZE;
+ break;
+ case 5:
+ if (!strncmp(str, "group", 5)) bflag |= HDFLAGS_GROUP;
+ else if (!strncmp(str, "sense", 5)) bflag |= HDFLAGS_SENSE;
+ else if (!strncmp(str, "files", 5)) bflag |= HDFLAGS_FILES;
+ break;
+ case 7:
+ if (!strncmp(str, "version", 7)) bflag |= HDFLAGS_VERSION;
+ else if (!strncmp(str, "release", 7)) bflag |= HDFLAGS_RELEASE;
+ else if (!strncmp(str, "dirsind", 7)) bflag |= HDFLAGS_DIRSIND;
+ break;
+ case 8:
+ if (!strncmp(str, "requires", 8)) bflag |= HDFLAGS_REQUIRES;
+ else if (!strncmp(str, "provides", 8)) bflag |= HDFLAGS_PROVIDES;
+ else if (!strncmp(str, "filesind", 8)) bflag |= HDFLAGS_FILESIND;
+ break;
+ case 9:
+ if (!strncmp(str, "obsoletes", 9)) bflag |= HDFLAGS_OBSOLETES;
+ else if (!strncmp(str, "conflicts", 9)) bflag |= HDFLAGS_CONFLICTS;
+ break;
+ }
+ }
+ bflag |= HDFLAGS_NAME; /* this one should always be used */
+ if (iprovides) bflag |= HDFLAGS_REQUIRES; /* not really usefull else */
+
+ /* start the big loop,
+ parse all header from fileno, then extract information to store into iinfo and iprovides. */
+ while (fd_is_hdlist >= 0 ? (fd_is_hdlist > 0 ?
+ ((header=headerRead(fd, HEADER_MAGIC_YES)) != 0) :
+ ((fd_is_hdlist = -1), rpmReadPackageHeader(fd, &header, &i, NULL, NULL) == 0)) : 0) {
+ char *name = get_name(header, RPMTAG_NAME);
+ SV* sv_name = newSVpv(name, 0);
+ HV* header_info = newHV();
+
+ if (bflag & HDFLAGS_NAME)
+ hv_store(header_info, "name", 4, SvREFCNT_inc(sv_name), 0);
+ if (bflag & HDFLAGS_VERSION)
+ hv_store(header_info, "version", 7, newSVpv(get_name(header, RPMTAG_VERSION), 0), 0);
+ if (bflag & HDFLAGS_RELEASE)
+ hv_store(header_info, "release", 7, newSVpv(get_name(header, RPMTAG_RELEASE), 0), 0);
+ if (bflag & HDFLAGS_ARCH)
+ hv_store(header_info, "arch", 4, newSVpv(get_name(header, RPMTAG_ARCH), 0), 0);
+ if (bflag & HDFLAGS_GROUP)
+ hv_store(header_info, "group", 5, newSVpv(get_name(header, RPMTAG_GROUP), 0), 0);
+ if (bflag & HDFLAGS_SIZE)
+ hv_store(header_info, "size", 4, newSViv(get_int(header, RPMTAG_SIZE)), 0);
+ if (bflag & HDFLAGS_REQUIRES)
+ hv_store(header_info, "requires", 8, get_table_sense(header, RPMTAG_REQUIRENAME,
+ bflag & HDFLAGS_SENSE ? RPMTAG_REQUIREFLAGS : 0,
+ bflag & HDFLAGS_SENSE ? RPMTAG_REQUIREVERSION : 0, iprovides), 0);
+ if (bflag & HDFLAGS_PROVIDES)
+ hv_store(header_info, "provides", 8, get_table_sense(header, RPMTAG_PROVIDENAME,
+ bflag & HDFLAGS_SENSE ? RPMTAG_PROVIDEFLAGS : 0,
+ bflag & HDFLAGS_SENSE ? RPMTAG_PROVIDEVERSION : 0, 0), 0);
+ if (bflag & HDFLAGS_OBSOLETES)
+ hv_store(header_info, "obsoletes", 9, get_table_sense(header, RPMTAG_OBSOLETENAME,
+ bflag & HDFLAGS_SENSE ? RPMTAG_OBSOLETEFLAGS : 0,
+ bflag & HDFLAGS_SENSE ? RPMTAG_OBSOLETEVERSION : 0, 0), 0);
+ if (bflag & HDFLAGS_CONFLICTS)
+ hv_store(header_info, "conflicts", 9, get_table_sense(header, RPMTAG_CONFLICTNAME,
+ bflag & HDFLAGS_SENSE ? RPMTAG_CONFLICTFLAGS : 0,
+ bflag & HDFLAGS_SENSE ? RPMTAG_CONFLICTVERSION : 0, 0), 0);
+ if (iprovides || (bflag & HDFLAGS_FILES)) {
+ /* at this point, there is a need to parse all files to update provides of needed files,
+ or to store them. */
+ AV* table_files = bflag & HDFLAGS_FILES ? newAV() : 0;
+ char ** baseNames, ** dirNames;
+ int_32 * dirIndexes;
+
+ headerGetEntry(header, RPMTAG_OLDFILENAMES, &type, (void **) &list, &count);
+
+ if (list) {
+ for (i = 0; i < count; i++) {
+ SV** isv;
+
+ len = strlen(list[i]);
+
+ if (iprovides && (isv = hv_fetch(iprovides, list[i], len, 0)) != 0) {
+ if (!SvROK(*isv) || SvTYPE(SvRV(*isv)) != SVt_PVAV) {
+ SV* choice_table = (SV*)newAV();
+ SvREFCNT_dec(*isv); /* drop the old as we are changing it */
+ *isv = choice_table ? newRV_noinc(choice_table) : &PL_sv_undef;
+ }
+ if (*isv != &PL_sv_undef) av_push((AV*)SvRV(*isv), SvREFCNT_inc(sv_name));
+ }
+ /* if (iprovides && hv_exists(iprovides, list[i], len))
+ hv_store(iprovides, list[i], len, newSVpv(name, 0), 0); */
+ if (table_files)
+ av_push(table_files, newSVpv(list[i], len));
+ }
+ }
+
+ headerGetEntry(header, RPMTAG_BASENAMES, &type, (void **) &baseNames,
+ &count);
+ headerGetEntry(header, RPMTAG_DIRINDEXES, &type, (void **) &dirIndexes,
+ NULL);
+ headerGetEntry(header, RPMTAG_DIRNAMES, &type, (void **) &dirNames, NULL);
+
+ if (baseNames && dirNames && dirIndexes) {
+ char buff[4096];
+ char *p;
+
+ for(i = 0; i < count; i++) {
+ SV** isv;
+
+ len = strlen(dirNames[dirIndexes[i]]);
+ if (len >= sizeof(buff)) continue;
+ memcpy(p = buff, dirNames[dirIndexes[i]], len + 1); p += len;
+ len = strlen(baseNames[i]);
+ if (p - buff + len >= sizeof(buff)) continue;
+ memcpy(p, baseNames[i], len + 1); p += len;
+
+ if (iprovides && (isv = hv_fetch(iprovides, buff, p - buff, 0)) != 0) {
+ if (!SvROK(*isv) || SvTYPE(SvRV(*isv)) != SVt_PVAV) {
+ SV* choice_table = (SV*)newAV();
+ SvREFCNT_dec(*isv); /* drop the old as we are changing it */
+ *isv = choice_table ? newRV_noinc(choice_table) : &PL_sv_undef;
+ }
+ if (*isv != &PL_sv_undef) av_push((AV*)SvRV(*isv), SvREFCNT_inc(sv_name));
+ }
+ if (table_files)
+ av_push(table_files, newSVpv(buff, p - buff));
+ }
+ }
+
+ if (table_files)
+ hv_store(header_info, "files", 5, newRV_noinc((SV*)table_files), 0);
+ }
+ if (iprovides) {
+ /* we have to examine provides to update the hash here. */
+ headerGetEntry(header, RPMTAG_PROVIDENAME, &type, (void **) &list, &count);
+
+ if (list) {
+ for (i = 0; i < count; i++) {
+ SV** isv;
+
+ len = strlen(list[i]);
+
+ isv = hv_fetch(iprovides, list[i], len, 1);
+ if (!SvROK(*isv) || SvTYPE(SvRV(*isv)) != SVt_PVAV) {
+ SV* choice_table = (SV*)newAV();
+ SvREFCNT_dec(*isv); /* drop the old as we are changing it */
+ *isv = choice_table ? newRV_noinc(choice_table) : &PL_sv_undef;
+ }
+ if (*isv != &PL_sv_undef) av_push((AV*)SvRV(*isv), SvREFCNT_inc(sv_name));
+ }
+ }
+ }
+
+ /* once the hash header_info is built, store a reference to it
+ in iinfo.
+ note sv_name is not incremented here, it has the default value of before. */
+ hv_store_ent(iinfo, sv_name, newRV_noinc((SV*)header_info), 0);
+
+ /* dispose of some memory */
+ headerFree(header);
+ }
+ fdClose(fd);
+ } else croak("bad arguments list");