File Coverage

blib/lib/Dpkg/Index.pm
Criterion Covered Total %
statement 65 142 45.7
branch 14 54 25.9
condition 4 23 17.3
subroutine 16 37 43.2
pod 14 14 100.0
total 113 270 41.8


line stmt bran cond sub pod time code
1             # Copyright © 2009 Raphaël Hertzog
2             # Copyright © 2012-2017 Guillem Jover
3             #
4             # This program is free software; you can redistribute it and/or modify
5             # it under the terms of the GNU General Public License as published by
6             # the Free Software Foundation; either version 2 of the License, or
7             # (at your option) any later version.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with this program. If not, see .
16              
17             package Dpkg::Index;
18              
19 4     4   69963 use strict;
  4         19  
  4         113  
20 4     4   19 use warnings;
  4         7  
  4         180  
21              
22             our $VERSION = '2.00';
23              
24 4     4   427 use Dpkg::Gettext;
  4         10  
  4         231  
25 4     4   472 use Dpkg::ErrorHandling;
  4         10  
  4         320  
26 4     4   436 use Dpkg::Control;
  4         9  
  4         335  
27              
28 4     4   26 use parent qw(Dpkg::Interface::Storable);
  4         8  
  4         26  
29              
30             use overload
31 0     0   0 '@{}' => sub { return $_[0]->{order} },
32 4     4   371 fallback => 1;
  4         9  
  4         32  
