File Coverage

blib/lib/MDV/Distribconf.pm
Criterion Covered Total %
statement 170 199 85.4
branch 94 124 75.8
condition 28 42 66.6
subroutine 21 22 95.4
pod 17 18 94.4
total 330 405 81.4


line stmt bran cond sub pod time code
1             package MDV::Distribconf;
2              
3             # $Id: Distribconf.pm 232708 2007-12-30 04:28:14Z nanardon $
4              
5             our $VERSION = '3.14';
6              
7             =head1 NAME
8              
9             MDV::Distribconf - Read and write config of a Mandriva Linux distribution tree
10              
11             =head1 SYNOPSIS
12              
13             use MDV::Distribconf;
14              
15             my $d = MDV::Distribconf->new("/path/to/the/distribution/root");
16             $d->load()
17             or die "This doesn't seem to be a distribution tree\n";
18              
19             print $d->getpath(undef, "root") ."\n";
20             foreach ($d->listmedia) {
21             printf "%s -> %s\n", $d->getpath($_, "hdlist"), $d->getpath($_, path);
22             }
23              
24             =head1 DESCRIPTION
25              
26             MDV::Distribconf is a module to get or write the configuration of a Mandriva
27             Linux distribution tree. This configuration is stored in a file called
28             F, aimed at replacing the old-style F file.
29              
30             The format of the F file is limited and doesn't allow to add new
31             values without breaking compatibility, while F is designed for
32             extensibility. To keep compatibility with old tools, this module is able
33             to generate an F file based on F.
34              
35             This module is able to manage both configuration of old-style trees
36             (F for OS versions 10.0 and older) and of new-style ones
37             (F for 10.1 and newer).
38              
39             =head1 media.cfg
40              
41             The F is structured like a classical F<.ini> file. All
42             parameters are optional; this means that a readable empty file is ok, if
43             this is what you want :)
44              
45             F contains sections, each section corresponding to a media,
46             except the C<[media_info]> section wich is used to store global info. The
47             section name is the (relative) path where the rpms are located. It is
48             sufficient to uniquely identify a media.
49              
50             Some values have specific signification:
51              
52             =over 4
53              
54             =item media specific values:
55              
56             =over 4
57              
58             =item B
59              
60             The path or basename of the hdlist. By default, this is
61             C, with slashes and spaces being replaced by '_'.
62              
63             =item B
64              
65             The path or basename of the synthesis. By default, this is the hdlist
66             name prefixed by C.
67              
68             =item B
69              
70             The path or basename of the gpg public key file. By default, this is
71             the media name prefixed by C.
72              
73             =item B
74              
75             A human-readable name for the media. By default this is the media path
76             (that is, the section name), where slashes have been replaced by
77             underscores.
78              
79             =back
80              
81             =item global specific values:
82              
83             =over 4
84              
85             =item B
86              
87             OS version.
88              
89             =item B
90              
91             OS branch (cooker, etc.)
92              
93             =item B
94              
95             Media target architecture.
96              
97             =item B
98              
99             The root path of the distribution tree. This value is not set in
100             F, can't be owerwritten, and is only used internally.
101              
102             =item B
103              
104             The default path relative to the 'root' path where media are
105             located. MDV::Distribconf is supposed to configure this automatically
106             to C or to C, depending on the OS version.
107              
108             =item B
109              
110             The default path relative to the 'root' path where distrib metadata
111             are located. MDV::Distribconf is supposed to configure this automatically
112             to C or to C, depending on the OS
113             version.
114              
115             =back
116              
117             =back
118              
119             For the paths of the hdlist and synthesis files, if only a basename is
120             provided, the path is assumed to be relative to the mediadir or infodir.
121             (hdlist and synthesis are created in both directories.) If it's a complete
122             path, it's assumed to be relative to the 'root'. For example,
123              
124             hdlist.cz -> //hdlist.cz
125             ./hdlist.cz -> /./hdlist.cz
126              
127             Here's a complete example of a F file:
128              
129             # Comment
130             [media_info]
131             # some tools can use those values
132             version=2006.0
133             branch=cooker
134              
135             [main]
136             hdlist=hdlist_main.cz
137             name=Main
138              
139             [../SRPMS/main]
140             hdlist=hdlist_main.src.cz
141             name=Main Sources
142             noauto=1
143              
144             [contrib]
145             hdlist=hdlist_contrib.cz
146             name=Contrib
147              
148             [../SRPMS/contrib]
149             hdlist=hdlist_contrib.src.cz
150             name=Contrib Sources
151             noauto=1
152              
153             =head1 METHODS
154              
155             =cut
156              
157 5     5   21639 use strict;
  5         9  
  5         155  
