#!/usr/bin/perl -w
# vim:ts=4:sts=4

BEGIN {
	my $use_nls="yes" eq "yes" ? 1 : 0;
	if ($use_nls)
	{
		eval 'use Locale::gettext';
		$use_nls=!$@;
	}
	if ($use_nls)
	{
		textdomain("rlinetd");
		eval ' sub _g { return gettext(shift); } ';
	}
	else
	{
		eval 'sub _g { return shift; }';
	}
}


my %services=();
my $dir="/etc/rlinetd.d";
my $ifile="";
my @lines=();
my $opt_force_overwrite=0;
my $opt_gen_comment=0;
my $opt_print_fnames=0;

while ( defined($opt = $ARGV[0]))  {
	if ($opt eq "--force-overwrite") {
		$opt_force_overwrite = 1;
		shift(@ARGV);
	} elsif ($opt eq "--print-file-names") {
		$opt_print_fnames = 1;
		shift(@ARGV);
	} elsif ($opt eq "--add-from-comment") {
		$opt_gen_comment = 1;
		shift(@ARGV);
	} elsif ($opt eq "-f" || $opt eq "-l" ) {
		shift(@ARGV);
		$arg = shift(@ARGV);
		die sprintf( _g("Option \`%s' requires an argument\n"), $opt) unless ($arg);
		$ifile =  $arg if $opt eq "-f";
		push(@lines, $arg) if $opt eq "-l";
	} else {
		last;
	}
}

