NEWS.stable: log cherry-pick [e446f25]->[c092018] relocatable-shell: Update suggested...
[gnulib.git] / build-aux / useless-if-before-free
index ef03675..4c76c75 100755 (executable)
@@ -1,14 +1,16 @@
-#!/usr/bin/perl -T
+eval '(exit $?0)' && eval 'exec perl -wST "$0" ${1+"$@"}'
+  & eval 'exec perl -wST "$0" $argv:q'
+    if 0;
 # Detect instances of "if (p) free (p);".
 # Detect instances of "if (p) free (p);".
-# Likewise for "if (p != NULL) free (p);".  And with braces.
+# Likewise "if (p != 0)", "if (0 != p)", or with NULL; and with braces.
 
 
-my $VERSION = '2008-02-10 16:09'; # UTC
+my $VERSION = '2012-01-06 07:23'; # UTC
 # The definition above must lie within the first 8 lines in order
 # for the Emacs time-stamp write hook (at end) to update it.
 # If you change this file with Emacs, please let the write hook
 # do its job.  Otherwise, update this string manually.
 
 # The definition above must lie within the first 8 lines in order
 # for the Emacs time-stamp write hook (at end) to update it.
 # If you change this file with Emacs, please let the write hook
 # do its job.  Otherwise, update this string manually.
 
-# Copyright (C) 2008 Free Software Foundation, Inc.
+# Copyright (C) 2008-2014 Free Software Foundation, Inc.
 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -25,22 +27,27 @@ my $VERSION = '2008-02-10 16:09'; # UTC
 
 # Written by Jim Meyering
 
 
 # Written by Jim Meyering
 
-# Exit status is like grep: 0 for no match. 1 for any number.
-# Note: giving line numbers isn't practical, since I've reset the
-# input record separator.
 use strict;
 use warnings;
 use Getopt::Long;
 
 (my $ME = $0) =~ s|.*/||;
 
 use strict;
 use warnings;
 use Getopt::Long;
 
 (my $ME = $0) =~ s|.*/||;
 
+# use File::Coda; # http://meyering.net/code/Coda/
+END {
+  defined fileno STDOUT or return;
+  close STDOUT and return;
+  warn "$ME: failed to close standard output: $!\n";
+  $? ||= 1;
+}
+
 sub usage ($)
 {
   my ($exit_code) = @_;
   my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
   if ($exit_code != 0)
     {
 sub usage ($)
 {
   my ($exit_code) = @_;
   my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
   if ($exit_code != 0)
     {
-      print $STREAM "Try `$ME --help' for more information.\n";
+      print $STREAM "Try '$ME --help' for more information.\n";
     }
   else
     {
     }
   else
     {
@@ -54,12 +61,19 @@ detect free-like functions named FOO and BAR.
 
 OPTIONS:
 
 
 OPTIONS:
 
-   --name=N     add name N to the list of `free'-like functions to detect;
+   --list       print only the name of each matching FILE (\\0-terminated)
+   --name=N     add name N to the list of \'free\'-like functions to detect;
                   may be repeated
 
    --help       display this help and exit
    --version    output version information and exit
 
                   may be repeated
 
    --help       display this help and exit
    --version    output version information and exit
 
+Exit status:
+
+  0   one or more matches
+  1   no match
+  2   an error
+
 EXAMPLE:
 
 For example, this command prints all removable "if" tests before "free"
 EXAMPLE:
 
 For example, this command prints all removable "if" tests before "free"
@@ -72,45 +86,88 @@ EOF
   exit $exit_code;
 }
 
   exit $exit_code;
 }
 
+sub is_NULL ($)
+{
+  my ($expr) = @_;
+  return ($expr eq 'NULL' || $expr eq '0');
+}
+
 {
 {
+  sub EXIT_MATCH {0}
+  sub EXIT_NO_MATCH {1}
+  sub EXIT_ERROR {2}
+  my $err = EXIT_NO_MATCH;
+
+  my $list;
   my @name = qw(free);
   GetOptions
     (
      help => sub { usage 0 },
      version => sub { print "$ME version $VERSION\n"; exit },
   my @name = qw(free);
   GetOptions
     (
      help => sub { usage 0 },
      version => sub { print "$ME version $VERSION\n"; exit },
+     list => \$list,
      'name=s@' => \@name,
     ) or usage 1;
 
   # Make sure we have the right number of non-option arguments.
   # Always tell the user why we fail.
   @ARGV < 1
      'name=s@' => \@name,
     ) or usage 1;
 
   # Make sure we have the right number of non-option arguments.
   # Always tell the user why we fail.
   @ARGV < 1
-    and (warn "$ME: missing FILE argument\n"), usage 1;
+    and (warn "$ME: missing FILE argument\n"), usage EXIT_ERROR;
 
   my $or = join '|', @name;
   my $regexp = qr/(?:$or)/;
 
   # Set the input record separator.
 
   my $or = join '|', @name;
   my $regexp = qr/(?:$or)/;
 
   # Set the input record separator.
+  # Note: this makes it impractical to print line numbers.
   $/ = '"';
 
   my $found_match = 0;
   $/ = '"';
 
   my $found_match = 0;
+ FILE:
   foreach my $file (@ARGV)
     {
       open FH, '<', $file
   foreach my $file (@ARGV)
     {
       open FH, '<', $file
-        or die "$ME: can't open `$file' for reading: $!\n";
+        or (warn "$ME: can't open '$file' for reading: $!\n"),
+          $err = EXIT_ERROR, next;
       while (defined (my $line = <FH>))
         {
       while (defined (my $line = <FH>))
         {
-          if ($line =~
-              /\b(if\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)
-               (?:   \s*$regexp\s*\(\s*\2\s*\)|
-                \s*\{\s*$regexp\s*\(\s*\2\s*\)\s*;\s*\}))/sx)
+          while ($line =~
+              /\b(if\s*\(\s*([^)]+?)(?:\s*!=\s*([^)]+?))?\s*\)
+              #  1          2                  3
+               (?:   \s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;|
+                \s*\{\s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;\s*\}))/sxg)
             {
             {
-              print "$file: $1\n";
-              $found_match = 1;
+              my $all = $1;
+              my ($lhs, $rhs) = ($2, $3);
+              my ($free_opnd, $braced_free_opnd) = ($4, $5);
+              my $non_NULL;
+              if (!defined $rhs) { $non_NULL = $lhs }
+              elsif (is_NULL $rhs) { $non_NULL = $lhs }
+              elsif (is_NULL $lhs) { $non_NULL = $rhs }
+              else { next }
+
+              # Compare the non-NULL part of the "if" expression and the
+              # free'd expression, without regard to white space.
+              $non_NULL =~ tr/ \t//d;
+              my $e2 = defined $free_opnd ? $free_opnd : $braced_free_opnd;
+              $e2 =~ tr/ \t//d;
+              if ($non_NULL eq $e2)
+                {
+                  $found_match = 1;
+                  $list
+                    and (print "$file\0"), next FILE;
+                  print "$file: $all\n";
+                }
             }
         }
             }
         }
+    }
+  continue
+    {
       close FH;
     }
       close FH;
     }
-  exit !$found_match;
+
+  $found_match && $err == EXIT_NO_MATCH
+    and $err = EXIT_MATCH;
+
+  exit $err;
 }
 
 my $foo = <<'EOF';
 }
 
 my $foo = <<'EOF';
@@ -118,8 +175,20 @@ my $foo = <<'EOF';
 # This adjusts them, removing the unnecessary "if (p)" part.
 
 # FIXME: do something like this as an option (doesn't do braces):
 # This adjusts them, removing the unnecessary "if (p)" part.
 
 # FIXME: do something like this as an option (doesn't do braces):
-git ls-files -z |xargs -0 \
-perl -0x3b -pi -e 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(k?free\s*\(\s*\1\s*\))/$2/s'
+free=xfree
+git grep -l -z "$free *(" \
+  | xargs -0 useless-if-before-free -l --name="$free" \
+  | xargs -0 perl -0x3b -pi -e \
+   's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s+('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\)\s*;)/$2/s'
+
+# Use the following to remove redundant uses of kfree inside braces.
+# Note that -0777 puts perl in slurp-whole-file mode;
+# but we have plenty of memory, these days...
+free=kfree
+git grep -l -z "$free *(" \
+  | xargs -0 useless-if-before-free -l --name="$free" \
+  | xargs -0 perl -0777 -pi -e \
+     's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*(?:0|NULL))?\s*\)\s*\{\s*('"$free"'\s*\((?:\s*\([^)]+\))?\s*\1\s*\);)\s*\}[^\n]*$/$2/gms'
 
 Be careful that the result of the above transformation is valid.
 If the matched string is followed by "else", then obviously, it won't be.
 
 Be careful that the result of the above transformation is valid.
 If the matched string is followed by "else", then obviously, it won't be.
@@ -128,6 +197,7 @@ When modifying files, refuse to process anything other than a regular file.
 EOF
 
 ## Local Variables:
 EOF
 
 ## Local Variables:
+## mode: perl
 ## indent-tabs-mode: nil
 ## eval: (add-hook 'write-file-hooks 'time-stamp)
 ## time-stamp-start: "my $VERSION = '"
 ## indent-tabs-mode: nil
 ## eval: (add-hook 'write-file-hooks 'time-stamp)
 ## time-stamp-start: "my $VERSION = '"