#!/usr/bin/perl
+# Simple script for generating prototypes for C functions
+# Written by Jelmer Vernooij
+# based on the original mkproto.sh by Andrew Tridgell
use strict;
-use warnings;
-my $header_name = '_PROTO_H_';
+# don't use warnings module as it is not portable enough
+# use warnings;
-if ($ARGV[0] eq '-h') {
- shift @ARGV;
- $header_name = shift @ARGV;
-}
+use Getopt::Long;
+use File::Basename;
+use File::Path;
+
+#####################################################################
+# read a file into a string
+my $public_file = undef;
+my $private_file = undef;
+my $all_file = undef;
+my $public_define = undef;
+my $private_define = undef;
+my $_public = "";
+my $_private = "";
+my $public_data = \$_public;
+my $private_data = \$_private;
+my $builddir = ".";
+my $srcdir = ".";
-sub print_header {
- print "#ifndef $header_name\n";
- print "#define $header_name\n\n";
- print "/* This file is automatically generated with \"make proto\". DO NOT EDIT */\n\n";
+sub public($)
+{
+ my ($d) = @_;
+ $$public_data .= $d;
}
-sub print_footer {
- printf "\n#endif /* %s */\n", $header_name;
+sub private($)
+{
+ my ($d) = @_;
+ $$private_data .= $d;
}
+sub usage()
+{
+ print "Usage: mkproto.pl [options] [c files]\n";
+ print "OPTIONS:\n";
+ print " --public=FILE Write prototypes for public functions to FILE\n";
+ print " --private=FILE Write prototypes for private functions to FILE\n";
+ print " --define=DEF Use DEF to check whether header was already included\n";
+ print " --public-define=DEF Same as --define, but just for public header\n";
+ print " --private-define=DEF Same as --define, but just for private header\n";
+ print " --srcdir=path Read files relative to this directory\n";
+ print " --builddir=path Write file relative to this directory\n";
+ print " --help Print this help message\n\n";
+ exit 0;
+}
-sub handle_loadparm {
- my $line = shift;
+GetOptions(
+ 'public=s' => sub { my ($f,$v) = @_; $public_file = $v; },
+ 'all=s' => sub { my ($f,$v) = @_; $public_file = $v; $private_file = $v; },
+ 'private=s' => sub { my ($f,$v) = @_; $private_file = $v; },
+ 'define=s' => sub {
+ my ($f,$v) = @_;
+ $public_define = $v;
+ $private_define = "$v\_PRIVATE";
+ },
+ 'public-define=s' => \$public_define,
+ 'private-define=s' => \$private_define,
+ 'srcdir=s' => sub { my ($f,$v) = @_; $srcdir = $v; },
+ 'builddir=s' => sub { my ($f,$v) = @_; $builddir = $v; },
+ 'help' => \&usage
+) or exit(1);
- if ($line =~ /^FN_GLOBAL_STRING/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "char *$fnName(void);\n";
- } elsif ($line =~ /^FN_LOCAL_STRING/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "char *$fnName(int );\n";
- } elsif ($line =~ /^FN_GLOBAL_BOOL/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "BOOL $fnName(void);\n";
- }
- elsif ($line =~ /^FN_LOCAL_BOOL/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "BOOL $fnName(int );\n";
- }
- elsif ($line =~ /^FN_GLOBAL_INTEGER/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "int $fnName(void);\n";
- }
- elsif ($line =~ /^FN_LOCAL_INTEGER/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "int $fnName(int );\n";
- }
- elsif ($line =~ /^FN_GLOBAL_LIST/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "const char **$fnName(void);\n";
- }
- elsif ($line =~ /^FN_LOCAL_LIST/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "const char **$fnName(int );\n";
- }
- elsif ($line =~ /^FN_GLOBAL_CONST_STRING/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "const char *$fnName(void);\n";
- }
- elsif ($line =~ /^FN_LOCAL_CONST_STRING/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "const char *$fnName(int );\n";
+sub normalize_define($$)
+{
+ my ($define, $file) = @_;
+
+ if (not defined($define) and defined($file)) {
+ $define = "__" . uc($file) . "__";
+ $define =~ tr{./}{__};
+ $define =~ tr{\-}{_};
+ } elsif (not defined($define)) {
+ $define = '_PROTO_H_';
}
- elsif ($line =~ /^FN_LOCAL_CHAR/o) {
- my $fnName = (split(/[\(,]/, $line))[1];
- print "char $fnName(int );\n";
+
+ return $define;
+}
+
+$public_define = normalize_define($public_define, $public_file);
+$private_define = normalize_define($private_define, $private_file);
+
+if ((defined($private_file) and defined($public_file) and ($private_file eq $public_file)) or
+ (not defined($private_file) and not defined($public_file))) {
+ $private_data = $public_data;
+}
+
+sub file_load($)
+{
+ my($filename) = @_;
+ local(*INPUTFILE);
+ open(INPUTFILE, $filename) or return undef;
+ my($saved_delim) = $/;
+ undef $/;
+ my($data) = <INPUTFILE>;
+ close(INPUTFILE);
+ $/ = $saved_delim;
+ return $data;
+}
+
+sub print_header($$)
+{
+ my ($file, $header_name) = @_;
+ $file->("#ifndef $header_name\n");
+ $file->("#define $header_name\n\n");
+ $file->("#undef _PRINTF_ATTRIBUTE\n");
+ $file->("#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)\n");
+ $file->("/* This file was automatically generated by mkproto.pl. DO NOT EDIT */\n\n");
+}
+
+sub print_footer($$)
+{
+ my ($file, $header_name) = @_;
+ $file->("#undef _PRINTF_ATTRIBUTE\n");
+ $file->("#define _PRINTF_ATTRIBUTE(a1, a2)\n");
+ $file->("\n#endif /* $header_name */\n\n");
+}
+
+sub handle_loadparm($$)
+{
+ my ($file,$line) = @_;
+ my $scope;
+ my $type;
+ my $name;
+ my $var;
+
+ if ($line =~ /^FN_(GLOBAL|LOCAL)_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),(.*)\)/o) {
+ $scope = $1;
+ $type = $2;
+ $name = $3;
+ $var = $4;
+ } elsif ($line =~ /^FN_(GLOBAL|LOCAL)_PARM_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),(.*)\)/o) {
+ $scope = $1;
+ $type = $2;
+ $name = $3;
+ $var = $4;
+ } else {
+ return;
}
+
+ my %tmap = (
+ "BOOL" => "bool ",
+ "CONST_STRING" => "const char *",
+ "STRING" => "const char *",
+ "INTEGER" => "int ",
+ "CHAR" => "char ",
+ "LIST" => "const char **",
+ );
+
+ my %smap = (
+ "GLOBAL" => "struct loadparm_context *",
+ "LOCAL" => "struct loadparm_service *, struct loadparm_service *"
+ );
+
+ $file->("$tmap{$type}lpcfg_$name($smap{$scope});\n");
}
+sub process_file($$$)
+{
+ my ($public_file, $private_file, $filename) = @_;
+
+ $filename =~ s/\.o$/\.c/g;
-sub process_files {
- my $line;
- my $inheader;
- my $gotstart;
+ if ($filename =~ /^\//) {
+ open(FH, "<$filename") or die("Failed to open $filename");
+ } elsif (!open(FH, "< $builddir/$filename")) {
+ open(FH, "< $srcdir/$filename") || die "Failed to open $filename";
+ }
- FILE: foreach my $filename (@ARGV) {
- next FILE unless (open(FH, "< $filename")); # skip over file unless it can be opened
- print "\n/* The following definitions come from $filename */\n\n";
+ $private_file->("\n/* The following definitions come from $filename */\n\n");
- $inheader = 0;
- $gotstart = 0;
- LINE: while (defined($line = <FH>)) {
+ my $comment = undef;
+ my $incomment = 0;
+ while (my $line = <FH>) {
+ my $target = \&private;
+ my $is_public = 0;
- if ($inheader) {
- # this chomp is somewhat expensive, so don't do it unless we know
- # that we probably want to use it
- chomp $line;
- if ($line =~ /\)\s*$/o) {
- $inheader = 0;
- print "$line;\n";
- } else {
- print "$line\n";
- }
- next LINE;
+ if ($line =~ /^\/\*\*/) {
+ $comment = "";
+ $incomment = 1;
+ }
+
+ if ($incomment) {
+ $comment .= $line;
+ if ($line =~ /\*\//) {
+ $incomment = 0;
}
+ }
- $gotstart = 0;
+ # these are ordered for maximum speed
+ next if ($line =~ /^\s/);
+
+ next unless ($line =~ /\(/);
- # ignore static and extern declarations
- if ($line =~ /^static|^extern/o ||
- $line !~ /^[a-zA-Z]/o ||
- $line =~ /[;]/o) {
- next LINE;
- }
+ next if ($line =~ /^\/|[;]/);
+ if ($line =~ /^FN_/) {
+ handle_loadparm($public_file, $line);
+ handle_loadparm($private_file, $line);
+ next;
+ }
- if ($line =~ /^FN_/) {
- handle_loadparm($line);
- }
+ if ($line =~ /^_PUBLIC_[\t ]/) {
+ $target = \&public;
+ $is_public = 1;
+ }
+ next unless ( $is_public || $line =~ /
+ ^(_DEPRECATED_ |_NORETURN_ |_WARN_UNUSED_RESULT_ |_PURE_ )*(
+ void|bool|int|struct|char|const|\w+_[tT]\s|uint|unsigned|long|NTSTATUS|
+ ADS_STATUS|enum\s.*\(|DATA_BLOB|WERROR|XFILE|FILE|DIR|
+ double|TDB_CONTEXT|TDB_DATA|TALLOC_CTX|NTTIME|FN_|init_module|
+ GtkWidget|GType|smb_ucs2_t|krb5_error_code|NET_API_STATUS)
+ /xo);
- # I'm going to leave these as is for now - perl can probably handle larger regex, though -- vance
- # I've also sort of put these in approximate order of most commonly called
+ next if ($line =~ /^int\s*main/);
- if ( $line =~ /
- ^void|^BOOL|^int|^struct|^char|^const|^\w+_[tT]\s|^uint|^unsigned|^long|
- ^NTSTATUS|^ADS_STATUS|^enum\s.*\(|^DATA_BLOB|^WERROR|^XFILE|^FILE|^DIR|
- ^double|^TDB_CONTEXT|^TDB_DATA|^TALLOC_CTX|^NTTIME
- /x) {
- $gotstart = 1;
- }
+ $target->("\n$comment") if (defined($comment)); $comment = undef;
+ if ( $line =~ /\(.*\)\s*$/o ) {
+ chomp $line;
+ $target->("$line;\n");
+ next;
+ }
- # goto next line if we don't have a start
- next LINE unless $gotstart;
+ $target->($line);
- if ( $line =~ /\(.*\)\s*$/o ) {
- # now that we're here, we know we
+ while ($line = <FH>) {
+ if ($line =~ /\)\s*$/o) {
chomp $line;
- print "$line;\n";
- next LINE;
- }
- elsif ( $line =~ /\(/o ) {
-
- $inheader = 1;
- # line hasn't been chomped, so we can assume it already has the \n
- print $line;
- next LINE;
+ $target->("$line;\n");
+ last;
}
+ $target->($line);
}
}
+
+ close(FH);
+}
+
+
+print_header(\&public, $public_define);
+if (defined($private_file) and defined($public_file) and $public_file ne $private_file) {
+ print_header(\&private, $private_define);
+
+ private("/* this file contains prototypes for functions that " .
+ "are private \n * to this subsystem or library. These functions " .
+ "should not be \n * used outside this particular subsystem! */\n\n");
+
+ public("/* this file contains prototypes for functions that " .
+ "are part of \n * the public API of this subsystem or library. */\n\n");
+
+}
+
+public("#ifndef _PUBLIC_\n#define _PUBLIC_\n#endif\n\n");
+public("#ifndef _PURE_\n#define _PURE_\n#endif\n\n");
+public("#ifndef _NORETURN_\n#define _NORETURN_\n#endif\n\n");
+public("#ifndef _DEPRECATED_\n#define _DEPRECATED_\n#endif\n\n");
+public("#ifndef _WARN_UNUSED_RESULT_\n#define _WARN_UNUSED_RESULT_\n#endif\n\n");
+
+process_file(\&public, \&private, $_) foreach (@ARGV);
+print_footer(\&public, $public_define);
+if (defined($private_file) and $public_file ne $private_file) {
+ print_footer(\&private, $private_define);
+}
+
+if (not defined($public_file)) {
+ print STDOUT $$public_data;
}
-print_header();
-process_files();
-print_footer();
+if (not defined($private_file) and defined($public_file)) {
+ print STDOUT $$private_data;
+}
+
+mkpath(dirname($public_file), 0, 0755);
+open(PUBLIC, ">$public_file") or die("Can't open `$public_file': $!");
+print PUBLIC "$$public_data";
+close(PUBLIC);
+
+if (defined($private_file) and $public_file ne $private_file) {
+ mkpath(dirname($private_file), 0, 0755);
+ open(PRIVATE, ">$private_file") or die("Can't open `$private_file': $!");
+ print PRIVATE "$$private_data";
+ close(PRIVATE);
+}