#! /usr/bin/perl -w
#
# Kernel patch applicator
#
# (C) 2001 Nick Holgate <holgate@debian.org>
#

$patchver    = "2.2.19";
$arch        = "m68k";

$defpatchdir = "/usr/src/kernel-patches/$arch/$patchver/patches";
$patch_opts  = "-l -p1";

$patchset    = "";
$patchdir    = "";
$maxnamelen  = 0;

##--------------------------------------------------------------------------##

# parse the command line for options
parse_args(@ARGV);

# use default patch directory if not specified
$patchdir = $defpatchdir if ($patchdir eq "");

# use environment if not specified on command line
$patchset = $ENV{KERNEL_PATCH_SET} if ($patchset eq "");

# use generic patch set if not specified on command line or in environment
$patchset = "generic" if (!defined $patchset or $patchset eq "");

# extract the kernel version from the kernel makefile
$kernelver = get_kernel_version();

if ( $kernelver ne $patchver ) {
	print STDERR "Wrong kernel source version for this patch set.\n";
	exit 1;
}

# read patchlists
$fn = "$patchdir/patch-list.$patchset";
if ( ! -f $fn ) {
	print STDERR "Patch set \"$patchset\" does not exist\n\n";
	exit 1;
}
read_patchlist($fn);

# make sure debian-patches directory exists
mkdir "debian-patches", 0755;

# Check if already patched with a different patch set
$fn = "debian-patches/APPLIED_$arch";
if ( -f $fn ) {
	open(IN, $fn) || die "Can't open $fn: $!";
	$s = <IN>;
	close IN;
	chomp $s;
	if ($s ne $patchset) {
		print STDERR "Patches already applied from a different patch set \"$s\"\n\n";
		exit 1;
	}
} else {
	open(OUT, ">$fn") || die "Can't create $fn: $!";
	print OUT "$patchset\n";
	close OUT;
}

# unbuffered output
$| = 1;

# apply patches
foreach (@patchlist) {
	($patchname) = split;

	print "Applying patch $patchname ..." . "." x ($maxnamelen - length $patchname) . " ";

	$sentinal = "debian-patches/APPLIED_${arch}_$patchname";
	if ( -f $sentinal ) {
		print "already applied.\n";
		next;
	}

	$patchpath = "$patchdir/$patchname.diff.gz";
	if ( ! -f $patchpath ) {
		print "MISSING.\n";
		next;
	}

	if (system("zcat $patchpath | patch $patch_opts >$sentinal")) {
		print "FAILED.\n";
	}
	else {
		print "applied.\n";
	}
}

print "\n";
exit 0;

##--------------------------------------------------------------------------##

sub read_patchlist {
	my ($filename) = shift;
	my ($namelen, $patchname);

	open (PL, $filename) || die "Can't open $filename: $!";
	while (<PL>) {
		chomp;
		s/\#.*//g; 			# strip comments
		next if /^\s*$/;	# skip blank lines
		push @patchlist, $_;
	
		($patchname) = split;
		$namelen     = length $patchname;
		$maxnamelen  = $namelen if ($namelen > $maxnamelen);
	}
	close PL;
}

##--------------------------------------------------------------------------##

sub parse_args {
	local(@args) = @_;
	local($i, $option);

	$i = 0;
	while ($i <= $#args) {
		$option = $args[$i];
		if ($option eq "-s" || $option eq "--patch-set") {
			if ($patchset ne "") {
				print STDERR "Only one $option option allowed.\n";
				exit 1;
			}
			if (++$i > $#args) {
				print STDERR "Missing patch set name for $option option.\n";
				exit 1;
			}
			$patchset = $args[$i];
		}
		elsif ($option eq "-d" || $option eq "--directory") {
			if ($patchdir ne "") {
				print STDERR "Only one $option option allowed.\n";
				exit 1;
			}
			if (++$i > $#args) {
				print STDERR "Missing directory name for $option option.\n";
				exit 1;
			}
			$patchdir = $args[$i];
		}
		elsif ($option eq "-?" || $option eq "-h" || $option eq "--help") {
			print STDERR "Syntax: apply [options]\n";
			print STDERR "Function: apply $arch kernel patches to kernel sources in current directory\n";
			print STDERR "Options:\n";
			print STDERR " -d or --directory <dir>  Specifies the directory containing the\n";
			print STDERR "                          kernel patches and patch-list files.\n";
			print STDERR "                          default = $defpatchdir\n";
			print STDERR " -s or --patch-set <name> Specifies the name of the patch set to apply\n";
			print STDERR "                          default = generic.\n";
			print STDERR "\n";
			exit 1;			
		}
		elsif ($option =~ /^-,*/) {
			print STDERR "Unrecognised option $option\n";
			exit 1;
		}
		else {
			print STDERR "Unexpected argument \"$option\"\n";
			exit 1;
		}

		$i++;
	}
}

##--------------------------------------------------------------------------##

sub get_kernel_version {
	my ($ver, $pat, $sub, $ext) = ("", "", "", "");

	if ( ! -d "kernel" || ! -d "Documentation" ) {
		print STDERR "Not in kernel top level directory. Exiting\n";
		exit 1;
	}

	# Get kernel version from Makefile
	open (MF, "Makefile") || die "Kernel Makefile missing: $!";
	while (<MF>) {
		if    ( /^VERSION\W+(\w+)/      ) { $ver = $1; }
		elsif ( /^PATCHLEVEL\W+(\w+)/   ) { $pat = $1; }
		elsif ( /^SUBLEVEL\W+(\w+)/     ) { $sub = $1; }
		elsif ( /^EXTRAVERSION\W+(\w+)/ ) { $ext = $1; }
	}
	close MF;

	return "$ver.$pat.$sub$ext";
}

##-----------------------------< end of file >------------------------------##
