source: soft/build_system/build_system/mkcd/tags/V3_8_2_1mdk/pm/Mkcd/Tools.pm @ 1

Last change on this file since 1 was 1, checked in by fasma, 12 years ago

Initial Import from Mandriva's soft revision 224062 and package revision 45733

File size: 30.4 KB
Line 
1package Mkcd::Tools;
2
3our $VERSION = '1.0.0';
4
5use strict;
6use File::NCopy qw(copy);       
7use Image::Size qw(:all);
8use Mkcd::Commandline qw(parseCommandLine usage);
9use Digest::MD5;
10use MDK::Common qw(all any);
11require Exporter;
12use URPM;
13our @ISA = qw(Exporter);
14our @EXPORT = qw(printTable getTracks du cpal checkcds checkDiscs cleanrpmsrate imageSize printDiscsFile readBatchFile printBatchFile config compute_md5 log_ include_md5 convert_size compute_files_md5 fix_dir filter_path);
15our ($GB, $MB, $KB, $INFO_OFFSET, $SIZE_OFFSET, $SKIP);
16$INFO_OFFSET = 883;
17$SIZE_OFFSET = 84;
18$SKIP = 15;
19
20$KB = 1024;
21$MB = 1024 * 1024;
22$GB = $MB * 1024;
23
24=head1 NAME
25
26tools - mkcd tools
27
28=head1 SYNOPSYS
29
30    require mkcd::tools;
31
32=head1 DESCRIPTION
33
34<mkcd::tools> includes mkcd tools.
35
36=head1 SEE ALSO
37
38mkcd
39
40=head1 COPYRIGHT
41
42Copyright (C) 2000-2003 Mandrakesoft <warly@mandrakesoft.com>
43
44This program is free software; you can redistribute it and/or modify
45it under the terms of the GNU General Public License as published by
46the Free Software Foundation; either version 2, or (at your option)
47any later version.
48
49This program is distributed in the hope that it will be useful,
50but WITHOUT ANY WARRANTY; without even the implied warranty of
51MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
52GNU General Public License for more details.
53
54You should have received a copy of the GNU General Public License
55along with this program; if not, write to the Free Software
56Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
57
58=head1 CREDITS
59
60md5 code highly inspired from Redhat anaconda md5 in ISO code
61
62=cut
63
64sub printTable {
65    my ($a, $log) = @_;
66    my $LOG; if ($log) { $LOG = $log } else { open $LOG, ">&STDERR" }
67    #
68    # iterative version of a recursive scanning of a table.
69    # ex: @config = [[[1,3],3,[1,3,[1,3]]],3,4,[4,[4,4]]]
70    #   
71    my @A;
72    my @i;
73    my @tab;
74    my $i = 0;
75    while ($a) {
76        my $u = ref $a;
77        if ($u eq 'ARRAY') {
78            while ($i < @$a) {
79                my $b = $a->[$i];
80                my $t = ref $b;
81                if ($t eq 'ARRAY') {
82                    push @tab, "\t";
83                    push @i, $i+1;
84                    push @A, $a;
85                    $i = 0;
86                    $a = $b;
87                    next
88                } elsif ($t eq 'HASH') { 
89                    $i++; print $LOG "@tab", join ' ', keys %$b, "\n"
90                } else { $i++; print $LOG "@tab$b\n" }
91            }
92        } else { print $LOG "$a\n" }
93        pop @tab;
94        $i = pop @i;
95        $a = pop @A;
96    }
97
98}
99
100sub getTracks {
101    my ($tracks, $log) = @_;
102    my $LOG; if ($log) { $LOG = $log } else { open $LOG, ">&STDERR" }
103    my @tracks = split ',',$tracks;
104    my @t;
105    foreach (@tracks) {
106        /(\d+)/ and push @t, $1;
107        /(\d+)-(\d+)/ and push @t, $1..$2       
108    }
109    my @ntracks;
110    my %done;
111    for (my $i = $#t; $i >= 0; $i--) {
112        push @ntracks, $t[$i] if !$done{$t[$i]};
113        $done{$t[$i]}=1
114    }
115    \@ntracks;
116}
117
118sub du {
119    my ($path, $inode) = @_;
120    my $size;
121    $inode ||= {};
122    if (-d $path) {
123        opendir O, $path;
124        foreach (readdir O) {
125            /^\.{1,2}$/ and next;
126            -l "$path/$_" or $size += du("$path/$_",$inode)
127        }
128    } else {
129        if (! -l $path) {
130            my @stat = stat $path;
131            if (!$inode->{$stat[0]}{$stat[1]}) {
132                $size = $stat[7] + 2048;
133                $inode->{$stat[0]}{$stat[1]} = 1
134            }
135        }
136    }
137    $size
138}
139
140sub cpal {
141    my ($source, $dest, $exclude, $verbose, $log) = @_;
142    my $LOG; if ($log) { $LOG = $log } else { open $LOG, ">&STDERR" }
143    if ($exclude && "$source/$_" =~ /$exclude/) { return 0 }
144    if (!-l $source && -d $source) {
145        mkdir $dest;
146        opendir O, $source; 
147        foreach (readdir O) {
148            /^\.{1,2}$/ and next;
149            cpal("$source/$_", "$dest/$_",$exclude,$verbose)
150        }
151    } else {
152        my $ok;
153        if (-d $dest) { my ($filename) = $source =~ m,([^/]*)$,; $dest .= "/$filename" }
154        $ok = link $source, $dest;
155        $verbose and print $LOG "cpal: link $source -> $dest\n";
156        if (!$ok) {
157            print $LOG "Linking failed $source -> $dest: $!, trying to copy\n";
158            $ok = copy $source, $dest;
159            if (!$ok) { print $LOG "Copying failed $source -> $dest: $!,\n"; return 0 }
160        }
161    }
162    1
163}
164
165sub checkDiscs {
166    my ($hdlists, $depslist, $discsFiles, $check, $log) = @_;
167    my $LOG; if ($log) { $LOG = $log } else { open $LOG, ">&STDOUT" }
168   
169    print $LOG "checkDiscs: depslist $depslist\n";
170    #
171    # depslist hdlist consistency -> error                        ok (not the same as install one, but duplicate will break anyway)
172    #
173    # in hdlist, not in depslist -> error                         ok
174    #
175    # in hdlist, not in dir -> error                              ok
176    #
177    # in hdlist with packdrake, no with parsehdlist -> error
178    #
179    # in depslist, not in hdlist -> error                         ok
180    #
181    # in depslist, not in dir -> error                            ok
182    #
183    # in dir, not in hdlist -> warning                            ok
184    #
185    # in dir, not in depslist -> warning                          ok
186    #
187    # multiple version in depslist -> error                       ok
188    #
189    # multiple version in hdlist -> error                         ok
190    #
191    # multiple in dir -> warning                                  ok
192    #
193   
194    my $ok = 1;
195    my $OK = 1;
196    my %depslist;
197    my %depslistname;
198    if ($depslist) {
199        my $i = 1;
200        open my $A, $depslist or print $LOG "ERROR: unable to open $depslist" and return 0;
201        print $LOG "checkDiscs: duplicate version in $depslist:";
202        while (<$A>) {
203            my ($pkg, $name, $arch) = ((split)[0]) =~ m/((.*)-[^-]+-[^-]+\.([^:]+))/;
204            $depslist{$pkg} and do { print $LOG "\n$pkg"; $ok = 0 };
205            $depslistname{$arch}{$name} and do { print $LOG "\n$name"; $ok = 0 };
206            $depslist{$pkg} = $i;
207            $depslistname{$arch}{$name} = $i++;
208        }
209        close $A;
210    }
211    $ok or $OK = 0;
212    $ok ? print $LOG " OK\n" : print $LOG "\nFAILED\n";
213    my %hdlist;
214    print $LOG "\ncheckDiscs: duplicate version in hdlists:";
215    my $maxidx;
216    my %rpm;
217    my (@rnh, @hnd, @duprep, @rnd, @hnr, %rpmKeys, %parsehdlist, @pnh, @hnp);
218    $ok = 1;
219    my $parsehdlist;
220    my $path = $0;
221    $path =~ s,[^/]*$,,;
222    if (-x "$path/parsehdlist") {
223        $parsehdlist = "$path/parsehdlist"
224    } elsif (-x "/usr/bin/parsehdlist") {
225        $parsehdlist = "/usr/bin/parsehdlist"
226    } else {
227        my $err = system('parsehdlist');
228        if ($err) {
229            $parsehdlist = "parsehdlist"       
230        } else {
231            print $LOG, "ERROR checkDiscs: could not find parsehdlist command ($!)\n";
232            return 0
233        }
234    }
235    for (my $i = 1; $i < @$hdlists; $i++) {
236        if (! -f $hdlists->[$i]) {
237            print $LOG "\nWARNING checkDiscs: $hdlists->[$i] is empty, ignoring\n";
238            next
239        }
240        my $packer = new packdrake($hdlists->[$i]);
241        my $j;
242        foreach my $file (@{$packer->{files}}) {
243            my ($rpm, $key) = $file =~ /([^:]*)(?::(.*))?/;
244            $rpmKeys{key}{$rpm} = $key || $rpm;
245            $rpmKeys{rpm}{$rpmKeys{key}{$rpm}} = $rpm;
246            my $sok;
247            foreach my $c (@{$check->[$i]}) {
248                my ($cd, $rep, $list) = @$c;
249                $discsFiles->[$cd]{$rep}{$list}{$rpmKeys{key}{$rpm}} and $sok = 1;
250            }
251            $sok or push @hnr, [ $i, $rpm ];
252            $hdlist{all}{$rpm} and do { print $LOG "\n$rpm"; $ok = 0 };
253            $hdlist{all}{$rpm} = 1;
254            $hdlist{cd}{$i}{$rpm}  = 1;
255            if ($depslist) {
256                $depslist{$rpm} or push @hnd, $rpm;
257                $depslist{$rpm} > $j and $j = $depslist{$rpm};
258                $depslist{$rpm} < $maxidx and print $LOG "ERROR checkDiscs: inconsistency in position between hdlist $i rpm $rpm and depslist.ordered\n"
259            }
260        }
261        foreach my $c (@{$check->[$i]}) {
262            my ($cd, $rep, $list) = @$c;
263            foreach my $rpm (keys %{$discsFiles->[$cd]{$rep}{$list}}) {
264                $rpm{$rpmKeys{rpm}{$rpm}} and push @duprep, $rpm;
265                $rpm{$rpmKeys{rpm}{$rpm}} = 1;
266                $depslist && $depslist{$rpmKeys{rpm}{$rpm}} or push @rnd,  [ $i, $cd, $rep, $rpm ];
267                $hdlist{cd}{$i}{$rpmKeys{rpm}{$rpm}} or push @rnh, [ $i, $rpm ]
268            }
269        }
270        open my $PAR, "$parsehdlist $hdlists->[$i] |";
271        while (<$PAR>) {
272            chomp;
273            s/\.rpm$//;
274            $parsehdlist{$i}{$_} = 1;
275            $hdlist{cd}{$i}{$_} and next;
276            push @pnh, $_
277        }
278        foreach my $p (keys %{$hdlist{cd}{$i}}) {
279            $parsehdlist{$i}{$p} or push @hnp, $p
280        }
281        $maxidx = $j;
282    }
283    $ok or $OK = 0;
284    $ok ? print $LOG " OK\n" : print $LOG "\nFAILED\n";
285
286    my @dnh;
287    $ok = 1;
288    if ($depslist) {
289        print $LOG "\ncheckDiscs: in depslist, not on discs:";
290        foreach my $rpm (keys %depslist) {
291            $hdlist{all}{$rpm} or do { push @dnh, $rpm };
292            $rpm{$rpm} or do { $ok = 0; print $LOG "\n$rpm" };
293        }
294        $ok or $OK = 0;
295        $ok ? print $LOG " OK\n" : print $LOG "\nFAILED\n";
296
297        print $LOG "\ncheckDiscs: in depslist, not in hdlists:";
298        @dnh ? do { print $LOG " FAILED\n" and $OK = 0 } : print $LOG " OK\n";
299        foreach (@dnh) {
300            print $LOG "$_\n"
301        }
302    }
303    print $LOG "\ncheckDiscs: in hdlists, not on discs:";
304    @hnr ? do { print $LOG " FAILED\n" and $OK = 0 } : print $LOG " OK\n";
305    foreach (@hnr) {
306        print $LOG "hdlist $_->[0] rpm $_->[3]\n"
307    }
308    print $LOG "\ncheckDiscs: in hdlists, not in depslist:";
309    @hnd ? do { print $LOG " FAILED\n" and $OK = 0 } : print $LOG " OK\n";
310    foreach (@hnd) {
311        print $LOG "$_\n"
312    }
313    print $LOG "\ncheckDiscs: in hdlists, not see with parsehdlist:";
314    @hnp ? do { print $LOG " FAILED\n" and $OK = 0 } : print $LOG " OK\n";
315    foreach (@hnp) {
316        print $LOG "$_\n"
317    }
318    print $LOG "\ncheckDiscs: see with parsehdlist, not with packdrake:";
319    @pnh ? do { print $LOG " FAILED\n" and $OK = 0 } : print $LOG " OK\n";
320    foreach (@pnh) {
321        print $LOG "$_\n"
322    }
323    print $LOG "\ncheckDiscs: on discs, not in hdlist:";
324    @rnh ? print $LOG " WARNING\n" : print $LOG " OK\n";
325    foreach (@rnh) {
326        print $LOG "hdlist $_->[0] rpm $_->[1]\n"
327    }
328    print $LOG "\ncheckDiscs: on discs, not in depslist:";
329    @rnd ? print $LOG " WARNING\n" : print $LOG " OK\n";
330    foreach (@rnd) {
331        print $LOG "hdlist $_->[0] cd $_->[1] rep $_->[2] missing rpm $_->[3]\n"
332    }
333    print $LOG "\ncheckDiscs: duplicate version on discs:";
334    @duprep ? print $LOG " WARNING\n" : print $LOG " OK\n";
335    foreach (@duprep) {
336        print $LOG "$_\n"
337    }
338    return $OK
339}
340
341#
342# check depslist, depslists.ordered and hdlists
343#
344sub checkcds {
345    my (@tops) = @_;
346   
347    my $top = "$tops[0]/";
348    my $depslist = "$tops[0]/Mandrake/base/depslist.ordered";
349    -f $depslist or print "ERROR: could not find depslist $depslist file\n" and return 0;
350    my $hdlists = "$top/Mandrake/base/hdlists";
351    open my $A, $hdlists or die "unable to open $hdlists";
352    my @hdlist = 0;
353    my @discsFiles;
354    my @check = 0;
355    while (<$A>) {
356        my ($hdlist, $dir, undef) = split;
357        my ($hdid) = $hdlist =~ /(\d*).cz/;
358        my $hdfile = "$tops[0]/Mandrake/base/$hdlist";
359        push @hdlist, $hdfile;
360        push @check, [[ $hdid, $dir, 1 ]];
361        -f $hdfile or print "ERROR: could not find $hdfile file\n" and return 0;
362        print "Reading $top/$dir\n";
363        my $C;
364        if (! opendir $C, "$top/$dir") {
365            foreach (@tops) {
366                opendir $C, "$_/$dir" or next;
367                last
368            }
369        }
370        foreach (readdir $C) {
371            /(.*)\.rpm/ or next;
372            $discsFiles[$hdid]{$dir}{1}{$1} = 1
373        }
374
375    }
376    checkDiscs(\@hdlist, $depslist, \@discsFiles, \@check)
377}
378
379#
380# regexp version
381#
382sub cleanrpmsrate2 {
383    my ($rpmsrate, @rpms) = @_;
384    my $LOG; open $LOG, ">&STDERR";
385    my @rpm;
386    foreach (@rpms) {
387        -d $_ or print $LOG "ERROR: $_ is not a directory\n" and next;
388        opendir my $A, $_;
389        push @rpm, grep { s/-[^-]+-[^-]+\.[^.]+\.rpm// } all $A;
390    }
391    my %done;
392    my (@flags, @c);
393    my ($mod, $text, $prev, $rate, $current);
394    my (%rate, %section);
395    open my $A, $rpmsrate or print $LOG "ERROR: cannot open $rpmsrate\n";
396    while (<$A>) {
397        s/#.*//;
398        /^\s*$/ and $text .= "\n" and next;
399        if (/^(\S+)/) {
400            $text .= "$1\n";
401            $current = $1;
402            @flags = $current;
403            next
404        }
405        my ($indent, $r, $flags, $data) = /^(\s*)([1-5]?)((?:\s+(?:(?:!\s*)?[0-9A-Z_]+(?:"[^"]*")?(?:\s+(?:\|\|\s+)?)*)+\s+)|\s+)(.*)$/;
406        if ($r) {
407            $rate = $r
408        } elsif ($prev) {
409            chop $indent;
410            $r = $prev
411        }
412        push @flags, split ' ', $flags; 
413        $data or $text .= "$indent$r$flags" and next;
414        my ($postfix) = $data =~ /(\s*)$/;
415        my @k;
416        foreach my $n (split ' ', $data) {
417            @c = grep { /^$n$/ } @rpm;
418            map { if ((!$done{$_}[1] || $current eq "INSTALL") && $done{$_}[0] ne $current) { push @k, $_; @{$done{$_}} = @flags } } @c
419        } 
420        if (@k) { $text .= "$indent$r$flags@k$postfix\n"; $prev = '' } else { $prev = $r };
421        @rate{@k} = ($rate) x @k;
422        push @{$section{$current}}, @k
423    }
424    close A;
425    if (@rpms) {
426        if (open A, ">$rpmsrate") {
427            print A $text;
428            close A
429        } else {
430            @rpms and print $LOG "ERROR: cannot open $rpmsrate for writing\n";
431            print $text
432        }
433    }
434    [\%rate, \%section, \%done];
435}
436
437sub cleanrpmsrate {
438    my ($rpmsrate, $output, $norpmsrate, $reprpms, $urpm) = @_;
439    $norpmsrate ||= [];
440    my $LOG; open $LOG, ">&STDERR";
441    open my $A, $rpmsrate or print $LOG "ERROR: cannot open $rpmsrate\n";
442    my (@rpmsrate, %potloc);
443    # must preread to get locale guessed packages
444    # postfix is just used not to break the diff when checking if the result is correct
445    while (<$A>) {
446        chomp;
447        s/#.*//;
448        #s/\s*$//;
449        /^(\s*)$/ and push @rpmsrate, [ '', 0, '', [] ] and next;
450        if (/^(\S+)(.*)$/) {
451            push @rpmsrate, [ 0, 0, $1, [], $2 ];
452            next
453        }
454        if (/^(\s*)([1-5])?(\s?[0-9A-Z_]+)$/) {
455            push @rpmsrate, [ $1, $2, $3, [] ];
456            next
457        }
458        my ($indent, $r, $flags, $data) = /^(\s*)([1-5])?(\s*(?:(?:(?:!\s*)?[0-9A-Z_]+(?:"[^"]*")?(?:\s+(?:\|\|\s+)?)*)+\s+)|\s*)(.*)$/;
459        my ($postfix) = $data =~ /(\s*)$/;
460        my @data;
461        my $i;
462        foreach ([$data =~ /(?:^|\s)(\S+)-(?:\S+)\s+\1-(?:\S+)(?:\s|$)/g], [split ' ', $data]) {
463            $data[$i++] = [ @$norpmsrate ? any { my $r = $_; $r if !any { $r =~ /$_/ } @$norpmsrate } @$_ : @$_ ]
464        }
465        $potloc{$_} = [] foreach  @{$data[0]};
466        push @rpmsrate, [ $indent,$r, $flags, $data[1], $postfix ];
467    }
468    my (%rpms, $text);
469    my (%rate, %section, %keyword);
470    my (%locale, %localized_pkg);
471    my $kernel_like = "((?:(?:NVIDIA_)?kernel.*)|NVIDIA_nforce.*|cm2020.*)";
472    my $urpm2 = new URPM;
473    foreach my $dir (keys %$reprpms) {
474        foreach (@{$reprpms->{$dir}}) { 
475            my $rpm = "$_.rpm";
476            my $key = $_;
477            s/-[^-]+-[^-]+\.[^.]+$// or next;
478            any { $rpm =~ /$_/ } @$norpmsrate and next;
479            if (/(.*?)([_-]*[\d._]*)-devel$/ || /$kernel_like(-[^.]+(?:\.[^.]+){3,5}mdk)$/) { 
480                if (!$rpms{$1}) { $rpms{$1} = $2 }
481                elsif (URPM::ranges_overlap("== $2", "> $rpms{$1}")) { $rpms{$1} = $2 }
482            } elsif (my ($pg, $loc) = /^(.*)-([^-+]+)$/) {
483                if ($potloc{$pg}) {
484                    my $pkg = $urpm->{rpm}{$urpm->{rpmkey}{key}{$key}} if ref $urpm;
485                    if (!$pkg) {
486                        my $id = $urpm2->parse_rpm("$dir/$rpm");
487                        $pkg = $urpm2->{depslist}[$id];
488                    }
489                    if (!$pkg) {
490                        print "ERROR cleanrpmsrate: parse_rpm $dir/$rpm ($key) failed\n";
491                        next
492                    }
493                    # some i18n packages does not require the same locale, e.g. kde-i18n-nb and nn requires locales-no
494                    # if (grep { s/locales-// && $loc =~ /^$_(_|$)/ } @{$header{REQUIRENAME}}) {
495                    if (any { /^locales-..$/ } $pkg->requires) {
496                        push @{$locale{$pg}}, $loc;
497                        $localized_pkg{"$pg-$loc"} = 1
498                    }
499                }
500            }
501        }
502    }
503    my (%done, @flags, $prev, @tree_rate, $prev_level);
504    foreach (@rpmsrate) {
505        if (!$_->[0]) {
506            $text .= "$_->[2]$_->[4]\n";
507            if ($_->[2]) {
508                @flags = $_->[2]
509            }
510            next
511        }
512        my ($indent, $r, $flags, $data, $postfix) = @$_;
513        my $level = (length $indent)/2 - 1;
514        my $rate;
515        if ($r) {
516            #print "tree_rate[$level] = $r\n";
517            $rate = $r;
518            $tree_rate[$level] = $r
519        } else {
520            if (@$data) {
521                if ($level > $prev_level) {
522                    $level-- 
523                } else {
524                    # fix a syntax error in rpmsrate such as
525                    # A
526                    #   1 toto
527                    #   B tata <---
528                    #     4 titi
529                    @$data = ()
530                }
531            }
532            $rate = $tree_rate[$level];
533        }
534        $prev_level = $level;
535        @flags = @flags[0 .. $level];
536        push @flags, grep { s/\s//; !/(\|\||[A-Z_]+"[^"]+")/ } split(' ', $flags);
537        my $flat_path = join ' ', @flags;
538        if (!@$data) { $text .= "$indent$r$flags$postfix\n"; next }
539        my @k;
540        foreach (@$data) {
541            my $c = $_;
542            if (any { $flat_path eq $_ } @{$done{$_}}) { next }
543            my ($d) = /(.*)-[^-]+/;
544            my ($a, $b);
545            if (($flags[0] ne "INSTALL" && s/(-devel)// ? ($b = "-devel") : /^$kernel_like/) && ($rpms{$_} || (defined $rpms{"lib$_"} and $a = "lib") || (defined $rpms{"lib64$_"} and $a = "lib64"))) {
546                my $d = "$a$_" . $rpms{"$a$_"} . $b;
547                $keyword{$c} = $d;
548                if (! ref $done{$d} || $flags[0] eq "INSTALL") { push @{$done{$d}}, $flat_path; push @k, $d }
549            }
550            if ($locale{$d} && $localized_pkg{$c}) {
551                foreach (sort @{$locale{$d}}) {
552                    next if any { $_ eq $flat_path } @{$done{"$d-$_"}};
553                    push @{$done{"$d-$_"}}, $flat_path; 
554                    push @k , "$d-$_"
555                }
556                next
557            }
558            push @k, $c;
559            push @{$done{$c}}, $flat_path
560        } 
561        if (@k) { $text .= "$indent$r$flags@k$postfix\n" }
562        @rate{@k} = ($rate) x @k;
563        my $path;
564        foreach (@flags) {
565            $path .= $path ? "/$_" : $_;
566            push @{$section{$path}}, @k
567        }
568    }
569    if (%rpms || $output) {
570        if (%$reprpms || $output) {
571            $output ||= $rpmsrate;
572            if (open A, ">$output") { 
573                print A $text;
574                close A
575            } else { 
576                print $LOG "ERROR cleanrpmsrate: cannot open $rpmsrate for writing\n";
577                print $text
578            }
579        }
580    }
581    [\%rate, \%section, \%keyword]
582}
583
584sub imageSize {
585    my ($file) = @_;
586    my ($width, $height, $err) = imgsize $file;
587
588    return (defined $width ?
589    [ $width, $height ] :
590    "error: $err")
591}
592
593sub printDiscsFile {
594    my ($config, $discsFiles, $PRINT, $metagroups) = @_;
595    my (%done, $output);
596    my $log = $config->{LOG};
597    if ($PRINT) { open $output, ">$PRINT" } else { $output = $config->{LOG} }
598    my $print_rejected = sub {
599        my ($groups, $i, $rpm, $size) = @_;
600        # FIXME ugly hack to display more rejected in multigroups buildings because discFiles is per disc and not per group.
601        # $done{$groups->[$i]{urpm}{rpmkey}{rpm}{$rpm}} && ! ref $groups->[$i]{rejected}{$rpm} and return 1;
602        $done{$groups->[$i]{urpm}{rpmkey}{rpm}{$rpm}} and return 1;
603        $groups->[$i]{done}{rep}{$rpm} and return 1;
604        if ($groups->[$i]{brokendeps}{$rpm} == 2) {
605            ref $groups->[$i]{rejected}{$rpm} or print $output "ERROR printDiscsFile: this should not happen, rejected is not a table for $rpm (group $i)\n" and next;
606        }
607        printf $output "REJECTED master disc $groups->[$i]{installDisc} %10d $rpm (", $size;
608        if (ref $groups->[$i]{rejected}{$rpm}) { 
609            print $output join(',', map { "$config->{rejected_options}{$_->[0]}: $_->[1]" } @{$groups->[$i]{rejected}{$rpm}})
610        } else {
611            print $output "not selected"
612        }
613        print $output ")\n";
614        0
615    };
616    my %size;
617    # this is not really correct as multiple list may have packages with the same name but different size
618    foreach (@$metagroups) {
619        my $groups = $_->[0];
620        for (my $i; $i < @$groups; $i++) {
621            foreach my $rpm (keys %{$groups->[$i]{size}}) {
622                foreach my $list (keys %{$groups->[$i]{size}{$rpm}}) { 
623                    $size{$rpm} = $groups->[$i]{size}{$rpm}{$list}[0] if $size{$rpm} < $groups->[$i]{size}{$rpm}{$list}[0] 
624                }
625            }
626        }
627    }
628    for (my $cd; $cd < @$discsFiles; $cd++) {
629        $discsFiles->[$cd] or next;
630        print $log "discsFiles: $cd\n";
631        my $cdname = $config->{disc}[$cd]{label};
632        foreach my $rep (keys %{$discsFiles->[$cd]}) {
633            foreach my $list (keys %{$discsFiles->[$cd]{$rep}}) {
634                foreach my $rpm (sort { $size{$a} <=> $size{$b} } keys %{$discsFiles->[$cd]{$rep}{$list}}) {
635                    #$done{$rpm} = 1;
636                    #$rpm =~ /src$/ and next;
637                    printf $output "$cdname %10d $rpm\n", $size{$rpm};
638                }
639            }
640        }
641    }
642    if (!$metagroups) { $output = $config->{LOG} }
643    foreach (@$metagroups) {
644        my $groups = $_->[0];
645        for (my $i; $i < @$groups; $i++) {
646            if (ref $groups->[$i]{buildlist}) {
647                foreach (sort { $size{$a} <=> $size{$b} } @{$groups->[$i]{buildlist}}) {
648                    $print_rejected->($groups, $i, $_, $size{$_}) and next;
649                    $done{$groups->[$i]{urpm}{rpmkey}{rpm}{$_}} = 1
650                }
651            }
652            foreach (sort { $size{$a} <=> $size{$b} } keys %{$groups->[$i]{urpm}{rpm}}) {
653                $print_rejected->($groups, $i, $_, $size{$_})
654            }
655        }
656    }
657}
658
659sub printBatchFile {
660    my ($config, $discsFiles, $PRINTSCRIPT) = @_;
661    # FIXME to please perl_checker
662    my $log = $config->{LOG}; 
663    if (-f $PRINTSCRIPT) {
664        my $err = unlink $PRINTSCRIPT;
665        if (!$err) { print $log "Unlinking failed $PRINTSCRIPT: $!\n"; return };
666    }
667    my $err = copy $config->{configfile}, $PRINTSCRIPT;
668    if (!$err) { print $log "Linking failed $PRINTSCRIPT: $!\n"; return };
669    open my $A, ">>$PRINTSCRIPT";
670    print $A "END\n";
671    for (my $cd; $cd < @$discsFiles; $cd++) {
672        $discsFiles->[$cd] or next;
673        print $log "discsFiles: $cd\n";
674        print $A "CD $cd\n";
675        foreach my $rep (keys %{$discsFiles->[$cd]}) {
676            print $A " REP $rep\n";
677            foreach my $list (keys %{$discsFiles->[$cd]{$rep}}) {
678                print $A "  LIST $list\n";
679                foreach my $rpm (keys %{$discsFiles->[$cd]{$rep}{$list}}) {
680                    $rpm and print $A "   $rpm $discsFiles->[$cd]{$rep}{$list}{$rpm}\n";
681                }
682            }
683        }
684    }
685}
686
687sub readBatchFile {
688    my ($file) = @_;
689    local *A; open A, $file or print "ERROR readBatchFile: could not open $file for reading\n" and return 0;
690    my @discsFiles;
691    my @cd;
692    while (<A>) { /^END/ and last }
693    my ($cd, $rep, $list);
694    while (<A>) {
695        if (/^CD (\d+)/) { $cd = $1; next }
696        if (/^ REP (\S+)/) { $rep = $1; next }
697        if (/^  LIST (\d+)/) { $list = $1; next }
698        if (/^   (\S+) (\S+)/) { 
699            $discsFiles[$cd]{$rep}{$list}{$1} = $2;
700            push @{$cd[$cd]{$rep}{$list}{$2}}, [ 1, "$1.rpm" ];
701            next 
702        }
703    }
704    return \@discsFiles, \@cd
705}
706
707sub config {
708    my ($file, $config, $functions) = @_;
709    my $log = $config->{LOG};
710    open F,$file or die "ERROR config: cannot open $file\n";
711    while (<F>) { chomp; /^#/ or !$_ or last }
712    chomp;
713    $config->{name} = (split)[0];
714    my $match_val = q((?:([^"\s]+)|"([^\"]+)"));
715    my $match_val2 = q(((?:[^"\s]*(?:[^"\s]+|"[^\"]+")[^"\s]*)+));
716    my ($cd, $fn, $nk, $type, @todo, $discMax);
717    $config->{virtual_disc} = [];
718    my ($line, $a);
719    while (<F>) {
720        /^#/ and next;
721        chomp;
722        $_ or next;
723        s/#.*//;
724        my $b = s/\\\s*$//;
725        if ($a) {
726            $line .= $_
727        } else {
728            $line = $_ 
729        }
730        $a = $b;
731        $a and next;
732        local $_ = $line;
733        if (/^list (.*)/) {
734                my $line = $1;
735                my @args;
736                while ($line =~ s/$match_val2//) { my $a = $1; $a =~ s/"//g; push @args, $a }
737                #print "config: args (" . ( join ' | ', @args) . ")\n";
738                my $todo = parseCommandLine("list", \@args, $functions->{list});
739                $cd = $todo->[0][1][0];
740                #print "config: list $cd (@{$todo->[0][1]})\n";
741                if (!$config->{list}[$cd]) {
742                    @args and usage('list', $functions->{list}, "list $cd, list definition (@args) too many arguments");
743                    foreach (@$todo) {
744                        log_("$_->[2]\n", $config->{verbose}, $log, 3);
745                        if (!&{$_->[0]}($cd, @{$_->[1]})) { log_("ERROR: $_->[2]\n", $config->{verbose}, $log); $nk = 1 }
746                    }
747                    $type = 1;
748                    $fn = 0
749                } else {
750                    $type = 0;
751                    log_("ERROR config: list $cd already defined, ignoring\n", $config->{verbose}, $log);
752                }
753            # FIXME keep for compatibility
754        } elsif (/^LIST /) {
755            if (/^LIST (\d+)(?:\s+(\S.*))*/) {
756                $cd = $1;
757                push @{$config->{list}[$cd]{filelist}},  (split ' ',$2) if $2;
758                $type = 1;
759                log_("LIST $1 $2\n", $config->{verbose}, $log, 3)
760            } else {
761                $nk = 1;
762                log_("WARNING: LIST syntax error ($_)\n", $config->{verbose}, $log);
763                log_("         LIST <list number> <file list 1> <file list 2> ... <file list n>\n", $config->{verbose}, $log)
764            }
765        } elsif (/^disc (.*)/) {
766                my $line = $1;
767                my @args;
768                while ($line =~ s/$match_val2//) { my $a = $1; $a =~ s/"//g; push @args, $a }
769                #print "config: args (" . ( join ' | ', @args) . ")\n";
770                my $todo = parseCommandLine("disc", \@args, $functions->{disc});
771                $cd = $todo->[0][1][0];
772                #print "config: disc $cd (@{$todo->[0][1]})\n";
773                if (!$config->{disc}[$cd]) {
774                    @args and usage('disc', $functions->{disc}, "disc $cd, disc definition (@args) too many arguments");
775                    foreach (@$todo) {
776                        log_("$_->[2]\n", $config->{verbose}, $log, 3);
777                        if (!&{$_->[0]}($cd, @{$_->[1]})) { log_("ERROR: $_->[2]\n", $config->{verbose}, $log); $nk = 1 }
778                    }
779                    $type = 2;
780                    $fn = 0
781                } else {
782                    $type = 0;
783                    log_("ERROR config: disc $cd already defined, ignoring\n", $config->{verbose}, $log);
784                }
785            # FIXME keep for compatibility
786        } elsif (/^DISC (.*)/) {
787            if (/^DISC (\d+)\s+(\d+)\s+$match_val(?:\s+DISC\s+(\d+))?\s+$match_val(?:\s+$match_val)?/) { 
788                #print "1($1) 2($2) 3($3) 4($4) 5($5) 6($6) 7($7) 8($8) 8($9)\n";
789                $config->{disc}[$1]{size} = $2;
790                my $disc = $config->{disc}[$1];
791                $disc->{serial} = substr "$3$4", 0, 128;
792                $disc->{name} = $5;
793                $disc->{longname} = "$6$7";
794                $disc->{appname} = substr("$6$7", 0, 128);
795                $disc->{label} = substr(("$6$7" ? "$8$9" : "$6$7"), 0, 32);
796                $cd = $1;
797                $type = 2;
798                $fn = 0;
799                $4 > $discMax and $discMax = $4;
800                log_("DISC $1 $2 $3$4 $5 $6$7 $8$9\n", $config->{verbose}, $log)
801            } else {
802                $nk = 1;
803                $type = 0;
804                log_("WARNING: DISC syntax error ($_)\n", $config->{verbose}, $log);
805                log_("         DISC <cd number> <cd size> <cd serial name> DISC <real cd number> <disc name>\n", $config->{verbose}, $log)
806            }
807        } elsif (/^END/) {
808            last       
809        } else {
810            my @args;
811            while (s/$match_val2//) { my $a = $1; $a =~ s/"//g; push @args, $a }
812            my $prog = shift @args;
813            log_("config: function $prog(" . join(' | ',@args) . ")\n", $config->{verbose}, $log,4);
814            $type == 1 and do {
815                if ($prog ne 'rpmlist') {
816                    push @{$config->{list}[$cd]{packages}}, { rpm => [ $prog ] , srpm => \@args } 
817                } else {
818                    push @todo, [ $prog, \@args, $cd, $fn ];
819                    $fn++;
820                }
821                next
822            };
823            $type == 2 and do {
824                push @todo, [$prog, \@args, $cd, $fn];
825                $fn++;
826                next
827            }
828        }
829    }
830    $config->{configfile} = $file;
831    $config->{discMax} = $discMax;
832    foreach (@todo) {
833        my ($prog, $args, $cd, $fn) = @$_;
834        if ($functions->{$prog}) {
835            log_("FUNCTION $prog (@$args)\n", $config->{verbose}, $log,5);
836            my $todo = parseCommandLine($prog, $args, $functions->{$prog});
837            @$args and usage($prog, $functions->{$prog}, "disc $cd, function $fn, @$args, too many arguments");
838            foreach (@$todo) {
839                log_("config: todo $_->[2]\n", $config->{verbose}, $log, 4);
840                if (!&{$_->[0]}($cd, $fn, @{$_->[1]})) { log_("ERROR: $_->[2]\n", $config->{verbose}, $log); $nk = 1 }
841            }
842        }
843    }
844    $nk and return 0;
845    #printTable($config);
846    1
847}
848
849sub compute_files_md5 {
850    my ($md5file, $files) = @_;
851    open my $MD5, ">$md5file";
852    my $text;
853    foreach (@$files) {
854        my $md5 = new Digest::MD5;
855        open my $F, $_ or die "FATAL: Could not open $_\n";
856        $md5->addfile($F);
857        my $digest = $md5->hexdigest;
858        $text .= "$digest  $1\n" if m,([^/]+)$,
859    }
860    print $MD5 $text;
861    close $MD5
862}
863
864sub compute_md5 {
865    my ($to_check, $ignore) = @_;
866    my @files;
867    md5_add_tree($to_check, \@files, $ignore);
868    my $md5 = new Digest::MD5;
869    foreach (sort { $a->[0] cmp $b->[0] } @files) {
870        my $f = $_->[1];
871        open my $A, $f;
872        $md5->addfile($A);
873        #my $tmpmd5 = new Digest::MD5;
874        #local *A, open A, $f;
875        #$tmpmd5->addfile(*A);
876        #print "MD5: $_->[0] (", $tmpmd5->hexdigest() ,")\n";
877    }
878    my $digest = $md5->hexdigest;
879    # print "IGNORE " , join " ",keys %$ignore ,"\n";
880    return $digest
881}
882
883sub md5_add_tree {
884    my ($to_check, $files, $ignore) = @_;
885    foreach (@$to_check) {
886        my ($dest, $f) = @$_;
887        $f =~ m|/?\.{1,2}$| and next;
888        $f =~ /~$/ and next;
889        $f =~ s|//+|/|g;
890        $dest =~ s|//+|/|g;
891        $ignore->{$dest} and next;
892        if (-d $f) {
893            md5_add_tree([ map { [ "$dest/$_", "$f/$_" ] } all $f ], $files, $ignore)
894        } else {
895            push @$files, [ $dest, $f ]
896        }
897    }
898}
899
900sub log_ {
901    my ($msg, $verbose, $log, $level) = @_;
902    return if $level > $verbose;
903    my $LOG;
904    if (!$log) { open $LOG, ">&STDERR" } else { $LOG = $log }
905    print $LOG $msg;
906}
907
908# TODO must add some check of maximum authorized size
909sub include_md5 {
910    my ($iso, $write, $verbose) = @_;
911    my $ISO; 
912    if ($write) {
913        open $ISO, "+<$iso" or return "ERROR include_md5: unable to open $iso ($!)\n"; 
914    } else {
915        open $ISO, $iso or return "ERROR include_md5: unable to open $iso ($!)\n";     
916    }
917    binmode $ISO;
918    my $offset = 16*2048;
919    # blank header
920    seek $ISO, $offset, 0;
921    my ($buf, $msg);
922    while (1) {
923        read $ISO,$buf,2048;
924        my $c = ord $buf;
925        last if $c == 1;
926        return "ERROR include_md5: could not find primary volume descriptor\n" if $c == 255;
927        $offset += 2048
928    }
929    my $size = ((ord substr $buf, $SIZE_OFFSET, 1) * 0x1000000 + 
930                (ord substr $buf, $SIZE_OFFSET + 1, 1) * 0x10000 + 
931                (ord substr $buf, $SIZE_OFFSET + 2, 1) * 0x100 + 
932                (ord substr $buf, $SIZE_OFFSET + 3, 1)) * 2048;
933    my $volume = substr $buf, 30, 40;
934    $volume =~ s/^\s*(\S.*\S)\s*$/$1/;
935    my $id = substr $buf, 180, 20;
936    $msg = "include_md5: volume name $volume volume id: $id iso size $size\n";
937    seek $ISO, $offset + $INFO_OFFSET, 0;
938    read $ISO, $buf,512;
939    my ($md5sum) = $buf =~ /.md5 = (\S+)/;
940    $msg .= "include_md5: previous data $buf\n";
941    seek $ISO, 0, 0;
942    my $md5 = new Digest::MD5;
943    my $read = read $ISO, $buf, $offset + $INFO_OFFSET;
944    $md5->add($buf);
945    seek $ISO, 512, 1;
946    $read += 512;
947    $|=1;
948    my $val = int $size/2048/100;
949    $verbose and print "\rReading: 0 %";
950    my ($i, $j);
951    # skip last $SKIP bytes that sometimes are not correctly burned by some drives
952    my $n = 1;
953    while ($n && $read < $size - $SKIP * 2048) {
954        $n = read $ISO, $buf,2048;
955        print "\rReading: ", $j++, " %" if ($verbose && !($i++ % $val));
956        $md5->add($buf);
957        $read += $n;
958    }
959    print "\n";
960    my $digest = $md5->hexdigest;
961    $msg .= "include_md5: computed md5 $digest\n";
962    my $res = $md5sum eq $digest;
963    if ($md5sum) {
964        $msg .= "include_md5: previous md5 $md5sum\ninclude_md5: md5sum check ";
965        $msg .= $res ? "OK\n" : "FAILED\n"
966    }
967    print $msg if $verbose;
968    $write or return $res;
969    seek $ISO, $offset + $INFO_OFFSET, 0;
970    my $str = substr "$volume.md5 = $digest", 0, 512;
971    my $l = length $str;
972    print $ISO ($l > 512 ? substr $str, -1, 512 : $str . ' ' x (512 - $l));
973    close $ISO
974}
975
976sub convert_size {
977    my ($size, $default, $LOG) = @_;
978    if ($size =~ /[\d.]+g$/i) {
979        $size = $size * $GB;
980    } elsif ($size =~ /[\d+.]+m$/i) {
981        $size = $size * $MB;
982    } elsif ($size =~ /[\d+.]+k$/i) {
983        $size = $size * $KB;
984    } elsif ($size !~ /[\d+.]+$/i) {
985        log_("ERROR disc: $size is invalid, using default ($default)\n",1,$LOG);
986        $size = $default;
987    }                   
988   $size 
989}
990
991sub fix_dir {
992    chomp(my $pwd = `pwd`);
993    return map { m,^/, or $_ = "$pwd/$_"; $_ } @_
994}
995
9961
997
998#
999# Changelog
1000#
1001# 2002 02 27
1002# make the locale constraint free on the right for cleanrpmsrate locale addition (kde-i18n-zh_BG and such)
1003#
1004# 2002 03 03
1005# fix typo in checkdiscs
1006#
1007# 2002 03 04
1008# fix checkcds pb with check[0] used.
1009#
1010# 2002 03 07
1011# add possibility to remove package from rpmsrate
1012#
1013# 2002 03 12
1014# add all .*kernel- in rpmsrate
1015#
1016# 2002 03 17
1017# add serial name instead of cdnumber when name is not know
1018#
1019# 2002 05 07
1020# add check_discs, compute_md5, write_graft, md5_add_tree
1021#
1022# 2002 05 22
1023# fix a pb in md5
1024#
1025# 2002 05 25
1026# add log function
1027#
1028# 2002 06 05
1029# fix md5 for isolinux
1030#
1031# 2002 08 12
1032# fix/change cleanrpmsrate
1033#
1034# 2002 09 04
1035# do not open for writing iso file in include_md5 if not in write mode
1036#
1037# 2002 09 25
1038# add completion feedback to include_md5
Note: See TracBrowser for help on using the repository browser.