# -*- perl -*-
use Test::More tests => 11;
use C::DynaLib ();
# optional dependency
eval "use sigtrap;";
ok(1);
sub goof {
require Carp;
Carp::confess "Illegal memory operation";
}
eval {
$SIG{SEGV} = \&goof;
$SIG{ILL} = \&goof;
};
use vars qw ($tmp1 $tmp2);
use Config;
# Don't let old Exporters ruin our fun.
sub DeclareSub { &C::DynaLib::DeclareSub }
sub PTR_TYPE () { &C::DynaLib::PTR_TYPE }
my $libc = new C::DynaLib($Config{'libc'} || "-lc");
if (! $libc) {
if ($^O =~ /linux/i) {
# Some glibc versions install "libc.so" as a linker script,
# unintelligible to dlopen().
$libc = new C::DynaLib("libc.so.6");
}
}
if (! $libc) {
ok(0, "no libc"); #2
die "Can't load -lc: ", DynaLoader::dl_error(), "\nGiving up.\n";
}
my $libm_arg = DynaLoader::dl_findfile("-lm");
my $libm;
if (! $libm_arg) {
$libm = $libc;
} elsif ($^O eq 'cygwin') {
$libm = $libc;
} else {
$libm = new C::DynaLib("-lm");
}
$libm and $pow = $libm->DeclareSub ({ "name" => "pow",
"return" => "d",
"args" => ["d", "d"]});
SKIP: {
skip "math lib tests. $pow $C::DynaLib::decl", 1
if !$pow or $C::DynaLib::decl eq 'hack30';
my $sqrt2 = 2**0.5;
ok(&$pow(2, 0.5) == $sqrt2, "pow(2, 0.5) from -lm"); #2
}
my $strlen = $libc->DeclareSub ({ "name" => "strlen",
"return" => "i",
"args" => ["p"],
});
# Can't do this in perl <= 5.00401 because it results in a
# pack("p", constant):
#
# $len = &$strlen("oof rab zab");
my $len = &$strlen($tmp = "oof rab zab");
ok($len == 11, "len == 11, got: $len"); #3
sub my_sprintf {
my ($fmt, @args) = @_;
my (@arg_types) = ("P", "p");
my ($width) = (length($fmt) + 1);
# note this is a *simplified* (non-crash-proof) printf parser!
while ($fmt =~ m/(?:%[-\#0 +\']*\d*(?:\.\d*)?h?(.).*?)[^%]*/g) {
my $spec = $1;
next if $spec eq "%";
if (index("dic", $spec) > -1) {
push @arg_types, "i";
$width += 20;
} elsif (index("ouxXp", $spec) > -1) {
push @arg_types, "I";
$width += 20;
} elsif (index("eEfgG", $spec) > -1) {
push @arg_types, "d";
$width += 30;
} elsif ("s" eq $spec) {
push @arg_types, "p";
$width += length($args[$#arg_types]);
} else {
die "Unknown printf specifier: $spec\n";
}
}
my $buffer = "\0" x $width;
&{$libc->DeclareSub("sprintf", "", @arg_types)}
($buffer, $fmt, @args);
$buffer =~ s/\0.*//;
return $buffer;
}
my $fmt = "%x %10sfoo %d %10.7g %f %d %d %d";
my @args = (253, "bar", -789, 2.32578, 3.14, 5, 6, 7);
my $expected = sprintf($fmt, @args);
my $got = my_sprintf($fmt, @args);
ok($got eq $expected, "expected: $expected"); #4
my $ptr_len = length(pack("p", $tmp = "foo"));
# Try passing a pointer to DeclareSub.
my $fopen_ptr = DynaLoader::dl_find_symbol($libc->LibRef(), "fopen")
or die DynaLoader::dl_error();
my $fopen = DeclareSub ({ "ptr" => $fopen_ptr,
"return" => PTR_TYPE,
"args" => ["p", "p"] });
open TEST, ">tmp.tmp"
or die "Can't write file tmp.tmp: $!\n";
print TEST "a string";
close TEST;
# Can't do &$fopen("tmp.tmp", "r") in perls before 5.00402.
my $fp = &$fopen($tmp1 = "tmp.tmp", $tmp2 = "r");
if (! $fp) {
ok(0, q(Can't do &$fopen("tmp.tmp", "r") in perls before 5.00402.)); #5
} else {
# Hope "I" will work for type size_t!
my $fread = $libc->DeclareSub("fread", "i",
"P", "I", "I", PTR_TYPE);
my $buffer = "\0" x 4;
my $result = &$fread($buffer, 1, length($buffer), $fp);
ok($result == 4); #5
ok($buffer eq "a st"); #6
}
unlink "tmp.tmp";
if (@$C::DynaLib::Callback::Config) {
sub compare_lengths {
length(unpack("p", $_[0])) <=> length(unpack("p", $_[1]));
}
my @list = qw(A bunch of elements with unique lengths);
my $array = pack("p*", @list);
my $callback = new C::DynaLib::Callback("compare_lengths", "i",
"P$ptr_len", "P$ptr_len");
my $qsort = $libc->DeclareSub("qsort", "",
"P", "I", "I", PTR_TYPE);
&$qsort($array, scalar(@list), length($array) / @list, $callback->Ptr());
my @expected = sort { length($a) <=> length($b) } @list;
my @got = unpack("p*", $array);
ok("[@got]" eq "[@expected]"); #7
# Hey! We've got callbacks. We've got a way to call them.
# Who needs libraries?
undef $callback;
$callback = new C::DynaLib::Callback
(sub {
$_[0] + 10*$_[1] + 100*$_[2];
}, "i", "i", "p", "i");
my $sub = DeclareSub($callback->Ptr(), "i", "i", "p", "i");
my $got = &$sub(1, $tmp = 7, 3.14);
my $expected = 371;
ok($got == $expected); #8
undef $callback;
$callback = new C::DynaLib::Callback(sub { shift }, "I", "i");
$sub = DeclareSub($callback->Ptr(), "I", "i");
$got = &$sub(-1);
# Can't do this generally because it's broken in too many Perl versions:
if (0) { # TODO: needed for an earlier version
$expected = unpack("I", pack("i", -1));
} else {
$expected = 0;
for ($i = 1; $i > 0; $i <<= 1) {
$expected += $i;
}
$expected -= $i;
}
ok($got == $expected, "Callback Ii $got == $expected"); #9
my $int_size = length(pack("i",0));
undef $callback;
$callback = new C::DynaLib::Callback
(sub {
$global = shift;
$global .= pack("i", shift);
return unpack(PTR_TYPE, pack("P", $global));
}, PTR_TYPE, "P".(2 * $int_size), "i");
$sub = DeclareSub($callback->Ptr(), "P".(3 * $int_size), PTR_TYPE, "i");
$array = pack("ii", 1729, 31415);
$pointer = unpack(PTR_TYPE, pack("P", $array));
$struct = &$sub($pointer, 253);
@got = unpack("iii", $struct);
ok("[@got]" eq "[1729 31415 253]"); #10
} else {
print ("# Skipping callback tests on this platform\n");
}
my $buf = "willo";
C::DynaLib::Poke(unpack(PTR_TYPE, pack("p", $buf)), "he");
ok($buf eq "hello"); #11