1# Make prototypes from .c files
2# $Id$
3
4##use Getopt::Std;
5require 'getopts.pl';
6
7use JSON;
8
9my $comment = 0;
10my $doxygen = 0;
11my $funcdoc = 0;
12my $if_0 = 0;
13my $brace = 0;
14my $line = "";
15my $debug = 0;
16my $oproto = 1;
17my $private_func_re = "^_";
18my %depfunction = ();
19my %exported;
20my %deprecated;
21my $apple = 0;
22my %documentation;
23
24Getopts('ax:m:o:p:dqE:R:P:') || die "foo";
25
26if($opt_a) {
27    $apple = 1;
28}
29
30if($opt_d) {
31    $debug = 1;
32}
33
34if($opt_q) {
35    $oproto = 0;
36}
37
38if($opt_R) {
39    $private_func_re = $opt_R;
40}
41my %flags = (
42	  'multiline-proto' => 1,
43	  'header' => 1,
44	  'function-blocking' => 0,
45	  'gnuc-attribute' => 1,
46	  'cxx' => 1
47	  );
48if($opt_m) {
49    foreach $i (split(/,/, $opt_m)) {
50	if($i eq "roken") {
51	    $flags{"multiline-proto"} = 0;
52	    $flags{"header"} = 0;
53	    $flags{"function-blocking"} = 0;
54	    $flags{"gnuc-attribute"} = 0;
55	    $flags{"cxx"} = 0;
56	} else {
57	    if(substr($i, 0, 3) eq "no-") {
58		$flags{substr($i, 3)} = 0;
59	    } else {
60		$flags{$i} = 1;
61	    }
62	}
63    }
64}
65
66if($opt_x) {
67    my $EXP;
68    local $/;
69    open(EXP, '<', $opt_x) || die "open ${opt_x}";
70    my $obj = JSON->new->utf8->decode(<EXP>);
71    close $EXP;
72
73    foreach my $x (keys %$obj) {
74	if (defined $obj->{$x}->{"export"}) {
75	    $exported{$x} = $obj->{$x};
76	}
77	if (defined $obj->{$x}->{"deprecated"}) {
78	    $deprecated{$x} = $obj->{$x}->{"deprecated"};
79	}
80    }
81}
82
83my %defineRules = (
84    'HEIMDAL_DEPRECATED' =>
85	"#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))\n".
86	"#define HEIMDAL_DEPRECATED __attribute__((deprecated))\n".
87	"#elif defined(_MSC_VER) && (_MSC_VER>1200)\n".
88	"#define HEIMDAL_DEPRECATED __declspec(deprecated)\n".
89	"#else\n".
90	"#define HEIMDAL_DEPRECATED\n".
91	"#endif",
92    'HEIMDAL_PRINTF_ATTRIBUTE' =>
93        "#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))\n".
94	"#define HEIMDAL_PRINTF_ATTRIBUTE(x) __attribute__((format x))\n".
95	"#else\n".
96	"#define HEIMDAL_PRINTF_ATTRIBUTE(x)\n".
97	"#endif",
98   'HEIMDAL_NORETURN_ATTRIBUTE' =>
99	"#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))\n".
100	"#define HEIMDAL_NORETURN_ATTRIBUTE __attribute__((noreturn))\n".
101	"#else\n".
102	"#define HEIMDAL_NORETURN_ATTRIBUTE\n".
103	"#endif",
104    'HEIMDAL_UNUSED_ATTRIBUTE' =>
105	"#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))\n".
106	"#define HEIMDAL_UNUSED_ATTRIBUTE __attribute__((unused))\n".
107	"#else\n".
108	"#define HEIMDAL_UNUSED_ATTRIBUTE\n".
109	"#endif"
110);
111
112my %usedRules;
113
114while(<>) {
115    print $brace, " ", $_ if($debug);
116
117    # Handle C comments
118    s@/\*.*\*/@@;
119    s@//.*/@@;
120    if ( s@/\*\*(.*)@@) { $comment = 1; $doxygen = 1; $funcdoc = $1;
121    } elsif ( s@/\*.*@@) { $comment = 1;
122    } elsif ($comment && s@.*\*/@@) { $comment = 0; $doxygen = 0;
123    } elsif ($doxygen) { $funcdoc .= $_; next;
124    } elsif ($comment) { next; }
125
126    if(/^\#if 0/) {
127	$if_0 = 1;
128    }
129    if($if_0 && /^\#endif/) {
130	$if_0 = 0;
131    }
132    if($if_0) { next }
133    if(/^\s*\#/) {
134	next;
135    }
136    if(/^\s*$/) {
137	$line = "";
138	next;
139    }
140    if(/\{/){
141	if (!/\}/) {
142	    $brace++;
143	}
144	$_ = $line;
145	while(s/\*\//\ca/){
146	    s/\/\*(.|\n)*\ca//;
147	}
148	s/^\s*//;
149	s/\s*$//;
150	s/\s+/ /g;
151	if($_ =~ /\)/){
152	    if(!/^static/ && !/^PRIVATE/){
153		$attr = "";
154		if(m/(.*)(__attribute__\s?\(.*\))/) {
155		    $attr .= " $2";
156		    $_ = $1;
157		}
158		if(m/(.*)\s(\w+DEPRECATED_FUNCTION)\s?(\(.*\))(.*)/) {
159		    $depfunction{$2} = 1;
160		    $attr .= " $2$3";
161		    $_ = "$1 $4";
162		}
163		if(m/(.*)\s(\w+DEPRECATED)(.*)/) {
164		    $usedRules{$2} = 1;
165		    $attr .= " $2";
166		    $_ = "$1 $3";
167		}
168		if(m/(.*)\s(HEIMDAL_\w+_ATTRIBUTE)\s?(\(.*\))?(.*)/) {
169		    $usedRules{$2} = 1;
170		    $attr .= " $2$3";
171		    $_ = "$1 $4";
172		}
173		# remove outer ()
174		s/\s*\(/</;
175		s/\)\s?$/>/;
176		# remove , within ()
177		while(s/\(([^()]*),(.*)\)/($1\$$2)/g){}
178		s/\<\s*void\s*\>/<>/;
179		# remove parameter names
180		if($opt_P eq "remove") {
181		    s/(\s*)([a-zA-Z0-9_]+)([,>])/$3/g;
182		    s/\s+\*/*/g;
183		    s/\(\*(\s*)([a-zA-Z0-9_]+)\)/(*)/g;
184		} elsif($opt_P eq "comment") {
185		    s/([a-zA-Z0-9_]+)([,>])/\/\*$1\*\/$2/g;
186		    s/\(\*([a-zA-Z0-9_]+)\)/(*\/\*$1\*\/)/g;
187		}
188		s/\<\>/<void>/;
189		# add newlines before parameters
190		if($flags{"multiline-proto"}) {
191		    s/,\s*/,\n\t/g;
192		} else {
193		    s/,\s*/, /g;
194		}
195		# fix removed ,
196		s/\$/,/g;
197		# match function name
198		/([a-zA-Z0-9_]+)\s*\</;
199		$f = $1;
200		if($oproto) {
201		    $LP = "__P((";
202		    $RP = "))";
203		} else {
204		    $LP = "(";
205		    $RP = ")";
206		}
207		# only add newline if more than one parameter
208                if($flags{"multiline-proto"} && /,/){
209		    s/\</ $LP\n\t/;
210		}else{
211		    s/\</ $LP/;
212		}
213		s/\>/$RP/;
214		# insert newline before function name
215		if($flags{"multiline-proto"}) {
216		    s/(.*)\s([a-zA-Z0-9_]+ \Q$LP\E)/$1\n$2/;
217		}
218		if($attr ne "") {
219		    $_ .= "\n    $attr";
220		}
221		if ($funcdoc) {
222		    $documentation{$f} = $funcdoc;
223		}
224		$funcdoc = undef;
225		if ($apple && exists $exported{$f}) {
226		    $ios = $exported{$f}{ios};
227		    $ios = "NA" if (!defined $ios);
228		    $mac = $exported{$f}{macos};
229		    $mac = "NA" if (!defined $mac);
230		    die "$f neither" if ($mac eq "NA" and $ios eq "NA");
231
232		    if (exists $exported{$f}{deprecated}) {
233			$iosDep = $exported{$f}{iosDep};
234			if ($ios eq "NA") {
235			    $iosDep = "NA";
236			}
237
238			$macDep = $exported{$f}{macosDep};
239			if ($mac eq "NA") {
240			    $macDep = "NA";
241			}
242
243			$_ = $_ . "  __OSX_AVAILABLE_BUT_DEPRECATED_MSG(__MAC_${mac}, __MAC_${macDep}, __IPHONE_${ios}, __IPHONE_${iosDep}, \"$exported{$f}{deprecated}\")";
244		    } else {
245			$_ = $_ . "  __OSX_AVAILABLE_STARTING(__MAC_${mac}, __IPHONE_${ios})";
246		    }
247		}
248		print "found function $f\n" if($debug);
249		if (exists $deprecated{$f} && !$apple && exists $exported{$f}) {
250		    $_ = $_ . "  GSSAPI_DEPRECATED_FUNCTION(\"$deprecated{$f}\")";
251		    $depfunction{GSSAPI_DEPRECATED_FUNCTION} = 1;
252		}
253		$_ = $_ . ";";
254		$funcs{$f} = $_;
255	    }
256	}
257	$line = "";
258    }
259    if(/\}/){
260	$brace--;
261    }
262    if(/^\}/){
263	$brace = 0;
264    }
265    if($brace == 0) {
266	$line = $line . " " . $_;
267    }
268}
269
270die "reached end of code and still in doxygen comment" if ($doxygen);
271die "reached end of code and still in comment" if ($comment);
272
273sub foo {
274    local ($arg) = @_;
275    $_ = $arg;
276    s/.*\/([^\/]*)/$1/;
277    s/.*\\([^\\]*)/$1/;
278    s/[^a-zA-Z0-9]/_/g;
279    "__" . $_ . "__";
280}
281
282if($opt_o) {
283    open(OUT, ">$opt_o");
284    $block = &foo($opt_o);
285} else {
286    $block = "__public_h__";
287}
288
289if($opt_p) {
290    open(PRIV, ">$opt_p");
291    $private = &foo($opt_p);
292} else {
293    $private = "__private_h__";
294}
295
296$public_h = "";
297$private_h = "";
298
299$public_h_header .= "/* This is a generated file */
300#ifndef $block
301#define $block
302
303";
304if ($oproto) {
305    $public_h_header .= "#ifdef __STDC__
306#include <stdarg.h>
307#ifndef __P
308#define __P(x) x
309#endif
310#else
311#ifndef __P
312#define __P(x) ()
313#endif
314#endif
315
316";
317} else {
318    $public_h_header .= "#include <stdarg.h>
319
320";
321}
322$public_h_trailer = "";
323
324$private_h_header = "/* This is a generated file */
325#ifndef $private
326#define $private
327
328";
329if($oproto) {
330    $private_h_header .= "#ifdef __STDC__
331#include <stdarg.h>
332#ifndef __P
333#define __P(x) x
334#endif
335#else
336#ifndef __P
337#define __P(x) ()
338#endif
339#endif
340
341";
342} else {
343    $private_h_header .= "#include <stdarg.h>
344
345";
346}
347$private_h_trailer = "";
348
349foreach(sort keys %usedRules) {
350    next if not exists $defineRules{$_};
351
352    my $var = "#ifndef $_
353$defineRules{$_}
354#endif
355
356";
357
358    $private_h_header .= $var;
359    $public_h_header .= $var;
360}
361
362foreach(sort keys %funcs){
363    if(/^(DllMain|main)$/) { next }
364    if ($funcs{$_} =~ /\^/) {
365	$beginblock = "#ifdef __BLOCKS__\n";
366	$endblock = "#endif /* __BLOCKS__ */\n";
367    } else {
368	$beginblock = $endblock = "";
369    }
370    # if we have an export table and doesn't have content, or matches private RE
371    if(((scalar keys %exported) ne 0 && !exists $exported{$_} ) || /$private_func_re/) {
372	$private_h .= $beginblock;
373#	if ($apple and not /$private_func_re/) {
374#	    $private_h .= "#define $_ __ApplePrivate_${_}\n";
375#	}
376	$private_h .= $funcs{$_} . "\n" ;
377	$private_h .= $endblock . "\n";
378	if($funcs{$_} =~ /__attribute__/) {
379	    $private_attribute_seen = 1;
380	}
381    } else {
382	if($documentation{$_}) {
383	    $public_h .= "/**\n";
384	    $public_h .= "$documentation{$_}";
385	    $public_h .= " */\n\n";
386	}
387	if($flags{"function-blocking"}) {
388	    $fupper = uc $_;
389	    if($exported{$_} =~ /proto/) {
390		$public_h .= "#if !defined(HAVE_$fupper) || defined(NEED_${fupper}_PROTO)\n";
391	    } else {
392		$public_h .= "#ifndef HAVE_$fupper\n";
393	    }
394	}
395	$public_h .= $beginblock . $funcs{$_} . "\n" . $endblock;
396	if($funcs{$_} =~ /__attribute__/) {
397	    $public_attribute_seen = 1;
398	}
399	if($flags{"function-blocking"}) {
400	    $public_h .= "#endif\n";
401	}
402	$public_h .= "\n";
403    }
404}
405
406if($flags{"gnuc-attribute"}) {
407    if ($public_attribute_seen) {
408	$public_h_header .= "#if !defined(__GNUC__) && !defined(__attribute__)
409#define __attribute__(x)
410#endif
411
412";
413    }
414
415    if ($private_attribute_seen) {
416	$private_h_header .= "#if !defined(__GNUC__) && !defined(__attribute__)
417#define __attribute__(x)
418#endif
419
420";
421    }
422}
423
424my $depstr = "";
425my $undepstr = "";
426foreach (keys %depfunction) {
427    $depstr .= "#ifndef $_
428#ifndef __has_extension
429#define __has_extension(x) 0
430#define ${_}has_extension 1
431#endif
432#if __has_extension(attribute_deprecated_with_message)
433#define $_(x) __attribute__((__deprecated__(x)))
434#elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))
435#define $_(X) __attribute__((__deprecated__))
436#else
437#define $_(X)
438#endif
439#ifdef ${_}has_extension
440#undef __has_extension
441#undef ${_}has_extension
442#endif
443#endif /* $_ */
444
445
446";
447    $public_h_trailer .= "#undef $_
448
449";
450    $private_h_trailer .= "#undef $_
451#define $_(X)
452
453";
454}
455
456$public_h_header .= $depstr;
457$private_h_header .= $depstr;
458
459
460if($flags{"cxx"}) {
461    $public_h_header .= "#ifdef __cplusplus
462extern \"C\" {
463#endif
464
465";
466    $public_h_trailer = "#ifdef __cplusplus
467}
468#endif
469
470" . $public_h_trailer;
471
472}
473if ($opt_E) {
474    $public_h_header .= "#ifndef $opt_E
475#ifndef ${opt_E}_FUNCTION
476#if defined(_WIN32)
477#define ${opt_E}_FUNCTION __declspec(dllimport)
478#define ${opt_E}_CALL __stdcall
479#define ${opt_E}_VARIABLE __declspec(dllimport)
480#else
481#define ${opt_E}_FUNCTION
482#define ${opt_E}_CALL
483#define ${opt_E}_VARIABLE
484#endif
485#endif
486#endif
487";
488
489    $private_h_header .= "#ifndef $opt_E
490#ifndef ${opt_E}_FUNCTION
491#if defined(_WIN32)
492#define ${opt_E}_FUNCTION __declspec(dllimport)
493#define ${opt_E}_CALL __stdcall
494#define ${opt_E}_VARIABLE __declspec(dllimport)
495#else
496#define ${opt_E}_FUNCTION
497#define ${opt_E}_CALL
498#define ${opt_E}_VARIABLE
499#endif
500#endif
501#endif
502
503";
504}
505
506$public_h_trailer .= $undepstr;
507$private_h_trailer .= $undepstr;
508
509if ($public_h ne "" && $flags{"header"}) {
510    $public_h = $public_h_header . $public_h .
511	$public_h_trailer . "#endif /* $block */\n";
512}
513if ($private_h ne "" && $flags{"header"}) {
514    $private_h = $private_h_header . $private_h .
515	$private_h_trailer . "#endif /* $private */\n";
516}
517
518if($opt_o) {
519    print OUT $public_h;
520}
521if($opt_p) {
522    print PRIV $private_h;
523}
524
525close OUT;
526close PRIV;
527