| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package XS::Check; | 
| 2 | 5 |  |  | 5 |  | 301912 | use warnings; | 
|  | 5 |  |  |  |  | 44 |  | 
|  | 5 |  |  |  |  | 174 |  | 
| 3 | 5 |  |  | 5 |  | 26 | use strict; | 
|  | 5 |  |  |  |  | 9 |  | 
|  | 5 |  |  |  |  | 129 |  | 
| 4 | 5 |  |  | 5 |  | 26 | use Carp; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 290 |  | 
| 5 | 5 |  |  | 5 |  | 30 | use utf8; | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 41 |  | 
| 6 |  |  |  |  |  |  | our $VERSION = '0.12'; | 
| 7 | 5 |  |  | 5 |  | 2910 | use C::Tokenize '0.14', ':all'; | 
|  | 5 |  |  |  |  | 23663 |  | 
|  | 5 |  |  |  |  | 1504 |  | 
| 8 | 5 |  |  | 5 |  | 2563 | use Text::LineNumber; | 
|  | 5 |  |  |  |  | 1812 |  | 
|  | 5 |  |  |  |  | 172 |  | 
| 9 | 5 |  |  | 5 |  | 2468 | use File::Slurper 'read_text'; | 
|  | 5 |  |  |  |  | 73396 |  | 
|  | 5 |  |  |  |  | 335 |  | 
| 10 | 5 |  |  | 5 |  | 41 | use Carp qw/croak carp cluck confess/; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 10743 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | #  ____       _            _ | 
| 13 |  |  |  |  |  |  | # |  _ \ _ __(_)_   ____ _| |_ ___ | 
| 14 |  |  |  |  |  |  | # | |_) | '__| \ \ / / _` | __/ _ \ | 
| 15 |  |  |  |  |  |  | # |  __/| |  | |\ V / (_| | ||  __/ | 
| 16 |  |  |  |  |  |  | # |_|   |_|  |_| \_/ \__,_|\__\___| | 
| 17 |  |  |  |  |  |  | # | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | sub debugmsg | 
| 20 |  |  |  |  |  |  | { | 
| 21 | 0 |  |  | 0 | 0 | 0 | my (undef, $file, $line) = caller (); | 
| 22 | 0 |  |  |  |  | 0 | printf ("%s:%d: ", $file, $line); | 
| 23 | 0 |  |  |  |  | 0 | print "@_\n"; | 
| 24 |  |  |  |  |  |  | } | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | sub get_line_number | 
| 27 |  |  |  |  |  |  | { | 
| 28 | 19 |  |  | 19 | 0 | 36 | my ($o) = @_; | 
| 29 | 19 |  |  |  |  | 33 | my $pos = pos ($o->{xs}); | 
| 30 | 19 | 50 |  |  |  | 71 | if (! defined ($pos)) { | 
| 31 | 0 |  |  |  |  | 0 | confess "Bad pos for XS text"; | 
| 32 | 0 |  |  |  |  | 0 | return "unknown"; | 
| 33 |  |  |  |  |  |  | } | 
| 34 | 19 |  |  |  |  | 66 | return $o->{tln}->off2lnr ($pos); | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # Report an error $message in $var | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | sub report | 
| 40 |  |  |  |  |  |  | { | 
| 41 | 19 |  |  | 19 | 0 | 60 | my ($o, $message) = @_; | 
| 42 | 19 |  |  |  |  | 52 | my $file = $o->get_file (); | 
| 43 | 19 |  |  |  |  | 60 | my $line = $o->get_line_number (); | 
| 44 | 19 | 50 |  |  |  | 386 | confess "No message" unless $message; | 
| 45 | 19 | 100 |  |  |  | 58 | if (my $r = $o->{reporter}) { | 
| 46 | 1 |  |  |  |  | 5 | &$r (file => $file, line => $line, message => $message); | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  | else { | 
| 49 | 18 |  |  |  |  | 179 | warn "$file$line: $message.\n"; | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | # Match a call to SvPV | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | my $svpv_re = qr/ | 
| 56 |  |  |  |  |  |  | ( | 
| 57 |  |  |  |  |  |  | (?:$word_re(?:->|\.))*$word_re | 
| 58 |  |  |  |  |  |  | ) | 
| 59 |  |  |  |  |  |  | \s*=[^;]* | 
| 60 |  |  |  |  |  |  | ( | 
| 61 |  |  |  |  |  |  | SvPV(?:byte|utf8)? | 
| 62 |  |  |  |  |  |  | (?:x|_(?:force|nolen))? | 
| 63 |  |  |  |  |  |  | ) | 
| 64 |  |  |  |  |  |  | \s*\(\s* | 
| 65 |  |  |  |  |  |  | ($word_re) | 
| 66 |  |  |  |  |  |  | \s*,\s* | 
| 67 |  |  |  |  |  |  | ($word_re) | 
| 68 |  |  |  |  |  |  | \s*\) | 
| 69 |  |  |  |  |  |  | /x; | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | # Look for problems with calls to SvPV. | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | sub check_svpv | 
| 74 |  |  |  |  |  |  | { | 
| 75 | 18 |  |  | 18 | 0 | 73 | my ($o) = @_; | 
| 76 | 18 |  |  |  |  | 982 | while ($o->{xs} =~ /($svpv_re)/g) { | 
| 77 | 8 |  |  |  |  | 56 | my ($match, $lvar, $svpv, $arg1, $arg2) = ($1, $2, $3, $4, $5); | 
| 78 | 8 |  |  |  |  | 22 | my $lvar_type = $o->get_type ($lvar); | 
| 79 | 8 |  |  |  |  | 14 | my $arg2_type = $o->get_type ($arg2); | 
| 80 | 8 | 50 |  |  |  | 18 | if ($o->{verbose}) { | 
| 81 | 0 |  |  |  |  | 0 | debugmsg ("<$match> $lvar_type $arg2_type"); | 
| 82 |  |  |  |  |  |  | } | 
| 83 | 8 | 100 | 66 |  |  | 66 | if ($lvar_type && $lvar_type !~ /\bconst\b/) { | 
| 84 | 5 |  |  |  |  | 19 | $o->report ("$lvar not a constant type"); | 
| 85 |  |  |  |  |  |  | } | 
| 86 | 8 | 100 | 100 |  |  | 82 | if ($arg2_type && $arg2_type !~ /\bSTRLEN\b/) { | 
| 87 | 1 |  |  |  |  | 5 | $o->report ("$arg2 is not a STRLEN variable ($arg2_type)"); | 
| 88 |  |  |  |  |  |  | } | 
| 89 | 8 | 100 |  |  |  | 54 | if ($svpv !~ /bytes?|utf8/) { | 
| 90 | 5 |  |  |  |  | 11 | $o->report ("Specify either SvPVbyte or SvPVutf8 to avoid ambiguity; see perldoc perlguts"); | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | # Best equivalents. | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | my %equiv = ( | 
| 98 |  |  |  |  |  |  | #  Newxc is for C++ programmers (cast malloc). | 
| 99 |  |  |  |  |  |  | malloc => 'Newx/Newxc', | 
| 100 |  |  |  |  |  |  | calloc => 'Newxz', | 
| 101 |  |  |  |  |  |  | free => 'Safefree', | 
| 102 |  |  |  |  |  |  | realloc => 'Renew', | 
| 103 |  |  |  |  |  |  | ); | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | # Look for calls to malloc/calloc/realloc/free and suggest replacing | 
| 106 |  |  |  |  |  |  | # them. | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | sub check_malloc | 
| 109 |  |  |  |  |  |  | { | 
| 110 | 18 |  |  | 18 | 0 | 34 | my ($o) = @_; | 
| 111 | 18 |  |  |  |  | 134 | while ($o->{xs} =~ /\b((?:m|c|re)alloc|free)\s*\(/g) { | 
| 112 |  |  |  |  |  |  | # Bad function | 
| 113 | 1 |  |  |  |  | 3 | my $badfun = $1; | 
| 114 | 1 |  |  |  |  | 2 | my $equiv = $equiv{$badfun}; | 
| 115 | 1 | 50 |  |  |  | 4 | if (! $equiv) { | 
| 116 | 0 |  |  |  |  | 0 | $o->report ("(BUG) No equiv for $badfun"); | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | else { | 
| 119 | 1 |  |  |  |  | 5 | $o->report ("Change $badfun to $equiv"); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | # Look for a Perl_ prefix before functions. | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | sub check_perl_prefix | 
| 127 |  |  |  |  |  |  | { | 
| 128 | 18 |  |  | 18 | 0 | 30 | my ($o) = @_; | 
| 129 | 18 |  |  |  |  | 283 | while ($o->{xs} =~ /\b(Perl_$word_re)\b/g) { | 
| 130 | 2 |  |  |  |  | 11 | $o->report ("Remove the 'Perl_' prefix from $1"); | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | # Regular expression to match a C declaration. | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | my $declare_re = qr/ | 
| 137 |  |  |  |  |  |  | ( | 
| 138 |  |  |  |  |  |  | ( | 
| 139 |  |  |  |  |  |  | (?: | 
| 140 |  |  |  |  |  |  | (?:$reserved_re|$word_re) | 
| 141 |  |  |  |  |  |  | (?:\b|\s+) | 
| 142 |  |  |  |  |  |  | | | 
| 143 |  |  |  |  |  |  | \*\s* | 
| 144 |  |  |  |  |  |  | )+ | 
| 145 |  |  |  |  |  |  | ) | 
| 146 |  |  |  |  |  |  | ( | 
| 147 |  |  |  |  |  |  | $word_re | 
| 148 |  |  |  |  |  |  | ) | 
| 149 |  |  |  |  |  |  | ) | 
| 150 |  |  |  |  |  |  | # Match initial value. | 
| 151 |  |  |  |  |  |  | \s*(?:=[^;]+)?; | 
| 152 |  |  |  |  |  |  | /x; | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | # Read the declarations. | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | sub read_declarations | 
| 157 |  |  |  |  |  |  | { | 
| 158 | 18 |  |  | 18 | 0 | 34 | my ($o) = @_; | 
| 159 | 18 |  |  |  |  | 766 | while ($o->{xs} =~ /$declare_re/g) { | 
| 160 | 21 |  |  |  |  | 55 | my $type = $2; | 
| 161 | 21 |  |  |  |  | 33 | my $var = $3; | 
| 162 | 21 | 50 |  |  |  | 43 | if ($o->{verbose}) { | 
| 163 | 0 |  |  |  |  | 0 | debugmsg ("type = $type for $var"); | 
| 164 |  |  |  |  |  |  | } | 
| 165 | 21 | 50 |  |  |  | 42 | if ($o->{vars}{$type}) { | 
| 166 |  |  |  |  |  |  | # This is very likely to produce false positives in a long | 
| 167 |  |  |  |  |  |  | # file. A better way to do this would be to have variables | 
| 168 |  |  |  |  |  |  | # associated with line numbers, so that x on line 10 is | 
| 169 |  |  |  |  |  |  | # different from x on line 20. | 
| 170 | 0 |  |  |  |  | 0 | warn "duplicate variable $var of type $type\n"; | 
| 171 |  |  |  |  |  |  | } | 
| 172 | 21 |  |  |  |  | 438 | $o->{vars}{$var} = $type; | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | # Get the type of variable $var. | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | sub get_type | 
| 179 |  |  |  |  |  |  | { | 
| 180 | 16 |  |  | 16 | 0 | 30 | my ($o, $var) = @_; | 
| 181 |  |  |  |  |  |  | # We currently do not have a way to store and retrieve types of | 
| 182 |  |  |  |  |  |  | # structure members | 
| 183 | 16 | 50 |  |  |  | 55 | if ($var =~ /->|\./) { | 
| 184 | 0 |  |  |  |  | 0 | $o->report ("Cannot get type of $var, please check manually"); | 
| 185 | 0 |  |  |  |  | 0 | return undef; | 
| 186 |  |  |  |  |  |  | } | 
| 187 | 16 |  |  |  |  | 31 | my $type = $o->{vars}{$var}; | 
| 188 | 16 | 100 |  |  |  | 30 | if (! $type) { | 
| 189 | 1 |  |  |  |  | 6 | $o->report ("(BUG) No type for $var"); | 
| 190 |  |  |  |  |  |  | } | 
| 191 | 16 |  |  |  |  | 36 | return $type; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | # Set up the line numbering object. | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | sub line_numbers | 
| 197 |  |  |  |  |  |  | { | 
| 198 | 18 |  |  | 18 | 0 | 39 | my ($o) = @_; | 
| 199 | 18 |  |  |  |  | 76 | my $tln = Text::LineNumber->new ($o->{xs}); | 
| 200 | 18 |  |  |  |  | 571 | $o->{tln} = $tln; | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | # This adds a colon to the end of the file, so it shouldn't really be | 
| 204 |  |  |  |  |  |  | # user-visible. | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | sub get_file | 
| 207 |  |  |  |  |  |  | { | 
| 208 | 19 |  |  | 19 | 0 | 31 | my ($o) = @_; | 
| 209 | 19 | 50 |  |  |  | 46 | if (! $o->{file}) { | 
| 210 | 19 |  |  |  |  | 51 | return ''; | 
| 211 |  |  |  |  |  |  | } | 
| 212 | 0 |  |  |  |  | 0 | return "$o->{file}:"; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | # Clear up old variables, inputs, etc. Don't delete everything since | 
| 216 |  |  |  |  |  |  | # we want to keep at least the field "reporter" from one call to | 
| 217 |  |  |  |  |  |  | # "check" to the next. | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub cleanup | 
| 220 |  |  |  |  |  |  | { | 
| 221 | 18 |  |  | 18 | 0 | 32 | my ($o) = @_; | 
| 222 | 18 |  |  |  |  | 35 | for (qw/vars xs file/) { | 
| 223 | 54 |  |  |  |  | 124 | delete $o->{$_}; | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | # Regex to match (void) in XS function call. | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | my $void_re = qr/ | 
| 230 |  |  |  |  |  |  | $word_re\s* | 
| 231 |  |  |  |  |  |  | \(\s*void\s*\)\s* | 
| 232 |  |  |  |  |  |  | (?= | 
| 233 |  |  |  |  |  |  | # CODE:, PREINIT:, etc. | 
| 234 |  |  |  |  |  |  | [A-Z]+: | 
| 235 |  |  |  |  |  |  | #		    | | 
| 236 |  |  |  |  |  |  | # Normal C function start | 
| 237 |  |  |  |  |  |  | #			\{ | 
| 238 |  |  |  |  |  |  | ) | 
| 239 |  |  |  |  |  |  | /xsm; | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | # Look for (void) XS functions | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | sub check_void_arg | 
| 244 |  |  |  |  |  |  | { | 
| 245 | 18 |  |  | 18 | 0 | 32 | my ($o) = @_; | 
| 246 | 18 |  |  |  |  | 174 | while ($o->{xs} =~ /$void_re/g) { | 
| 247 | 1 |  |  |  |  | 3 | $o->report ("Don't use (void) in function arguments"); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | sub | 
| 252 |  |  |  |  |  |  | check_hash_comments | 
| 253 |  |  |  |  |  |  | { | 
| 254 | 18 |  |  | 18 | 0 | 28 | my ($o) = @_; | 
| 255 | 18 |  |  |  |  | 51 | while ($o->{xs} =~ /^#\s*(\w*)/gsm) { | 
| 256 | 3 |  |  |  |  | 15 | my $hash = $1; | 
| 257 | 3 | 100 |  |  |  | 24 | if ($hash !~ /^(?: | 
| 258 |  |  |  |  |  |  | define| | 
| 259 |  |  |  |  |  |  | else| | 
| 260 |  |  |  |  |  |  | endif| | 
| 261 |  |  |  |  |  |  | error| | 
| 262 |  |  |  |  |  |  | ifdef| | 
| 263 |  |  |  |  |  |  | ifndef| | 
| 264 |  |  |  |  |  |  | if| | 
| 265 |  |  |  |  |  |  | include| | 
| 266 |  |  |  |  |  |  | line| | 
| 267 |  |  |  |  |  |  | undef| | 
| 268 |  |  |  |  |  |  | warning| | 
| 269 |  |  |  |  |  |  | ZZZZZZZZZZZ)(\s+|$)/x) { | 
| 270 | 1 |  |  |  |  | 6 | $o->report ("Put whitespace before # in comments"); | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub | 
| 276 |  |  |  |  |  |  | check_c_pre | 
| 277 |  |  |  |  |  |  | { | 
| 278 | 18 |  |  | 18 | 0 | 30 | my ($o) = @_; | 
| 279 | 18 |  |  |  |  | 66 | while ($o->{xs} =~ /^#\s*(\w*)/gsm) { | 
| 280 | 3 |  |  |  |  | 8 | my $hash = $1; | 
| 281 | 3 | 50 |  |  |  | 18 | if ($hash =~ /(?:if|else|endif)\s+/) { | 
| 282 |  |  |  |  |  |  | # Complicated! | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | sub check_fetch_deref | 
| 288 |  |  |  |  |  |  | { | 
| 289 | 18 |  |  | 18 | 0 | 29 | my ($o) = @_; | 
| 290 | 18 |  |  |  |  | 92 | while ($o->{xs} =~ m!(\*\s*(?:a|h)v_fetch)!g) { | 
| 291 | 1 |  |  |  |  | 4 | $o->report ("Dereference of av/hv_fetch"); | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | sub check_av_len | 
| 296 |  |  |  |  |  |  | { | 
| 297 | 18 |  |  | 18 | 0 | 40 | my ($o) = @_; | 
| 298 | 18 |  |  |  |  | 57 | while ($o->{xs} =~ m!^(.*av_len\s*\([^\)]*\)(.*))!g) { | 
| 299 | 1 |  |  |  |  | 4 | my $later = $2; | 
| 300 | 1 | 50 |  |  |  | 3 | if ($later !~ /\+\s*1/) { | 
| 301 | 1 |  |  |  |  | 4 | $o->report ("Add one to av_len"); | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | #  _   _                       _     _ _     _ | 
| 307 |  |  |  |  |  |  | # | | | |___  ___ _ __  __   _(_)___(_) |__ | | ___ | 
| 308 |  |  |  |  |  |  | # | | | / __|/ _ \ '__| \ \ / / / __| | '_ \| |/ _ \ | 
| 309 |  |  |  |  |  |  | # | |_| \__ \  __/ |     \ V /| \__ \ | |_) | |  __/ | 
| 310 |  |  |  |  |  |  | #  \___/|___/\___|_|      \_/ |_|___/_|_.__/|_|\___| | 
| 311 |  |  |  |  |  |  | # | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | sub new | 
| 314 |  |  |  |  |  |  | { | 
| 315 | 18 |  |  | 18 | 1 | 13435 | my ($class, %options) = @_; | 
| 316 | 18 |  |  |  |  | 44 | my $o = bless {}; | 
| 317 | 18 | 100 |  |  |  | 58 | if (my $r = $options{reporter}) { | 
| 318 | 2 | 100 |  |  |  | 7 | if (ref $r ne 'CODE') { | 
| 319 | 1 |  |  |  |  | 208 | carp "reporter should be a code reference"; | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  | else { | 
| 322 | 1 |  |  |  |  | 13 | $o->{reporter} = $r; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | } | 
| 325 | 18 | 50 |  |  |  | 53 | if (defined $options{verbose}) { | 
| 326 | 0 |  |  |  |  | 0 | $o->{verbose} = $options{verbose}; | 
| 327 |  |  |  |  |  |  | } | 
| 328 | 18 |  |  |  |  | 45 | return $o; | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | sub set_file | 
| 332 |  |  |  |  |  |  | { | 
| 333 | 0 |  |  | 0 | 1 | 0 | my ($o, $file) = @_; | 
| 334 | 0 | 0 |  |  |  | 0 | if (! $file) { | 
| 335 | 0 |  |  |  |  | 0 | $file = undef; | 
| 336 |  |  |  |  |  |  | } | 
| 337 | 0 |  |  |  |  | 0 | $o->{file} = $file; | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | # Check the XS. | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | sub check | 
| 343 |  |  |  |  |  |  | { | 
| 344 | 18 |  |  | 18 | 1 | 1341 | my ($o, $xs) = @_; | 
| 345 | 18 |  |  |  |  | 54 | $o->{xs} = $xs; | 
| 346 | 18 |  |  |  |  | 122 | $o->{xs} = strip_comments ($o->{xs}); | 
| 347 | 18 |  |  |  |  | 811 | $o->line_numbers (); | 
| 348 | 18 |  |  |  |  | 54 | $o->read_declarations (); | 
| 349 | 18 |  |  |  |  | 59 | $o->check_svpv (); | 
| 350 | 18 |  |  |  |  | 108 | $o->check_malloc (); | 
| 351 | 18 |  |  |  |  | 57 | $o->check_perl_prefix (); | 
| 352 | 18 |  |  |  |  | 69 | $o->check_void_arg (); | 
| 353 | 18 |  |  |  |  | 59 | $o->check_c_pre (); | 
| 354 | 18 |  |  |  |  | 48 | $o->check_hash_comments (); | 
| 355 | 18 |  |  |  |  | 43 | $o->check_fetch_deref (); | 
| 356 | 18 |  |  |  |  | 50 | $o->check_av_len (); | 
| 357 |  |  |  |  |  |  | # Final line | 
| 358 | 18 |  |  |  |  | 44 | $o->cleanup (); | 
| 359 |  |  |  |  |  |  | } | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | sub check_file | 
| 362 |  |  |  |  |  |  | { | 
| 363 | 0 |  |  | 0 | 1 |  | my ($o, $file) = @_; | 
| 364 | 0 |  |  |  |  |  | $o->set_file ($file); | 
| 365 | 0 |  |  |  |  |  | my $xs = read_text ($file); | 
| 366 | 0 |  |  |  |  |  | $o->check ($xs); | 
| 367 |  |  |  |  |  |  | } | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | 1; |