33              
34             =encoding utf8
35              
36             =head1 NAME
37              
38             Dpkg::Index - generic index of control information
39              
40             =head1 DESCRIPTION
41              
42             This class represent a set of Dpkg::Control objects.
43              
44             =head1 METHODS
45              
46             =over 4
47              
48             =item $index = Dpkg::Index->new(%opts)
49              
50             Creates a new empty index. See set_options() for more details.
51              
52             =cut
53              
54             sub new {
55 39     39 1 133 my ($this, %opts) = @_;
56 39   33     144 my $class = ref($this) || $this;
57              
58             my $self = {
59             items => {},
60             order => [],
61             unique_tuple_key => 1,
62 0     0   0 get_key_func => sub { return $_[0]->{Package} },
63 39         312 type => CTRL_UNKNOWN,
64             };
65 39         82 bless $self, $class;
66 39         160 $self->set_options(%opts);
67 39 50       123 if (exists $opts{load}) {
68 0         0 $self->load($opts{load});
69             }
70              
71 39         100 return $self;
72             }
73              
74             =item $index->set_options(%opts)
75              
76             The "type" option is checked first to define default values for other
77             options. Here are the relevant options: "get_key_func" is a function
78             returning a key for the item passed in parameters, "unique_tuple_key" is
79             a boolean requesting whether the default key should be the unique tuple
80             (default to true). The index can only contain one item with a given key.
81             The "get_key_func" function used depends on the type:
82              
83             =over
84              
85             =item *
86              
87             for CTRL_INFO_SRC, it is the Source field;
88              
89             =item *
90              
91             for CTRL_INDEX_SRC and CTRL_PKG_SRC it is the Package and Version fields
92             (concatenated with "_") when "unique_tuple_key" is true (the default), or
93             otherwise the Package field;
94              
95             =item *
96              
97             for CTRL_INFO_PKG it is simply the Package field;
98              
99             =item *
100              
101             for CTRL_INDEX_PKG and CTRL_PKG_DEB it is the Package, Version and
102             Architecture fields (concatenated with "_") when "unique_tuple_key" is
103             true (the default) or otherwise the Package field;
104              
105             =item *
106              
107             for CTRL_CHANGELOG it is the Source and the Version fields (concatenated
108             with an intermediary "_");
109              
110             =item *
111              
112             for CTRL_TESTS is either the Tests or Test-Command fields;
113              
114             =item *
115              
116             for CTRL_FILE_CHANGES it is the Source, Version and Architecture fields
117             (concatenated with "_");
118              
119             =item *
120              
121             for CTRL_FILE_VENDOR it is the Vendor field;
122              
123             =item *
124              
125             for CTRL_FILE_STATUS it is the Package and Architecture fields (concatenated
126             with "_");
127              
128             =item *
129              
130             otherwise it is the Package field by default.
131              
132             =back
133              
134             =cut
135              
136             sub set_options {
137 39     39 1 106 my ($self, %opts) = @_;
138              
139             # Default values based on type
140 39 50       103 if (exists $opts{type}) {
141 39         75 my $t = $opts{type};
142 39 50 0     163 if ($t == CTRL_INFO_PKG) {
    50 0        
    100          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
143 0     0   0 $self->{get_key_func} = sub { return $_[0]->{Package}; };
  0         0  
144             } elsif ($t == CTRL_INFO_SRC) {
145 0     0   0 $self->{get_key_func} = sub { return $_[0]->{Source}; };
  0         0  
146             } elsif ($t == CTRL_CHANGELOG) {
147             $self->{get_key_func} = sub {
148 498     498   915 return $_[0]->{Source} . '_' . $_[0]->{Version};
149 36         313 };
150             } elsif ($t == CTRL_COPYRIGHT_HEADER) {
151             # This is a bit pointless, because the value will almost always
152             # be the same, but guarantees that we use a known field.
153 0     0   0 $self->{get_key_func} = sub { return $_[0]->{Format}; };
  0         0  
154             } elsif ($t == CTRL_COPYRIGHT_FILES) {
155 0     0   0 $self->{get_key_func} = sub { return $_[0]->{Files}; };
  0         0  
156             } elsif ($t == CTRL_COPYRIGHT_LICENSE) {
157 0     0   0 $self->{get_key_func} = sub { return $_[0]->{License}; };
  0         0  
158             } elsif ($t == CTRL_TESTS) {
159             $self->{get_key_func} = sub {
160 9   66 9   16 return $_[0]->{Tests} || $_[0]->{'Test-Command'};
161 3         47 };
162             } elsif ($t == CTRL_INDEX_SRC or $t == CTRL_PKG_SRC) {
163 0 0 0     0 if ($opts{unique_tuple_key} // $self->{unique_tuple_key}) {
164             $self->{get_key_func} = sub {
165 0     0   0 return $_[0]->{Package} . '_' . $_[0]->{Version};
166 0         0 };
167             } else {
168             $self->{get_key_func} = sub {
169 0     0   0 return $_[0]->{Package};
170 0         0 };
171             }
172             } elsif ($t == CTRL_INDEX_PKG or $t == CTRL_PKG_DEB) {
173 0 0 0     0 if ($opts{unique_tuple_key} // $self->{unique_tuple_key}) {
174             $self->{get_key_func} = sub {
175             return $_[0]->{Package} . '_' . $_[0]->{Version} . '_' .
176 0     0   0 $_[0]->{Architecture};
177 0         0 };
178             } else {
179             $self->{get_key_func} = sub {
180 0     0   0 return $_[0]->{Package};
181 0         0 };
182             }
183             } elsif ($t == CTRL_FILE_CHANGES) {
184             $self->{get_key_func} = sub {
185             return $_[0]->{Source} . '_' . $_[0]->{Version} . '_' .
186 0     0   0 $_[0]->{Architecture};
187 0         0 };
188             } elsif ($t == CTRL_FILE_VENDOR) {
189 0     0   0 $self->{get_key_func} = sub { return $_[0]->{Vendor}; };
  0         0  
190             } elsif ($t == CTRL_FILE_STATUS) {
191             $self->{get_key_func} = sub {
192 0     0   0 return $_[0]->{Package} . '_' . $_[0]->{Architecture};
193 0         0 };
194             }
195             }
196              
197             # Options set by the user override default values
198 39         180 $self->{$_} = $opts{$_} foreach keys %opts;
199             }
200              
201             =item $index->get_type()
202              
203             Returns the type of control information stored. See the type parameter
204             set during new().
205              
206             =cut
207              
208             sub get_type {
209 0     0 1 0 my $self = shift;
210 0         0 return $self->{type};
211             }
212              
213             =item $index->add($item, [$key])
214              
215             Add a new item in the index. If the $key parameter is omitted, the key
216             will be generated with the get_key_func function (see set_options() for
217             details).
218              
219             =cut
220              
221             sub add {
222 507     507 1 764 my ($self, $item, $key) = @_;
223              
224 507   33     1264 $key //= $self->{get_key_func}($item);
225 507 50       1263 if (not exists $self->{items}{$key}) {
226 507         617 push @{$self->{order}}, $key;
  507         1033  
227             }
228 507         1484 $self->{items}{$key} = $item;
229             }
230              
231             =item $index->parse($fh, $desc)
232              
233             Reads the filehandle and creates all items parsed. When called multiple
234             times, the parsed stanzas are accumulated.
235              
236             Returns the number of items parsed.
237              
238             =cut
239              
240             sub parse {
241 3     3 1 7 my ($self, $fh, $desc) = @_;
242 3         8 my $item = $self->new_item();
243 3         4 my $i = 0;
244 3         15 while ($item->parse($fh, $desc)) {
245 9         25 $self->add($item);
246 9         19 $item = $self->new_item();
247 9         19 $i++;
248             }
249 1         5 return $i;
250             }
251              
252             =item $index->load($file)
253              
254             Reads the file and creates all items parsed. Returns the number of items
255             parsed. Handles compressed files transparently based on their extensions.
256              
257             =item $item = $index->new_item()
258              
259             Creates a new item. Mainly useful for derived objects that would want
260             to override this method to return something else than a Dpkg::Control
261             object.
262              
263             =cut
264              
265             sub new_item {
266 0     0 1 0 my $self = shift;
267 0         0 return Dpkg::Control->new(type => $self->{type});
268             }
269              
270             =item $item = $index->get_by_key($key)
271              
272             Returns the item identified by $key or undef.
273              
274             =cut
275              
276             sub get_by_key {
277 15     15 1 28 my ($self, $key) = @_;
278 15 50       68 return $self->{items}{$key} if exists $self->{items}{$key};
279 0         0 return;
280             }
281              
282             =item @keys = $index->get_keys(%criteria)
283              
284             Returns the keys of items that matches all the criteria. The key of the
285             %criteria hash is a field name and the value is either a regex that needs
286             to match the field value, or a reference to a function that must return
287             true and that receives the field value as single parameter, or a scalar
288             that must be equal to the field value.
289              
290             =cut
291              
292             sub get_keys {
293 7     7 1 16 my ($self, %crit) = @_;
294 7         13 my @selected = @{$self->{order}};
  7         21  
295 7         21 foreach my $s_crit (keys %crit) { # search criteria
296 0 0       0 if (ref($crit{$s_crit}) eq 'Regexp') {
    0          
297             @selected = grep {
298 0         0 exists $self->{items}{$_}{$s_crit} and
299 0 0       0 $self->{items}{$_}{$s_crit} =~ $crit{$s_crit}
300             } @selected;
301             } elsif (ref($crit{$s_crit}) eq 'CODE') {
302             @selected = grep {
303 0         0 $crit{$s_crit}->($self->{items}{$_}{$s_crit});
  0         0  
304             } @selected;
305             } else {
306             @selected = grep {
307 0         0 exists $self->{items}{$_}{$s_crit} and
308 0 0       0 $self->{items}{$_}{$s_crit} eq $crit{$s_crit}
309             } @selected;
310             }
311             }
312 7         20 return @selected;
313             }
314              
315             =item @items = $index->get(%criteria)
316              
317             Returns all the items that matches all the criteria.
318              
319             =cut
320              
321             sub get {
322 0     0 1 0 my ($self, %crit) = @_;
323 0         0 return map { $self->{items}{$_} } $self->get_keys(%crit);
  0         0  
324             }
325              
326             =item $index->remove_by_key($key)
327              
328             Remove the item identified by the given key.
329              
330             =cut
331              
332             sub remove_by_key {
333 0     0 1 0 my ($self, $key) = @_;
334 0         0 @{$self->{order}} = grep { $_ ne $key } @{$self->{order}};
  0         0  
  0         0  
  0         0  
335 0         0 return delete $self->{items}{$key};
336             }
337              
338             =item @items = $index->remove(%criteria)
339              
340             Returns and removes all the items that matches all the criteria.
341              
342             =cut
343              
344             sub remove {
345 0     0 1 0 my ($self, %crit) = @_;
346 0         0 my @keys = $self->get_keys(%crit);
347 0         0 my (%keys, @ret);
348 0         0 foreach my $key (@keys) {
349 0         0 $keys{$key} = 1;
350 0 0       0 push @ret, $self->{items}{$key} if defined wantarray;
351 0         0 delete $self->{items}{$key};
352             }
353 0         0 @{$self->{order}} = grep { not exists $keys{$_} } @{$self->{order}};
  0         0  
  0         0  
  0         0  
354 0         0 return @ret;
355             }
356              
357             =item $index->merge($other_index, %opts)
358              
359             Merge the entries of the other index. While merging, the keys of the merged
360             index are used, they are not re-computed (unless you have set the options
361             "keep_keys" to "0"). It's your responsibility to ensure that they have been
362             computed with the same function.
363              
364             =cut
365              
366             sub merge {
367 0     0 1 0 my ($self, $other, %opts) = @_;
368 0   0     0 $opts{keep_keys} //= 1;
369 0         0 foreach my $key ($other->get_keys()) {
370 0 0       0 $self->add($other->get_by_key($key), $opts{keep_keys} ? $key : undef);
371             }
372             }
373              
374             =item $index->sort(\&sortfunc)
375              
376             Sort the index with the given sort function. If no function is given, an
377             alphabetic sort is done based on the keys. The sort function receives the
378             items themselves as parameters and not the keys.
379              
380             =cut
381              
382             sub sort {
383 0     0 1 0 my ($self, $func) = @_;
384 0 0       0 if (defined $func) {
385 0         0 @{$self->{order}} = sort {
386 0         0 $func->($self->{items}{$a}, $self->{items}{$b})
387 0         0 } @{$self->{order}};
  0         0  
388             } else {
389 0         0 @{$self->{order}} = sort @{$self->{order}};
  0         0  
  0         0  
390             }
391             }
392              
393             =item $str = $index->output([$fh])
394              
395             =item "$index"
396              
397             Get a string representation of the index. The L objects are
398             output in the order which they have been read or added except if the order
399             have been changed with sort().
400              
401             Print the string representation of the index to a filehandle if $fh has
402             been passed.
403              
404             =cut
405              
406             sub output {
407 7     7 1 316 my ($self, $fh) = @_;
408 7         16 my $str = '';
409 7         25 foreach my $key ($self->get_keys()) {
410 15 50       35 if (defined $fh) {
411 0         0 print { $fh } $self->get_by_key($key) . "\n";
  0         0  
412             }
413 15 50       31 if (defined wantarray) {
414 15         36 $str .= $self->get_by_key($key) . "\n";
415             }
416             }
417 7         126 return $str;
418             }
419              
420             =item $index->save($file)
421              
422             Writes the content of the index in a file. Auto-compresses files
423             based on their extensions.
424              
425             =back
426              
427             =head1 CHANGES
428              
429             =head2 Version 2.00 (dpkg 1.20.0)
430              
431             Change behavior: The "unique_tuple_key" option now defaults to true.
432              
433             =head2 Version 1.01 (dpkg 1.19.0)
434              
435             New option: Add new "unique_tuple_key" option to $index->set_options() to set
436             better default "get_key_func" options, which will become the default behavior
437             in 1.20.x.
438              
439             =head2 Version 1.00 (dpkg 1.15.6)
440              
441             Mark the module as public.
442              
443             =cut
444              
445             1;