158 5     5   30 use warnings;
  5         8  
  5         135  
159 5     5   5809 use Config::IniFiles;
  5         147964  
  5         15520  
160              
161             sub mymediacfg_version {
162 88     88 0 355 $VERSION =~ /^(\d+)\./;
163 88         591 $1
164             }
165              
166             =head2 MDV::Distribconf->new($root)
167              
168             Returns a new MDV::Distribconf object, C<$root> being the top level
169             directory of the tree.
170              
171             =cut
172              
173             sub new {
174 49     49 1 21958 my ($class, $path, $mediacfg_version) = @_;
175 49         533 my $distrib = {
176             root => $path,
177             infodir => '',
178             mediadir => '',
179             type => '', # mdk vs mdv
180             mediainfodir => '',
181             cfg => new Config::IniFiles(-default => 'media_info', -allowcontinue => 1),
182             };
183              
184 49 100       4852 if (!defined($mediacfg_version)) {
185 46         353 $distrib->{cfg}->newval('media_info', 'mediacfg_version', mymediacfg_version());
186             }
187              
188 49         6662 bless($distrib, $class)
189             }
190              
191             =head2 $distrib->load()
192              
193             Finds and loads the configuration of the distrib: locate the path where
194             information is found; if available loads F, if available loads
195             F.
196              
197             Returns 1 on success, 0 error (that is, if no directory containing media
198             information is found, or if no F, neither F files are
199             found).
200              
201             See also L, L and L.
202              
203             =cut
204              
205             sub load {
206 44     44 1 77 my ($distrib) = @_;
207 44 100       109 $distrib->loadtree() or return 0;
208 43 50 66     218 $distrib->parse_mediacfg() || $distrib->parse_hdlists() or return 0;
209 43         290 return 1;
210             }
211              
212             =head2 $distrib->loadtree()
213              
214             Tries to find a valid media information directory, and set infodir and
215             mediadir. Returns 1 on success, 0 if no media information directory was
216             found.
217              
218             =cut
219              
220             sub loadtree {
221 44     44 1 57 my ($distrib) = @_;
222              
223 44 100       1481 if (-d "$distrib->{root}/media/media_info") {
    100          
224 29         70 $distrib->{infodir} = 'media/media_info';
225 29         57 $distrib->{mediadir} = 'media';
226 29         60 $distrib->{mediainfodir} = '/media_info';
227 29         49 $distrib->{type} = 'mandriva';
228             } elsif (-d "$distrib->{root}/Mandrake/base") {
229 14         22 $distrib->{infodir} = 'Mandrake/base';
230 14         18 $distrib->{mediadir} = 'Mandrake';
231 14         24 $distrib->{mediainfodir} = '';
232 14         20 $distrib->{type} = 'mandrake';
233             } else {
234 1         7 return 0;
235             }
236 43         114 return 1;
237             }
238              
239             =head2 check_mediacfg_version($wanted_version)
240              
241             Check that the current distrib uses this version or lesser, which means it is
242             supported.
243              
244             =cut
245              
246             sub check_mediacfg_version {
247 21     21 1 58 my ($distrib, $wanted_version) = @_;
248              
249             # Check wanted version is <= than the module
250             # Otherwise the module can't properly handle it
251 21 50       55 return 0 if (mymediacfg_version() < $wanted_version);
252              
253 21 50       98 return 0 if ($wanted_version < $distrib->getvalue(undef, 'mediacfg_version'));
254              
255 21         714 return 1
256             }
257              
258             =head2 $distrib->settree($spec)
259              
260             Virtual set the internal structure of the distrib.
261              
262             $spec can be 'mandrake' or 'mandriva' to automatically load a know structure
263             (old and new fascion, or a hashref:
264              
265             mediadir => 'media',
266             infodir => 'media/media_info',
267              
268             =cut
269              
270             sub settree {
271 4     4 1 9 my ($distrib, $spec) = @_;
272              
273 4 100 100     230 if (ref($spec) eq 'HASH') {
    100          
274 1         3 foreach (qw(infodir mediadir mediainfodir)) {
275 3   100     29 $distrib->{$_} = $spec->{$_} || '';
276             }
277             } elsif ($spec && $spec =~ /mandrake/i) {
278 1         3 $distrib->{infodir} = 'Mandrake/base';
279 1         1 $distrib->{mediadir} = 'Mandrake';
280 1         3 $distrib->{type} = 'mandrake';
281 1         4 $distrib->{mediainfodir} = '';
282             } else { # finally it can be everything, we do not care
283 2         6 $distrib->{infodir} = 'media/media_info';
284 2         5 $distrib->{mediadir} = 'media';
285 2         4 $distrib->{mediainfodir} = '/media_info';
286 2         7 $distrib->{type} = 'mandriva';
287             }
288             }
289              
290              
291             =head2 $distrib->parse_hdlists($hdlists)
292              
293             Reads the F file whose path is given by the parameter $hdlist,
294             or, if no parameter is specified, the F file found in the media
295             information directory of the distribution. Returns 1 on success, 0 if no
296             F can be found or parsed.
297              
298             =cut
299              
300             sub parse_hdlists {
301 23     23 1 28 my ($distrib, $hdlists) = @_;
302 23   33     107 $hdlists ||= "$distrib->{root}/$distrib->{infodir}/hdlists";
303              
304 23 50       1021 open my $h_hdlists, "<", $hdlists
305             or return 0;
306 23         148 $distrib->{cfg} = new Config::IniFiles( -default => 'media_info', -allowcontinue => 1);
307 23         1714 my $i = 0;
308 23         602 foreach (<$h_hdlists>) {
309 56         2333 s/#.*//; s/^\s*//;
  56         144  
310 56         78 chomp;
311 56 50       113 length or next;
312 56         54 my ($options, %media);
313 56         549 ($options, @media{qw(hdlist path name size)}) = /^\s*(?:(.*):)?(\S+)\s+(\S+)\s+([^(]*)(?:\s+\((\w+)\))?$/;
314 56 100       195 if (!$media{hdlist}) { # Hack because hdlists format really sucks
315 3         21 ($options, @media{qw(hdlist path name size)}) = /^\s*(?:(.*):)?(\S+)\s+(\S+)\s+(.*)$/;
316             }
317 56 50       95 if ($options) {
318 0         0 $media{$_} = 1 foreach split /:/, $options;
319             }
320 56         286 $media{name} =~ s/\s*$//;
321 56         359 $media{path} =~ s!^$distrib->{mediadir}/+!!;
322 56 50       143 foreach (qw(hdlist name size), $options ? split(/:/, $options) : ()) {
323 168 50       9733 $distrib->{cfg}->newval($media{path}, $_, $media{$_})
324             or die "Can't set value [$_]\n";
325             }
326             }
327 23         1809 close($h_hdlists);
328              
329 23         142 return 1;
330             }
331              
332             =head2 $distrib->parse_version($fversion)
333              
334             Reads the F file whose path is given by the parameter $fversion,
335             or, if no parameter is specified, the F file found in the media
336             information directory of the distribution. Returns 1 on success, 0 if no
337             F can be found or parsed.
338              
339             =cut
340              
341             sub parse_version {
342 0     0 1 0 my ($distrib, $fversion) = @_;
343 0   0     0 $fversion ||= $distrib->getfullpath(undef, 'VERSION');
344 0 0       0 open my $h_ver, "<", $fversion
345             or return 0;
346 0         0 my $l = <$h_ver>;
347 0         0 close $h_ver;
348 0         0 chomp $l;
349             # XXX heuristics ahead. This breaks regularly.
350 0         0 my ($version, $branch, $product, $arch) = $l =~ /^(?:mandrake|mandriva) ?linux\s+(\w+)\s+([^- ]*)-([^- ]*)-([^- ]*)/i;
351 0         0 $distrib->{cfg}->newval('media_info', 'version', $version);
352 0         0 $distrib->{cfg}->newval('media_info', 'branch', $branch);
353 0         0 $distrib->{cfg}->newval('media_info', 'product', $product);
354 0         0 $distrib->{cfg}->newval('media_info', 'arch', $arch);
355 0         0 return 1;
356             }
357              
358             =head2 $distrib->parse_mediacfg($mediacfg)
359              
360             Reads the F file whose path is given by the parameter
361             $mediacfg, or, if no parameter is specified, the F file found
362             in the media information directory of the distribution. Returns 1 on
363             success, 0 if no F can be found or parsed.
364              
365             =cut
366              
367             sub parse_mediacfg {
368 44     44 1 61 my ($distrib, $mediacfg) = @_;
369 44   66     257 $mediacfg ||= "$distrib->{root}/$distrib->{infodir}/media.cfg";
370 44 50 66     1100 (-f $mediacfg && -r _) &&
      66        
371             ($distrib->{cfg} = new Config::IniFiles( -file => $mediacfg, -default => 'media_info', -allowcontinue => 1))
372             or return 0;
373              
374 21         225385 return $distrib->check_mediacfg_version(mymediacfg_version());
375             }
376              
377             =head2 $distrib->listmedia()
378              
379             Returns an array of existing media in the configuration
380              
381             =cut
382              
383             sub listmedia {
384 89     89 1 11408 my ($distrib) = @_;
385 89         289 return grep { $_ ne 'media_info' } $distrib->{cfg}->Sections;
  488         1703  
386             }
387              
388             =head2 $distrib->mediaexists($media)
389              
390             Return true if $media exists
391              
392             =cut
393              
394             sub mediaexists {
395 1772     1772 1 2117 my ($distrib, $media) = @_;
396 1772   100     3426 $media ||= 'media_info';
397 1772   66     7933 return ($media eq 'media_info' || $distrib->{cfg}->SectionExists($media));
398             }
399              
400             sub _expand {
401 296     296   5953 my ($self, $media, $value, $level) = @_;
402 296 100       623 $value or return $value; # being lazy
403             # unsupported if < 3
404 256 100       529 $self->getvalue(undef, 'mediacfg_version') < 3 and return $value;
405 33   100     741 $media ||= 'media_info';
406 33   100     113 $level ||= 0; # avoid infinite loop
407 33 50       64 ++$level >= 15 and return $value;
408              
409 33         63 $value =~ s@\%{(\w+)}@
410 3 100       9 $self->getvalue($media, $1) || '%{' . $1 . '}';
411             @eg;
412 33         56 $value =~ s@\${(\w+)}@
413 2 50       7 $self->getvalue('media_info', $1, $level) || '${' . $1 . '}';
414             @eg;
415              
416 33         130 $value
417             }
418              
419             =head2 $distrib->getvalue($media, $var)
420              
421             Returns the $var value for $media, or C if the value is not set.
422              
423             If $var is "name", "hdlist" or "synthesis", and if the value is not explicitly
424             defined, the return value is expanded from $media.
425              
426             If $media is "media_info" or C, you'll get the global value.
427              
428             This function doesn't cares about path, see L for that.
429              
430             =cut
431              
432             sub getvalue {
433 1257     1257 1 10799 my ($distrib, $media, $var, $level) = @_;
434 1257   100     3359 $media ||= 'media_info';
435              
436 1257 50       2149 $distrib->mediaexists($media) or return;
437              
438 1257         8130 my $default = "";
439 1257         1883 for ($var) {
440 1257 100       2316 /^synthesis$/ and $default = 'synthesis.' . lc($distrib->getvalue($media, 'hdlist', $level));
441 1257 100       2148 /^hdlist$/ and $default = 'hdlist_' . lc($distrib->getvalue($media, 'name', $level)) . '.cz';
442 1257 100       3082 /^pubkey$/ and $default = 'pubkey_' . lc($distrib->getvalue($media, 'name', $level));
443 1257 100       2611 /^(pubkey|hdlist|synthesis)$/ and do {
444 53         180 $default =~ s![/ ]+!_!g;
445             };
446 1257 100       2237 /^name$/ and do {
447 61         78 $default = $media;
448 61         133 $default =~ s![/ ]+!_!g;
449 61         85 last;
450             };
451 1196 50       1980 /^productid$/ and do {
452 0   0     0 return join(',', map { "$_=" . ($distrib->getvalue(undef, $_) || '') }
  0         0  
453             qw(vendor distribution type version branch release arch product));
454             };
455 1196 100       2559 /^path$/ and return $media;
456 1009 100       2273 /^root$/ and return $distrib->{root};
457 764 100 100     2803 /^mediacfg_version$/ and
458             return $distrib->{cfg}->val('media_info', 'mediacfg_version') || 1;
459 231 100       496 /^VERSION$/ and do { $default = 'VERSION'; last };
  4         8  
  4         9  
460 227 50       382 /^product.id$/ and do { $default = 'product.id'; last };
  0         0  
  0         0  
461 227 50       368 /^product$/ and do { $default = 'Download'; last };
  0         0  
  0         0  
462             /^(MD5SUM|depslist.ordered|compss|provides)$/
463 227 50       508 and do { $default = $_; last };
  0         0  
  0         0  
464 227 50       513 /^(?:tag|branch)$/ and do { $default = ''; last };
  0         0  
  0         0  
465 227 100       435 /^(?:media|info)dir$/ and do { $default = $distrib->{$var}; last };
  8         20  
  8         16  
466 219 100       426 /^os$/ and do { $default = 'linux'; last; };
  19         28  
  19         31  
467 200 100       361 /^gnu$/ and do { $default = 1; last; };
  19         27  
  19         23  
468 181 100       343 /^vendor$/ and do { $default = $distrib->{type}; last; };
  19         57  
  19         28  
469 162 100       412 /^arch$/ and do { $default = undef; last; };
  57         59  
  57         83  
470 105 100       205 /^platform$/ and do {
471 19         48 my $arch = $distrib->getvalue($media, 'arch');
472 19 50       457 $default = defined($arch) ? sprintf('%s-%s-%s%s',
    50          
473             $arch,
474             $distrib->getvalue($media, 'vendor'),
475             $distrib->getvalue($media, 'os'),
476             $distrib->getvalue($media, 'gnu') ? '-gnu' : '',
477             ) : undef;
478 19         549 last;
479             };
480 86 50       163 /^rpmsrate$/ and do { $default = 'rpmsrate'; last; };
  0         0  
  0         0  
481 86 100       165 /^description$/ and do { $default = 'description'; last; };
  4         8  
  4         7  
482 82 50       139 /^provide$/ and do { $default = 'description'; last; };
  0         0  
  0         0  
483 82 50       223 /^depslist.ordered$/ and do { $default = 'description'; last; };
  0         0  
  0         0  
484             }
485 292         1009 return $distrib->_expand($media, $distrib->{cfg}->val($media, $var, $default), $level);
486             }
487              
488             =head2 $distrib->getpath($media, $var)
489              
490             Gives relative path of $var from the root of the distrib. This function is
491             useful to know where files are actually located. It takes care of location
492             of media, location of index files, and paths set in the configuration.
493              
494             =cut
495              
496             sub getpath {
497 513     513 1 2184 my ($distrib, $media, $var) = @_;
498 513 50       891 $distrib->mediaexists($media) or return;
499 513   50     6277 $var ||= ""; # Avoid undef value
500 513         948 my $val = $distrib->getvalue($media, $var);
501 513 100       9205 $var =~ /^(?:root|VERSION|product\.id|(?:media|info)dir)$/ and return $val;
502 256 100       641 my $thispath = $var eq 'path' ? $distrib->{mediadir} : $distrib->{infodir};
503 256 100       441 if ($distrib->getvalue(undef, 'mediacfg_version') >= 2) {
504 135         3569 return $thispath . '/' . $val;
505             } else {
506 121 100       3400 return ($val =~ m!/! ? "" :
    100          
507             ($var eq 'path' ? $distrib->{mediadir} : $distrib->{infodir} )
508             . "/") . $val;
509             }
510             }
511              
512             =head2 $distrib->getmediapath($media, $var)
513              
514             This function does the same than getpath except it return the path proper
515             to the media for files having doble location (index for example).
516              
517             =cut
518              
519              
520             sub getmediapath {
521 177     177 1 241 my ($distrib, $media, $var) = @_;
522 177         796 my %files = (
523             pubkey => 'pubkey',
524             hdlist => 'hdlist.cz',
525             synthesis => 'synthesis.hdlist.cz',
526             MD5SUM => 'MD5SUM',
527             infodir => '',
528             );
529 177 50       354 $var eq 'path' and return $distrib->getpath($media, 'path');
530 177         327 return $distrib->getpath($media, 'path') . $distrib->{mediainfodir} . "/$files{$var}";
531             }
532              
533             =head2 $distrib->getfullpath($media, $var)
534              
535             Does the same thing than getpath(), but the return value will be
536             prefixed by the 'root' path. This is a shortcut for:
537              
538             $distrib->getpath(undef, 'root') . '/' . $distrib->getpath($media, $var).
539              
540             =cut
541              
542             sub getfullpath {
543 22     22 1 35 my $distrib = shift;
544 22 50       58 my $path = $distrib->getpath(@_) or return;
545 22 50 50     81 return $distrib->getpath(undef, 'root') if (($_[1] || '') eq 'root');
546 22         41 return $distrib->getpath(undef, 'root') . '/' . $path;
547             }
548              
549             =head2 $distrib->getfullmediapath($media, $var)
550              
551             This function does the same than getpath except it return the path proper
552             to the media for files having doble location (index for example).
553              
554             =cut
555              
556             sub getfullmediapath {
557 10     10 1 14 my $distrib = shift;
558 10 50       28 my $path = $distrib->getmediapath(@_) or return;
559 10         24 return $distrib->getpath(undef, 'root') . '/' . $path;
560             }
561              
562             =head2 $distrib->getdpath($media, $var)
563              
564             Does the same thing than getpath(), but the return always return the best for
565             file having twice location (index).
566              
567             You may want to use this function to ensure you allways the good value.
568              
569             =cut
570              
571             sub getdpath {
572 213     213 1 299 my ($distrib, $media, $var) = @_;
573              
574 213 100       660 if ($var =~ /^(hdlist|synthesis|pubkey|MD5SUM)$/) {
575 197 100       405 if ($distrib->{type} eq 'mandriva') {
576 163         522 return $distrib->getmediapath($media, $var);
577             } else {
578 34         58 return $distrib->getpath($media, $var);
579             }
580             } else {
581 16         41 return $distrib->getpath($media, $var);
582             }
583             }
584              
585             =head2 $distrib->getfulldpath($media, $var)
586              
587             Does the same thing than getfullpath(), but the return always return the best
588             for file having twice location (index).
589              
590             You may want to use this function to ensure you allways the good value.
591              
592             =cut
593              
594             sub getfulldpath {
595 197     197 1 1067 my $distrib = shift;
596 197 50       352 my $path = $distrib->getdpath(@_) or return;
597 197         442 return $distrib->getpath(undef, 'root') . '/' . $path;
598             }
599              
600              
601             1;
602              
603             __END__