From f8b245f06572634f7b7ad20dfb91736b10da7343 Mon Sep 17 00:00:00 2001 From: Francois Pons Date: Fri, 25 Aug 2000 14:49:48 +0000 Subject: *** empty log message *** --- Makefile | 3 + Makefile.PL | 16 +++ genbasefiles | 78 +++++++++++ rpmtools.pm | 347 +++++++++++++++++++++++++++++++++++++++++++++++ rpmtools.spec | 33 +++-- rpmtools.xs | 429 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 894 insertions(+), 12 deletions(-) create mode 100644 Makefile.PL create mode 100755 genbasefiles create mode 100644 rpmtools.pm create mode 100644 rpmtools.xs 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 \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 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 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 +#include +#include + +#undef Fflush +#undef Mkdir +#undef Stat +#include +#include + +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"); -- cgit v1.2.1