#!/usr/bin/perl -w ################################################################################ # # apicheck.pl -- generate C source for automated API check # ################################################################################ # # $Revision: 37 $ # $Author: mhx $ # $Date: 2010/03/07 13:15:43 +0100 $ # ################################################################################ # # Version 3.x, Copyright (C) 2004-2010, Marcus Holland-Moritz. # Version 2.x, Copyright (C) 2001, Paul Marquess. # Version 1.x, Copyright (C) 1999, Kenneth Albanowski. # # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # ################################################################################ use strict; require 'parts/ppptools.pl'; if (@ARGV) { my $file = pop @ARGV; open OUT, ">$file" or die "$file: $!\n"; } else { *OUT = \*STDOUT; } my @f = parse_embed(qw( parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc )); my %todo = %{&parse_todo}; my %tmap = ( void => 'int', ); my %amap = ( SP => 'SP', type => 'int', cast => 'int', ); my %void = ( void => 1, Free_t => 1, Signal_t => 1, ); my %castvoid = ( map { ($_ => 1) } qw( Nullav Nullcv Nullhv Nullch Nullsv HEf_SVKEY SP MARK SVt_PV SVt_IV SVt_NV SVt_PVMG SVt_PVAV SVt_PVHV SVt_PVCV SvUOK G_SCALAR G_ARRAY G_VOID G_DISCARD G_EVAL G_NOARGS XS_VERSION ), ); my %ignorerv = ( map { ($_ => 1) } qw( newCONSTSUB ), ); my %stack = ( ORIGMARK => ['dORIGMARK;'], POPpx => ['STRLEN n_a;'], POPpbytex => ['STRLEN n_a;'], PUSHp => ['dTARG;'], PUSHn => ['dTARG;'], PUSHi => ['dTARG;'], PUSHu => ['dTARG;'], XPUSHp => ['dTARG;'], XPUSHn => ['dTARG;'], XPUSHi => ['dTARG;'], XPUSHu => ['dTARG;'], UNDERBAR => ['dUNDERBAR;'], XCPT_TRY_START => ['dXCPT;'], XCPT_TRY_END => ['dXCPT;'], XCPT_CATCH => ['dXCPT;'], XCPT_RETHROW => ['dXCPT;'], ); my %ignore = ( map { ($_ => 1) } qw( svtype items ix dXSI32 XS CLASS THIS RETVAL StructCopy ), ); print OUT < 0) } @ARGV; @f = grep { exists $want{$_->{name}} } @f; for (@f) { $want{$_->{name}}++ } for (keys %want) { die "nothing found for '$_'\n" unless $want{$_}; } } my $f; for $f (@f) { $ignore{$f->{name}} and next; $f->{flags}{A} or next; # only public API members $ignore{$f->{name}} = 1; # ignore duplicates my $Perl_ = $f->{flags}{p} ? 'Perl_' : ''; my $stack = ''; my @arg; my $aTHX = ''; my $i = 1; my $ca; my $varargs = 0; for $ca (@{$f->{args}}) { my $a = $ca->[0]; if ($a eq '...') { $varargs = 1; push @arg, qw(VARarg1 VARarg2 VARarg3); last; } my($n, $p, $d) = $a =~ /^ (\w+(?:\s+\w+)*)\s* # type name => $n (\**) # pointer => $p (?:\s*const\s*)? # const ((?:\[[^\]]*\])*) # dimension => $d $/x or die "$0 - cannot parse argument: [$a]\n"; if (exists $amap{$n}) { push @arg, $amap{$n}; next; } $n = $tmap{$n} || $n; if ($n eq 'const char' and $p eq '*' and !$f->{flags}{f}) { push @arg, '"foo"'; } else { my $v = 'arg' . $i++; push @arg, $v; $stack .= " static $n $p$v$d;\n"; } } unless ($f->{flags}{n} || $f->{flags}{'m'}) { $stack = " dTHX;\n$stack"; $aTHX = @arg ? 'aTHX_ ' : 'aTHX'; } if ($stack{$f->{name}}) { my $s = ''; for (@{$stack{$f->{name}}}) { $s .= " $_\n"; } $stack = "$s$stack"; } my $args = join ', ', @arg; my $rvt = $f->{ret} || 'void'; my $ret; if ($void{$rvt}) { $ret = $castvoid{$f->{name}} ? '(void) ' : ''; } else { $stack .= " $rvt rval;\n"; $ret = $ignorerv{$f->{name}} ? '(void) ' : "rval = "; } my $aTHX_args = "$aTHX$args"; if (!$f->{flags}{'m'} or $f->{flags}{'b'} or @arg > 0) { $args = "($args)"; $aTHX_args = "($aTHX_args)"; } print OUT <{name} * ******************************************************************************/ HEAD if ($todo{$f->{name}}) { my($ver,$sub) = $todo{$f->{name}} =~ /^5\.(\d{3})(\d{3})$/ or die; for ($ver, $sub) { s/^0+(\d)/$1/ } if ($ver < 6 && $sub > 0) { $sub =~ s/0$// or die; } print OUT "#if PERL_VERSION > $ver || (PERL_VERSION == $ver && PERL_SUBVERSION >= $sub) /* TODO */\n"; } my $final = $varargs ? "$Perl_$f->{name}$aTHX_args" : "$f->{name}$args"; $f->{cond} and print OUT "#if $f->{cond}\n"; print OUT <{name} (void) { dXSARGS; $stack { #ifdef $f->{name} $ret$f->{name}$args; #endif } { #ifdef $f->{name} $ret$final; #else $ret$Perl_$f->{name}$aTHX_args; #endif } } END $f->{cond} and print OUT "#endif\n"; $todo{$f->{name}} and print OUT "#endif\n"; print OUT "\n"; } @ARGV and close OUT;