#!/usr/bin/perl
use FileHandle;

my %depend = ();
my %minver = ();
my %config = ();
my %intopt = ();
my %hexopt = ();
my %tristate = ();
my $version, $level, $sublevel;

my $force_kconfig=shift;

sub add_bool($)
{
	my $arg=shift;

	exists $config{$arg} or die "Adding unknown boolean '$arg'";
	$tristate{$arg}="bool";
#	printf "Boolean:%s\n",$arg;
}

sub add_tristate($)
{
	my $arg=shift;

	exists $config{$arg} or die "Adding unknown tristate '$arg'";
	$tristate{$arg}="tristate";
#	printf "Tristate:%s\n",$arg;
}

sub add_int($)
{
	my $arg=shift;

	exists $config{$arg} or die "Adding unknown int '$arg'";
	$intopt{$arg} = 0;
}

sub add_hex($)
{
	my $arg=shift;

	exists $config{$arg} or die "Adding unknown hex '$arg'";
	$hexopt{$arg} = 0;
}

sub set_int_value($$)
{
	my $key = shift;
	my $val = shift;

	exists $intopt{$key} or die "Default for unknown int option '$key'";
	$intopt{$key} = $val;
}

sub set_hex_value($$)
{
	my $key = shift;
	my $val = shift;

	exists $hexopt{$key} or die "Default for unknown hex option '$key'";
	$hexopt{$key} = "0x".$val;
}

sub add_config($)
{
	my $arg=shift;

	if ($arg =~ m/^(\w+)/) {
		# Have option default to 'on'
		$config{$1} = 1;
	} else {
	    die "Do not understand config variable '$arg'";
	}
}

# Turn option off, iff it already exists
sub disable_config($)
{
	my $key = shift;

	$config{$key} = 0 if (exists $config{$key});
}

sub check_deps($)
{
	my $arg=shift;
	$arg=$arg." ";

	while ($arg ne "") {
		if ($arg =~ m/^([A-Z0-9_]+) /) {
			my $val=$1;
			my $prev=$depend{$val};
			$depend { $val } = 1+$prev;
		}
		$arg =~ s/^[^ ]+ //;
	}
}

sub open_kconfig($$) {
	my ($dir,$file)=@_;
	my $in = new FileHandle;
	my $disabled=0;
	my $key;

#print "opening $file\n";
	open $in,"$file";
	while (<$in>) {
#		if (m;^\s*source[\s\"]+drivers/media/(video|dvb)/Kconfig;) {
#			next;
#		}

		# start of a new stanza, reset
		if (m/^\w/) {
			$disabled = 0;
			$default_seen = 0;
			$key = undef;
		}

		if (m|^\s*source\s+"([^"]+)"\s*$| ||
		    m|^\s*source\s+(\S+)\s*$|) {
			open_kconfig($dir,"$dir/$1");
			next;
		}
		if (m|^\s+depends on\s+(.*)\n|) {
			check_deps ($1);
		}
		if (m|^\s+select\s+(.*)\n|) {
			check_deps ($1);
		}
		if (m|^\s+bool(ean)?\s|) {
			add_bool($key);
		}
		if (m|^\s+tristate\s|) {
			add_tristate($key);
		}
		if (m|^\s+int\s|) {
			add_int($key);
		}
		if (m|^\s+hex\s|) {
			add_hex($key);
		}
		# Get default for int options
		if (m|^\s+default "(\d+)"| && exists $intopt{$key}) {
			set_int_value($key, $1);
		}
		# Get default for hex options
		if (m|^\s+default "(0x)?([[:xdigit:]]+)"| && exists $hexopt{$key}) {
			set_hex_value($key, $2);
		}
		# Override default for disabled tri/bool options
		if (m/^\s+default (y|n|m|"yes"|"no")\s+(if .*)?$/ &&
		    exists $tristate{$key} && $disabled) {
			$default_seen = 1;
			$_ = "\tdefault n\n";
		}

		# check for end of config definition for disabled drivers
		# we need to make sure we've disabled it, and add a bit
		# to the help text
		if (m|^\s*(---)?help(---)?\s*$| && $disabled) {
		    	if(exists $tristate{$key} && !$default_seen) {
				print OUT "\tdefault n\n";
			}
			print OUT <<"EOF";
	depends on VIDEO_KERNEL_VERSION
	help
	  WARNING! This driver needs at least kernel $minver{$key}!  It may not
	  compile or work correctly on your kernel, which is too old.

EOF
			next;
		}

		if (m|^\s*config (\w+)\s*$|) {
			$key=$1;
			add_config ($1);

			my $min=$minver { $key };
			my $minversion, $minlevel, $minsublevel;
			if ($min =~ m/(\d+)\.(\d+)\.(\d+)/) {
				$minversion=$1;
				$minlevel=$2;
				$minsublevel=$3;
			} else {
				die "Minimum version for $key not found at versions.txt";
			}
			if ( ($version < $minversion) ||
				($level < $minlevel) ||
				($sublevel < $minsublevel) ) {
				$disabled=1;
				disable_config ($key);
				print "$key requires version $minversion.$minlevel.$minsublevel\n";
				print OUT "# $key disabled for insufficient kernel version\n";
			} else {
#				print "OK: $key requires version $minversion.$minlevel.$minsublevel\n";
				$disabled=0;
			}
		}

		s/^main(menu\s\"[^\"]+)/\1 - DON'T CHANGE IT!/;

		print OUT $_;
	}
	close $in;
}

sub parse_versions ()
{
	my $in = new FileHandle;
	my $ver;

	open $in,"versions.txt";
	while (<$in>) {
		if (m/\[([\d.]*)\]/) {
			$ver=$1;
			next;
		}
		s/\n//;
		if (m/^\s*([\w\d_]+)/) {
			$minver { $1 } = $ver;
#			printf ("%s=%s\n",$1,$ver);
		}
	}
	close $in;
}

parse_versions;

open IN,".version";
while (<IN>) {
	if (m/KERNELRELEASE\s*[:]*[=]+\s*(\d+)\.(\d+)\.(\d+)/) {
		$version=$1;
		$level=$2;
		$sublevel=$3;
	}
}
close IN;

printf "Preparing to compile for kernel version %d.%d.%d\n",$version,$level,$sublevel;

open OUT,">Kconfig";

print OUT <<"EOF";
mainmenu "V4L/DVB menu"
source Kconfig.kern
config VIDEO_KERNEL_VERSION
	bool "Enable drivers not supported by this kernel"
	default n
	---help---
	  Normally drivers that require a kernel newer $version.$level.$sublevel,
	  the kernel you are compiling for now, will be disabled.
	  
	  Turning this switch on will let you enabled them, but be warned
	  they may not work properly or even compile.  
	  
	  They may also work fine, and the only reason they are listed as
	  requiring a newer kernel is that no one has tested them with an
	  older one yet.

	   If the driver works, please post a report at V4L mailing list:
			video4linux-list\@redhat.com.

	  Unless you know what you are doing, you should answer N.

EOF

open_kconfig ("../linux","../linux/drivers/media/Kconfig");
close OUT;

while ( my ($key, $value) = each(%config) ) {
		delete $depend{$key};
}

open OUT,">Kconfig.kern";

print OUT "config MODULES\n\tboolean\n\tdefault y\n\n";
add_config('MODULES');
add_bool('MODULES');

while ( my ($key, $value) = each(%depend) ) {
	print OUT "# $key with $value refs\nconfig $key\n\ttristate\n\tdefault m\n\n";
}
close OUT;

# These options should default to off
disable_config('DVB_AV7110_FIRMWARE');
disable_config('DVB_CINERGYT2_TUNING');

# Produce a .config file if it's forced or one doesn't already exist
if (($force_kconfig eq 1) || !open IN,".config") {
	open OUT,">.config";
	while ( my ($key,$value) = each(%tristate) ) {
		if (!$config{$key}) {
			print OUT "CONFIG_$key=n\n";
		} elsif ($value eq 'tristate') {
			print OUT "CONFIG_$key=m\n";
		} else { # must be 'bool'
			print OUT "CONFIG_$key=y\n";
		}
	}
	while ( my ($key,$value) = each(%intopt) ) {
		print OUT "CONFIG_$key=$value\n";
	}
	while ( my ($key,$value) = each(%hexopt) ) {
		print OUT "CONFIG_$key=$value\n";
	}
	close OUT;
}
