On 2017-05-17 22:11, Tom Lane wrote:
> Piotr Stefaniak <postg...@piotr-stefaniak.me> writes:
>> The third significant issue I kept in my mind was to shift some
>> work-arounds from pgindent to indent.
> 
> Yeah, I was wondering about that too.
> 
>> When I use my indent as the base
>> for pgindent and set its options like this:
>> -bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 -lp -nip -npro -bbb
>> -cli1 -sac -ts4 -cp10
> 
> Ah, thanks, ignore the message I just sent asking for that ...
> 
>> I can remove most of the work-arounds written in the perl script and
>> still get pretty decent results. But I expect some debate over a few things.
> 
> ... but what parts of the perl script do you remove?  I'm trying
> to duplicate whatever results you're currently getting.

Full copy of my pgindent attached.  Changes commented below.


@@ -17,7 +17,7 @@ my $devnull        = File::Spec->devnull;

 # Common indent settings
 my $indent_opts =
-  "-bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 -lp -nip -npro
-bbb";
+  "-bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 -lp -nip -npro
-bbb -cli1 -sac -ts4 -cp10";

 # indent-dependent settings
 my $extra_opts = "";

The additional options are necessary. Mostly they replace the work-arounds.


@@ -198,60 +198,16 @@ sub pre_indent
 {
        my $source = shift;

-       # remove trailing whitespace
-       $source =~ s/[ \t]+$//gm;
-

I'm not sure there won't be any trailing white-space, but I've committed
changes that should limit it.


        ## Comments

        # Convert // comments to /* */
        $source =~ s!^([ \t]*)//(.*)$!$1/* $2 */!gm;

-       # 'else' followed by a single-line comment, followed by
-       # a brace on the next line confuses BSD indent, so we push
-       # the comment down to the next line, then later pull it
-       # back up again.  Add space before _PGMV or indent will add
-       # it for us.
-       # AMD: A symptom of not getting this right is that you see
errors like:
-       # FILE: ../../../src/backend/rewrite/rewriteHandler.c
-       # Error@2259:
-       # Stuff missing from end of file
-       $source =~
-         s!(\}|[ \t])else[ \t]*(/\*)(.*\*/)[ \t]*$!$1else\n    $2
_PGMV$3!gm;
-
-       # Indent multi-line after-'else' comment so BSD indent will move it
-       # properly. We already moved down single-line comments above.
-       # Check for '*' to make sure we are not in a single-line comment
that
-       # has other text on the line.
-       $source =~ s!(\}|[ \t])else[ \t]*(/\*[^*]*)[ \t]*$!$1else\n
$2!gm;

These are definitely fixed.


        ## Other

-       # Work around bug where function that defines no local variables
-       # misindents switch() case lines and line after #else.  Do not do
-       # for struct/enum.
-       my @srclines = split(/\n/, $source);
-       foreach my $lno (1 .. $#srclines)
-       {
-               my $l2 = $srclines[$lno];
-
-               # Line is only a single open brace in column 0
-               next unless $l2 =~ /^\{[ \t]*$/;
-
-               # previous line has a closing paren
-               next unless $srclines[ $lno - 1 ] =~ /\)/;
-
-               # previous line was struct, etc.
-               next
-                 if $srclines[ $lno - 1 ] =~
-                         m!=|^(struct|enum|[ \t]*typedef|extern[ \t]+"C")!;
-
-               $srclines[$lno] = "$l2\nint pgindent_func_no_var_fix;";
-       }
-       $source = join("\n", @srclines) . "\n";    # make sure there's a
final \n
-

I don't have time now to double-check, but the above shouldn't be needed
anymore.


-       # Pull up single-line comment after 'else' that was pulled down
above
-       $source =~ s!else\n[ \t]+/\* _PGMV!else\t/*!g;
-
-       # Indent single-line after-'else' comment by only one tab.
-       $source =~ s!(\}|[ \t])else[ \t]+(/\*.*\*/)[ \t]*$!$1else\t$2!gm;
-
-       # Add tab before comments with no whitespace before them (on a
tab stop)
-       $source =~ s!(\S)(/\*.*\*/)$!$1\t$2!gm;
-
-       # Remove blank line between opening brace and block comment.
-       $source =~ s!(\t*\{\n)\n([ \t]+/\*)$!$1$2!gm;
-

These are almost definitely fixed in indent.


-       # cpp conditionals
-
-       # Reduce whitespace between #endif and comments to one tab
-       $source =~ s!^\#endif[ \t]+/\*!#endif   /*!gm;
-
        ## Functions

-       # Work around misindenting of function with no variables defined.
-       $source =~ s!^[ \t]*int[ \t]+pgindent_func_no_var_fix;[
\t]*\n{1,2}!!gm;
-

These have likely been fixed.


-       ## Other
-
-       # Remove too much indenting after closing brace.
-       $source =~ s!^\}\t[ \t]+!}\t!gm;
-
-       # Workaround indent bug that places excessive space before 'static'.
-       $source =~ s!^static[ \t]+!static !gm;
-
-       # Remove leading whitespace from typedefs
-       $source =~ s!^[ \t]+typedef enum!typedef enum!gm
-         if $source_filename =~ 'libpq-(fe|events).h$';