$dir = $ARGV[0] if defined $ARGV[0];
die sprintf(_g("Directory %s does not exist\n"), $dir) unless -d $dir;
die _g("Options \`-f' and \`-l' cannot be mixed together\n") if ($ifile ne "" && $#lines > -1);

if ($#lines > -1) {
	for $_ (@lines) {
		&add_single_line($_);
	}

} else {

	$ifile = "-" if $ifile eq "";
	open IFILE, $ifile or die sprintf(_g("Cannot open %s: %s\n"), $ifile, $!);
	while (<IFILE>) {
		&add_single_line($_);
	}
	close IFILE;

}

&output_services();

exit 0;

sub warn {
	$_ = shift;

	print STDERR $_;
	return 1;
}

sub add_single_line {
	$_ = shift;

	chomp;
	s/\\t/ /g;
	my $enabled = s/^\s*#<off>#\s*// ? "no" : "yes";
	return if(/^\s*#/);
	return unless(/^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/);

	my $out			= "";
	my $server		= "";
	my $gid			= "";
	my $rpcvers		= "";
	my $instances	= "";
	my $family		= "";
	my $name		= $1;
	my $proto		= $2;
	my $wait		= $3;
	my $uid			= $4;
	my $exec		= $5;

	&warn(sprintf(_g("skipping internal service: %s\n"), $name)) && return
		if ($exec eq "internal");

	if($proto =~ /^rpc\/(.+)$/) {
		$proto = $1;
		if($name =~ /^(.+)\/(.+)$/) {
			$name = $1;
			$rpcvers = $2;
		}
	}

	if($proto =~ /^(udp|tcp)(4|6)$/) {
		$proto = $1;
		$family = "ipv".$2;
	}

	&warn(sprintf(_g("unknown protocol: %s; skipping service %s\n"), $proto, $name)) && return
		unless ($proto eq "udp" || $proto eq "tcp");

	if($uid =~ /^(.+)\.(.+)$/) {
		$uid = $1;
		$gid = $2;
	}

	if($exec =~ /^(\S+)\s+(.+)$/) {
		if($1 ne $2) {
			$exec = $2;
			$server = $1;
		}
	}

	if($wait =~ /^nowait\.(\d+)$/) {
		$instances = $1;
	}

	my $key = $proto eq "tcp" ? $name : "${name}_${proto}";
	$out .= $services{$key} if defined $services{$key};
	$out .= "# Generated from: $_\n" if $opt_gen_comment;

	$out .= "service \"${name}_${proto}\" {\n";
	$out .= "\tenabled $enabled;\n";
	$out .= "\tprotocol $proto;\n";
	$out .= "\tfamily $family;\n" if ($family);
	$out .= "\tport \"$name\";\n" unless ($rpcvers);
	$out .= "\tuser \"$uid\";\n";
	$out .= "\texec \"$exec\";\n";
	$out .= "\tgroup \"$gid\";\n" if($gid);
	$out .= "\tserver \"$server\";\n" if($server);
	$out .= "\tinstances $instances;\n" if($instances);
	$out .= "\twait yes;\n" if ($wait eq "wait");
	$out .= "\trpc {\n\t\tname \"$name\";\n\t\tversion $rpcvers;\n\t}\n" if($rpcvers);
	$out .= "}\n";

	$services{$key} = $out;

}

sub output_services {

	my $tempfile = "$dir/.inetd2rlinetd.$$";
	my $realfile;

	for my $service (keys %services) {
		open OFILE, ">$tempfile" or die sprintf(_g("Cannot open temporary file for writing: %s\n"), $!);
		print OFILE "# This file was automatically generated by inetd2rlinetd\n\n";
		print OFILE $services{$service};
		close OFILE;

		$realfile = "$dir/$service";
		print $realfile . "\n" if $opt_print_fnames;
		my $mode= (-e $realfile) ? &ask_user($realfile, $tempfile) : "rename";


		if ($mode eq "append") {
			open OFILE, ">>$realfile" or die sprintf(_g("Cannot open file %s: %s\n"), $realfile, $!);
			print OFILE "\n" . $services{$service};
			close OFILE;
		}
		elsif ($mode eq "rename") {
			rename($tempfile, $realfile) or die sprintf(_g("Cannot rename tempfile: %s\n"), $!);
		}

		unlink $tempfile;
	}


}


sub ask_user {
	my $real_f = shift;
	my $tmp_f  = shift;

	# check if the two files are different
	open FILE, "<$real_f" or die sprintf(_g("Cannot open file %s: %s\n"), $real_f, $!);
	my @real_data = <FILE>;
	close FILE;
	open FILE,  "<$tmp_f"	or die sprintf(_g("Cannot open file %s: %s\n"), $tmp_f, $!);
	my @tmp_data  = <FILE>;
	close FILE;

	return "skip" if join('', @real_data) eq join('', @tmp_data);

	return "rename" if $opt_force_overwrite;


	my $mode="";
	my $input = undef;
	my $on_tty = ( -t STDERR ) and
		( -t STDIN or open $input, "</dev/tty" );
	if (! $on_tty ) {
		&warn( sprintf(_g("File %s already exists.\n") .
			_g("I'm refusing to overwrite it with different version,\nwhen I'm not running on the terminal.\n"), $real_f));
		return "skip"
	}

	while ($mode eq "") {

		print STDERR sprintf("\n\n" . _g("File %s already exists.\n") . "\n", $real_f);
		print STDERR _g("* Show [d]ifference between new version.\n");
		print STDERR _g("* [O]verwrite the file.\n");
		print STDERR _g("* [S]kip the file.\n");
		print STDERR _g("* [A]ppend to the end of the file.\n") . "\n";

		do {
			print STDERR _g("Please choose: [D/O/S/A]: ");
			$_=  (defined $input) ? <$input> : <STDIN>  ;
			chomp;
			$_= uc $_;

			$mode = "rename" 	if ($_ eq "O");
			$mode = "skip"		if ($_ eq "S");
			$mode = "append"	if ($_ eq "A");
			$mode = "diff"		if ($_ eq "D");
		} until ($mode ne "");

		if ($mode eq "diff") {
			system "diff", "-Nus", "$real_f", "$tmp_f";
			$mode="";
		}

	}

	close $input if defined ($input ) ;
	return $mode;
}


