2006-03-07 22:15:05 +03:00
#!/usr/bin/perl
# Simple script for updating the prototypes in a C header file
#
# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
# Published under the GNU GPL
use strict ;
use warnings ;
use Getopt::Long ;
= head1 NAME
update - proto - automatically update prototypes in header files
= head1 SYNOPSIS
update - proto [ OPTIONS ] <HEADER> <C-FILE> ...
update - proto [ OPTIONS ] <HEADER>
= head1 DESCRIPTION
Update - proto makes sure the prototypes in a C header file are current
by comparing the existing prototypes in a header with the function definition
in the source file . It aims to keep the diff between the original header
and generated one as small as possible .
New prototypes are inserted before any line that contains the following comment:
/* New prototypes are inserted above this line */
It will automatically parse C files after it encounters a line that contains:
/* The following definitions come from FILE */
2006-03-18 00:24:24 +03:00
When two or more prototypes exist for a function , only the first one
will be kept .
2006-03-07 22:15:05 +03:00
= head1 OPTIONS
= over 4
= item I <--verbose|-v>
Increase verbosity . Currently only two levels of verbosity are used .
= item I <--help>
Show list of options
= back
= head1 BUGS
2006-03-07 23:22:26 +03:00
Strange complex functions are not recognized . In particular those
created by macros or returning ( without typedef ) function pointers .
2006-03-07 22:15:05 +03:00
= head1 LICENSE
update - proto is licensed under the GNU General Public License L <http://www.gnu.org/licenses/gpl.html> .
= head1 AUTHOR
update - proto was written by Jelmer Vernooij L <jelmer@samba.org> .
= cut
sub Usage ()
{
print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n" ;
exit 1 ;
}
sub Help ()
{
print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n" ;
print "Options:\n" ;
print " --help Show this help message\n" ;
print " --verbose Write changes made to standard error\n\n" ;
exit 0 ;
}
my % new_protos = ( ) ;
my $ verbose = 0 ;
GetOptions (
'help|h' = > \ & Help ,
'v|verbose' = > sub { $ verbose += 1 ; }
) or Usage ( ) ;
2006-03-07 23:22:26 +03:00
sub count ($$)
{
my ( $ t , $ s ) = @ _ ;
my $ count = 0 ;
while ( $ s =~ s/^(.)// ) { $ count + + if $ 1 eq $ t ; }
return $ count ;
}
2006-03-07 22:15:05 +03:00
my $ header = shift @ ARGV ;
sub process_file ($)
{
my $ file = shift ;
open ( IN , "<$file" ) ;
while ( my $ line = <IN> ) {
2006-03-07 23:22:26 +03:00
$ _ = $ line ;
next if /^\s/ ;
next unless /\(/ ;
next if /^\/|[;]|^#|}|^\s*static/ ;
s/\/\*(.*?)\*\///g ;
my $ public = s/_PUBLIC_//g ;
s/_PRINTF_ATTRIBUTE\([^)]+\)//g ;
next unless /^(struct\s+\w+|union\s+\w+|\w+)\s+\**\s*(\w+)\s*\((.*)$/ ;
2006-03-07 22:15:05 +03:00
2006-03-07 23:22:26 +03:00
my $ name = $ 2 ;
2006-03-07 22:15:05 +03:00
next if ( $ name eq "main" ) ;
2006-03-07 23:22:26 +03:00
# Read continuation lines if any
my $ prn = 1 + count ( "(" , $ 3 ) - count ( ")" , $ 3 ) ;
while ( $ prn ) {
my $ l = <IN> ;
$ l or die ( "EOF while parsing function prototype" ) ;
$ line . = $ l ;
$ prn += count ( "(" , $ l ) - count ( ")" , $ l ) ;
}
$ line =~ s/\n$// ;
# Strip off possible start of function
$ line =~ s/{\s*$//g ;
2006-03-07 22:15:05 +03:00
$ new_protos { $ name } = "$line;" ;
}
close ( IN ) ;
}
process_file ( $ _ ) foreach ( @ ARGV ) ;
my $ added = 0 ;
my $ modified = 0 ;
my $ deleted = 0 ;
my $ kept = 0 ;
sub insert_new_protos ()
{
foreach ( keys % new_protos ) {
print "$new_protos{$_}\n" ;
print STDERR "Inserted prototype for `$_'\n" if ( $ verbose ) ;
$ added += 1 ;
}
% new_protos = ( ) ;
}
2006-03-07 23:22:26 +03:00
my $ blankline_due = 0 ;
2006-03-07 22:15:05 +03:00
open ( HDR , "<$header" ) ;
while ( my $ line = <HDR> ) {
2006-03-07 23:22:26 +03:00
if ( $ line eq "\n" ) {
$ blankline_due = 1 ;
$ line = <HDR> ;
2006-03-07 22:15:05 +03:00
}
# Recognize C files that prototypes came from
2006-03-07 23:22:26 +03:00
if ( $ line =~ /\/\* The following definitions come from (.*) \*\// ) {
2006-03-07 22:15:05 +03:00
insert_new_protos ( ) ;
2006-03-07 23:22:26 +03:00
if ( $ blankline_due ) {
print "\n" ;
$ blankline_due = 0 ;
}
2006-03-07 22:15:05 +03:00
process_file ( $ 1 ) ;
print "$line" ;
next ;
}
2006-03-07 23:22:26 +03:00
if ( $ blankline_due ) {
print "\n" ;
$ blankline_due = 0 ;
}
# Insert prototypes that weren't in the header before
if ( $ line =~ /\/\* New prototypes are inserted above this line.*\*\/\s*/ ) {
insert_new_protos ( ) ;
print "$line\n" ;
next ;
}
2006-03-07 22:15:05 +03:00
if ( $ line =~ /^\s*typedef |^\#|^\s*static/ ) {
print "$line" ;
next ;
}
2006-03-07 23:22:26 +03:00
$ _ = $ line ;
s/\/\*(.*?)\*\///g ;
my $ public = s/_PUBLIC_//g ;
s/_PRINTF_ATTRIBUTE\([^)]+\)//g ;
unless ( /^(struct\s+\w+|union\s+\w+|\w+)\s+\**\s*(\w+)\s*\((.*)$/ ) {
2006-03-07 22:15:05 +03:00
print "$line" ;
next ;
}
2006-03-07 23:22:26 +03:00
# Read continuation lines if any
my $ prn = 1 + count ( "(" , $ 3 ) - count ( ")" , $ 3 ) ;
while ( $ prn ) {
my $ l = <HDR> ;
$ l or die ( "EOF while parsing function prototype" ) ;
$ line . = $ l ;
$ prn += count ( "(" , $ l ) - count ( ")" , $ l ) ;
}
my $ name = $ 2 ;
2006-03-07 22:15:05 +03:00
# This prototype is for a function that was removed
unless ( defined ( $ new_protos { $ name } ) ) {
$ deleted += 1 ;
print STDERR "Removed prototype for `$name'\n" if ( $ verbose ) ;
next ;
}
my $ nline = $ line ;
chop ( $ nline ) ;
if ( $ new_protos { $ name } ne $ nline ) {
$ modified += 1 ;
print STDERR "Updated prototype for `$name'\n" if ( $ verbose ) ;
print "$new_protos{$name}\n" ;
} else {
$ kept += 1 ;
print STDERR "Prototype for `$name' didn't change\n" if ( $ verbose > 1 ) ;
print "$line" ;
}
delete $ new_protos { $ name } ;
}
close ( HDR ) ;
print STDERR "$added added, $modified modified, $deleted deleted, $kept unchanged.\n" ;
1 ;