Fix uninstallman.
[ira/wip.git] / source4 / script / update-proto.pl
1 #!/usr/bin/perl
2 # Simple script for updating the prototypes in a C header file
3 #
4 # Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
5 # Published under the GNU GPL
6
7 use strict;
8 use warnings;
9 use Getopt::Long;
10
11 =head1 NAME
12
13 update-proto - automatically update prototypes in header files
14
15 =head1 SYNOPSIS
16
17 update-proto [OPTIONS] <HEADER> <C-FILE>...
18
19 update-proto [OPTIONS] <HEADER> 
20
21 =head1 DESCRIPTION
22
23 Update-proto makes sure the prototypes in a C header file are current
24 by comparing the existing prototypes in a header with the function definition 
25 in the source file. It aims to keep the diff between the original header 
26 and generated one as small as possible.
27
28 New prototypes are inserted before any line that contains the following comment:
29
30 /* New prototypes are inserted above this line */
31
32 It will automatically parse C files after it encounters a line that contains:
33
34 /* The following definitions come from FILE */
35
36 When two or more prototypes exist for a function, only the first one 
37 will be kept.
38
39 =head1 OPTIONS
40
41 =over 4
42
43 =item I<--verbose|-v>
44
45 Increase verbosity. Currently only two levels of verbosity are used.
46
47 =item I<--help>
48
49 Show list of options
50
51 =back
52
53 =head1 BUGS
54
55 Strange complex functions are not recognized. In particular those 
56 created by macros or returning (without typedef) function pointers.
57
58 =head1 LICENSE
59
60 update-proto is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>.
61
62 =head1 AUTHOR
63
64 update-proto was written by Jelmer Vernooij L<jelmer@samba.org>.
65         
66 =cut
67
68 sub Usage()
69 {
70         print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n";
71         exit 1;
72 }
73
74 sub Help()
75 {
76         print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n";
77         print "Options:\n";
78         print " --help                  Show this help message\n";
79         print " --verbose               Write changes made to standard error\n\n";
80         exit 0;
81 }
82
83 my %new_protos = ();
84
85 my $verbose = 0;
86
87 GetOptions(
88         'help|h' => \&Help,
89         'v|verbose' => sub { $verbose += 1; }
90 ) or Usage();
91
92 sub count($$)
93 {
94         my ($t, $s) = @_;
95         my $count = 0;
96         while($s =~ s/^(.)//) { $count++ if $1 eq $t; }
97         return $count;
98 }
99
100 my $header = shift @ARGV;
101
102 sub process_file($)
103 {
104         my $file = shift;
105         open (IN, "<$file");
106         while (my $line = <IN>) {
107                 $_ = $line;
108                 next if /^\s/;
109                 next unless /\(/;
110                 next if /^\/|[;]|^#|}|^\s*static/;
111                 s/\/\*(.*?)\*\///g;
112                 my $public = s/_PUBLIC_//g;
113                 s/_PRINTF_ATTRIBUTE\([^)]+\)//g;
114                 next unless /^(struct\s+\w+|union\s+\w+|\w+)\s+\**\s*(\w+)\s*\((.*)$/;
115
116                 my $name = $2;
117
118                 next if ($name eq "main");
119
120                 # Read continuation lines if any
121                 my $prn = 1 + count("(", $3) - count(")", $3);
122
123                 while ($prn) {
124                         my $l = <IN>;
125                         $l or die("EOF while parsing function prototype");
126                         $line .= $l;
127                         $prn += count("(", $l) - count(")", $l);
128                 }
129
130                 $line =~ s/\n$//;
131
132                 # Strip off possible start of function
133                 $line =~ s/{\s*$//g;
134                 
135                 $new_protos{$name} = "$line;";
136         }
137         close(IN);
138 }
139
140 process_file($_) foreach (@ARGV);
141
142 my $added = 0;
143 my $modified = 0;
144 my $deleted = 0;
145 my $kept = 0;
146
147 sub insert_new_protos()
148 {
149         foreach (keys %new_protos) {
150                 print "$new_protos{$_}\n";
151                 print STDERR "Inserted prototype for `$_'\n" if ($verbose);
152                 $added+=1;
153         }
154         %new_protos = ();
155 }
156
157 my $blankline_due = 0;
158
159 open (HDR, "<$header");
160 while (my $line = <HDR>) {
161         if ($line eq "\n") {
162                 $blankline_due = 1;
163                 $line = <HDR>;
164         }
165
166         # Recognize C files that prototypes came from
167         if ($line =~ /\/\* The following definitions come from (.*) \*\//) {
168                 insert_new_protos();
169                 if ($blankline_due) {
170                         print "\n";
171                         $blankline_due = 0;
172                 }
173                 process_file($1);
174                 print "$line";
175                 next;
176         }
177
178         if ($blankline_due) {
179                 print "\n";
180                 $blankline_due = 0;
181         }
182
183         # Insert prototypes that weren't in the header before
184         if ($line =~ /\/\* New prototypes are inserted above this line.*\*\/\s*/) {
185                 insert_new_protos();
186                 print "$line\n";
187                 next;
188         }
189         
190         if ($line =~ /^\s*typedef |^\#|^\s*static/) {
191                 print "$line";
192                 next;
193         }
194
195         $_ = $line;
196         s/\/\*(.*?)\*\///g;
197         my $public = s/_PUBLIC_//g;
198         s/_PRINTF_ATTRIBUTE\([^)]+\)//g;
199         unless (/^(struct\s+\w+|union\s+\w+|\w+)\s+\**\s*(\w+)\s*\((.*)$/) {
200                 print "$line";
201                 next;
202         }
203
204         # Read continuation lines if any
205         my $prn = 1 + count("(", $3) - count(")", $3);
206
207         while ($prn) {
208                 my $l = <HDR>;
209                 $l or die("EOF while parsing function prototype");
210                 $line .= $l;
211                 $prn += count("(", $l) - count(")", $l);
212         }
213
214         my $name = $2;
215
216         # This prototype is for a function that was removed
217         unless (defined($new_protos{$name})) {
218                 $deleted+=1;
219                 print STDERR "Removed prototype for `$name'\n" if ($verbose);
220                 next;
221         }
222
223         my $nline = $line;
224         chop($nline);
225
226         if ($new_protos{$name} ne $nline) {
227                 $modified+=1;
228                 print STDERR "Updated prototype for `$name'\n" if ($verbose);
229                 print "$new_protos{$name}\n";
230         } else {
231                 $kept+=1;
232                 print STDERR "Prototype for `$name' didn't change\n" if ($verbose > 1);
233                 print "$line";
234         }
235
236         delete $new_protos{$name};
237 }
238 close(HDR);
239
240 print STDERR "$added added, $modified modified, $deleted deleted, $kept unchanged.\n";
241
242 1;