xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/ExtUtils/xsubpp (revision 0:68f95e015346)
1*0Sstevel@tonic-gate#!./miniperl
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gate=head1 NAME
4*0Sstevel@tonic-gate
5*0Sstevel@tonic-gatexsubpp - compiler to convert Perl XS code into C code
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gate=head1 SYNOPSIS
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gateB<xsubpp> [B<-v>] [B<-C++>] [B<-except>] [B<-s pattern>] [B<-prototypes>] [B<-noversioncheck>] [B<-nolinenumbers>] [B<-nooptimize>] [B<-typemap typemap>] ... file.xs
10*0Sstevel@tonic-gate
11*0Sstevel@tonic-gate=head1 DESCRIPTION
12*0Sstevel@tonic-gate
13*0Sstevel@tonic-gateThis compiler is typically run by the makefiles created by L<ExtUtils::MakeMaker>.
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gateI<xsubpp> will compile XS code into C code by embedding the constructs
16*0Sstevel@tonic-gatenecessary to let C functions manipulate Perl values and creates the glue
17*0Sstevel@tonic-gatenecessary to let Perl access those functions.  The compiler uses typemaps to
18*0Sstevel@tonic-gatedetermine how to map C function parameters and variables to Perl values.
19*0Sstevel@tonic-gate
20*0Sstevel@tonic-gateThe compiler will search for typemap files called I<typemap>.  It will use
21*0Sstevel@tonic-gatethe following search path to find default typemaps, with the rightmost
22*0Sstevel@tonic-gatetypemap taking precedence.
23*0Sstevel@tonic-gate
24*0Sstevel@tonic-gate	../../../typemap:../../typemap:../typemap:typemap
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gate=head1 OPTIONS
27*0Sstevel@tonic-gate
28*0Sstevel@tonic-gateNote that the C<XSOPT> MakeMaker option may be used to add these options to
29*0Sstevel@tonic-gateany makefiles generated by MakeMaker.
30*0Sstevel@tonic-gate
31*0Sstevel@tonic-gate=over 5
32*0Sstevel@tonic-gate
33*0Sstevel@tonic-gate=item B<-C++>
34*0Sstevel@tonic-gate
35*0Sstevel@tonic-gateAdds ``extern "C"'' to the C code.
36*0Sstevel@tonic-gate
37*0Sstevel@tonic-gate=item B<-hiertype>
38*0Sstevel@tonic-gate
39*0Sstevel@tonic-gateRetains '::' in type names so that C++ hierachical types can be mapped.
40*0Sstevel@tonic-gate
41*0Sstevel@tonic-gate=item B<-except>
42*0Sstevel@tonic-gate
43*0Sstevel@tonic-gateAdds exception handling stubs to the C code.
44*0Sstevel@tonic-gate
45*0Sstevel@tonic-gate=item B<-typemap typemap>
46*0Sstevel@tonic-gate
47*0Sstevel@tonic-gateIndicates that a user-supplied typemap should take precedence over the
48*0Sstevel@tonic-gatedefault typemaps.  This option may be used multiple times, with the last
49*0Sstevel@tonic-gatetypemap having the highest precedence.
50*0Sstevel@tonic-gate
51*0Sstevel@tonic-gate=item B<-v>
52*0Sstevel@tonic-gate
53*0Sstevel@tonic-gatePrints the I<xsubpp> version number to standard output, then exits.
54*0Sstevel@tonic-gate
55*0Sstevel@tonic-gate=item B<-prototypes>
56*0Sstevel@tonic-gate
57*0Sstevel@tonic-gateBy default I<xsubpp> will not automatically generate prototype code for
58*0Sstevel@tonic-gateall xsubs. This flag will enable prototypes.
59*0Sstevel@tonic-gate
60*0Sstevel@tonic-gate=item B<-noversioncheck>
61*0Sstevel@tonic-gate
62*0Sstevel@tonic-gateDisables the run time test that determines if the object file (derived
63*0Sstevel@tonic-gatefrom the C<.xs> file) and the C<.pm> files have the same version
64*0Sstevel@tonic-gatenumber.
65*0Sstevel@tonic-gate
66*0Sstevel@tonic-gate=item B<-nolinenumbers>
67*0Sstevel@tonic-gate
68*0Sstevel@tonic-gatePrevents the inclusion of `#line' directives in the output.
69*0Sstevel@tonic-gate
70*0Sstevel@tonic-gate=item B<-nooptimize>
71*0Sstevel@tonic-gate
72*0Sstevel@tonic-gateDisables certain optimizations.  The only optimization that is currently
73*0Sstevel@tonic-gateaffected is the use of I<target>s by the output C code (see L<perlguts>).
74*0Sstevel@tonic-gateThis may significantly slow down the generated code, but this is the way
75*0Sstevel@tonic-gateB<xsubpp> of 5.005 and earlier operated.
76*0Sstevel@tonic-gate
77*0Sstevel@tonic-gate=item B<-noinout>
78*0Sstevel@tonic-gate
79*0Sstevel@tonic-gateDisable recognition of C<IN>, C<OUT_LIST> and C<INOUT_LIST> declarations.
80*0Sstevel@tonic-gate
81*0Sstevel@tonic-gate=item B<-noargtypes>
82*0Sstevel@tonic-gate
83*0Sstevel@tonic-gateDisable recognition of ANSI-like descriptions of function signature.
84*0Sstevel@tonic-gate
85*0Sstevel@tonic-gate=back
86*0Sstevel@tonic-gate
87*0Sstevel@tonic-gate=head1 ENVIRONMENT
88*0Sstevel@tonic-gate
89*0Sstevel@tonic-gateNo environment variables are used.
90*0Sstevel@tonic-gate
91*0Sstevel@tonic-gate=head1 AUTHOR
92*0Sstevel@tonic-gate
93*0Sstevel@tonic-gateLarry Wall
94*0Sstevel@tonic-gate
95*0Sstevel@tonic-gate=head1 MODIFICATION HISTORY
96*0Sstevel@tonic-gate
97*0Sstevel@tonic-gateSee the file F<changes.pod>.
98*0Sstevel@tonic-gate
99*0Sstevel@tonic-gate=head1 SEE ALSO
100*0Sstevel@tonic-gate
101*0Sstevel@tonic-gateperl(1), perlxs(1), perlxstut(1)
102*0Sstevel@tonic-gate
103*0Sstevel@tonic-gate=cut
104*0Sstevel@tonic-gate
105*0Sstevel@tonic-gaterequire 5.002;
106*0Sstevel@tonic-gateuse Cwd;
107*0Sstevel@tonic-gateuse vars qw($cplusplus $hiertype);
108*0Sstevel@tonic-gateuse vars '%v';
109*0Sstevel@tonic-gate
110*0Sstevel@tonic-gateuse Config;
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gatesub Q ;
113*0Sstevel@tonic-gate
114*0Sstevel@tonic-gate# Global Constants
115*0Sstevel@tonic-gate
116*0Sstevel@tonic-gate$XSUBPP_version = "1.9508";
117*0Sstevel@tonic-gate
118*0Sstevel@tonic-gatemy ($Is_VMS, $SymSet);
119*0Sstevel@tonic-gateif ($^O eq 'VMS') {
120*0Sstevel@tonic-gate    $Is_VMS = 1;
121*0Sstevel@tonic-gate    # Establish set of global symbols with max length 28, since xsubpp
122*0Sstevel@tonic-gate    # will later add the 'XS_' prefix.
123*0Sstevel@tonic-gate    require ExtUtils::XSSymSet;
124*0Sstevel@tonic-gate    $SymSet = new ExtUtils::XSSymSet 28;
125*0Sstevel@tonic-gate}
126*0Sstevel@tonic-gate
127*0Sstevel@tonic-gate$FH = 'File0000' ;
128*0Sstevel@tonic-gate
129*0Sstevel@tonic-gate$usage = "Usage: xsubpp [-v] [-C++] [-except] [-prototypes] [-noversioncheck] [-nolinenumbers] [-nooptimize] [-noinout] [-noargtypes] [-s pattern] [-typemap typemap]... file.xs\n";
130*0Sstevel@tonic-gate
131*0Sstevel@tonic-gate$proto_re = "[" . quotemeta('\$%&*@;[]') . "]" ;
132*0Sstevel@tonic-gate
133*0Sstevel@tonic-gate$except = "";
134*0Sstevel@tonic-gate$WantPrototypes = -1 ;
135*0Sstevel@tonic-gate$WantVersionChk = 1 ;
136*0Sstevel@tonic-gate$ProtoUsed = 0 ;
137*0Sstevel@tonic-gate$WantLineNumbers = 1 ;
138*0Sstevel@tonic-gate$WantOptimize = 1 ;
139*0Sstevel@tonic-gate$Overload = 0;
140*0Sstevel@tonic-gate$Fallback = 'PL_sv_undef';
141*0Sstevel@tonic-gate
142*0Sstevel@tonic-gatemy $process_inout = 1;
143*0Sstevel@tonic-gatemy $process_argtypes = 1;
144*0Sstevel@tonic-gate
145*0Sstevel@tonic-gateSWITCH: while (@ARGV and $ARGV[0] =~ /^-./) {
146*0Sstevel@tonic-gate    $flag = shift @ARGV;
147*0Sstevel@tonic-gate    $flag =~ s/^-// ;
148*0Sstevel@tonic-gate    $spat = quotemeta shift,	next SWITCH	if $flag eq 's';
149*0Sstevel@tonic-gate    $cplusplus = 1,	next SWITCH	if $flag eq 'C++';
150*0Sstevel@tonic-gate    $hiertype  = 1,	next SWITCH	if $flag eq 'hiertype';
151*0Sstevel@tonic-gate    $WantPrototypes = 0, next SWITCH	if $flag eq 'noprototypes';
152*0Sstevel@tonic-gate    $WantPrototypes = 1, next SWITCH	if $flag eq 'prototypes';
153*0Sstevel@tonic-gate    $WantVersionChk = 0, next SWITCH	if $flag eq 'noversioncheck';
154*0Sstevel@tonic-gate    $WantVersionChk = 1, next SWITCH	if $flag eq 'versioncheck';
155*0Sstevel@tonic-gate    # XXX left this in for compat
156*0Sstevel@tonic-gate    next SWITCH                         if $flag eq 'object_capi';
157*0Sstevel@tonic-gate    $except = " TRY",	next SWITCH	if $flag eq 'except';
158*0Sstevel@tonic-gate    push(@tm,shift),	next SWITCH	if $flag eq 'typemap';
159*0Sstevel@tonic-gate    $WantLineNumbers = 0, next SWITCH	if $flag eq 'nolinenumbers';
160*0Sstevel@tonic-gate    $WantLineNumbers = 1, next SWITCH	if $flag eq 'linenumbers';
161*0Sstevel@tonic-gate    $WantOptimize = 0, next SWITCH	if $flag eq 'nooptimize';
162*0Sstevel@tonic-gate    $WantOptimize = 1, next SWITCH	if $flag eq 'optimize';
163*0Sstevel@tonic-gate    $process_inout = 0, next SWITCH	if $flag eq 'noinout';
164*0Sstevel@tonic-gate    $process_inout = 1, next SWITCH	if $flag eq 'inout';
165*0Sstevel@tonic-gate    $process_argtypes = 0, next SWITCH	if $flag eq 'noargtypes';
166*0Sstevel@tonic-gate    $process_argtypes = 1, next SWITCH	if $flag eq 'argtypes';
167*0Sstevel@tonic-gate    (print "xsubpp version $XSUBPP_version\n"), exit
168*0Sstevel@tonic-gate	if $flag eq 'v';
169*0Sstevel@tonic-gate    die $usage;
170*0Sstevel@tonic-gate}
171*0Sstevel@tonic-gateif ($WantPrototypes == -1)
172*0Sstevel@tonic-gate  { $WantPrototypes = 0}
173*0Sstevel@tonic-gateelse
174*0Sstevel@tonic-gate  { $ProtoUsed = 1 }
175*0Sstevel@tonic-gate
176*0Sstevel@tonic-gate
177*0Sstevel@tonic-gate@ARGV == 1 or die $usage;
178*0Sstevel@tonic-gate($dir, $filename) = $ARGV[0] =~ m#(.*)/(.*)#
179*0Sstevel@tonic-gate	or ($dir, $filename) = $ARGV[0] =~ m#(.*)\\(.*)#
180*0Sstevel@tonic-gate	or ($dir, $filename) = $ARGV[0] =~ m#(.*[>\]])(.*)#
181*0Sstevel@tonic-gate	or ($dir, $filename) = ('.', $ARGV[0]);
182*0Sstevel@tonic-gatechdir($dir);
183*0Sstevel@tonic-gate$pwd = cwd();
184*0Sstevel@tonic-gate
185*0Sstevel@tonic-gate++ $IncludedFiles{$ARGV[0]} ;
186*0Sstevel@tonic-gate
187*0Sstevel@tonic-gatemy(@XSStack) = ({type => 'none'});	# Stack of conditionals and INCLUDEs
188*0Sstevel@tonic-gatemy($XSS_work_idx, $cpp_next_tmp) = (0, "XSubPPtmpAAAA");
189*0Sstevel@tonic-gate
190*0Sstevel@tonic-gate
191*0Sstevel@tonic-gatesub TrimWhitespace
192*0Sstevel@tonic-gate{
193*0Sstevel@tonic-gate    $_[0] =~ s/^\s+|\s+$//go ;
194*0Sstevel@tonic-gate}
195*0Sstevel@tonic-gate
196*0Sstevel@tonic-gatesub TidyType
197*0Sstevel@tonic-gate{
198*0Sstevel@tonic-gate    local ($_) = @_ ;
199*0Sstevel@tonic-gate
200*0Sstevel@tonic-gate    # rationalise any '*' by joining them into bunches and removing whitespace
201*0Sstevel@tonic-gate    s#\s*(\*+)\s*#$1#g;
202*0Sstevel@tonic-gate    s#(\*+)# $1 #g ;
203*0Sstevel@tonic-gate
204*0Sstevel@tonic-gate    # change multiple whitespace into a single space
205*0Sstevel@tonic-gate    s/\s+/ /g ;
206*0Sstevel@tonic-gate
207*0Sstevel@tonic-gate    # trim leading & trailing whitespace
208*0Sstevel@tonic-gate    TrimWhitespace($_) ;
209*0Sstevel@tonic-gate
210*0Sstevel@tonic-gate    $_ ;
211*0Sstevel@tonic-gate}
212*0Sstevel@tonic-gate
213*0Sstevel@tonic-gate$typemap = shift @ARGV;
214*0Sstevel@tonic-gateforeach $typemap (@tm) {
215*0Sstevel@tonic-gate    die "Can't find $typemap in $pwd\n" unless -r $typemap;
216*0Sstevel@tonic-gate}
217*0Sstevel@tonic-gateunshift @tm, qw(../../../../lib/ExtUtils/typemap ../../../lib/ExtUtils/typemap
218*0Sstevel@tonic-gate                ../../lib/ExtUtils/typemap ../../../typemap ../../typemap
219*0Sstevel@tonic-gate                ../typemap typemap);
220*0Sstevel@tonic-gateforeach $typemap (@tm) {
221*0Sstevel@tonic-gate    next unless -f $typemap ;
222*0Sstevel@tonic-gate    # skip directories, binary files etc.
223*0Sstevel@tonic-gate    warn("Warning: ignoring non-text typemap file '$typemap'\n"), next
224*0Sstevel@tonic-gate	unless -T $typemap ;
225*0Sstevel@tonic-gate    open(TYPEMAP, $typemap)
226*0Sstevel@tonic-gate	or warn ("Warning: could not open typemap file '$typemap': $!\n"), next;
227*0Sstevel@tonic-gate    $mode = 'Typemap';
228*0Sstevel@tonic-gate    $junk = "" ;
229*0Sstevel@tonic-gate    $current = \$junk;
230*0Sstevel@tonic-gate    while (<TYPEMAP>) {
231*0Sstevel@tonic-gate	next if /^\s*#/;
232*0Sstevel@tonic-gate        my $line_no = $. + 1;
233*0Sstevel@tonic-gate	if (/^INPUT\s*$/)   { $mode = 'Input';   $current = \$junk;  next; }
234*0Sstevel@tonic-gate	if (/^OUTPUT\s*$/)  { $mode = 'Output';  $current = \$junk;  next; }
235*0Sstevel@tonic-gate	if (/^TYPEMAP\s*$/) { $mode = 'Typemap'; $current = \$junk;  next; }
236*0Sstevel@tonic-gate	if ($mode eq 'Typemap') {
237*0Sstevel@tonic-gate	    chomp;
238*0Sstevel@tonic-gate	    my $line = $_ ;
239*0Sstevel@tonic-gate            TrimWhitespace($_) ;
240*0Sstevel@tonic-gate	    # skip blank lines and comment lines
241*0Sstevel@tonic-gate	    next if /^$/ or /^#/ ;
242*0Sstevel@tonic-gate	    my($type,$kind, $proto) = /^\s*(.*?\S)\s+(\S+)\s*($proto_re*)\s*$/ or
243*0Sstevel@tonic-gate		warn("Warning: File '$typemap' Line $. '$line' TYPEMAP entry needs 2 or 3 columns\n"), next;
244*0Sstevel@tonic-gate            $type = TidyType($type) ;
245*0Sstevel@tonic-gate	    $type_kind{$type} = $kind ;
246*0Sstevel@tonic-gate            # prototype defaults to '$'
247*0Sstevel@tonic-gate            $proto = "\$" unless $proto ;
248*0Sstevel@tonic-gate            warn("Warning: File '$typemap' Line $. '$line' Invalid prototype '$proto'\n")
249*0Sstevel@tonic-gate                unless ValidProtoString($proto) ;
250*0Sstevel@tonic-gate            $proto_letter{$type} = C_string($proto) ;
251*0Sstevel@tonic-gate	}
252*0Sstevel@tonic-gate	elsif (/^\s/) {
253*0Sstevel@tonic-gate	    $$current .= $_;
254*0Sstevel@tonic-gate	}
255*0Sstevel@tonic-gate	elsif ($mode eq 'Input') {
256*0Sstevel@tonic-gate	    s/\s+$//;
257*0Sstevel@tonic-gate	    $input_expr{$_} = '';
258*0Sstevel@tonic-gate	    $current = \$input_expr{$_};
259*0Sstevel@tonic-gate	}
260*0Sstevel@tonic-gate	else {
261*0Sstevel@tonic-gate	    s/\s+$//;
262*0Sstevel@tonic-gate	    $output_expr{$_} = '';
263*0Sstevel@tonic-gate	    $current = \$output_expr{$_};
264*0Sstevel@tonic-gate	}
265*0Sstevel@tonic-gate    }
266*0Sstevel@tonic-gate    close(TYPEMAP);
267*0Sstevel@tonic-gate}
268*0Sstevel@tonic-gate
269*0Sstevel@tonic-gateforeach $key (keys %input_expr) {
270*0Sstevel@tonic-gate    $input_expr{$key} =~ s/;*\s+\z//;
271*0Sstevel@tonic-gate}
272*0Sstevel@tonic-gate
273*0Sstevel@tonic-gate$bal = qr[(?:(?>[^()]+)|\((??{ $bal })\))*];	# ()-balanced
274*0Sstevel@tonic-gate$cast = qr[(?:\(\s*SV\s*\*\s*\)\s*)?];		# Optional (SV*) cast
275*0Sstevel@tonic-gate$size = qr[,\s* (??{ $bal }) ]x;		# Third arg (to setpvn)
276*0Sstevel@tonic-gate
277*0Sstevel@tonic-gateforeach $key (keys %output_expr) {
278*0Sstevel@tonic-gate    use re 'eval';
279*0Sstevel@tonic-gate
280*0Sstevel@tonic-gate    my ($t, $with_size, $arg, $sarg) =
281*0Sstevel@tonic-gate      ($output_expr{$key} =~
282*0Sstevel@tonic-gate	 m[^ \s+ sv_set ( [iunp] ) v (n)? 	# Type, is_setpvn
283*0Sstevel@tonic-gate	     \s* \( \s* $cast \$arg \s* ,
284*0Sstevel@tonic-gate	     \s* ( (??{ $bal }) )		# Set from
285*0Sstevel@tonic-gate	     ( (??{ $size }) )?			# Possible sizeof set-from
286*0Sstevel@tonic-gate	     \) \s* ; \s* $
287*0Sstevel@tonic-gate	  ]x);
288*0Sstevel@tonic-gate    $targetable{$key} = [$t, $with_size, $arg, $sarg] if $t;
289*0Sstevel@tonic-gate}
290*0Sstevel@tonic-gate
291*0Sstevel@tonic-gate$END = "!End!\n\n";		# "impossible" keyword (multiple newline)
292*0Sstevel@tonic-gate
293*0Sstevel@tonic-gate# Match an XS keyword
294*0Sstevel@tonic-gate$BLOCK_re= '\s*(' . join('|', qw(
295*0Sstevel@tonic-gate	REQUIRE BOOT CASE PREINIT INPUT INIT CODE PPCODE OUTPUT
296*0Sstevel@tonic-gate	CLEANUP ALIAS ATTRS PROTOTYPES PROTOTYPE VERSIONCHECK INCLUDE
297*0Sstevel@tonic-gate	SCOPE INTERFACE INTERFACE_MACRO C_ARGS POSTCALL OVERLOAD FALLBACK
298*0Sstevel@tonic-gate	)) . "|$END)\\s*:";
299*0Sstevel@tonic-gate
300*0Sstevel@tonic-gate# Input:  ($_, @line) == unparsed input.
301*0Sstevel@tonic-gate# Output: ($_, @line) == (rest of line, following lines).
302*0Sstevel@tonic-gate# Return: the matched keyword if found, otherwise 0
303*0Sstevel@tonic-gatesub check_keyword {
304*0Sstevel@tonic-gate	$_ = shift(@line) while !/\S/ && @line;
305*0Sstevel@tonic-gate	s/^(\s*)($_[0])\s*:\s*(?:#.*)?/$1/s && $2;
306*0Sstevel@tonic-gate}
307*0Sstevel@tonic-gate
308*0Sstevel@tonic-gatemy ($C_group_rex, $C_arg);
309*0Sstevel@tonic-gate# Group in C (no support for comments or literals)
310*0Sstevel@tonic-gate$C_group_rex = qr/ [({\[]
311*0Sstevel@tonic-gate		   (?: (?> [^()\[\]{}]+ ) | (??{ $C_group_rex }) )*
312*0Sstevel@tonic-gate		   [)}\]] /x ;
313*0Sstevel@tonic-gate# Chunk in C without comma at toplevel (no comments):
314*0Sstevel@tonic-gate$C_arg = qr/ (?: (?> [^()\[\]{},"']+ )
315*0Sstevel@tonic-gate	     |   (??{ $C_group_rex })
316*0Sstevel@tonic-gate	     |   " (?: (?> [^\\"]+ )
317*0Sstevel@tonic-gate		   |   \\.
318*0Sstevel@tonic-gate		   )* "		# String literal
319*0Sstevel@tonic-gate	     |   ' (?: (?> [^\\']+ ) | \\. )* ' # Char literal
320*0Sstevel@tonic-gate	     )* /xs;
321*0Sstevel@tonic-gate
322*0Sstevel@tonic-gateif ($WantLineNumbers) {
323*0Sstevel@tonic-gate    {
324*0Sstevel@tonic-gate	package xsubpp::counter;
325*0Sstevel@tonic-gate	sub TIEHANDLE {
326*0Sstevel@tonic-gate	    my ($class, $cfile) = @_;
327*0Sstevel@tonic-gate	    my $buf = "";
328*0Sstevel@tonic-gate	    $SECTION_END_MARKER = "#line --- \"$cfile\"";
329*0Sstevel@tonic-gate	    $line_no = 1;
330*0Sstevel@tonic-gate	    bless \$buf;
331*0Sstevel@tonic-gate	}
332*0Sstevel@tonic-gate
333*0Sstevel@tonic-gate	sub PRINT {
334*0Sstevel@tonic-gate	    my $self = shift;
335*0Sstevel@tonic-gate	    for (@_) {
336*0Sstevel@tonic-gate		$$self .= $_;
337*0Sstevel@tonic-gate		while ($$self =~ s/^([^\n]*\n)//) {
338*0Sstevel@tonic-gate		    my $line = $1;
339*0Sstevel@tonic-gate		    ++ $line_no;
340*0Sstevel@tonic-gate		    $line =~ s|^\#line\s+---(?=\s)|#line $line_no|;
341*0Sstevel@tonic-gate		    print STDOUT $line;
342*0Sstevel@tonic-gate		}
343*0Sstevel@tonic-gate	    }
344*0Sstevel@tonic-gate	}
345*0Sstevel@tonic-gate
346*0Sstevel@tonic-gate	sub PRINTF {
347*0Sstevel@tonic-gate	    my $self = shift;
348*0Sstevel@tonic-gate	    my $fmt = shift;
349*0Sstevel@tonic-gate	    $self->PRINT(sprintf($fmt, @_));
350*0Sstevel@tonic-gate	}
351*0Sstevel@tonic-gate
352*0Sstevel@tonic-gate	sub DESTROY {
353*0Sstevel@tonic-gate	    # Not necessary if we're careful to end with a "\n"
354*0Sstevel@tonic-gate	    my $self = shift;
355*0Sstevel@tonic-gate	    print STDOUT $$self;
356*0Sstevel@tonic-gate	}
357*0Sstevel@tonic-gate    }
358*0Sstevel@tonic-gate
359*0Sstevel@tonic-gate    my $cfile = $filename;
360*0Sstevel@tonic-gate    $cfile =~ s/\.xs$/.c/i or $cfile .= ".c";
361*0Sstevel@tonic-gate    tie(*PSEUDO_STDOUT, 'xsubpp::counter', $cfile);
362*0Sstevel@tonic-gate    select PSEUDO_STDOUT;
363*0Sstevel@tonic-gate}
364*0Sstevel@tonic-gate
365*0Sstevel@tonic-gatesub print_section {
366*0Sstevel@tonic-gate    # the "do" is required for right semantics
367*0Sstevel@tonic-gate    do { $_ = shift(@line) } while !/\S/ && @line;
368*0Sstevel@tonic-gate
369*0Sstevel@tonic-gate    print("#line ", $line_no[@line_no - @line -1], " \"$filename\"\n")
370*0Sstevel@tonic-gate	if $WantLineNumbers && !/^\s*#\s*line\b/ && !/^#if XSubPPtmp/;
371*0Sstevel@tonic-gate    for (;  defined($_) && !/^$BLOCK_re/o;  $_ = shift(@line)) {
372*0Sstevel@tonic-gate	print "$_\n";
373*0Sstevel@tonic-gate    }
374*0Sstevel@tonic-gate    print "$xsubpp::counter::SECTION_END_MARKER\n" if $WantLineNumbers;
375*0Sstevel@tonic-gate}
376*0Sstevel@tonic-gate
377*0Sstevel@tonic-gatesub merge_section {
378*0Sstevel@tonic-gate    my $in = '';
379*0Sstevel@tonic-gate
380*0Sstevel@tonic-gate    while (!/\S/ && @line) {
381*0Sstevel@tonic-gate        $_ = shift(@line);
382*0Sstevel@tonic-gate    }
383*0Sstevel@tonic-gate
384*0Sstevel@tonic-gate    for (;  defined($_) && !/^$BLOCK_re/o;  $_ = shift(@line)) {
385*0Sstevel@tonic-gate	$in .= "$_\n";
386*0Sstevel@tonic-gate    }
387*0Sstevel@tonic-gate    chomp $in;
388*0Sstevel@tonic-gate    return $in;
389*0Sstevel@tonic-gate}
390*0Sstevel@tonic-gate
391*0Sstevel@tonic-gatesub process_keyword($)
392*0Sstevel@tonic-gate{
393*0Sstevel@tonic-gate    my($pattern) = @_ ;
394*0Sstevel@tonic-gate    my $kwd ;
395*0Sstevel@tonic-gate
396*0Sstevel@tonic-gate    &{"${kwd}_handler"}()
397*0Sstevel@tonic-gate        while $kwd = check_keyword($pattern) ;
398*0Sstevel@tonic-gate}
399*0Sstevel@tonic-gate
400*0Sstevel@tonic-gatesub CASE_handler {
401*0Sstevel@tonic-gate    blurt ("Error: `CASE:' after unconditional `CASE:'")
402*0Sstevel@tonic-gate	if $condnum && $cond eq '';
403*0Sstevel@tonic-gate    $cond = $_;
404*0Sstevel@tonic-gate    TrimWhitespace($cond);
405*0Sstevel@tonic-gate    print "   ", ($condnum++ ? " else" : ""), ($cond ? " if ($cond)\n" : "\n");
406*0Sstevel@tonic-gate    $_ = '' ;
407*0Sstevel@tonic-gate}
408*0Sstevel@tonic-gate
409*0Sstevel@tonic-gatesub INPUT_handler {
410*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
411*0Sstevel@tonic-gate	last if /^\s*NOT_IMPLEMENTED_YET/;
412*0Sstevel@tonic-gate	next unless /\S/;	# skip blank lines
413*0Sstevel@tonic-gate
414*0Sstevel@tonic-gate	TrimWhitespace($_) ;
415*0Sstevel@tonic-gate	my $line = $_ ;
416*0Sstevel@tonic-gate
417*0Sstevel@tonic-gate	# remove trailing semicolon if no initialisation
418*0Sstevel@tonic-gate	s/\s*;$//g unless /[=;+].*\S/ ;
419*0Sstevel@tonic-gate
420*0Sstevel@tonic-gate	# Process the length(foo) declarations
421*0Sstevel@tonic-gate	if (s/^([^=]*)\blength\(\s*(\w+)\s*\)\s*$/$1 XSauto_length_of_$2=NO_INIT/x) {
422*0Sstevel@tonic-gate	  print "\tSTRLEN\tSTRLEN_length_of_$2;\n";
423*0Sstevel@tonic-gate	  $lengthof{$2} = $name;
424*0Sstevel@tonic-gate	  # $islengthof{$name} = $1;
425*0Sstevel@tonic-gate	  $deferred .= "\n\tXSauto_length_of_$2 = STRLEN_length_of_$2;";
426*0Sstevel@tonic-gate	}
427*0Sstevel@tonic-gate
428*0Sstevel@tonic-gate	# check for optional initialisation code
429*0Sstevel@tonic-gate	my $var_init = '' ;
430*0Sstevel@tonic-gate	$var_init = $1 if s/\s*([=;+].*)$//s ;
431*0Sstevel@tonic-gate	$var_init =~ s/"/\\"/g;
432*0Sstevel@tonic-gate
433*0Sstevel@tonic-gate	s/\s+/ /g;
434*0Sstevel@tonic-gate	my ($var_type, $var_addr, $var_name) = /^(.*?[^&\s])\s*(\&?)\s*\b(\w+)$/s
435*0Sstevel@tonic-gate	    or blurt("Error: invalid argument declaration '$line'"), next;
436*0Sstevel@tonic-gate
437*0Sstevel@tonic-gate	# Check for duplicate definitions
438*0Sstevel@tonic-gate	blurt ("Error: duplicate definition of argument '$var_name' ignored"), next
439*0Sstevel@tonic-gate	    if $arg_list{$var_name}++
440*0Sstevel@tonic-gate	      or defined $argtype_seen{$var_name} and not $processing_arg_with_types;
441*0Sstevel@tonic-gate
442*0Sstevel@tonic-gate	$thisdone |= $var_name eq "THIS";
443*0Sstevel@tonic-gate	$retvaldone |= $var_name eq "RETVAL";
444*0Sstevel@tonic-gate	$var_types{$var_name} = $var_type;
445*0Sstevel@tonic-gate	# XXXX This check is a safeguard against the unfinished conversion of
446*0Sstevel@tonic-gate	# generate_init().  When generate_init() is fixed,
447*0Sstevel@tonic-gate	# one can use 2-args map_type() unconditionally.
448*0Sstevel@tonic-gate	if ($var_type =~ / \( \s* \* \s* \) /x) {
449*0Sstevel@tonic-gate	  # Function pointers are not yet supported with &output_init!
450*0Sstevel@tonic-gate	  print "\t" . &map_type($var_type, $var_name);
451*0Sstevel@tonic-gate	  $name_printed = 1;
452*0Sstevel@tonic-gate	} else {
453*0Sstevel@tonic-gate	  print "\t" . &map_type($var_type);
454*0Sstevel@tonic-gate	  $name_printed = 0;
455*0Sstevel@tonic-gate	}
456*0Sstevel@tonic-gate	$var_num = $args_match{$var_name};
457*0Sstevel@tonic-gate
458*0Sstevel@tonic-gate        $proto_arg[$var_num] = ProtoString($var_type)
459*0Sstevel@tonic-gate	    if $var_num ;
460*0Sstevel@tonic-gate	$func_args =~ s/\b($var_name)\b/&$1/ if $var_addr;
461*0Sstevel@tonic-gate	if ($var_init =~ /^[=;]\s*NO_INIT\s*;?\s*$/
462*0Sstevel@tonic-gate	    or $in_out{$var_name} and $in_out{$var_name} =~ /^OUT/
463*0Sstevel@tonic-gate	    and $var_init !~ /\S/) {
464*0Sstevel@tonic-gate	  if ($name_printed) {
465*0Sstevel@tonic-gate	    print ";\n";
466*0Sstevel@tonic-gate	  } else {
467*0Sstevel@tonic-gate	    print "\t$var_name;\n";
468*0Sstevel@tonic-gate	  }
469*0Sstevel@tonic-gate	} elsif ($var_init =~ /\S/) {
470*0Sstevel@tonic-gate	    &output_init($var_type, $var_num, $var_name, $var_init, $name_printed);
471*0Sstevel@tonic-gate	} elsif ($var_num) {
472*0Sstevel@tonic-gate	    # generate initialization code
473*0Sstevel@tonic-gate	    &generate_init($var_type, $var_num, $var_name, $name_printed);
474*0Sstevel@tonic-gate	} else {
475*0Sstevel@tonic-gate	    print ";\n";
476*0Sstevel@tonic-gate	}
477*0Sstevel@tonic-gate    }
478*0Sstevel@tonic-gate}
479*0Sstevel@tonic-gate
480*0Sstevel@tonic-gatesub OUTPUT_handler {
481*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
482*0Sstevel@tonic-gate	next unless /\S/;
483*0Sstevel@tonic-gate	if (/^\s*SETMAGIC\s*:\s*(ENABLE|DISABLE)\s*/) {
484*0Sstevel@tonic-gate	    $DoSetMagic = ($1 eq "ENABLE" ? 1 : 0);
485*0Sstevel@tonic-gate	    next;
486*0Sstevel@tonic-gate	}
487*0Sstevel@tonic-gate	my ($outarg, $outcode) = /^\s*(\S+)\s*(.*?)\s*$/s ;
488*0Sstevel@tonic-gate	blurt ("Error: duplicate OUTPUT argument '$outarg' ignored"), next
489*0Sstevel@tonic-gate	    if $outargs{$outarg} ++ ;
490*0Sstevel@tonic-gate	if (!$gotRETVAL and $outarg eq 'RETVAL') {
491*0Sstevel@tonic-gate	    # deal with RETVAL last
492*0Sstevel@tonic-gate	    $RETVAL_code = $outcode ;
493*0Sstevel@tonic-gate	    $gotRETVAL = 1 ;
494*0Sstevel@tonic-gate	    next ;
495*0Sstevel@tonic-gate	}
496*0Sstevel@tonic-gate	blurt ("Error: OUTPUT $outarg not an argument"), next
497*0Sstevel@tonic-gate	    unless defined($args_match{$outarg});
498*0Sstevel@tonic-gate	blurt("Error: No input definition for OUTPUT argument '$outarg' - ignored"), next
499*0Sstevel@tonic-gate	    unless defined $var_types{$outarg} ;
500*0Sstevel@tonic-gate	$var_num = $args_match{$outarg};
501*0Sstevel@tonic-gate	if ($outcode) {
502*0Sstevel@tonic-gate	    print "\t$outcode\n";
503*0Sstevel@tonic-gate	    print "\tSvSETMAGIC(ST(" , $var_num-1 , "));\n" if $DoSetMagic;
504*0Sstevel@tonic-gate	} else {
505*0Sstevel@tonic-gate	    &generate_output($var_types{$outarg}, $var_num, $outarg, $DoSetMagic);
506*0Sstevel@tonic-gate	}
507*0Sstevel@tonic-gate	delete $in_out{$outarg} 	# No need to auto-OUTPUT
508*0Sstevel@tonic-gate	  if exists $in_out{$outarg} and $in_out{$outarg} =~ /OUT$/;
509*0Sstevel@tonic-gate    }
510*0Sstevel@tonic-gate}
511*0Sstevel@tonic-gate
512*0Sstevel@tonic-gatesub C_ARGS_handler() {
513*0Sstevel@tonic-gate    my $in = merge_section();
514*0Sstevel@tonic-gate
515*0Sstevel@tonic-gate    TrimWhitespace($in);
516*0Sstevel@tonic-gate    $func_args = $in;
517*0Sstevel@tonic-gate}
518*0Sstevel@tonic-gate
519*0Sstevel@tonic-gatesub INTERFACE_MACRO_handler() {
520*0Sstevel@tonic-gate    my $in = merge_section();
521*0Sstevel@tonic-gate
522*0Sstevel@tonic-gate    TrimWhitespace($in);
523*0Sstevel@tonic-gate    if ($in =~ /\s/) {		# two
524*0Sstevel@tonic-gate        ($interface_macro, $interface_macro_set) = split ' ', $in;
525*0Sstevel@tonic-gate    } else {
526*0Sstevel@tonic-gate        $interface_macro = $in;
527*0Sstevel@tonic-gate	$interface_macro_set = 'UNKNOWN_CVT'; # catch later
528*0Sstevel@tonic-gate    }
529*0Sstevel@tonic-gate    $interface = 1;		# local
530*0Sstevel@tonic-gate    $Interfaces = 1;		# global
531*0Sstevel@tonic-gate}
532*0Sstevel@tonic-gate
533*0Sstevel@tonic-gatesub INTERFACE_handler() {
534*0Sstevel@tonic-gate    my $in = merge_section();
535*0Sstevel@tonic-gate
536*0Sstevel@tonic-gate    TrimWhitespace($in);
537*0Sstevel@tonic-gate
538*0Sstevel@tonic-gate    foreach (split /[\s,]+/, $in) {
539*0Sstevel@tonic-gate        $Interfaces{$_} = $_;
540*0Sstevel@tonic-gate    }
541*0Sstevel@tonic-gate    print Q<<"EOF";
542*0Sstevel@tonic-gate#	XSFUNCTION = $interface_macro($ret_type,cv,XSANY.any_dptr);
543*0Sstevel@tonic-gateEOF
544*0Sstevel@tonic-gate    $interface = 1;		# local
545*0Sstevel@tonic-gate    $Interfaces = 1;		# global
546*0Sstevel@tonic-gate}
547*0Sstevel@tonic-gate
548*0Sstevel@tonic-gatesub CLEANUP_handler() { print_section() }
549*0Sstevel@tonic-gatesub PREINIT_handler() { print_section() }
550*0Sstevel@tonic-gatesub POSTCALL_handler() { print_section() }
551*0Sstevel@tonic-gatesub INIT_handler()    { print_section() }
552*0Sstevel@tonic-gate
553*0Sstevel@tonic-gatesub GetAliases
554*0Sstevel@tonic-gate{
555*0Sstevel@tonic-gate    my ($line) = @_ ;
556*0Sstevel@tonic-gate    my ($orig) = $line ;
557*0Sstevel@tonic-gate    my ($alias) ;
558*0Sstevel@tonic-gate    my ($value) ;
559*0Sstevel@tonic-gate
560*0Sstevel@tonic-gate    # Parse alias definitions
561*0Sstevel@tonic-gate    # format is
562*0Sstevel@tonic-gate    #    alias = value alias = value ...
563*0Sstevel@tonic-gate
564*0Sstevel@tonic-gate    while ($line =~ s/^\s*([\w:]+)\s*=\s*(\w+)\s*//) {
565*0Sstevel@tonic-gate        $alias = $1 ;
566*0Sstevel@tonic-gate        $orig_alias = $alias ;
567*0Sstevel@tonic-gate        $value = $2 ;
568*0Sstevel@tonic-gate
569*0Sstevel@tonic-gate        # check for optional package definition in the alias
570*0Sstevel@tonic-gate	$alias = $Packprefix . $alias if $alias !~ /::/ ;
571*0Sstevel@tonic-gate
572*0Sstevel@tonic-gate        # check for duplicate alias name & duplicate value
573*0Sstevel@tonic-gate	Warn("Warning: Ignoring duplicate alias '$orig_alias'")
574*0Sstevel@tonic-gate	    if defined $XsubAliases{$alias} ;
575*0Sstevel@tonic-gate
576*0Sstevel@tonic-gate	Warn("Warning: Aliases '$orig_alias' and '$XsubAliasValues{$value}' have identical values")
577*0Sstevel@tonic-gate	    if $XsubAliasValues{$value} ;
578*0Sstevel@tonic-gate
579*0Sstevel@tonic-gate	$XsubAliases = 1;
580*0Sstevel@tonic-gate	$XsubAliases{$alias} = $value ;
581*0Sstevel@tonic-gate	$XsubAliasValues{$value} = $orig_alias ;
582*0Sstevel@tonic-gate    }
583*0Sstevel@tonic-gate
584*0Sstevel@tonic-gate    blurt("Error: Cannot parse ALIAS definitions from '$orig'")
585*0Sstevel@tonic-gate        if $line ;
586*0Sstevel@tonic-gate}
587*0Sstevel@tonic-gate
588*0Sstevel@tonic-gatesub ATTRS_handler ()
589*0Sstevel@tonic-gate{
590*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
591*0Sstevel@tonic-gate	next unless /\S/;
592*0Sstevel@tonic-gate	TrimWhitespace($_) ;
593*0Sstevel@tonic-gate        push @Attributes, $_;
594*0Sstevel@tonic-gate    }
595*0Sstevel@tonic-gate}
596*0Sstevel@tonic-gate
597*0Sstevel@tonic-gatesub ALIAS_handler ()
598*0Sstevel@tonic-gate{
599*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
600*0Sstevel@tonic-gate	next unless /\S/;
601*0Sstevel@tonic-gate	TrimWhitespace($_) ;
602*0Sstevel@tonic-gate        GetAliases($_) if $_ ;
603*0Sstevel@tonic-gate    }
604*0Sstevel@tonic-gate}
605*0Sstevel@tonic-gate
606*0Sstevel@tonic-gatesub OVERLOAD_handler()
607*0Sstevel@tonic-gate{
608*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
609*0Sstevel@tonic-gate	next unless /\S/;
610*0Sstevel@tonic-gate	TrimWhitespace($_) ;
611*0Sstevel@tonic-gate        while ( s/^\s*([\w:"\\)\+\-\*\/\%\<\>\.\&\|\^\!\~\{\}\=]+)\s*//) {
612*0Sstevel@tonic-gate	    $Overload = 1 unless $Overload;
613*0Sstevel@tonic-gate	    my $overload = "$Package\::(".$1 ;
614*0Sstevel@tonic-gate            push(@InitFileCode,
615*0Sstevel@tonic-gate    	     "        newXS(\"$overload\", XS_$Full_func_name, file$proto);\n");
616*0Sstevel@tonic-gate        }
617*0Sstevel@tonic-gate    }
618*0Sstevel@tonic-gate
619*0Sstevel@tonic-gate}
620*0Sstevel@tonic-gate
621*0Sstevel@tonic-gatesub FALLBACK_handler()
622*0Sstevel@tonic-gate{
623*0Sstevel@tonic-gate    # the rest of the current line should contain either TRUE,
624*0Sstevel@tonic-gate    # FALSE or UNDEF
625*0Sstevel@tonic-gate
626*0Sstevel@tonic-gate    TrimWhitespace($_) ;
627*0Sstevel@tonic-gate    my %map = (
628*0Sstevel@tonic-gate	TRUE => "PL_sv_yes", 1 => "PL_sv_yes",
629*0Sstevel@tonic-gate	FALSE => "PL_sv_no", 0 => "PL_sv_no",
630*0Sstevel@tonic-gate	UNDEF => "PL_sv_undef",
631*0Sstevel@tonic-gate    ) ;
632*0Sstevel@tonic-gate
633*0Sstevel@tonic-gate    # check for valid FALLBACK value
634*0Sstevel@tonic-gate    death ("Error: FALLBACK: TRUE/FALSE/UNDEF") unless exists $map{uc $_} ;
635*0Sstevel@tonic-gate
636*0Sstevel@tonic-gate    $Fallback = $map{uc $_} ;
637*0Sstevel@tonic-gate}
638*0Sstevel@tonic-gate
639*0Sstevel@tonic-gatesub REQUIRE_handler ()
640*0Sstevel@tonic-gate{
641*0Sstevel@tonic-gate    # the rest of the current line should contain a version number
642*0Sstevel@tonic-gate    my ($Ver) = $_ ;
643*0Sstevel@tonic-gate
644*0Sstevel@tonic-gate    TrimWhitespace($Ver) ;
645*0Sstevel@tonic-gate
646*0Sstevel@tonic-gate    death ("Error: REQUIRE expects a version number")
647*0Sstevel@tonic-gate	unless $Ver ;
648*0Sstevel@tonic-gate
649*0Sstevel@tonic-gate    # check that the version number is of the form n.n
650*0Sstevel@tonic-gate    death ("Error: REQUIRE: expected a number, got '$Ver'")
651*0Sstevel@tonic-gate	unless $Ver =~ /^\d+(\.\d*)?/ ;
652*0Sstevel@tonic-gate
653*0Sstevel@tonic-gate    death ("Error: xsubpp $Ver (or better) required--this is only $XSUBPP_version.")
654*0Sstevel@tonic-gate        unless $XSUBPP_version >= $Ver ;
655*0Sstevel@tonic-gate}
656*0Sstevel@tonic-gate
657*0Sstevel@tonic-gatesub VERSIONCHECK_handler ()
658*0Sstevel@tonic-gate{
659*0Sstevel@tonic-gate    # the rest of the current line should contain either ENABLE or
660*0Sstevel@tonic-gate    # DISABLE
661*0Sstevel@tonic-gate
662*0Sstevel@tonic-gate    TrimWhitespace($_) ;
663*0Sstevel@tonic-gate
664*0Sstevel@tonic-gate    # check for ENABLE/DISABLE
665*0Sstevel@tonic-gate    death ("Error: VERSIONCHECK: ENABLE/DISABLE")
666*0Sstevel@tonic-gate        unless /^(ENABLE|DISABLE)/i ;
667*0Sstevel@tonic-gate
668*0Sstevel@tonic-gate    $WantVersionChk = 1 if $1 eq 'ENABLE' ;
669*0Sstevel@tonic-gate    $WantVersionChk = 0 if $1 eq 'DISABLE' ;
670*0Sstevel@tonic-gate
671*0Sstevel@tonic-gate}
672*0Sstevel@tonic-gate
673*0Sstevel@tonic-gatesub PROTOTYPE_handler ()
674*0Sstevel@tonic-gate{
675*0Sstevel@tonic-gate    my $specified ;
676*0Sstevel@tonic-gate
677*0Sstevel@tonic-gate    death("Error: Only 1 PROTOTYPE definition allowed per xsub")
678*0Sstevel@tonic-gate        if $proto_in_this_xsub ++ ;
679*0Sstevel@tonic-gate
680*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
681*0Sstevel@tonic-gate	next unless /\S/;
682*0Sstevel@tonic-gate	$specified = 1 ;
683*0Sstevel@tonic-gate	TrimWhitespace($_) ;
684*0Sstevel@tonic-gate        if ($_ eq 'DISABLE') {
685*0Sstevel@tonic-gate	   $ProtoThisXSUB = 0
686*0Sstevel@tonic-gate        }
687*0Sstevel@tonic-gate        elsif ($_ eq 'ENABLE') {
688*0Sstevel@tonic-gate	   $ProtoThisXSUB = 1
689*0Sstevel@tonic-gate        }
690*0Sstevel@tonic-gate        else {
691*0Sstevel@tonic-gate            # remove any whitespace
692*0Sstevel@tonic-gate            s/\s+//g ;
693*0Sstevel@tonic-gate            death("Error: Invalid prototype '$_'")
694*0Sstevel@tonic-gate                unless ValidProtoString($_) ;
695*0Sstevel@tonic-gate            $ProtoThisXSUB = C_string($_) ;
696*0Sstevel@tonic-gate        }
697*0Sstevel@tonic-gate    }
698*0Sstevel@tonic-gate
699*0Sstevel@tonic-gate    # If no prototype specified, then assume empty prototype ""
700*0Sstevel@tonic-gate    $ProtoThisXSUB = 2 unless $specified ;
701*0Sstevel@tonic-gate
702*0Sstevel@tonic-gate    $ProtoUsed = 1 ;
703*0Sstevel@tonic-gate
704*0Sstevel@tonic-gate}
705*0Sstevel@tonic-gate
706*0Sstevel@tonic-gatesub SCOPE_handler ()
707*0Sstevel@tonic-gate{
708*0Sstevel@tonic-gate    death("Error: Only 1 SCOPE declaration allowed per xsub")
709*0Sstevel@tonic-gate        if $scope_in_this_xsub ++ ;
710*0Sstevel@tonic-gate
711*0Sstevel@tonic-gate    for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
712*0Sstevel@tonic-gate		next unless /\S/;
713*0Sstevel@tonic-gate		TrimWhitespace($_) ;
714*0Sstevel@tonic-gate        if ($_ =~ /^DISABLE/i) {
715*0Sstevel@tonic-gate		   $ScopeThisXSUB = 0
716*0Sstevel@tonic-gate        }
717*0Sstevel@tonic-gate        elsif ($_ =~ /^ENABLE/i) {
718*0Sstevel@tonic-gate		   $ScopeThisXSUB = 1
719*0Sstevel@tonic-gate        }
720*0Sstevel@tonic-gate    }
721*0Sstevel@tonic-gate
722*0Sstevel@tonic-gate}
723*0Sstevel@tonic-gate
724*0Sstevel@tonic-gatesub PROTOTYPES_handler ()
725*0Sstevel@tonic-gate{
726*0Sstevel@tonic-gate    # the rest of the current line should contain either ENABLE or
727*0Sstevel@tonic-gate    # DISABLE
728*0Sstevel@tonic-gate
729*0Sstevel@tonic-gate    TrimWhitespace($_) ;
730*0Sstevel@tonic-gate
731*0Sstevel@tonic-gate    # check for ENABLE/DISABLE
732*0Sstevel@tonic-gate    death ("Error: PROTOTYPES: ENABLE/DISABLE")
733*0Sstevel@tonic-gate        unless /^(ENABLE|DISABLE)/i ;
734*0Sstevel@tonic-gate
735*0Sstevel@tonic-gate    $WantPrototypes = 1 if $1 eq 'ENABLE' ;
736*0Sstevel@tonic-gate    $WantPrototypes = 0 if $1 eq 'DISABLE' ;
737*0Sstevel@tonic-gate    $ProtoUsed = 1 ;
738*0Sstevel@tonic-gate
739*0Sstevel@tonic-gate}
740*0Sstevel@tonic-gate
741*0Sstevel@tonic-gatesub INCLUDE_handler ()
742*0Sstevel@tonic-gate{
743*0Sstevel@tonic-gate    # the rest of the current line should contain a valid filename
744*0Sstevel@tonic-gate
745*0Sstevel@tonic-gate    TrimWhitespace($_) ;
746*0Sstevel@tonic-gate
747*0Sstevel@tonic-gate    death("INCLUDE: filename missing")
748*0Sstevel@tonic-gate        unless $_ ;
749*0Sstevel@tonic-gate
750*0Sstevel@tonic-gate    death("INCLUDE: output pipe is illegal")
751*0Sstevel@tonic-gate        if /^\s*\|/ ;
752*0Sstevel@tonic-gate
753*0Sstevel@tonic-gate    # simple minded recursion detector
754*0Sstevel@tonic-gate    death("INCLUDE loop detected")
755*0Sstevel@tonic-gate        if $IncludedFiles{$_} ;
756*0Sstevel@tonic-gate
757*0Sstevel@tonic-gate    ++ $IncludedFiles{$_} unless /\|\s*$/ ;
758*0Sstevel@tonic-gate
759*0Sstevel@tonic-gate    # Save the current file context.
760*0Sstevel@tonic-gate    push(@XSStack, {
761*0Sstevel@tonic-gate	type		=> 'file',
762*0Sstevel@tonic-gate        LastLine        => $lastline,
763*0Sstevel@tonic-gate        LastLineNo      => $lastline_no,
764*0Sstevel@tonic-gate        Line            => \@line,
765*0Sstevel@tonic-gate        LineNo          => \@line_no,
766*0Sstevel@tonic-gate        Filename        => $filename,
767*0Sstevel@tonic-gate        Handle          => $FH,
768*0Sstevel@tonic-gate        }) ;
769*0Sstevel@tonic-gate
770*0Sstevel@tonic-gate    ++ $FH ;
771*0Sstevel@tonic-gate
772*0Sstevel@tonic-gate    # open the new file
773*0Sstevel@tonic-gate    open ($FH, "$_") or death("Cannot open '$_': $!") ;
774*0Sstevel@tonic-gate
775*0Sstevel@tonic-gate    print Q<<"EOF" ;
776*0Sstevel@tonic-gate#
777*0Sstevel@tonic-gate#/* INCLUDE:  Including '$_' from '$filename' */
778*0Sstevel@tonic-gate#
779*0Sstevel@tonic-gateEOF
780*0Sstevel@tonic-gate
781*0Sstevel@tonic-gate    $filename = $_ ;
782*0Sstevel@tonic-gate
783*0Sstevel@tonic-gate    # Prime the pump by reading the first
784*0Sstevel@tonic-gate    # non-blank line
785*0Sstevel@tonic-gate
786*0Sstevel@tonic-gate    # skip leading blank lines
787*0Sstevel@tonic-gate    while (<$FH>) {
788*0Sstevel@tonic-gate        last unless /^\s*$/ ;
789*0Sstevel@tonic-gate    }
790*0Sstevel@tonic-gate
791*0Sstevel@tonic-gate    $lastline = $_ ;
792*0Sstevel@tonic-gate    $lastline_no = $. ;
793*0Sstevel@tonic-gate
794*0Sstevel@tonic-gate}
795*0Sstevel@tonic-gate
796*0Sstevel@tonic-gatesub PopFile()
797*0Sstevel@tonic-gate{
798*0Sstevel@tonic-gate    return 0 unless $XSStack[-1]{type} eq 'file' ;
799*0Sstevel@tonic-gate
800*0Sstevel@tonic-gate    my $data     = pop @XSStack ;
801*0Sstevel@tonic-gate    my $ThisFile = $filename ;
802*0Sstevel@tonic-gate    my $isPipe   = ($filename =~ /\|\s*$/) ;
803*0Sstevel@tonic-gate
804*0Sstevel@tonic-gate    -- $IncludedFiles{$filename}
805*0Sstevel@tonic-gate        unless $isPipe ;
806*0Sstevel@tonic-gate
807*0Sstevel@tonic-gate    close $FH ;
808*0Sstevel@tonic-gate
809*0Sstevel@tonic-gate    $FH         = $data->{Handle} ;
810*0Sstevel@tonic-gate    $filename   = $data->{Filename} ;
811*0Sstevel@tonic-gate    $lastline   = $data->{LastLine} ;
812*0Sstevel@tonic-gate    $lastline_no = $data->{LastLineNo} ;
813*0Sstevel@tonic-gate    @line       = @{ $data->{Line} } ;
814*0Sstevel@tonic-gate    @line_no    = @{ $data->{LineNo} } ;
815*0Sstevel@tonic-gate
816*0Sstevel@tonic-gate    if ($isPipe and $? ) {
817*0Sstevel@tonic-gate        -- $lastline_no ;
818*0Sstevel@tonic-gate        print STDERR "Error reading from pipe '$ThisFile': $! in $filename, line $lastline_no\n"  ;
819*0Sstevel@tonic-gate        exit 1 ;
820*0Sstevel@tonic-gate    }
821*0Sstevel@tonic-gate
822*0Sstevel@tonic-gate    print Q<<"EOF" ;
823*0Sstevel@tonic-gate#
824*0Sstevel@tonic-gate#/* INCLUDE: Returning to '$filename' from '$ThisFile' */
825*0Sstevel@tonic-gate#
826*0Sstevel@tonic-gateEOF
827*0Sstevel@tonic-gate
828*0Sstevel@tonic-gate    return 1 ;
829*0Sstevel@tonic-gate}
830*0Sstevel@tonic-gate
831*0Sstevel@tonic-gatesub ValidProtoString ($)
832*0Sstevel@tonic-gate{
833*0Sstevel@tonic-gate    my($string) = @_ ;
834*0Sstevel@tonic-gate
835*0Sstevel@tonic-gate    if ( $string =~ /^$proto_re+$/ ) {
836*0Sstevel@tonic-gate        return $string ;
837*0Sstevel@tonic-gate    }
838*0Sstevel@tonic-gate
839*0Sstevel@tonic-gate    return 0 ;
840*0Sstevel@tonic-gate}
841*0Sstevel@tonic-gate
842*0Sstevel@tonic-gatesub C_string ($)
843*0Sstevel@tonic-gate{
844*0Sstevel@tonic-gate    my($string) = @_ ;
845*0Sstevel@tonic-gate
846*0Sstevel@tonic-gate    $string =~ s[\\][\\\\]g ;
847*0Sstevel@tonic-gate    $string ;
848*0Sstevel@tonic-gate}
849*0Sstevel@tonic-gate
850*0Sstevel@tonic-gatesub ProtoString ($)
851*0Sstevel@tonic-gate{
852*0Sstevel@tonic-gate    my ($type) = @_ ;
853*0Sstevel@tonic-gate
854*0Sstevel@tonic-gate    $proto_letter{$type} or "\$" ;
855*0Sstevel@tonic-gate}
856*0Sstevel@tonic-gate
857*0Sstevel@tonic-gatesub check_cpp {
858*0Sstevel@tonic-gate    my @cpp = grep(/^\#\s*(?:if|e\w+)/, @line);
859*0Sstevel@tonic-gate    if (@cpp) {
860*0Sstevel@tonic-gate	my ($cpp, $cpplevel);
861*0Sstevel@tonic-gate	for $cpp (@cpp) {
862*0Sstevel@tonic-gate	    if ($cpp =~ /^\#\s*if/) {
863*0Sstevel@tonic-gate		$cpplevel++;
864*0Sstevel@tonic-gate	    } elsif (!$cpplevel) {
865*0Sstevel@tonic-gate		Warn("Warning: #else/elif/endif without #if in this function");
866*0Sstevel@tonic-gate		print STDERR "    (precede it with a blank line if the matching #if is outside the function)\n"
867*0Sstevel@tonic-gate		    if $XSStack[-1]{type} eq 'if';
868*0Sstevel@tonic-gate		return;
869*0Sstevel@tonic-gate	    } elsif ($cpp =~ /^\#\s*endif/) {
870*0Sstevel@tonic-gate		$cpplevel--;
871*0Sstevel@tonic-gate	    }
872*0Sstevel@tonic-gate	}
873*0Sstevel@tonic-gate	Warn("Warning: #if without #endif in this function") if $cpplevel;
874*0Sstevel@tonic-gate    }
875*0Sstevel@tonic-gate}
876*0Sstevel@tonic-gate
877*0Sstevel@tonic-gate
878*0Sstevel@tonic-gatesub Q {
879*0Sstevel@tonic-gate    my($text) = @_;
880*0Sstevel@tonic-gate    $text =~ s/^#//gm;
881*0Sstevel@tonic-gate    $text =~ s/\[\[/{/g;
882*0Sstevel@tonic-gate    $text =~ s/\]\]/}/g;
883*0Sstevel@tonic-gate    $text;
884*0Sstevel@tonic-gate}
885*0Sstevel@tonic-gate
886*0Sstevel@tonic-gateopen($FH, $filename) or die "cannot open $filename: $!\n";
887*0Sstevel@tonic-gate
888*0Sstevel@tonic-gate# Identify the version of xsubpp used
889*0Sstevel@tonic-gateprint <<EOM ;
890*0Sstevel@tonic-gate/*
891*0Sstevel@tonic-gate * This file was generated automatically by xsubpp version $XSUBPP_version from the
892*0Sstevel@tonic-gate * contents of $filename. Do not edit this file, edit $filename instead.
893*0Sstevel@tonic-gate *
894*0Sstevel@tonic-gate *	ANY CHANGES MADE HERE WILL BE LOST!
895*0Sstevel@tonic-gate *
896*0Sstevel@tonic-gate */
897*0Sstevel@tonic-gate
898*0Sstevel@tonic-gateEOM
899*0Sstevel@tonic-gate
900*0Sstevel@tonic-gate
901*0Sstevel@tonic-gateprint("#line 1 \"$filename\"\n")
902*0Sstevel@tonic-gate    if $WantLineNumbers;
903*0Sstevel@tonic-gate
904*0Sstevel@tonic-gatefirstmodule:
905*0Sstevel@tonic-gatewhile (<$FH>) {
906*0Sstevel@tonic-gate    if (/^=/) {
907*0Sstevel@tonic-gate        my $podstartline = $.;
908*0Sstevel@tonic-gate    	do {
909*0Sstevel@tonic-gate	    if (/^=cut\s*$/) {
910*0Sstevel@tonic-gate		# We can't just write out a /* */ comment, as our embedded
911*0Sstevel@tonic-gate		# POD might itself be in a comment. We can't put a /**/
912*0Sstevel@tonic-gate		# comment inside #if 0, as the C standard says that the source
913*0Sstevel@tonic-gate		# file is decomposed into preprocessing characters in the stage
914*0Sstevel@tonic-gate		# before preprocessing commands are executed.
915*0Sstevel@tonic-gate		# I don't want to leave the text as barewords, because the spec
916*0Sstevel@tonic-gate		# isn't clear whether macros are expanded before or after
917*0Sstevel@tonic-gate		# preprocessing commands are executed, and someone pathological
918*0Sstevel@tonic-gate		# may just have defined one of the 3 words as a macro that does
919*0Sstevel@tonic-gate		# something strange. Multiline strings are illegal in C, so
920*0Sstevel@tonic-gate		# the "" we write must be a string literal. And they aren't
921*0Sstevel@tonic-gate		# concatenated until 2 steps later, so we are safe.
922*0Sstevel@tonic-gate		print("#if 0\n  \"Skipped embedded POD.\"\n#endif\n");
923*0Sstevel@tonic-gate		printf("#line %d \"$filename\"\n", $. + 1)
924*0Sstevel@tonic-gate		  if $WantLineNumbers;
925*0Sstevel@tonic-gate		next firstmodule
926*0Sstevel@tonic-gate	    }
927*0Sstevel@tonic-gate
928*0Sstevel@tonic-gate	} while (<$FH>);
929*0Sstevel@tonic-gate	# At this point $. is at end of file so die won't state the start
930*0Sstevel@tonic-gate	# of the problem, and as we haven't yet read any lines &death won't
931*0Sstevel@tonic-gate	# show the correct line in the message either.
932*0Sstevel@tonic-gate	die ("Error: Unterminated pod in $filename, line $podstartline\n")
933*0Sstevel@tonic-gate	  unless $lastline;
934*0Sstevel@tonic-gate    }
935*0Sstevel@tonic-gate    last if ($Module, $Package, $Prefix) =
936*0Sstevel@tonic-gate	/^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/;
937*0Sstevel@tonic-gate
938*0Sstevel@tonic-gate    print $_;
939*0Sstevel@tonic-gate}
940*0Sstevel@tonic-gate&Exit unless defined $_;
941*0Sstevel@tonic-gate
942*0Sstevel@tonic-gateprint "$xsubpp::counter::SECTION_END_MARKER\n" if $WantLineNumbers;
943*0Sstevel@tonic-gate
944*0Sstevel@tonic-gate$lastline    = $_;
945*0Sstevel@tonic-gate$lastline_no = $.;
946*0Sstevel@tonic-gate
947*0Sstevel@tonic-gate# Read next xsub into @line from ($lastline, <$FH>).
948*0Sstevel@tonic-gatesub fetch_para {
949*0Sstevel@tonic-gate    # parse paragraph
950*0Sstevel@tonic-gate    death ("Error: Unterminated `#if/#ifdef/#ifndef'")
951*0Sstevel@tonic-gate	if !defined $lastline && $XSStack[-1]{type} eq 'if';
952*0Sstevel@tonic-gate    @line = ();
953*0Sstevel@tonic-gate    @line_no = () ;
954*0Sstevel@tonic-gate    return PopFile() if !defined $lastline;
955*0Sstevel@tonic-gate
956*0Sstevel@tonic-gate    if ($lastline =~
957*0Sstevel@tonic-gate	/^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/) {
958*0Sstevel@tonic-gate	$Module = $1;
959*0Sstevel@tonic-gate	$Package = defined($2) ? $2 : '';	# keep -w happy
960*0Sstevel@tonic-gate	$Prefix  = defined($3) ? $3 : '';	# keep -w happy
961*0Sstevel@tonic-gate	$Prefix = quotemeta $Prefix ;
962*0Sstevel@tonic-gate	($Module_cname = $Module) =~ s/\W/_/g;
963*0Sstevel@tonic-gate	($Packid = $Package) =~ tr/:/_/;
964*0Sstevel@tonic-gate	$Packprefix = $Package;
965*0Sstevel@tonic-gate	$Packprefix .= "::" if $Packprefix ne "";
966*0Sstevel@tonic-gate	$lastline = "";
967*0Sstevel@tonic-gate    }
968*0Sstevel@tonic-gate
969*0Sstevel@tonic-gate    for(;;) {
970*0Sstevel@tonic-gate	# Skip embedded PODs
971*0Sstevel@tonic-gate	while ($lastline =~ /^=/) {
972*0Sstevel@tonic-gate    	    while ($lastline = <$FH>) {
973*0Sstevel@tonic-gate	    	last if ($lastline =~ /^=cut\s*$/);
974*0Sstevel@tonic-gate	    }
975*0Sstevel@tonic-gate	    death ("Error: Unterminated pod") unless $lastline;
976*0Sstevel@tonic-gate	    $lastline = <$FH>;
977*0Sstevel@tonic-gate	    chomp $lastline;
978*0Sstevel@tonic-gate	    $lastline =~ s/^\s+$//;
979*0Sstevel@tonic-gate	}
980*0Sstevel@tonic-gate	if ($lastline !~ /^\s*#/ ||
981*0Sstevel@tonic-gate	    # CPP directives:
982*0Sstevel@tonic-gate	    #	ANSI:	if ifdef ifndef elif else endif define undef
983*0Sstevel@tonic-gate	    #		line error pragma
984*0Sstevel@tonic-gate	    #	gcc:	warning include_next
985*0Sstevel@tonic-gate	    #   obj-c:	import
986*0Sstevel@tonic-gate	    #   others:	ident (gcc notes that some cpps have this one)
987*0Sstevel@tonic-gate	    $lastline =~ /^#[ \t]*(?:(?:if|ifn?def|elif|else|endif|define|undef|pragma|error|warning|line\s+\d+|ident)\b|(?:include(?:_next)?|import)\s*["<].*[>"])/) {
988*0Sstevel@tonic-gate	    last if $lastline =~ /^\S/ && @line && $line[-1] eq "";
989*0Sstevel@tonic-gate	    push(@line, $lastline);
990*0Sstevel@tonic-gate	    push(@line_no, $lastline_no) ;
991*0Sstevel@tonic-gate	}
992*0Sstevel@tonic-gate
993*0Sstevel@tonic-gate	# Read next line and continuation lines
994*0Sstevel@tonic-gate	last unless defined($lastline = <$FH>);
995*0Sstevel@tonic-gate	$lastline_no = $.;
996*0Sstevel@tonic-gate	my $tmp_line;
997*0Sstevel@tonic-gate	$lastline .= $tmp_line
998*0Sstevel@tonic-gate	    while ($lastline =~ /\\$/ && defined($tmp_line = <$FH>));
999*0Sstevel@tonic-gate
1000*0Sstevel@tonic-gate	chomp $lastline;
1001*0Sstevel@tonic-gate	$lastline =~ s/^\s+$//;
1002*0Sstevel@tonic-gate    }
1003*0Sstevel@tonic-gate    pop(@line), pop(@line_no) while @line && $line[-1] eq "";
1004*0Sstevel@tonic-gate    1;
1005*0Sstevel@tonic-gate}
1006*0Sstevel@tonic-gate
1007*0Sstevel@tonic-gatePARAGRAPH:
1008*0Sstevel@tonic-gatewhile (fetch_para()) {
1009*0Sstevel@tonic-gate    # Print initial preprocessor statements and blank lines
1010*0Sstevel@tonic-gate    while (@line && $line[0] !~ /^[^\#]/) {
1011*0Sstevel@tonic-gate	my $line = shift(@line);
1012*0Sstevel@tonic-gate	print $line, "\n";
1013*0Sstevel@tonic-gate	next unless $line =~ /^\#\s*((if)(?:n?def)?|elsif|else|endif)\b/;
1014*0Sstevel@tonic-gate	my $statement = $+;
1015*0Sstevel@tonic-gate	if ($statement eq 'if') {
1016*0Sstevel@tonic-gate	    $XSS_work_idx = @XSStack;
1017*0Sstevel@tonic-gate	    push(@XSStack, {type => 'if'});
1018*0Sstevel@tonic-gate	} else {
1019*0Sstevel@tonic-gate	    death ("Error: `$statement' with no matching `if'")
1020*0Sstevel@tonic-gate		if $XSStack[-1]{type} ne 'if';
1021*0Sstevel@tonic-gate	    if ($XSStack[-1]{varname}) {
1022*0Sstevel@tonic-gate		push(@InitFileCode, "#endif\n");
1023*0Sstevel@tonic-gate		push(@BootCode,     "#endif");
1024*0Sstevel@tonic-gate	    }
1025*0Sstevel@tonic-gate
1026*0Sstevel@tonic-gate	    my(@fns) = keys %{$XSStack[-1]{functions}};
1027*0Sstevel@tonic-gate	    if ($statement ne 'endif') {
1028*0Sstevel@tonic-gate		# Hide the functions defined in other #if branches, and reset.
1029*0Sstevel@tonic-gate		@{$XSStack[-1]{other_functions}}{@fns} = (1) x @fns;
1030*0Sstevel@tonic-gate		@{$XSStack[-1]}{qw(varname functions)} = ('', {});
1031*0Sstevel@tonic-gate	    } else {
1032*0Sstevel@tonic-gate		my($tmp) = pop(@XSStack);
1033*0Sstevel@tonic-gate		0 while (--$XSS_work_idx
1034*0Sstevel@tonic-gate			 && $XSStack[$XSS_work_idx]{type} ne 'if');
1035*0Sstevel@tonic-gate		# Keep all new defined functions
1036*0Sstevel@tonic-gate		push(@fns, keys %{$tmp->{other_functions}});
1037*0Sstevel@tonic-gate		@{$XSStack[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;
1038*0Sstevel@tonic-gate	    }
1039*0Sstevel@tonic-gate	}
1040*0Sstevel@tonic-gate    }
1041*0Sstevel@tonic-gate
1042*0Sstevel@tonic-gate    next PARAGRAPH unless @line;
1043*0Sstevel@tonic-gate
1044*0Sstevel@tonic-gate    if ($XSS_work_idx && !$XSStack[$XSS_work_idx]{varname}) {
1045*0Sstevel@tonic-gate	# We are inside an #if, but have not yet #defined its xsubpp variable.
1046*0Sstevel@tonic-gate	print "#define $cpp_next_tmp 1\n\n";
1047*0Sstevel@tonic-gate	push(@InitFileCode, "#if $cpp_next_tmp\n");
1048*0Sstevel@tonic-gate	push(@BootCode,     "#if $cpp_next_tmp");
1049*0Sstevel@tonic-gate	$XSStack[$XSS_work_idx]{varname} = $cpp_next_tmp++;
1050*0Sstevel@tonic-gate    }
1051*0Sstevel@tonic-gate
1052*0Sstevel@tonic-gate    death ("Code is not inside a function"
1053*0Sstevel@tonic-gate	   ." (maybe last function was ended by a blank line "
1054*0Sstevel@tonic-gate	   ." followed by a statement on column one?)")
1055*0Sstevel@tonic-gate	if $line[0] =~ /^\s/;
1056*0Sstevel@tonic-gate
1057*0Sstevel@tonic-gate    # initialize info arrays
1058*0Sstevel@tonic-gate    undef(%args_match);
1059*0Sstevel@tonic-gate    undef(%var_types);
1060*0Sstevel@tonic-gate    undef(%defaults);
1061*0Sstevel@tonic-gate    undef($class);
1062*0Sstevel@tonic-gate    undef($static);
1063*0Sstevel@tonic-gate    undef($elipsis);
1064*0Sstevel@tonic-gate    undef($wantRETVAL) ;
1065*0Sstevel@tonic-gate    undef($RETVAL_no_return) ;
1066*0Sstevel@tonic-gate    undef(%arg_list) ;
1067*0Sstevel@tonic-gate    undef(@proto_arg) ;
1068*0Sstevel@tonic-gate    undef(@fake_INPUT_pre) ;	# For length(s) generated variables
1069*0Sstevel@tonic-gate    undef(@fake_INPUT) ;
1070*0Sstevel@tonic-gate    undef($processing_arg_with_types) ;
1071*0Sstevel@tonic-gate    undef(%argtype_seen) ;
1072*0Sstevel@tonic-gate    undef(@outlist) ;
1073*0Sstevel@tonic-gate    undef(%in_out) ;
1074*0Sstevel@tonic-gate    undef(%lengthof) ;
1075*0Sstevel@tonic-gate    # undef(%islengthof) ;
1076*0Sstevel@tonic-gate    undef($proto_in_this_xsub) ;
1077*0Sstevel@tonic-gate    undef($scope_in_this_xsub) ;
1078*0Sstevel@tonic-gate    undef($interface);
1079*0Sstevel@tonic-gate    undef($prepush_done);
1080*0Sstevel@tonic-gate    $interface_macro = 'XSINTERFACE_FUNC' ;
1081*0Sstevel@tonic-gate    $interface_macro_set = 'XSINTERFACE_FUNC_SET' ;
1082*0Sstevel@tonic-gate    $ProtoThisXSUB = $WantPrototypes ;
1083*0Sstevel@tonic-gate    $ScopeThisXSUB = 0;
1084*0Sstevel@tonic-gate    $xsreturn = 0;
1085*0Sstevel@tonic-gate
1086*0Sstevel@tonic-gate    $_ = shift(@line);
1087*0Sstevel@tonic-gate    while ($kwd = check_keyword("REQUIRE|PROTOTYPES|FALLBACK|VERSIONCHECK|INCLUDE")) {
1088*0Sstevel@tonic-gate        &{"${kwd}_handler"}() ;
1089*0Sstevel@tonic-gate        next PARAGRAPH unless @line ;
1090*0Sstevel@tonic-gate        $_ = shift(@line);
1091*0Sstevel@tonic-gate    }
1092*0Sstevel@tonic-gate
1093*0Sstevel@tonic-gate    if (check_keyword("BOOT")) {
1094*0Sstevel@tonic-gate	&check_cpp;
1095*0Sstevel@tonic-gate	push (@BootCode, "#line $line_no[@line_no - @line] \"$filename\"")
1096*0Sstevel@tonic-gate	  if $WantLineNumbers && $line[0] !~ /^\s*#\s*line\b/;
1097*0Sstevel@tonic-gate        push (@BootCode, @line, "") ;
1098*0Sstevel@tonic-gate        next PARAGRAPH ;
1099*0Sstevel@tonic-gate    }
1100*0Sstevel@tonic-gate
1101*0Sstevel@tonic-gate
1102*0Sstevel@tonic-gate    # extract return type, function name and arguments
1103*0Sstevel@tonic-gate    ($ret_type) = TidyType($_);
1104*0Sstevel@tonic-gate    $RETVAL_no_return = 1 if $ret_type =~ s/^NO_OUTPUT\s+//;
1105*0Sstevel@tonic-gate
1106*0Sstevel@tonic-gate    # Allow one-line ANSI-like declaration
1107*0Sstevel@tonic-gate    unshift @line, $2
1108*0Sstevel@tonic-gate      if $process_argtypes
1109*0Sstevel@tonic-gate	and $ret_type =~ s/^(.*?\w.*?)\s*\b(\w+\s*\(.*)/$1/s;
1110*0Sstevel@tonic-gate
1111*0Sstevel@tonic-gate    # a function definition needs at least 2 lines
1112*0Sstevel@tonic-gate    blurt ("Error: Function definition too short '$ret_type'"), next PARAGRAPH
1113*0Sstevel@tonic-gate	unless @line ;
1114*0Sstevel@tonic-gate
1115*0Sstevel@tonic-gate    $static = 1 if $ret_type =~ s/^static\s+//;
1116*0Sstevel@tonic-gate
1117*0Sstevel@tonic-gate    $func_header = shift(@line);
1118*0Sstevel@tonic-gate    blurt ("Error: Cannot parse function definition from '$func_header'"), next PARAGRAPH
1119*0Sstevel@tonic-gate	unless $func_header =~ /^(?:([\w:]*)::)?(\w+)\s*\(\s*(.*?)\s*\)\s*(const)?\s*(;\s*)?$/s;
1120*0Sstevel@tonic-gate
1121*0Sstevel@tonic-gate    ($class, $func_name, $orig_args) =  ($1, $2, $3) ;
1122*0Sstevel@tonic-gate    $class = "$4 $class" if $4;
1123*0Sstevel@tonic-gate    ($pname = $func_name) =~ s/^($Prefix)?/$Packprefix/;
1124*0Sstevel@tonic-gate    ($clean_func_name = $func_name) =~ s/^$Prefix//;
1125*0Sstevel@tonic-gate    $Full_func_name = "${Packid}_$clean_func_name";
1126*0Sstevel@tonic-gate    if ($Is_VMS) { $Full_func_name = $SymSet->addsym($Full_func_name); }
1127*0Sstevel@tonic-gate
1128*0Sstevel@tonic-gate    # Check for duplicate function definition
1129*0Sstevel@tonic-gate    for $tmp (@XSStack) {
1130*0Sstevel@tonic-gate	next unless defined $tmp->{functions}{$Full_func_name};
1131*0Sstevel@tonic-gate	Warn("Warning: duplicate function definition '$clean_func_name' detected");
1132*0Sstevel@tonic-gate	last;
1133*0Sstevel@tonic-gate    }
1134*0Sstevel@tonic-gate    $XSStack[$XSS_work_idx]{functions}{$Full_func_name} ++ ;
1135*0Sstevel@tonic-gate    %XsubAliases = %XsubAliasValues = %Interfaces = @Attributes = ();
1136*0Sstevel@tonic-gate    $DoSetMagic = 1;
1137*0Sstevel@tonic-gate
1138*0Sstevel@tonic-gate    $orig_args =~ s/\\\s*/ /g;		# process line continuations
1139*0Sstevel@tonic-gate
1140*0Sstevel@tonic-gate    my %only_C_inlist;	# Not in the signature of Perl function
1141*0Sstevel@tonic-gate    if ($process_argtypes and $orig_args =~ /\S/) {
1142*0Sstevel@tonic-gate	my $args = "$orig_args ,";
1143*0Sstevel@tonic-gate	if ($args =~ /^( (??{ $C_arg }) , )* $ /x) {
1144*0Sstevel@tonic-gate	    @args = ($args =~ /\G ( (??{ $C_arg }) ) , /xg);
1145*0Sstevel@tonic-gate	    for ( @args ) {
1146*0Sstevel@tonic-gate		s/^\s+//;
1147*0Sstevel@tonic-gate		s/\s+$//;
1148*0Sstevel@tonic-gate		my ($arg, $default) = / ( [^=]* ) ( (?: = .* )? ) /x;
1149*0Sstevel@tonic-gate		my ($pre, $name) = ($arg =~ /(.*?) \s*
1150*0Sstevel@tonic-gate					     \b ( \w+ | length\( \s*\w+\s* \) )
1151*0Sstevel@tonic-gate					     \s* $ /x);
1152*0Sstevel@tonic-gate		next unless length $pre;
1153*0Sstevel@tonic-gate		my $out_type;
1154*0Sstevel@tonic-gate		my $inout_var;
1155*0Sstevel@tonic-gate		if ($process_inout and s/^(IN|IN_OUTLIST|OUTLIST|OUT|IN_OUT)\s+//) {
1156*0Sstevel@tonic-gate		    my $type = $1;
1157*0Sstevel@tonic-gate		    $out_type = $type if $type ne 'IN';
1158*0Sstevel@tonic-gate		    $arg =~ s/^(IN|IN_OUTLIST|OUTLIST|OUT|IN_OUT)\s+//;
1159*0Sstevel@tonic-gate		    $pre =~ s/^(IN|IN_OUTLIST|OUTLIST|OUT|IN_OUT)\s+//;
1160*0Sstevel@tonic-gate		}
1161*0Sstevel@tonic-gate		my $islength;
1162*0Sstevel@tonic-gate		if ($name =~ /^length\( \s* (\w+) \s* \)\z/x) {
1163*0Sstevel@tonic-gate		  $name = "XSauto_length_of_$1";
1164*0Sstevel@tonic-gate		  $islength = 1;
1165*0Sstevel@tonic-gate		  die "Default value on length() argument: `$_'"
1166*0Sstevel@tonic-gate		    if length $default;
1167*0Sstevel@tonic-gate		}
1168*0Sstevel@tonic-gate		if (length $pre or $islength) {	# Has a type
1169*0Sstevel@tonic-gate		    if ($islength) {
1170*0Sstevel@tonic-gate		      push @fake_INPUT_pre, $arg;
1171*0Sstevel@tonic-gate		    } else {
1172*0Sstevel@tonic-gate		      push @fake_INPUT, $arg;
1173*0Sstevel@tonic-gate		    }
1174*0Sstevel@tonic-gate		    # warn "pushing '$arg'\n";
1175*0Sstevel@tonic-gate		    $argtype_seen{$name}++;
1176*0Sstevel@tonic-gate		    $_ = "$name$default"; # Assigns to @args
1177*0Sstevel@tonic-gate		}
1178*0Sstevel@tonic-gate		$only_C_inlist{$_} = 1 if $out_type eq "OUTLIST" or $islength;
1179*0Sstevel@tonic-gate		push @outlist, $name if $out_type =~ /OUTLIST$/;
1180*0Sstevel@tonic-gate		$in_out{$name} = $out_type if $out_type;
1181*0Sstevel@tonic-gate	    }
1182*0Sstevel@tonic-gate	} else {
1183*0Sstevel@tonic-gate	    @args = split(/\s*,\s*/, $orig_args);
1184*0Sstevel@tonic-gate	    Warn("Warning: cannot parse argument list '$orig_args', fallback to split");
1185*0Sstevel@tonic-gate	}
1186*0Sstevel@tonic-gate    } else {
1187*0Sstevel@tonic-gate	@args = split(/\s*,\s*/, $orig_args);
1188*0Sstevel@tonic-gate	for (@args) {
1189*0Sstevel@tonic-gate	    if ($process_inout and s/^(IN|IN_OUTLIST|OUTLIST|IN_OUT|OUT)\s+//) {
1190*0Sstevel@tonic-gate		my $out_type = $1;
1191*0Sstevel@tonic-gate		next if $out_type eq 'IN';
1192*0Sstevel@tonic-gate		$only_C_inlist{$_} = 1 if $out_type eq "OUTLIST";
1193*0Sstevel@tonic-gate		push @outlist, $name if $out_type =~ /OUTLIST$/;
1194*0Sstevel@tonic-gate		$in_out{$_} = $out_type;
1195*0Sstevel@tonic-gate	    }
1196*0Sstevel@tonic-gate	}
1197*0Sstevel@tonic-gate    }
1198*0Sstevel@tonic-gate    if (defined($class)) {
1199*0Sstevel@tonic-gate	my $arg0 = ((defined($static) or $func_name eq 'new')
1200*0Sstevel@tonic-gate		    ? "CLASS" : "THIS");
1201*0Sstevel@tonic-gate	unshift(@args, $arg0);
1202*0Sstevel@tonic-gate	($report_args = "$arg0, $report_args") =~ s/^\w+, $/$arg0/;
1203*0Sstevel@tonic-gate    }
1204*0Sstevel@tonic-gate    my $extra_args = 0;
1205*0Sstevel@tonic-gate    @args_num = ();
1206*0Sstevel@tonic-gate    $num_args = 0;
1207*0Sstevel@tonic-gate    my $report_args = '';
1208*0Sstevel@tonic-gate    foreach $i (0 .. $#args) {
1209*0Sstevel@tonic-gate	    if ($args[$i] =~ s/\.\.\.//) {
1210*0Sstevel@tonic-gate		    $elipsis = 1;
1211*0Sstevel@tonic-gate		    if ($args[$i] eq '' && $i == $#args) {
1212*0Sstevel@tonic-gate		        $report_args .= ", ...";
1213*0Sstevel@tonic-gate			pop(@args);
1214*0Sstevel@tonic-gate			last;
1215*0Sstevel@tonic-gate		    }
1216*0Sstevel@tonic-gate	    }
1217*0Sstevel@tonic-gate	    if ($only_C_inlist{$args[$i]}) {
1218*0Sstevel@tonic-gate		push @args_num, undef;
1219*0Sstevel@tonic-gate	    } else {
1220*0Sstevel@tonic-gate		push @args_num, ++$num_args;
1221*0Sstevel@tonic-gate		$report_args .= ", $args[$i]";
1222*0Sstevel@tonic-gate	    }
1223*0Sstevel@tonic-gate	    if ($args[$i] =~ /^([^=]*[^\s=])\s*=\s*(.*)/s) {
1224*0Sstevel@tonic-gate		    $extra_args++;
1225*0Sstevel@tonic-gate		    $args[$i] = $1;
1226*0Sstevel@tonic-gate		    $defaults{$args[$i]} = $2;
1227*0Sstevel@tonic-gate		    $defaults{$args[$i]} =~ s/"/\\"/g;
1228*0Sstevel@tonic-gate	    }
1229*0Sstevel@tonic-gate	    $proto_arg[$i+1] = "\$" ;
1230*0Sstevel@tonic-gate    }
1231*0Sstevel@tonic-gate    $min_args = $num_args - $extra_args;
1232*0Sstevel@tonic-gate    $report_args =~ s/"/\\"/g;
1233*0Sstevel@tonic-gate    $report_args =~ s/^,\s+//;
1234*0Sstevel@tonic-gate    my @func_args = @args;
1235*0Sstevel@tonic-gate    shift @func_args if defined($class);
1236*0Sstevel@tonic-gate
1237*0Sstevel@tonic-gate    for (@func_args) {
1238*0Sstevel@tonic-gate	s/^/&/ if $in_out{$_};
1239*0Sstevel@tonic-gate    }
1240*0Sstevel@tonic-gate    $func_args = join(", ", @func_args);
1241*0Sstevel@tonic-gate    @args_match{@args} = @args_num;
1242*0Sstevel@tonic-gate
1243*0Sstevel@tonic-gate    $PPCODE = grep(/^\s*PPCODE\s*:/, @line);
1244*0Sstevel@tonic-gate    $CODE = grep(/^\s*CODE\s*:/, @line);
1245*0Sstevel@tonic-gate    # Detect CODE: blocks which use ST(n)= or XST_m*(n,v)
1246*0Sstevel@tonic-gate    #   to set explicit return values.
1247*0Sstevel@tonic-gate    $EXPLICIT_RETURN = ($CODE &&
1248*0Sstevel@tonic-gate		("@line" =~ /(\bST\s*\([^;]*=) | (\bXST_m\w+\s*\()/x ));
1249*0Sstevel@tonic-gate    $ALIAS  = grep(/^\s*ALIAS\s*:/,  @line);
1250*0Sstevel@tonic-gate    $INTERFACE  = grep(/^\s*INTERFACE\s*:/,  @line);
1251*0Sstevel@tonic-gate
1252*0Sstevel@tonic-gate    $xsreturn = 1 if $EXPLICIT_RETURN;
1253*0Sstevel@tonic-gate
1254*0Sstevel@tonic-gate    # print function header
1255*0Sstevel@tonic-gate    print Q<<"EOF";
1256*0Sstevel@tonic-gate#XS(XS_${Full_func_name}); /* prototype to pass -Wmissing-prototypes */
1257*0Sstevel@tonic-gate#XS(XS_${Full_func_name})
1258*0Sstevel@tonic-gate#[[
1259*0Sstevel@tonic-gate#    dXSARGS;
1260*0Sstevel@tonic-gateEOF
1261*0Sstevel@tonic-gate    print Q<<"EOF" if $ALIAS ;
1262*0Sstevel@tonic-gate#    dXSI32;
1263*0Sstevel@tonic-gateEOF
1264*0Sstevel@tonic-gate    print Q<<"EOF" if $INTERFACE ;
1265*0Sstevel@tonic-gate#    dXSFUNCTION($ret_type);
1266*0Sstevel@tonic-gateEOF
1267*0Sstevel@tonic-gate    if ($elipsis) {
1268*0Sstevel@tonic-gate	$cond = ($min_args ? qq(items < $min_args) : 0);
1269*0Sstevel@tonic-gate    }
1270*0Sstevel@tonic-gate    elsif ($min_args == $num_args) {
1271*0Sstevel@tonic-gate	$cond = qq(items != $min_args);
1272*0Sstevel@tonic-gate    }
1273*0Sstevel@tonic-gate    else {
1274*0Sstevel@tonic-gate	$cond = qq(items < $min_args || items > $num_args);
1275*0Sstevel@tonic-gate    }
1276*0Sstevel@tonic-gate
1277*0Sstevel@tonic-gate    print Q<<"EOF" if $except;
1278*0Sstevel@tonic-gate#    char errbuf[1024];
1279*0Sstevel@tonic-gate#    *errbuf = '\0';
1280*0Sstevel@tonic-gateEOF
1281*0Sstevel@tonic-gate
1282*0Sstevel@tonic-gate    if ($ALIAS)
1283*0Sstevel@tonic-gate      { print Q<<"EOF" if $cond }
1284*0Sstevel@tonic-gate#    if ($cond)
1285*0Sstevel@tonic-gate#       Perl_croak(aTHX_ "Usage: %s($report_args)", GvNAME(CvGV(cv)));
1286*0Sstevel@tonic-gateEOF
1287*0Sstevel@tonic-gate    else
1288*0Sstevel@tonic-gate      { print Q<<"EOF" if $cond }
1289*0Sstevel@tonic-gate#    if ($cond)
1290*0Sstevel@tonic-gate#	Perl_croak(aTHX_ "Usage: $pname($report_args)");
1291*0Sstevel@tonic-gateEOF
1292*0Sstevel@tonic-gate
1293*0Sstevel@tonic-gate    #gcc -Wall: if an xsub has no arguments and PPCODE is used
1294*0Sstevel@tonic-gate    #it is likely none of ST, XSRETURN or XSprePUSH macros are used
1295*0Sstevel@tonic-gate    #hence `ax' (setup by dXSARGS) is unused
1296*0Sstevel@tonic-gate    #XXX: could breakup the dXSARGS; into dSP;dMARK;dITEMS
1297*0Sstevel@tonic-gate    #but such a move could break third-party extensions
1298*0Sstevel@tonic-gate    print Q<<"EOF" if $PPCODE and $num_args == 0;
1299*0Sstevel@tonic-gate#   PERL_UNUSED_VAR(ax); /* -Wall */
1300*0Sstevel@tonic-gateEOF
1301*0Sstevel@tonic-gate
1302*0Sstevel@tonic-gate    print Q<<"EOF" if $PPCODE;
1303*0Sstevel@tonic-gate#    SP -= items;
1304*0Sstevel@tonic-gateEOF
1305*0Sstevel@tonic-gate
1306*0Sstevel@tonic-gate    # Now do a block of some sort.
1307*0Sstevel@tonic-gate
1308*0Sstevel@tonic-gate    $condnum = 0;
1309*0Sstevel@tonic-gate    $cond = '';			# last CASE: condidional
1310*0Sstevel@tonic-gate    push(@line, "$END:");
1311*0Sstevel@tonic-gate    push(@line_no, $line_no[-1]);
1312*0Sstevel@tonic-gate    $_ = '';
1313*0Sstevel@tonic-gate    &check_cpp;
1314*0Sstevel@tonic-gate    while (@line) {
1315*0Sstevel@tonic-gate	&CASE_handler if check_keyword("CASE");
1316*0Sstevel@tonic-gate	print Q<<"EOF";
1317*0Sstevel@tonic-gate#   $except [[
1318*0Sstevel@tonic-gateEOF
1319*0Sstevel@tonic-gate
1320*0Sstevel@tonic-gate	# do initialization of input variables
1321*0Sstevel@tonic-gate	$thisdone = 0;
1322*0Sstevel@tonic-gate	$retvaldone = 0;
1323*0Sstevel@tonic-gate	$deferred = "";
1324*0Sstevel@tonic-gate	%arg_list = () ;
1325*0Sstevel@tonic-gate        $gotRETVAL = 0;
1326*0Sstevel@tonic-gate
1327*0Sstevel@tonic-gate	INPUT_handler() ;
1328*0Sstevel@tonic-gate	process_keyword("INPUT|PREINIT|INTERFACE_MACRO|C_ARGS|ALIAS|ATTRS|PROTOTYPE|SCOPE|OVERLOAD") ;
1329*0Sstevel@tonic-gate
1330*0Sstevel@tonic-gate	print Q<<"EOF" if $ScopeThisXSUB;
1331*0Sstevel@tonic-gate#   ENTER;
1332*0Sstevel@tonic-gate#   [[
1333*0Sstevel@tonic-gateEOF
1334*0Sstevel@tonic-gate
1335*0Sstevel@tonic-gate	if (!$thisdone && defined($class)) {
1336*0Sstevel@tonic-gate	    if (defined($static) or $func_name eq 'new') {
1337*0Sstevel@tonic-gate		print "\tchar *";
1338*0Sstevel@tonic-gate		$var_types{"CLASS"} = "char *";
1339*0Sstevel@tonic-gate		&generate_init("char *", 1, "CLASS");
1340*0Sstevel@tonic-gate	    }
1341*0Sstevel@tonic-gate	    else {
1342*0Sstevel@tonic-gate		print "\t$class *";
1343*0Sstevel@tonic-gate		$var_types{"THIS"} = "$class *";
1344*0Sstevel@tonic-gate		&generate_init("$class *", 1, "THIS");
1345*0Sstevel@tonic-gate	    }
1346*0Sstevel@tonic-gate	}
1347*0Sstevel@tonic-gate
1348*0Sstevel@tonic-gate	# do code
1349*0Sstevel@tonic-gate	if (/^\s*NOT_IMPLEMENTED_YET/) {
1350*0Sstevel@tonic-gate		print "\n\tPerl_croak(aTHX_ \"$pname: not implemented yet\");\n";
1351*0Sstevel@tonic-gate		$_ = '' ;
1352*0Sstevel@tonic-gate	} else {
1353*0Sstevel@tonic-gate		if ($ret_type ne "void") {
1354*0Sstevel@tonic-gate			print "\t" . &map_type($ret_type, 'RETVAL') . ";\n"
1355*0Sstevel@tonic-gate				if !$retvaldone;
1356*0Sstevel@tonic-gate			$args_match{"RETVAL"} = 0;
1357*0Sstevel@tonic-gate			$var_types{"RETVAL"} = $ret_type;
1358*0Sstevel@tonic-gate			print "\tdXSTARG;\n"
1359*0Sstevel@tonic-gate				if $WantOptimize and $targetable{$type_kind{$ret_type}};
1360*0Sstevel@tonic-gate		}
1361*0Sstevel@tonic-gate
1362*0Sstevel@tonic-gate		if (@fake_INPUT or @fake_INPUT_pre) {
1363*0Sstevel@tonic-gate		    unshift @line, @fake_INPUT_pre, @fake_INPUT, $_;
1364*0Sstevel@tonic-gate		    $_ = "";
1365*0Sstevel@tonic-gate		    $processing_arg_with_types = 1;
1366*0Sstevel@tonic-gate		    INPUT_handler() ;
1367*0Sstevel@tonic-gate		}
1368*0Sstevel@tonic-gate		print $deferred;
1369*0Sstevel@tonic-gate
1370*0Sstevel@tonic-gate        process_keyword("INIT|ALIAS|ATTRS|PROTOTYPE|INTERFACE_MACRO|INTERFACE|C_ARGS|OVERLOAD") ;
1371*0Sstevel@tonic-gate
1372*0Sstevel@tonic-gate		if (check_keyword("PPCODE")) {
1373*0Sstevel@tonic-gate			print_section();
1374*0Sstevel@tonic-gate			death ("PPCODE must be last thing") if @line;
1375*0Sstevel@tonic-gate			print "\tLEAVE;\n" if $ScopeThisXSUB;
1376*0Sstevel@tonic-gate			print "\tPUTBACK;\n\treturn;\n";
1377*0Sstevel@tonic-gate		} elsif (check_keyword("CODE")) {
1378*0Sstevel@tonic-gate			print_section() ;
1379*0Sstevel@tonic-gate		} elsif (defined($class) and $func_name eq "DESTROY") {
1380*0Sstevel@tonic-gate			print "\n\t";
1381*0Sstevel@tonic-gate			print "delete THIS;\n";
1382*0Sstevel@tonic-gate		} else {
1383*0Sstevel@tonic-gate			print "\n\t";
1384*0Sstevel@tonic-gate			if ($ret_type ne "void") {
1385*0Sstevel@tonic-gate				print "RETVAL = ";
1386*0Sstevel@tonic-gate				$wantRETVAL = 1;
1387*0Sstevel@tonic-gate			}
1388*0Sstevel@tonic-gate			if (defined($static)) {
1389*0Sstevel@tonic-gate			    if ($func_name eq 'new') {
1390*0Sstevel@tonic-gate				$func_name = "$class";
1391*0Sstevel@tonic-gate			    } else {
1392*0Sstevel@tonic-gate				print "${class}::";
1393*0Sstevel@tonic-gate			    }
1394*0Sstevel@tonic-gate			} elsif (defined($class)) {
1395*0Sstevel@tonic-gate			    if ($func_name eq 'new') {
1396*0Sstevel@tonic-gate				$func_name .= " $class";
1397*0Sstevel@tonic-gate			    } else {
1398*0Sstevel@tonic-gate				print "THIS->";
1399*0Sstevel@tonic-gate			    }
1400*0Sstevel@tonic-gate			}
1401*0Sstevel@tonic-gate			$func_name =~ s/^($spat)//
1402*0Sstevel@tonic-gate			    if defined($spat);
1403*0Sstevel@tonic-gate			$func_name = 'XSFUNCTION' if $interface;
1404*0Sstevel@tonic-gate			print "$func_name($func_args);\n";
1405*0Sstevel@tonic-gate		}
1406*0Sstevel@tonic-gate	}
1407*0Sstevel@tonic-gate
1408*0Sstevel@tonic-gate	# do output variables
1409*0Sstevel@tonic-gate	$gotRETVAL = 0;		# 1 if RETVAL seen in OUTPUT section;
1410*0Sstevel@tonic-gate	undef $RETVAL_code ;	# code to set RETVAL (from OUTPUT section);
1411*0Sstevel@tonic-gate	# $wantRETVAL set if 'RETVAL =' autogenerated
1412*0Sstevel@tonic-gate	($wantRETVAL, $ret_type) = (0, 'void') if $RETVAL_no_return;
1413*0Sstevel@tonic-gate	undef %outargs ;
1414*0Sstevel@tonic-gate	process_keyword("POSTCALL|OUTPUT|ALIAS|ATTRS|PROTOTYPE|OVERLOAD");
1415*0Sstevel@tonic-gate
1416*0Sstevel@tonic-gate	&generate_output($var_types{$_}, $args_match{$_}, $_, $DoSetMagic)
1417*0Sstevel@tonic-gate	  for grep $in_out{$_} =~ /OUT$/, keys %in_out;
1418*0Sstevel@tonic-gate
1419*0Sstevel@tonic-gate	# all OUTPUT done, so now push the return value on the stack
1420*0Sstevel@tonic-gate	if ($gotRETVAL && $RETVAL_code) {
1421*0Sstevel@tonic-gate	    print "\t$RETVAL_code\n";
1422*0Sstevel@tonic-gate	} elsif ($gotRETVAL || $wantRETVAL) {
1423*0Sstevel@tonic-gate	    my $t = $WantOptimize && $targetable{$type_kind{$ret_type}};
1424*0Sstevel@tonic-gate	    my $var = 'RETVAL';
1425*0Sstevel@tonic-gate	    my $type = $ret_type;
1426*0Sstevel@tonic-gate
1427*0Sstevel@tonic-gate	    # 0: type, 1: with_size, 2: how, 3: how_size
1428*0Sstevel@tonic-gate	    if ($t and not $t->[1] and $t->[0] eq 'p') {
1429*0Sstevel@tonic-gate		# PUSHp corresponds to setpvn.  Treate setpv directly
1430*0Sstevel@tonic-gate		my $what = eval qq("$t->[2]");
1431*0Sstevel@tonic-gate		warn $@ if $@;
1432*0Sstevel@tonic-gate
1433*0Sstevel@tonic-gate		print "\tsv_setpv(TARG, $what); XSprePUSH; PUSHTARG;\n";
1434*0Sstevel@tonic-gate		$prepush_done = 1;
1435*0Sstevel@tonic-gate	    }
1436*0Sstevel@tonic-gate	    elsif ($t) {
1437*0Sstevel@tonic-gate		my $what = eval qq("$t->[2]");
1438*0Sstevel@tonic-gate		warn $@ if $@;
1439*0Sstevel@tonic-gate
1440*0Sstevel@tonic-gate		my $size = $t->[3];
1441*0Sstevel@tonic-gate		$size = '' unless defined $size;
1442*0Sstevel@tonic-gate		$size = eval qq("$size");
1443*0Sstevel@tonic-gate		warn $@ if $@;
1444*0Sstevel@tonic-gate		print "\tXSprePUSH; PUSH$t->[0]($what$size);\n";
1445*0Sstevel@tonic-gate		$prepush_done = 1;
1446*0Sstevel@tonic-gate	    }
1447*0Sstevel@tonic-gate	    else {
1448*0Sstevel@tonic-gate		# RETVAL almost never needs SvSETMAGIC()
1449*0Sstevel@tonic-gate		&generate_output($ret_type, 0, 'RETVAL', 0);
1450*0Sstevel@tonic-gate	    }
1451*0Sstevel@tonic-gate	}
1452*0Sstevel@tonic-gate
1453*0Sstevel@tonic-gate	$xsreturn = 1 if $ret_type ne "void";
1454*0Sstevel@tonic-gate	my $num = $xsreturn;
1455*0Sstevel@tonic-gate	my $c = @outlist;
1456*0Sstevel@tonic-gate	# (PP)CODE set different values of SP; reset to PPCODE's with 0 output
1457*0Sstevel@tonic-gate	print "\tXSprePUSH;"    if $c and not $prepush_done;
1458*0Sstevel@tonic-gate	# Take into account stuff already put on stack
1459*0Sstevel@tonic-gate	print "\t++SP;"         if $c and not $prepush_done and $xsreturn;
1460*0Sstevel@tonic-gate	# Now SP corresponds to ST($xsreturn), so one can combine PUSH and ST()
1461*0Sstevel@tonic-gate	print "\tEXTEND(SP,$c);\n" if $c;
1462*0Sstevel@tonic-gate	$xsreturn += $c;
1463*0Sstevel@tonic-gate	generate_output($var_types{$_}, $num++, $_, 0, 1) for @outlist;
1464*0Sstevel@tonic-gate
1465*0Sstevel@tonic-gate	# do cleanup
1466*0Sstevel@tonic-gate	process_keyword("CLEANUP|ALIAS|ATTRS|PROTOTYPE|OVERLOAD") ;
1467*0Sstevel@tonic-gate
1468*0Sstevel@tonic-gate	print Q<<"EOF" if $ScopeThisXSUB;
1469*0Sstevel@tonic-gate#   ]]
1470*0Sstevel@tonic-gateEOF
1471*0Sstevel@tonic-gate	print Q<<"EOF" if $ScopeThisXSUB and not $PPCODE;
1472*0Sstevel@tonic-gate#   LEAVE;
1473*0Sstevel@tonic-gateEOF
1474*0Sstevel@tonic-gate
1475*0Sstevel@tonic-gate	# print function trailer
1476*0Sstevel@tonic-gate	print Q<<EOF;
1477*0Sstevel@tonic-gate#    ]]
1478*0Sstevel@tonic-gateEOF
1479*0Sstevel@tonic-gate	print Q<<EOF if $except;
1480*0Sstevel@tonic-gate#    BEGHANDLERS
1481*0Sstevel@tonic-gate#    CATCHALL
1482*0Sstevel@tonic-gate#	sprintf(errbuf, "%s: %s\\tpropagated", Xname, Xreason);
1483*0Sstevel@tonic-gate#    ENDHANDLERS
1484*0Sstevel@tonic-gateEOF
1485*0Sstevel@tonic-gate	if (check_keyword("CASE")) {
1486*0Sstevel@tonic-gate	    blurt ("Error: No `CASE:' at top of function")
1487*0Sstevel@tonic-gate		unless $condnum;
1488*0Sstevel@tonic-gate	    $_ = "CASE: $_";	# Restore CASE: label
1489*0Sstevel@tonic-gate	    next;
1490*0Sstevel@tonic-gate	}
1491*0Sstevel@tonic-gate	last if $_ eq "$END:";
1492*0Sstevel@tonic-gate	death(/^$BLOCK_re/o ? "Misplaced `$1:'" : "Junk at end of function");
1493*0Sstevel@tonic-gate    }
1494*0Sstevel@tonic-gate
1495*0Sstevel@tonic-gate    print Q<<EOF if $except;
1496*0Sstevel@tonic-gate#    if (errbuf[0])
1497*0Sstevel@tonic-gate#	Perl_croak(aTHX_ errbuf);
1498*0Sstevel@tonic-gateEOF
1499*0Sstevel@tonic-gate
1500*0Sstevel@tonic-gate    if ($xsreturn) {
1501*0Sstevel@tonic-gate        print Q<<EOF unless $PPCODE;
1502*0Sstevel@tonic-gate#    XSRETURN($xsreturn);
1503*0Sstevel@tonic-gateEOF
1504*0Sstevel@tonic-gate    } else {
1505*0Sstevel@tonic-gate        print Q<<EOF unless $PPCODE;
1506*0Sstevel@tonic-gate#    XSRETURN_EMPTY;
1507*0Sstevel@tonic-gateEOF
1508*0Sstevel@tonic-gate    }
1509*0Sstevel@tonic-gate
1510*0Sstevel@tonic-gate    print Q<<EOF;
1511*0Sstevel@tonic-gate#]]
1512*0Sstevel@tonic-gate#
1513*0Sstevel@tonic-gateEOF
1514*0Sstevel@tonic-gate
1515*0Sstevel@tonic-gate    my $newXS = "newXS" ;
1516*0Sstevel@tonic-gate    my $proto = "" ;
1517*0Sstevel@tonic-gate
1518*0Sstevel@tonic-gate    # Build the prototype string for the xsub
1519*0Sstevel@tonic-gate    if ($ProtoThisXSUB) {
1520*0Sstevel@tonic-gate	$newXS = "newXSproto";
1521*0Sstevel@tonic-gate
1522*0Sstevel@tonic-gate	if ($ProtoThisXSUB eq 2) {
1523*0Sstevel@tonic-gate	    # User has specified empty prototype
1524*0Sstevel@tonic-gate	    $proto = ', ""' ;
1525*0Sstevel@tonic-gate	}
1526*0Sstevel@tonic-gate        elsif ($ProtoThisXSUB ne 1) {
1527*0Sstevel@tonic-gate            # User has specified a prototype
1528*0Sstevel@tonic-gate            $proto = ', "' . $ProtoThisXSUB . '"';
1529*0Sstevel@tonic-gate        }
1530*0Sstevel@tonic-gate        else {
1531*0Sstevel@tonic-gate	    my $s = ';';
1532*0Sstevel@tonic-gate            if ($min_args < $num_args)  {
1533*0Sstevel@tonic-gate                $s = '';
1534*0Sstevel@tonic-gate		$proto_arg[$min_args] .= ";" ;
1535*0Sstevel@tonic-gate	    }
1536*0Sstevel@tonic-gate            push @proto_arg, "$s\@"
1537*0Sstevel@tonic-gate                if $elipsis ;
1538*0Sstevel@tonic-gate
1539*0Sstevel@tonic-gate            $proto = ', "' . join ("", @proto_arg) . '"';
1540*0Sstevel@tonic-gate        }
1541*0Sstevel@tonic-gate    }
1542*0Sstevel@tonic-gate
1543*0Sstevel@tonic-gate    if (%XsubAliases) {
1544*0Sstevel@tonic-gate	$XsubAliases{$pname} = 0
1545*0Sstevel@tonic-gate	    unless defined $XsubAliases{$pname} ;
1546*0Sstevel@tonic-gate	while ( ($name, $value) = each %XsubAliases) {
1547*0Sstevel@tonic-gate	    push(@InitFileCode, Q<<"EOF");
1548*0Sstevel@tonic-gate#        cv = newXS(\"$name\", XS_$Full_func_name, file);
1549*0Sstevel@tonic-gate#        XSANY.any_i32 = $value ;
1550*0Sstevel@tonic-gateEOF
1551*0Sstevel@tonic-gate	push(@InitFileCode, Q<<"EOF") if $proto;
1552*0Sstevel@tonic-gate#        sv_setpv((SV*)cv$proto) ;
1553*0Sstevel@tonic-gateEOF
1554*0Sstevel@tonic-gate        }
1555*0Sstevel@tonic-gate    }
1556*0Sstevel@tonic-gate    elsif (@Attributes) {
1557*0Sstevel@tonic-gate	    push(@InitFileCode, Q<<"EOF");
1558*0Sstevel@tonic-gate#        cv = newXS(\"$pname\", XS_$Full_func_name, file);
1559*0Sstevel@tonic-gate#        apply_attrs_string("$Package", cv, "@Attributes", 0);
1560*0Sstevel@tonic-gateEOF
1561*0Sstevel@tonic-gate    }
1562*0Sstevel@tonic-gate    elsif ($interface) {
1563*0Sstevel@tonic-gate	while ( ($name, $value) = each %Interfaces) {
1564*0Sstevel@tonic-gate	    $name = "$Package\::$name" unless $name =~ /::/;
1565*0Sstevel@tonic-gate	    push(@InitFileCode, Q<<"EOF");
1566*0Sstevel@tonic-gate#        cv = newXS(\"$name\", XS_$Full_func_name, file);
1567*0Sstevel@tonic-gate#        $interface_macro_set(cv,$value) ;
1568*0Sstevel@tonic-gateEOF
1569*0Sstevel@tonic-gate	    push(@InitFileCode, Q<<"EOF") if $proto;
1570*0Sstevel@tonic-gate#        sv_setpv((SV*)cv$proto) ;
1571*0Sstevel@tonic-gateEOF
1572*0Sstevel@tonic-gate        }
1573*0Sstevel@tonic-gate    }
1574*0Sstevel@tonic-gate    else {
1575*0Sstevel@tonic-gate	push(@InitFileCode,
1576*0Sstevel@tonic-gate	     "        ${newXS}(\"$pname\", XS_$Full_func_name, file$proto);\n");
1577*0Sstevel@tonic-gate    }
1578*0Sstevel@tonic-gate}
1579*0Sstevel@tonic-gate
1580*0Sstevel@tonic-gateif ($Overload) # make it findable with fetchmethod
1581*0Sstevel@tonic-gate{
1582*0Sstevel@tonic-gate
1583*0Sstevel@tonic-gate    print Q<<"EOF";
1584*0Sstevel@tonic-gate#XS(XS_${Packid}_nil); /* prototype to pass -Wmissing-prototypes */
1585*0Sstevel@tonic-gate#XS(XS_${Packid}_nil)
1586*0Sstevel@tonic-gate#{
1587*0Sstevel@tonic-gate#   XSRETURN_EMPTY;
1588*0Sstevel@tonic-gate#}
1589*0Sstevel@tonic-gate#
1590*0Sstevel@tonic-gateEOF
1591*0Sstevel@tonic-gate    unshift(@InitFileCode, <<"MAKE_FETCHMETHOD_WORK");
1592*0Sstevel@tonic-gate    /* Making a sub named "${Package}::()" allows the package */
1593*0Sstevel@tonic-gate    /* to be findable via fetchmethod(), and causes */
1594*0Sstevel@tonic-gate    /* overload::Overloaded("${Package}") to return true. */
1595*0Sstevel@tonic-gate    newXS("${Package}::()", XS_${Packid}_nil, file$proto);
1596*0Sstevel@tonic-gateMAKE_FETCHMETHOD_WORK
1597*0Sstevel@tonic-gate}
1598*0Sstevel@tonic-gate
1599*0Sstevel@tonic-gate# print initialization routine
1600*0Sstevel@tonic-gate
1601*0Sstevel@tonic-gateprint Q<<"EOF";
1602*0Sstevel@tonic-gate##ifdef __cplusplus
1603*0Sstevel@tonic-gate#extern "C"
1604*0Sstevel@tonic-gate##endif
1605*0Sstevel@tonic-gateEOF
1606*0Sstevel@tonic-gate
1607*0Sstevel@tonic-gateprint Q<<"EOF";
1608*0Sstevel@tonic-gate#XS(boot_$Module_cname); /* prototype to pass -Wmissing-prototypes */
1609*0Sstevel@tonic-gate#XS(boot_$Module_cname)
1610*0Sstevel@tonic-gateEOF
1611*0Sstevel@tonic-gate
1612*0Sstevel@tonic-gateprint Q<<"EOF";
1613*0Sstevel@tonic-gate#[[
1614*0Sstevel@tonic-gate#    dXSARGS;
1615*0Sstevel@tonic-gateEOF
1616*0Sstevel@tonic-gate
1617*0Sstevel@tonic-gate#-Wall: if there is no $Full_func_name there are no xsubs in this .xs
1618*0Sstevel@tonic-gate#so `file' is unused
1619*0Sstevel@tonic-gateprint Q<<"EOF" if $Full_func_name;
1620*0Sstevel@tonic-gate#    char* file = __FILE__;
1621*0Sstevel@tonic-gateEOF
1622*0Sstevel@tonic-gate
1623*0Sstevel@tonic-gateprint Q "#\n";
1624*0Sstevel@tonic-gate
1625*0Sstevel@tonic-gateprint Q<<"EOF" if $WantVersionChk ;
1626*0Sstevel@tonic-gate#    XS_VERSION_BOOTCHECK ;
1627*0Sstevel@tonic-gate#
1628*0Sstevel@tonic-gateEOF
1629*0Sstevel@tonic-gate
1630*0Sstevel@tonic-gateprint Q<<"EOF" if defined $XsubAliases or defined $Interfaces ;
1631*0Sstevel@tonic-gate#    {
1632*0Sstevel@tonic-gate#        CV * cv ;
1633*0Sstevel@tonic-gate#
1634*0Sstevel@tonic-gateEOF
1635*0Sstevel@tonic-gate
1636*0Sstevel@tonic-gateprint Q<<"EOF" if ($Overload);
1637*0Sstevel@tonic-gate#    /* register the overloading (type 'A') magic */
1638*0Sstevel@tonic-gate#    PL_amagic_generation++;
1639*0Sstevel@tonic-gate#    /* The magic for overload gets a GV* via gv_fetchmeth as */
1640*0Sstevel@tonic-gate#    /* mentioned above, and looks in the SV* slot of it for */
1641*0Sstevel@tonic-gate#    /* the "fallback" status. */
1642*0Sstevel@tonic-gate#    sv_setsv(
1643*0Sstevel@tonic-gate#        get_sv( "${Package}::()", TRUE ),
1644*0Sstevel@tonic-gate#        $Fallback
1645*0Sstevel@tonic-gate#    );
1646*0Sstevel@tonic-gateEOF
1647*0Sstevel@tonic-gate
1648*0Sstevel@tonic-gateprint @InitFileCode;
1649*0Sstevel@tonic-gate
1650*0Sstevel@tonic-gateprint Q<<"EOF" if defined $XsubAliases or defined $Interfaces ;
1651*0Sstevel@tonic-gate#    }
1652*0Sstevel@tonic-gateEOF
1653*0Sstevel@tonic-gate
1654*0Sstevel@tonic-gateif (@BootCode)
1655*0Sstevel@tonic-gate{
1656*0Sstevel@tonic-gate    print "\n    /* Initialisation Section */\n\n" ;
1657*0Sstevel@tonic-gate    @line = @BootCode;
1658*0Sstevel@tonic-gate    print_section();
1659*0Sstevel@tonic-gate    print "\n    /* End of Initialisation Section */\n\n" ;
1660*0Sstevel@tonic-gate}
1661*0Sstevel@tonic-gate
1662*0Sstevel@tonic-gateprint Q<<"EOF";;
1663*0Sstevel@tonic-gate#    XSRETURN_YES;
1664*0Sstevel@tonic-gate#]]
1665*0Sstevel@tonic-gate#
1666*0Sstevel@tonic-gateEOF
1667*0Sstevel@tonic-gate
1668*0Sstevel@tonic-gatewarn("Please specify prototyping behavior for $filename (see perlxs manual)\n")
1669*0Sstevel@tonic-gate    unless $ProtoUsed ;
1670*0Sstevel@tonic-gate&Exit;
1671*0Sstevel@tonic-gate
1672*0Sstevel@tonic-gatesub output_init {
1673*0Sstevel@tonic-gate    local($type, $num, $var, $init, $name_printed) = @_;
1674*0Sstevel@tonic-gate    local($arg) = "ST(" . ($num - 1) . ")";
1675*0Sstevel@tonic-gate
1676*0Sstevel@tonic-gate    if(  $init =~ /^=/  ) {
1677*0Sstevel@tonic-gate        if ($name_printed) {
1678*0Sstevel@tonic-gate	  eval qq/print " $init\\n"/;
1679*0Sstevel@tonic-gate	} else {
1680*0Sstevel@tonic-gate	  eval qq/print "\\t$var $init\\n"/;
1681*0Sstevel@tonic-gate	}
1682*0Sstevel@tonic-gate	warn $@   if  $@;
1683*0Sstevel@tonic-gate    } else {
1684*0Sstevel@tonic-gate	if(  $init =~ s/^\+//  &&  $num  ) {
1685*0Sstevel@tonic-gate	    &generate_init($type, $num, $var, $name_printed);
1686*0Sstevel@tonic-gate	} elsif ($name_printed) {
1687*0Sstevel@tonic-gate	    print ";\n";
1688*0Sstevel@tonic-gate	    $init =~ s/^;//;
1689*0Sstevel@tonic-gate	} else {
1690*0Sstevel@tonic-gate	    eval qq/print "\\t$var;\\n"/;
1691*0Sstevel@tonic-gate	    warn $@   if  $@;
1692*0Sstevel@tonic-gate	    $init =~ s/^;//;
1693*0Sstevel@tonic-gate	}
1694*0Sstevel@tonic-gate	$deferred .= eval qq/"\\n\\t$init\\n"/;
1695*0Sstevel@tonic-gate	warn $@   if  $@;
1696*0Sstevel@tonic-gate    }
1697*0Sstevel@tonic-gate}
1698*0Sstevel@tonic-gate
1699*0Sstevel@tonic-gatesub Warn
1700*0Sstevel@tonic-gate{
1701*0Sstevel@tonic-gate    # work out the line number
1702*0Sstevel@tonic-gate    my $line_no = $line_no[@line_no - @line -1] ;
1703*0Sstevel@tonic-gate
1704*0Sstevel@tonic-gate    print STDERR "@_ in $filename, line $line_no\n" ;
1705*0Sstevel@tonic-gate}
1706*0Sstevel@tonic-gate
1707*0Sstevel@tonic-gatesub blurt
1708*0Sstevel@tonic-gate{
1709*0Sstevel@tonic-gate    Warn @_ ;
1710*0Sstevel@tonic-gate    $errors ++
1711*0Sstevel@tonic-gate}
1712*0Sstevel@tonic-gate
1713*0Sstevel@tonic-gatesub death
1714*0Sstevel@tonic-gate{
1715*0Sstevel@tonic-gate    Warn @_ ;
1716*0Sstevel@tonic-gate    exit 1 ;
1717*0Sstevel@tonic-gate}
1718*0Sstevel@tonic-gate
1719*0Sstevel@tonic-gatesub generate_init {
1720*0Sstevel@tonic-gate    local($type, $num, $var) = @_;
1721*0Sstevel@tonic-gate    local($arg) = "ST(" . ($num - 1) . ")";
1722*0Sstevel@tonic-gate    local($argoff) = $num - 1;
1723*0Sstevel@tonic-gate    local($ntype);
1724*0Sstevel@tonic-gate    local($tk);
1725*0Sstevel@tonic-gate
1726*0Sstevel@tonic-gate    $type = TidyType($type) ;
1727*0Sstevel@tonic-gate    blurt("Error: '$type' not in typemap"), return
1728*0Sstevel@tonic-gate	unless defined($type_kind{$type});
1729*0Sstevel@tonic-gate
1730*0Sstevel@tonic-gate    ($ntype = $type) =~ s/\s*\*/Ptr/g;
1731*0Sstevel@tonic-gate    ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;
1732*0Sstevel@tonic-gate    $tk = $type_kind{$type};
1733*0Sstevel@tonic-gate    $tk =~ s/OBJ$/REF/ if $func_name =~ /DESTROY$/;
1734*0Sstevel@tonic-gate    if ($tk eq 'T_PV' and exists $lengthof{$var}) {
1735*0Sstevel@tonic-gate      print "\t$var" unless $name_printed;
1736*0Sstevel@tonic-gate      print " = ($type)SvPV($arg, STRLEN_length_of_$var);\n";
1737*0Sstevel@tonic-gate      die "default value not supported with length(NAME) supplied"
1738*0Sstevel@tonic-gate	if defined $defaults{$var};
1739*0Sstevel@tonic-gate      return;
1740*0Sstevel@tonic-gate    }
1741*0Sstevel@tonic-gate    $type =~ tr/:/_/ unless $hiertype;
1742*0Sstevel@tonic-gate    blurt("Error: No INPUT definition for type '$type', typekind '$type_kind{$type}' found"), return
1743*0Sstevel@tonic-gate        unless defined $input_expr{$tk} ;
1744*0Sstevel@tonic-gate    $expr = $input_expr{$tk};
1745*0Sstevel@tonic-gate    if ($expr =~ /DO_ARRAY_ELEM/) {
1746*0Sstevel@tonic-gate        blurt("Error: '$subtype' not in typemap"), return
1747*0Sstevel@tonic-gate	    unless defined($type_kind{$subtype});
1748*0Sstevel@tonic-gate        blurt("Error: No INPUT definition for type '$subtype', typekind '$type_kind{$subtype}' found"), return
1749*0Sstevel@tonic-gate            unless defined $input_expr{$type_kind{$subtype}} ;
1750*0Sstevel@tonic-gate	$subexpr = $input_expr{$type_kind{$subtype}};
1751*0Sstevel@tonic-gate        $subexpr =~ s/\$type/\$subtype/g;
1752*0Sstevel@tonic-gate	$subexpr =~ s/ntype/subtype/g;
1753*0Sstevel@tonic-gate	$subexpr =~ s/\$arg/ST(ix_$var)/g;
1754*0Sstevel@tonic-gate	$subexpr =~ s/\n\t/\n\t\t/g;
1755*0Sstevel@tonic-gate	$subexpr =~ s/is not of (.*\")/[arg %d] is not of $1, ix_$var + 1/g;
1756*0Sstevel@tonic-gate	$subexpr =~ s/\$var/${var}[ix_$var - $argoff]/;
1757*0Sstevel@tonic-gate	$expr =~ s/DO_ARRAY_ELEM/$subexpr/;
1758*0Sstevel@tonic-gate    }
1759*0Sstevel@tonic-gate    if ($expr =~ m#/\*.*scope.*\*/#i) { # "scope" in C comments
1760*0Sstevel@tonic-gate    	$ScopeThisXSUB = 1;
1761*0Sstevel@tonic-gate    }
1762*0Sstevel@tonic-gate    if (defined($defaults{$var})) {
1763*0Sstevel@tonic-gate	    $expr =~ s/(\t+)/$1    /g;
1764*0Sstevel@tonic-gate	    $expr =~ s/        /\t/g;
1765*0Sstevel@tonic-gate	    if ($name_printed) {
1766*0Sstevel@tonic-gate	      print ";\n";
1767*0Sstevel@tonic-gate	    } else {
1768*0Sstevel@tonic-gate	      eval qq/print "\\t$var;\\n"/;
1769*0Sstevel@tonic-gate	      warn $@   if  $@;
1770*0Sstevel@tonic-gate	    }
1771*0Sstevel@tonic-gate	    if ($defaults{$var} eq 'NO_INIT') {
1772*0Sstevel@tonic-gate		$deferred .= eval qq/"\\n\\tif (items >= $num) {\\n$expr;\\n\\t}\\n"/;
1773*0Sstevel@tonic-gate	    } else {
1774*0Sstevel@tonic-gate		$deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t    $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/;
1775*0Sstevel@tonic-gate	    }
1776*0Sstevel@tonic-gate	    warn $@   if  $@;
1777*0Sstevel@tonic-gate    } elsif ($ScopeThisXSUB or $expr !~ /^\s*\$var =/) {
1778*0Sstevel@tonic-gate	    if ($name_printed) {
1779*0Sstevel@tonic-gate	      print ";\n";
1780*0Sstevel@tonic-gate	    } else {
1781*0Sstevel@tonic-gate	      eval qq/print "\\t$var;\\n"/;
1782*0Sstevel@tonic-gate	      warn $@   if  $@;
1783*0Sstevel@tonic-gate	    }
1784*0Sstevel@tonic-gate	    $deferred .= eval qq/"\\n$expr;\\n"/;
1785*0Sstevel@tonic-gate	    warn $@   if  $@;
1786*0Sstevel@tonic-gate    } else {
1787*0Sstevel@tonic-gate	    die "panic: do not know how to handle this branch for function pointers"
1788*0Sstevel@tonic-gate	      if $name_printed;
1789*0Sstevel@tonic-gate	    eval qq/print "$expr;\\n"/;
1790*0Sstevel@tonic-gate	    warn $@   if  $@;
1791*0Sstevel@tonic-gate    }
1792*0Sstevel@tonic-gate}
1793*0Sstevel@tonic-gate
1794*0Sstevel@tonic-gatesub generate_output {
1795*0Sstevel@tonic-gate    local($type, $num, $var, $do_setmagic, $do_push) = @_;
1796*0Sstevel@tonic-gate    local($arg) = "ST(" . ($num - ($num != 0)) . ")";
1797*0Sstevel@tonic-gate    local($argoff) = $num - 1;
1798*0Sstevel@tonic-gate    local($ntype);
1799*0Sstevel@tonic-gate
1800*0Sstevel@tonic-gate    $type = TidyType($type) ;
1801*0Sstevel@tonic-gate    if ($type =~ /^array\(([^,]*),(.*)\)/) {
1802*0Sstevel@tonic-gate            print "\t$arg = sv_newmortal();\n";
1803*0Sstevel@tonic-gate	    print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1));\n";
1804*0Sstevel@tonic-gate	    print "\tSvSETMAGIC($arg);\n" if $do_setmagic;
1805*0Sstevel@tonic-gate    } else {
1806*0Sstevel@tonic-gate	    blurt("Error: '$type' not in typemap"), return
1807*0Sstevel@tonic-gate		unless defined($type_kind{$type});
1808*0Sstevel@tonic-gate            blurt("Error: No OUTPUT definition for type '$type', typekind '$type_kind{$type}' found"), return
1809*0Sstevel@tonic-gate                unless defined $output_expr{$type_kind{$type}} ;
1810*0Sstevel@tonic-gate	    ($ntype = $type) =~ s/\s*\*/Ptr/g;
1811*0Sstevel@tonic-gate	    $ntype =~ s/\(\)//g;
1812*0Sstevel@tonic-gate	    ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;
1813*0Sstevel@tonic-gate	    $expr = $output_expr{$type_kind{$type}};
1814*0Sstevel@tonic-gate	    if ($expr =~ /DO_ARRAY_ELEM/) {
1815*0Sstevel@tonic-gate	        blurt("Error: '$subtype' not in typemap"), return
1816*0Sstevel@tonic-gate		    unless defined($type_kind{$subtype});
1817*0Sstevel@tonic-gate                blurt("Error: No OUTPUT definition for type '$subtype', typekind '$type_kind{$subtype}' found"), return
1818*0Sstevel@tonic-gate                    unless defined $output_expr{$type_kind{$subtype}} ;
1819*0Sstevel@tonic-gate		$subexpr = $output_expr{$type_kind{$subtype}};
1820*0Sstevel@tonic-gate		$subexpr =~ s/ntype/subtype/g;
1821*0Sstevel@tonic-gate		$subexpr =~ s/\$arg/ST(ix_$var)/g;
1822*0Sstevel@tonic-gate		$subexpr =~ s/\$var/${var}[ix_$var]/g;
1823*0Sstevel@tonic-gate		$subexpr =~ s/\n\t/\n\t\t/g;
1824*0Sstevel@tonic-gate		$expr =~ s/DO_ARRAY_ELEM\n/$subexpr/;
1825*0Sstevel@tonic-gate		eval "print qq\a$expr\a";
1826*0Sstevel@tonic-gate		warn $@   if  $@;
1827*0Sstevel@tonic-gate		print "\t\tSvSETMAGIC(ST(ix_$var));\n" if $do_setmagic;
1828*0Sstevel@tonic-gate	    }
1829*0Sstevel@tonic-gate	    elsif ($var eq 'RETVAL') {
1830*0Sstevel@tonic-gate		if ($expr =~ /^\t\$arg = new/) {
1831*0Sstevel@tonic-gate		    # We expect that $arg has refcnt 1, so we need to
1832*0Sstevel@tonic-gate		    # mortalize it.
1833*0Sstevel@tonic-gate		    eval "print qq\a$expr\a";
1834*0Sstevel@tonic-gate		    warn $@   if  $@;
1835*0Sstevel@tonic-gate		    print "\tsv_2mortal(ST($num));\n";
1836*0Sstevel@tonic-gate		    print "\tSvSETMAGIC(ST($num));\n" if $do_setmagic;
1837*0Sstevel@tonic-gate		}
1838*0Sstevel@tonic-gate		elsif ($expr =~ /^\s*\$arg\s*=/) {
1839*0Sstevel@tonic-gate		    # We expect that $arg has refcnt >=1, so we need
1840*0Sstevel@tonic-gate		    # to mortalize it!
1841*0Sstevel@tonic-gate		    eval "print qq\a$expr\a";
1842*0Sstevel@tonic-gate		    warn $@   if  $@;
1843*0Sstevel@tonic-gate		    print "\tsv_2mortal(ST(0));\n";
1844*0Sstevel@tonic-gate		    print "\tSvSETMAGIC(ST(0));\n" if $do_setmagic;
1845*0Sstevel@tonic-gate		}
1846*0Sstevel@tonic-gate		else {
1847*0Sstevel@tonic-gate		    # Just hope that the entry would safely write it
1848*0Sstevel@tonic-gate		    # over an already mortalized value. By
1849*0Sstevel@tonic-gate		    # coincidence, something like $arg = &sv_undef
1850*0Sstevel@tonic-gate		    # works too.
1851*0Sstevel@tonic-gate		    print "\tST(0) = sv_newmortal();\n";
1852*0Sstevel@tonic-gate		    eval "print qq\a$expr\a";
1853*0Sstevel@tonic-gate		    warn $@   if  $@;
1854*0Sstevel@tonic-gate		    # new mortals don't have set magic
1855*0Sstevel@tonic-gate		}
1856*0Sstevel@tonic-gate	    }
1857*0Sstevel@tonic-gate	    elsif ($do_push) {
1858*0Sstevel@tonic-gate	        print "\tPUSHs(sv_newmortal());\n";
1859*0Sstevel@tonic-gate		$arg = "ST($num)";
1860*0Sstevel@tonic-gate		eval "print qq\a$expr\a";
1861*0Sstevel@tonic-gate		warn $@   if  $@;
1862*0Sstevel@tonic-gate		print "\tSvSETMAGIC($arg);\n" if $do_setmagic;
1863*0Sstevel@tonic-gate	    }
1864*0Sstevel@tonic-gate	    elsif ($arg =~ /^ST\(\d+\)$/) {
1865*0Sstevel@tonic-gate		eval "print qq\a$expr\a";
1866*0Sstevel@tonic-gate		warn $@   if  $@;
1867*0Sstevel@tonic-gate		print "\tSvSETMAGIC($arg);\n" if $do_setmagic;
1868*0Sstevel@tonic-gate	    }
1869*0Sstevel@tonic-gate    }
1870*0Sstevel@tonic-gate}
1871*0Sstevel@tonic-gate
1872*0Sstevel@tonic-gatesub map_type {
1873*0Sstevel@tonic-gate    my($type, $varname) = @_;
1874*0Sstevel@tonic-gate
1875*0Sstevel@tonic-gate    # C++ has :: in types too so skip this
1876*0Sstevel@tonic-gate    $type =~ tr/:/_/ unless $hiertype;
1877*0Sstevel@tonic-gate    $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
1878*0Sstevel@tonic-gate    if ($varname) {
1879*0Sstevel@tonic-gate      if ($varname && $type =~ / \( \s* \* (?= \s* \) ) /xg) {
1880*0Sstevel@tonic-gate	(substr $type, pos $type, 0) = " $varname ";
1881*0Sstevel@tonic-gate      } else {
1882*0Sstevel@tonic-gate	$type .= "\t$varname";
1883*0Sstevel@tonic-gate      }
1884*0Sstevel@tonic-gate    }
1885*0Sstevel@tonic-gate    $type;
1886*0Sstevel@tonic-gate}
1887*0Sstevel@tonic-gate
1888*0Sstevel@tonic-gate
1889*0Sstevel@tonic-gatesub Exit {
1890*0Sstevel@tonic-gate# If this is VMS, the exit status has meaning to the shell, so we
1891*0Sstevel@tonic-gate# use a predictable value (SS$_Normal or SS$_Abort) rather than an
1892*0Sstevel@tonic-gate# arbitrary number.
1893*0Sstevel@tonic-gate#    exit ($Is_VMS ? ($errors ? 44 : 1) : $errors) ;
1894*0Sstevel@tonic-gate    exit ($errors ? 1 : 0);
1895*0Sstevel@tonic-gate}
1896