#!/usr/bin/perl -w
#
# Analyze whether a symbol is defined  in the include tree of a source
# file.
#
# This script is only supposed to  work in the Linux kernel! If you're
# lucky, you might find it to work somewhere else.
#
# (c) 2003, Thunder Anklin <thunder@keepsake.ch>
#

my @includefiles = ();
my @parsedfiles = ();
my @file_lifo = ();
my @includepath = ();
my $arg;
my $gcc_searchpath;
my $debug = 0;

sub for_each_include {
	# Please  enter code  here which  shall be  executed  for each
	# includefile.
	#
	# $_[0] is the filename.
}

sub for_each_processed {
	# Please enter code here which  shall be executed after a file
	# was processed.
	#
	# $_[0] is the filename.
}

sub printd {
	if ($debug) {
		printf @_;
	}
}

sub gcc_get_searchpath {
	local ($fd, $buf, $retval);

	$retval = "";
	open($fd, "gcc -print-search-dirs |") or goto out;

	while (defined($buf = <$fd>)) {
		if ($buf =~ /^install:\s+(.*)\r?\n/) {
			if (-e $1 . "/include") {
				$retval = $1 . "/include";
			}
		}
	}

	close($fd);

      out:
	return $retval;
}

sub includepath_add {
	local ($dirname) = @_;

	if ($dirname eq ".") {
		$dirname = $ENV{"PWD"};
	}

	if (!($dirname =~ /^\//)) {
		$dirname = $ENV{"PWD"} . "/" . $dirname;
	}
	push(@includepath, $dirname) unless (! -d $dirname);

	return;
}

sub elem_in_stack {
	local ($elem, @stack) = @_;
	local ($i) = (0);

	for ($i = 0; $i < scalar(@stack); $i++) {
		if ($stack[$i] eq $elem) {
			return 1;
		}
	}

	return 0;
}

sub process_file {
	local ($filename) = @_;
	local ($buf, $i) = ("", 0);
	local ($fd);

	printd("Hit " . $filename . "\n");
	push(@parsedfiles, $filename);

	for ($i = 0; $i < scalar(@includepath); $i++) {
		if (-e $includepath[$i] . "/" . $filename) {
			if (open($fd, "<" . $includepath[$i] . "/" .
				 $filename)) {
				goto found_file;
			}
			printd("Whoops: %s: %s\n", $includepath[$i] . "/" .
			       $filename, $!);
		}
	}

	if ($! == 2) {
		printd("WARNING: %s: no file found!\n", $filename);
		return 0;
	}
	return $!;

      found_file:
	while (defined($buf = <$fd>)) {
		if ($buf =~ /^\s*\#\s*include\s*\<([^\>]+)\>/) {
			local ($includefile) = ($1);

			if (!elem_in_stack($includefile, @includefiles)) {
				for_each_include($includefile);

				push(@includefiles, $includefile);
				push(@file_lifo, $includefile);
			}
		}
	}

	close($fd) or return($!);

	for_each_processed($filename);

	return 0;
}

if (!scalar(@ARGV)) {
	print(STDERR "Usage:\n" .
	      __FILE__ . " [ -Iincludepath ... ] file [ file ... ]\n");
	exit(1);
}

includepath_add(".");
includepath_add("include");

$gcc_searchpath = gcc_get_searchpath();

if (length($gcc_searchpath) && -e $gcc_searchpath) {
	includepath_add($gcc_searchpath);
}

while (($ARGV[0] . "\n") =~ /^-I(.*)$/) {
	includepath_add($1);
	shift();
}

printd("Include path:");
foreach $arg (@includepath) {
	print(" " . $arg);
}
print("\n");

if (!scalar(@ARGV)) {
	print(STDERR "Usage:\n" .
	      __FILE__ . " [ -Iincludepath ... ] file [ file ... ]\n");
	exit(1);
}

foreach $arg (@ARGV) {
	local ($retval) = (0);

	@includefiles = (); @parsedfiles = (); @file_lifo = ($arg);

	while (scalar(@file_lifo)) {
		$retval = process_file(shift(@file_lifo));

		if ($retval) {
			printf("%s: Error: %i\n", $parsedfiles[-1],
			       $retval);
		}
	}

	printd("%s: Processed %i includefiles.\n", $arg,
	       scalar(@parsedfiles));
}

exit(0);

# Local variables:
#  perl-indent-level: 8
# End:

