f1c2cf9d0e8294bdcfce8415b20f00554464acda
[ira/wip.git] / source / 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 sub normalize_define($$)
72 {
73         my ($define, $file) = @_;
74
75         if (not defined($define) and defined($file)) {
76                 $define = "__" . uc($file) . "__";
77                 $define =~ tr{./}{__};
78                 $define =~ tr{\-}{_};
79         } elsif (not defined($define)) {
80                 $define = '_PROTO_H_';
81         }
82
83         return $define;
84 }
85
86 $public_define = normalize_define($public_define, $public_file);
87 $private_define = normalize_define($private_define, $private_file);
88
89 if ((defined($private_file) and defined($public_file) and ($private_file eq $public_file)) or 
90         (not defined($private_file) and not defined($public_file))) {
91         $private_data = $public_data;
92 }
93
94 sub file_load($)
95 {
96     my($filename) = shift;
97     local(*INPUTFILE);
98     open(INPUTFILE, $filename) || return undef;
99     my($saved_delim) = $/;
100     undef $/;
101     my($data) = <INPUTFILE>;
102     close(INPUTFILE);
103     $/ = $saved_delim;
104     return $data;
105 }
106
107 sub print_header($$)
108 {
109         my ($file, $header_name) = @_;
110         $file->("#ifndef $header_name\n");
111         $file->("#define $header_name\n\n");
112         $file->("#undef _PRINTF_ATTRIBUTE\n");
113         $file->("#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)\n");
114         $file->("/* This file was automatically generated by mkproto.pl. DO NOT EDIT */\n\n");
115 }
116
117 sub print_footer($$) 
118 {
119         my ($file, $header_name) = @_;
120         $file->("#undef _PRINTF_ATTRIBUTE\n");
121         $file->("#define _PRINTF_ATTRIBUTE(a1, a2)\n");
122         $file->("\n#endif /* $header_name */\n\n");
123 }
124
125 sub handle_loadparm($$) 
126 {
127         my ($file,$line) = @_;
128
129         if ($line =~ /^_PUBLIC_ FN_(GLOBAL|LOCAL)_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),.*\)/o) {
130                 my $scope = $1;
131                 my $type = $2;
132                 my $name = $3;
133
134                 my %tmap = (
135                             "BOOL" => "bool ",
136                             "CONST_STRING" => "const char *",
137                             "STRING" => "const char *",
138                             "INTEGER" => "int ",
139                             "CHAR" => "char ",
140                             "LIST" => "const char **",
141                             );
142
143                 my %smap = (
144                             "GLOBAL" => "struct loadparm_context *",
145                             "LOCAL" => "struct loadparm_service *"
146                             );
147
148                 $file->("$tmap{$type}$name($smap{$scope});\n");
149         }
150 }
151
152 sub process_file($$$) 
153 {
154         my ($public_file, $private_file, $filename) = @_;
155
156         $filename =~ s/\.o$/\.c/g;
157
158         if (!open(FH, "< $builddir/$filename")) {
159             open(FH, "< $srcdir/$filename") || die "Failed to open $filename";
160         }
161
162         $private_file->("\n/* The following definitions come from $filename  */\n\n");
163
164         my $comment = undef;
165         my $incomment = 0;
166         while (my $line = <FH>) {             
167                 my $target = \&private;
168                 my $is_public = 0;
169
170                 if ($line =~ /^\/\*\*/) { 
171                         $comment = "";
172                         $incomment = 1;
173                 }
174
175                 if ($incomment) {
176                         $comment .= $line;
177                         if ($line =~ /\*\//) {
178                                 $incomment = 0;
179                         }
180                 } 
181
182                 # these are ordered for maximum speed
183                 next if ($line =~ /^\s/);
184               
185                 next unless ($line =~ /\(/);
186
187                 next if ($line =~ /^\/|[;]/);
188
189                 if ($line =~ /^_PUBLIC_ FN_/) {
190                         handle_loadparm($public_file, $line);
191                         next;
192                 }
193
194                 if ($line =~ /^_PUBLIC_[\t ]/) {
195                         $target = \&public;
196                         $is_public = 1;
197                 }
198
199                 next unless ( $is_public || $line =~ /
200                               ^(_DEPRECATED_ |_NORETURN_ |_WARN_UNUSED_RESULT_ |_PURE_ )*(
201                                   void|bool|int|struct|char|const|\w+_[tT]\s|uint|unsigned|long|NTSTATUS|
202                                   ADS_STATUS|enum\s.*\(|DATA_BLOB|WERROR|XFILE|FILE|DIR|
203                               double|TDB_CONTEXT|TDB_DATA|TALLOC_CTX|NTTIME|FN_|init_module|
204                               GtkWidget|GType|smb_ucs2_t|krb5_error_code)
205                               /xo);
206
207                 next if ($line =~ /^int\s*main/);
208
209                 $target->("\n$comment") if (defined($comment)); $comment = undef;
210
211                 if ( $line =~ /\(.*\)\s*$/o ) {
212                         chomp $line;
213                         $target->("$line;\n");
214                         next;
215                 }
216
217                 $target->($line);
218
219                 while ($line = <FH>) {
220                         if ($line =~ /\)\s*$/o) {
221                                 chomp $line;
222                                 $target->("$line;\n");
223                                 last;
224                         }
225                         $target->($line);
226                 }
227         }
228
229         close(FH);
230 }
231
232
233 print_header(\&public, $public_define);
234 if ($public_file ne $private_file) {
235         print_header(\&private, $private_define);
236
237         private("/* this file contains prototypes for functions that " .
238                         "are private \n * to this subsystem or library. These functions " .
239                         "should not be \n * used outside this particular subsystem! */\n\n");
240
241         public("/* this file contains prototypes for functions that " . 
242                         "are part of \n * the public API of this subsystem or library. */\n\n");
243
244 }
245
246 public("#ifndef _PUBLIC_\n#define _PUBLIC_\n#endif\n\n");
247 public("#ifndef _PURE_\n#define _PURE_\n#endif\n\n");
248 public("#ifndef _NORETURN_\n#define _NORETURN_\n#endif\n\n");
249 public("#ifndef _DEPRECATED_\n#define _DEPRECATED_\n#endif\n\n");
250 public("#ifndef _WARN_UNUSED_RESULT_\n#define _WARN_UNUSED_RESULT_\n#endif\n\n");
251
252 process_file(\&public, \&private, $_) foreach (@ARGV);
253 print_footer(\&public, $public_define);
254 if ($public_file ne $private_file) {
255         print_footer(\&private, $private_define);
256 }
257
258 if (not defined($public_file)) {
259         print STDOUT $$public_data;
260 }
261
262 if (not defined($private_file) and defined($public_file)) {
263         print STDOUT $$private_data;
264 }
265
266 my $old_public_data = file_load($public_file);
267 my $old_private_data = file_load($private_file);
268
269 mkpath(dirname($public_file), 0, 0755);
270 open(PUBLIC, ">$public_file") or die("Can't open `$public_file': $!"); 
271 print PUBLIC "$$public_data";
272 close(PUBLIC);
273
274 if ($public_file ne $private_file) {
275         mkpath(dirname($private_file), 0, 0755);
276         open(PRIVATE, ">$private_file") or die("Can't open `$private_file': $!"); 
277         print PRIVATE "$$private_data";
278         close(PRIVATE);
279 }