1N/A # Establish set of global symbols with max length 28, since xsubpp 1N/A # will later add the 'XS_' prefix. 1N/A$usage = "Usage: xsubpp [-v] [-C++] [-except] [-prototypes] [-noversioncheck] [-nolinenumbers] [-nooptimize] [-noinout] [-noargtypes] [-s pattern] [-typemap typemap]... file.xs\n"; 1N/A # XXX left this in for compat 1N/Amy(@XSStack) = ({type => 'none'}); # Stack of conditionals and INCLUDEs 1N/A $_[0] =~ s/^\s+|\s+$//go ; 1N/A # rationalise any '*' by joining them into bunches and removing whitespace 1N/A # change multiple whitespace into a single space 1N/A # trim leading & trailing whitespace 1N/A # skip directories, binary files etc. 1N/A # skip blank lines and comment lines 1N/A # prototype defaults to '$' 1N/A ( (??{ $size }) )? # Possible sizeof set-from 1N/A$END = "!End!\n\n"; # "impossible" keyword (multiple newline) 1N/A# Match an XS keyword 1N/A# Input: ($_, @line) == unparsed input. 1N/A# Output: ($_, @line) == (rest of line, following lines). 1N/A# Return: the matched keyword if found, otherwise 0 1N/A s/^(\s*)($_[0])\s*:\s*(?:#.*)?/$1/s && $2; 1N/A# Group in C (no support for comments or literals) 1N/A# Chunk in C without comma at toplevel (no comments): 1N/A | " (?: (?> [^\\"]+ ) 1N/A )* " # String literal 1N/A | ' (?: (?> [^\\']+ ) | \\. )* ' # Char literal 1N/A # Not necessary if we're careful to end with a "\n" 1N/A # the "do" is required for right semantics 1N/A &{"${kwd}_handler"}() 1N/A last if /^\s*NOT_IMPLEMENTED_YET/; 1N/A next unless /\S/; # skip blank lines 1N/A TrimWhitespace($_) ; 1N/A # remove trailing semicolon if no initialisation 1N/A s/\s*;$//g unless /[=;+].*\S/ ; 1N/A # Process the length(foo) declarations 1N/A if (s/^([^=]*)\blength\(\s*(\w+)\s*\)\s*$/$1 XSauto_length_of_$2=NO_INIT/x) { 1N/A # check for optional initialisation code 1N/A # Check for duplicate definitions 1N/A # XXXX This check is a safeguard against the unfinished conversion of 1N/A # generate_init(). When generate_init() is fixed, 1N/A # one can use 2-args map_type() unconditionally. 1N/A # Function pointers are not yet supported with &output_init! 1N/A # generate initialization code 1N/A if (/^\s*SETMAGIC\s*:\s*(ENABLE|DISABLE)\s*/) { 1N/A # deal with RETVAL last 1N/Asub C_ARGS_handler() { 1N/Asub INTERFACE_MACRO_handler() { 1N/Asub INTERFACE_handler() { 1N/A foreach (split /[\s,]+/, $in) { 1N/Asub CLEANUP_handler() { print_section() } 1N/Asub PREINIT_handler() { print_section() } 1N/Asub POSTCALL_handler() { print_section() } 1N/Asub INIT_handler() { print_section() } 1N/A # Parse alias definitions 1N/A # alias = value alias = value ... 1N/A while ($line =~ s/^\s*([\w:]+)\s*=\s*(\w+)\s*//) { 1N/A # check for optional package definition in the alias 1N/A # check for duplicate alias name & duplicate value 1N/A TrimWhitespace($_) ; 1N/A push @Attributes, $_; 1N/A TrimWhitespace($_) ; 1N/A GetAliases($_) if $_ ; 1N/Asub OVERLOAD_handler() 1N/A TrimWhitespace($_) ; 1N/A while ( s/^\s*([\w:"\\)\+\-\*\/\%\<\>\.\&\|\^\!\~\{\}\=]+)\s*//) { 1N/A # the rest of the current line should contain either TRUE, 1N/A TRUE => "PL_sv_yes", 1 => "PL_sv_yes", 1N/A # check for valid FALLBACK value 1N/A # the rest of the current line should contain a version number 1N/A death ("Error: REQUIRE expects a version number") 1N/A # check that the version number is of the form n.n 1N/A # the rest of the current line should contain either ENABLE or 1N/A death("Error: Only 1 PROTOTYPE definition allowed per xsub") 1N/A # remove any whitespace 1N/A # If no prototype specified, then assume empty prototype "" 1N/A death("Error: Only 1 SCOPE declaration allowed per xsub") 1N/A # the rest of the current line should contain either ENABLE or 1N/A # the rest of the current line should contain a valid filename 1N/A death("INCLUDE: output pipe is illegal") 1N/A # simple minded recursion detector 1N/A # Save the current file context. 1N/A#/* INCLUDE: Including '$_' from '$filename' */ 1N/A # Prime the pump by reading the first 1N/A # skip leading blank lines 1N/A#/* INCLUDE: Returning to '$filename' from '$ThisFile' */ 1N/A print STDERR " (precede it with a blank line if the matching #if is outside the function)\n" 1N/A# Identify the version of xsubpp used 1N/A * This file was generated automatically by xsubpp version $XSUBPP_version from the 1N/A * contents of $filename. Do not edit this file, edit $filename instead. 1N/A * ANY CHANGES MADE HERE WILL BE LOST! 1N/A # We can't just write out a /* */ comment, as our embedded 1N/A # POD might itself be in a comment. We can't put a /**/ 1N/A # comment inside #if 0, as the C standard says that the source 1N/A # file is decomposed into preprocessing characters in the stage 1N/A # before preprocessing commands are executed. 1N/A # I don't want to leave the text as barewords, because the spec 1N/A # isn't clear whether macros are expanded before or after 1N/A # preprocessing commands are executed, and someone pathological 1N/A # may just have defined one of the 3 words as a macro that does 1N/A # something strange. Multiline strings are illegal in C, so 1N/A # the "" we write must be a string literal. And they aren't 1N/A # concatenated until 2 steps later, so we are safe. 1N/A print("#if 0\n \"Skipped embedded POD.\"\n#endif\n"); 1N/A # At this point $. is at end of file so die won't state the start 1N/A # of the problem, and as we haven't yet read any lines &death won't 1N/A # show the correct line in the message either. 1N/A# Read next xsub into @line from ($lastline, <$FH>). 1N/A death ("Error: Unterminated `#if/#ifdef/#ifndef'") 1N/A # Skip embedded PODs 1N/A # ANSI: if ifdef ifndef elif else endif define undef 1N/A # gcc: warning include_next 1N/A # others: ident (gcc notes that some cpps have this one) 1N/A $lastline =~ /^#[ \t]*(?:(?:if|ifn?def|elif|else|endif|define|undef|pragma|error|warning|line\s+\d+|ident)\b|(?:include(?:_next)?|import)\s*["<].*[>"])/) { 1N/A # Read next line and continuation lines 1N/A # Print initial preprocessor statements and blank lines 1N/A push(@InitFileCode, "#endif\n"); 1N/A # Hide the functions defined in other #if branches, and reset. 1N/A # Keep all new defined functions 1N/A # We are inside an #if, but have not yet #defined its xsubpp variable. 1N/A ." (maybe last function was ended by a blank line " 1N/A ." followed by a statement on column one?)") 1N/A # initialize info arrays 1N/A # undef(%islengthof) ; 1N/A &{"${kwd}_handler"}() ; 1N/A # extract return type, function name and arguments 1N/A # Allow one-line ANSI-like declaration 1N/A # a function definition needs at least 2 lines 1N/A # Check for duplicate function definition 1N/A die "Default value on length() argument: `$_'" 1N/A if (length $pre or $islength) { # Has a type 1N/A push @fake_INPUT_pre, $arg; 1N/A push @fake_INPUT, $arg; 1N/A $argtype_seen{$name}++; 1N/A $_ = "$name$default"; # Assigns to @args 1N/A $only_C_inlist{$_} = 1 if $out_type eq "OUTLIST" or $islength; 1N/A push @outlist, $name if $out_type =~ /OUTLIST$/; 1N/A $in_out{$name} = $out_type if $out_type; 1N/A @args = split(/\s*,\s*/, $orig_args); 1N/A Warn("Warning: cannot parse argument list '$orig_args', fallback to split"); 1N/A @args = split(/\s*,\s*/, $orig_args); 1N/A if ($process_inout and s/^(IN|IN_OUTLIST|OUTLIST|IN_OUT|OUT)\s+//) { 1N/A next if $out_type eq 'IN'; 1N/A $only_C_inlist{$_} = 1 if $out_type eq "OUTLIST"; 1N/A push @outlist, $name if $out_type =~ /OUTLIST$/; 1N/A $in_out{$_} = $out_type; 1N/A if (defined($class)) { 1N/A my $arg0 = ((defined($static) or $func_name eq 'new') 1N/A ? "CLASS" : "THIS"); 1N/A unshift(@args, $arg0); 1N/A ($report_args = "$arg0, $report_args") =~ s/^\w+, $/$arg0/; 1N/A my $report_args = ''; 1N/A foreach $i (0 .. $#args) { 1N/A if ($args[$i] =~ s/\.\.\.//) { 1N/A if ($args[$i] eq '' && $i == $#args) { 1N/A $report_args .= ", ..."; 1N/A if ($only_C_inlist{$args[$i]}) { 1N/A push @args_num, undef; 1N/A push @args_num, ++$num_args; 1N/A $report_args .= ", $args[$i]"; 1N/A if ($args[$i] =~ /^([^=]*[^\s=])\s*=\s*(.*)/s) { 1N/A $defaults{$args[$i]} = $2; 1N/A $defaults{$args[$i]} =~ s/"/\\"/g; 1N/A $proto_arg[$i+1] = "\$" ; 1N/A $min_args = $num_args - $extra_args; 1N/A $report_args =~ s/"/\\"/g; 1N/A $report_args =~ s/^,\s+//; 1N/A my @func_args = @args; 1N/A shift @func_args if defined($class); 1N/A s/^/&/ if $in_out{$_}; 1N/A $func_args = join(", ", @func_args); 1N/A @args_match{@args} = @args_num; 1N/A $PPCODE = grep(/^\s*PPCODE\s*:/, @line); 1N/A $CODE = grep(/^\s*CODE\s*:/, @line); 1N/A # Detect CODE: blocks which use ST(n)= or XST_m*(n,v) 1N/A # to set explicit return values. 1N/A $EXPLICIT_RETURN = ($CODE && 1N/A ("@line" =~ /(\bST\s*\([^;]*=) | (\bXST_m\w+\s*\()/x )); 1N/A $ALIAS = grep(/^\s*ALIAS\s*:/, @line); 1N/A $INTERFACE = grep(/^\s*INTERFACE\s*:/, @line); 1N/A $xsreturn = 1 if $EXPLICIT_RETURN; 1N/A # print function header 1N/A#XS(XS_${Full_func_name}); /* prototype to pass -Wmissing-prototypes */ 1N/A#XS(XS_${Full_func_name}) 1N/A print Q<<"EOF" if $ALIAS ; 1N/A print Q<<"EOF" if $INTERFACE ; 1N/A# dXSFUNCTION($ret_type); 1N/A $cond = ($min_args ? qq(items < $min_args) : 0); 1N/A elsif ($min_args == $num_args) { 1N/A $cond = qq(items != $min_args); 1N/A $cond = qq(items < $min_args || items > $num_args); 1N/A print Q<<"EOF" if $except; 1N/A { print Q<<"EOF" if $cond } 1N/A# Perl_croak(aTHX_ "Usage: %s($report_args)", GvNAME(CvGV(cv))); 1N/A { print Q<<"EOF" if $cond } 1N/A# Perl_croak(aTHX_ "Usage: $pname($report_args)"); 1N/A #gcc -Wall: if an xsub has no arguments and PPCODE is used 1N/A #it is likely none of ST, XSRETURN or XSprePUSH macros are used 1N/A #XXX: could breakup the dXSARGS; into dSP;dMARK;dITEMS 1N/A #but such a move could break third-party extensions 1N/A# PERL_UNUSED_VAR(ax); /* -Wall */ 1N/A # Now do a block of some sort. 1N/A # do initialization of input variables 1N/A process_keyword("INPUT|PREINIT|INTERFACE_MACRO|C_ARGS|ALIAS|ATTRS|PROTOTYPE|SCOPE|OVERLOAD") ; 1N/A print "\n\tPerl_croak(aTHX_ \"$pname: not implemented yet\");\n"; 1N/A print "\tdXSTARG;\n" 1N/A print "\tPUTBACK;\n\treturn;\n"; 1N/A print "delete THIS;\n"; 1N/A # do output variables 1N/A # $wantRETVAL set if 'RETVAL =' autogenerated 1N/A # all OUTPUT done, so now push the return value on the stack 1N/A # 0: type, 1: with_size, 2: how, 3: how_size 1N/A # PUSHp corresponds to setpvn. Treate setpv directly 1N/A print "\tsv_setpv(TARG, $what); XSprePUSH; PUSHTARG;\n"; 1N/A # RETVAL almost never needs SvSETMAGIC() 1N/A # (PP)CODE set different values of SP; reset to PPCODE's with 0 output 1N/A # Take into account stuff already put on stack 1N/A # Now SP corresponds to ST($xsreturn), so one can combine PUSH and ST() 1N/A print "\tEXTEND(SP,$c);\n" if $c; 1N/A # print function trailer 1N/A print Q<<EOF if $except; 1N/A# sprintf(errbuf, "%s: %s\\tpropagated", Xname, Xreason); 1N/A $_ = "CASE: $_"; # Restore CASE: label 1N/A last if $_ eq "$END:"; 1N/A death(/^$BLOCK_re/o ? "Misplaced `$1:'" : "Junk at end of function"); 1N/A# Perl_croak(aTHX_ errbuf); 1N/A # Build the prototype string for the xsub 1N/A # User has specified empty prototype 1N/A # User has specified a prototype 1N/A push @proto_arg, "$s\@" 1N/A $proto = ', "' . join ("", @proto_arg) . '"'; 1N/A push(@InitFileCode, Q<<"EOF"); 1N/A elsif (@Attributes) { 1N/A push(@InitFileCode, Q<<"EOF"); 1N/A push(@InitFileCode, Q<<"EOF"); 1N/A#XS(XS_${Packid}_nil); /* prototype to pass -Wmissing-prototypes */ 1N/A#XS(XS_${Packid}_nil) 1N/A /* Making a sub named "${Package}::()" allows the package */ 1N/A /* to be findable via fetchmethod(), and causes */ 1N/A /* overload::Overloaded("${Package}") to return true. */ 1N/AMAKE_FETCHMETHOD_WORK 1N/A# print initialization routine 1N/Aprint Q<<"EOF" if $Full_func_name; 1N/A# char* file = __FILE__; 1N/Aprint Q<<"EOF" if $WantVersionChk ; 1N/A# XS_VERSION_BOOTCHECK ; 1N/Aprint Q<<"EOF" if defined $XsubAliases or defined $Interfaces ; 1N/Aprint Q<<"EOF" if ($Overload); 1N/A# /* register the overloading (type 'A') magic */ 1N/A# PL_amagic_generation++; 1N/A# /* The magic for overload gets a GV* via gv_fetchmeth as */ 1N/A# /* mentioned above, and looks in the SV* slot of it for */ 1N/A# /* the "fallback" status. */ 1N/A# get_sv( "${Package}::()", TRUE ), 1N/Aprint Q<<"EOF" if defined $XsubAliases or defined $Interfaces ; 1N/A print "\n /* Initialisation Section */\n\n" ; 1N/A print "\n /* End of Initialisation Section */\n\n" ; 1N/Awarn("Please specify prototyping behavior for $filename (see perlxs manual)\n") 1N/A local($type, $num, $var, $init, $name_printed) = @_; 1N/A local($arg) = "ST(" . ($num - 1) . ")"; 1N/A if( $init =~ /^=/ ) { 1N/A if ($name_printed) { 1N/A if( $init =~ s/^\+// && $num ) { 1N/A &generate_init($type, $num, $var, $name_printed); 1N/A } elsif ($name_printed) { 1N/A $deferred .= eval qq/"\\n\\t$init\\n"/; 1N/A # work out the line number 1N/A my $line_no = $line_no[@line_no - @line -1] ; 1N/A print STDERR "@_ in $filename, line $line_no\n" ; 1N/A local($type, $num, $var) = @_; 1N/A local($arg) = "ST(" . ($num - 1) . ")"; 1N/A local($argoff) = $num - 1; 1N/A $type = TidyType($type) ; 1N/A blurt("Error: '$type' not in typemap"), return 1N/A unless defined($type_kind{$type}); 1N/A ($ntype = $type) =~ s/\s*\*/Ptr/g; 1N/A ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//; 1N/A $tk = $type_kind{$type}; 1N/A $tk =~ s/OBJ$/REF/ if $func_name =~ /DESTROY$/; 1N/A if ($tk eq 'T_PV' and exists $lengthof{$var}) { 1N/A print "\t$var" unless $name_printed; 1N/A print " = ($type)SvPV($arg, STRLEN_length_of_$var);\n"; 1N/A die "default value not supported with length(NAME) supplied" 1N/A if defined $defaults{$var}; 1N/A $type =~ tr/:/_/ unless $hiertype; 1N/A unless defined $input_expr{$tk} ; 1N/A $expr = $input_expr{$tk}; 1N/A if ($expr =~ /DO_ARRAY_ELEM/) { 1N/A unless defined($type_kind{$subtype}); 1N/A unless defined $input_expr{$type_kind{$subtype}} ; 1N/A $subexpr = $input_expr{$type_kind{$subtype}}; 1N/A $subexpr =~ s/\$type/\$subtype/g; 1N/A $subexpr =~ s/\n\t/\n\t\t/g; 1N/A $subexpr =~ s/is not of (.*\")/[arg %d] is not of $1, ix_$var + 1/g; 1N/A $subexpr =~ s/\$var/${var}[ix_$var - $argoff]/; 1N/A if ($expr =~ m#/\*.*scope.*\*/#i) { # "scope" in C comments 1N/A if (defined($defaults{$var})) { 1N/A $expr =~ s/(\t+)/$1 /g; 1N/A if ($name_printed) { 1N/A $deferred .= eval qq/"\\n\\tif (items >= $num) {\\n$expr;\\n\\t}\\n"/; 1N/A $deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/; 1N/A } elsif ($ScopeThisXSUB or $expr !~ /^\s*\$var =/) { 1N/A if ($name_printed) { 1N/A $deferred .= eval qq/"\\n$expr;\\n"/; 1N/A die "panic: do not know how to handle this branch for function pointers" 1N/Asub generate_output { 1N/A local($type, $num, $var, $do_setmagic, $do_push) = @_; 1N/A local($arg) = "ST(" . ($num - ($num != 0)) . ")"; 1N/A local($argoff) = $num - 1; 1N/A $type = TidyType($type) ; 1N/A if ($type =~ /^array\(([^,]*),(.*)\)/) { 1N/A print "\t$arg = sv_newmortal();\n"; 1N/A print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1));\n"; 1N/A print "\tSvSETMAGIC($arg);\n" if $do_setmagic; 1N/A blurt("Error: '$type' not in typemap"), return 1N/A unless defined($type_kind{$type}); 1N/A unless defined $output_expr{$type_kind{$type}} ; 1N/A ($ntype = $type) =~ s/\s*\*/Ptr/g; 1N/A $ntype =~ s/\(\)//g; 1N/A ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//; 1N/A $expr = $output_expr{$type_kind{$type}}; 1N/A if ($expr =~ /DO_ARRAY_ELEM/) { 1N/A unless defined($type_kind{$subtype}); 1N/A unless defined $output_expr{$type_kind{$subtype}} ; 1N/A $subexpr = $output_expr{$type_kind{$subtype}}; 1N/A $subexpr =~ s/\$var/${var}[ix_$var]/g; 1N/A $subexpr =~ s/\n\t/\n\t\t/g; 1N/A eval "print qq\a$expr\a"; 1N/A print "\t\tSvSETMAGIC(ST(ix_$var));\n" if $do_setmagic; 1N/A if ($expr =~ /^\t\$arg = new/) { 1N/A # We expect that $arg has refcnt 1, so we need to 1N/A eval "print qq\a$expr\a"; 1N/A print "\tsv_2mortal(ST($num));\n"; 1N/A print "\tSvSETMAGIC(ST($num));\n" if $do_setmagic; 1N/A elsif ($expr =~ /^\s*\$arg\s*=/) { 1N/A # We expect that $arg has refcnt >=1, so we need 1N/A eval "print qq\a$expr\a"; 1N/A print "\tsv_2mortal(ST(0));\n"; 1N/A print "\tSvSETMAGIC(ST(0));\n" if $do_setmagic; 1N/A # Just hope that the entry would safely write it 1N/A # over an already mortalized value. By 1N/A # coincidence, something like $arg = &sv_undef 1N/A print "\tST(0) = sv_newmortal();\n"; 1N/A eval "print qq\a$expr\a"; 1N/A print "\tPUSHs(sv_newmortal());\n"; 1N/A # C++ has :: in types too so skip this 1N/A# If this is VMS, the exit status has meaning to the shell, so we 1N/A# use a predictable value (SS$_Normal or SS$_Abort) rather than an 1N/A# exit ($Is_VMS ? ($errors ? 44 : 1) : $errors) ;