New module 'mbrtowc'.
[gnulib.git] / build-aux / useless-if-before-free
index ef03675..0bae2c4 100755 (executable)
@@ -2,7 +2,7 @@
 # Detect instances of "if (p) free (p);".
 # Likewise for "if (p != NULL) free (p);".  And with braces.
 
 # Detect instances of "if (p) free (p);".
 # Likewise for "if (p != NULL) free (p);".  And with braces.
 
-my $VERSION = '2008-02-10 16:09'; # UTC
+my $VERSION = '2008-05-25 17:36'; # 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
 # 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
@@ -25,15 +25,20 @@ 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) = @_;
 sub usage ($)
 {
   my ($exit_code) = @_;
@@ -54,12 +59,19 @@ detect free-like functions named FOO and BAR.
 
 OPTIONS:
 
 
 OPTIONS:
 
+   --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
 
    --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
 
+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"
@@ -73,44 +85,71 @@ EOF
 }
 
 {
 }
 
 {
+  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*NULL)?\s*\)
+               (?:   \s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)|
+                \s*\{\s*$regexp\s*\((?:\s*\([^)]+\))?\s*([^)]+)\)\s*;\s*\}))/sxg)
             {
             {
-              print "$file: $1\n";
-              $found_match = 1;
+              # Compare "if" expression and free'd expression,
+              # without regard to white space.
+              (my $e1 = $2) =~ tr/ \t//d;
+              my $e2 = defined $3 ? $3 : $4;
+              $e2 =~ tr/ \t//d;
+              if ($e1 eq $e2)
+                {
+                  $found_match = 1;
+                  $list
+                    and (print "$file\0"), next FILE;
+                  print "$file: $1\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 +157,12 @@ 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'
+useless-if-before-free -l $(lid -knone free) | xargs -0 \
+  perl -0x3b -pi -e \
+   's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\((?:\s*\([^)]+\))?\s*\1\s*\))/$2/s'
+
+# Or, with git:
+git ls-files -z |xargs -0 perl -0x3b -pi -e '...'
 
 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.