Add non-destructive modes to pgindent
authorAndrew Dunstan <andrew@dunslane.net>
Mon, 23 Jan 2023 12:00:06 +0000 (07:00 -0500)
committerAndrew Dunstan <andrew@dunslane.net>
Mon, 23 Jan 2023 12:09:51 +0000 (07:09 -0500)
This adds two modes of running pgindent, neither of which results in
any changes being made to the source code. The --show-diff option shows
what changes would have been made, and the --silent-diff option just
exits with a status of 2 if any changes would be made. The second of
these is intended for scripting use in places such as git hooks.

Along the way some code cleanup is done, and a --help option is also
added.

Reviewed by Tom Lane

Discussion: https://postgr.es/m/c9c9fa6d-6de6-48c2-4f8b-0fbeef026439@dunslane.net

src/tools/pgindent/pgindent
src/tools/pgindent/pgindent.man

index 741b0ccb586056e4993b4364793f9721a112854d..73caa831d0afb9b2248cbc404617d7edc8a8882a 100755 (executable)
@@ -21,16 +21,28 @@ my $indent_opts =
 
 my $devnull = File::Spec->devnull;
 
-my ($typedefs_file, $typedef_str, $code_base, $excludes, $indent, $build);
+my ($typedefs_file, $typedef_str, $code_base,
+   $excludes,      $indent,      $build,
+   $show_diff,     $silent_diff, $help);
+
+$help = 0;
 
 my %options = (
+   "help"               => \$help,
    "typedefs=s"         => \$typedefs_file,
    "list-of-typedefs=s" => \$typedef_str,
    "code-base=s"        => \$code_base,
    "excludes=s"         => \$excludes,
    "indent=s"           => \$indent,
-   "build"              => \$build,);
-GetOptions(%options) || die "bad command line argument\n";
+   "build"              => \$build,
+   "show-diff"          => \$show_diff,
+   "silent-diff"        => \$silent_diff,);
+GetOptions(%options) || usage("bad command line argument");
+
+usage() if $help;
+
+usage("Cannot have both --silent-diff and --show-diff")
+  if $silent_diff && $show_diff;
 
 run_build($code_base) if ($build);
 
@@ -229,8 +241,7 @@ sub pre_indent
 
 sub post_indent
 {
-   my $source          = shift;
-   my $source_filename = shift;
+   my $source = shift;
 
    # Restore CATALOG lines
    $source =~ s!^/\*(CATALOG\(.*)\*/$!$1!gm;
@@ -280,33 +291,21 @@ sub run_indent
    close($src_out);
 
    return $source;
-
 }
 
-
-# for development diagnostics
-sub diff
+sub show_diff
 {
-   my $pre   = shift;
-   my $post  = shift;
-   my $flags = shift || "";
-
-   print STDERR "running diff\n";
+   my $indented        = shift;
+   my $source_filename = shift;
 
-   my $pre_fh  = new File::Temp(TEMPLATE => "pgdiffbXXXXX");
-   my $post_fh = new File::Temp(TEMPLATE => "pgdiffaXXXXX");
+   my $post_fh = new File::Temp(TEMPLATE => "pgdiffXXXXX");
 
-   print $pre_fh $pre;
-   print $post_fh $post;
+   print $post_fh $indented;
 
-   $pre_fh->close();
    $post_fh->close();
 
-   system( "diff $flags "
-         . $pre_fh->filename . " "
-         . $post_fh->filename
-         . " >&2");
-   return;
+   my $diff = `diff -upd $source_filename $post_fh->filename  2>&1`;
+   return $diff;
 }
 
 
@@ -377,6 +376,34 @@ sub build_clean
    return;
 }
 
+sub usage
+{
+   my $message  = shift;
+   my $helptext = <<'EOF';
+Usage:
+pgindent [OPTION]... [FILE]...
+Options:
+   --help                  show this message and quit
+   --typedefs=FILE         file containing a list of typedefs
+   --list-of-typedefs=STR  string containing typedefs, space separated
+   --code-base=DIR         path to the base of PostgreSQL source code
+   --excludes=PATH         file containing list of filename patterns to ignore
+   --indent=PATH           path to pg_bsd_indent program
+   --build                 build the pg_bsd_indent program
+   --show-diff             show the changes that would be made
+   --silent-diff           exit with status 2 if any changes would be made
+EOF
+   if ($help)
+   {
+       print $helptext;
+       exit 0;
+   }
+   else
+   {
+       print STDERR "$message\n", $helptext;
+       exit 1;
+   }
+}
 
 # main
 
@@ -404,6 +431,8 @@ push(@files, @ARGV);
 
 foreach my $source_filename (@files)
 {
+   # ignore anything that's not a .c or .h file
+   next unless $source_filename =~ /\.[ch]$/;
 
    # Automatically ignore .c and .h files that correspond to a .y or .l
    # file.  indent tends to get badly confused by Bison/flex output,
@@ -427,9 +456,26 @@ foreach my $source_filename (@files)
        next;
    }
 
-   $source = post_indent($source, $source_filename);
+   $source = post_indent($source);
+
+   if ($source ne $orig_source)
+   {
+       if ($silent_diff)
+       {
+           exit 2;
+       }
+       elsif ($show_diff)
+       {
+           print show_diff($source, $source_filename);
+       }
+       else
+       {
+           write_source($source, $source_filename);
+       }
+   }
 
-   write_source($source, $source_filename) if $source ne $orig_source;
 }
 
 build_clean($code_base) if $build;
+
+exit 0;
index 1c5aedda356aff91e2f115acec00482e94702119..d9fe7273ef1dba735d11d698131118b4897c540c 100644 (file)
@@ -33,6 +33,13 @@ file can be specified using the --excludes command line option. If indenting
 a PostgreSQL source tree, this option isn't necessary, as it will find the file
 src/tools/pgindent/exclude_file_patterns.
 
+There are also two non-destructive modes of pgindent. If given the --show-diff
+option pgindent will show the changes it would make, but doesn't actually make
+them. If given instead the --silent-diff option, pgindent will exit with a
+status of 2 if it finds any indent changes are required, but will not
+make the changes or give any other information. This mode is intended for
+possible use in a git pre-commit hook.
+
 Any non-option arguments are taken as the names of files to be indented. In this
 case only these files will be changed, and nothing else will be touched. If the
 first non-option argument is not a .c or .h file, it is treated as the name