1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
|
#!/usr/bin/perl
# Configure.pm. Version 1.00 Copyright (C) 1995, Kenneth Albanowski
#
# You are welcome to use this code in your own perl modules, I just
# request that you don't distribute modified copies without making it clear
# that you have changed something. If you have a change you think is worth
# merging into the original, please contact me at kjahds@kjahds.com or
# CIS:70705,126
#
# $Id: Configure.pm,v 1.1 2016/05/07 16:03:09 espie Exp $
#
# Todo: clean up redudant code in CPP, Compile, Link, and Execute
#
# for when no_index is not enough
package
Configure;
use strict;
use vars qw(@EXPORT @ISA);
use Carp;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw( CPP
Compile
Link
Execute
FindHeader
FindLib
Apply
ApplyHeaders
ApplyLibs
ApplyHeadersAndLibs
ApplyHeadersAndLibsAndExecute
CheckHeader
CheckStructure
CheckField
CheckHSymbol
CheckSymbol
CheckLSymbol
GetSymbol
GetTextSymbol
GetNumericSymbol
GetConstants);
use Cwd;
use Config;
my ($C_usrinc, $C_libpth, $C_cppstdin, $C_cppflags, $C_cppminus,
$C_ccflags,$C_ldflags,$C_cc,$C_libs) =
@Config{qw( usrinc libpth cppstdin cppflags cppminus
ccflags ldflags cc libs)};
my $Verbose = 0;
=head1 NAME
Configure.pm - provide auto-configuration utilities
=head1 SUMMARY
This perl module provides tools to figure out what is present in the C
compilation environment. This is intended mostly for perl extensions to use
to configure themselves. There are a number of functions, with widely varying
levels of specificity, so here is a summary of what the functions can do:
CheckHeader: Look for headers.
CheckStructure: Look for a structure.
CheckField: Look for a field in a structure.
CheckHSymbol: Look for a symbol in a header.
CheckLSymbol: Look for a symbol in a library.
CheckSymbol: Look for a symbol in a header and library.
GetTextSymbol: Get the contents of a symbol as text.
GetNumericSymbol: Get the contents of a symbol as a number.
Apply: Try compiling code with a set of headers and libs.
ApplyHeaders: Try compiling code with a set of headers.
ApplyLibraries: Try linking code with a set of libraries.
ApplyHeadersAndLibaries: You get the idea.
ApplyHeadersAndLibariesAnExecute: You get the idea.
CPP: Feed some code through the C preproccessor.
Compile: Try to compile some C code.
Link: Try to compile & link some C code.
Execute: Try to compile, link, & execute some C code.
=head1 FUNCTIONS
=cut
# Here we go into the actual functions
=head2 CPP
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C preproccessor (that preproccessor being
previously determined by perl's Configure script.) Any additional arguments
provided are passed to the preprocessing command.
In a scalar context, the return value is either undef, if something went wrong,
or the text returned by the preprocessor. In an array context, two values are
returned: the numeric exit status and the output of the preproccessor.
=cut
sub CPP { # Feed code to preproccessor, returning error value and output
my($code,@options) = @_;
my($options) = join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = ($file.".c",$file.".o");
open(F,">$in");
print F $code;
close(F);
print "Preprocessing |$code|\n" if $Verbose;
my($result) = scalar(`$C_cppstdin $C_cppflags $C_cppminus $options < $in 2>/dev/null`);
print "Executing '$C_cppstdin $C_cppflags $C_cppminus $options < $in 2>/dev/null'\n" if $Verbose;
my($error) = $?;
print "Returned |$result|\n" if $Verbose;
unlink($in,$out);
return ($error ? undef : $result) unless wantarray;
($error,$result);
}
=head2 Compile
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C compiler (that compiler being
previously determined by perl's Configure script.) Any additional arguments
provided are passed to the compiler command.
In a scalar context, either 0 or 1 will be returned, with 1 indicating a
successful compilation. In an array context, three values are returned: the
numeric exit status of the compiler, a string consisting of the output
generated by the compiler, and a numeric value that is false if a ".o" file
wasn't produced by the compiler, error status or no.
=cut
sub Compile { # Feed code to compiler. On error, return status and text
my($code,@options) = @_;
my($options)=join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = ($file.".c",$file.".o");
open(F,">$in");
print F $code;
close(F);
print "Compiling |$code|\n" if $Verbose;
my($result) = scalar(`$C_cc $C_ccflags -c $in $C_ldflags $C_libs $options 2>&1`);
print "Executing '$C_cc $C_ccflags -c $in $C_ldflags $C_libs $options 2>&1'\n" if $Verbose;
my($error) = $?;
my($error2) = ! -e $out;
unlink($in,$out);
return (($error || $error2) ? 0 : 1) unless wantarray;
($error,$result,$error2);
}
=head2 Link
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C compiler and linker (that compiler and
linker being previously determined by perl's Configure script.) Any
additional arguments provided are passed to the compilation/link command.
In a scalar context, either 0 or 1 is returned, with 1 indicating a
successful compilation. In an array context, two values are returned: the
numeric exit status of the compiler/linker, and a string consisting of the
output generated by the compiler/linker.
Note that this command I<only> compiles and links the C code. It does not
attempt to execute it.
=cut
sub Link { # Feed code to compiler and linker. On error, return status and text
my($code,@options) = @_;
my($options) = join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = $file.".c",$file.".o";
open(F,">$in");
print F $code;
close(F);
print "Linking |$code|\n" if $Verbose;
my($result) = scalar(`$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1`);
print "Executing '$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1'\n" if $Verbose;
my($error)=$?;
print "Error linking: $error, |$result|\n" if $Verbose;
unlink($in,$out,$file);
return (($error || $result ne "")?0:1) unless wantarray;
($error,$result);
}
=head2 Execute
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C compiler and linker (that compiler and
linker being previously determined by perl's metaconfig script.) and then
executed. Any additional arguments provided are passed to the
compilation/link command. (There is no way to feed arguments to the program
being executed.)
In a scalar context, the return value is either undef, indicating the
compilation or link failed, or that the executed program returned a nonzero
status. Otherwise, the return value is the text output by the program.
In an array context, an array consisting of three values is returned: the
first value is 0 or 1, 1 if the compile/link succeeded. The second value either
the exist status of the compiler or program, and the third is the output text.
=cut
sub Execute { #Compile, link, and execute.
my($code,@options) = @_;
my($options)=join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = $file.".c",$file.".o";
open(F,">$in");
print F $code;
close(F);
print "Executing |$code|\n" if $Verbose;
my($result) = scalar(`$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1`);
print "Executing '$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1'\n" if $Verbose;
my($error) = $?;
unlink($in,$out);
if(!$error) {
my($result2) = scalar(`./$file`);
$error = $?;
unlink($file);
return ($error?undef:$result2) unless wantarray;
print "Executed successfully, status $error, link $result, exec |$result2|\n" if $Verbose;
(1,$error,$result2);
} else {
print "Link failed, status $error, message |$result|\n" if $Verbose;
return undef unless wantarray;
(0,$error,$result);
}
}
=head2 FindHeader
Takes an unlimited number of arguments, consisting of both header names in
the form "header.h", or directory specifications such as "-I/usr/include/bsd".
For each supplied header, FindHeader will attempt to find the complete path.
The return value is an array consisting of all the headers that were located.
=cut
sub FindHeader { #For each supplied header name, find full path
my(@headers) = grep(!/^-I/,@_);
my(@I) = grep(/^-I/,@_);
my($h);
for $h (@headers) {
print "Searching for $h... " if $Verbose;
if($h eq "") {$h=undef; next}
if( -f $h) {next}
if( -f $Config{"usrinc"}."/".$h) {
$h = $Config{"usrinc"}."/".$h;
print "Found as $h.\n" if $Verbose;
} else {
my $text;
if($text = CPP("#include <$h>",join(" ",@I))) {
grepcpp:
for (split(/\s+/,(grep(/^\s*#.*$h/,split(/\n/,$text)))[0])) {
if(/$h/) {
s/^\"(.*)\"$/$1/;
s/^\'(.*)\'$/$1/;
$h = $_;
print "Found as $h.\n" if $Verbose;
last grepcpp;
}
}
} else {
$h = undef; # remove header from resulting list
print "Not found.\n" if $Verbose;
}
}
}
grep($_,@headers);
}
=head2 FindLib
Takes an unlimited number of arguments, consisting of both library names in
the form "-llibname", "/usr/lib/libxyz.a" or "dld", or directory
specifications such as "-L/usr/lib/foo". For each supplied library, FindLib
will attempt to find the complete path. The return value is an array
consisting of the full paths to all of the libraries that were located.
=cut
sub FindLib { #For each supplied library name, find full path
my(@libs) = grep(!/^-L/,@_);
my(@L) = (grep(/^-L/,@_),split(" ",$Config{"libpth"}));
grep(s/^-L//,@L);
my($l);
my($so) = $Config{"so"};
my($found);
#print "Libaries I am searching for: ",join(",",@libs),"\n";
#print "Directories: ",join(",",@L),"\n";
my $lib;
for $lib (@libs) {
print "Searching for $lib... " if $Verbose;
$found=0;
$lib =~ s/^-l//;
if($lib eq "") {$lib=undef; next}
next if -f $lib;
my $path;
for $path (@L) {
my ( $fullname, @fullname );
print "Searching $path for $lib...\n" if $Verbose;
if (@fullname=<${path}/lib${lib}.${so}.[0-9]*>){
$fullname=$fullname[-1]; #ATTN: 10 looses against 9!
} elsif (-f ($fullname="$path/lib$lib.$so")){
} elsif (-f ($fullname="$path/lib${lib}_s.a")
&& ($lib .= "_s") ){ # we must explicitly ask for _s version
} elsif (-f ($fullname="$path/lib$lib.a")){
} elsif (-f ($fullname="$path/Slib$lib.a")){
} else {
warn "$lib not found in $path\n" if $Verbose;
next;
}
warn "'-l$lib' found at $fullname\n" if $Verbose;
$lib = $fullname;
$found=1;
}
if(!$found) {
$lib = undef; # Remove lib if not found
print "Not found.\n" if $Verbose;
}
}
grep($_,@libs);
}
=head2
Apply takes a chunk of code, a series of libraries and headers, and attempts
to apply them, in series, to a given perl command. In a scalar context, the
return value of the first set of headers and libraries that produces a
non-zero return value from the command is returned. In an array context, the
header and library set it returned.
This is best explained by some examples:
Apply(\&Compile,"main(){}","sgtty.h","");
In a scalar context either C<undef> or C<1>. In an array context,
this returns C<()> or C<("sgtty.h","")>.
Apply(\&Link,"main(){int i=COLOR_PAIRS;}","curses.h","-lcurses",
"ncurses.h","-lncurses","ncurses/ncurses.h","-lncurses");
In a scalar context, this returns either C<undef>, C<1>. In an array context,
this returns C<("curses.h","-lcurses")>, C<("ncurses.h","-lncurses")>,
C<("ncurses/ncurses.h","-lncurses")>, or C<()>.
If we had instead said
C<Apply(\&Execute,'main(){printf("%d",(int)COLOR_PAIRS)',...)> then in a scalar
context either C<undef> or the value of COLOR_PAIRS would be returned.
Note that you can also supply multiple headers and/or libraries at one time,
like this:
Apply(\&Compile,"main(){fcntl(0,F_GETFD);}","fcntl.h","",
"ioctl.h fcntl.h","","sys/ioctl.h fcntl.h"","");
So if fcntl needs ioctl or sys/ioctl loaded first, this will catch it. In an
array context, C<()>, C<("fcntl.h","")>, C<("ioctl.h fcntl.h","")>, or
C<("sys/ioctl.h fcntl.h","")> could be returned.
You can also use nested arrays to get exactly the same effect. The returned
array will always consist of a string, though, with elements separated by
spaces.
Apply(\&Compile,"main(){fcntl(0,F_GETFD);}",["fcntl.h"],"",
["ioctl.h","fcntl.h"],"",["sys/ioctl.h","fcntl.h"],"");
Note that there are many functions that provide simpler ways of doing these
things, from GetNumericSymbol to get the value of a symbol, to ApplyHeaders
which doesn't ask for libraries.
=cut
sub Apply { #
my($cmd,$code,@lookup) = @_;
my(@l,@h,$i,$ret);
for ($i=0;$i<@lookup;$i+=2) {
if( ref($lookup[$i]) eq "ARRAY" ) {
@h = @{$lookup[$i]};
} else {
@h = split(/\s+/,$lookup[$i]);
}
if( ref($lookup[$i+1]) eq "ARRAY" ) {
@l = @{$lookup[$i+1]};
} else {
@l = split(/\s+/,$lookup[$i+1]);
}
if($ret=&{$cmd == \&Link && !@l?\&Compile:$cmd}(join("",map($_?"#include <$_>\n":"",grep(!/^-I/,@h))).
$code,grep(/^-I/,@h),@l)) {
print "Ret=|$ret|\n" if $Verbose;
return $ret unless wantarray;
return (join(" ",@h),join(" ",@l));
}
}
return 0 unless wantarray;
();
}
=head2 ApplyHeadersAndLibs
This function takes the same sort of arguments as Apply, it just sends them
directly to Link.
=cut
sub ApplyHeadersAndLibs { #
my($code,@lookup) = @_;
Apply \&Link,$code,@lookup;
}
=head2 ApplyHeadersAndLibsAndExecute
This function is similar to Apply and ApplyHeadersAndLibs, but it always
uses Execute.
=cut
sub ApplyHeadersAndLibsAndExecute { #
my($code,@lookup) = @_;
Apply \&Execute,$code,@lookup;
}
=head2 ApplyHeaders
If you are only checking headers, and don't need to look at libs, then
you will probably want to use ApplyHeaders. The return value is the same
in a scalar context, but in an array context the returned array will only
consists of the headers, spread out.
=cut
sub ApplyHeaders {
my($code,@headers) = @_;
return scalar(ApplyHeadersAndLibs $code, map(($_,""),@headers))
unless wantarray;
split(/\s+/,(ApplyHeadersAndLibs $code, map(($_,""),@headers))[0]);
}
=head2 ApplyLibs
If you are only checking libraries, and don't need to look at headers, then
you will probably want to use ApplyLibs. The return value is the same
in a scalar context, but in an array context the returned array will only
consists of the libraries, spread out.
=cut
sub ApplyLibs {
my($code,@libs) = @_;
return scalar(ApplyHeadersAndLibs $code, map(("",$_),@libs))
unless wantarray;
split(/\s+/,(ApplyHeadersAndLibs $code, map(("",$_),@libs))[0]);
}
=head2 CheckHeader
Takes an unlimited number of arguments, consiting of headers in the
Apply style. The first set that is fully accepted
by the compiler is returned.
=cut
sub CheckHeader { #Find a header (or set of headers) that exists
ApplyHeaders("main(){}",@_);
}
=head2 CheckStructure
Takes the name of a structure, and an unlimited number of further arguments
consisting of header groups. The first group that defines that structure
properly will be returned. B<undef> will be returned if nothing succeeds.
=cut
sub CheckStructure { # Check existance of a structure.
my($structname,@headers) = @_;
ApplyHeaders("main(){ struct $structname s;}",@headers);
}
=head2 CheckField
Takes the name of a structure, the name of a field, and an unlimited number
of further arguments consisting of header groups. The first group that
defines a structure that contains the field will be returned. B<undef> will
be returned if nothing succeeds.
=cut
sub CheckField { # Check for the existance of specified field in structure
my($structname,$fieldname,@headers) = @_;
ApplyHeaders("main(){ struct $structname s1; struct $structname s2;
s1.$fieldname = s2.$fieldname; }",@headers);
}
=head2 CheckLSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of library groups. The first group of libraries that defines
that symbol will be returned. B<undef> will be returned if nothing succeeds.
=cut
sub CheckLSymbol { # Check for linkable symbol
my($symbol,@libs) = @_;
ApplyLibs("main() { void * f = (void *)($symbol); }",@libs);
}
=head2 CheckSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header and library groups, in the Apply format. The first
group of headers and libraries that defines that symbol will be returned.
B<undef> will be returned if nothing succeeds.
=cut
sub CheckSymbol { # Check for linkable/header symbol
my($symbol,@lookup) = @_;
ApplyHeadersAndLibs("main() { void * f = (void *)($symbol); }",@lookup);
}
=head2 CheckHSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header groups. The first group of headers that defines
that symbol will be returned. B<undef> will be returned if nothing succeeds.
=cut
sub CheckHSymbol { # Check for header symbol
my($symbol,@headers) = @_;
ApplyHeaders("main() { void * f = (void *)($symbol); }",@headers);
}
=head2 CheckHPrototype (unexported)
An experimental routine that takes a name of a function, a nested array
consisting of the prototype, and then the normal header groups. It attempts
to deduce whether the given prototype matches what the header supplies.
Basically, it doesn't work. Or maybe it does. I wouldn't reccomend it,
though.
=cut
sub CheckHPrototype { # Check for header prototype.
# Note: This function is extremely picky about "const int" versus "int",
# and depends on having an extremely snotty compiler. Anything but GCC
# may fail, and even GCC may not work properly. In any case, if the
# names function doesn't exist, this call will _succeed_. Caveat Utilitor.
my($function,$proto,@headers) = @_;
my(@proto) = @{$proto};
ApplyHeaders("main() { extern ".$proto[0]." $function(".
join(",",@proto[1..$#proto])."); }",@headers);
}
=head2 GetSymbol
Takes the name of a symbol, a printf command, a cast, and an unlimited
number of further arguments consisting of header and library groups, in the
Apply. The first group of headers and libraries that defines that symbol
will be used to get the contents of the symbol in the format, and return it.
B<undef> will be returned if nothing defines that symbol.
Example:
GetSymbol("__LINE__","ld","long","","");
=cut
sub GetSymbol { # Check for linkable/header symbol
my($symbol,$printf,$cast,@lookup) = @_,"","";
scalar(ApplyHeadersAndLibsAndExecute(
"main(){ printf(\"\%$printf\",($cast)($symbol));exit(0);}",@lookup));
}
=head2 GetTextSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header and library groups, in the ApplyHeadersAndLibs format.
The first group of headers and libraries that defines that symbol will be
used to get the contents of the symbol in text format, and return it.
B<undef> will be returned if nothing defines that symbol.
Note that the symbol I<must> actually be text, either a char* or a constant
string. Otherwise, the results are undefined.
=cut
sub GetTextSymbol { # Check for linkable/header symbol
my($symbol,@lookup) = @_,"","";
my($result) = GetSymbol($symbol,"s","char*",@lookup);
$result .= "" if defined($result);
$result;
}
=head2 GetNumericSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header and library groups, in the ApplyHeadersAndLibs format.
The first group of headers and libraries that defines that symbol will be
used to get the contents of the symbol in numeric format, and return it.
B<undef> will be returned if nothing defines that symbol.
Note that the symbol I<must> actually be numeric, in a format compatible
with a float. Otherwise, the results are undefined.
=cut
sub GetNumericSymbol { # Check for linkable/header symbol
my($symbol,@lookup) = @_,"","";
my($result) = GetSymbol($symbol,"f","float",@lookup);
$result += 0 if defined($result);
$result;
}
=head2 GetConstants
Takes a list of header names (possibly including -I directives) and attempts
to grep the specified files for constants, a constant being something #defined
with a name that matches /[A-Z0-9_]+/. Returns the list of names.
=cut
sub GetConstants { # Try to grep constants out of a header
my(@headers) = @_;
@headers = FindHeader(@headers);
my %seen;
my(%results);
map($seen{$_}=1,@headers);
while(@headers) {
$_=shift(@headers);
next if !defined($_);
open(SEARCHHEADER,"<$_");
while(<SEARCHHEADER>) {
if(/^\s*#\s*define\s+([A-Z_][A-Za-z0-9_]+)\s+/) {
$results{$1} = 1;
} elsif(/^\s*#\s*include\s+[<"]?([^">]+)[>"]?/) {
my(@include) = FindHeader($1);
@include = grep(!$seen{$_},map(defined($_)?$_:(),@include));
push(@headers,@include);
map($seen{$_}=1,@include);
}
}
close(SEARCHHEADER);
}
keys %results;
}
=head2 DeducePrototype (unexported)
This one is B<really> experimental. The idea is to figure out some basic
characteristics of the compiler, and then attempt to "feel out" the prototype
of a function. Eventually, it may work. It is guaranteed to be very slow,
and it may simply not be capable of working on some systems.
=cut
my $firstdeduce = 1;
sub DeducePrototype {
my (@types, $checkreturn, $checknilargs, $checkniletcargs, $checkreturnnil);
if($firstdeduce) {
$firstdeduce=0;
my $checknumber=!Compile("extern int func(int a,int b);
extern int func(int a,int b,int c);
main(){}");
$checkreturn=!Compile("extern int func(int a,int b);
extern long func(int a,int b);
main(){}");
my $checketc= !Compile("extern int func(int a,int b);
extern long func(int a,...);
main(){}");
my $checknumberetc=!Compile("extern int func(int a,int b);
extern int func(int a,int b,...);
main(){}");
my $checketcnumber=!Compile("extern int func(int a,int b,int c,...);
extern int func(int a,int b,...);
main(){}");
my $checkargtypes=!Compile("extern int func(int a);
extern int func(long a);
main(){}");
my $checkargsnil=!Compile("extern int func();
extern int func(int a,int b,int c);
main(){}");
$checknilargs=!Compile("extern int func(int a,int b,int c);
extern int func();
main(){}");
my $checkargsniletc=!Compile("extern int func(...);
extern int func(int a,int b,int c);
main(){}");
$checkniletcargs=!Compile("extern int func(int a,int b,int c);
extern int func(...);
main(){}");
my $checkconst=!Compile("extern int func(const int * a);
extern int func(int * a);
main(){ }");
my $checksign=!Compile("extern int func(int a);
extern int func(unsigned int a);
main(){ }");
$checkreturnnil=!Compile("extern func(int a);
extern void func(int a);
main(){ }");
@types = sort grep(Compile("main(){$_ a;}"),
"void","int","long int","unsigned int","unsigned long int","long long int",
"long long","unsigned long long",
"unsigned long long int","float","long float",
"double","long double",
"char","unsigned char","short int","unsigned short int");
if(Compile("main(){flurfie a;}")) { @types = (); }
$Verbose=0;
# Attempt to remove duplicate types (if any) from type list
my ( $i, $j );
if($checkargtypes) {
for ($i=0;$i<=$#types;$i++) {
for ($j=$i+1;$j<=$#types;$j++) {
next if $j==$i;
if(Compile("extern void func($types[$i]);
extern void func($types[$j]); main(){}")) {
print "Removing type $types[$j] because it equals $types[$i]\n";
splice(@types,$j,1);
$j--;
}
}
}
} elsif($checkreturn) {
for ($i=0;$i<=$#types;$i++) {
for ($j=$i+1;$j<=$#types;$j++) {
next if $j==$i;
if(Compile("$types[$i] func(void);
extern $types[$j] func(void); main(){}")) {
print "Removing type $types[$j] because it equals $types[$i]\n";
splice(@types,$j,1);
$j--;
}
}
}
}
$Verbose=1;
print "Detect differing numbers of arguments: $checknumber\n";
print "Detect differing return types: $checkreturn\n";
print "Detect differing argument types if one is ...: $checketc\n";
print "Detect differing numbers of arguments if ... is involved: $checknumberetc\n";
print "Detect differing numbers of arguments if ... is involved #2: $checketcnumber\n";
print "Detect differing argument types: $checkargtypes\n";
print "Detect differing argument types if first has no defined args: $checkargsnil\n";
print "Detect differing argument types if second has no defined args: $checknilargs\n";
print "Detect differing argument types if first has only ...: $checkargsniletc\n";
print "Detect differing argument types if second has only ...: $checkniletcargs\n";
print "Detect differing argument types by constness: $checkconst\n";
print "Detect differing argument types by signedness: $checksign\n";
print "Detect differing return types if one is not defined: $checkreturnnil\n";
print "Types known: ",join(",",@types),"\n";
}
my($function,@headers) = @_;
@headers = CheckHSymbol($function,@headers);
return undef if !@headers;
my $rettype = undef;
my @args = ();
my @validcount = ();
# Can we check the return type without worry about arguements?
if($checkreturn and (!$checknilargs or !$checkniletcargs)) {
for (@types) {
if(ApplyHeaders("extern $_ $function(". ($checknilargs?"...":"").");main(){}",[@headers])) {
$rettype = $_; # Great, we found the return type.
last;
}
}
}
if(!defined($rettype) and $checkreturnnil) {
die "No way to deduce function prototype in a rational amount of time";
}
my $numargs=-1;
my $varargs=0;
for (0..32) {
if(ApplyHeaders("main(){ $function(".join(",",("0") x $_).");}",@headers)) {
$numargs=$_;
if(ApplyHeaders("main(){ $function(".join(",",("0") x ($_+1)).");}",@headers)) {
$varargs=1;
}
last
}
}
die "Unable to deduce number of arguments" if $numargs==-1;
if($varargs) { $args[$numargs]="..."; }
# OK, now we know how many arguments the thing takes.
if(@args>0 and !defined($rettype)) {
for (@types) {
if(defined(ApplyHeaders("extern $_ $function(".join(",",@args).");main(){}",[@headers]))) {
$rettype = $_; # Great, we found the return type.
last;
}
}
}
print "Return type: $rettype\nArguments: ",join(",",@args),"\n";
print "Valid number of arguments: $numargs\n";
print "Accepts variable number of args: $varargs\n";
}
#$Verbose=1;
#print scalar(join("|",CheckHeader("sgtty.h"))),"\n";
#print scalar(join("|",FindHeader(CheckHeader("sgtty.h")))),"\n";
#print scalar(join("|",CheckSymbol("COLOR_PAIRS","curses.h","-lcurses","ncurses.h","-lncurses","ncurses/ncurses.h","ncurses/libncurses.a"))),"\n";
#print scalar(join("|",GetNumericSymbol("PRIO_USER","sys/resource.h",""))),"\n";
|