New script and module: useless-if-before-free
[gnulib.git] / build-aux / useless-if-before-free
1 #!/usr/bin/perl -T
2 # Detect instances of "if (p) free (p);".
3 # Likewise for "if (p != NULL) free (p);".  And with braces.
4
5 my $VERSION = '2008-02-10 16:09'; # UTC
6 # The definition above must lie within the first 8 lines in order
7 # for the Emacs time-stamp write hook (at end) to update it.
8 # If you change this file with Emacs, please let the write hook
9 # do its job.  Otherwise, update this string manually.
10
11 # Copyright (C) 2008 Free Software Foundation, Inc.
12
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22
23 # You should have received a copy of the GNU General Public License
24 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
26 # Written by Jim Meyering
27
28 # Exit status is like grep: 0 for no match. 1 for any number.
29 # Note: giving line numbers isn't practical, since I've reset the
30 # input record separator.
31 use strict;
32 use warnings;
33 use Getopt::Long;
34
35 (my $ME = $0) =~ s|.*/||;
36
37 sub usage ($)
38 {
39   my ($exit_code) = @_;
40   my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
41   if ($exit_code != 0)
42     {
43       print $STREAM "Try `$ME --help' for more information.\n";
44     }
45   else
46     {
47       print $STREAM <<EOF;
48 Usage: $ME [OPTIONS] FILE...
49
50 Detect any instance in FILE of a useless "if" test before a free call, e.g.,
51 "if (p) free (p);".  Any such test may be safely removed without affecting
52 the semantics of the C code in FILE.  Use --name=FOO --name=BAR to also
53 detect free-like functions named FOO and BAR.
54
55 OPTIONS:
56
57    --name=N     add name N to the list of `free'-like functions to detect;
58                   may be repeated
59
60    --help       display this help and exit
61    --version    output version information and exit
62
63 EXAMPLE:
64
65 For example, this command prints all removable "if" tests before "free"
66 and "kfree" calls in the linux kernel sources:
67
68     git ls-files -z |xargs -0 $ME --name=kfree
69
70 EOF
71     }
72   exit $exit_code;
73 }
74
75 {
76   my @name = qw(free);
77   GetOptions
78     (
79      help => sub { usage 0 },
80      version => sub { print "$ME version $VERSION\n"; exit },
81      'name=s@' => \@name,
82     ) or usage 1;
83
84   # Make sure we have the right number of non-option arguments.
85   # Always tell the user why we fail.
86   @ARGV < 1
87     and (warn "$ME: missing FILE argument\n"), usage 1;
88
89   my $or = join '|', @name;
90   my $regexp = qr/(?:$or)/;
91
92   # Set the input record separator.
93   $/ = '"';
94
95   my $found_match = 0;
96   foreach my $file (@ARGV)
97     {
98       open FH, '<', $file
99         or die "$ME: can't open `$file' for reading: $!\n";
100       while (defined (my $line = <FH>))
101         {
102           if ($line =~
103               /\b(if\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)
104                (?:   \s*$regexp\s*\(\s*\2\s*\)|
105                 \s*\{\s*$regexp\s*\(\s*\2\s*\)\s*;\s*\}))/sx)
106             {
107               print "$file: $1\n";
108               $found_match = 1;
109             }
110         }
111       close FH;
112     }
113   exit !$found_match;
114 }
115
116 my $foo = <<'EOF';
117 # The above is to *find* them.
118 # This adjusts them, removing the unnecessary "if (p)" part.
119
120 # FIXME: do something like this as an option (doesn't do braces):
121 git ls-files -z |xargs -0 \
122 perl -0x3b -pi -e 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(k?free\s*\(\s*\1\s*\))/$2/s'
123
124 Be careful that the result of the above transformation is valid.
125 If the matched string is followed by "else", then obviously, it won't be.
126
127 When modifying files, refuse to process anything other than a regular file.
128 EOF
129
130 ## Local Variables:
131 ## indent-tabs-mode: nil
132 ## eval: (add-hook 'write-file-hooks 'time-stamp)
133 ## time-stamp-start: "my $VERSION = '"
134 ## time-stamp-format: "%:y-%02m-%02d %02H:%02M"
135 ## time-stamp-time-zone: "UTC"
136 ## time-stamp-end: "'; # UTC"
137 ## End: