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