#!/usr/bin/perl

$RULES = "Rules.make";
$BATCH = 0;
$current = `uname -r`;
chomp $current;

$DFL{KERNELDIR} = '/usr/src/linux-2.4';
$moduledir = "/lib/modules/$current/";
if (-l "${moduledir}build" && -d "${moduledir}build/") {
	$DFL{KERNELDIR} = readlink("${moduledir}build");
	unless ($DFL{KERNELDIR} =~ m#^/#) {
		$DFL{KERNELDIR} = $moduledir . $DFL{KERNELDIR};
	}
} elsif (! -d $DFL{KERNELDIR}) {
	$DFL{KERNELDIR} = '/usr/src/linux';
	if (! -d $DFL{KERNELDIR}) {
		$DFL{KERNELDIR} = '/usr/src/redhat/linux';
		if (! -d $DFL{KERNELDIR}) {
			$DFL{KERNELDIR} = '';
			for (glob "/usr/src/*") {
				if (-d "$_/include") {
					$DFL{KERNELDIR} = $_;
					last;
				}
			}
		}
	}
}

$DFL{prefix} = '/usr/local';
$DFL{CC} = 'cc';
$DFL{CDEBUG} = '-O2';
$DFL{LIBS} = '-lreadline -lncurses';
$DFL{XFS} = 'yes';
$DFL{AR} = '/usr/bin/ar';
$DFL{NM} = '/usr/bin/nm';

if (open(FH, $RULES)) {
	while (<FH>) {
		next unless ($name, $value) = /^(\w+)\s*\??=\s*(.*)/;
		$DFL{$name} = $value;
	}
	close FH;
}

while ($_ = shift) {
	if (/^--([\w-]+)=(.*)/) {
		$DFL{$1} = $2;
		if ($1 eq 'target' && $2 =~ /(\w+)-\w+-\w+/) {
			$DFL{ARCH} = $1;
			$BATCH = 1;
		}
	} elsif (/^--BATCH$/) {
		$BATCH = 1;
	} elsif (/^(\w+)-\w*-linux$/) {
		$DFL{ARCH} = $1;
		$BATCH = 1;
	} else {
		die "ERROR: unknown argument \"$_\"\n";
	}
}

if ($DFL{ARCH}) {
	$ARCH = $DFL{ARCH};
} else {
	$ARCH = `uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/`;
	chomp $ARCH;
}
print "mcrash for $ARCH Linux configuration\n\n";

while (1) {
	$KERNELDIR = &input("kernel make directory", $DFL{KERNELDIR});
	next if $KERNELDIR eq '';

	if (! -e "$KERNELDIR") {
		print "ERROR: $KERNELDIR not exists\n";
		next
	}
	if (! -d "$KERNELDIR") {
		print "ERROR: $KERNELDIR is not directory\n";
		next;
	}
	if (! -f "$KERNELDIR/Makefile") {
		print "ERROR: $KERNELDIR/Makefile is not exists\n";
		next;
	}
	if (! -d "$KERNELDIR/include/linux") {
		print "ERROR: $KERNELDIR/include/linux is not exists\n";
		next;
	}
	if (! -d "$KERNELDIR/include/asm") {
		print "ERROR: $KERNELDIR/include/asm is not exists\n";
		print "ERROR: (maybe you must make kernel on $KERNELDIR)\n";
		next;
	}
	open(FH, "$KERNELDIR/Makefile") || next;
	while (<FH>) {
		next unless ($name, $value) = /^(\w+)\s*=\s*(.+)/;
		$MKF{$name} = $value;
	}
	close FH;
	$VERSION = $MKF{VERSION};
	$PATCHLEVEL = $MKF{PATCHLEVEL};
	$SUBLEVEL = $MKF{SUBLEVEL};
	$EXTRAVERSION = $MKF{EXTRAVERSION};
	printf "Found linux $VERSION.$PATCHLEVEL.$SUBLEVEL$EXTRAVERSION\n";
	last;
}

$autoconf="autoconf.h";
while (defined $autoconf) {
	last unless (open(FH, "$KERNELDIR/include/linux/$autoconf"));
	undef $autoconf;
	while (<FH>) {
		if (($name) = /^#define\s+CONFIG_(\w+)/) {
			$AUTOCONF_DEFINE{$name} = 1;
		} elsif ((($file) = /^#include <linux\/(autoconf.+h)>/) &&
				!defined($autoconf)) {
			$autoconf = $file;
		}
	}
	close FH;
}

while (1) {
	$prefix = &input("install prefix", $DFL{prefix});
	next if $prefix eq '';

	if (! -e $prefix) {
		print "ERROR: $prefix not exists\n";
		next;
	}
	if (! -d $prefix) {
		print "ERROR: $prefix is not directory\n";
		next;
	}
	last;
}

while (1) {
	$CC = &input("compiler", $DFL{CC});
	next if $CC eq '';
	if ($CC =~ m#^/# && ! -x $CC) {
		print "ERROR: $CC not found\n";
		next;
	}
	last;
}

$CDEBUG = &input("compiler option", $DFL{CDEBUG});
$LIBS = &input("libraries", $DFL{LIBS});

$AR = $DFL{AR};
if ($AR eq '' || ($AR =~ m#^/# && ! -x $AR)) {
	while (1) {
		$AR = &input("archiver", $DFL{AR});
		next if $AR eq '';
		if ($AR =~ m#^/# && ! -x $AR) {
			print "ERROR: $AR not found\n";
			next;
		}
		last;
	}
}
$NM = $DFL{NM};

while (1) {
	if (! -d "$KERNELDIR/fs/xfs" || (!defined $AUTOCONF_DEFINE{XFS_FS} && !defined $AUTOCONF_DEFINE{XFS_FS_MODULE})) {
		$XFS = 'no';
		last;
	}
	$XFS = &input("using SGI XFS analysis", $DFL{XFS});
	if ($XFS ne 'yes' && $XFS ne 'no') {
		print "ERROR: enter `yes' or `no'\n";
		next;
	}
	if ($XFS eq 'yes') {
		if (-d "$KERNELDIR/fs/xfs/pagebuf") {
			$XFS_OLDDIR = "/*undef XFS_OLDDIR*/\n";
		} elsif (-f "$KERNELDIR/include/linux/page_buf.h") {
			$XFS_OLDDIR = "#define XFS_OLDDIR\n";
			$XFS .= "\nPBDIR\t= old";
		} else {
			$XFS_OLDDIR = "#define XFS_NOPAGEBUF_H\n";
			$XFS .= "\nPBDIR\t= no";
		}
	}
	last;
}

system("$CC -o test1 test1.c > /dev/null 2>&1");
if ($? || !-x "test1") {
	die "\n== ERROR: compile error by $CC\n== ABORT\n";
}
$PTRSIZE = `./test1`;
unlink("test1");
chomp $PTRSIZE;

system("$CC -c test2.c > /dev/null 2>&1");
if ($?) {
	$TEST2 = "#define CONF_OLD_READLINE";
} else {
	$TEST2 = "/*undef CONF_OLD_READLINE*/";
}
unlink("test2.o");

system("$CC -o test3 test3.c > /dev/null 2>&1");
if ($? || !-x "test3") {
	die "\n== ERROR: compile error by $CC\n== ABORT\n";
}
$ADDR0 = `$NM test3 | grep ' sub\$' | cut -d' ' -f1`;
$ADDR1 = `./test3`;
if ($ADDR0 ne $ADDR1) {
	$ADDR2 = `./test3 indirect`;
	if ($ADDR0 eq $ADDR2) {
		$TEST3 = "#define CONF_INDIRECT_CALL\t1\n";
	}
}
unlink("test3");

open(FH, ">config.h") || die "$!";
print FH <<EOF1;
/* This file was created automatically by configure program. */

#define	VERSION		$VERSION
#define PATCHLEVEL	$PATCHLEVEL
#define SUBLEVEL	$SUBLEVEL
#define EXTRAVERSION	"$EXTRAVERSION"
#define LINUXVER	$VERSION$PATCHLEVEL
#define ARCH_$ARCH
#define ARCHNAME	"$ARCH"
#define PTRSIZE		$PTRSIZE

$TEST2
$TEST3$XFS_OLDDIR
#define NMPATH	"$NM"
EOF1

open(FH, ">$RULES") || die "$!";
print FH <<EOF2;
# This file was created automatically by configure program.

VERSION = $VERSION
PATCHLEVEL = $PATCHLEVEL
SUBLEVEL = $SUBLEVEL
EXTRAVERSION = $EXTRAVERSION
ARCH	= $ARCH

KERNELDIR = $KERNELDIR
prefix	= $prefix
CC	= $CC
CDEBUG	= $CDEBUG
LIBS	= $LIBS
XFS	= $XFS

INCDIR	= \$(KERNELDIR)/include
OBJS	= \$(CSRCS:.c=.o)
LINUXVER = \$(VERSION)\$(PATCHLEVEL)
AR	= $AR
NM	= $NM
GETHEAD	= \$(TOP)/script/gethead
BITNAME	= \$(TOP)/script/bitname
EOF2

print "updated `config.h' and `$RULES'\n";
exit 0;

sub input
{
	local ($message, $default) = @_;

	if ($BATCH) {
		if ($lastmessage eq $message) {
			die "ERROR: cannot determine $message\n";
		}
		print "  $message: $default\n";
		$lastmessage = $message;
		return $default;
	}
		
	print "Enter $message [$default]: ";
	$_ = <STDIN>;
	chop;
	if ($_ eq '') {
		return $default;
	}
	$_;
}
