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 7use strict; 8use warnings; 9use Getopt::Long; 10 11=head1 NAME 12 13update-proto - automatically update prototypes in header files 14 15=head1 SYNOPSIS 16 17update-proto [OPTIONS] <HEADER> <C-FILE>... 18 19update-proto [OPTIONS] <HEADER> 20 21=head1 DESCRIPTION 22 23Update-proto makes sure the prototypes in a C header file are current 24by comparing the existing prototypes in a header with the function definition 25in the source file. It aims to keep the diff between the original header 26and generated one as small as possible. 27 28New prototypes are inserted before any line that contains the following comment: 29 30/* New prototypes are inserted above this line */ 31 32It will automatically parse C files after it encounters a line that contains: 33 34/* The following definitions come from FILE */ 35 36When two or more prototypes exist for a function, only the first one 37will be kept. 38 39=head1 OPTIONS 40 41=over 4 42 43=item I<--verbose|-v> 44 45Increase verbosity. Currently only two levels of verbosity are used. 46 47=item I<--help> 48 49Show list of options 50 51=back 52 53=head1 BUGS 54 55Strange complex functions are not recognized. In particular those 56created by macros or returning (without typedef) function pointers. 57 58=head1 LICENSE 59 60update-proto is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>. 61 62=head1 AUTHOR 63 64update-proto was written by Jelmer Vernooij L<jelmer@samba.org>. 65 66=cut 67 68sub Usage() 69{ 70 print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n"; 71 exit 1; 72} 73 74sub 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 83my %new_protos = (); 84 85my $verbose = 0; 86 87GetOptions( 88 'help|h' => \&Help, 89 'v|verbose' => sub { $verbose += 1; } 90) or Usage(); 91 92sub count($$) 93{ 94 my ($t, $s) = @_; 95 my $count = 0; 96 while($s =~ s/^(.)//) { $count++ if $1 eq $t; } 97 return $count; 98} 99 100my $header = shift @ARGV; 101 102sub 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 140process_file($_) foreach (@ARGV); 141 142my $added = 0; 143my $modified = 0; 144my $deleted = 0; 145my $kept = 0; 146 147sub 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 157my $blankline_due = 0; 158 159open (HDR, "<$header"); 160while (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} 238close(HDR); 239 240print STDERR "$added added, $modified modified, $deleted deleted, $kept unchanged.\n"; 241 2421; 243