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.
 
-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
@@ -25,15 +25,20 @@ my $VERSION = '2008-02-10 16:09'; # UTC
 
 # 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 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) = @_;
@@ -54,12 +59,19 @@ detect free-like functions named FOO and BAR.
 
 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
 
+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"
@@ -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 },
+     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
-    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.
+  # Note: this makes it impractical to print line numbers.
   $/ = '"';
 
   my $found_match = 0;
+ 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>))
         {
-          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;
     }
-  exit !$found_match;
+
+  $found_match && $err == EXIT_NO_MATCH
+    and $err = EXIT_MATCH;
+
+  exit $err;
 }
 
 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):
-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.