File Coverage

blib/lib/ExtUtils/ParseXS/Utilities.pm
Criterion Covered Total %
statement 117 119 98.3
branch 36 38 94.7
condition 1 2 50.0
subroutine 23 23 100.0
pod 16 17 94.1
total 193 199 96.9


line stmt bran cond sub pod time code
1             package ExtUtils::ParseXS::Utilities;
2 31     31   384995 use strict;
  31         57  
  31         1122  
3 31     31   352 use warnings;
  31         71  
  31         1710  
4 31     31   184 use Exporter;
  31         50  
  31         1319  
5 31     31   120 use File::Spec;
  31         53  
  31         582  
6 31     31   1640 use ExtUtils::ParseXS::Constants ();
  31         49  
  31         50917  
7              
8             our $VERSION = '3.63';
9              
10             our (@ISA, @EXPORT_OK);
11             @ISA = qw(Exporter);
12             @EXPORT_OK = qw(
13             standard_typemap_locations
14             trim_whitespace
15             C_string
16             valid_proto_string
17             process_typemaps
18             map_type
19             set_cond
20             Warn
21             WarnHint
22             current_line_number
23             blurt
24             death
25             deathHint
26             check_conditional_preprocessor_statements
27             escape_file_for_line_directive
28             report_typemap_failure
29             looks_like_MODULE_line
30             );
31              
32             =head1 NAME
33              
34             ExtUtils::ParseXS::Utilities - Subroutines used with ExtUtils::ParseXS
35              
36             =head1 SYNOPSIS
37              
38             use ExtUtils::ParseXS::Utilities qw(
39             standard_typemap_locations
40             trim_whitespace
41             C_string
42             valid_proto_string
43             process_typemaps
44             map_type
45             set_cond
46             Warn
47             blurt
48             death
49             check_conditional_preprocessor_statements
50             escape_file_for_line_directive
51             report_typemap_failure
52             );
53              
54             =head1 SUBROUTINES
55              
56             The following functions are not considered to be part of the public interface.
57             They are documented here for the benefit of future maintainers of this module.
58              
59             =head2 C
60              
61             =over 4
62              
63             =item * Purpose
64              
65             Returns a standard list of filepaths where F files may be found.
66             This will typically be something like:
67              
68             map("$_/ExtUtils/typemap", reverse @INC),
69             qw(
70             ../../../../lib/ExtUtils/typemap
71             ../../../../typemap
72             ../../../lib/ExtUtils/typemap
73             ../../../typemap
74             ../../lib/ExtUtils/typemap
75             ../../typemap
76             ../lib/ExtUtils/typemap
77             ../typemap
78             typemap
79             )
80              
81             but the style of the pathnames may vary with OS. Note that the value to
82             use for C<@INC> is passed as an array reference, and can be something
83             other than C<@INC> itself.
84              
85             Pathnames are returned in the order they are expected to be processed;
86             this means that later files will update or override entries found in
87             earlier files. So in particular, F in the current directory has
88             highest priority. C<@INC> is searched in reverse order so that earlier
89             entries in C<@INC> are processed later and so have higher priority.
90              
91             The values of C<-typemap> switches are not used here; they should be added
92             by the caller to the list of pathnames returned by this function.
93              
94             =item * Arguments
95              
96             my @stl = standard_typemap_locations(\@INC);
97              
98             A single argument: a reference to an array to use as if it were C<@INC>.
99              
100             =item * Return Value
101              
102             A list of F pathnames.
103              
104             =back
105              
106             =cut
107              
108             sub standard_typemap_locations {
109 438     438 1 150542 my $include_ref = shift;
110              
111 438         869 my @tm;
112              
113             # See function description above for why 'reverse' is used here.
114 438         872 foreach my $dir (reverse @{$include_ref}) {
  438         3816  
115 3932         61284 my $file = File::Spec->catfile($dir, ExtUtils => 'typemap');
116 3932         11857 push @tm, $file;
117             }
118              
119 438         7229 my $updir = File::Spec->updir();
120 438         10079 foreach my $dir (
121             File::Spec->catdir(($updir) x 4),
122             File::Spec->catdir(($updir) x 3),
123             File::Spec->catdir(($updir) x 2),
124             File::Spec->catdir(($updir) x 1),
125             ) {
126 1752         21903 push @tm, File::Spec->catfile($dir, lib => ExtUtils => 'typemap');
127 1752         12778 push @tm, File::Spec->catfile($dir, 'typemap');
128             }
129              
130 438         2836 push @tm, 'typemap';
131              
132 438         4153 return @tm;
133             }
134              
135             =head2 C
136              
137             =over 4
138              
139             =item * Purpose
140              
141             Perform an in-place trimming of leading and trailing whitespace from the
142             first argument provided to the function.
143              
144             =item * Argument
145              
146             trim_whitespace($arg);
147              
148             =item * Return Value
149              
150             None. Remember: this is an I modification of the argument.
151              
152             =back
153              
154             =cut
155              
156             sub trim_whitespace {
157 1491     1491 1 353330 $_[0] =~ s/^\s+|\s+$//go;
158             }
159              
160             =head2 C
161              
162             =over 4
163              
164             =item * Purpose
165              
166             Escape backslashes (C<\>) in prototype strings.
167              
168             =item * Arguments
169              
170             $ProtoThisXSUB = C_string($_);
171              
172             String needing escaping.
173              
174             =item * Return Value
175              
176             Properly escaped string.
177              
178             =back
179              
180             =cut
181              
182             sub C_string {
183 240     240 1 929 my($string) = @_;
184              
185 240         268 $string =~ s[\\][\\\\]g;
186 240         595 $string;
187             }
188              
189             =head2 C
190              
191             =over 4
192              
193             =item * Purpose
194              
195             Validate prototype string.
196              
197             =item * Arguments
198              
199             String needing checking.
200              
201             =item * Return Value
202              
203             Upon success, returns the same string passed as argument.
204              
205             Upon failure, returns C<0>.
206              
207             =back
208              
209             =cut
210              
211             sub valid_proto_string {
212 15     15 1 163976 my ($string) = @_;
213              
214 15 100       270 if ( $string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/ ) {
215 12         75 return $string;
216             }
217              
218 3         22 return 0;
219             }
220              
221             =head2 C
222              
223             =over 4
224              
225             =item * Purpose
226              
227             Process all typemap files. Reads in any typemap files specified explicitly
228             with C<-typemap> switches or similar, plus any typemap files found in
229             standard locations relative to C<@INC> and the current directory.
230              
231             =item * Arguments
232              
233             my $typemaps_object = process_typemaps( $args{typemap}, $pwd );
234              
235             The first argument is the C element from C<%args>; the second is
236             the current working directory (which is only needed for error messages).
237              
238             =item * Return Value
239              
240             Upon success, returns an L object which contains the
241             accumulated results of all processed typemap files.
242              
243             =back
244              
245             =cut
246              
247             sub process_typemaps {
248 440     440 1 141053 my ($tmap, $pwd) = @_;
249              
250 440         4599 my @tm = standard_typemap_locations( \@INC );
251              
252 440 100       1623 my @explicit = ref $tmap ? @{$tmap} : ($tmap);
  439         1286  
253 440         2004 foreach my $typemap (@explicit) {
254 7 100       184 die "Can't find $typemap in $pwd\n" unless -r $typemap;
255             }
256 438         843 push @tm, @explicit;
257              
258 438         17689 require ExtUtils::Typemaps;
259 438         11264 my $typemap = ExtUtils::Typemaps->new;
260 438         2176 foreach my $typemap_loc (@tm) {
261 7820 100       83239 next unless -f $typemap_loc;
262             # skip directories, binary files etc.
263 876 50       74281 warn("Warning: ignoring non-text typemap file '$typemap_loc'\n"), next
264             unless -T $typemap_loc;
265              
266 876         10527 $typemap->merge(file => $typemap_loc, replace => 1);
267             }
268              
269 438         5930 return $typemap;
270             }
271              
272              
273             =head2 C
274              
275             Returns a mapped version of the C type C<$type>. In particular, it
276             converts C to C, converts the special C
277             into C, and inserts C<$varname> (if present) into any function
278             pointer type. So C<...(*)...> becomes C<...(* foo)...>.
279              
280             =cut
281              
282             sub map_type {
283 700     700 1 5433 my ExtUtils::ParseXS $self = shift;
284 700         1658 my ($type, $varname) = @_;
285              
286             # C++ has :: in types too so skip this
287 700 100       2208 $type =~ tr/:/_/ unless $self->{config_RetainCplusplusHierarchicalTypes};
288              
289             # map the special return type 'array(type, n)' to 'type *'
290 700         1432 $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
291              
292 700 100       1449 if ($varname) {
293 8 100       51 if ($type =~ / \( \s* \* (?= \s* \) ) /xg) {
294 4         61 (substr $type, pos $type, 0) = " $varname ";
295             }
296             else {
297 4         8 $type .= "\t$varname";
298             }
299             }
300 700         3140 return $type;
301             }
302              
303              
304             =head2 C
305              
306             =over 4
307              
308             =item * Purpose
309              
310             Return a string containing a snippet of C code which tests for the 'wrong
311             number of arguments passed' condition, depending on whether there are
312             default arguments or ellipsis.
313              
314             =item * Arguments
315              
316             C true if the xsub's signature has a trailing C<, ...>.
317              
318             C<$min_args> the smallest number of args which may be passed.
319              
320             C<$num_args> the number of parameters in the signature.
321              
322             =item * Return Value
323              
324             The text of a short C code snippet.
325              
326             =back
327              
328             =cut
329              
330             sub set_cond {
331 433     433 1 1614 my ($ellipsis, $min_args, $num_args) = @_;
332 433         1031 my $cond;
333 433 100       1353 if ($ellipsis) {
    100          
334 14 100       54 $cond = ($min_args ? qq(items < $min_args) : 0);
335             }
336             elsif ($min_args == $num_args) {
337 394         970 $cond = qq(items != $min_args);
338             }
339             else {
340 25         115 $cond = qq(items < $min_args || items > $num_args);
341             }
342 433         1050 return $cond;
343             }
344              
345             =head2 C
346              
347             =over 4
348              
349             =item * Purpose
350              
351             Figures out the current line number in the XS file.
352              
353             =item * Arguments
354              
355             C<$self>
356              
357             =item * Return Value
358              
359             The current line number.
360              
361             =back
362              
363             =cut
364              
365             sub current_line_number {
366 368     368 1 873 my ExtUtils::ParseXS $self = shift;
367             # NB: until the first MODULE line is encountered, $self->{line_no} etc
368             # won't have been populated
369 368         1630 my $line_number = @{$self->{line_no}}
370 366         1373 ? $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1]
  366         1186  
371 368 100       599 : $self->{lastline_no};
372 368         2275 return $line_number;
373             }
374              
375              
376              
377             =head2 Error handling methods
378              
379             There are four main methods for reporting warnings and errors.
380              
381             =over
382              
383             =item C<< $self->Warn(@messages) >>
384              
385             This is equivalent to:
386              
387             warn "@messages in foo.xs, line 123\n";
388              
389             The file and line number are based on the file currently being parsed. It
390             is intended for use where you wish to warn, but can continue parsing and
391             still generate a correct C output file.
392              
393             =item C<< $self->blurt(@messages) >>
394              
395             This is equivalent to C, except that it also increments the internal
396             error count (which can be retrieved with C). It is
397             used to report an error, but where parsing can continue (so typically for
398             a semantic error rather than a syntax error). It is expected that the
399             caller will eventually signal failure in some fashion. For example,
400             C has this as its last line:
401              
402             exit($self->report_error_count() ? 1 : 0);
403              
404             =item C<< $self->death(@messages) >>
405              
406             This normally equivalent to:
407              
408             $self->Warn(@messages);
409             exit(1);
410              
411             It is used for something like a syntax error, where parsing can't
412             continue. However, this is inconvenient for testing purposes, as the
413             error can't be trapped. So if C<$self> is created with the C
414             flag, or if C<$ExtUtils::ParseXS::DIE_ON_ERROR> is true when process_file()
415             is called, then instead it will die() with that message.
416              
417             =item C<< $self->WarnHint(@messages, $hints) >>
418              
419             This is a more obscure twin to C, which does the same as C,
420             but afterwards, outputs any lines contained in the C<$hints> string, with
421             the paragraph wrapped in parentheses. For example:
422              
423             $self->WarnHint(@messages,
424             "Have you set the foo switch?\nSee the manual for further info");
425              
426              
427             =item C<< $self->deathHint(@messages, $hints) >>
428              
429             This is a more obscure twin to C, which does the same as C,
430             but afterwards, outputs any lines contained in the C<$hints> string, with
431             the paragraph wrapped in parentheses. For example:
432              
433             $self->deathHint(@messages,
434             "Have you set the foo switch?\nSee the manual for further info");
435              
436             =back
437              
438             =cut
439              
440              
441             # see L above
442              
443             sub Warn {
444 130     130 1 1835 my ExtUtils::ParseXS $self = shift;
445 130         681 $self->WarnHint(@_,undef);
446             }
447              
448              
449             # see L above
450              
451             sub WarnHint {
452 138     138 1 534 warn _MsgHint(@_);
453             }
454              
455              
456             # see L above
457              
458             sub _MsgHint {
459 201     201   583 my ExtUtils::ParseXS $self = shift;
460 201         451 my $hint = pop;
461 201         869 my $warn_line_number = $self->current_line_number();
462 201         1143 my $ret = join("",@_) . " in $self->{in_filename}, line $warn_line_number\n";
463 201 100       695 if ($hint) {
464 10         122 my @lines = map " $_", split /\n/, $hint;
465 10         99 $lines[0] =~ s/^ /(/;
466 10         31 $lines[-1] .= ')';
467 10         60 $ret .= " $_\n" for @lines;
468             }
469 201         1261 return $ret;
470             }
471              
472              
473             # see L above
474              
475             sub blurt {
476 93     93 1 789 my ExtUtils::ParseXS $self = shift;
477 93         765 $self->Warn(@_);
478 93         382 $self->{error_count}++
479             }
480              
481             # see L above
482              
483             sub deathHint {
484 63     63 1 257 my ExtUtils::ParseXS $self = $_[0];
485 63         488 my $message = _MsgHint(@_);
486 63 50       394 if ($self->{config_die_on_error}) {
487 63         3779 die $message;
488             } else {
489 0         0 warn $message;
490             }
491 0         0 exit 1;
492             }
493              
494              
495             # see L above
496              
497             sub death {
498 58     58 1 994 deathHint(@_, undef);
499             }
500              
501              
502              
503             =head2 C
504              
505             =over 4
506              
507             =item * Purpose
508              
509             Warn if the lines in C<< @{ $self->{line} } >> don't have balanced C<#if>,
510             C etc.
511              
512             =item * Arguments
513              
514             None
515              
516             =item * Return Value
517              
518             None
519              
520             =back
521              
522             =cut
523              
524             sub check_conditional_preprocessor_statements {
525 466     466 1 145928 my ExtUtils::ParseXS $self = $_[0];
526 466         837 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
  466         3079  
527 466 100       1815 if (@cpp) {
528 5         8 my $cpplevel;
529 5         10 for my $cpp (@cpp) {
530 13 100       50 if ($cpp =~ /^\#\s*if/) {
    100          
    100          
531 4         6 $cpplevel++;
532             }
533             elsif (!$cpplevel) {
534 2         10 $self->Warn("Warning: #else/elif/endif without #if in this function");
535 2         8 return;
536             }
537             elsif ($cpp =~ /^\#\s*endif/) {
538 3         4 $cpplevel--;
539             }
540             }
541 3 100       18 $self->Warn("Warning: #if without #endif in this function") if $cpplevel;
542             }
543             }
544              
545             =head2 C
546              
547             =over 4
548              
549             =item * Purpose
550              
551             Escapes a given code source name (typically a file name but can also
552             be a command that was read from) so that double-quotes and backslashes are escaped.
553              
554             =item * Arguments
555              
556             A string.
557              
558             =item * Return Value
559              
560             A string with escapes for double-quotes and backslashes.
561              
562             =back
563              
564             =cut
565              
566             sub escape_file_for_line_directive {
567 489     489 1 939 my $string = shift;
568 489         1288 $string =~ s/\\/\\\\/g;
569 489         1217 $string =~ s/"/\\"/g;
570 489         1739 return $string;
571             }
572              
573             =head2 C
574              
575             =over 4
576              
577             =item * Purpose
578              
579             Do error reporting for missing typemaps.
580              
581             =item * Arguments
582              
583             The C object.
584              
585             An C object.
586              
587             The string that represents the C type that was not found in the typemap.
588              
589             Optionally, the string C or C to choose
590             whether the error is immediately fatal or not. Default: C
591              
592             =item * Return Value
593              
594             Returns nothing. Depending on the arguments, this
595             may call C or C, the former of which is
596             fatal.
597              
598             =back
599              
600             =cut
601              
602             sub report_typemap_failure {
603 7     7 1 34 my ExtUtils::ParseXS $self = shift;
604 7         41 my ($tm, $ctype, $error_method) = @_;
605 7   50     48 $error_method ||= 'blurt';
606              
607 7         40 my @avail_ctypes = $tm->list_mapped_ctypes;
608              
609 7         109 my $err = "Could not find a typemap for C type '$ctype'.\n"
610             . "The following C types are mapped by the current typemap:\n'"
611             . join("', '", @avail_ctypes) . "'\n";
612              
613 7         77 $self->$error_method($err);
614 7         41 return();
615             }
616              
617             =head2 C
618              
619             Returns true if the passed line looks like an attempt to be a MODULE line.
620             Note that it doesn't check for valid syntax. This allows the caller to do
621             its own parsing of the line, providing some sort of 'invalid MODULE line'
622             check. As compared with thinking that its not a MODULE line if its syntax
623             is slightly off, leading instead to some weird error about a bad start to
624             an XSUB or something.
625              
626             In particular, a line starting C returns true, because it's
627             likely to be an attempt by the programmer to write a MODULE line, even
628             though it's invalid syntax.
629              
630             =cut
631              
632             sub looks_like_MODULE_line {
633 2173     2173 0 3450 my $line = shift;
634 2173         69613 $line =~ /^MODULE\s*[=:]/;
635             }
636              
637              
638              
639             1;
640              
641             # vim: ts=2 sw=2 et: