#!/usr/bin/perl # Copyright (C) 2005, 2006, 2007 Christopher Faylor # # This software is a copyrighted work licensed under the terms of the # GNU General Public License. See http://www.gnu.org/copyleft/gpl.html # for details. # use File::Basename; use Digest::MD5; use Getopt::Long; use strict; sub mywarn(@); sub myerror(@); sub usage(); my %md5s; if( open F, "md5sums" ) { while() { chomp; my($md5,$s,$t,$f) = split / /; next unless -f $f; $md5s{$f}{md5} = $md5; $md5s{$f}{s} = $s; $md5s{$f}{t} = $t; } close F; } my @okmissing; my ($outfile, $help, $recursive); GetOptions('okmissing=s'=>\@okmissing, 'output=s'=>\$outfile, 'help'=>\$help, 'recursive'=>\$recursive) or usage; $help and usage; @main::okmissing{@okmissing} = @okmissing; if (defined($outfile)) { open(STDOUT, '>', $outfile) or die "$0: can't open $outfile - $!\n"; } my %pkg; for my $f (@ARGV) { if (-d "$f/.") { parsedir($f); } else { parse($f); } } print <<'EOF'; # This file is automatically generated. If you edit it, your # edits will be discarded next time the file is generated. # See http://cygwin.com/setup.html for details. # EOF print "$main::setup_timestamp\n" if $main::setup_timestamp; print "$main::setup_version\n" if $main::setup_version; undef $main::curfile; for my $p (sort keys %pkg) { print "\n@ $p\n"; for my $key ('sdesc', 'ldesc', 'category', 'requires') { my $val = $pkg{$p}{''}{$key}; if (!defined($val) && $pkg{$p}{''}{'install'} !~ /_obsolete/o) { mywarn "package $p is missing a $key field" unless defined $main::okmissing{$key}; } else { if ($key eq 'requires') { for my $p1 (split(' ', $val)) { mywarn "package $p requires an unknown package '$p1'" unless $pkg{$p}; } } elsif ($key eq 'category') { for my $c (split(' ', $val)) { mywarn "package $p uses an invalid category '$c'" unless $main::categories{lc $c}; } } print "$key: ", $val, "\n" if $val; } } for my $what ('', "[prev]\n", "[test]\n") { $pkg{$p}{$what} or next; print "$what"; for my $key ('version', 'install', 'source') { my $val = $pkg{$p}{$what}{$key} or next; print "$key: ", $val, "\n"; } } } open F, ">md5sums"; for my $f (keys %md5s) { print F $md5s{$f}{md5} . " " . $md5s{$f}{s} . " " . $md5s{$f}{t} . " " . $f . "\n"; } close F; sub get { my $FH = shift; my $key = shift; my $val = shift; if (substr($val, 0, 1) ne '"') { $val = '"'. $val . '"' if $key eq 'ldesc' || $key eq 'sdesc'; } else { while (length($val) == 1 || $val !~ /"$/os) { $_ = <$FH>; length or last; chomp; s/(\S)\s+$/$1/; $val .= "\n" . $_; } } $val =~ s/(.)"(.)/$1'$2/mog; return $val; } sub parse { my $f = shift; my $pname = shift; my $what; $main::curfile = $f; $. = 0; open(\*F, '<', $f) or die "$0: couldn't open $f - $!\n"; while () { chomp; s/#.*$//o; s/^\s+//o; s/(\S)\s+$/$1/o; length or next; /^setup-timestamp:/ and do { $main::setup_timestamp = $_; next; }; /^setup-version:/ and do { $main::setup_version = $_; next; }; /^\@\s+(\S+)/ and do { $pname = $1; $what = ''; next; }; /^([^:]+):\s*(.*)$/ and do { my $key = $1; my $val = $2; if ($key !~ /^(?:prev|curr|test)/) { $pkg{$pname}{$what}{$key} = get(\*F, $key, $val); } else { if ($key eq 'curr') { $key = ''; } else { $key = "[$key]\n"; } $pkg{$pname}{$key}{'version'} = $val; } next; }; /^\[[^\]]+\]/ and do { $what = $_ . "\n"; next; }; die "$0: unrecognized input at line file $f, line $.\n"; } } sub compare_versions { my($a, $b) = @_; my @a = split /\./, $a; my @b = split /\./, $b; my $n = @a < @b ? @a : @b; while( @a && @b ) { my $a = shift @a; my $b = shift @b; next if $a eq $b; my ($an) = $a =~ /^(\d+)/; my ($bn) = $b =~ /^(\d+)/; return defined $an && defined $bn ? $an <=> $bn : $an cmp $bn; } return @a ? 1 : @b ? -1 : 0; } sub parsedir { my $d = shift; my $pname = basename($d); delete $pkg{$pname}; if ($recursive) { for my $drecur (glob("$d/*/.")) { last if $drecur =~ /\*/; parsedir(dirname($drecur)); } } my $setup_hint = "$d/setup.hint"; return unless -e $setup_hint; parse("$setup_hint", $pname); my $explicit = 0; for my $what ('', "[prev]\n", "[test]\n") { my $x = $pkg{$pname}{$what}; next unless $x->{'version'}; $explicit = 1; addfiles($pname, $x, $d); } return if $explicit; my @files = sort { my($an,$av,$ap) = ($a =~ /(.*)-([^-]+)-(\d+).tar.bz2$/); my($bn,$bv,$bp) = ($b =~ /(.*)-([^-]+)-(\d+).tar.bz2$/); my $r; if( defined $ap && defined $bp ) { $r = ($an ne $bn) ? $an cmp $bn : $av ne $bv ? compare_versions($av, $bv) : compare_versions($ap, $bp); } else { $r = $a cmp $b; } return $r; } grep { !/-src\.tar.bz2/ } glob("$d/*.tar.bz2"); if (!@files) { myerror "not enough package files in $d"; return; } for my $what ('', "[prev]\n") { my $f = pop @files or last; $pkg{$pname}{$what}{-unused} = 1; my $x = $pkg{$pname}{$what}; my $p; ($p, $x->{'version'}) = getver($f); addfiles($p, $x, $d); } } sub addfiles { my $pname = shift; my $x = shift; my $d = shift; my $install = "$d/" . tarball($pname, $x->{'version'}); filer($x, 'install', $install); if ($pkg{$pname}{''}{'external-source'}) { $pname = $pkg{$pname}{''}{'external-source'}; $d = finddir($d, $pname) or return; } my $source = "$d/" . tarball($pname, $x->{'version'}, 'src'); filer($x, 'source', $source); } sub getver { my $f = basename($_[0]); my @a = ($f =~ /^(.*?)-(\d.*)\.tar/); return wantarray ? @a : $a[1]; } sub filer { my $x = shift; my $what = shift; my $f = shift; unless( -f $f ) { # For OSGeo4W we don't mind lacking a source file. $what == "source" || myerror "can't open $f - $!" unless $main::okmissing{$what}; return undef; } my $digest = $md5s{$f}{md5}; my ($s,$t) = (stat $f)[7,9]; # size and mtime unless( defined $digest && $md5s{$f}{s}==$s && $md5s{$f}{t}==$t ) { open(*F, '<', $f) or die "cannot open $f"; my $md5 = Digest::MD5->new; $md5->addfile(\*F); $digest = $md5->hexdigest; $md5s{$f}{md5} = $digest; $md5s{$f}{s} = $s; $md5s{$f}{t} = $t; } $x->{$what} = join(' ', $f, -s $f, $digest); } sub tarball { return join('-', @_) . '.tar.bz2'; } sub fnln { return $main::curfile ? "$main::curfile:$.: " : ''; } sub mywarn(@) { warn "warning: " . fnln . "@_\n"; } sub myerror(@) { warn "error: " . fnln . "@_\n"; } sub finddir { my $d = $_[0]; my $pname = $_[1]; while (($d = dirname($d)) ne '.' && length($d)) { return "$d/$pname" if -d "$d/$pname/."; } myerror "couldn't find package directory for external-source '$pname'"; return undef; } sub usage() { print STDERR <<'EOF'; Usage: genini [--okmissing=key ...] [--output=file] [--help] [setup.ini] [dir ...] Create cygwin setup.ini from setup.ini, setup.hint and tar ball information. --okmissing=key don't warn if key is missing from setup.ini or setup.hint --output=file output setup.ini info to file --help display this message Report bugs to cygwin mailing list. EOF exit 0; } BEGIN { my @cats = qw' Commandline_Utilities Desktop Libs Web Web_Applications _obsolete _PostInstallLast '; @main::categories{map {lc $_} @cats} = @cats; }