1package ExtUtils::ParseXS::Utilities; 2use strict; 3use warnings; 4use Exporter; 5use File::Spec; 6use ExtUtils::ParseXS::Constants (); 7 8our $VERSION = '3.19'; 9 10our (@ISA, @EXPORT_OK); 11@ISA = qw(Exporter); 12@EXPORT_OK = qw( 13 standard_typemap_locations 14 trim_whitespace 15 tidy_type 16 C_string 17 valid_proto_string 18 process_typemaps 19 make_targetable 20 map_type 21 standard_XS_defs 22 assign_func_args 23 analyze_preprocessor_statements 24 set_cond 25 Warn 26 current_line_number 27 blurt 28 death 29 check_conditional_preprocessor_statements 30 escape_file_for_line_directive 31 report_typemap_failure 32); 33 34=head1 NAME 35 36ExtUtils::ParseXS::Utilities - Subroutines used with ExtUtils::ParseXS 37 38=head1 SYNOPSIS 39 40 use ExtUtils::ParseXS::Utilities qw( 41 standard_typemap_locations 42 trim_whitespace 43 tidy_type 44 C_string 45 valid_proto_string 46 process_typemaps 47 make_targetable 48 map_type 49 standard_XS_defs 50 assign_func_args 51 analyze_preprocessor_statements 52 set_cond 53 Warn 54 blurt 55 death 56 check_conditional_preprocessor_statements 57 escape_file_for_line_directive 58 report_typemap_failure 59 ); 60 61=head1 SUBROUTINES 62 63The following functions are not considered to be part of the public interface. 64They are documented here for the benefit of future maintainers of this module. 65 66=head2 C<standard_typemap_locations()> 67 68=over 4 69 70=item * Purpose 71 72Provide a list of filepaths where F<typemap> files may be found. The 73filepaths -- relative paths to files (not just directory paths) -- appear in this list in lowest-to-highest priority. 74 75The highest priority is to look in the current directory. 76 77 'typemap' 78 79The second and third highest priorities are to look in the parent of the 80current directory and a directory called F<lib/ExtUtils> underneath the parent 81directory. 82 83 '../typemap', 84 '../lib/ExtUtils/typemap', 85 86The fourth through ninth highest priorities are to look in the corresponding 87grandparent, great-grandparent and great-great-grandparent directories. 88 89 '../../typemap', 90 '../../lib/ExtUtils/typemap', 91 '../../../typemap', 92 '../../../lib/ExtUtils/typemap', 93 '../../../../typemap', 94 '../../../../lib/ExtUtils/typemap', 95 96The tenth and subsequent priorities are to look in directories named 97F<ExtUtils> which are subdirectories of directories found in C<@INC> -- 98I<provided> a file named F<typemap> actually exists in such a directory. 99Example: 100 101 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap', 102 103However, these filepaths appear in the list returned by 104C<standard_typemap_locations()> in reverse order, I<i.e.>, lowest-to-highest. 105 106 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap', 107 '../../../../lib/ExtUtils/typemap', 108 '../../../../typemap', 109 '../../../lib/ExtUtils/typemap', 110 '../../../typemap', 111 '../../lib/ExtUtils/typemap', 112 '../../typemap', 113 '../lib/ExtUtils/typemap', 114 '../typemap', 115 'typemap' 116 117=item * Arguments 118 119 my @stl = standard_typemap_locations( \@INC ); 120 121Reference to C<@INC>. 122 123=item * Return Value 124 125Array holding list of directories to be searched for F<typemap> files. 126 127=back 128 129=cut 130 131sub standard_typemap_locations { 132 my $include_ref = shift; 133 my @tm = qw(typemap); 134 135 my $updir = File::Spec->updir(); 136 foreach my $dir ( 137 File::Spec->catdir(($updir) x 1), 138 File::Spec->catdir(($updir) x 2), 139 File::Spec->catdir(($updir) x 3), 140 File::Spec->catdir(($updir) x 4), 141 ) { 142 unshift @tm, File::Spec->catfile($dir, 'typemap'); 143 unshift @tm, File::Spec->catfile($dir, lib => ExtUtils => 'typemap'); 144 } 145 foreach my $dir (@{ $include_ref}) { 146 my $file = File::Spec->catfile($dir, ExtUtils => 'typemap'); 147 unshift @tm, $file if -e $file; 148 } 149 return @tm; 150} 151 152=head2 C<trim_whitespace()> 153 154=over 4 155 156=item * Purpose 157 158Perform an in-place trimming of leading and trailing whitespace from the 159first argument provided to the function. 160 161=item * Argument 162 163 trim_whitespace($arg); 164 165=item * Return Value 166 167None. Remember: this is an I<in-place> modification of the argument. 168 169=back 170 171=cut 172 173sub trim_whitespace { 174 $_[0] =~ s/^\s+|\s+$//go; 175} 176 177=head2 C<tidy_type()> 178 179=over 4 180 181=item * Purpose 182 183Rationalize any asterisks (C<*>) by joining them into bunches, removing 184interior whitespace, then trimming leading and trailing whitespace. 185 186=item * Arguments 187 188 ($ret_type) = tidy_type($_); 189 190String to be cleaned up. 191 192=item * Return Value 193 194String cleaned up. 195 196=back 197 198=cut 199 200sub tidy_type { 201 local ($_) = @_; 202 203 # rationalise any '*' by joining them into bunches and removing whitespace 204 s#\s*(\*+)\s*#$1#g; 205 s#(\*+)# $1 #g; 206 207 # change multiple whitespace into a single space 208 s/\s+/ /g; 209 210 # trim leading & trailing whitespace 211 trim_whitespace($_); 212 213 $_; 214} 215 216=head2 C<C_string()> 217 218=over 4 219 220=item * Purpose 221 222Escape backslashes (C<\>) in prototype strings. 223 224=item * Arguments 225 226 $ProtoThisXSUB = C_string($_); 227 228String needing escaping. 229 230=item * Return Value 231 232Properly escaped string. 233 234=back 235 236=cut 237 238sub C_string { 239 my($string) = @_; 240 241 $string =~ s[\\][\\\\]g; 242 $string; 243} 244 245=head2 C<valid_proto_string()> 246 247=over 4 248 249=item * Purpose 250 251Validate prototype string. 252 253=item * Arguments 254 255String needing checking. 256 257=item * Return Value 258 259Upon success, returns the same string passed as argument. 260 261Upon failure, returns C<0>. 262 263=back 264 265=cut 266 267sub valid_proto_string { 268 my($string) = @_; 269 270 if ( $string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/ ) { 271 return $string; 272 } 273 274 return 0; 275} 276 277=head2 C<process_typemaps()> 278 279=over 4 280 281=item * Purpose 282 283Process all typemap files. 284 285=item * Arguments 286 287 my $typemaps_object = process_typemaps( $args{typemap}, $pwd ); 288 289List of two elements: C<typemap> element from C<%args>; current working 290directory. 291 292=item * Return Value 293 294Upon success, returns an L<ExtUtils::Typemaps> object. 295 296=back 297 298=cut 299 300sub process_typemaps { 301 my ($tmap, $pwd) = @_; 302 303 my @tm = ref $tmap ? @{$tmap} : ($tmap); 304 305 foreach my $typemap (@tm) { 306 die "Can't find $typemap in $pwd\n" unless -r $typemap; 307 } 308 309 push @tm, standard_typemap_locations( \@INC ); 310 311 require ExtUtils::Typemaps; 312 my $typemap = ExtUtils::Typemaps->new; 313 foreach my $typemap_loc (@tm) { 314 next unless -f $typemap_loc; 315 # skip directories, binary files etc. 316 warn("Warning: ignoring non-text typemap file '$typemap_loc'\n"), next 317 unless -T $typemap_loc; 318 319 $typemap->merge(file => $typemap_loc, replace => 1); 320 } 321 322 return $typemap; 323} 324 325=head2 C<make_targetable()> 326 327=over 4 328 329=item * Purpose 330 331Populate C<%targetable>. This constitutes a refinement of the output of 332C<process_typemaps()> with respect to its fourth output, C<$output_expr_ref>. 333 334=item * Arguments 335 336 %targetable = make_targetable($output_expr_ref); 337 338Single hash reference: the fourth such ref returned by C<process_typemaps()>. 339 340=item * Return Value 341 342Hash. 343 344=back 345 346=cut 347 348sub make_targetable { 349 my $output_expr_ref = shift; 350 351 our $bal; # ()-balanced 352 $bal = qr[ 353 (?: 354 (?>[^()]+) 355 | 356 \( (??{ $bal }) \) 357 )* 358 ]x; 359 360 # matches variations on (SV*) 361 my $sv_cast = qr[ 362 (?: 363 \( \s* SV \s* \* \s* \) \s* 364 )? 365 ]x; 366 367 my $size = qr[ # Third arg (to setpvn) 368 , \s* (??{ $bal }) 369 ]x; 370 371 my %targetable; 372 foreach my $key (keys %{ $output_expr_ref }) { 373 # We can still bootstrap compile 're', because in code re.pm is 374 # available to miniperl, and does not attempt to load the XS code. 375 use re 'eval'; 376 377 my ($type, $with_size, $arg, $sarg) = 378 ($output_expr_ref->{$key} =~ 379 m[^ 380 \s+ 381 sv_set([iunp])v(n)? # Type, is_setpvn 382 \s* 383 \( \s* 384 $sv_cast \$arg \s* , \s* 385 ( (??{ $bal }) ) # Set from 386 ( (??{ $size }) )? # Possible sizeof set-from 387 \) \s* ; \s* $ 388 ]x 389 ); 390 $targetable{$key} = [$type, $with_size, $arg, $sarg] if $type; 391 } 392 return %targetable; 393} 394 395=head2 C<map_type()> 396 397=over 4 398 399=item * Purpose 400 401Performs a mapping at several places inside C<PARAGRAPH> loop. 402 403=item * Arguments 404 405 $type = map_type($self, $type, $varname); 406 407List of three arguments. 408 409=item * Return Value 410 411String holding augmented version of second argument. 412 413=back 414 415=cut 416 417sub map_type { 418 my ($self, $type, $varname) = @_; 419 420 # C++ has :: in types too so skip this 421 $type =~ tr/:/_/ unless $self->{hiertype}; 422 $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s; 423 if ($varname) { 424 if ($type =~ / \( \s* \* (?= \s* \) ) /xg) { 425 (substr $type, pos $type, 0) = " $varname "; 426 } 427 else { 428 $type .= "\t$varname"; 429 } 430 } 431 return $type; 432} 433 434=head2 C<standard_XS_defs()> 435 436=over 4 437 438=item * Purpose 439 440Writes to the C<.c> output file certain preprocessor directives and function 441headers needed in all such files. 442 443=item * Arguments 444 445None. 446 447=item * Return Value 448 449Returns true. 450 451=back 452 453=cut 454 455sub standard_XS_defs { 456 print <<"EOF"; 457#ifndef PERL_UNUSED_VAR 458# define PERL_UNUSED_VAR(var) if (0) var = var 459#endif 460 461#ifndef dVAR 462# define dVAR dNOOP 463#endif 464 465 466/* This stuff is not part of the API! You have been warned. */ 467#ifndef PERL_VERSION_DECIMAL 468# define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s) 469#endif 470#ifndef PERL_DECIMAL_VERSION 471# define PERL_DECIMAL_VERSION \\ 472 PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION) 473#endif 474#ifndef PERL_VERSION_GE 475# define PERL_VERSION_GE(r,v,s) \\ 476 (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s)) 477#endif 478#ifndef PERL_VERSION_LE 479# define PERL_VERSION_LE(r,v,s) \\ 480 (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s)) 481#endif 482 483/* XS_INTERNAL is the explicit static-linkage variant of the default 484 * XS macro. 485 * 486 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include 487 * "STATIC", ie. it exports XSUB symbols. You probably don't want that 488 * for anything but the BOOT XSUB. 489 * 490 * See XSUB.h in core! 491 */ 492 493 494/* TODO: This might be compatible further back than 5.10.0. */ 495#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1) 496# undef XS_EXTERNAL 497# undef XS_INTERNAL 498# if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING) 499# define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name) 500# define XS_INTERNAL(name) STATIC XSPROTO(name) 501# endif 502# if defined(__SYMBIAN32__) 503# define XS_EXTERNAL(name) EXPORT_C XSPROTO(name) 504# define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name) 505# endif 506# ifndef XS_EXTERNAL 507# if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus) 508# define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__) 509# define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__) 510# else 511# ifdef __cplusplus 512# define XS_EXTERNAL(name) extern "C" XSPROTO(name) 513# define XS_INTERNAL(name) static XSPROTO(name) 514# else 515# define XS_EXTERNAL(name) XSPROTO(name) 516# define XS_INTERNAL(name) STATIC XSPROTO(name) 517# endif 518# endif 519# endif 520#endif 521 522/* perl >= 5.10.0 && perl <= 5.15.1 */ 523 524 525/* The XS_EXTERNAL macro is used for functions that must not be static 526 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL 527 * macro defined, the best we can do is assume XS is the same. 528 * Dito for XS_INTERNAL. 529 */ 530#ifndef XS_EXTERNAL 531# define XS_EXTERNAL(name) XS(name) 532#endif 533#ifndef XS_INTERNAL 534# define XS_INTERNAL(name) XS(name) 535#endif 536 537/* Now, finally, after all this mess, we want an ExtUtils::ParseXS 538 * internal macro that we're free to redefine for varying linkage due 539 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use 540 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to! 541 */ 542 543#undef XS_EUPXS 544#if defined(PERL_EUPXS_ALWAYS_EXPORT) 545# define XS_EUPXS(name) XS_EXTERNAL(name) 546#else 547 /* default to internal */ 548# define XS_EUPXS(name) XS_INTERNAL(name) 549#endif 550 551EOF 552 553 print <<"EOF"; 554#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE 555#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params) 556 557/* prototype to pass -Wmissing-prototypes */ 558STATIC void 559S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params); 560 561STATIC void 562S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params) 563{ 564 const GV *const gv = CvGV(cv); 565 566 PERL_ARGS_ASSERT_CROAK_XS_USAGE; 567 568 if (gv) { 569 const char *const gvname = GvNAME(gv); 570 const HV *const stash = GvSTASH(gv); 571 const char *const hvname = stash ? HvNAME(stash) : NULL; 572 573 if (hvname) 574 Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params); 575 else 576 Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params); 577 } else { 578 /* Pants. I don't think that it should be possible to get here. */ 579 Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params); 580 } 581} 582#undef PERL_ARGS_ASSERT_CROAK_XS_USAGE 583 584#ifdef PERL_IMPLICIT_CONTEXT 585#define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b) 586#else 587#define croak_xs_usage S_croak_xs_usage 588#endif 589 590#endif 591 592/* NOTE: the prototype of newXSproto() is different in versions of perls, 593 * so we define a portable version of newXSproto() 594 */ 595#ifdef newXS_flags 596#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0) 597#else 598#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv) 599#endif /* !defined(newXS_flags) */ 600 601EOF 602 return 1; 603} 604 605=head2 C<assign_func_args()> 606 607=over 4 608 609=item * Purpose 610 611Perform assignment to the C<func_args> attribute. 612 613=item * Arguments 614 615 $string = assign_func_args($self, $argsref, $class); 616 617List of three elements. Second is an array reference; third is a string. 618 619=item * Return Value 620 621String. 622 623=back 624 625=cut 626 627sub assign_func_args { 628 my ($self, $argsref, $class) = @_; 629 my @func_args = @{$argsref}; 630 shift @func_args if defined($class); 631 632 for my $arg (@func_args) { 633 $arg =~ s/^/&/ if $self->{in_out}->{$arg}; 634 } 635 return join(", ", @func_args); 636} 637 638=head2 C<analyze_preprocessor_statements()> 639 640=over 4 641 642=item * Purpose 643 644Within each function inside each Xsub, print to the F<.c> output file certain 645preprocessor statements. 646 647=item * Arguments 648 649 ( $self, $XSS_work_idx, $BootCode_ref ) = 650 analyze_preprocessor_statements( 651 $self, $statement, $XSS_work_idx, $BootCode_ref 652 ); 653 654List of four elements. 655 656=item * Return Value 657 658Modifed values of three of the arguments passed to the function. In 659particular, the C<XSStack> and C<InitFileCode> attributes are modified. 660 661=back 662 663=cut 664 665sub analyze_preprocessor_statements { 666 my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_; 667 668 if ($statement eq 'if') { 669 $XSS_work_idx = @{ $self->{XSStack} }; 670 push(@{ $self->{XSStack} }, {type => 'if'}); 671 } 672 else { 673 $self->death("Error: '$statement' with no matching 'if'") 674 if $self->{XSStack}->[-1]{type} ne 'if'; 675 if ($self->{XSStack}->[-1]{varname}) { 676 push(@{ $self->{InitFileCode} }, "#endif\n"); 677 push(@{ $BootCode_ref }, "#endif"); 678 } 679 680 my(@fns) = keys %{$self->{XSStack}->[-1]{functions}}; 681 if ($statement ne 'endif') { 682 # Hide the functions defined in other #if branches, and reset. 683 @{$self->{XSStack}->[-1]{other_functions}}{@fns} = (1) x @fns; 684 @{$self->{XSStack}->[-1]}{qw(varname functions)} = ('', {}); 685 } 686 else { 687 my($tmp) = pop(@{ $self->{XSStack} }); 688 0 while (--$XSS_work_idx 689 && $self->{XSStack}->[$XSS_work_idx]{type} ne 'if'); 690 # Keep all new defined functions 691 push(@fns, keys %{$tmp->{other_functions}}); 692 @{$self->{XSStack}->[$XSS_work_idx]{functions}}{@fns} = (1) x @fns; 693 } 694 } 695 return ($self, $XSS_work_idx, $BootCode_ref); 696} 697 698=head2 C<set_cond()> 699 700=over 4 701 702=item * Purpose 703 704=item * Arguments 705 706=item * Return Value 707 708=back 709 710=cut 711 712sub set_cond { 713 my ($ellipsis, $min_args, $num_args) = @_; 714 my $cond; 715 if ($ellipsis) { 716 $cond = ($min_args ? qq(items < $min_args) : 0); 717 } 718 elsif ($min_args == $num_args) { 719 $cond = qq(items != $min_args); 720 } 721 else { 722 $cond = qq(items < $min_args || items > $num_args); 723 } 724 return $cond; 725} 726 727=head2 C<current_line_number()> 728 729=over 4 730 731=item * Purpose 732 733Figures out the current line number in the XS file. 734 735=item * Arguments 736 737C<$self> 738 739=item * Return Value 740 741The current line number. 742 743=back 744 745=cut 746 747sub current_line_number { 748 my $self = shift; 749 my $line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1]; 750 return $line_number; 751} 752 753=head2 C<Warn()> 754 755=over 4 756 757=item * Purpose 758 759=item * Arguments 760 761=item * Return Value 762 763=back 764 765=cut 766 767sub Warn { 768 my $self = shift; 769 my $warn_line_number = $self->current_line_number(); 770 print STDERR "@_ in $self->{filename}, line $warn_line_number\n"; 771} 772 773=head2 C<blurt()> 774 775=over 4 776 777=item * Purpose 778 779=item * Arguments 780 781=item * Return Value 782 783=back 784 785=cut 786 787sub blurt { 788 my $self = shift; 789 $self->Warn(@_); 790 $self->{errors}++ 791} 792 793=head2 C<death()> 794 795=over 4 796 797=item * Purpose 798 799=item * Arguments 800 801=item * Return Value 802 803=back 804 805=cut 806 807sub death { 808 my $self = shift; 809 $self->Warn(@_); 810 exit 1; 811} 812 813=head2 C<check_conditional_preprocessor_statements()> 814 815=over 4 816 817=item * Purpose 818 819=item * Arguments 820 821=item * Return Value 822 823=back 824 825=cut 826 827sub check_conditional_preprocessor_statements { 828 my ($self) = @_; 829 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} }); 830 if (@cpp) { 831 my $cpplevel; 832 for my $cpp (@cpp) { 833 if ($cpp =~ /^\#\s*if/) { 834 $cpplevel++; 835 } 836 elsif (!$cpplevel) { 837 $self->Warn("Warning: #else/elif/endif without #if in this function"); 838 print STDERR " (precede it with a blank line if the matching #if is outside the function)\n" 839 if $self->{XSStack}->[-1]{type} eq 'if'; 840 return; 841 } 842 elsif ($cpp =~ /^\#\s*endif/) { 843 $cpplevel--; 844 } 845 } 846 $self->Warn("Warning: #if without #endif in this function") if $cpplevel; 847 } 848} 849 850=head2 C<escape_file_for_line_directive()> 851 852=over 4 853 854=item * Purpose 855 856Escapes a given code source name (typically a file name but can also 857be a command that was read from) so that double-quotes and backslashes are escaped. 858 859=item * Arguments 860 861A string. 862 863=item * Return Value 864 865A string with escapes for double-quotes and backslashes. 866 867=back 868 869=cut 870 871sub escape_file_for_line_directive { 872 my $string = shift; 873 $string =~ s/\\/\\\\/g; 874 $string =~ s/"/\\"/g; 875 return $string; 876} 877 878=head2 C<report_typemap_failure> 879 880=over 4 881 882=item * Purpose 883 884Do error reporting for missing typemaps. 885 886=item * Arguments 887 888The C<ExtUtils::ParseXS> object. 889 890An C<ExtUtils::Typemaps> object. 891 892The string that represents the C type that was not found in the typemap. 893 894Optionally, the string C<death> or C<blurt> to choose 895whether the error is immediately fatal or not. Default: C<blurt> 896 897=item * Return Value 898 899Returns nothing. Depending on the arguments, this 900may call C<death> or C<blurt>, the former of which is 901fatal. 902 903=back 904 905=cut 906 907sub report_typemap_failure { 908 my ($self, $tm, $ctype, $error_method) = @_; 909 $error_method ||= 'blurt'; 910 911 my @avail_ctypes = $tm->list_mapped_ctypes; 912 913 my $err = "Could not find a typemap for C type '$ctype'.\n" 914 . "The following C types are mapped by the current typemap:\n'" 915 . join("', '", @avail_ctypes) . "'\n"; 916 917 $self->$error_method($err); 918 return(); 919} 920 9211; 922 923# vim: ts=2 sw=2 et: 924