Blame build-aux/useless-if-before-free

Packit 549fdc
eval '(exit $?0)' && eval 'exec perl -wST "$0" "$@"'
Packit 549fdc
  & eval 'exec perl -wST "$0" $argv:q'
Packit 549fdc
    if 0;
Packit 549fdc
# Detect instances of "if (p) free (p);".
Packit 549fdc
# Likewise "if (p != 0)", "if (0 != p)", or with NULL; and with braces.
Packit 549fdc
Packit 549fdc
my $VERSION = '2016-08-01 17:47'; # UTC
Packit 549fdc
# The definition above must lie within the first 8 lines in order
Packit 549fdc
# for the Emacs time-stamp write hook (at end) to update it.
Packit 549fdc
# If you change this file with Emacs, please let the write hook
Packit 549fdc
# do its job.  Otherwise, update this string manually.
Packit 549fdc
Packit 549fdc
# Copyright (C) 2008-2016 Free Software Foundation, Inc.
Packit 549fdc
Packit 549fdc
# This program is free software: you can redistribute it and/or modify
Packit 549fdc
# it under the terms of the GNU General Public License as published by
Packit 549fdc
# the Free Software Foundation, either version 3 of the License, or
Packit 549fdc
# (at your option) any later version.
Packit 549fdc
Packit 549fdc
# This program is distributed in the hope that it will be useful,
Packit 549fdc
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 549fdc
# GNU General Public License for more details.
Packit 549fdc
Packit 549fdc
# You should have received a copy of the GNU General Public License
Packit 549fdc
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 549fdc
Packit 549fdc
# Written by Jim Meyering
Packit 549fdc
Packit 549fdc
use strict;
Packit 549fdc
use warnings;
Packit 549fdc
use Getopt::Long;
Packit 549fdc
Packit 549fdc
(my $ME = $0) =~ s|.*/||;
Packit 549fdc
Packit 549fdc
# use File::Coda; # http://meyering.net/code/Coda/
Packit 549fdc
END {
Packit 549fdc
  defined fileno STDOUT or return;
Packit 549fdc
  close STDOUT and return;
Packit 549fdc
  warn "$ME: failed to close standard output: $!\n";
Packit 549fdc
  $? ||= 1;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
sub usage ($)
Packit 549fdc
{
Packit 549fdc
  my ($exit_code) = @_;
Packit 549fdc
  my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
Packit 549fdc
  if ($exit_code != 0)
Packit 549fdc
    {
Packit 549fdc
      print $STREAM "Try '$ME --help' for more information.\n";
Packit 549fdc
    }
Packit 549fdc
  else
Packit 549fdc
    {
Packit 549fdc
      print $STREAM <
Packit 549fdc
Usage: $ME [OPTIONS] FILE...
Packit 549fdc
Packit 549fdc
Detect any instance in FILE of a useless "if" test before a free call, e.g.,
Packit 549fdc
"if (p) free (p);".  Any such test may be safely removed without affecting
Packit 549fdc
the semantics of the C code in FILE.  Use --name=FOO --name=BAR to also
Packit 549fdc
detect free-like functions named FOO and BAR.
Packit 549fdc
Packit 549fdc
OPTIONS:
Packit 549fdc
Packit 549fdc
   --list       print only the name of each matching FILE (\\0-terminated)
Packit 549fdc
   --name=N     add name N to the list of \'free\'-like functions to detect;
Packit 549fdc
                  may be repeated
Packit 549fdc
Packit 549fdc
   --help       display this help and exit
Packit 549fdc
   --version    output version information and exit
Packit 549fdc
Packit 549fdc
Exit status:
Packit 549fdc
Packit 549fdc
  0   one or more matches
Packit 549fdc
  1   no match
Packit 549fdc
  2   an error
Packit 549fdc
Packit 549fdc
EXAMPLE:
Packit 549fdc
Packit 549fdc
For example, this command prints all removable "if" tests before "free"
Packit 549fdc
and "kfree" calls in the linux kernel sources:
Packit 549fdc
Packit 549fdc
    git ls-files -z |xargs -0 $ME --name=kfree
Packit 549fdc
Packit 549fdc
EOF
Packit 549fdc
    }
Packit 549fdc
  exit $exit_code;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
sub is_NULL ($)
Packit 549fdc
{
Packit 549fdc
  my ($expr) = @_;
Packit 549fdc
  return ($expr eq 'NULL' || $expr eq '0');
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
{
Packit 549fdc
  sub EXIT_MATCH {0}
Packit 549fdc
  sub EXIT_NO_MATCH {1}
Packit 549fdc
  sub EXIT_ERROR {2}
Packit 549fdc
  my $err = EXIT_NO_MATCH;
Packit 549fdc
Packit 549fdc
  my $list;
Packit 549fdc
  my @name = qw(free);
Packit 549fdc
  GetOptions
Packit 549fdc
    (
Packit 549fdc
     help => sub { usage 0 },
Packit 549fdc
     version => sub { print "$ME version $VERSION\n"; exit },
Packit 549fdc
     list => \$list,
Packit 549fdc
     'name=s@' => \@name,
Packit 549fdc
    ) or usage 1;
Packit 549fdc
Packit 549fdc
  # Make sure we have the right number of non-option arguments.
Packit 549fdc
  # Always tell the user why we fail.
Packit 549fdc
  @ARGV < 1
Packit 549fdc
    and (warn "$ME: missing FILE argument\n"), usage EXIT_ERROR;
Packit 549fdc
Packit 549fdc
  my $or = join '|', @name;
Packit 549fdc
  my $regexp = qr/(?:$or)/;
Packit 549fdc
Packit 549fdc
  # Set the input record separator.
Packit 549fdc
  # Note: this makes it impractical to print line numbers.
Packit 549fdc
  $/ = '"';
Packit 549fdc
Packit 549fdc
  my $found_match = 0;
Packit 549fdc
 FILE:
Packit 549fdc
  foreach my $file (@ARGV)
Packit 549fdc
    {
Packit 549fdc
      open FH, '<', $file
Packit 549fdc
        or (warn "$ME: can't open '$file' for reading: $!\n"),
Packit 549fdc
          $err = EXIT_ERROR, next;
Packit 549fdc
      while (defined (my $line = <FH>))
Packit 549fdc
        {
Packit 549fdc
          # Skip non-matching lines early to save time
Packit 549fdc
          $line =~ /\bif\b/
Packit 549fdc
            or next;
Packit 549fdc
          while ($line =~
Packit 549fdc
              /\b(if\s*\(\s*([^)]+?)(?:\s*!=\s*([^)]+?))?\s*\)
Packit 549fdc
              #  1          2                  3
Packit 549fdc
               (?:   \s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;|
Packit 549fdc
                \s*\{\s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;\s*\}))/sxg)
Packit 549fdc
            {
Packit 549fdc
              my $all = $1;
Packit 549fdc
              my ($lhs, $rhs) = ($2, $3);
Packit 549fdc
              my ($free_opnd, $braced_free_opnd) = ($4, $5);
Packit 549fdc
              my $non_NULL;
Packit 549fdc
              if (!defined $rhs) { $non_NULL = $lhs }
Packit 549fdc
              elsif (is_NULL $rhs) { $non_NULL = $lhs }
Packit 549fdc
              elsif (is_NULL $lhs) { $non_NULL = $rhs }
Packit 549fdc
              else { next }
Packit 549fdc
Packit 549fdc
              # Compare the non-NULL part of the "if" expression and the
Packit 549fdc
              # free'd expression, without regard to white space.
Packit 549fdc
              $non_NULL =~ tr/ \t//d;
Packit 549fdc
              my $e2 = defined $free_opnd ? $free_opnd : $braced_free_opnd;
Packit 549fdc
              $e2 =~ tr/ \t//d;
Packit 549fdc
              if ($non_NULL eq $e2)
Packit 549fdc
                {
Packit 549fdc
                  $found_match = 1;
Packit 549fdc
                  $list
Packit 549fdc
                    and (print "$file\0"), next FILE;
Packit 549fdc
                  print "$file: $all\n";
Packit 549fdc
                }
Packit 549fdc
            }
Packit 549fdc
        }
Packit 549fdc
    }
Packit 549fdc
  continue
Packit 549fdc
    {
Packit 549fdc
      close FH;
Packit 549fdc
    }
Packit 549fdc
Packit 549fdc
  $found_match && $err == EXIT_NO_MATCH
Packit 549fdc
    and $err = EXIT_MATCH;
Packit 549fdc
Packit 549fdc
  exit $err;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
my $foo = <<'EOF';
Packit 549fdc
# The above is to *find* them.
Packit 549fdc
# This adjusts them, removing the unnecessary "if (p)" part.
Packit 549fdc
Packit 549fdc
# FIXME: do something like this as an option (doesn't do braces):
Packit 549fdc
free=xfree
Packit 549fdc
git grep -l -z "$free *(" \
Packit 549fdc
  | xargs -0 useless-if-before-free -l --name="$free" \
Packit 549fdc
  | xargs -0 perl -0x3b -pi -e \
Packit 549fdc
   's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s+('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\)\s*;)/$2/s'
Packit 549fdc
Packit 549fdc
# Use the following to remove redundant uses of kfree inside braces.
Packit 549fdc
# Note that -0777 puts perl in slurp-whole-file mode;
Packit 549fdc
# but we have plenty of memory, these days...
Packit 549fdc
free=kfree
Packit 549fdc
git grep -l -z "$free *(" \
Packit 549fdc
  | xargs -0 useless-if-before-free -l --name="$free" \
Packit 549fdc
  | xargs -0 perl -0777 -pi -e \
Packit 549fdc
     's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s*\{\s*('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\);)\s*\}[^\n]*$/$2/gms'
Packit 549fdc
Packit 549fdc
Be careful that the result of the above transformation is valid.
Packit 549fdc
If the matched string is followed by "else", then obviously, it won't be.
Packit 549fdc
Packit 549fdc
When modifying files, refuse to process anything other than a regular file.
Packit 549fdc
EOF
Packit 549fdc
Packit 549fdc
## Local Variables:
Packit 549fdc
## mode: perl
Packit 549fdc
## indent-tabs-mode: nil
Packit 549fdc
## eval: (add-hook 'write-file-hooks 'time-stamp)
Packit 549fdc
## time-stamp-start: "my $VERSION = '"
Packit 549fdc
## time-stamp-format: "%:y-%02m-%02d %02H:%02M"
Packit 549fdc
## time-stamp-time-zone: "UTC0"
Packit 549fdc
## time-stamp-end: "'; # UTC"
Packit 549fdc
## End: