#!/usr/bin/perl -w 

use strict;
use RPM::Specfile::Multispec;
use Getopt::Long;

my $defsource = 'foo.orig.tar.gz';
my $defpatch  = 'foo.diff.gz';
my $path= '.';

my $verbose=0;
my ($help);

GetOptions (
    "help"  => \$help,
    "verbose+"  => \$verbose,
);

if ($help) {
	_usage();
	exit 0;
}

unless (@ARGV) {
    if ( -f 'rules' and -f 'control') {
	$path= '.';
    } elsif ( -d 'debian' and -f 'debian/control') {
	$path= 'debian';
    } else {
	_usage();
	exit 1;
    }
}

sub _usage {
    print <<EOF
usage: debian2spec [-v] <path to 'debian' directory> [ <source/patch files to be mentioned in spec file> ]
Options:
-h,--help	this help
-v,--verbose	increase the verbosity level
EOF
;
}

$path= shift @ARGV;

my $debian=Debian::Src->new(debug=>$verbose, path=>$path);
my @debpackage=@{$debian->{PKGDEBS}};
my $spec=RPM::Specfile::Multispec->new();

&set_source($spec,$debpackage[0]);
foreach (@ARGV) {
    if (/\.(d?patch|diff)(?:\.(Z|gz|bz2?))$/) {
	$spec->push_patch($_);
    } else {    
	$spec->push_source($_);
    }
}

$spec->prep('%setup -q');

my $src=$debian->{SRCDEB};
$spec->buildrequires(&rpmpkglist($src->{'Build-Depends'}));
$spec->group(&RPM::Specfile::ALTLinux::substitute_section($src->{'Section'})) if $src->{'Section'};
$spec->release("alt1");
$spec->version("?");

if ($debpackage[0]->{'copyright'}) {
    if ($debpackage[0]->{'copyright'} =~/GNU General Public License/) {
	if ($debpackage[0]->{'copyright'} =~/version 2 dated/) {
	    $spec->license("GPL2");
	} elsif ($debpackage[0]->{'copyright'} =~/version 2 or hi/) {
	    $spec->license("GPL2+");
	} elsif ($debpackage[0]->{'copyright'} =~/version 3 or hi/) {
	    $spec->license("GPL3+");
	} elsif ($debpackage[0]->{'copyright'} =~/version 3/) {
	    $spec->license("GPL3");
	} else {
	    $spec->license("GPL");
	}
    } else {
	warn "copyright not detected";
	$spec->license("?");
    }
} else {
    warn "copyright not found";
    $spec->license("?");
}
$spec->url("?");

if ($debpackage[0]->{'watch'}) {
    my $url=(split(/\n/,$debpackage[0]->{'watch'}))[1];
    $url=~s/\s*\\?$//;
    $spec->url($url);
}

if ($debpackage[0]->{'dpatch'}) {
    foreach (@{$debpackage[0]->{'dpatch'}}) {
	$spec->push_patch($_);
    }
}


&RPM::Specfile::Macro::load_macro();
my $packager=&RPM::Specfile::Macro::get_macro('packager');
#my $packager=$RPM::Specfile::Macro::macro{'packager'};
$spec->packager($packager) if $packager;

$spec->build(
'
## --------------------- debian rules makefile -----------------------------
## NOTE: it is makefile, not a shell file!!! you should convert it manually!
## -------------------------------------------------------------------------
'.$src->{'rules'}.'
## --------------------- end debian rules makefile --------------------------
') if $src->{'rules'};

my $install='';
foreach my $debpkg (@debpackage) {
    $install.="# $debpkg->{'doc-base'}\n#".join("\n#",split (/\n/,$debpkg->{'doc-base-text'}))."\n"
	if $debpkg->{'doc-base-text'};
}

$spec->install($install);

&fill_spec($spec,$debpackage[0]);
&add_man($spec,$debpackage[0]);

for (my $i=1; $i<@debpackage; $i++) {
    my $package=RPM::Specfile::Multispec->new();
    &fill_spec($package,$debpackage[$i]);
    $spec->push_package($package);
}

print $spec->generate_specfile();


sub fill_spec {
    my $spec=shift;
    my $pkg=shift;
    my $name=$pkg->{'Package'};
    $name=~s/-dev$/-devel/;
    $spec->name($name);
    $spec->requires(&rpmpkglist($pkg->{'Depends'})) if $pkg->{'Depends'};
    $spec->push_provide(&rpmpkglist($pkg->{'Provides'})) if $pkg->{'Provides'};
    $spec->conflicts(&rpmpkglist($pkg->{'Conflicts'})) if $pkg->{'Conflicts'};
    my ($summary,$description) =split ("\n",$pkg->{'Description'},2);
    $spec->summary($summary);
    $spec->description($description);
    $spec->preun($pkg->{'prerm'}) if $pkg->{'prerm'};
    $spec->postun($pkg->{'postrm'}) if $pkg->{'postrm'};
    $spec->pre($pkg->{'preinst'}) if $pkg->{'preinst'};
    $spec->post($pkg->{'postinst'}) if $pkg->{'postinst'};
    if ($pkg->{'changelog'}) {
	if ($pkg->{'changelog'}=~/^$pkg->{'Package'}\s+\(([^\)\s-]+)-([\d.]+)\)/) {
	    #$spec->release("alt1.deb$2");
	    $spec->version("$1");
	}
    }
    map{ $spec->push_file("%doc ".&RPM::Specfile::ALTLinux::macroize_path($_)) } split /\n/, $pkg->{'docs'} if $pkg->{'docs'};
    map{ $spec->push_file("%config ".&RPM::Specfile::ALTLinux::macroize_path($_)) } split /\n/, $pkg->{'conffiles'} if $pkg->{'conffiles'};
    map{ $spec->push_file(&RPM::Specfile::ALTLinux::macroize_dir($_)) } split /\n/, $pkg->{'dirs'} if $pkg->{'dirs'};
    map{ $spec->push_file(&RPM::Specfile::ALTLinux::macroize_path($_)) } split /\n/, $pkg->{'files'} if $pkg->{'files'};
}

sub add_man {
    my $spec=shift;
    my $pkg=shift;
    my $man_install='';
    my %man_dir;
    foreach (@{$pkg->{'man'}}) {
	/\.(.)$/;
	my $man_level=$1;
	my $man_path=&RPM::Specfile::ALTLinux::macroize_path("usr/share/man/man$man_level");
	$man_dir{$man_path}=1;
	$spec->push_file("$man_path/$_*");
	$man_install .= "install -m644 debian/$_ \${RPM_BUILD_ROOT}$man_path/\n";
    }
    my $man_dirlist='{'.join(',',keys(%man_dir)).'}';
    $man_dirlist=join(',',keys(%man_dir)) if (keys(%man_dir)==1);
    $spec->install(
	$spec->install()."

# installing debian man pages
install -d \${RPM_BUILD_ROOT}$man_dirlist
$man_install
") if $man_install;
}

sub set_source {
    my $spec=shift;
    my $pkg=shift;
    if ($pkg->{'changelog'}) {
	if ($pkg->{'changelog'}=~/^([^\s]+)\s+\(([^\)\s-]+)-([\d.]+)\)/) {
	    my $debsrcname=$1;
	    my $debsrcver=$2;
	    my $debsrcrel=$3;
	    $debsrcver=~s/^\d://;
	    #$spec->release("alt1.deb$debsrcrel");
	    $spec->version($debsrcver);
	    $spec->push_source("$debsrcname-$debsrcver.tar.gz");
	    $spec->push_patch($debsrcname."_$debsrcver-$debsrcrel.diff.gz");
	}
    } else {
	$spec->push_source($defsource);
	$spec->push_patch($defpatch);
    }
}

sub rpmpkglist {
    my $debpkglist=shift;
    my @pkglist;
    return '' unless $debpkglist;
    foreach (split /\,\s*/, $debpkglist) {
	next if /^(debhelper|g?awk)/;
	next if /\$\{shlibs:Depends\}/;
	s/-dev(\s|$)/-devel/;
	s/\(= \$\{Source-Version\}\)/ = %name-%version/;
	push @pkglist, $_;
    }
    return join (' ',@pkglist);
}

package Debian::Src;

sub new {
    my $class=shift;
    my $self={
	source => 'foo.orig.tar.gz',
	patch => 'foo.diff.gz',
	path=> '.',
	debug=> 0,
	@_
	};
    bless $self, $class;
    my $dir=$self->{path};
    my ($src, $pkgptr)=&_read_debian_control($self->{path});
    map {&_show_obj($_);} $src, @$pkgptr if $self->{debug};
    $self->{SRCDEB}=$src;
    $self->{PKGDEBS}=$pkgptr;
    return $self;
}

sub _read_debian_control {
    my $dir=shift;
    open CTL, "$dir/control";
    my $src={'__dir__' => $dir};
    my @pkgs;
    my $curobj=$src;
    my $multiline=0;
    my $multitag='';
    while (<CTL>) {
	chomp;
	if (/^\s*$/) {
	    $multiline=0;
	    $multitag='';
	    push @pkgs, $curobj unless $curobj == $src;
	    $curobj={'__dir__' => $dir};
	} elsif ($multiline) {
	    $curobj->{$multitag}.="\n$_";
	} elsif (/^([-\w]+): (.+)$/) {
	    $curobj->{$1}=$2;
	    if ($1 eq 'Description') {
		$multiline=1;
		$multitag='Description';
	    }
	}
    }
    push @pkgs, $curobj if $curobj != $src and keys(%$curobj)>2; #not a placeholder
    close CTL;

    #&_show_obj($src);
    #map {&_show_obj($_)} @pkgs;
    #exit;

    $src->{'rules'}=&_read_file("$dir/rules");
    $pkgs[0]->{__default__}=1 if @pkgs>0;
    map {&_read_deb_files($_);} @pkgs;
    return $src, \@pkgs;
    
}

sub _show_obj {
    my $obj=shift;
    my ($key,$val);
    print "================\n";
    while (($key,$val) = each %{$obj}) {
	print "$key: $val\n";
    }
    print "================\n";
}

sub _read_deb_files {
    my $obj=shift;
    my $dir=$obj->{'__dir__'};
    my $name=$obj->{'Package'};
    warn "_read_deb_files: internal error: __dir__ is empty" unless $dir;
    warn "_read_deb_files: internal error: Package is empty" unless $name;
    my $isdefault=$obj->{'__default__'};
    foreach my $key (qw/
	changelog
	conffiles
	copyright
	dirs
	doc-base
	docs
	files
	menu
	menu-method
	postinst
	postrm
	preinst
	prerm
	README
	head
	watch
		     /) {
	# bug: see tipa: do not load main files, dirs in subpackages
	if (-e "$dir/$name.$key") {
	    $obj->{$key}=&_read_file("$dir/$name.$key");
	} elsif ($isdefault && -e "$dir/$key") {
	    $obj->{$key}=&_read_file("$dir/$key");
	}
	if ($key eq 'doc-base' and $obj->{'doc-base'}) {
	    my $docbase="$dir/../".$obj->{'doc-base'};
	    chomp $docbase;
	    $obj->{'doc-base-text'}=&_read_file($docbase) if -f $docbase;
	}
    }
    if ($isdefault) {
	my @mans;
	my @dpatchs;
	opendir DIR, $dir || die "can't opendir $dir: $!";
	foreach (readdir(DIR)) {
	    next unless -f "$dir/$_";
	    push @mans, $_ if /^.+\.[\dnx]$/;
	    push @dpatchs, $_ if /^.+\.dpatch$/;
	}
	close DIR;
	$obj->{'man'}=\@mans;
	$obj->{'dpatch'}=\@dpatchs;
    }
    if ($obj->{'changelog'} && $obj->{'changelog'}=~/^$obj->{'Package'}\s+\(([^\)\s-]+)-([\d.]+)\)/) {
	$obj->{release}=$2;
	$obj->{version}=$1;
    }
}

sub _read_file {
    my $file=shift;
    my $content = '';
    open my $fh, $file or die $!;
    {
	local $/;
	$content = <$fh>;
    }
    close $fh;
    return $content;
}

package RPM::Specfile::Macro;

BEGIN{
    use vars qw/%macro/;
    %macro = ('nil', '');
}

sub load_macro {
    open (RPMMACROS, $ENV{'HOME'}.'/.rpmmacros') || warn "can't open .rpmmacros:$!";
    while (<RPMMACROS>) {
	if (/^[%]([\w_\/.\d]+)\s+(.+)\s*$/) {
	    $macro{$1}=$2 ;
	    &macro_subst(\$macro{$1});
	}
    }
    close (RPMMACROS);
}

sub get_macro {
    return $macro{shift()};
}
sub macro_subst {
    my $argptr=shift;
    while ($$argptr=~/\%\{?(\w+)\}?/ and $macro{$1}) {
	$$argptr=~s/\%\{?(\w+)\}?/$macro{$1}/;
    }
}


package RPM::Specfile::ALTLinux;

use vars qw/%pathsubst %groupsubst/;

