1#!/usr/bin/perl -w
2# find a list of #include lines in C code that might not be needed
3# usually called with something like this:
4#    minimal_includes.pl `find . -name "*.c"`
5# Andrew Tridgell <tridge@samba.org>
6
7use strict;
8use Data::Dumper;
9use Getopt::Long;
10
11my $opt_help = 0;
12my $opt_remove = 0;
13my $opt_skip_system = 0;
14
15#####################################################################
16# write a string into a file
17sub FileSave($$)
18{
19    my($filename) = shift;
20    my($v) = shift;
21    local(*FILE);
22    open(FILE, ">$filename") || die "can't open $filename";
23    print FILE $v;
24    close(FILE);
25}
26
27sub load_lines($)
28{
29	my $fname = shift;
30	my @lines = split(/^/m, `cat $fname`);
31	return @lines;
32}
33
34sub save_lines($$)
35{
36	my $fname = shift;
37	my $lines = shift;
38	my $data = join('', @{$lines});
39	FileSave($fname, $data);
40}
41
42sub test_compile($)
43{
44	my $fname = shift;
45	my $obj;
46	if ($fname =~ s/(.*)\.c$/$1.o/) {
47		$obj = "$1.o";
48	} else {
49		return "NOT A C FILE";
50	}
51	unlink($obj);
52	my $ret = `make $obj 2>&1`;
53	if (!unlink("$obj")) {
54		return "COMPILE FAILED";
55	}
56	return $ret;
57}
58
59sub test_include($$$$)
60{
61	my $fname = shift;
62	my $lines = shift;
63	my $i = shift;
64	my $original = shift;
65	my $line = $lines->[$i];
66	my $testfname;
67
68	$lines->[$i] = "";
69
70	`/bin/mv -f $fname $fname.misaved` && die "failed to rename $fname";
71	save_lines($fname, $lines);
72
73	my $out = test_compile($fname);
74
75	if ($out eq $original) {
76		if ($opt_remove) {
77			if ($opt_skip_system &&
78			    $line =~ /system\//) {
79				print "$fname: not removing system include $line\n";
80			} else {
81				print "$fname: removing $line\n";
82				return;
83			}
84		} else {
85			print "$fname: might be able to remove $line\n";
86		}
87	}
88
89	$lines->[$i] = $line;
90	`/bin/mv -f $fname.misaved $fname` && die "failed to restore $fname";
91}
92
93sub process_file($)
94{
95	my $fname = shift;
96	my @lines = load_lines($fname);
97	my $num_lines = $#lines;
98
99	my $original = test_compile($fname);
100
101	if ($original eq "COMPILE FAILED") {
102		print "Failed to compile $fname\n";
103		return;
104	}
105
106	print "Processing $fname (with $num_lines lines)\n";
107
108	my $if_level = 0;
109
110	for (my $i=0;$i<=$num_lines;$i++) {
111		my $line = $lines[$i];
112		if ($line =~ /^\#\s*if/) {
113			$if_level++;
114		}
115		if ($line =~ /^\#\s*endif/) {
116			$if_level--;
117		}
118		if ($if_level == 0 &&
119		    $line =~ /^\#\s*include/ &&
120		    !($line =~ /needed/)) {
121			test_include($fname, \@lines, $i, $original);
122		}
123	}
124}
125
126
127#########################################
128# display help text
129sub ShowHelp()
130{
131    print "
132           minimise includes
133           Copyright (C) tridge\@samba.org
134
135	   Usage: minimal_includes.pl [options] <C files....>
136
137	   Options:
138                 --help         show help
139                 --remove       remove includes, don't just list them
140                 --skip-system  don't remove system/ includes
141";
142}
143
144
145# main program
146GetOptions (
147	    'h|help|?' => \$opt_help,
148	    'remove' => \$opt_remove,
149	    'skip-system' => \$opt_skip_system,
150	    );
151
152if ($opt_help) {
153	ShowHelp();
154	exit(0);
155}
156
157for (my $i=0;$i<=$#ARGV;$i++) {
158	my $fname = $ARGV[$i];
159	process_file($fname);
160}
161