Commit | Line | Data |
---|---|---|
c84bdaf6 LC |
1 | eval '(exit $?0)' && eval 'exec perl -wST "$0" ${1+"$@"}' |
2 | & eval 'exec perl -wST "$0" $argv:q' | |
3 | if 0; | |
4 | # Detect instances of "if (p) free (p);". | |
49114fd4 | 5 | # Likewise "if (p != 0)", "if (0 != p)", or with NULL; and with braces. |
c84bdaf6 | 6 | |
f0007cad | 7 | my $VERSION = '2012-01-06 07:23'; # UTC |
c84bdaf6 LC |
8 | # The definition above must lie within the first 8 lines in order |
9 | # for the Emacs time-stamp write hook (at end) to update it. | |
10 | # If you change this file with Emacs, please let the write hook | |
11 | # do its job. Otherwise, update this string manually. | |
12 | ||
5e69ceb7 | 13 | # Copyright (C) 2008-2014 Free Software Foundation, Inc. |
c84bdaf6 LC |
14 | |
15 | # This program is free software: you can redistribute it and/or modify | |
16 | # it under the terms of the GNU General Public License as published by | |
17 | # the Free Software Foundation, either version 3 of the License, or | |
18 | # (at your option) any later version. | |
19 | ||
20 | # This program is distributed in the hope that it will be useful, | |
21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | # GNU General Public License for more details. | |
24 | ||
25 | # You should have received a copy of the GNU General Public License | |
26 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
27 | ||
28 | # Written by Jim Meyering | |
29 | ||
30 | use strict; | |
31 | use warnings; | |
32 | use Getopt::Long; | |
33 | ||
34 | (my $ME = $0) =~ s|.*/||; | |
35 | ||
36 | # use File::Coda; # http://meyering.net/code/Coda/ | |
37 | END { | |
38 | defined fileno STDOUT or return; | |
39 | close STDOUT and return; | |
40 | warn "$ME: failed to close standard output: $!\n"; | |
41 | $? ||= 1; | |
42 | } | |
43 | ||
44 | sub usage ($) | |
45 | { | |
46 | my ($exit_code) = @_; | |
47 | my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); | |
48 | if ($exit_code != 0) | |
49 | { | |
f0007cad | 50 | print $STREAM "Try '$ME --help' for more information.\n"; |
c84bdaf6 LC |
51 | } |
52 | else | |
53 | { | |
54 | print $STREAM <<EOF; | |
55 | Usage: $ME [OPTIONS] FILE... | |
56 | ||
57 | Detect any instance in FILE of a useless "if" test before a free call, e.g., | |
58 | "if (p) free (p);". Any such test may be safely removed without affecting | |
59 | the semantics of the C code in FILE. Use --name=FOO --name=BAR to also | |
60 | detect free-like functions named FOO and BAR. | |
61 | ||
62 | OPTIONS: | |
63 | ||
49114fd4 | 64 | --list print only the name of each matching FILE (\\0-terminated) |
f0007cad | 65 | --name=N add name N to the list of \'free\'-like functions to detect; |
c84bdaf6 LC |
66 | may be repeated |
67 | ||
68 | --help display this help and exit | |
69 | --version output version information and exit | |
70 | ||
71 | Exit status: | |
72 | ||
73 | 0 one or more matches | |
74 | 1 no match | |
75 | 2 an error | |
76 | ||
77 | EXAMPLE: | |
78 | ||
79 | For example, this command prints all removable "if" tests before "free" | |
80 | and "kfree" calls in the linux kernel sources: | |
81 | ||
82 | git ls-files -z |xargs -0 $ME --name=kfree | |
83 | ||
84 | EOF | |
85 | } | |
86 | exit $exit_code; | |
87 | } | |
88 | ||
89 | sub is_NULL ($) | |
90 | { | |
91 | my ($expr) = @_; | |
92 | return ($expr eq 'NULL' || $expr eq '0'); | |
93 | } | |
94 | ||
95 | { | |
96 | sub EXIT_MATCH {0} | |
97 | sub EXIT_NO_MATCH {1} | |
98 | sub EXIT_ERROR {2} | |
99 | my $err = EXIT_NO_MATCH; | |
100 | ||
101 | my $list; | |
102 | my @name = qw(free); | |
103 | GetOptions | |
104 | ( | |
105 | help => sub { usage 0 }, | |
106 | version => sub { print "$ME version $VERSION\n"; exit }, | |
107 | list => \$list, | |
108 | 'name=s@' => \@name, | |
109 | ) or usage 1; | |
110 | ||
111 | # Make sure we have the right number of non-option arguments. | |
112 | # Always tell the user why we fail. | |
113 | @ARGV < 1 | |
114 | and (warn "$ME: missing FILE argument\n"), usage EXIT_ERROR; | |
115 | ||
116 | my $or = join '|', @name; | |
117 | my $regexp = qr/(?:$or)/; | |
118 | ||
119 | # Set the input record separator. | |
120 | # Note: this makes it impractical to print line numbers. | |
121 | $/ = '"'; | |
122 | ||
123 | my $found_match = 0; | |
124 | FILE: | |
125 | foreach my $file (@ARGV) | |
126 | { | |
127 | open FH, '<', $file | |
f0007cad | 128 | or (warn "$ME: can't open '$file' for reading: $!\n"), |
c84bdaf6 LC |
129 | $err = EXIT_ERROR, next; |
130 | while (defined (my $line = <FH>)) | |
131 | { | |
132 | while ($line =~ | |
133 | /\b(if\s*\(\s*([^)]+?)(?:\s*!=\s*([^)]+?))?\s*\) | |
134 | # 1 2 3 | |
dd7d0148 | 135 | (?: \s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;| |
c84bdaf6 LC |
136 | \s*\{\s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;\s*\}))/sxg) |
137 | { | |
138 | my $all = $1; | |
139 | my ($lhs, $rhs) = ($2, $3); | |
140 | my ($free_opnd, $braced_free_opnd) = ($4, $5); | |
141 | my $non_NULL; | |
142 | if (!defined $rhs) { $non_NULL = $lhs } | |
143 | elsif (is_NULL $rhs) { $non_NULL = $lhs } | |
144 | elsif (is_NULL $lhs) { $non_NULL = $rhs } | |
145 | else { next } | |
146 | ||
147 | # Compare the non-NULL part of the "if" expression and the | |
148 | # free'd expression, without regard to white space. | |
149 | $non_NULL =~ tr/ \t//d; | |
150 | my $e2 = defined $free_opnd ? $free_opnd : $braced_free_opnd; | |
151 | $e2 =~ tr/ \t//d; | |
152 | if ($non_NULL eq $e2) | |
153 | { | |
154 | $found_match = 1; | |
155 | $list | |
156 | and (print "$file\0"), next FILE; | |
157 | print "$file: $all\n"; | |
158 | } | |
159 | } | |
160 | } | |
161 | } | |
162 | continue | |
163 | { | |
164 | close FH; | |
165 | } | |
166 | ||
167 | $found_match && $err == EXIT_NO_MATCH | |
168 | and $err = EXIT_MATCH; | |
169 | ||
170 | exit $err; | |
171 | } | |
172 | ||
173 | my $foo = <<'EOF'; | |
174 | # The above is to *find* them. | |
175 | # This adjusts them, removing the unnecessary "if (p)" part. | |
176 | ||
177 | # FIXME: do something like this as an option (doesn't do braces): | |
178 | free=xfree | |
179 | git grep -l -z "$free *(" \ | |
180 | | xargs -0 useless-if-before-free -l --name="$free" \ | |
181 | | xargs -0 perl -0x3b -pi -e \ | |
dd7d0148 | 182 | 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s+('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\)\s*;)/$2/s' |
c84bdaf6 LC |
183 | |
184 | # Use the following to remove redundant uses of kfree inside braces. | |
185 | # Note that -0777 puts perl in slurp-whole-file mode; | |
186 | # but we have plenty of memory, these days... | |
187 | free=kfree | |
188 | git grep -l -z "$free *(" \ | |
189 | | xargs -0 useless-if-before-free -l --name="$free" \ | |
190 | | xargs -0 perl -0777 -pi -e \ | |
191 | 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s*\{\s*('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\);)\s*\}[^\n]*$/$2/gms' | |
192 | ||
193 | Be careful that the result of the above transformation is valid. | |
194 | If the matched string is followed by "else", then obviously, it won't be. | |
195 | ||
196 | When modifying files, refuse to process anything other than a regular file. | |
197 | EOF | |
198 | ||
199 | ## Local Variables: | |
200 | ## mode: perl | |
201 | ## indent-tabs-mode: nil | |
202 | ## eval: (add-hook 'write-file-hooks 'time-stamp) | |
203 | ## time-stamp-start: "my $VERSION = '" | |
204 | ## time-stamp-format: "%:y-%02m-%02d %02H:%02M" | |
205 | ## time-stamp-time-zone: "UTC" | |
206 | ## time-stamp-end: "'; # UTC" | |
207 | ## End: |