openpkg/index.pl

Thu, 04 Oct 2012 20:30:05 +0200

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 04 Oct 2012 20:30:05 +0200
changeset 715
c10fb90893b9
permissions
-rw-r--r--

Correct out of date build configuration, porting to Solaris 11 network
link infrastructure and new libpcap logic. This additionally allows for
device drivers in subdirectories of /dev. Correct packaged nmap
personalities and signatures to work out of the box. Finally, hack
arpd logic to properly close sockets and quit on TERM by repeating
signaling in the run command script. Sadly, all this fails to correct
the run time behaviour of honeyd which fails to bind to the IP layer.

michael@428 1 ##
michael@428 2 ## index.pl -- OpenPKG Maintenance Tool (backend for indexing)
michael@428 3 ## Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>
michael@428 4 ##
michael@428 5 ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
michael@428 6 ## All rights reserved. Licenses which grant limited permission to use,
michael@428 7 ## copy, modify and distribute this software are available from the
michael@428 8 ## OpenPKG GmbH.
michael@428 9 ##
michael@428 10 ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
michael@428 11 ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
michael@428 12 ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@428 13 ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
michael@428 14 ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@428 15 ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@428 16 ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
michael@428 17 ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@428 18 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
michael@428 19 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
michael@428 20 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
michael@428 21 ## SUCH DAMAGE.
michael@428 22 ##
michael@428 23
michael@428 24 #############################################################################
michael@428 25 ##
michael@428 26 ## MAIN PROCEDURE
michael@428 27 ##
michael@428 28 #############################################################################
michael@428 29
michael@428 30 require 5;
michael@428 31 #use strict;
michael@428 32
michael@428 33 # determine OpenPKG instance prefix via
michael@428 34 # 1. the environment of the "openpkg build" framework
michael@428 35 # 2. the installation path of the script
michael@428 36 # 3. the installation path of the Perl interpreter
michael@428 37 # 4. the path of the "openpkg" command in $PATH
michael@428 38 my $l_prefix = $ENV{'OPENPKG_PREFIX'};
michael@428 39 if (not $l_prefix) {
michael@428 40 ($l_prefix) = ($0 =~ m/^(.+)\/lib(exec)?\/openpkg(-tools)?\/build(\.pl)?$/);
michael@428 41 }
michael@428 42 if (not $l_prefix) {
michael@428 43 ($l_prefix) = ($^X =~ m/^(.+)\/bin\/perl.*$/);
michael@428 44 }
michael@428 45 if (not $l_prefix) {
michael@428 46 $l_prefix = (`(which openpkg) 2>/dev/null` =~ m/^(.+)\/bin\/openpkg$/);
michael@428 47 }
michael@428 48 if (not -x "$l_prefix/bin/openpkg") {
michael@428 49 die "openpkg:index:FATAL: cannot determine OpenPKG instance prefix";
michael@428 50 }
michael@428 51
michael@428 52 # determine tools
michael@428 53 my $RPM = ((-f "$l_prefix/bin/openpkg" && -f "$l_prefix/libexec/openpkg/rpm") ?
michael@428 54 "$l_prefix/bin/openpkg rpm" : "$l_prefix/bin/rpm");
michael@428 55 my $R2C = ((-f "$l_prefix/bin/openpkg" && -f "$l_prefix/libexec/openpkg/rpm2cpio") ?
michael@428 56 "$l_prefix/bin/openpkg rpm2cpio" : "$l_prefix/bin/rpm2cpio");
michael@428 57 my $BZ = "$l_prefix/lib/openpkg/bzip2 -9";
michael@428 58 my $OPENSSL = "$l_prefix/lib/openpkg/openssl";
michael@428 59
michael@428 60 # parse command line
michael@428 61 our ($opt_r, $opt_p, $opt_S, $opt_T, $opt_D, $opt_C, $opt_o, $opt_c, $opt_i);
michael@428 62 my $getopts = 'r:p:STDC:o:ci';
michael@428 63 getopts($getopts);
michael@428 64 if ($#ARGV < 0) {
michael@428 65 print "openpkg:index:USAGE: openpkg index [-r resource] [-p platform] [-S] [-T] [-D] [-C cache.db] [-o index.rdf] [-c] [-i] dir ...\n";
michael@428 66 exit(1);
michael@428 67 }
michael@428 68
michael@428 69 # optionally open cache file
michael@428 70 my %cache;
michael@428 71 if ($opt_C) {
michael@428 72 eval {
michael@428 73 require DB_File;
michael@428 74 };
michael@428 75 if ($@) {
michael@428 76 die "openpkg:index:FATAL: The -C option requires an installed DB_File perl module.";
michael@428 77 }
michael@428 78 eval {
michael@428 79 require Fcntl;
michael@428 80 };
michael@428 81 if ($@) {
michael@428 82 die "openpkg:index:FATAL: The -C option requires an installed Fcntl perl module.";
michael@428 83 }
michael@428 84 tie %cache, 'DB_File', $opt_C, Fcntl::O_CREAT()|Fcntl::O_RDWR(), 0666, $DB_File::DB_HASH
michael@428 85 or die "openpkg:index:FATAL: cannot tie cache '$opt_C' ($!)";
michael@428 86 }
michael@428 87
michael@428 88 # provide default for repository path
michael@428 89 $opt_r = 'OpenPKG-CURRENT/Source/' if (not defined($opt_r));
michael@428 90
michael@428 91 # create make_resource function closure
michael@428 92 my $make_resource = gen_make_resource();
michael@428 93
michael@428 94 # determine output channel
michael@428 95 my $fh;
michael@428 96 my $tmpo;
michael@428 97 if (defined($opt_o)) {
michael@428 98 $tmpo = $opt_o . '.tmp';
michael@428 99 if ($opt_c) {
michael@428 100 open(FH, "| $BZ -c > '$tmpo'")
michael@428 101 or die "openpkg:index:FATAL: cannot write '$tmpo' ($!)";
michael@428 102 } else {
michael@428 103 open(FH, "> $tmpo")
michael@428 104 or die "openpkg:index:FATAL: cannot write '$tmpo' ($!)";
michael@428 105 }
michael@428 106 } else {
michael@428 107 if ($opt_c) {
michael@428 108 open(FH, "| $BZ -c")
michael@428 109 or die "openpkg:index:FATAL: cannot write to stdout ($!)";
michael@428 110 } else {
michael@428 111 open(FH, ">&=1")
michael@428 112 or die "openpkg:index:FATAL: cannot write to stdout ($!)";
michael@428 113 }
michael@428 114 }
michael@428 115 $fh = \*FH;
michael@428 116
michael@428 117 # generate XML/RDF output
michael@428 118 xml_head($fh, $opt_r);
michael@428 119 foreach my $prefix (@ARGV) {
michael@428 120 my $list;
michael@428 121 if (-d $prefix) {
michael@428 122 if ($opt_i) {
michael@428 123 $list = list_rpmdir($prefix);
michael@428 124 } else {
michael@428 125 $list = list_specdir($prefix);
michael@428 126 }
michael@428 127 } else {
michael@428 128 $list = [ $prefix ];
michael@428 129 $prefix = dirname($prefix);
michael@428 130 }
michael@428 131 write_index($fh, $prefix, $opt_r, $opt_p, $list, $opt_C ? \%cache : undef);
michael@428 132 }
michael@428 133 xml_foot($fh);
michael@428 134
michael@428 135 # close output channel
michael@428 136 close($fh)
michael@428 137 or die "openpkg:index:FATAL: write error on output ($!)";
michael@428 138
michael@428 139 # post-process output
michael@428 140 if (defined($tmpo)) {
michael@428 141 rename($tmpo, $opt_o)
michael@428 142 or die "openpkg:index:FATAL: cannot rename $tmpo to $opt_o ($!)";
michael@428 143 }
michael@428 144
michael@428 145 # die gracefully
michael@428 146 exit(0);
michael@428 147
michael@428 148 #############################################################################
michael@428 149 ##
michael@428 150 ## FUNCTIONS: PARSING
michael@428 151 ##
michael@428 152 #############################################################################
michael@428 153
michael@428 154 # home-brewn getopt(3) style option parser
michael@428 155 sub getopts ($) {
michael@428 156 my ($opts) = @_;
michael@428 157 my (%optf) = map { m/(\w)/; $1 => $_ } $opts =~ m/(\w:|\w)/g;
michael@428 158 my (%opts, @argv, $optarg);
michael@428 159
michael@428 160 foreach (@ARGV) {
michael@428 161 if (@argv) {
michael@428 162 push @argv, $_;
michael@428 163 } elsif (defined $optarg) {
michael@428 164 if (exists $opts{$optarg}) {
michael@428 165 $opts{$optarg} .= " $_";
michael@428 166 } else {
michael@428 167 $opts{$optarg} = $_;
michael@428 168 }
michael@428 169 $optarg = undef;
michael@428 170 } elsif (!/^[-]/) {
michael@428 171 push @argv, $_;
michael@428 172 } else {
michael@428 173 while (/^\-(\w)(.*)/) {
michael@428 174 if (exists $optf{$1}) {
michael@428 175 if (length($optf{$1}) > 1) {
michael@428 176 if ($2 ne '') {
michael@428 177 if (exists $opts{$1}) {
michael@428 178 $opts{$1} .= " $2";
michael@428 179 } else {
michael@428 180 $opts{$1} = $2;
michael@428 181 }
michael@428 182 } else {
michael@428 183 $optarg = $1;
michael@428 184 }
michael@428 185 last;
michael@428 186 } else {
michael@428 187 $opts{$1} = 1;
michael@428 188 }
michael@428 189 } else {
michael@428 190 warn "openpkg:index:WARNING: unknown option $_\n";
michael@428 191 }
michael@428 192 $_ = "-$2";
michael@428 193 }
michael@428 194 }
michael@428 195 }
michael@428 196 if (defined $optarg) {
michael@428 197 warn "openpkg:index:WARNING: option $optarg requires an argument\n";
michael@428 198 }
michael@428 199 foreach (keys %opts) {
michael@428 200 eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";';
michael@428 201 }
michael@428 202 @ARGV = @argv;
michael@428 203 }
michael@428 204
michael@428 205 #############################################################################
michael@428 206 ##
michael@428 207 ## OpenPKG RPM Package Specification Parsing
michael@428 208 ##
michael@428 209 #############################################################################
michael@428 210
michael@428 211 # escape XML for output in RDF file
michael@428 212 sub e ($) {
michael@428 213 my ($s) = @_;
michael@428 214 my ($i);
michael@428 215
michael@428 216 # remove trailing whitespace
michael@428 217 $s =~ s/\n+$//sg;
michael@428 218 $s =~ s/[^\S\n]+$//mg;
michael@428 219
michael@428 220 # remove common leading whitespace
michael@428 221 $i = undef;
michael@428 222 while ($s =~ m/^([^\S\n]+)/mg) {
michael@428 223 $i = $1 if (!defined($i) || (length($1) < length($i)));
michael@428 224 }
michael@428 225 $s =~ s/^\Q$i\E//mg if (defined($i));
michael@428 226
michael@428 227 # escape XML special characters
michael@428 228 $s =~ s/&/&amp;/sg;
michael@428 229 $s =~ s/</&lt;/sg;
michael@428 230 $s =~ s/>/&gt;/sg;
michael@428 231
michael@428 232 return $s;
michael@428 233 }
michael@428 234
michael@428 235 # make_resource closure
michael@428 236 sub gen_make_resource {
michael@428 237 # generate operator regular expression
michael@428 238 # (used in make_resource below)
michael@428 239 my %attrname = (
michael@428 240 '==' => 'equ',
michael@428 241 '=' => 'equ',
michael@428 242 '>=' => 'geq',
michael@428 243 '=>' => 'geq',
michael@428 244 '<=' => 'leq',
michael@428 245 '=<' => 'leq',
michael@428 246 '>' => 'gt',
michael@428 247 '<' => 'lt'
michael@428 248 );
michael@428 249 my ($opreg) =
michael@428 250 join '|',
michael@428 251 map {
michael@428 252 "\Q$_\E"
michael@428 253 } sort {
michael@428 254 length($b) <=> length($a)
michael@428 255 || $b cmp $a
michael@428 256 } keys(%attrname);
michael@428 257
michael@428 258 # return function which makes resource object
michael@428 259 return sub ($) {
michael@428 260 my ($s) = @_;
michael@428 261
michael@428 262 if ($s =~ m/(\S+)\s*($opreg)\s*(.*?)\s*$/o) {
michael@428 263 return {
michael@428 264 resource => $1,
michael@428 265 attrname => $attrname{$2},
michael@428 266 attrval => $3
michael@428 267 }
michael@428 268 }
michael@428 269 else {
michael@428 270 return {
michael@428 271 resource => $s
michael@428 272 }
michael@428 273 }
michael@428 274 };
michael@428 275 };
michael@428 276
michael@428 277 # split string into array at comma seperator
michael@428 278 # (optioanlly map resource operators into resource objects)
michael@428 279 sub commasep ($$) {
michael@428 280 my ($k, $v) = @_;
michael@428 281
michael@428 282 if ($k =~ m/^(NoSource)$/) {
michael@428 283 return split(/\s*,\s*/, $v);
michael@428 284 } elsif ($k =~ /^(PreReq|BuildPreReq|Provides|Conflicts)$/) {
michael@428 285 return map { &$make_resource($_) }
michael@428 286 split(/\s*,\s*/, $v);
michael@428 287 }
michael@428 288 return $v;
michael@428 289 }
michael@428 290
michael@428 291 # escape option string
michael@428 292 sub optesc ($) {
michael@428 293 my ($s) = @_;
michael@428 294
michael@428 295 $s =~ s/([\x00-\x1f\x80-\xbf\s\%])/sprintf("%%%02x", ord($1))/eg;
michael@428 296 return $s;
michael@428 297 }
michael@428 298
michael@428 299 # variable substitution
michael@428 300 sub vsub ($$) {
michael@428 301 my ($var, $v) = @_;
michael@428 302
michael@428 303 $v =~ s/\%\{([^}]+)\}/
michael@428 304 exists $var->{$1} ? $var->{$1} : '%{'.$1.'}'/emg;
michael@428 305 return $v;
michael@428 306 }
michael@428 307
michael@428 308 # Umgekehrte Polnische Notation (UPN)
michael@428 309 # Reverse Polish Notation (RPN)
michael@428 310 # << ( %{foo} == "yes" ) && ( ! %{bar} == "no" ) || ( %{baz} == "yes" )
michael@428 311 # >> %{foo} %{bar} ! && %{baz} ||
michael@428 312 sub upn ($) {
michael@428 313 my ($t) = @_;
michael@428 314 my (@tok);
michael@428 315 my (@out, $op, $o);
michael@428 316 my (@save);
michael@428 317
michael@428 318 # split string into tokens
michael@428 319 @tok = ($t =~ m/(\(|\)|\&\&|\|\||\!|\S+)/g);
michael@428 320
michael@428 321 # iterate over all tokens
michael@428 322 $op = [];
michael@428 323 foreach (@tok) {
michael@428 324 if ($_ eq '(') {
michael@428 325 push(@save, $op);
michael@428 326 $op = [];
michael@428 327 } elsif ($_ eq ')') {
michael@428 328 die "openpkg:index:FATAL: unresolved operators in: @tok\n" if (@$op);
michael@428 329 $op = pop(@save)
michael@428 330 or die "openpkg:index:FATAL: unmatched closing parenthesis in: @tok\n";
michael@428 331 while ($o = pop(@$op)) {
michael@428 332 push(@out, $o->[0]);
michael@428 333 last if ($o->[1]);
michael@428 334 }
michael@428 335 } elsif ($_ eq '&&') {
michael@428 336 push(@$op, [ '+', 1 ]);
michael@428 337 } elsif ($_ eq '||') {
michael@428 338 push(@$op, [ '|', 1 ]);
michael@428 339 } elsif ($_ eq '!') {
michael@428 340 push(@$op, [ '!', 0 ]);
michael@428 341 } elsif (m/^\%\{(\S*?)\}$/) {
michael@428 342 push(@out, $1);
michael@428 343 while ($o = pop(@$op)) {
michael@428 344 push(@out, $o->[0]);
michael@428 345 last if ($o->[1]); # binary operator
michael@428 346 }
michael@428 347 }
michael@428 348 }
michael@428 349 return join (' ', @out);
michael@428 350 }
michael@428 351
michael@428 352 # deduce external variables from description
michael@428 353 # (backward compatibility for times before openpkg-20021230)
michael@428 354 sub find_options ($) {
michael@428 355 my ($descr) = @_;
michael@428 356 my $evar = {};
michael@428 357 $descr =~ s/--define\s*'(\S+)\s*\%\{\1\}'/$evar->{$1} = '%{'.$1.'}', ''/sge;
michael@428 358 return $evar;
michael@428 359 }
michael@428 360
michael@428 361 # translate default section from spec-file into a hash
michael@428 362 # - %if/%ifdef/%define... are translated to #/#ifdef/#define
michael@428 363 # - #defines are interpolated (correct ?)
michael@428 364 # - #if/#ifdef/... sections are stripped
michael@428 365 # result is the same as if all conditions evaluate false (!)
michael@428 366 # - all attributes are of the form key: value
michael@428 367 # - repeated attributes are coalesced into a list
michael@428 368 # hint: evar = expansion variables, ovar = option variables
michael@428 369 sub package2data ($$) {
michael@428 370 my ($s, $ovar) = @_;
michael@428 371 my (%evar, %var);
michael@428 372 my (@term, $term);
michael@428 373 my (%attr, %avar);
michael@428 374 my ($l, $v, $cond, $d, $p);
michael@428 375 my ($re, @defs);
michael@428 376
michael@428 377 # combine multilines (line continuation!)
michael@428 378 $s =~ s/\\\n/ /sg;
michael@428 379
michael@428 380 # map syntax of conditional variable macros
michael@428 381 $s =~ s/^#\{\!\?([^:]*):\s*%(.*?)\s*\}\s*$/#ifndef $1\n#$2\n#endif/mg;
michael@428 382 $s =~ s/^#\{\!\?([^:]*):\s*(.*?)\s*\}\s*$/#ifndef $1\n$2\n#endif/mg;
michael@428 383
michael@428 384 # map syntax of option macro
michael@428 385 $s =~ s/^#option\s+(\S+)\s*(.*?)\s*$/#ifndef $1\n#define $1 $2\n#endif\n#provides $1 $2/mg;
michael@428 386
michael@428 387 # use option variables for expansion
michael@428 388 %evar = %$ovar;
michael@428 389
michael@428 390 # guess more external parameters by scanning for (and removing) "set option default" sections
michael@428 391 $re = '^\#ifndef\s+[\w\_]+\s*\n((?:\#define\s+[\w\_]+\s.*\n)+)\#endif\n';
michael@428 392 @defs = ($s =~ m/$re/gm);
michael@428 393 foreach (@defs) {
michael@428 394 while (m/^\#define\s+([\w\_]+)\s(.*?)\s*$/mg) {
michael@428 395 $ovar->{$1} = $2;
michael@428 396 $evar{$1} = '%{'.$1.'}';
michael@428 397 }
michael@428 398 }
michael@428 399 $s =~ s/$re//gm;
michael@428 400
michael@428 401 # add everything looking like a "with_xxx" variable
michael@428 402 $re = '%{(with\_[\w\_]+)}';
michael@428 403 @defs = ($s =~ /$re/gm);
michael@428 404 foreach (@defs) {
michael@428 405 next if (exists($ovar->{$1}));
michael@428 406 $ovar->{$1} = '%{'.$1.'}'; # unexpanded
michael@428 407 $evar{$1} = '%{'.$1.'}';
michael@428 408 }
michael@428 409
michael@428 410 # extract all conditional sections (#if/#else/#endif)
michael@428 411 @term = ();
michael@428 412 %var = ();
michael@428 413 $cond = '';
michael@428 414 foreach $l (split(/\n/, $s)) {
michael@428 415 # expand variables
michael@428 416 $v = vsub(\%avar, vsub(\%var, $l));
michael@428 417
michael@428 418 if (($p) = ($v =~ /^\#if\s+(.*?)\s*$/)) {
michael@428 419 # normalize "%{variable}" != ..."
michael@428 420 $p =~ s/("\%\{[^}]+\}")\s*!=\s*"(yes|no)"/$1 . " == \"" . ($2 ne "yes" ? "yes" : "no") . "\""/sge;
michael@428 421
michael@428 422 # normalize #if expressions
michael@428 423 # - "%{variable}" == "yes"
michael@428 424 # - "%{variable}" == "no"
michael@428 425 # - operators ! && ||
michael@428 426 # warn on:
michael@428 427 # - operator !=
michael@428 428 # - any other word
michael@428 429 $term = '';
michael@428 430 while ($p =~ m/(!=)|(\!|\|\||\&\&|\(|\))|"\%\{([^}]+)\}"\s*==\s*"(yes|no)"|(\S+)/g) {
michael@428 431 if (defined($1)) {
michael@428 432 warn "openpkg:index:WARNING: unknown token '$1':\n< $l\n> $v";
michael@428 433 } elsif (defined($5)) {
michael@428 434 warn "openpkg:index:WARNING: unknown token '$5':\n< $l\n> $v";
michael@428 435 } elsif (defined($2)) {
michael@428 436 $term .= " $2 ";
michael@428 437 } elsif (exists($evar{$3})) {
michael@428 438 $term .= ($4 eq 'no' ? '! ' : '').vsub(\%evar, '%{'.$3.'}');
michael@428 439 } else {
michael@428 440 warn "openpkg:index:WARNING: unknown condition variable '$3':\n< $l\n> $v";
michael@428 441 }
michael@428 442 }
michael@428 443
michael@428 444 # join with previous conditions for this #if/#endif block
michael@428 445 if ($term ne '') {
michael@428 446 push(@term, "( $term )");
michael@428 447 $cond = join(' && ', grep { $_ ne '' } @term) . '';
michael@428 448 } else {
michael@428 449 push(@term, '');
michael@428 450 }
michael@428 451 }
michael@428 452 elsif ($v =~ /^\#else\s*$/) {
michael@428 453 # reverse last condition
michael@428 454 if (@term) {
michael@428 455 $term[-1] = ' ! '. $term[-1];
michael@428 456 $cond = join(' && ', grep { $_ ne '' } @term) . '';
michael@428 457 } else {
michael@428 458 die "openpkg:index:FATAL: \"else\" without \"if\"";
michael@428 459 }
michael@428 460 }
michael@428 461 elsif ($v =~ /^\#endif\s*$/) {
michael@428 462 # unwind last #if expression
michael@428 463 pop(@term);
michael@428 464 $cond = join(' && ', grep { $_ ne '' } @term) . '';
michael@428 465 }
michael@428 466 elsif ($v =~ /^\#(?:define)\s*(\S+)\s*(.*?)\s*$/) {
michael@428 467 # define conditional variables
michael@428 468 # - truth-value becomes current condition
michael@428 469 # define internal variables
michael@428 470 # - store for subsequent substitution
michael@428 471 if (exists($evar{$1})) {
michael@428 472 if ($2 eq 'yes') {
michael@428 473 if ($cond eq '') {
michael@428 474 $evar{$1} = "( \%\{$1\} )";
michael@428 475 } else {
michael@428 476 $evar{$1} = "( \%\{$1\} || ( $cond ) )";
michael@428 477 }
michael@428 478 } elsif ($2 eq 'no') {
michael@428 479 if ($cond eq '') {
michael@428 480 $evar{$1} = "( \%\{$1\} )";
michael@428 481 } else {
michael@428 482 $evar{$1} = "( %\{$1\} && ! ( $cond ) )";
michael@428 483 }
michael@428 484 } else {
michael@428 485 warn "openpkg:index:WARNING: logic too complex for '$1' (boolean expressions allowed only):\n< $l\n> $v";
michael@428 486 }
michael@428 487 } else {
michael@428 488 $var{$1} = $2;
michael@428 489 }
michael@428 490 }
michael@428 491 elsif ($v =~ /^\#(?:undefine)\s*(\S+)\s*$/) {
michael@428 492 # undefine conditional variables
michael@428 493 # undefine internal variables
michael@428 494 if (exists($evar{$1})) {
michael@428 495 $evar{$1} = "\%\{$1\}";
michael@428 496 } else {
michael@428 497 delete($var{$1});
michael@428 498 }
michael@428 499 }
michael@428 500 elsif ($v =~ /^\#(?:provides)\s*(\S+)\s*(.*?)\s*$/) {
michael@428 501 # store option for current condition
michael@428 502 if (exists($attr{'Name'}->{''})) {
michael@428 503 push(@{$attr{'Provides'}->{$cond}}, {
michael@428 504 resource => $attr{'Name'}->{''}->[0] . '::' . $1,
michael@428 505 attrname => 'equ',
michael@428 506 attrval => optesc($2)
michael@428 507 });
michael@428 508 } else {
michael@428 509 warn "openpkg:index:ERROR: no package name set for option \"$1 = $2\"";
michael@428 510 }
michael@428 511 }
michael@428 512 elsif ($v =~ /^\#NoSource\s*(.*?)\s*$/) {
michael@428 513 # store conditional NoSource attribute
michael@428 514 push(@{$attr{'NoSource'}->{$cond}}, commasep('NoSource', $1));
michael@428 515
michael@428 516 }
michael@428 517 elsif ($v =~ /^\s*([^\#]\S*)\s*:\s*(.*?)\s*$/) {
michael@428 518 # store "attribute: value" headers for current condition
michael@428 519 push(@{$attr{$1}->{$cond}}, commasep($1,$2));
michael@428 520 $avar{lc($1)} = $2 if ($cond eq '');
michael@428 521 }
michael@428 522 }
michael@428 523
michael@428 524 # return all header "attributes"
michael@428 525 return \%attr;
michael@428 526 }
michael@428 527
michael@428 528 # split spec file into sections starting with a %word
michael@428 529 # - concatenate extended lines
michael@428 530 # - strip comment lines
michael@428 531 # - map %command to #command
michael@428 532 # - split sections
michael@428 533 # - return package2data() from default section (before first %xxx section)
michael@428 534 sub spec2data ($) {
michael@428 535 my ($s) = @_;
michael@428 536 my (%map);
michael@428 537 my ($a, $o);
michael@428 538 my $spec = $s;
michael@428 539
michael@428 540 # remove comments
michael@428 541 $s =~ s/^\s*#.*?\n//mg;
michael@428 542
michael@428 543 # map commands
michael@428 544 $s =~ s/^%(ifdef|ifndef|if|NoSource|option|undefine|define|else|endif|\{)/#$1/mg;
michael@428 545
michael@428 546 # split sections
michael@428 547 foreach (split(m/^(?=%\w+\s*\n)/m, $s)) {
michael@428 548 if (m/^%(\w+)\s*\n/) {
michael@428 549 $map{$1} .= $';
michael@428 550 } else {
michael@428 551 $map{'*'} .= $_;
michael@428 552 }
michael@428 553 }
michael@428 554
michael@428 555 # translate package information into "data"
michael@428 556 if (exists($map{'description'})) {
michael@428 557 # backward compatibility (options are in description)
michael@428 558 $o = find_options($map{'description'});
michael@428 559 $a = package2data($map{'*'}, $o );
michael@428 560 $a->{'Description'} = { '' => [ $map{'description'} ] };
michael@428 561 } else {
michael@428 562 # standard case
michael@428 563 $a = package2data($map{'*'}, {});
michael@428 564 }
michael@428 565 return $a;
michael@428 566 }
michael@428 567
michael@428 568 #############################################################################
michael@428 569 ##
michael@428 570 ## XML/RDF Generation
michael@428 571 ##
michael@428 572 #############################################################################
michael@428 573
michael@428 574 # start of XML file
michael@428 575 sub xml_head ($$) {
michael@428 576 my ($fh, $res) = @_;
michael@428 577 print $fh <<EOFEOF;
michael@428 578 <?xml version="1.0" encoding="iso-8859-1"?>
michael@428 579 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
michael@428 580 xmlns="http://www.openpkg.org/xml-rdf-index/0.9">
michael@428 581 <Repository rdf:resource="$res">
michael@428 582 EOFEOF
michael@428 583 }
michael@428 584
michael@428 585 # end of XML file, corresponds with start tags
michael@428 586 sub xml_foot ($) {
michael@428 587 my($fh) = @_;
michael@428 588 print $fh <<EOFEOF;
michael@428 589 </Repository>
michael@428 590 </rdf:RDF>
michael@428 591 EOFEOF
michael@428 592 }
michael@428 593
michael@428 594 # conditional expansion of attribute
michael@428 595 sub n ($$) {
michael@428 596 my ($a, $k) = @_;
michael@428 597 return if (not $a->{$k});
michael@428 598 return if (not $a->{$k}->{''});
michael@428 599 return ($a->{$k}->{''}->[0]);
michael@428 600 }
michael@428 601
michael@428 602 # send out $a->{$k} as text-style tag
michael@428 603 sub xml_text ($$$;$) {
michael@428 604 my ($i, $a, $k, $tag) = @_;
michael@428 605 my ($out);
michael@428 606 return "" if (not exists($a->{$k}));
michael@428 607 $tag = $k if (not defined($tag));
michael@428 608 $i = ' ' x $i;
michael@428 609 $out = e(n($a, $k));
michael@428 610 return if $out eq '';
michael@428 611 return "$i<$tag>\n$out\n$i</$tag>\n";
michael@428 612 }
michael@428 613
michael@428 614 # send out @{$a->{$k}} as body of an XML tag
michael@428 615 # $k is the name of the tag unless overridden by $tag
michael@428 616 # $i denotes the depth of indentation to form nicely
michael@428 617 # looking files.
michael@428 618 # all data from the list is flattened into a single
michael@428 619 # body, separated by LF and escaped for XML metachars.
michael@428 620 sub xml_tag ($$$;$) {
michael@428 621 my ($i, $a, $k, $tag) = @_;
michael@428 622 my ($out, $cond, $upn);
michael@428 623 return "" if (not exists($a->{$k}));
michael@428 624 $tag = $k if (not defined($tag));
michael@428 625 $out = '';
michael@428 626 $i = ' ' x $i;
michael@428 627 foreach $cond (sort keys(%{$a->{$k}})) {
michael@428 628 $upn = e(upn($cond));
michael@428 629 $out .= $i .
michael@428 630 ($cond ne '' ? "<$tag cond=\"$upn\">" : "<$tag>") .
michael@428 631 join("\n", map { e($_) } @{$a->{$k}->{$cond}}) .
michael@428 632 "</$tag>\n";
michael@428 633 }
michael@428 634 return $out;
michael@428 635 }
michael@428 636
michael@428 637 # send out @{$a->{$k}} as a rdf:bag
michael@428 638 # $k is the name of the outer tag unless overriden by $tag
michael@428 639 # $i denotes the depth of indentation, inner tags are indented
michael@428 640 # 2 or 4 more character positions.
michael@428 641 # each element of the bag is listed
michael@428 642 sub xml_bag ($$$;$) {
michael@428 643 my ($i, $a, $k, $tag) = @_;
michael@428 644 my ($out, $cond, $upn);
michael@428 645 return "" if (not exists($a->{$k}));
michael@428 646 $tag = $k if (not defined($tag));
michael@428 647 $out = '';
michael@428 648 $i = ' ' x $i;
michael@428 649 foreach $cond (sort keys %{$a->{$k}}) {
michael@428 650 next if (not @{$a->{$k}->{$cond}});
michael@428 651 $upn = e(upn($cond));
michael@428 652 $out .= $i .
michael@428 653 ($cond ne '' ? "<$tag cond=\"$upn\">\n" : "<$tag>\n") .
michael@428 654 "$i <rdf:bag>\n" .
michael@428 655 join("",
michael@428 656 map {
michael@428 657 ref $_
michael@428 658 ? "$i <resource".
michael@428 659 ( exists $_->{attrname}
michael@428 660 ? " $_->{attrname}=\"".e($_->{attrval})."\""
michael@428 661 : ""
michael@428 662 ) .
michael@428 663 ">".e($_->{resource})."</resource>\n"
michael@428 664 : "$i <rdf:li>".e($_)."</rdf:li>\n"
michael@428 665 }
michael@428 666 @{$a->{$k}->{$cond}}) .
michael@428 667 "$i </rdf:bag>\n" .
michael@428 668 "$i</$tag>\n";
michael@428 669 }
michael@428 670 return $out;
michael@428 671 }
michael@428 672
michael@428 673 # send out reference to another RDF
michael@428 674 sub xml_reference ($$$$$$) {
michael@428 675 my($fh, $res, $href, $S, $T, $D) = @_;
michael@428 676 print $fh " <Repository rdf:resource=\"$res\" href=\"$href\"";
michael@428 677 if ($opt_S && defined($S) && $S ne "") {
michael@428 678 print $fh " size=\"$S\"";
michael@428 679 }
michael@428 680 if ($opt_T && defined($T) && $T ne "") {
michael@428 681 print $fh " mtime=\"$T\"";
michael@428 682 }
michael@428 683 if ($opt_D && defined($D) && $D ne "") {
michael@428 684 print $fh " md5=\"$D\"";
michael@428 685 }
michael@428 686 print $fh "/>\n";
michael@428 687 }
michael@428 688
michael@428 689 # send out reference to other resources
michael@428 690 sub xml_other ($$$$$$) {
michael@428 691 my($fh, $about, $href, $S, $T, $D) = @_;
michael@428 692 print $fh " <rdf:Description about=\"$about\" href=\"$href\"";
michael@428 693 if ($opt_S && defined($S) && $S ne "") {
michael@428 694 print $fh " size=\"$S\"";
michael@428 695 }
michael@428 696 if ($opt_T && defined($T) && $T ne "") {
michael@428 697 print $fh " mtime=\"$T\"";
michael@428 698 }
michael@428 699 if ($opt_D && defined($D) && $D ne "") {
michael@428 700 print $fh " md5=\"$D\"";
michael@428 701 }
michael@428 702 print $fh ">\n";
michael@428 703 print $fh " </rdf:Description>\n";
michael@428 704 }
michael@428 705
michael@428 706 # translate attributes from %$a as generated by package2data
michael@428 707 # into XML and write to file $fh
michael@428 708 sub xml_record ($$$$$$) {
michael@428 709 my ($fh, $a, $href, $S, $T, $D) = @_;
michael@428 710 my ($maj, $min, $rel, $about);
michael@428 711
michael@428 712 $about =
michael@428 713 n($a, 'Name') . '-' .
michael@428 714 n($a, 'Version') . '-' .
michael@428 715 n($a, 'Release');
michael@428 716 if (not defined($href)) {
michael@428 717 # guess location from Information in Specfile
michael@428 718 if (exists($a->{'NoSource'})) {
michael@428 719 $href = "$about.nosrc.rpm";
michael@428 720 } else {
michael@428 721 $href = "$about.src.rpm";
michael@428 722 }
michael@428 723 ($maj, $min, $rel) = (n($a, 'Release') =~ m/^(\d+)\.(\d+)\.(\d+)/);
michael@428 724 if (defined($min)) {
michael@428 725 if ($maj > 1 || ($maj == 1 && $min > 0)) {
michael@428 726 # OpenPKG-1.1-RELEASE or later
michael@428 727 if (n($a, 'Distribution') =~ /\[PLUS\]/) {
michael@428 728 $href = 'PLUS/'.$href;
michael@428 729 }
michael@428 730 }
michael@428 731 if ($maj > 1 || ($maj == 1 && $min >= 0)) {
michael@428 732 # OpenPKG-1.0-RELEASE or later
michael@428 733 if ($rel > 0) {
michael@428 734 $href = 'UPD/'.$href;
michael@428 735 }
michael@428 736 }
michael@428 737 } else {
michael@428 738 # OpenPKG-CURRENT
michael@428 739 }
michael@428 740 }
michael@428 741
michael@428 742 print $fh " <rdf:Description about=\"$about\" href=\"$href\"";
michael@428 743 if ($opt_S && defined($S) && $S ne "") {
michael@428 744 print $fh " size=\"$S\"";
michael@428 745 }
michael@428 746 if ($opt_T && defined($T) && $T ne "") {
michael@428 747 print $fh " mtime=\"$T\"";
michael@428 748 }
michael@428 749 if ($opt_D && defined($D) && $D ne "") {
michael@428 750 print $fh " md5=\"$D\"";
michael@428 751 }
michael@428 752 print $fh ">\n";
michael@428 753
michael@428 754 # fake Source attribute from Source\d attribtutes
michael@428 755 # XXX only default conditional
michael@428 756 $a->{'Source'} = { '' => [
michael@428 757 map {
michael@428 758 s/\Q%{name}\E/n($a, 'Name')/esg;
michael@428 759 s/\Q%{version}\E/n($a, 'Version')/esg;
michael@428 760 s/\Q%{release}\E/n($a, 'Release')/esg;
michael@428 761 $_;
michael@428 762 }
michael@428 763 map {
michael@428 764 $a->{$_}->{''} ? @{$a->{$_}->{''}} : ()
michael@428 765 }
michael@428 766 sort {
michael@428 767 my ($x) = ($a =~ /^(\d*)$/);
michael@428 768 my ($y) = ($b =~ /^(\d*)$/);
michael@428 769 return $x <=> $y;
michael@428 770 }
michael@428 771 grep {
michael@428 772 /^Source\d*$/
michael@428 773 } keys(%$a)
michael@428 774 ]};
michael@428 775 delete($a->{'Source'}) if (not @{$a->{'Source'}->{''}});
michael@428 776
michael@428 777 print $fh
michael@428 778 xml_tag(6, $a, 'Name'),
michael@428 779 xml_tag(6, $a, 'Version'),
michael@428 780 xml_tag(6, $a, 'Release'),
michael@428 781 xml_tag(6, $a, 'Distribution'),
michael@428 782 xml_tag(6, $a, 'Class'),
michael@428 783 xml_tag(6, $a, 'Group'),
michael@428 784 xml_tag(6, $a, 'License'),
michael@428 785 xml_tag(6, $a, 'Packager'),
michael@428 786 xml_tag(6, $a, 'Summary'),
michael@428 787 xml_tag(6, $a, 'URL'),
michael@428 788 xml_tag(6, $a, 'Vendor'),
michael@428 789 xml_tag(6, $a, 'SourceRPM'),
michael@428 790 xml_tag(6, $a, 'Arch'),
michael@428 791 xml_tag(6, $a, 'Os'),
michael@428 792 xml_tag(6, $a, 'BuildHost'),
michael@428 793 xml_tag(6, $a, 'BuildSystem'),
michael@428 794 xml_tag(6, $a, 'BuildTime'),
michael@428 795 xml_tag(6, $a, 'Relocations'),
michael@428 796 xml_tag(6, $a, 'Size'),
michael@428 797 xml_tag(6, $a, 'Prefixes'),
michael@428 798 xml_tag(6, $a, 'Platform'),
michael@428 799 xml_tag(6, $a, 'SigSize'),
michael@428 800 xml_tag(6, $a, 'SigMD5'),
michael@428 801 xml_tag(6, $a, 'SigPGP'),
michael@428 802 xml_tag(6, $a, 'SigGPG'),
michael@428 803 xml_bag(6, $a, 'BuildPreReq'),
michael@428 804 xml_bag(6, $a, 'PreReq'),
michael@428 805 xml_bag(6, $a, 'Provides'),
michael@428 806 xml_bag(6, $a, 'Conflicts'),
michael@428 807 xml_bag(6, $a, 'Source'),
michael@428 808 xml_bag(6, $a, 'NoSource'),
michael@428 809 xml_bag(6, $a, 'Filenames'),
michael@428 810 xml_text(6, $a, 'Description');
michael@428 811
michael@428 812 print $fh " </rdf:Description>\n";
michael@428 813 }
michael@428 814
michael@428 815 #############################################################################
michael@428 816 ##
michael@428 817 ## OpenPKG RPM Package Payload Extraction
michael@428 818 ##
michael@428 819 #############################################################################
michael@428 820
michael@428 821 # extract foo.spec from foo-V-R.src.rpm file
michael@428 822 sub rpm2spec ($) {
michael@428 823 my ($fn) = @_;
michael@428 824 local($SIG{'PIPE'}) = 'IGNORE';
michael@428 825 local(*PIPE);
michael@428 826 open(PIPE, "$R2C '$fn' |")
michael@428 827 or die "openpkg:index:FATAL: cannot read '$fn' ($!)\n";
michael@428 828 my ($buf, @hdr, $n, $m, $name, $step);
michael@428 829 my ($spec);
michael@428 830 while (read(PIPE, $buf, 110) == 110) {
michael@428 831 @hdr = unpack('a6a8a8a8a8a8a8a8a8a8a8a8a8a8', $buf);
michael@428 832 $n = hex($hdr[12]); # filename length
michael@428 833 $m = int(($n+5)/4)*4-2; # filename size (padded)
michael@428 834 last if (not (read(PIPE,$buf,$m) == $m));
michael@428 835 $name = substr($buf, 0, $n-1);
michael@428 836 $n = hex($hdr[7]); # file length
michael@428 837 $m = int(($n+3)/4)*4; # file size (padded)
michael@428 838 if ($name !~ m/.spec$/) {
michael@428 839 while ($m > 0) {
michael@428 840 $step = $m > 8192 ? 8192 : $m;
michael@428 841 last if (not read(PIPE,$buf,$step));
michael@428 842 $m -= length($buf);
michael@428 843 }
michael@428 844 } else {
michael@428 845 if (read(PIPE,$buf,$n) == $n) {
michael@428 846 $spec = $buf;
michael@428 847 }
michael@428 848 last;
michael@428 849 }
michael@428 850 }
michael@428 851 close(PIPE);
michael@428 852 return $spec;
michael@428 853 }
michael@428 854
michael@428 855 #############################################################################
michael@428 856 ##
michael@428 857 ## OpenPKG RPM Package Header Extraction
michael@428 858 ##
michael@428 859 #############################################################################
michael@428 860
michael@428 861 # extract header information from foo-V-R.src.rpm
michael@428 862 sub rpm2data ($$) {
michael@428 863 my ($fn, $platform) = @_;
michael@428 864 my ($q, %a);
michael@428 865 my ($t, $v);
michael@428 866 local(*PIPE);
michael@428 867
michael@428 868 if (not defined($platform)) {
michael@428 869 die "openpkg:index:FATAL: indexing binary package '$fn' requires -p option\n";
michael@428 870 }
michael@428 871
michael@428 872 $q = <<EOFEOF;
michael@428 873 Name %{Name}
michael@428 874 Version %{Version}
michael@428 875 Release %{Release}
michael@428 876 URL %{URL}
michael@428 877 Summary %{Summary}
michael@428 878 Copyright %{Copyright}
michael@428 879 License %{License}
michael@428 880 Distribution %{Distribution}
michael@428 881 Vendor %{Vendor}
michael@428 882 Class %{Class}
michael@428 883 Group %{Group}
michael@428 884 Packager %{Packager}
michael@428 885 Prefixes %{Prefixes}
michael@428 886 BuildHost %{BuildHost}
michael@428 887 BuildTime %{BuildTime}
michael@428 888 Arch %{Arch}
michael@428 889 Os %{Os}
michael@428 890 Size %{Size}
michael@428 891 SigSize %{SigSize}
michael@428 892 SigMD5 %{SigMD5}
michael@428 893 SigPGP %{SigPGP}
michael@428 894 SigGPG %{SigGPG}
michael@428 895 SourceRPM %{SourceRPM}
michael@428 896 [Patch %{Patch}
michael@428 897 ]
michael@428 898 [Source %{Source}
michael@428 899 ]
michael@428 900 [Filenames %{Filenames}
michael@428 901 ]
michael@428 902 [Conflicts %{CONFLICTNAME} %|CONFLICTFLAGS?{%{CONFLICTFLAGS:depflags} %{CONFLICTVERSION}}:{}|
michael@428 903 ]
michael@428 904 [PreReq %{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|
michael@428 905 ]
michael@428 906 [Provides %{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|
michael@428 907 ]
michael@428 908 Description %{Description}
michael@428 909 EOFEOF
michael@428 910
michael@428 911 open(PIPE, "$RPM -qp --qf '$q' '$fn' |")
michael@428 912 or die "FATAL: cannot read '$fn' ($!)\n";
michael@428 913 while (<PIPE>) {
michael@428 914 if (/^(\S+)\s+(.*?)\s*$/) {
michael@428 915 $t = $1;
michael@428 916 $v = $2;
michael@428 917 } elsif (/^(\s+.+?)\s*$/) {
michael@428 918 next if (not defined($t));
michael@428 919 $v = $1;
michael@428 920 } else {
michael@428 921 $t = undef;
michael@428 922 next;
michael@428 923 }
michael@428 924 if (exists($a{$t})) {
michael@428 925 $a{$t} .= "\n$v";
michael@428 926 } else {
michael@428 927 $a{$t} = $v;
michael@428 928 }
michael@428 929 }
michael@428 930 close(PIPE);
michael@428 931
michael@428 932 %a = map { $_ => $a{$_} }
michael@428 933 grep { $a{$_} ne '(none)' }
michael@428 934 keys(%a);
michael@428 935 if ($a{'Relocations'} eq '(non relocatable)') {
michael@428 936 delete($a{'Relocations'});
michael@428 937 }
michael@428 938 if ($a{'SigMD5'} eq '(unknown type)') {
michael@428 939 delete($a{'SigMD5'});
michael@428 940 }
michael@428 941 if (defined($platform)) {
michael@428 942 $a{'Platform'} = $platform;
michael@428 943 }
michael@428 944 $a{'Description'} = [ $a{'Description'} ];
michael@428 945
michael@428 946 foreach ('Conflicts', 'PreReq', 'Provides') {
michael@428 947 $a{$_} = [
michael@428 948 map { &$make_resource($_) }
michael@428 949 grep { !/^rpmlib\(/ }
michael@428 950 split(/\n+/, $a{$_})
michael@428 951 ];
michael@428 952 }
michael@428 953
michael@428 954 return { map {
michael@428 955 $_ => { '' => (ref $a{$_} ? $a{$_} : [ split(/\n+/, $a{$_}) ]) }
michael@428 956 } keys(%a) };
michael@428 957 }
michael@428 958
michael@428 959 #############################################################################
michael@428 960 ##
michael@428 961 ## OpenPKG XML/RDF Index Locating
michael@428 962 ##
michael@428 963 #############################################################################
michael@428 964
michael@428 965 # detect index files
michael@428 966 sub getindex ($) {
michael@428 967 my ($dir) = @_;
michael@428 968 my (@idx) =
michael@428 969 sort { -M $a <=> -M $b; }
michael@428 970 grep { -f $_ }
michael@428 971 ( <$dir/00INDEX.rdf>, <$dir/00INDEX.rdf.*> );
michael@428 972 return if (not @idx);
michael@428 973 return $idx[0];
michael@428 974 }
michael@428 975
michael@428 976 # list RPM directories of unpacked .spec files
michael@428 977 sub list_specdir ($) {
michael@428 978 my ($dir) = @_;
michael@428 979 my ($d, $path);
michael@428 980 my (@list);
michael@428 981
michael@428 982 local(*DH);
michael@428 983 opendir(DH, $dir);
michael@428 984 while ($d = readdir(DH)) {
michael@428 985 next if ($d =~ m/^\./);
michael@428 986 $path = "$dir/$d/$d.spec";
michael@428 987 push(@list, $path) if (-f $path);
michael@428 988 }
michael@428 989 closedir(DH);
michael@428 990 return \@list;
michael@428 991 }
michael@428 992
michael@428 993 # list index or RPM file directories
michael@428 994 sub list_rpmdir ($) {
michael@428 995 my ($dir) = @_;
michael@428 996 my ($d, $path);
michael@428 997 my (@list, $idx, $sub);
michael@428 998
michael@428 999 local(*DH);
michael@428 1000 opendir(DH, $dir);
michael@428 1001 while ($d = readdir(DH)) {
michael@428 1002 next if ($d =~ m/^\./);
michael@428 1003 $path = "$dir/$d";
michael@428 1004 if (-d $path) {
michael@428 1005 $idx = getindex($path);
michael@428 1006 if (defined($idx)) {
michael@428 1007 push(@list, $idx);
michael@428 1008 } else {
michael@428 1009 $sub = list_rpmdir($path);
michael@428 1010 push(@list, @$sub);
michael@428 1011 undef $sub;
michael@428 1012 }
michael@428 1013 } else {
michael@428 1014 next if (not ($d =~ m/\.(?:rpm|sh)$/ && -f $path));
michael@428 1015 push(@list, $path);
michael@428 1016 }
michael@428 1017 }
michael@428 1018 closedir(DH);
michael@428 1019 return \@list;
michael@428 1020 }
michael@428 1021
michael@428 1022 #############################################################################
michael@428 1023 ##
michael@428 1024 ## OpenPKG XML/RDF Index Reading
michael@428 1025 ##
michael@428 1026 #############################################################################
michael@428 1027
michael@428 1028 # fetch a whole file
michael@428 1029 sub readfile ($) {
michael@428 1030 my ($fn) = @_;
michael@428 1031 local(*FH);
michael@428 1032 open(FH, "< $fn")
michael@428 1033 or die "FATAL: cannot read '$fn' ($!)\n";
michael@428 1034 my $l; { local $/; $l = <FH>; }
michael@428 1035 close(FH);
michael@428 1036 return $l;
michael@428 1037 }
michael@428 1038
michael@428 1039 # create relative path by stripping instance prefix
michael@428 1040 sub relpath ($$) {
michael@428 1041 my ($prefix, $path) = @_;
michael@428 1042 $path =~ s/^\Q$prefix\E\///s;
michael@428 1043 return $path;
michael@428 1044 }
michael@428 1045
michael@428 1046 # create directory path with trailing slash
michael@428 1047 sub dirname ($) {
michael@428 1048 my ($path) = @_;
michael@428 1049 $path =~ s/\/[^\/]*$//s;
michael@428 1050 return $path.'/';
michael@428 1051 }
michael@428 1052
michael@428 1053 # peek at resource identifier of index file
michael@428 1054 sub getresource ($) {
michael@428 1055 my ($fn) = @_;
michael@428 1056 my ($fh, $buf);
michael@428 1057
michael@428 1058 local(*FH);
michael@428 1059 if ($fn =~ /\.bz2$/) {
michael@428 1060 open(FH, "$BZ -dc $fn |")
michael@428 1061 or die "FATAL: cannot read '$fn' ($!)\n";
michael@428 1062 } else {
michael@428 1063 open(FH, "< $fn")
michael@428 1064 or die "FATAL: cannot read '$fn' ($!)\n";
michael@428 1065 }
michael@428 1066 read(FH, $buf, 1024);
michael@428 1067 close(FH);
michael@428 1068 if ($buf =~ /<Repository.*?rdf:resource="([^"]+)"/) {
michael@428 1069 return $1;
michael@428 1070 }
michael@428 1071 return undef;
michael@428 1072 }
michael@428 1073
michael@428 1074 #############################################################################
michael@428 1075 ##
michael@428 1076 ## OpenPKG XML/RDF Index Output Generation
michael@428 1077 ##
michael@428 1078 #############################################################################
michael@428 1079
michael@428 1080 sub sizetime ($) {
michael@428 1081 my ($file) = @_;
michael@428 1082 my @stat = stat($file);
michael@428 1083 return ($stat[7], $stat[9]);
michael@428 1084 }
michael@428 1085
michael@428 1086 sub digest ($) {
michael@428 1087 my ($file) = @_;
michael@428 1088 my $digest = "";
michael@428 1089 my $output = `($OPENSSL md5 -hex "$file") 2>/dev/null || true`;
michael@428 1090 if ($output =~ m/MD5\(.+?\)=\s*([0-9a-fA-F]+)/s) {
michael@428 1091 $digest = uc($1);
michael@428 1092 }
michael@428 1093 return $digest;
michael@428 1094 }
michael@428 1095
michael@428 1096 sub write_index ($$$$$$) {
michael@428 1097 my ($fh, $prefix, $resource, $platform, $list, $cache) = @_;
michael@428 1098 my ($a, $h, $r, $spec, $T, $D);
michael@428 1099
michael@428 1100 foreach (@$list) {
michael@428 1101 $a = undef;
michael@428 1102 $h = undef;
michael@428 1103 $r = undef;
michael@428 1104 $S = undef;
michael@428 1105 $T = undef;
michael@428 1106 $D = undef;
michael@428 1107 $o = undef;
michael@428 1108
michael@428 1109 # determine information
michael@428 1110 if (m/\.spec$/) {
michael@428 1111 $spec = readfile($_);
michael@428 1112 $a = spec2data($spec);
michael@428 1113 ($S, $T) = sizetime($_) if ($opt_S or $opt_T);
michael@428 1114 $D = digest($_) if ($opt_D);
michael@428 1115 } elsif (m/([^\/]+\.(?:no)?src\.rpm)$/) {
michael@428 1116 $h = relpath($prefix, $_);
michael@428 1117 ($S, $T) = sizetime($_) if ($opt_S or $opt_T or $cache);
michael@428 1118 if ($cache) {
michael@428 1119 if ( exists($cache->{"T$_"})
michael@428 1120 && $cache->{"T$_"} == $T
michael@428 1121 && exists($cache->{"s$_"})
michael@428 1122 && $cache->{"s$_"} ne ""
michael@428 1123 && (!$opt_S || ($opt_S && exists($cache->{"S$_"}) && $cache->{"S$_"} ne ""))
michael@428 1124 && (!$opt_D || ($opt_D && exists($cache->{"D$_"}) && $cache->{"D$_"} ne ""))) {
michael@428 1125 # found in cache
michael@428 1126 $spec = $cache->{"s$_"};
michael@428 1127 $S = $cache->{"S$_"} if ($opt_S);
michael@428 1128 $D = $cache->{"D$_"} if ($opt_D);
michael@428 1129 } else {
michael@428 1130 # not found in cache
michael@428 1131 $spec = rpm2spec($_);
michael@428 1132 $cache->{"s$_"} = $spec;
michael@428 1133 $cache->{"S$_"} = $S;
michael@428 1134 $cache->{"T$_"} = $T;
michael@428 1135 $D = digest($_) if ($opt_D);
michael@428 1136 $cache->{"D$_"} = $D if ($opt_D);
michael@428 1137 }
michael@428 1138 } else {
michael@428 1139 # no cache at all
michael@428 1140 $spec = rpm2spec($_);
michael@428 1141 $D = digest($_) if ($opt_D);
michael@428 1142 }
michael@428 1143 $a = spec2data($spec);
michael@428 1144 } elsif (m/([^\/]+\.rpm)$/) {
michael@428 1145 $h = relpath($prefix, $_);
michael@428 1146 $a = rpm2data($_, $platform);
michael@428 1147 ($S, $T) = sizetime($_) if ($opt_S or $opt_T);
michael@428 1148 $D = digest($_) if ($opt_D);
michael@428 1149 } elsif (m/(([^\/]+)\.(?:no)?src\.sh)$/) {
michael@428 1150 $h = relpath($prefix, $_);
michael@428 1151 ($S, $T) = sizetime($_) if ($opt_S or $opt_T);
michael@428 1152 $D = digest($_) if ($opt_D);
michael@428 1153 $o = "$2:bootstrap";
michael@428 1154 } elsif (m/([^\/]+\.rdf[^\/]*)$/) {
michael@428 1155 $h = relpath($prefix, $_);
michael@428 1156 $r = getresource($_) || $resource.dirname($h);
michael@428 1157 ($S, $T) = sizetime($_) if ($opt_S or $opt_T);
michael@428 1158 $D = digest($_) if ($opt_D);
michael@428 1159 }
michael@428 1160
michael@428 1161 # process information
michael@428 1162 if ($a) {
michael@428 1163 xml_record($fh, $a, $h, $S, $T, $D);
michael@428 1164 } elsif ($o) {
michael@428 1165 xml_other($fh, $o, $h, $S, $T, $D);
michael@428 1166 } elsif ($r) {
michael@428 1167 xml_reference($fh, $r, $h, $S, $T, $D);
michael@428 1168 } else {
michael@428 1169 warn "openpkg:index:ERROR: cannot process $_";
michael@428 1170 }
michael@428 1171 }
michael@428 1172 }
michael@428 1173

mercurial