#!./miniperl=head1 NAMExsubpp - compiler to convert Perl XS code into C code=head1 SYNOPSISB<xsubpp> [B<-v>] [B<-C++>] [B<-except>] [B<-s pattern>] [B<-prototypes>] [B<-noversioncheck>] [B<-typemap typemap>]... file.xs=head1 DESCRIPTIONI<xsubpp> will compile XS code into C code by embedding the constructsnecessary to let C functions manipulate Perl values and creates the gluenecessary to let Perl access those functions.  The compiler uses typemaps todetermine how to map C function parameters and variables to Perl values.The compiler will search for typemap files called I<typemap>.  It will usethe following search path to find default typemaps, with the rightmosttypemap taking precedence.	../../../typemap:../../typemap:../typemap:typemap=head1 OPTIONS=over 5=item B<-C++>Adds ``extern "C"'' to the C code.=item B<-except>Adds exception handling stubs to the C code.=item B<-typemap typemap>Indicates that a user-supplied typemap should take precedence over thedefault typemaps.  This option may be used multiple times, with the lasttypemap having the highest precedence.=item B<-v>Prints the I<xsubpp> version number to standard output, then exits.=item B<-prototypes>By default I<xsubpp> will not automatically generate prototype code forall xsubs. This flag will enable prototypes.=item B<-noversioncheck>Disables the run time test that determines if the object file (derivedfrom the C<.xs> file) and the C<.pm> files have the same versionnumber.=back=head1 ENVIRONMENTNo environment variables are used.=head1 AUTHORLarry Wall=head1 MODIFICATION HISTORYSee the file F<changes.pod>.=head1 SEE ALSOperl(1), perlxs(1), perlxstut(1), perlapi(1)=cut# Global Constants$XSUBPP_version = "1.935";require 5.002;use vars '$cplusplus';sub Q ;$FH = 'File0000' ;$usage = "Usage: xsubpp [-v] [-C++] [-except] [-prototypes] [-noversioncheck] [-s pattern] [-typemap typemap]... file.xs\n";$proto_re = "[" . quotemeta('\$%&*@;') . "]" ;$except = "";$WantPrototypes = -1 ;$WantVersionChk = 1 ;$ProtoUsed = 0 ;SWITCH: while (@ARGV and $ARGV[0] =~ /^-./) {    $flag = shift @ARGV;    $flag =~ s/^-// ;    $spat = shift,	next SWITCH	if $flag eq 's';    $cplusplus = 1,	next SWITCH	if $flag eq 'C++';    $WantPrototypes = 0, next SWITCH	if $flag eq 'noprototypes';    $WantPrototypes = 1, next SWITCH	if $flag eq 'prototypes';    $WantVersionChk = 0, next SWITCH	if $flag eq 'noversioncheck';    $WantVersionChk = 1, next SWITCH	if $flag eq 'versioncheck';    $except = " TRY",	next SWITCH	if $flag eq 'except';    push(@tm,shift),	next SWITCH	if $flag eq 'typemap';    (print "xsubpp version $XSUBPP_version\n"), exit  		if $flag eq 'v';    die $usage;}if ($WantPrototypes == -1)  { $WantPrototypes = 0}else  { $ProtoUsed = 1 }@ARGV == 1 or die $usage;($dir, $filename) = $ARGV[0] =~ m#(.*)/(.*)#	or ($dir, $filename) = $ARGV[0] =~ m#(.*[>\]])(.*)#	or ($dir, $filename) = ('.', $ARGV[0]);chdir($dir);# Check for VMS; Config.pm may not be installed yet, but this routine# is built into VMS perlif (defined(&VMS::Filespec::vmsify)) { $Is_VMS = 1; $pwd = $ENV{DEFAULT}; }else                                 { $Is_VMS = 0; chomp($pwd = `pwd`);   }$Is_MacOS = $^O eq 'MacOS';++ $IncludedFiles{$ARGV[0]} ;my(@XSStack) = ({type => 'none'});	# Stack of conditionals and INCLUDEsmy($XSS_work_idx, $cpp_next_tmp) = (0, "XSubPPtmpAAAA");sub TrimWhitespace{    $_[0] =~ s/^\s+|\s+$//go ;}sub TidyType{    local ($_) = @_ ;    # rationalise any '*' by joining them into bunches and removing whitespace    s#\s*(\*+)\s*#$1#g;    s#(\*+)# $1 #g ;    # change multiple whitespace into a single space    s/\s+/ /g ;        # trim leading & trailing whitespace    TrimWhitespace($_) ;    $_ ;}$typemap = shift @ARGV;foreach $typemap (@tm) {    die "Can't find $typemap in $pwd\n" unless -r $typemap;}if ($Is_MacOS) {    unshift @tm, qw(:::::lib:ExtUtils:typemap ::::lib:ExtUtils:typemap                :::lib:ExtUtils:typemap ::::Mac:typemap :::Mac:typemap 		::Mac:typemap ::::typemap :::typemap                ::typemap typemap);} else {    unshift @tm, qw(../../../../lib/ExtUtils/typemap ../../../lib/ExtUtils/typemap                ../../lib/ExtUtils/typemap ../../../typemap ../../typemap                ../typemap typemap);}foreach $typemap (@tm) {    next unless -e $typemap ;    # skip directories, binary files etc.    warn("Warning: ignoring non-text typemap file '$typemap'\n"), next 	unless -T $typemap ;    open(TYPEMAP, $typemap) 	or warn ("Warning: could not open typemap file '$typemap': $!\n"), next;    $mode = 'Typemap';    $junk = "" ;    $current = \$junk;    while (<TYPEMAP>) {	next if /^\s*#/;	if (/^INPUT\s*$/)   { $mode = 'Input';   $current = \$junk;  next; }	if (/^OUTPUT\s*$/)  { $mode = 'Output';  $current = \$junk;  next; }	if (/^TYPEMAP\s*$/) { $mode = 'Typemap'; $current = \$junk;  next; }	if ($mode eq 'Typemap') {	    chomp;	    my $line = $_ ;            TrimWhitespace($_) ;	    # skip blank lines and comment lines	    next if /^$/ or /^#/ ;	    my($type,$kind, $proto) = /^\s*(.*?\S)\s+(\S+)\s*($proto_re*)\s*$/ or		warn("Warning: File '$typemap' Line $. '$line' TYPEMAP entry needs 2 or 3 columns\n"), next;            $type = TidyType($type) ;	    $type_kind{$type} = $kind ;            # prototype defaults to '$'            $proto = '$' unless $proto ;            warn("Warning: File '$typemap' Line $. '$line' Invalid prototype '$proto'\n")                 unless ValidProtoString($proto) ;            $proto_letter{$type} = C_string($proto) ;	}	elsif (/^\s/) {	    $$current .= $_;	}	elsif ($mode eq 'Input') {	    s/\s+$//;	    $input_expr{$_} = '';	    $current = \$input_expr{$_};	}	else {	    s/\s+$//;	    $output_expr{$_} = '';	    $current = \$output_expr{$_};	}    }    close(TYPEMAP);}foreach $key (keys %input_expr) {    $input_expr{$key} =~ s/\n+$//;}$END = "!End!\n\n";		# "impossible" keyword (multiple newline)# Match an XS keyword$BLOCK_re= '\s*(' . join('|', qw(	REQUIRE BOOT CASE PREINIT INPUT INIT CODE PPCODE OUTPUT 	CLEANUP ALIAS PROTOTYPES PROTOTYPE VERSIONCHECK INCLUDE	)) . "|$END)\\s*:";# Input:  ($_, @line) == unparsed input.# Output: ($_, @line) == (rest of line, following lines).# Return: the matched keyword if found, otherwise 0sub check_keyword {	$_ = shift(@line) while !/\S/ && @line;	s/^(\s*)($_[0])\s*:\s*(?:#.*)?/$1/s && $2;}sub print_section {    $_ = shift(@line) while !/\S/ && @line;    for (;  defined($_) && !/^$BLOCK_re/o;  $_ = shift(@line)) {	print "$_\n";    }}sub process_keyword($){    my($pattern) = @_ ;    my $kwd ;    &{"${kwd}_handler"}()         while $kwd = check_keyword($pattern) ;}sub CASE_handler {    blurt ("Error: `CASE:' after unconditional `CASE:'")	if $condnum && $cond eq '';    $cond = $_;    TrimWhitespace($cond);    print "   ", ($condnum++ ? " else" : ""), ($cond ? " if ($cond)\n" : "\n");    $_ = '' ;}sub INPUT_handler {    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {	last if /^\s*NOT_IMPLEMENTED_YET/;	next unless /\S/;	# skip blank lines 	TrimWhitespace($_) ;	my $line = $_ ;	# remove trailing semicolon if no initialisation	s/\s*;$//g unless /=/ ;	# check for optional initialisation code	my $var_init = '' ;	$var_init = $1 if s/\s*(=.*)$//s ;	$var_init =~ s/"/\\"/g;	s/\s+/ /g;	my ($var_type, $var_addr, $var_name) = /^(.*?[^& ]) *(\&?) *\b(\w+)$/s	    or blurt("Error: invalid argument declaration '$line'"), next;	# Check for duplicate definitions	blurt ("Error: duplicate definition of argument '$var_name' ignored"), next	    if $arg_list{$var_name} ++  ;	$thisdone |= $var_name eq "THIS";	$retvaldone |= $var_name eq "RETVAL";	$var_types{$var_name} = $var_type;	print "\t" . &map_type($var_type);	$var_num = $args_match{$var_name};        $proto_arg[$var_num] = ProtoString($var_type) 	    if $var_num ;	if ($var_addr) {	    $var_addr{$var_name} = 1;	    $func_args =~ s/\b($var_name)\b/&$1/;	}	if ($var_init =~ /^=\s*NO_INIT\s*;?\s*$/) {	    print "\t$var_name;\n";	} elsif ($var_init =~ /\S/) {	    &output_init($var_type, $var_num, "$var_name $var_init");	} elsif ($var_num) {	    # generate initialization code	    &generate_init($var_type, $var_num, $var_name);	} else {	    print ";\n";	}    }}sub OUTPUT_handler {    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {	next unless /\S/;	my ($outarg, $outcode) = /^\s*(\S+)\s*(.*?)\s*$/s ;	blurt ("Error: duplicate OUTPUT argument '$outarg' ignored"), next	    if $outargs{$outarg} ++ ;	if (!$gotRETVAL and $outarg eq 'RETVAL') {	    # deal with RETVAL last	    $RETVAL_code = $outcode ;	    $gotRETVAL = 1 ;	    next ;	}	blurt ("Error: OUTPUT $outarg not an argument"), next	    unless defined($args_match{$outarg});	blurt("Error: No input definition for OUTPUT argument '$outarg' - ignored"), next	    unless defined $var_types{$outarg} ;	if ($outcode) {	    print "\t$outcode\n";	} else {	    $var_num = $args_match{$outarg};	    &generate_output($var_types{$outarg}, $var_num, $outarg); 	}    }}sub CLEANUP_handler() { print_section() } sub PREINIT_handler() { print_section() } sub INIT_handler()    { print_section() } sub GetAliases{    my ($line) = @_ ;    my ($orig) = $line ;    my ($alias) ;    my ($value) ;    # Parse alias definitions    # format is    #    alias = value alias = value ...    while ($line =~ s/^\s*([\w:]+)\s*=\s*(\w+)\s*//) {        $alias = $1 ;        $orig_alias = $alias ;        $value = $2 ;        # check for optional package definition in the alias	$alias = $Packprefix . $alias if $alias !~ /::/ ;                # check for duplicate alias name & duplicate value	Warn("Warning: Ignoring duplicate alias '$orig_alias'")	    if defined $XsubAliases{$alias} ;	Warn("Warning: Aliases '$orig_alias' and '$XsubAliasValues{$value}' have identical values")	    if $XsubAliasValues{$value} ;	$XsubAliases = 1;	$XsubAliases{$alias} = $value ;	$XsubAliasValues{$value} = $orig_alias ;    }    blurt("Error: Cannot parse ALIAS definitions from '$orig'")        if $line ;}sub ALIAS_handler (){    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {	next unless /\S/;	TrimWhitespace($_) ;        GetAliases($_) if $_ ;    }}sub REQUIRE_handler (){    # the rest of the current line should contain a version number    my ($Ver) = $_ ;    TrimWhitespace($Ver) ;    death ("Error: REQUIRE expects a version number")	unless $Ver ;    # check that the version number is of the form n.n    death ("Error: REQUIRE: expected a number, got '$Ver'")	unless $Ver =~ /^\d+(\.\d*)?/ ;    death ("Error: xsubpp $Ver (or better) required--this is only $XSUBPP_version.")        unless $XSUBPP_version >= $Ver ; }sub VERSIONCHECK_handler (){    # the rest of the current line should contain either ENABLE or    # DISABLE     TrimWhitespace($_) ;     # check for ENABLE/DISABLE    death ("Error: VERSIONCHECK: ENABLE/DISABLE")        unless /^(ENABLE|DISABLE)/i ;     $WantVersionChk = 1 if $1 eq 'ENABLE' ;    $WantVersionChk = 0 if $1 eq 'DISABLE' ; }sub PROTOTYPE_handler (){    my $specified ;    death("Error: Only 1 PROTOTYPE definition allowed per xsub")         if $proto_in_this_xsub ++ ;    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {	next unless /\S/;	$specified = 1 ;	TrimWhitespace($_) ;        if ($_ eq 'DISABLE') {	   $ProtoThisXSUB = 0         }        elsif ($_ eq 'ENABLE') {	   $ProtoThisXSUB = 1         }        else {            # remove any whitespace            s/\s+//g ;            death("Error: Invalid prototype '$_'")                unless ValidProtoString($_) ;            $ProtoThisXSUB = C_string($_) ;        }    }    # If no prototype specified, then assume empty prototype ""    $ProtoThisXSUB = 2 unless $specified ;    $ProtoUsed = 1 ;}sub PROTOTYPES_handler (){    # the rest of the current line should contain either ENABLE or    # DISABLE     TrimWhitespace($_) ;    # check for ENABLE/DISABLE    death ("Error: PROTOTYPES: ENABLE/DISABLE")        unless /^(ENABLE|DISABLE)/i ;    $WantPrototypes = 1 if $1 eq 'ENABLE' ;    $WantPrototypes = 0 if $1 eq 'DISABLE' ;    $ProtoUsed = 1 ;}sub INCLUDE_handler (){    # the rest of the current line should contain a valid filename     TrimWhitespace($_) ;     death("INCLUDE: filename missing")        unless $_ ;    death("INCLUDE: output pipe is illegal")        if /^\s*\|/ ;    # simple minded recursion detector    death("INCLUDE loop detected")        if $IncludedFiles{$_} ;    ++ $IncludedFiles{$_} unless /\|\s*$/ ;    # Save the current file context.    push(@XSStack, {	type		=> 'file',        LastLine        => $lastline,        LastLineNo      => $lastline_no,        Line            => \@line,        LineNo          => \@line_no,        Filename        => $filename,        Handle          => $FH,        }) ;     ++ $FH ;    # open the new file    open ($FH, "$_") or death("Cannot open '$_': $!") ;     print Q<<"EOF" ;##/* INCLUDE:  Including '$_' from '$filename' */#EOF    $filename = $_ ;    # Prime the pump by reading the first     # non-blank line    # skip leading blank lines    while (<$FH>) {        last unless /^\s*$/ ;    }    $lastline = $_ ;    $lastline_no = $. ; } sub PopFile(){    return 0 unless $XSStack[-1]{type} eq 'file' ;    my $data     = pop @XSStack ;    my $ThisFile = $filename ;    my $isPipe   = ($filename =~ /\|\s*$/) ;     -- $IncludedFiles{$filename}        unless $isPipe ;    close $FH ;    $FH         = $data->{Handle} ;    $filename   = $data->{Filename} ;    $lastline   = $data->{LastLine} ;    $lastline_no = $data->{LastLineNo} ;    @line       = @{ $data->{Line} } ;    @line_no    = @{ $data->{LineNo} } ;    if ($isPipe and $? ) {        -- $lastline_no ;        print STDERR "Error reading from pipe '$ThisFile': $! in $filename, line $lastline_no\n"  ;        exit 1 ;    }    print Q<<"EOF" ;##/* INCLUDE: Returning to '$filename' from '$ThisFile' */#EOF    return 1 ;}sub ValidProtoString ($){    my($string) = @_ ;    if ( $string =~ /^$proto_re+$/ ) {        return $string ;    }    return 0 ;}sub C_string ($){    my($string) = @_ ;    $string =~ s[\\][\\\\]g ;    $string ;}sub ProtoString ($){    my ($type) = @_ ;    $proto_letter{$type} or '$' ;}sub check_cpp {    my @cpp = grep(/^\#\s*(?:if|e\w+)/, @line);    if (@cpp) {	my ($cpp, $cpplevel);	for $cpp (@cpp) {	    if ($cpp =~ /^\#\s*if/) {		$cpplevel++;	    } elsif (!$cpplevel) {		Warn("Warning: #else/elif/endif without #if in this function");		print STDERR "    (precede it with a blank line if the matching #if is outside the function)\n"		    if $XSStack[-1]{type} eq 'if';		return;	    } elsif ($cpp =~ /^\#\s*endif/) {		$cpplevel--;	    }	}	Warn("Warning: #if without #endif in this function") if $cpplevel;    }}sub Q {    my($text) = @_;    $text =~ s/^#//gm;    $text =~ s/\[\[/{/g;    $text =~ s/\]\]/}/g;    $text;}open($FH, $filename) or die "cannot open $filename: $!\n";# Identify the version of xsubpp usedprint <<EOM ;/* * This file was generated automatically by xsubpp version $XSUBPP_version from the  * contents of $filename. Don't edit this file, edit $filename instead. * *	ANY CHANGES MADE HERE WILL BE LOST!  * */EOM firstmodule:while (<$FH>) {    if (/^=/) {    	while (<$FH>) {	    next firstmodule if /^=cut\s*$/;	}	&Exit;    }    last if ($Module, $Package, $Prefix) =	/^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/;    print $_;}&Exit unless defined $_;$lastline    = $_;$lastline_no = $.;# Read next xsub into @line from ($lastline, <$FH>).sub fetch_para {    # parse paragraph    death ("Error: Unterminated `#if/#ifdef/#ifndef'")	if !defined $lastline && $XSStack[-1]{type} eq 'if';    @line = ();    @line_no = () ;    return PopFile() if !defined $lastline;        if ($lastline =~	/^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/) {	$Module = $1;	$Package = defined($2) ? $2 : '';	# keep -w happy	$Prefix  = defined($3) ? $3 : '';	# keep -w happy	($Module_cname = $Module) =~ s/\W/_/g;	($Packid = $Package) =~ tr/:/_/;	$Packprefix = $Package;	$Packprefix .= "::" if $Packprefix ne "";	$lastline = "";    }    for(;;) {	# Skip embedded PODs 	while ($lastline =~ /^=/)Ê{    	    while ($lastline = <$FH>) {	    	last if ($lastline =~ /^=cut\s*$/);	    }	    death ("Error: Unterminated pod") unless $lastline;	    $lastline = <$FH>;	    chomp $lastline;	    $lastline =~ s/^\s+$//;	}	if ($lastline !~ /^\s*#/ ||	    # CPP directives:	    #	ANSI:	if ifdef ifndef elif else endif define undef	    #		line error pragma	    #	gcc:	warning include_next	    #   obj-c:	import	    #   others:	ident (gcc notes that some cpps have this one)	    $lastline =~ /^#[ \t]*(?:(?:if|ifn?def|elif|else|endif|define|undef|pragma|error|warning|line\s+\d+|ident)\b|(?:include(?:_next)?|import)\s*["<].*[>"])/) {	    last if $lastline =~ /^\S/ && @line && $line[-1] eq "";	    push(@line, $lastline);	    push(@line_no, $lastline_no) ;	}	# Read next line and continuation lines	last unless defined($lastline = <$FH>);	$lastline_no = $.;	my $tmp_line;	$lastline .= $tmp_line	    while ($lastline =~ /\\$/ && defined($tmp_line = <$FH>));	    	chomp $lastline;	$lastline =~ s/^\s+$//;    }    pop(@line), pop(@line_no) while @line && $line[-1] eq "";    1;}PARAGRAPH:while (fetch_para()) {    # Print initial preprocessor statements and blank lines    while (@line && $line[0] !~ /^[^\#]/) {	my $line = shift(@line);	print $line, "\n";	next unless $line =~ /^\#\s*((if)(?:n?def)?|elsif|else|endif)\b/;	my $statement = $+;	if ($statement eq 'if') {	    $XSS_work_idx = @XSStack;	    push(@XSStack, {type => 'if'});	} else {	    death ("Error: `$statement' with no matching `if'")		if $XSStack[-1]{type} ne 'if';	    if ($XSStack[-1]{varname}) {		push(@InitFileCode, "#endif\n");		push(@BootCode,     "#endif");	    }	    my(@fns) = keys %{$XSStack[-1]{functions}};	    if ($statement ne 'endif') {		# Hide the functions defined in other #if branches, and reset.		@{$XSStack[-1]{other_functions}}{@fns} = (1) x @fns;		@{$XSStack[-1]}{qw(varname functions)} = ('', {});	    } else {		my($tmp) = pop(@XSStack);		0 while (--$XSS_work_idx			 && $XSStack[$XSS_work_idx]{type} ne 'if');		# Keep all new defined functions		push(@fns, keys %{$tmp->{other_functions}});		@{$XSStack[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;	    }	}    }    next PARAGRAPH unless @line;    if ($XSS_work_idx && !$XSStack[$XSS_work_idx]{varname}) {	# We are inside an #if, but have not yet #defined its xsubpp variable.	print "#define $cpp_next_tmp 1\n\n";	push(@InitFileCode, "#if $cpp_next_tmp\n");	push(@BootCode,     "#if $cpp_next_tmp");	$XSStack[$XSS_work_idx]{varname} = $cpp_next_tmp++;    }    death ("Code is not inside a function")	if $line[0] =~ /^\s/;    # initialize info arrays    undef(%args_match);    undef(%var_types);    undef(%var_addr);    undef(%defaults);    undef($class);    undef($static);    undef($elipsis);    undef($wantRETVAL) ;    undef(%arg_list) ;    undef(@proto_arg) ;    undef($proto_in_this_xsub) ;    $ProtoThisXSUB = $WantPrototypes ;    $_ = shift(@line);    while ($kwd = check_keyword("REQUIRE|PROTOTYPES|VERSIONCHECK|INCLUDE")) {        &{"${kwd}_handler"}() ;        next PARAGRAPH unless @line ;        $_ = shift(@line);    }    if (check_keyword("BOOT")) {	&check_cpp;        push (@BootCode, $_, @line, "") ;        next PARAGRAPH ;    }    # extract return type, function name and arguments    my($ret_type) = TidyType($_);    # a function definition needs at least 2 lines    blurt ("Error: Function definition too short '$ret_type'"), next PARAGRAPH	unless @line ;    $static = 1 if $ret_type =~ s/^static\s+//;    $func_header = shift(@line);    blurt ("Error: Cannot parse function definition from '$func_header'"), next PARAGRAPH	unless $func_header =~ /^(?:([\w:]*)::)?(\w+)\s*\(\s*(.*?)\s*\)\s*$/s;    ($class, $func_name, $orig_args) =  ($1, $2, $3) ;    ($pname = $func_name) =~ s/^($Prefix)?/$Packprefix/;    $Full_func_name = "${Packid}_$func_name";    # Check for duplicate function definition    for $tmp (@XSStack) {	next unless defined $tmp->{functions}{$Full_func_name};	Warn("Warning: duplicate function definition '$func_name' detected");	last;    }    $XSStack[$XSS_work_idx]{functions}{$Full_func_name} ++ ;    %XsubAliases = %XsubAliasValues = ();    @args = split(/\s*,\s*/, $orig_args);    if (defined($class)) {	my $arg0 = ((defined($static) or $func_name =~ /^new/) ? "CLASS" : "THIS");	unshift(@args, $arg0);	($orig_args = "$arg0, $orig_args") =~ s/^$arg0, $/$arg0/;    }    $orig_args =~ s/"/\\"/g;    $min_args = $num_args = @args;    foreach $i (0..$num_args-1) {	    if ($args[$i] =~ s/\.\.\.//) {		    $elipsis = 1;		    $min_args--;		    if ($args[$i] eq '' && $i == $num_args - 1) {			pop(@args);			last;		    }	    }	    if ($args[$i] =~ /^([^=]*[^\s=])\s*=\s*(.*)/s) {		    $min_args--;		    $args[$i] = $1;		    $defaults{$args[$i]} = $2;		    $defaults{$args[$i]} =~ s/"/\\"/g;	    }	    $proto_arg[$i+1] = '$' ;    }    if (defined($class)) {	    $func_args = join(", ", @args[1..$#args]);    } else {	    $func_args = join(", ", @args);    }    @args_match{@args} = 1..@args;    $PPCODE = grep(/^\s*PPCODE\s*:/, @line);    $ALIAS  = grep(/^\s*ALIAS\s*:/,  @line);    # print function header    print Q<<"EOF";#XS(XS_${Packid}_$func_name)#[[#    dXSARGS;EOF    print Q<<"EOF" if $ALIAS ;#    dXSI32;EOF    if ($elipsis) {	$cond = ($min_args ? qq(items < $min_args) : 0);    }    elsif ($min_args == $num_args) {	$cond = qq(items != $min_args);    }    else {	$cond = qq(items < $min_args || items > $num_args);    }    print Q<<"EOF" if $except;#    char errbuf[1024];#    *errbuf = '\0';EOF    if ($ALIAS)       { print Q<<"EOF" if $cond }#    if ($cond)#       croak("Usage: %s($orig_args)", GvNAME(CvGV(cv)));EOF    else       { print Q<<"EOF" if $cond }#    if ($cond)#	croak("Usage: $pname($orig_args)");EOF    print Q<<"EOF" if $PPCODE;#    SP -= items;EOF    # Now do a block of some sort.    $condnum = 0;    $cond = '';			# last CASE: condidional    push(@line, "$END:");    push(@line_no, $line_no[-1]);    $_ = '';    &check_cpp;    while (@line) {	&CASE_handler if check_keyword("CASE");	print Q<<"EOF";#   $except [[EOF	# do initialization of input variables	$thisdone = 0;	$retvaldone = 0;	$deferred = "";	%arg_list = () ;        $gotRETVAL = 0;	INPUT_handler() ;	process_keyword("INPUT|PREINIT|ALIAS|PROTOTYPE") ;	if (!$thisdone && defined($class)) {	    if (defined($static) or $func_name =~ /^new/) {		print "\tchar *";		$var_types{"CLASS"} = "char *";		&generate_init("char *", 1, "CLASS");	    }	    else {		print "\t$class *";		$var_types{"THIS"} = "$class *";		&generate_init("$class *", 1, "THIS");	    }	}	# do code	if (/^\s*NOT_IMPLEMENTED_YET/) {		print "\n\tcroak(\"$pname: not implemented yet\");\n";		$_ = '' ;	} else {		if ($ret_type ne "void") {			print "\t" . &map_type($ret_type) . "\tRETVAL;\n"				if !$retvaldone;			$args_match{"RETVAL"} = 0;			$var_types{"RETVAL"} = $ret_type;		}		print $deferred;                process_keyword("INIT|ALIAS|PROTOTYPE") ;		if (check_keyword("PPCODE")) {			print_section();			death ("PPCODE must be last thing") if @line;			print "\tPUTBACK;\n\treturn;\n";		} elsif (check_keyword("CODE")) {			print_section() ;		} elsif (defined($class) and $func_name eq "DESTROY") {			print "\n\t";			print "delete THIS;\n";		} else {			print "\n\t";			if ($ret_type ne "void") {				print "RETVAL = ";				$wantRETVAL = 1;			}			if (defined($static)) {			    if ($func_name =~ /^new/) {				$func_name = "$class";			    } else {				print "${class}::";			    }			} elsif (defined($class)) {			    if ($func_name =~ /^new/) {				$func_name .= " $class";			    } else {				print "THIS->";			    }			}			$func_name =~ s/^($spat)//			    if defined($spat);			print "$func_name($func_args);\n";		}	}	# do output variables	$gotRETVAL = 0;	undef $RETVAL_code ;	undef %outargs ;        process_keyword("OUTPUT|ALIAS|PROTOTYPE"); 	# all OUTPUT done, so now push the return value on the stack	if ($gotRETVAL && $RETVAL_code) {	    print "\t$RETVAL_code\n";	} elsif ($gotRETVAL || $wantRETVAL) {	    &generate_output($ret_type, 0, 'RETVAL');	}	# do cleanup	process_keyword("CLEANUP|ALIAS|PROTOTYPE") ;	# print function trailer	print Q<<EOF;#    ]]EOF	print Q<<EOF if $except;#    BEGHANDLERS#    CATCHALL#	sprintf(errbuf, "%s: %s\\tpropagated", Xname, Xreason);#    ENDHANDLERSEOF	if (check_keyword("CASE")) {	    blurt ("Error: No `CASE:' at top of function")		unless $condnum;	    $_ = "CASE: $_";	# Restore CASE: label	    next;	}	last if $_ eq "$END:";	death(/^$BLOCK_re/o ? "Misplaced `$1:'" : "Junk at end of function");    }    print Q<<EOF if $except;#    if (errbuf[0])#	croak(errbuf);EOF    print Q<<EOF unless $PPCODE;#    XSRETURN(1);EOF    print Q<<EOF;#]]#EOF    my $newXS = "newXS" ;    my $proto = "" ;    # Build the prototype string for the xsub    if ($ProtoThisXSUB) {	$newXS = "newXSproto";	if ($ProtoThisXSUB == 2) {	    # User has specified empty prototype	    $proto = ', ""' ;	}        elsif ($ProtoThisXSUB != 1) {            # User has specified a prototype            $proto = ', "' . $ProtoThisXSUB . '"';        }        else {	    my $s = ';';            if ($min_args < $num_args)  {                $s = ''; 		$proto_arg[$min_args] .= ";" ;	    }            push @proto_arg, "$s\@"                 if $elipsis ;                $proto = ', "' . join ("", @proto_arg) . '"';        }    }    if (%XsubAliases) {	$XsubAliases{$pname} = 0 	    unless defined $XsubAliases{$pname} ;	while ( ($name, $value) = each %XsubAliases) {	    push(@InitFileCode, Q<<"EOF");#        cv = newXS(\"$name\", XS_$Full_func_name, file);#        XSANY.any_i32 = $value ;EOF	push(@InitFileCode, Q<<"EOF") if $proto;#        sv_setpv((SV*)cv$proto) ;EOF        }    }    else {	push(@InitFileCode,	     "        ${newXS}(\"$pname\", XS_$Full_func_name, file$proto);\n");    }}# print initialization routineprint Q<<"EOF";##ifdef __cplusplus#extern "C"##endif#XS(boot_$Module_cname)#[[#    dXSARGS;#    char* file = __FILE__;#EOFprint Q<<"EOF" if $WantVersionChk ;#    XS_VERSION_BOOTCHECK ;#EOFprint Q<<"EOF" if defined $XsubAliases ;#    {#        CV * cv ;#EOFprint @InitFileCode;print Q<<"EOF" if defined $XsubAliases ;#    }EOFif (@BootCode){    print "\n    /* Initialisation Section */\n" ;    print grep (s/$/\n/, @BootCode) ;    print "\n    /* End of Initialisation Section */\n\n" ;}print Q<<"EOF";;#    ST(0) = &sv_yes;#    XSRETURN(1);#]]EOFwarn("Please specify prototyping behavior for $filename (see perlxs manual)\n")     unless $ProtoUsed ;&Exit;sub output_init {    local($type, $num, $init) = @_;    local($arg) = "ST(" . ($num - 1) . ")";    eval qq/print " $init\\\n"/;}sub Warn{    # work out the line number    my $line_no = $line_no[@line_no - @line -1] ;     print STDERR "@_ in $filename, line $line_no\n" ;}sub blurt {     Warn @_ ;    $errors ++ }sub death{    Warn @_ ;    exit 1 ;}sub generate_init {    local($type, $num, $var) = @_;    local($arg) = "ST(" . ($num - 1) . ")";    local($argoff) = $num - 1;    local($ntype);    local($tk);    $type = TidyType($type) ;    blurt("Error: '$type' not in typemap"), return 	unless defined($type_kind{$type});    ($ntype = $type) =~ s/\s*\*/Ptr/g;    ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;    $tk = $type_kind{$type};    $tk =~ s/OBJ$/REF/ if $func_name =~ /DESTROY$/;    $type =~ tr/:/_/;    blurt("Error: No INPUT definition for type '$type' found"), return        unless defined $input_expr{$tk} ;    $expr = $input_expr{$tk};    if ($expr =~ /DO_ARRAY_ELEM/) {        blurt("Error: '$subtype' not in typemap"), return 	    unless defined($type_kind{$subtype});        blurt("Error: No INPUT definition for type '$subtype' found"), return            unless defined $input_expr{$type_kind{$subtype}} ;	$subexpr = $input_expr{$type_kind{$subtype}};	$subexpr =~ s/ntype/subtype/g;	$subexpr =~ s/\$arg/ST(ix_$var)/g;	$subexpr =~ s/\n\t/\n\t\t/g;	$subexpr =~ s/is not of (.*")/[arg %d] is not of $1, ix_$var + 1/g;	$subexpr =~ s/\$var/${var}[ix_$var - $argoff]/;	$expr =~ s/DO_ARRAY_ELEM/$subexpr/;    }    if (defined($defaults{$var})) {	    $expr =~ s/(\t+)/$1    /g;	    $expr =~ s/        /\t/g;	    eval qq/print "\\t$var;\\n"/;	    $deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t    $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/;    } elsif ($expr !~ /^\t\$var =/) {	    eval qq/print "\\t$var;\\n"/;	    $deferred .= eval qq/"\\n$expr;\\n"/;    } else {	    eval qq/print "$expr;\\n"/;    }}sub generate_output {    local($type, $num, $var) = @_;    local($arg) = "ST(" . ($num - ($num != 0)) . ")";    local($argoff) = $num - 1;    local($ntype);    $type = TidyType($type) ;    if ($type =~ /^array\(([^,]*),(.*)\)/) {	    print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1)), XFree((char *)$var);\n";    } else {	    blurt("Error: '$type' not in typemap"), return		unless defined($type_kind{$type});            blurt("Error: No OUTPUT definition for type '$type' found"), return                unless defined $output_expr{$type_kind{$type}} ;	    ($ntype = $type) =~ s/\s*\*/Ptr/g;	    $ntype =~ s/\(\)//g;	    ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;	    $expr = $output_expr{$type_kind{$type}};	    if ($expr =~ /DO_ARRAY_ELEM/) {	        blurt("Error: '$subtype' not in typemap"), return		    unless defined($type_kind{$subtype});                blurt("Error: No OUTPUT definition for type '$subtype' found"), return                    unless defined $output_expr{$type_kind{$subtype}} ;		$subexpr = $output_expr{$type_kind{$subtype}};		$subexpr =~ s/ntype/subtype/g;		$subexpr =~ s/\$arg/ST(ix_$var)/g;		$subexpr =~ s/\$var/${var}[ix_$var]/g;		$subexpr =~ s/\n\t/\n\t\t/g;		$expr =~ s/DO_ARRAY_ELEM\n/$subexpr/;		eval "print qq\a$expr\a";	    }	    elsif ($var eq 'RETVAL') {		if ($expr =~ /^\t\$arg = /) {		    eval "print qq\a$expr\a";		    print "\tsv_2mortal(ST(0));\n";		}		else {		    print "\tST(0) = sv_newmortal();\n";		    eval "print qq\a$expr\a";		}	    }	    elsif ($arg =~ /^ST\(\d+\)$/) {		eval "print qq\a$expr\a";	    }    }}sub map_type {    my($type) = @_;    $type =~ tr/:/_/;    $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;    $type;}sub Exit {# If this is VMS, the exit status has meaning to the shell, so we# use a predictable value (SS$_Normal or SS$_Abort) rather than an# arbitrary number.    exit ($Is_VMS ? ($errors ? 44 : 1) : $errors) ;}