Protect pcap_fopen calls
[freeradius.git] / scripts / min-includes.pl
1 #!/usr/bin/env perl
2 ######################################################################
3 #
4 #  This script find duplicates of #include files, ignoring #ifdef's, etc.
5 #  from C source files, and (at your command) removes the duplicates.
6 #
7 #  It is meant to be run ONLY by FreeRADUS developers, and has nothing
8 #  whatsoever to do with RADIUS, FreeRADIUS, or confguring a RADIUS server.
9 #
10 ######################################################################
11 #
12 #  Run as: ./min-includes.pl `find . -name "*.c" -print`
13 #               prints out duplicate includes from files.
14 #
15 #          ./min-includes.pl +n `find . -name "*.c" -print`
16 #               removes the duplicate includes from each file.
17 #               Remember to check that it still builds!
18 #
19 #  It has to be run from the TOP of the FreeRADIUS build tree,
20 #  i.e. where the top-level "configure" script is located.
21 #
22 ######################################################################
23 #
24 #  FIXME: We don't handle include files taken from the current
25 #  directory...
26 #
27 #  FIXME: we should take -I <path> from the command line.
28 #
29 ######################################################################
30 #
31 #  Copyright (C) 2006 Alan DeKok <aland@freeradius.org>
32 #
33 #  $Id$
34 #
35 ######################################################################
36
37 my %processed;
38
39 $any_dups = 0;
40 $debug = 0;
41
42 #
43 #  Find the #include's for one file.
44 #
45 sub process($) {
46     my $file = shift;
47
48     return if ($processed{$file});
49
50     $processed{$file}++;
51
52     open FILE, "<$file" or die "Failed to open $file: $!\n";
53
54     $line = 0;
55     while (<FILE>) {
56         $line++;
57
58         next if (!/^\s*\#\s*include\s+/);
59
60         if (/^\s*\#\s*include\s+"(.+?)"/) {
61             $refs{$file}{$1} = $line;
62
63             # FIXME: local header files?
64             # src/foo/bar.c: #include "foo.h"
65             #   src/foo/foo.h do stuff..
66
67             $include{$1}++;
68         } elsif (/^\s*\#\s*include\s+<(.+?)>/) {
69             $refs{$file}{$1} = $line;
70             $include{$1}++;
71         }
72     }
73
74     close FILE;
75 }
76
77 #
78 #  Where include files are located.
79 #
80 #  FIXME:
81 #
82 @directories = ("src/lib", "src");
83 $do_it = 0;
84
85 #
86 #  Horrid.
87 #
88 if ($ARGV[0] eq "+n") {
89     shift;
90     $do_it = 1;
91 }
92
93 #
94 #  Bootstrap the basic C files.
95 #
96 foreach $file (@ARGV) {
97     process($file);
98 }
99
100
101 #
102 #  Process the include files referenced from the C files, to find out
103 #  what they include Note that we create a temporary array, rather
104 #  than walking over %include, because the process() function adds
105 #  entries to the %include hash.
106 #
107 @work = sort keys %include;
108 foreach $inc (@work) {
109
110     foreach $dir (@directories) {
111         $path = $dir . "/" . $inc;
112
113         # normalize path
114         $path =~ s:/.*?/\.\.::;
115         $path =~ s:/.*?/\.\.::;
116
117         next if (! -e $path);
118         process($path);
119         $forward{$inc} = $path;
120         $reverse{$path} = $inc;
121
122         # ignore system include files
123         next if ((scalar keys %{$refs{$path}}) == 0);
124
125         #  Remember that X includes Y, and push Y onto the list
126         #  of files to scan.
127         foreach $inc2 (sort keys %{$refs{$path}}) {
128             $maps{$inc}{$inc2} = 0;
129             push @work, $inc2;
130         }
131     }
132 }
133
134 #
135 #  Process all of the forward refs, so that we have a complete
136 #  list of who's referencing who.
137 #
138 #  This doesn't find the shortest path from A to B, but it does
139 #  find one path.
140 #
141 foreach $inc (sort keys %maps) {
142     foreach $inc2 (sort keys %{$maps{$inc}}) {
143         foreach $inc3 (sort keys %{$maps{$inc2}}) {
144             # map is already there...
145             next if (defined $maps{$inc}{$inc3});
146
147             $maps{$inc}{$inc3} = $maps{$inc2}{$inc3} + 1;
148         }
149     }
150 }
151
152 #
153 #  Walk through the files again, looking for includes that are
154 #  unnecessary.  Note that we process header files, too.
155 #
156 foreach $file (sort keys %refs) {
157
158     # print out some debugging information.
159     if ($debug > 0) {
160         if (defined $reverse{$file}) {
161             print $file, "\t(", $reverse{$file}, ")\n";
162         } else {
163             print $file, "\n";
164         }
165     }
166
167     #  walk of the list of include's in this file
168     foreach $ref (sort keys %{$refs{$file}}) {
169
170         #  walk over the include files we include, or included by
171         #  files that we include.
172         foreach $inc2 (sort keys %{$maps{$ref}}) {
173             #
174             #  If we include X, and X includes Y, and we include
175             #  Y ourselves *after* X, it's a definite dupe.
176             #
177             #  Note that this is a *guaranteed* duplicate.
178             #
179             #  Sometimes order matters, so we can't always delete X if
180             #  we include Y after X, and Y includes X
181             #
182             if (defined $refs{$file}{$inc2} &&
183                 ($refs{$file}{$inc2} > $refs{$file}{$ref})) {
184                 $duplicate{$file}{$inc2} = $ref;
185
186                 # mark the line to be deleted.
187                 $delete_line{$file}{$refs{$file}{$inc2}}++;
188
189                 $any_dups++;
190             }
191         }
192         print "\t", $ref, "\n" if ($debug > 0);
193     }
194 }
195
196 if ($debug > 0) {
197     print "------------------------------------\n";
198 }
199
200 #
201 #  Maybe just print out the dups so that a person can validate them.
202 #
203 if (!$do_it) {
204     foreach $file (sort keys %duplicate) {
205         print $file, "\n";
206
207         foreach $inc (sort keys %{$duplicate{$file}}) {
208             print "\t[", $refs{$file}{$inc}, "] ", $inc, " (", $duplicate{$file}{$inc}, " at ", $refs{$file}{$duplicate{$file}{$inc}}, ")\n";
209         }
210     }
211 } else {
212     foreach $file (sort keys %duplicate) {
213         open FILE, "<$file" or die "Failed to open $file: $!\n";
214         open OUTPUT, ">$file.tmp" or die "Failed to create $file.tmp: $!\n";
215
216         $line = 0;
217         while (<FILE>) {
218             $line++;
219
220             # supposed to delete this line, don't print it to the output.
221             next if (defined $delete_line{$file}{$line});
222
223             print OUTPUT;
224         }
225
226         rename "$file.tmp", $file;
227     }
228
229 }
230
231 #  If we succeeded in re-writing the files, it's OK.
232 exit 0 if ($do_it);
233
234 #  If there are no duplicates, then we're OK.
235 exit 0 if (!$any_dups);
236
237 #  Else there are duplicates, complain.
238 exit 1