our
$VERSION
=
'3.51'
;
our
(
@ISA
,
@EXPORT_OK
);
@ISA
=
qw(Exporter)
;
@EXPORT_OK
=
qw(
standard_typemap_locations
trim_whitespace
C_string
valid_proto_string
process_typemaps
map_type
standard_XS_defs
assign_func_args
analyze_preprocessor_statements
set_cond
Warn
WarnHint
current_line_number
blurt
death
check_conditional_preprocessor_statements
escape_file_for_line_directive
report_typemap_failure
)
;
SCOPE: {
my
@tm_template
;
sub
standard_typemap_locations {
my
$include_ref
=
shift
;
if
(not
@tm_template
) {
@tm_template
=
qw(typemap)
;
my
$updir
= File::Spec->updir();
foreach
my
$dir
(
File::Spec->catdir((
$updir
) x 1),
File::Spec->catdir((
$updir
) x 2),
File::Spec->catdir((
$updir
) x 3),
File::Spec->catdir((
$updir
) x 4),
) {
unshift
@tm_template
, File::Spec->catfile(
$dir
,
'typemap'
);
unshift
@tm_template
, File::Spec->catfile(
$dir
,
lib
=>
ExtUtils
=>
'typemap'
);
}
}
my
@tm
=
@tm_template
;
foreach
my
$dir
(@{
$include_ref
}) {
my
$file
= File::Spec->catfile(
$dir
,
ExtUtils
=>
'typemap'
);
unshift
@tm
,
$file
if
-e
$file
;
}
return
@tm
;
}
}
sub
trim_whitespace {
$_
[0] =~ s/^\s+|\s+$//go;
}
sub
C_string {
my
(
$string
) =
@_
;
$string
=~ s[\\][\\\\]g;
$string
;
}
sub
valid_proto_string {
my
(
$string
) =
@_
;
if
(
$string
=~ /^
$ExtUtils::ParseXS::Constants::PrototypeRegexp
+$/ ) {
return
$string
;
}
return
0;
}
sub
process_typemaps {
my
(
$tmap
,
$pwd
) =
@_
;
my
@tm
=
ref
$tmap
? @{
$tmap
} : (
$tmap
);
foreach
my
$typemap
(
@tm
) {
die
"Can't find $typemap in $pwd\n"
unless
-r
$typemap
;
}
push
@tm
, standard_typemap_locations( \
@INC
);
my
$typemap
= ExtUtils::Typemaps->new;
foreach
my
$typemap_loc
(
@tm
) {
next
unless
-f
$typemap_loc
;
warn
(
"Warning: ignoring non-text typemap file '$typemap_loc'\n"
),
next
unless
-T
$typemap_loc
;
$typemap
->merge(
file
=>
$typemap_loc
,
replace
=> 1);
}
return
$typemap
;
}
sub
map_type {
my
(
$self
,
$type
,
$varname
) =
@_
;
$type
=~
tr
/:/_/
unless
$self
->{RetainCplusplusHierarchicalTypes};
$type
=~ s/^array\(([^,]*),(.*)\).*/$1 */s;
if
(
$varname
) {
if
(
$type
=~ / \( \s* \* (?= \s* \) ) /xg) {
(
substr
$type
,
pos
$type
, 0) =
" $varname "
;
}
else
{
$type
.=
"\t$varname"
;
}
}
return
$type
;
}
sub
standard_XS_defs {
print
<<"EOF";
#ifndef PERL_UNUSED_VAR
# define PERL_UNUSED_VAR(var) if (0) var = var
#endif
#ifndef dVAR
# define dVAR dNOOP
#endif
/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
# define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
# define PERL_DECIMAL_VERSION \\
PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
# define PERL_VERSION_GE(r,v,s) \\
(PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
# define PERL_VERSION_LE(r,v,s) \\
(PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif
/* XS_INTERNAL is the explicit static-linkage variant of the default
* XS macro.
*
* XS_EXTERNAL is the same as XS_INTERNAL except it does not include
* "STATIC", ie. it exports XSUB symbols. You probably don't want that
* for anything but the BOOT XSUB.
*
* See XSUB.h in core!
*/
/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
# undef XS_EXTERNAL
# undef XS_INTERNAL
# if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
# define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
# define XS_INTERNAL(name) STATIC XSPROTO(name)
# endif
# if defined(__SYMBIAN32__)
# define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
# define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
# endif
# ifndef XS_EXTERNAL
# if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
# define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
# define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
# else
# ifdef __cplusplus
# define XS_EXTERNAL(name) extern "C" XSPROTO(name)
# define XS_INTERNAL(name) static XSPROTO(name)
# else
# define XS_EXTERNAL(name) XSPROTO(name)
# define XS_INTERNAL(name) STATIC XSPROTO(name)
# endif
# endif
# endif
#endif
/* perl >= 5.10.0 && perl <= 5.15.1 */
/* The XS_EXTERNAL macro is used for functions that must not be static
* like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
* macro defined, the best we can do is assume XS is the same.
* Dito for XS_INTERNAL.
*/
#ifndef XS_EXTERNAL
# define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
# define XS_INTERNAL(name) XS(name)
#endif
/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
* internal macro that we're free to redefine for varying linkage due
* to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
* XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
*/
#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
# define XS_EUPXS(name) XS_EXTERNAL(name)
#else
/* default to internal */
# define XS_EUPXS(name) XS_INTERNAL(name)
#endif
EOF
print
<<"EOF";
#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
/* prototype to pass -Wmissing-prototypes */
STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params);
STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params)
{
const GV *const gv = CvGV(cv);
PERL_ARGS_ASSERT_CROAK_XS_USAGE;
if (gv) {
const char *const gvname = GvNAME(gv);
const HV *const stash = GvSTASH(gv);
const char *const hvname = stash ? HvNAME(stash) : NULL;
if (hvname)
Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
else
Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
} else {
/* Pants. I don't think that it should be possible to get here. */
Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
}
}
#undef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define croak_xs_usage S_croak_xs_usage
#endif
/* NOTE: the prototype of newXSproto() is different in versions of perls,
* so we define a portable version of newXSproto()
*/
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */
#if PERL_VERSION_LE(5, 21, 5)
# define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
# define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif
EOF
return
1;
}
sub
assign_func_args {
my
(
$self
,
$argsref
,
$class
) =
@_
;
my
@func_args
= @{
$argsref
};
shift
@func_args
if
defined
(
$class
);
for
my
$arg
(
@func_args
) {
$arg
=~ s/^/&/
if
$self
->{in_out}->{
$arg
};
}
return
join
(
", "
,
@func_args
);
}
sub
analyze_preprocessor_statements {
my
(
$self
,
$statement
,
$XSS_work_idx
,
$BootCode_ref
) =
@_
;
if
(
$statement
eq
'if'
) {
$XSS_work_idx
= @{
$self
->{XSStack} };
push
(@{
$self
->{XSStack} }, {
type
=>
'if'
});
}
else
{
$self
->death(
"Error: '$statement' with no matching 'if'"
)
if
$self
->{XSStack}->[-1]{type} ne
'if'
;
if
(
$self
->{XSStack}->[-1]{varname}) {
push
(@{
$self
->{InitFileCode} },
"#endif\n"
);
push
(@{
$BootCode_ref
},
"#endif"
);
}
my
(
@fns
) =
keys
%{
$self
->{XSStack}->[-1]{functions}};
if
(
$statement
ne
'endif'
) {
@{
$self
->{XSStack}->[-1]{other_functions}}{
@fns
} = (1) x
@fns
;
@{
$self
->{XSStack}->[-1]}{
qw(varname functions)
} = (
''
, {});
}
else
{
my
(
$tmp
) =
pop
(@{
$self
->{XSStack} });
0
while
(--
$XSS_work_idx
&&
$self
->{XSStack}->[
$XSS_work_idx
]{type} ne
'if'
);
push
(
@fns
,
keys
%{
$tmp
->{other_functions}});
@{
$self
->{XSStack}->[
$XSS_work_idx
]{functions}}{
@fns
} = (1) x
@fns
;
}
}
return
(
$self
,
$XSS_work_idx
,
$BootCode_ref
);
}
sub
set_cond {
my
(
$ellipsis
,
$min_args
,
$num_args
) =
@_
;
my
$cond
;
if
(
$ellipsis
) {
$cond
= (
$min_args
?
qq(items < $min_args)
: 0);
}
elsif
(
$min_args
==
$num_args
) {
$cond
=
qq(items != $min_args)
;
}
else
{
$cond
=
qq(items < $min_args || items > $num_args)
;
}
return
$cond
;
}
sub
current_line_number {
my
$self
=
shift
;
my
$line_number
=
$self
->{line_no}->[@{
$self
->{line_no} } - @{
$self
->{line} } -1];
return
$line_number
;
}
sub
Warn {
my
(
$self
)=
shift
;
$self
->WarnHint(
@_
,
undef
);
}
sub
WarnHint {
warn
_MsgHint(
@_
);
}
sub
_MsgHint {
my
$self
=
shift
;
my
$hint
=
pop
;
my
$warn_line_number
=
$self
->current_line_number();
my
$ret
=
join
(
""
,
@_
) .
" in $self->{filename}, line $warn_line_number\n"
;
if
(
$hint
) {
$ret
.=
" ($_)\n"
for
split
/\n/,
$hint
;
}
return
$ret
;
}
sub
blurt {
my
$self
=
shift
;
$self
->Warn(
@_
);
$self
->{errors}++
}
sub
death {
my
(
$self
) = (
@_
);
my
$message
= _MsgHint(
@_
,
""
);
if
(
$self
->{die_on_error}) {
die
$message
;
}
else
{
warn
$message
;
}
exit
1;
}
sub
check_conditional_preprocessor_statements {
my
(
$self
) =
@_
;
my
@cpp
=
grep
(/^\
if
(
@cpp
) {
my
$cpplevel
;
for
my
$cpp
(
@cpp
) {
if
(
$cpp
=~ /^\
$cpplevel
++;
}
elsif
(!
$cpplevel
) {
$self
->Warn(
"Warning: #else/elif/endif without #if in this function"
);
print
STDERR
" (precede it with a blank line if the matching #if is outside the function)\n"
if
$self
->{XSStack}->[-1]{type} eq
'if'
;
return
;
}
elsif
(
$cpp
=~ /^\
$cpplevel
--;
}
}
$self
->Warn(
"Warning: #if without #endif in this function"
)
if
$cpplevel
;
}
}
sub
escape_file_for_line_directive {
my
$string
=
shift
;
$string
=~ s/\\/\\\\/g;
$string
=~ s/
"/\\"
/g;
return
$string
;
}
sub
report_typemap_failure {
my
(
$self
,
$tm
,
$ctype
,
$error_method
) =
@_
;
$error_method
||=
'blurt'
;
my
@avail_ctypes
=
$tm
->list_mapped_ctypes;
my
$err
=
"Could not find a typemap for C type '$ctype'.\n"
.
"The following C types are mapped by the current typemap:\n'"
.
join
(
"', '"
,
@avail_ctypes
) .
"'\n"
;
$self
->
$error_method
(
$err
);
return
();
}
1;