summaryrefslogtreecommitdiffstats
path: root/scripts/min-includes.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/min-includes.pl')
-rwxr-xr-xscripts/min-includes.pl238
1 files changed, 238 insertions, 0 deletions
diff --git a/scripts/min-includes.pl b/scripts/min-includes.pl
new file mode 100755
index 0000000..37044ed
--- /dev/null
+++ b/scripts/min-includes.pl
@@ -0,0 +1,238 @@
+#!/usr/bin/env perl
+######################################################################
+#
+# This script find duplicates of #include files, ignoring #ifdef's, etc.
+# from C source files, and (at your command) removes the duplicates.
+#
+# It is meant to be run ONLY by FreeRADUS developers, and has nothing
+# whatsoever to do with RADIUS, FreeRADIUS, or configuring a RADIUS server.
+#
+######################################################################
+#
+# Run as: ./min-includes.pl `find . -name "*.c" -print`
+# prints out duplicate includes from files.
+#
+# ./min-includes.pl +n `find . -name "*.c" -print`
+# removes the duplicate includes from each file.
+# Remember to check that it still builds!
+#
+# It has to be run from the TOP of the FreeRADIUS build tree,
+# i.e. where the top-level "configure" script is located.
+#
+######################################################################
+#
+# FIXME: We don't handle include files taken from the current
+# directory...
+#
+# FIXME: we should take -I <path> from the command line.
+#
+######################################################################
+#
+# Copyright (C) 2006 Alan DeKok <aland@freeradius.org>
+#
+# $Id$
+#
+######################################################################
+
+my %processed;
+
+$any_dups = 0;
+$debug = 0;
+
+#
+# Find the #include's for one file.
+#
+sub process($) {
+ my $file = shift;
+
+ return if ($processed{$file});
+
+ $processed{$file}++;
+
+ open FILE, "<$file" or die "Failed to open $file: $!\n";
+
+ $line = 0;
+ while (<FILE>) {
+ $line++;
+
+ next if (!/^\s*\#\s*include\s+/);
+
+ if (/^\s*\#\s*include\s+"(.+?)"/) {
+ $refs{$file}{$1} = $line;
+
+ # FIXME: local header files?
+ # src/foo/bar.c: #include "foo.h"
+ # src/foo/foo.h do stuff..
+
+ $include{$1}++;
+ } elsif (/^\s*\#\s*include\s+<(.+?)>/) {
+ $refs{$file}{$1} = $line;
+ $include{$1}++;
+ }
+ }
+
+ close FILE;
+}
+
+#
+# Where include files are located.
+#
+# FIXME:
+#
+@directories = ("src/lib", "src");
+$do_it = 0;
+
+#
+# Horrid.
+#
+if ($ARGV[0] eq "+n") {
+ shift;
+ $do_it = 1;
+}
+
+#
+# Bootstrap the basic C files.
+#
+foreach $file (@ARGV) {
+ process($file);
+}
+
+
+#
+# Process the include files referenced from the C files, to find out
+# what they include Note that we create a temporary array, rather
+# than walking over %include, because the process() function adds
+# entries to the %include hash.
+#
+@work = sort keys %include;
+foreach $inc (@work) {
+
+ foreach $dir (@directories) {
+ $path = $dir . "/" . $inc;
+
+ # normalize path
+ $path =~ s:/.*?/\.\.::;
+ $path =~ s:/.*?/\.\.::;
+
+ next if (! -e $path);
+ process($path);
+ $forward{$inc} = $path;
+ $reverse{$path} = $inc;
+
+ # ignore system include files
+ next if ((scalar keys %{$refs{$path}}) == 0);
+
+ # Remember that X includes Y, and push Y onto the list
+ # of files to scan.
+ foreach $inc2 (sort keys %{$refs{$path}}) {
+ $maps{$inc}{$inc2} = 0;
+ push @work, $inc2;
+ }
+ }
+}
+
+#
+# Process all of the forward refs, so that we have a complete
+# list of who's referencing who.
+#
+# This doesn't find the shortest path from A to B, but it does
+# find one path.
+#
+foreach $inc (sort keys %maps) {
+ foreach $inc2 (sort keys %{$maps{$inc}}) {
+ foreach $inc3 (sort keys %{$maps{$inc2}}) {
+ # map is already there...
+ next if (defined $maps{$inc}{$inc3});
+
+ $maps{$inc}{$inc3} = $maps{$inc2}{$inc3} + 1;
+ }
+ }
+}
+
+#
+# Walk through the files again, looking for includes that are
+# unnecessary. Note that we process header files, too.
+#
+foreach $file (sort keys %refs) {
+
+ # print out some debugging information.
+ if ($debug > 0) {
+ if (defined $reverse{$file}) {
+ print $file, "\t(", $reverse{$file}, ")\n";
+ } else {
+ print $file, "\n";
+ }
+ }
+
+ # walk of the list of include's in this file
+ foreach $ref (sort keys %{$refs{$file}}) {
+
+ # walk over the include files we include, or included by
+ # files that we include.
+ foreach $inc2 (sort keys %{$maps{$ref}}) {
+ #
+ # If we include X, and X includes Y, and we include
+ # Y ourselves *after* X, it's a definite dupe.
+ #
+ # Note that this is a *guaranteed* duplicate.
+ #
+ # Sometimes order matters, so we can't always delete X if
+ # we include Y after X, and Y includes X
+ #
+ if (defined $refs{$file}{$inc2} &&
+ ($refs{$file}{$inc2} > $refs{$file}{$ref})) {
+ $duplicate{$file}{$inc2} = $ref;
+
+ # mark the line to be deleted.
+ $delete_line{$file}{$refs{$file}{$inc2}}++;
+
+ $any_dups++;
+ }
+ }
+ print "\t", $ref, "\n" if ($debug > 0);
+ }
+}
+
+if ($debug > 0) {
+ print "------------------------------------\n";
+}
+
+#
+# Maybe just print out the dups so that a person can validate them.
+#
+if (!$do_it) {
+ foreach $file (sort keys %duplicate) {
+ print $file, "\n";
+
+ foreach $inc (sort keys %{$duplicate{$file}}) {
+ print "\t[", $refs{$file}{$inc}, "] ", $inc, " (", $duplicate{$file}{$inc}, " at ", $refs{$file}{$duplicate{$file}{$inc}}, ")\n";
+ }
+ }
+} else {
+ foreach $file (sort keys %duplicate) {
+ open FILE, "<$file" or die "Failed to open $file: $!\n";
+ open OUTPUT, ">$file.tmp" or die "Failed to create $file.tmp: $!\n";
+
+ $line = 0;
+ while (<FILE>) {
+ $line++;
+
+ # supposed to delete this line, don't print it to the output.
+ next if (defined $delete_line{$file}{$line});
+
+ print OUTPUT;
+ }
+
+ rename "$file.tmp", $file;
+ }
+
+}
+
+# If we succeeded in re-writing the files, it's OK.
+exit 0 if ($do_it);
+
+# If there are no duplicates, then we're OK.
+exit 0 if (!$any_dups);
+
+# Else there are duplicates, complain.
+exit 1