BEGIN {
# reverse sorting order is important
# sort -r -k1,1
%pathsubst = qw !
var/spool	%_spooldir
var/log	%_logdir
var/lock	%_lockdir
var/lib	%_localstatedir
var/cache	%_cachedir
usr/X11R6/share	%_x11datadir
usr/X11R6/man	%_x11mandir
usr/X11R6/lib/X11/fonts/Type1	%_type1fontsdir
usr/X11R6/lib/X11/fonts	%_x11fontsdir
usr/X11R6/lib/X11	%_x11x11dir
usr/X11R6/lib	%_x11libdir
usr/X11R6/include	%_x11includedir
usr/X11R6/bin	%_x11bindir
usr/X11R6	%_x11dir
usr/share/tcl	%_tcldatadir
usr/share/pixmaps	%_pixmapsdir
usr/share/man/man9	%_man9dir
usr/share/man/man8	%_man8dir
usr/share/man/man7	%_man7dir
usr/share/man/man6	%_man6dir
usr/share/man/man5	%_man5dir
usr/share/man/man4	%_man4dir
usr/share/man/man3	%_man3dir
usr/share/man/man2	%_man2dir
usr/share/man/man1	%_man1dir
usr/share/man	%_mandir
usr/share/license	%_licensedir
usr/share/javadoc	%_javadocdir
usr/share/java	%_javadir
usr/share/info	%_infodir
usr/share/icons/mini	%_miconsdir
usr/share/icons/large	%_liconsdir
usr/share/icons	%_niconsdir
usr/share/icons	%_iconsdir
usr/share/gtk-doc/html	%_gtkdocdir
usr/share/games	%_gamesdatadir
usr/share/fonts/type1	%_type1fontsdir
usr/share/fonts/ttf	%_ttffontsdir
usr/share/fonts/otf	%_otffontsdir
usr/share/fonts/bitmap	%_bitmapfontsdir
usr/share/fonts	%_fontsdir
usr/share/fillup-templates	%_fillupdir
usr/share/emacs/site-lisp	%_emacslispdir
usr/share/emacs/etc	%_emacs_etc_dir
usr/share/emacs	%_emacs_datadir
usr/share/doc/HTML	%_kdedocdir
usr/share/doc	%_defaultdocdir
usr/share/applications	%_desktopdir
usr/share/aclocal	%_aclocaldir
usr/share	%_datadir
usr/sbin	%_sbindir
usr/lib/tcl	%_tcllibdir
usr/lib/pkgconfig	%_pkgconfigdir
usr/lib/menu	%_menudir
usr/libexec	%_libexecdir
usr/lib64	%_libdir
usr/lib	%_libdir
usr/include	%_oldincludedir
usr/include	%_includedir
usr/games	%_gamesbindir
usr/com	%_sharedstatedir
usr/bin	%_bindir
usr/sbin	%_sbindir
lib/security	%_pam_modules_dir
etc/rc.d/init.d	%_initdir
etc/init.d	%_initrddir
etc/emacs/site-start.d	%_emacs_sitestart_dir
etc	%_sysconfdir
!;

%groupsubst = (
'admin',	'Security/Antivirus|Security/Networking|File tools|Monitoring|Networking/Other|Networking/Remote access',
'base',	'System/Base',
'comm',	'Office',
'devel',	'Development/(C|C++|Databases|Debuggers|Documentation|Functional|GNOME and GTK+|Haskell|Java|KDE and QT|Kernel|Lisp|ML|Objective-C|Other|Ruby|Scheme|Tcl)',
'doc',	'Documentation',
'editors',	'Editors',
'electronics',	'Development/Other',
'embedded',	'Development/Other',
'games',	'Toys|Games/(Adventure|Arcade|Boards|Cards|Educational|Other|Puzzles|Sports|Strategy)',
'gnome',	'Graphical desktop/GNOME',
'graphics',	'Graphics',
'hamradio',	'Communications',
'interpreters',	'Development/(Functional|Haskell|Lisp|ML|Other|Ruby|Scheme|Tcl)',
'kde',	'Graphical desktop/KDE',
'libdevel',	'Development/(C|C++)',
'libs',	'System/Libraries',
'mail',	'Networking/Mail',
'math',	'Sciences/Mathematics',
'misc',	'<?misc?>',
'net',	'Networking/Chat|DNS|File transfer|IRC|Instant messaging|Other|Remote access',
'news',	'Networking/News',
'oldlibs',	'System/Libraries',
'otherosfs',	'Emulators|System/Kernel and hardware|File tools|Archiving/Backup|Archiving/Cd burning|Archiving/Compression|Archiving/Other|Communications',
'perl',	'Development/Perl',
'python',	'Development/Python',
'science',	'Sciences/Astronomy|Biology|Chemistry|Computer science|Geosciences|Medicine|Other|Physics',
'shells',	'Shells',
'sound',	'Sound',
'tex',	'Publishing',
'text',	'Text tools',
'utils',	'<?utils?>',
'web',	'Networking/WWW',
'x11',	'System/X11'
);
}

sub macroize_path {
    my $path=shift;
    my $len=0;
    my $goodpath="";
    foreach my $syspath (keys %pathsubst) {
	# aggressive subst:
	#if ($path=~/$syspath/ && ($len < length($syspath))) {
	if ($path=~/^$syspath/ && ($len < length($syspath))) {
	    $len=length($syspath);
	    $goodpath=$syspath;
	}
    }
    $path=~s/$goodpath/$pathsubst{$goodpath}/ if $len>0;
    $path=~s!^//!/!;
    $path=~s!^/%!%!;
    return $path;
}

sub macroize_dir  {
    my $path=shift;
    my $substed_path=&macroize_path($path);
    return $substed_path.'/*';
}

sub substitute_section {
    my $section=shift;
    return unless $section;
    $section=~s,^contrib/,,;
    $section=~s,^non-free/,,;
    return $groupsubst{$section} ? $groupsubst{$section} : $section;
}

1;


# Local Variables:
# mode: perl
# End:
