openpkg-index.pl 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. ##
  2. ## openpkg-index.pl -- create index from spec files
  3. ##
  4. ## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
  5. ## Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
  6. ## Copyright (c) 2000-2002 Ralf S. Engelschall <rse@engelschall.com>
  7. ##
  8. ## Permission to use, copy, modify, and distribute this software for
  9. ## any purpose with or without fee is hereby granted, provided that
  10. ## the above copyright notice and this permission notice appear in all
  11. ## copies.
  12. ##
  13. ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  14. ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  15. ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16. ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
  17. ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  18. ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  19. ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  20. ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  21. ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  22. ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  23. ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. ## SUCH DAMAGE.
  25. ##
  26. require 5;
  27. use strict;
  28. use Getopt::Std;
  29. getopts('r:p:C:o:ci');
  30. use vars qw/$opt_r $opt_p $opt_C $opt_o $opt_c $opt_i/;
  31. use FileHandle;
  32. use DirHandle;
  33. my $l_prefix = "@l_prefix@";
  34. my $RPM = "$l_prefix/bin/rpm";
  35. my $R2C = "$l_prefix/bin/rpm2cpio";
  36. my $BZ = "$l_prefix/lib/openpkg/bzip2 -9";
  37. #########################################################################
  38. #
  39. # escape XML special characters for output in RDF file
  40. #
  41. # remove trailing whitespace
  42. # remove common leading whitespace
  43. #
  44. sub e ($) {
  45. my($s) = @_;
  46. my($i);
  47. $s =~ s/\n+$//sg;
  48. $s =~ s/\s+$//mg;
  49. $i = undef;
  50. while ($s =~ /^(\s+)/mg) {
  51. $i = $1 if !defined $i || length($1) < length($i);
  52. }
  53. $s =~ s/^\Q$i\E//mg if defined $i;
  54. $s =~ s/&/&amp;/sg;
  55. $s =~ s/</&lt;/sg;
  56. $s =~ s/>/&gt;/sg;
  57. return $s;
  58. }
  59. sub commasep ($$) {
  60. my($k,$v) = @_;
  61. if ($k =~ /^(PreReq|BuildPreReq|Provides|Conflicts)$/) {
  62. return split(/\s*,\s*/, $v);
  63. }
  64. return $v;
  65. }
  66. sub vsub ($$) {
  67. my($var,$v) = @_;
  68. $v =~ s/\%\{([^}]+)\}/exists $var->{$1} ? $var->{$1} : '%{'.$1.'}'/emg;
  69. return $v;
  70. }
  71. sub upn ($) {
  72. my($t) = @_;
  73. my(@tok) = $t =~ /(\(|\)|\&\&|\|\||\!|\S+)/g;
  74. my(@out,$op,$o);
  75. my(@save);
  76. $op = [];
  77. foreach (@tok) {
  78. if ($_ eq '(') {
  79. push @save, $op;
  80. $op = [];
  81. } elsif ($_ eq ')') {
  82. die "FATAL: unresolved operators in: @tok\n" if @$op;
  83. $op = pop @save
  84. or die "FATAL: parenthesis stack underflow in: @tok\n";
  85. while ($o = pop @$op) {
  86. push @out, $o->[0];
  87. last if $o->[1];
  88. }
  89. } elsif ($_ eq '&&') {
  90. push @$op, [ '+', 1 ] ;
  91. } elsif ($_ eq '||') {
  92. push @$op, [ '|', 1 ] ;
  93. } elsif ($_ eq '!') {
  94. push @$op, [ '!', 0 ];
  95. } elsif (/^\%\{(\S*?)\}$/) {
  96. push @out, $1;
  97. while ($o = pop @$op) {
  98. push @out, $o->[0];
  99. last if $o->[1]; # binop
  100. }
  101. }
  102. }
  103. return join (' ',@out);
  104. }
  105. #
  106. # deduce external variables from description
  107. #
  108. sub find_options ($) {
  109. my($descr) = @_;
  110. my(%evar);
  111. %evar = map {
  112. $1 => '%{'.$1.'}'
  113. } $descr =~ /--define\s*'(\S+)\s*\%\{\1\}'/;
  114. return \%evar;
  115. }
  116. #
  117. # translate default section from spec-file
  118. # into a hash
  119. # %if/%ifdef/%define... are translated to #if/#ifdef/#define
  120. #
  121. # #defines are interpolated (correct ?)
  122. #
  123. # #if/#ifdef/... sections are stripped
  124. # result is the same as if all conditions evaluate false (!)
  125. #
  126. # all attributes are of the form key: value
  127. # repeated attributes are coalesced into a list
  128. #
  129. sub package2data ($$) {
  130. my($s,$evar) = @_;
  131. my(%var);
  132. my(@term, $term);
  133. my(%attr);
  134. my($l, $v, $cond, $d, $p);
  135. my($re,@defs);
  136. # combine multilines
  137. $s =~ s/\\\n/ /sg;
  138. #
  139. # map conditional variable macros
  140. #
  141. $s =~ s/^#\{\!\?([^:]*):\s*%(.*?)\s*\}\s*$/#ifndef $1\n#$2\n#endif/mg;
  142. $s =~ s/^#\{\!\?([^:]*):\s*(.*?)\s*\}\s*$/#ifndef $1\n$2\n#endif/mg;
  143. #
  144. # guess more external parameters by scanning for "default" sections.
  145. #
  146. $re = '^\#ifndef\s+[\w\_]+\s*\n((?:\#define\s+[\w\_]+\s.*\n)+)\#endif\n';
  147. @defs = $s =~ /$re/gm;
  148. foreach (@defs) {
  149. while (/^\#define\s+([\w\_]+)\s(.*?)\s*$/mg) {
  150. $evar->{$1} = '%{'.$1.'}';
  151. }
  152. }
  153. $s =~ s/$re//gm;
  154. #
  155. # add everything looking like a with_ variable
  156. #
  157. $re = '%{(with\_[\w\_]+)}';
  158. @defs = $s =~ /$re/gm;
  159. foreach (@defs) {
  160. $evar->{$1} = '%{'.$1.'}';
  161. }
  162. #
  163. # extract all conditional sections
  164. #
  165. @term = ();
  166. %var = ();
  167. $cond = '';
  168. foreach $l (split(/\n/, $s)) {
  169. $v = vsub(\%var,$l);
  170. if (($p) = $v =~ /^\#if\s+(.*?)\s*$/) {
  171. #
  172. # normalize #if expressions
  173. # "%{variable}" == "yes"
  174. # "%{variable}" == "no"
  175. # operators ! && ||
  176. #
  177. $term = '';
  178. while ($p =~ /(!=)|(\!|\|\||\&\&|\(|\))|"\%\{([^}]+)\}"\s*==\s*"(yes|no)"|(\S+)/g) {
  179. if (defined $1) {
  180. warn "WARNING: unknown token '$1':\n< $l\n> $v\n";
  181. } elsif (defined $5) {
  182. warn "WARNING: unknown token '$5':\n< $l\n> $v\n";
  183. } elsif (defined $2) {
  184. $term .= " $2 ";
  185. } elsif (exists $evar->{$3}) {
  186. $term .= ($4 eq 'no' ? '! ' : '').vsub($evar,'%{'.$3.'}');
  187. } else {
  188. warn "WARNING: unknown conditional '$2':\n< $l\n> $v\n";
  189. }
  190. }
  191. #
  192. # join with previous conditions for this #if/#endif block
  193. #
  194. if ($term ne '') {
  195. push @term, "( $term )";
  196. $cond = join(' && ', grep { $_ ne '' } @term).'';
  197. } else {
  198. push @term, '';
  199. }
  200. } elsif ($v =~ /^\#else\s*$/) {
  201. #
  202. # reverse last condition
  203. #
  204. if (@term) {
  205. $term[-1] = ' ! '.$term[-1];
  206. $cond = join(' && ', grep { $_ ne '' } @term).'';
  207. } else {
  208. die "FATAL: else without if\n";
  209. }
  210. } elsif ($v =~ /^\#endif\s*$/) {
  211. #
  212. # unwind last #if expression
  213. #
  214. pop @term;
  215. $cond = join(' && ', grep { $_ ne '' } @term).'';
  216. } elsif ($v =~ /^\#(?:define)\s*(\S+)\s*(.*?)\s*$/) {
  217. #
  218. # define conditional variables
  219. # truth-value becomes current condition
  220. #
  221. # define internal variables
  222. # -> store for subsequent substitution
  223. #
  224. if (exists $evar->{$1}) {
  225. if ($2 eq 'yes') {
  226. $evar->{$1} = "( \%\{$1\} || ( $cond ) )";
  227. } elsif ($2 eq 'no') {
  228. $evar->{$1} = "( %\{$1\} && ! ( $cond ) )";
  229. } else {
  230. warn "WARNING: logic too complex for '$1':\n< $l\n> $v\n";
  231. }
  232. } else {
  233. $var{$1} = $2;
  234. }
  235. } elsif ($v =~ /^\s*([^\#]\S*)\s*:\s*(.*?)\s*$/) {
  236. #
  237. # store attribute=value for current condition
  238. #
  239. push @{$attr{$1}->{$cond}}, commasep($1,$2);
  240. }
  241. }
  242. return \%attr;
  243. }
  244. #
  245. # split spec file into sections starting with a %word
  246. #
  247. # concatenate extended lines
  248. # strip comment lines
  249. # map %command to #command
  250. # split sections
  251. #
  252. # return package2data from default section.
  253. #
  254. sub spec2data ($) {
  255. my($s) = @_;
  256. my(%map);
  257. my($a,$o);
  258. # remove comments
  259. $s =~ s/^\s*#.*?\n//mg;
  260. # map commands
  261. $s =~ s/^%(ifdef|ifndef|if|define|else|endif|\{)/#$1/mg;
  262. # split sections
  263. foreach (split(/^(?=%\w+\s*\n)/m, $s)) {
  264. if (/^%(\w+)\s*\n/) {
  265. $map{$1} .= $';
  266. } else {
  267. $map{'*'} .= $_;
  268. }
  269. }
  270. $o = find_options($map{'description'});
  271. $a = package2data($map{'*'}, $o);
  272. if (exists $map{'description'}) {
  273. $a->{'Description'} = { '' => [ $map{'description'} ] };
  274. }
  275. return $a;
  276. }
  277. ##########################################################################
  278. #
  279. # start of XML file
  280. #
  281. sub xml_head ($$) {
  282. my($fh,$res) = @_;
  283. print $fh <<EOFEOF;
  284. <?xml version="1.0" encoding="iso-8859-1"?>
  285. <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  286. xmlns="http://www.openpkg.org/xml-rdf-index/0.9">
  287. <Repository rdf:resource="$res">
  288. EOFEOF
  289. }
  290. #
  291. # end of XML file, corresponds with start tags
  292. #
  293. sub xml_foot ($) {
  294. my($fh) = @_;
  295. print $fh <<EOFEOF;
  296. </Repository>
  297. </rdf:RDF>
  298. EOFEOF
  299. }
  300. sub n($$) {
  301. my($a,$k) = @_;
  302. return unless $a->{$k};
  303. return unless $a->{$k}->{''};
  304. return $a->{$k}->{''}->[0];
  305. }
  306. #
  307. # send out $a->{$k} as text-style tag
  308. #
  309. sub xml_text ($$$;$) {
  310. my($i,$a,$k,$tag) = @_;
  311. my($out);
  312. return "" unless exists $a->{$k};
  313. $tag = $k unless defined $tag;
  314. $i = ' ' x $i;
  315. $out = e(n($a,$k));
  316. return if $out eq '';
  317. return "$i<$tag>\n$out\n$i</$tag>\n";
  318. }
  319. #
  320. # send out @{$a->{$k}} as body of an XML tag
  321. # $k is the name of the tag unless overridden by $tag
  322. # $i denotes the depth of indentation to form nicely
  323. # looking files.
  324. #
  325. # all data from the list is flattened into a single
  326. # body, separated by LF and escaped for XML metachars.
  327. #
  328. sub xml_tag ($$$;$) {
  329. my($i,$a,$k,$tag) = @_;
  330. my($out,$cond,$upn);
  331. return "" unless exists $a->{$k};
  332. $tag = $k unless defined $tag;
  333. $out = '';
  334. $i = ' ' x $i;
  335. foreach $cond (sort keys %{$a->{$k}}) {
  336. $upn = e(upn($cond));
  337. $out .= $i.
  338. ($cond ne '' ? "<$tag cond=\"$upn\">" : "<$tag>").
  339. join("\n", map { e($_) } @{$a->{$k}->{$cond}}).
  340. "</$tag>\n";
  341. }
  342. return $out;
  343. }
  344. #
  345. # send out @{$a->{$k}} as a rdf:bag
  346. # $k is the name of the outer tag unless overriden by $tag
  347. # $i denotes the depth of indentation, inner tags are indented
  348. # 2 or 4 more character positions.
  349. #
  350. sub xml_bag ($$$;$) {
  351. my($i,$a,$k,$tag) = @_;
  352. my($out,$cond,$upn);
  353. return "" unless exists $a->{$k};
  354. $tag = $k unless defined $tag;
  355. $out = '';
  356. $i = ' ' x $i;
  357. foreach $cond (sort keys %{$a->{$k}}) {
  358. $upn = e(upn($cond));
  359. $out .= $i.
  360. ($cond ne '' ? "<$tag cond=\"$upn\">\n" : "<$tag>\n").
  361. "$i <rdf:bag>\n".
  362. join("",
  363. map { "$i <rdf:li>".e($_)."</rdf:li>\n" }
  364. @{$a->{$k}->{$cond}}).
  365. "$i </rdf:bag>\n".
  366. "$i</$tag>\n";
  367. }
  368. return $out;
  369. }
  370. #
  371. # send out reference to another RDF
  372. #
  373. sub xml_reference ($$$) {
  374. my($fh, $res, $href) = @_;
  375. print $fh <<EOFEOF;
  376. <Repository rdf:resource="$res" href="$href"/>
  377. EOFEOF
  378. }
  379. #
  380. # translate attributes from %$a as generated by package2data
  381. # into XML and write to file $fh
  382. #
  383. sub xml_record ($$$) {
  384. my($fh, $a, $href) = @_;
  385. my($maj,$min,$rel,$about);
  386. $about =
  387. n($a,'Name').'-'.
  388. n($a,'Version').'-'.
  389. n($a,'Release');
  390. unless (defined $href) {
  391. # guess location from Information in Specfile
  392. $href = "$about.src.rpm";
  393. ($maj,$min,$rel) = n($a,'Release') =~ /^(\d+)\.(\d+)\.(\d+)/;
  394. if (defined $min) {
  395. if ($maj > 1 || ($maj == 1 && $min > 0)) {
  396. # 1.1 or later
  397. if (n($a,'Distribution') =~ /\[PLUS\]/) {
  398. $href = 'PLUS/'.$href;
  399. }
  400. }
  401. if ($maj > 1 || ($maj == 1 && $min >= 0)) {
  402. # 1.0 or later
  403. if ($rel > 0) {
  404. $href = 'UPD/'.$href;
  405. }
  406. }
  407. } else {
  408. # current
  409. }
  410. }
  411. print $fh <<EOFEOF;
  412. <rdf:Description about="$about" href="$href">
  413. EOFEOF
  414. # fake Source attribute from Source\d attribtutes
  415. # XXX only default conditional
  416. $a->{'Source'} = { '' => [
  417. map {
  418. s/\Q%{name}\E/n($a,'Name')/esg;
  419. s/\Q%{version}\E/n($a,'Version')/esg;
  420. s/\Q%{release}\E/n($a,'Release')/esg;
  421. s/.*\///;
  422. $_;
  423. }
  424. map {
  425. $a->{$_}->{''} ? @{$a->{$_}->{''}} : ()
  426. }
  427. sort {
  428. my($x) = $a =~ /^(\d*)$/;
  429. my($y) = $b =~ /^(\d*)$/;
  430. return $x <=> $y;
  431. }
  432. grep {
  433. /^Source\d*$/
  434. } keys %$a
  435. ]};
  436. delete $a->{'Source'} unless @{$a->{'Source'}->{''}};
  437. print $fh
  438. xml_tag(6, $a, 'Name'),
  439. xml_tag(6, $a, 'Version'),
  440. xml_tag(6, $a, 'Release'),
  441. xml_tag(6, $a, 'Distribution'),
  442. xml_tag(6, $a, 'Group'),
  443. xml_tag(6, $a, 'License'),
  444. xml_tag(6, $a, 'Packager'),
  445. xml_tag(6, $a, 'Summary'),
  446. xml_tag(6, $a, 'URL'),
  447. xml_tag(6, $a, 'Vendor'),
  448. xml_tag(6, $a, 'SourceRPM'),
  449. xml_tag(6, $a, 'Arch'),
  450. xml_tag(6, $a, 'Os'),
  451. xml_tag(6, $a, 'BuildRoot'),
  452. xml_tag(6, $a, 'BuildHost'),
  453. xml_tag(6, $a, 'BuildSystem'),
  454. xml_tag(6, $a, 'BuildTime'),
  455. xml_tag(6, $a, 'Relocations'),
  456. xml_tag(6, $a, 'Size'),
  457. xml_tag(6, $a, 'Prefixes'),
  458. xml_tag(6, $a, 'Platform'),
  459. xml_tag(6, $a, 'SigSize'),
  460. xml_tag(6, $a, 'SigMD5'),
  461. xml_tag(6, $a, 'SigPGP'),
  462. xml_tag(6, $a, 'SigGPG'),
  463. xml_bag(6, $a, 'BuildPreReq'),
  464. xml_bag(6, $a, 'PreReq'),
  465. xml_bag(6, $a, 'Provides'),
  466. xml_bag(6, $a, 'Conflicts'),
  467. xml_bag(6, $a, 'Source'),
  468. xml_bag(6, $a, 'Filenames'),
  469. xml_text(6, $a, 'Description');
  470. print $fh <<EOFEOF;
  471. </rdf:Description>
  472. EOFEOF
  473. }
  474. #####################################################################
  475. sub rpm2spec ($) {
  476. my($fn) = @_;
  477. my($pipe) = new FileHandle "$R2C '$fn' |"
  478. or die "FATAL: cannot read '$fn' ($!)\n";
  479. my($buf,@hdr,$n,$m,$name,$step);
  480. my($spec);
  481. while (read($pipe,$buf,110) == 110) {
  482. @hdr = unpack('a6a8a8a8a8a8a8a8a8a8a8a8a8a8',$buf);
  483. $n = hex($hdr[12]); # filename length
  484. $m = int(($n+5)/4)*4-2; # filename size (padded)
  485. last unless read($pipe,$buf,$m) == $m;
  486. $name = substr($buf,0,$n-1);
  487. $n = hex($hdr[7]); # file length
  488. $m = int(($n+3)/4)*4; # file size (padded)
  489. if ($name !~ /.spec$/) {
  490. while ($m > 0) {
  491. $step = $m > 8192 ? 8192 : $m;
  492. last unless read($pipe,$buf,$step);
  493. $m -= length($buf);
  494. }
  495. } else {
  496. if (read($pipe,$buf,$n) == $n) {
  497. $spec = $buf;
  498. }
  499. last;
  500. }
  501. }
  502. $pipe->close;
  503. return $spec;
  504. }
  505. #####################################################################
  506. sub rpm2data ($$) {
  507. my($fn,$platform) = @_;
  508. my($q,$pipe,%a);
  509. my($t,$v);
  510. $q = <<EOFEOF;
  511. Name %{Name}
  512. Version %{Version}
  513. Release %{Release}
  514. URL %{URL}
  515. Summary %{Summary}
  516. Copyright %{Copyright}
  517. License %{License}
  518. Distribution %{Distribution}
  519. Vendor %{Vendor}
  520. Group %{Group}
  521. Packager %{Packager}
  522. Prefixes %{Prefixes}
  523. BuildRoot %{BuildRoot}
  524. BuildHost %{BuildHost}
  525. BuildTime %{BuildTime}
  526. Arch %{Arch}
  527. Os %{Os}
  528. Size %{Size}
  529. SigSize %{SigSize}
  530. SigMD5 %{SigMD5}
  531. SigPGP %{SigPGP}
  532. SigGPG %{SigGPG}
  533. SourceRPM %{SourceRPM}
  534. [Patch %{Patch}
  535. ]
  536. [Source %{Source}
  537. ]
  538. [Filenames %{Filenames}
  539. ]
  540. [Conflicts %{CONFLICTNAME} %|CONFLICTFLAGS?{%{CONFLICTFLAGS:depflags} %{CONFLICTVERSION}}:{}|
  541. ]
  542. [PreReq %{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|
  543. ]
  544. [Provides %{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|
  545. ]
  546. Description %{Description}
  547. EOFEOF
  548. $pipe = new FileHandle "$RPM -qp --qf '$q' '$fn' |"
  549. or die "FATAL: cannot read '$fn' ($!)\n";
  550. while (<$pipe>) {
  551. if (/^(\S+)\s+(.*?)\s*$/) {
  552. $t = $1;
  553. $v = $2;
  554. } elsif (/^(\s+.+?)\s*$/) {
  555. next unless defined $t;
  556. $v = $1;
  557. } else {
  558. $t = undef;
  559. next;
  560. }
  561. if (exists $a{$t}) {
  562. $a{$t} .= "\n$v";
  563. } else {
  564. $a{$t} = $v;
  565. }
  566. }
  567. $pipe->close;
  568. %a = map { $_ => $a{$_} }
  569. grep { $a{$_} ne '(none)' }
  570. keys %a;
  571. if ($a{'Relocations'} eq '(non relocatable)') {
  572. delete $a{'Relocations'};
  573. }
  574. if ($a{'SigMD5'} eq '(unknown type)') {
  575. delete $a{'SigMD5'};
  576. }
  577. $a{'Platform'} = "$a{'Arch'}-$platform-$a{'Os'}";
  578. $a{'PreReq'} =~ s/^rpmlib\(.*$//mg;
  579. $a{'Description'} = [ $a{'Description'} ];
  580. return { map {
  581. $_ => { '' => (ref $a{$_} ? $a{$_} : [ split(/\n+/, $a{$_}) ]) }
  582. } keys %a };
  583. }
  584. #####################################################################
  585. sub getindex ($) {
  586. my($dir) = @_;
  587. my(@idx) = sort { -M $a <=> -M $b; }
  588. grep { -f $_ }
  589. ( <$dir/00INDEX.rdf>, <$dir/00INDEX.rdf.*> );
  590. return unless @idx;
  591. return $idx[0];
  592. }
  593. sub list_specdir ($) {
  594. my($dir) = @_;
  595. my($dh,$d,$path);
  596. my(@list);
  597. $dh = new DirHandle($dir);
  598. while ($d = $dh->read) {
  599. next if $d =~ /^\./;
  600. $path = "$dir/$d/$d.spec";
  601. push @list, $path if -f $path;
  602. }
  603. return \@list;
  604. }
  605. sub list_rpmdir ($) {
  606. my($dir) = @_;
  607. my($dh,$d,$path);
  608. my(@list,$idx,$sub);
  609. $dh = new DirHandle($dir);
  610. while ($d = $dh->read) {
  611. next if $d =~ /^\./;
  612. $path = "$dir/$d";
  613. if (-d $path) {
  614. $idx = getindex($path);
  615. if (defined $idx) {
  616. push @list, $idx;
  617. } else {
  618. $sub = list_rpmdir($path);
  619. push @list, @$sub;
  620. undef $sub;
  621. }
  622. } else {
  623. next unless $d =~ /\.rpm$/ && -f $path;
  624. push @list, $path;
  625. }
  626. }
  627. return \@list;
  628. }
  629. #####################################################################
  630. sub readfile ($) {
  631. my($fn) = @_;
  632. my($fh) = new FileHandle "< $fn"
  633. or die "FATAL: cannot read '$fn' ($!)\n";
  634. my(@l) = <$fh>;
  635. $fh->close;
  636. return join('',@l);
  637. }
  638. sub relpath ($$) {
  639. my($prefix,$path) = @_;
  640. $path =~ s/^\Q$prefix\E\///s;
  641. return $path;
  642. }
  643. sub dirname ($) {
  644. my($path) = @_;
  645. $path =~ s/\/[^\/]*$//s;
  646. return $path.'/';
  647. }
  648. sub getresource ($) {
  649. my($fn) = @_;
  650. my($fh, $buf);
  651. if ($fn =~ /\.bz2$/) {
  652. $fh = new FileHandle "$BZ -dc $fn |"
  653. or die "FATAL: cannot read '$fn' ($!)\n";
  654. } else {
  655. $fh = new FileHandle "< $fn"
  656. or die "FATAL: cannot read '$fn' ($!)\n";
  657. }
  658. $fh->read($buf, 1024);
  659. $fh->close;
  660. if ($buf =~ /<Repository.*?rdf:resource="([^"]+)"/) {
  661. return $1;
  662. }
  663. return undef;
  664. }
  665. #####################################################################
  666. sub write_index ($$$$$$) {
  667. my($fh,$prefix,$resource,$platform,$list,$cache) = @_;
  668. my($a,$h,$r,$spec);
  669. my($mtime);
  670. foreach (@$list) {
  671. $a = undef;
  672. $h = undef;
  673. $r = undef;
  674. if (/\.spec$/) {
  675. $spec = readfile($_);
  676. $a = spec2data($spec);
  677. } elsif (/([^\/]+\.src\.rpm)$/) {
  678. $h = relpath($prefix, $_);
  679. if ($cache) {
  680. $mtime = (stat $_)[9];
  681. if (exists $cache->{"M$_"} &&
  682. $cache->{"M$_"} == $mtime) {
  683. $spec = $cache->{"S$_"};
  684. } else {
  685. $spec = rpm2spec($_);
  686. $cache->{"S$_"} = $spec;
  687. $cache->{"M$_"} = $mtime;
  688. }
  689. } else {
  690. $spec = rpm2spec($_);
  691. }
  692. $a = spec2data($spec);
  693. } elsif (/([^\/]+\.rpm)$/) {
  694. $h = relpath($prefix, $_);
  695. $a = rpm2data($_, $platform);
  696. } elsif (/([^\/]+\.rdf[^\/]*)$/) {
  697. $h = relpath($prefix, $_);
  698. $r = getresource($_) || $resource.dirname($h);
  699. }
  700. if ($a) {
  701. xml_record($fh, $a, $h);
  702. } elsif ($r) {
  703. xml_reference($fh, $r, $h);
  704. } else {
  705. warn "ERROR: cannot process $_\n";
  706. }
  707. }
  708. }
  709. #####################################################################
  710. my($prefix,$list,$fh,%cache,$tmpo);
  711. if ($#ARGV < 0) {
  712. print "openpkg:index:USAGE: $0 [-r resource] [-p platform] [-C cache.db] [-o index.rdf] [-c] [-i] dir ...\n";
  713. die "\n";
  714. }
  715. if ($opt_C) {
  716. require DB_File;
  717. tie %cache, 'DB_File', $opt_C, O_CREAT|O_RDWR, 0666, $DB_File::DB_HASH
  718. or die "FATAL: cannot tie cache '$opt_C' ($!)\n";
  719. }
  720. $opt_r = 'OpenPKG-CURRENT/Source/' unless defined $opt_r;
  721. $opt_p = 'unknown' unless defined $opt_p;
  722. if (defined $opt_o) {
  723. $tmpo = $opt_o . '.tmp';
  724. if ($opt_c) {
  725. $fh = new FileHandle "| $BZ -c > '$tmpo'"
  726. or die "FATAL: cannot write '$tmpo' ($!)\n";
  727. } else {
  728. $fh = new FileHandle "> $tmpo"
  729. or die "FATAL: cannot write '$tmpo' ($!)\n";
  730. }
  731. } else {
  732. if ($opt_c) {
  733. $fh = new FileHandle "| $BZ -c"
  734. or die "FATAL: cannot write to stdout ($!)\n";
  735. } else {
  736. $fh = new FileHandle ">&=1"
  737. or die "FATAL: cannot write to stdout ($!)\n";
  738. }
  739. }
  740. xml_head($fh, $opt_r);
  741. foreach $prefix (@ARGV) {
  742. die "FATAL: $prefix is not a directory\n" unless -d $prefix;
  743. if ($opt_i) {
  744. $list = list_rpmdir($prefix);
  745. } else {
  746. $list = list_specdir($prefix);
  747. }
  748. write_index($fh, $prefix, $opt_r, $opt_p, $list, $opt_C ? \%cache : undef);
  749. }
  750. xml_foot($fh);
  751. $fh->close
  752. or die "FATAL: write error on output ($!)\n";
  753. if (defined $tmpo) {
  754. rename $tmpo,$opt_o
  755. or die "FATAL: cannot rename $tmpo to $opt_o ($!)\n";
  756. }