# remove trailing semicolon if no initialisation
s/\s*;$//g unless /[=;+].*\S/ ;
+ # Process the length(foo) declarations
+ if (s/^([^=]*)\blength\(\s*(\w+)\s*\)\s*$/$1 XSauto_length_of_$2=NO_INIT/x) {
+ print "\tSTRLEN\tSTRLEN_length_of_$2;\n";
+ $lengthof{$2} = $name;
+ # $islengthof{$name} = $1;
+ $deferred .= "\n\tXSauto_length_of_$2 = STRLEN_length_of_$2;";
+ }
+
# check for optional initialisation code
my $var_init = '' ;
$var_init = $1 if s/\s*([=;+].*)$//s ;
# Check for duplicate definitions
blurt ("Error: duplicate definition of argument '$var_name' ignored"), next
if $arg_list{$var_name}++
- or defined $arg_types{$var_name} and not $processing_arg_with_types;
+ or defined $argtype_seen{$var_name} and not $processing_arg_with_types;
$thisdone |= $var_name eq "THIS";
$retvaldone |= $var_name eq "RETVAL";
undef($RETVAL_no_return) ;
undef(%arg_list) ;
undef(@proto_arg) ;
- undef(@arg_with_types) ;
+ undef(@fake_INPUT_pre) ; # For length(s) generated variables
+ undef(@fake_INPUT) ;
undef($processing_arg_with_types) ;
- undef(%arg_types) ;
+ undef(%argtype_seen) ;
undef(@outlist) ;
undef(%in_out) ;
+ undef(%lengthof) ;
+ # undef(%islengthof) ;
undef($proto_in_this_xsub) ;
undef($scope_in_this_xsub) ;
undef($interface);
$orig_args =~ s/\\\s*/ /g; # process line continuations
- my %only_outlist;
+ my %only_C_inlist; # Not in the signature of Perl function
if ($process_argtypes and $orig_args =~ /\S/) {
my $args = "$orig_args ,";
if ($args =~ /^( (??{ $C_arg }) , )* $ /x) {
for ( @args ) {
s/^\s+//;
s/\s+$//;
- my $arg = $_;
- my $default;
- ($arg, $default) = / ( [^=]* ) ( (?: = .* )? ) /x;
- my ($pre, $name) = ($arg =~ /(.*?) \s* \b(\w+) \s* $ /x);
+ my ($arg, $default) = / ( [^=]* ) ( (?: = .* )? ) /x;
+ my ($pre, $name) = ($arg =~ /(.*?) \s*
+ \b ( \w+ | length\( \s*\w+\s* \) )
+ \s* $ /x);
next unless length $pre;
my $out_type;
my $inout_var;
my $type = $1;
$out_type = $type if $type ne 'IN';
$arg =~ s/^(IN|IN_OUTLIST|OUTLIST|OUT|IN_OUT)\s+//;
+ $pre =~ s/^(IN|IN_OUTLIST|OUTLIST|OUT|IN_OUT)\s+//;
+ }
+ my $islength;
+ if ($name =~ /^length\( \s* (\w+) \s* \)\z/x) {
+ $name = "XSauto_length_of_$1";
+ $islength = 1;
+ die "Default value on length() argument: `$_'"
+ if length $default;
}
- if (/\W/) { # Has a type
- push @arg_with_types, $arg;
+ if (length $pre or $islength) { # Has a type
+ if ($islength) {
+ push @fake_INPUT_pre, $arg;
+ } else {
+ push @fake_INPUT, $arg;
+ }
# warn "pushing '$arg'\n";
- $arg_types{$name} = $arg;
- $_ = "$name$default";
+ $argtype_seen{$name}++;
+ $_ = "$name$default"; # Assigns to @args
}
- $only_outlist{$_} = 1 if $out_type eq "OUTLIST";
+ $only_C_inlist{$_} = 1 if $out_type eq "OUTLIST" or $islength;
push @outlist, $name if $out_type =~ /OUTLIST$/;
$in_out{$name} = $out_type if $out_type;
}
if ($process_inout and s/^(IN|IN_OUTLIST|OUTLIST|IN_OUT|OUT)\s+//) {
my $out_type = $1;
next if $out_type eq 'IN';
- $only_outlist{$_} = 1 if $out_type eq "OUTLIST";
+ $only_C_inlist{$_} = 1 if $out_type eq "OUTLIST";
push @outlist, $name if $out_type =~ /OUTLIST$/;
$in_out{$_} = $out_type;
}
last;
}
}
- if ($only_outlist{$args[$i]}) {
+ if ($only_C_inlist{$args[$i]}) {
push @args_num, undef;
} else {
push @args_num, ++$num_args;
if $WantOptimize and $targetable{$type_kind{$ret_type}};
}
- if (@arg_with_types) {
- unshift @line, @arg_with_types, $_;
+ if (@fake_INPUT or @fake_INPUT_pre) {
+ unshift @line, @fake_INPUT_pre, @fake_INPUT, $_;
$_ = "";
$processing_arg_with_types = 1;
INPUT_handler() ;
($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;
$tk = $type_kind{$type};
$tk =~ s/OBJ$/REF/ if $func_name =~ /DESTROY$/;
+ if ($tk eq 'T_PV' and exists $lengthof{$var}) {
+ print "\t$var" unless $name_printed;
+ print " = ($type)SvPV($arg, STRLEN_length_of_$var);\n";
+ die "default value not supported with length(NAME) supplied"
+ if defined $defaults{$var};
+ return;
+ }
$type =~ tr/:/_/;
blurt("Error: No INPUT definition for type '$type', typekind '$type_kind{$type}' found"), return
unless defined $input_expr{$tk} ;
$deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/;
}
warn $@ if $@;
- } elsif ($ScopeThisXSUB or $expr !~ /^\t\$var =/) {
+ } elsif ($ScopeThisXSUB or $expr !~ /^\s*\$var =/) {
if ($name_printed) {
print ";\n";
} else {
my ($day, $month);
day_month($day, time, $month);
+=head2 The C<length(NAME)> Keyword
+
+If one of the input arguments to the C function is the length of a string
+argument C<NAME>, one can substitute the name of the length-argument by
+C<length(NAME)> in the XSUB declaration. This argument must be omited when
+the generated Perl function is called. E.g.,
+
+ void
+ dump_chars(char *s, short l)
+ {
+ short n = 0;
+ while (n < l) {
+ printf("s[%d] = \"\\%#03o\"\n", n, (int)s[n]);
+ n++;
+ }
+ }
+
+ MODULE = x PACKAGE = x
+
+ void dump_chars(char *s, short length(s))
+
+should be called as C<dump_chars($string)>.
+
+This directive is supported with ANSI-type function declarations only.
+
=head2 Variable-length Parameter Lists
XSUBs can have variable-length parameter lists by specifying an ellipsis