88e3e2a121a54fa26115de6a098945f61a8e3ac0
[amitay/samba.git] / source4 / script / mkproto.pl
1 #!/usr/bin/perl
2 # Simple script for generating prototypes for C functions
3 # Written by Jelmer Vernooij
4 # based on the original mkproto.sh by Andrew Tridgell
5
6 use strict;
7
8 # don't use warnings module as it is not portable enough
9 # use warnings;
10
11 use Getopt::Long;
12 use File::Basename;
13 use File::Path;
14
15 #####################################################################
16 # read a file into a string
17
18 my $public_file = undef;
19 my $private_file = undef;
20 my $public_define = undef;
21 my $private_define = undef;
22 my $_public = "";
23 my $_private = "";
24 my $public_data = \$_public;
25 my $private_data = \$_private;
26 my $builddir = undef;
27 my $srcdir = undef;
28
29 sub public($)
30 {
31         my ($d) = @_;
32         $$public_data .= $d;
33 }
34
35 sub private($)
36 {
37         my ($d) = @_;
38         $$private_data .= $d;
39 }
40
41 sub usage()
42 {
43         print "Usage: mkproto.pl [options] [c files]\n";
44         print "OPTIONS:\n";
45         print "  --public=FILE          Write prototypes for public functions to FILE\n";
46         print "  --private=FILE         Write prototypes for private functions to FILE\n";
47         print "  --define=DEF           Use DEF to check whether header was already included\n";
48         print "  --public-define=DEF    Same as --define, but just for public header\n";
49         print "  --private-define=DEF   Same as --define, but just for private header\n";
50         print "  --srcdir=path          Read files relative to this directory\n";
51         print "  --builddir=path        Write file relative to this directory\n";
52         print "  --help                 Print this help message\n\n";
53         exit 0;
54 }
55
56 GetOptions(
57         'public=s' => sub { my ($f,$v) = @_; $public_file = $v; },
58         'private=s' => sub { my ($f,$v) = @_; $private_file = $v; },
59         'define=s' => sub { 
60                 my ($f,$v) = @_; 
61                 $public_define = $v; 
62                 $private_define = "$v\_PRIVATE"; 
63         },
64         'public-define=s' => \$public_define,
65         'private-define=s' => \$private_define,
66         'srcdir=s' => sub { my ($f,$v) = @_; $srcdir = $v; },
67         'builddir=s' => sub { my ($f,$v) = @_; $builddir = $v; },
68         'help' => \&usage
69 ) or exit(1);
70
71 if (not defined($public_define) and defined($public_file)) {
72         $public_define = ".." . uc($public_file) . "__";
73         $public_define =~ tr{./}{__};
74 } elsif (not defined($public_define)) {
75         $public_define = '_PROTO_H_';
76 }
77
78 if (not defined($private_define) and defined($private_file)) {
79         $private_define = "__" . uc($private_file) . "__";
80         $private_define =~ tr{./}{__};
81 } elsif (not defined($public_define)) {
82         $public_define = '_PROTO_H_';
83 }
84
85 if ((defined($private_file) and defined($public_file) and ($private_file eq $public_file)) or 
86         (not defined($private_file) and not defined($public_file))) {
87         $private_data = $public_data;
88 }
89
90 sub file_load($)
91 {
92     my($filename) = shift;
93     local(*INPUTFILE);
94     open(INPUTFILE, $filename) || return undef;
95     my($saved_delim) = $/;
96     undef $/;
97     my($data) = <INPUTFILE>;
98     close(INPUTFILE);
99     $/ = $saved_delim;
100     return $data;
101 }
102
103 sub print_header($$)
104 {
105         my ($file, $header_name) = @_;
106         $file->("#ifndef $header_name\n");
107         $file->("#define $header_name\n\n");
108         $file->("#undef _PRINTF_ATTRIBUTE\n");
109         $file->("#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)\n");
110         $file->("/* This file was automatically generated by mkproto.pl. DO NOT EDIT */\n\n");
111 }
112
113 sub print_footer($$) 
114 {
115         my ($file, $header_name) = @_;
116         $file->("#undef _PRINTF_ATTRIBUTE\n");
117         $file->("#define _PRINTF_ATTRIBUTE(a1, a2)\n");
118         $file->("\n#endif /* $header_name */\n\n");
119 }
120
121 sub handle_loadparm($$) 
122 {
123         my ($file,$line) = @_;
124
125         if ($line =~ /^_PUBLIC_ FN_(GLOBAL|LOCAL)_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),.*\)/o) {
126                 my $scope = $1;
127                 my $type = $2;
128                 my $name = $3;
129
130                 my %tmap = (
131                             "BOOL" => "BOOL ",
132                                 "bool" => "bool ",
133                             "CONST_STRING" => "const char *",
134                             "STRING" => "const char *",
135                             "INTEGER" => "int ",
136                             "CHAR" => "char ",
137                             "LIST" => "const char **",
138                             );
139
140                 my %smap = (
141                             "GLOBAL" => "void",
142                             "LOCAL" => "int "
143                             );
144
145                 $file->("$tmap{$type}$name($smap{$scope});\n");
146         }
147 }
148
149 sub process_file($$$) 
150 {
151         my ($public_file, $private_file, $filename) = @_;
152
153         $filename =~ s/\.o$/\.c/g;
154
155         if (!open(FH, "< $builddir/$filename")) {
156             open(FH, "< $srcdir/$filename") || die "Failed to open $filename";
157         }
158
159         $private_file->("\n/* The following definitions come from $filename  */\n\n");
160
161         while (my $line = <FH>) {             
162                 my $target = \&private;
163                 my $is_public = 0;
164
165                 # these are ordered for maximum speed
166                 next if ($line =~ /^\s/);
167               
168                 next unless ($line =~ /\(/);
169
170                 next if ($line =~ /^\/|[;]/);
171
172                 if ($line =~ /^_PUBLIC_ FN_/) {
173                         handle_loadparm($public_file, $line);
174                         next;
175                 }
176
177                 if ($line =~ /^_PUBLIC_[\t ]/) {
178                         $target = \&public;
179                         $is_public = 1;
180                 }
181
182                 next unless ( $is_public || $line =~ /
183                               ^void|^BOOL|^bool|^int|^struct|^char|^const|^\w+_[tT]\s|^uint|^unsigned|^long|
184                               ^NTSTATUS|^ADS_STATUS|^enum\s.*\(|^DATA_BLOB|^WERROR|^XFILE|^FILE|^DIR|
185                               ^double|^TDB_CONTEXT|^TDB_DATA|^TALLOC_CTX|^NTTIME|^FN_|^init_module|
186                               ^GtkWidget|^GType|^smb_ucs2_t
187                               /xo);
188
189                 next if ($line =~ /^int\s*main/);
190
191                 if ( $line =~ /\(.*\)\s*$/o ) {
192                         chomp $line;
193                         $target->("$line;\n");
194                         next;
195                 }
196
197                 $target->($line);
198
199                 while ($line = <FH>) {
200                         if ($line =~ /\)\s*$/o) {
201                                 chomp $line;
202                                 $target->("$line;\n");
203                                 last;
204                         }
205                         $target->($line);
206                 }
207         }
208
209         close(FH);
210 }
211
212
213 print_header(\&public, $public_define);
214 if ($public_file ne $private_file) {
215         print_header(\&private, $private_define);
216
217         private("/* this file contains prototypes for functions that " .
218                         "are private \n * to this subsystem or library. These functions " .
219                         "should not be \n * used outside this particular subsystem! */\n\n");
220
221         public("/* this file contains prototypes for functions that " . 
222                         "are part of \n * the public API of this subsystem or library. */\n\n");
223
224 }
225
226 public("#ifndef _PUBLIC_\n#define _PUBLIC_\n#endif\n\n");
227
228 process_file(\&public, \&private, $_) foreach (@ARGV);
229 print_footer(\&public, $public_define);
230 if ($public_file ne $private_file) {
231         print_footer(\&private, $private_define);
232 }
233
234 if (not defined($public_file)) {
235         print STDOUT $$public_data;
236 }
237
238 if (not defined($private_file) and defined($public_file)) {
239         print STDOUT $$private_data;
240 }
241
242 my $old_public_data = file_load($public_file);
243 my $old_private_data = file_load($private_file);
244
245 mkpath(dirname($public_file), 0, 0755);
246 open(PUBLIC, ">$public_file") or die("Can't open `$public_file': $!"); 
247 print PUBLIC "$$public_data";
248 close(PUBLIC);
249
250 if ($public_file ne $private_file) {
251         mkpath(dirname($private_file), 0, 0755);
252         open(PRIVATE, ">$private_file") or die("Can't open `$private_file': $!"); 
253         print PRIVATE "$$private_data";
254         close(PRIVATE);
255 }