I believe these have been fixed as well.


-       # Remove trailing blank lines
-       $source =~ s!\n+\z!\n!

I'm not sure, but shouldn't be necessary.


@@ -541,7 +462,6 @@ foreach my $source_filename (@files)

        # Protect backslashes in DATA() and wrapping in CATALOG()

-       $source = detab($source);
        $source =~ s!^((DATA|CATALOG)\(.*)$!/*$1*/!gm;

        $source = run_indent($source, \$error_message);
@@ -553,7 +473,6 @@ foreach my $source_filename (@files)

  # Restore DATA/CATALOG lines; must be done here so tab alignment is
preserved
        $source =~ s!^/\*((DATA|CATALOG)\(.*)\*/$!$1!gm;
-       $source = entab($source);

Definitely unnecessary if you use indent -ts4. There are slight differences.


Sorry for the delay, I was unprepared for answering this tonight.
#!/usr/bin/perl

use strict;
use warnings;
use 5.008001;

use Cwd qw(abs_path getcwd);
use File::Find;
use File::Spec qw(devnull);
use File::Temp;
use IO::Handle;
use Getopt::Long;

# Update for pg_bsd_indent version
my $INDENT_VERSION = "1.3";
my $devnull        = File::Spec->devnull;

# Common indent settings
my $indent_opts =
  "-bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 -lp -nip -npro -bbb 
-cli1 -sac -ts4 -cp10";

# indent-dependent settings
my $extra_opts = "";

my ($typedefs_file, $typedef_str, $code_base, $excludes, $indent, $build);

my %options = (
        "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";

run_build($code_base) if ($build);

# command line option wins, then first non-option arg,
# then environment (which is how --build sets it) ,
# then locations. based on current dir, then default location
$typedefs_file ||= shift if @ARGV && $ARGV[0] !~ /\.[ch]$/;
$typedefs_file ||= $ENV{PGTYPEDEFS};

# build mode sets PGINDENT and PGENTAB
$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent";
my $entab = $ENV{PGENTAB} || "entab";

# no non-option arguments given. so do everything in the current directory
$code_base ||= '.' unless @ARGV;

# if it's the base of a postgres tree, we will exclude the files
# postgres wants excluded
$excludes ||= "$code_base/src/tools/pgindent/exclude_file_patterns"
  if $code_base && -f "$code_base/src/tools/pgindent/exclude_file_patterns";

# globals
my @files;
my $filtered_typedefs_fh;


sub check_indent
{
        system("$entab < $devnull");
        if ($?)
        {
                print STDERR
"Go to the src/tools/entab directory and do 'make' and 'make install'.\n",
                  "This will put the 'entab' command in your path.\n",
                  "Then run $0 again.\n";
                exit 1;
        }

        system("$indent -? < $devnull > $devnull 2>&1");
        if ($? >> 8 != 1)
        {
                print STDERR
                  "You do not appear to have 'indent' installed on your 
system.\n";
                exit 1;
        }

        if (`$indent -V` !~ m/ $INDENT_VERSION$/)
        {
                print STDERR
"You do not appear to have $indent version $INDENT_VERSION installed on your 
system.\n";
                exit 1;
        }

        system("$indent -gnu < $devnull > $devnull 2>&1");
        if ($? == 0)
        {
                print STDERR
                  "You appear to have GNU indent rather than BSD indent.\n",
                  "See the pgindent/README file for a description of its 
problems.\n";
                $extra_opts = "-cdb -bli0 -npcs -cli4 -sc";
        }
        else
        {
                $extra_opts = "-cli1";
        }
}


sub load_typedefs
{

        # try fairly hard to find the typedefs file if it's not set

        foreach my $try ('.', 'src/tools/pgindent', '/usr/local/etc')
        {
                $typedefs_file ||= "$try/typedefs.list"
                  if (-f "$try/typedefs.list");
        }

        # try to find typedefs by moving up directory levels
        my $tdtry = "..";
        foreach (1 .. 5)
        {
                $typedefs_file ||= "$tdtry/src/tools/pgindent/typedefs.list"
                  if (-f "$tdtry/src/tools/pgindent/typedefs.list");
                $tdtry = "$tdtry/..";
        }
        die "cannot locate typedefs file \"$typedefs_file\"\n"
          unless $typedefs_file && -f $typedefs_file;

        open(my $typedefs_fh, '<', $typedefs_file)
          || die "cannot open typedefs file \"$typedefs_file\": $!\n";
        my @typedefs = <$typedefs_fh>;
        close($typedefs_fh);

        # add command-line-supplied typedefs?
        if (defined($typedef_str))
        {
                foreach my $typedef (split(m/[, \t\n]+/, $typedef_str))
                {
                        push(@typedefs, $typedef . "\n");
                }
        }

        # remove certain entries
        @typedefs =
          grep { !m/^(FD_SET|date|interval|timestamp|ANY)\n?$/ } @typedefs;

        # write filtered typedefs
        my $filter_typedefs_fh = new File::Temp(TEMPLATE => "pgtypedefXXXXX");
        print $filter_typedefs_fh @typedefs;
        $filter_typedefs_fh->close();

        # temp file remains because we return a file handle reference
        return $filter_typedefs_fh;
}


sub process_exclude
{
        if ($excludes && @files)
        {
                open(my $eh, '<', $excludes)
                  || die "cannot open exclude file \"$excludes\"\n";
                while (my $line = <$eh>)
                {
                        chomp $line;
                        my $rgx = qr!$line!;
                        @files = grep { $_ !~ /$rgx/ } @files if $rgx;
                }
                close($eh);
        }
}


sub read_source
{
        my $source_filename = shift;
        my $source;

        open(my $src_fd, '<', $source_filename)
          || die "cannot open file \"$source_filename\": $!\n";
        local ($/) = undef;
        $source = <$src_fd>;
        close($src_fd);

        return $source;
}


sub write_source
{
        my $source          = shift;
        my $source_filename = shift;

        open(my $src_fh, '>', $source_filename)
          || die "cannot open file \"$source_filename\": $!\n";
        print $src_fh $source;
        close($src_fh);
}


sub pre_indent
{
        my $source = shift;

        ## Comments

        # Convert // comments to /* */
        $source =~ s!^([ \t]*)//(.*)$!$1/* $2 */!gm;

        # Mark some comments for special treatment later
        $source =~ s!/\* +---!/*---X_X!g;

        ## Other

        # Prevent indenting of code in 'extern "C"' blocks.
        # we replace the braces with comments which we'll reverse later
        my $extern_c_start = '/* Open extern "C" */';
        my $extern_c_stop  = '/* Close extern "C" */';
        $source =~
s!(^#ifdef[ \t]+__cplusplus.*\nextern[ \t]+"C"[ \t]*\n)\{[ 
\t]*$!$1$extern_c_start!gm;
        $source =~ s!(^#ifdef[ \t]+__cplusplus.*\n)\}[ 
\t]*$!$1$extern_c_stop!gm;

        return $source;
}


sub post_indent
{
        my $source          = shift;
        my $source_filename = shift;

        # put back braces for extern "C"
        $source =~ s!^/\* Open extern "C" \*/$!{!gm;
        $source =~ s!^/\* Close extern "C" \*/$!}!gm;

        ## Comments

        # remove special comment marker
        $source =~ s!/\*---X_X!/* ---!g;

        ## Functions

        # Use a single space before '*' in function return types
        $source =~ s!^([A-Za-z_]\S*)[ \t]+\*$!$1 *!gm;

        #  Move prototype names to the same line as return type.  Useful
        # for ctags.  Indent should do this, but it does not.  It formats
        # prototypes just like real functions.

        my $ident   = qr/[a-zA-Z_][a-zA-Z_0-9]*/;
        my $comment = qr!/\*.*\*/!;

        $source =~ s!
                        (\n$ident[^(\n]*)\n                  # e.g. static void
                        (
                                $ident\(\n?                      # func_name(
                                (.*,([ \t]*$comment)?\n)*        # args b4 
final ln
                                .*\);([ \t]*$comment)?$          # final line
                        )
                !$1 . (substr($1,-1,1) eq '*' ? '' : ' ') . $2!gmxe;

        return $source;
}


sub run_indent
{
        my $source        = shift;
        my $error_message = shift;

        my $cmd =
          "$indent $indent_opts $extra_opts -U" . 
$filtered_typedefs_fh->filename;

        my $tmp_fh = new File::Temp(TEMPLATE => "pgsrcXXXXX");
        my $filename = $tmp_fh->filename;
        print $tmp_fh $source;
        $tmp_fh->close();

        $$error_message = `$cmd $filename 2>&1`;

        return "" if ($? || length($$error_message) > 0);

        unlink "$filename.BAK";

        open(my $src_out, '<', $filename);
        local ($/) = undef;
        $source = <$src_out>;
        close($src_out);

        return $source;

}

# XXX Ideally we'd implement entab/detab in pure perl.

sub detab
{
        my $source = shift;

        my $tmp_fh = new File::Temp(TEMPLATE => "pgdetXXXXX");
        print $tmp_fh $source;
        $tmp_fh->close();

        open(my $entab, '-|', "$entab -d -t4 -qc " . $tmp_fh->filename);
        local ($/) = undef;
        $source = <$entab>;
        close($entab);

        return $source;
}


sub entab
{
        my $source = shift;

        my $tmp_fh = new File::Temp(TEMPLATE => "pgentXXXXX");
        print $tmp_fh $source;
        $tmp_fh->close();

        open(
                my $entab,
                '-|',
                "$entab -d -t8 -qc "
                  . $tmp_fh->filename
                  . " | $entab -t4 -qc | $entab -d -t4 -m");
        local ($/) = undef;
        $source = <$entab>;
        close($entab);

        return $source;
}


# for development diagnostics
sub diff
{
        my $pre   = shift;
        my $post  = shift;
        my $flags = shift || "";

        print STDERR "running diff\n";

        my $pre_fh  = new File::Temp(TEMPLATE => "pgdiffbXXXXX");
        my $post_fh = new File::Temp(TEMPLATE => "pgdiffaXXXXX");

        print $pre_fh $pre;
        print $post_fh $post;

        $pre_fh->close();
        $post_fh->close();

        system( "diff $flags "
                  . $pre_fh->filename . " "
                  . $post_fh->filename
                  . " >&2");
}


sub run_build
{
        eval "use LWP::Simple;";  ## no critic (ProhibitStringyEval);

        my $code_base = shift || '.';
        my $save_dir = getcwd();

        # look for the code root
        foreach (1 .. 5)
        {
                last if -d "$code_base/src/tools/pgindent";
                $code_base = "$code_base/..";
        }

        die "cannot locate src/tools/pgindent directory in \"$code_base\"\n"
          unless -d "$code_base/src/tools/pgindent";

        chdir "$code_base/src/tools/pgindent";

        my $typedefs_list_url =
          "http://buildfarm.postgresql.org/cgi-bin/typedefs.pl";;

        my $rv = getstore($typedefs_list_url, "tmp_typedefs.list");

        die "cannot fetch typedefs list from $typedefs_list_url\n"
          unless is_success($rv);

        $ENV{PGTYPEDEFS} = abs_path('tmp_typedefs.list');

        my $pg_bsd_indent_url =
            "http://ftp.postgresql.org/pub/dev/pg_bsd_indent-";
          . $INDENT_VERSION
          . ".tar.gz";

        $rv = getstore($pg_bsd_indent_url, "pg_bsd_indent.tgz");

        die "cannot fetch BSD indent tarfile from $pg_bsd_indent_url\n"
          unless is_success($rv);

        # XXX add error checking here

        system("tar -z -xf pg_bsd_indent.tgz");
        chdir "pg_bsd_indent";
        system("make > $devnull 2>&1");

        $ENV{PGINDENT} = abs_path('pg_bsd_indent');

        chdir "../../entab";
        system("make > $devnull 2>&1");

        $ENV{PGENTAB} = abs_path('entab');

        chdir $save_dir;
}


sub build_clean
{
        my $code_base = shift || '.';

        # look for the code root
        foreach (1 .. 5)
        {
                last if -d "$code_base/src/tools/pgindent";
                $code_base = "$code_base/..";
        }

        die "cannot locate src/tools/pgindent directory in \"$code_base\"\n"
          unless -d "$code_base/src/tools/pgindent";

        chdir "$code_base";

        system("rm -rf src/tools/pgindent/bsdindent");
        system("git clean -q -f src/tools/entab src/tools/pgindent");
}


# main

# get the list of files under code base, if it's set
File::Find::find(
        {   wanted => sub {
                        my ($dev, $ino, $mode, $nlink, $uid, $gid);
                        (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))
                          && -f _
                          && /^.*\.[ch]\z/s
                          && push(@files, $File::Find::name);
                  }
        },
        $code_base) if $code_base;

process_exclude();

$filtered_typedefs_fh = load_typedefs();

check_indent();

# make sure we process any non-option arguments.
push(@files, @ARGV);

foreach my $source_filename (@files)
{
        my $source        = read_source($source_filename);
        my $error_message = '';

        $source = pre_indent($source);

        # Protect backslashes in DATA() and wrapping in CATALOG()

        $source =~ s!^((DATA|CATALOG)\(.*)$!/*$1*/!gm;

        $source = run_indent($source, \$error_message);
        if ($source eq "")
        {
                print STDERR "Failure in $source_filename: " . $error_message . 
"\n";
                next;
        }

 # Restore DATA/CATALOG lines; must be done here so tab alignment is preserved
        $source =~ s!^/\*((DATA|CATALOG)\(.*)\*/$!$1!gm;

        $source = post_indent($source, $source_filename);

        write_source($source, $source_filename);
}

build_clean($code_base) if $build;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to