summaryrefslogtreecommitdiffstats
path: root/urpm/xml_info.pm
diff options
context:
space:
mode:
Diffstat (limited to 'urpm/xml_info.pm')
-rw-r--r--urpm/xml_info.pm168
1 files changed, 168 insertions, 0 deletions
diff --git a/urpm/xml_info.pm b/urpm/xml_info.pm
new file mode 100644
index 00000000..4148940f
--- /dev/null
+++ b/urpm/xml_info.pm
@@ -0,0 +1,168 @@
+package urpm::xml_info;
+
+use XML::LibXML::Reader;
+
+# throw an exception on error
+sub get_nodes {
+ my ($xml_info, $xml_info_file, $fullnames) = @_;
+
+ my $get_one_node = _get_one_node($xml_info);
+ _get_xml_info_nodes($xml_info_file, $get_one_node, $fullnames);
+}
+
+# throw an exception on error
+sub do_something_with_nodes {
+ my ($xml_info, $xml_info_file, $do, $o_wanted_attributes) = @_;
+
+ my $get_one_node = _get_one_node($xml_info, $o_wanted_attributes);
+ _do_something_with_xml_info_nodes($xml_info_file, $get_one_node, $do);
+}
+
+
+################################################################################
+sub _open_xml_reader {
+ my ($xml_info_file) = @_;
+
+ open(my $F, "lzma -dc $xml_info_file |");
+ my $reader = new XML::LibXML::Reader(IO => $F) or die "cannot read $xml_info_file\n";
+
+ $reader->read;
+ $reader->name eq 'media_info' or die "global <media_info> tag not found\n";
+
+ $reader->read; # first tag
+
+ $reader;
+}
+
+sub _get_all_attributes {
+ my ($reader) = @_;
+ my %entry;
+
+ $reader->moveToFirstAttribute;
+
+ do {
+ $entry{$reader->name} = $reader->value;
+ } while $reader->moveToNextAttribute == 1;
+
+ \%entry;
+}
+
+sub _get_attributes {
+ my ($reader, $o_wanted_attributes) = @_;
+
+ if ($o_wanted_attributes) {
+ my %entry = map { $_ => $reader->getAttribute($_) } @$o_wanted_attributes;
+ \%entry;
+ } else {
+ _get_all_attributes($reader);
+ }
+}
+
+sub _get_simple_value_node {
+ my ($value_name, $o_wanted_attributes) = @_;
+
+ sub {
+ my ($reader) = @_;
+ my $entry = _get_attributes($reader, $o_wanted_attributes);
+
+ $reader->read; # get value
+ $entry->{$value_name} = $reader->value;
+ $entry->{$value_name} =~ s/^\n//;
+
+ $reader->read; # close tag
+ $reader->read; # open next tag
+
+ $entry;
+ };
+}
+
+sub _get_changelog_node {
+ my ($reader, $fn) = @_;
+
+ $reader->nextElement('log'); # get first <log>
+
+ my @changelogs;
+ my $time;
+ while ($time = $reader->getAttribute('time')) {
+ push @changelogs, my $e = { time => $time };
+
+ $reader->nextElement('log_name'); $reader->read;
+ $e->{name} = $reader->value;
+
+ $reader->nextElement('log_text'); $reader->read;
+ $e->{text} = $reader->value;
+
+ $reader->read; # </log_text>
+ $reader->read; # </log>
+ $reader->read; # <log>
+ $reader->read if $reader->readState != 0; # there may be SIGNIFICANT_WHITESPACE between </log_text> and </log>
+ }
+
+ { fn => $fn, changelogs => \@changelogs };
+}
+
+sub _get_one_node {
+ my ($xml_info, $o_wanted_attributes) = @_;
+
+ if ($xml_info eq 'changelog') {
+ \&_get_changelog_node;
+ } elsif ($xml_info eq 'info') {
+ _get_simple_value_node('description', $o_wanted_attributes);
+ } else {
+ _get_simple_value_node('files', $o_wanted_attributes);
+ }
+}
+
+sub _get_xml_info_nodes {
+ my ($xml_info_file, $get_node, $fullnames) = @_;
+
+ my $fullnames_re = '^(' . join('|', map { quotemeta $_ } @$fullnames) . ')$';
+
+ my %todo = map { $_ => 1 } @$fullnames;
+ my %nodes;
+ _iterate_on_nodes($xml_info_file,
+ sub {
+ my ($reader, $fn) = @_;
+ if ($fn =~ /$fullnames_re/) {
+ $nodes{$fn} = $get_node->($reader);
+ delete $todo{$fn};
+ %todo == ();
+ } else {
+ $reader->next;
+ 0;
+ }
+ });
+
+ %todo and die "could not find " . join(', ', keys %todo) . " in $xml_info_file\n";
+
+ %nodes;
+}
+
+sub _do_something_with_xml_info_nodes {
+ my ($xml_info_file, $get_node, $do) = @_;
+
+ _iterate_on_nodes($xml_info_file,
+ sub {
+ my ($reader, $fn) = @_;
+ my $h = $get_node->($reader, $fn); # will read until closing tag
+ $do->($h);
+ 0;
+ });
+}
+
+sub _iterate_on_nodes {
+ my ($xml_info_file, $do) = @_;
+
+ my $reader = _open_xml_reader($xml_info_file);
+
+ my $fn;
+ while ($fn = $reader->getAttribute('fn')) {
+ $do->($reader, $fn) and return; # $do must go to next node otherwise it loops!
+ }
+
+ $reader->readState == 3 || $reader->name eq 'media_info'
+ or die qq(missing attribute "fn" in tag ") . $reader->name . qq("\n);
+}
+
+1;
+