-#!/bin/sh
-
-# src/tools/pgindent/pgindent
-
-# Known bugs:
-#
-# Blank line is added after parentheses; seen as a function definition, no space
-# after *:
-# y = (int) x *y;
-#
-# Structure/union pointers in function prototypes and definitions have an extra
-# space after the asterisk:
-#
-# void x(struct xxc * a);
-
-if [ "$#" -lt 2 ]
-then echo "Usage: $(basename $0) typedefs file [...]" 1>&2
- exit 1
-fi
-
-TYPEDEFS="$1"
-shift
-
-[ -z "$INDENT" ] && INDENT=pg_bsd_indent
-INDENT_VERSION="1.1"
-
-trap "rm -f /tmp/$$ /tmp/$$a" 0 1 2 3 15
-
-# check the environment
-
-entab </dev/null >/dev/null
-if [ "$?" -ne 0 ]
-then echo "Go to the src/tools/entab directory and do a 'make' and 'make install'." >&2
- echo "This will put the 'entab' command in your path." >&2
- echo "Then run $0 again."
- exit 1
-fi
-$INDENT -? </dev/null >/dev/null 2>&1
-if [ "$?" -ne 1 ]
-then echo "You do not appear to have '$INDENT' installed on your system." >&2
- exit 1
-fi
-if [ "`$INDENT -V`" != "$INDENT $INDENT_VERSION" ]
-then echo "You do not appear to have $INDENT version $INDENT_VERSION installed on your system." >&2
- exit 1
-fi
-$INDENT -gnu </dev/null >/dev/null 2>&1
-if [ "$?" -eq 0 ]
-then echo "You appear to have GNU indent rather than BSD indent." >&2
- echo "See the pgindent/README file for a description of its problems." >&2
- EXTRA_OPTS="-cdb -bli0 -npcs -cli4 -sc"
-else
- EXTRA_OPTS="-cli1"
-fi
-
-for FILE
-do
- cat "$FILE" |
-
-# Convert // comments to /* */
- sed 's;^\([ ]*\)//\(.*\)$;\1/* \2 */;g' |
-
-# Mark some comments for special treatment later
- sed 's;/\* *---;/*---X_X;g' |
-
-# '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.
- sed 's;\([} ]\)else[ ]*\(/\*\)\(.*\*/\)[ ]*$;\1else\
- \2 _PGMV\3;g' |
-
-# 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.
- sed 's;\([} ]\)else[ ]*\(/\*[^\*]*\)[ ]*$;\1else\
- \2;g' |
- detab -t4 -qc |
-
-# Work around bug where function that defines no local variables misindents
-# switch() case lines and line after #else. Do not do for struct/enum.
- awk ' BEGIN {line1 = ""; line2 = ""}
- {
- line2 = $0;
- if (NR >= 2)
- print line1;
- if (NR >= 2 &&
- line2 ~ /^{[ ]*$/ &&
- line1 !~ /^struct/ &&
- line1 !~ /^enum/ &&
- line1 !~ /^typedef/ &&
- line1 !~ /^extern[ ][ ]*"C"/ &&
- line1 !~ /=/ &&
- line1 ~ /\)/)
- print "int pgindent_func_no_var_fix;";
- line1 = line2;
- }
- END {
- if (NR >= 1)
- print line1;
- }' |
+#!/usr/bin/perl
-# Prevent indenting of code in 'extern "C"' blocks.
- awk ' BEGIN {line1 = ""; line2 = ""; skips = 0}
- {
- line2 = $0;
- if (skips > 0)
- skips--;
- if (line1 ~ /^#ifdef[ ]*__cplusplus/ &&
- line2 ~ /^extern[ ]*"C"[ ]*$/)
- {
- print line1;
- print line2;
- if (getline && $0 ~ /^{[ ]*$/)
- print "/* Open extern \"C\" */";
- else print $0;
- line2 = "";
- skips = 2;
- }
- else if (line1 ~ /^#ifdef[ ]*__cplusplus/ &&
- line2 ~ /^}[ ]*$/)
- {
- print line1;
- print "/* Close extern \"C\" */";
- line2 = "";
- skips = 2;
- }
- else
- if (skips == 0 && NR >= 2)
- print line1;
- line1 = line2;
- }
- END {
- if (NR >= 1 && skips <= 1)
- print line1;
- }' |
+use strict;
+use warnings;
+
+use Cwd qw(abs_path getcwd);
+use File::Find;
+use File::Spec qw(devnull);
+use File::Temp;
+use IO::Handle;
+use Getopt::Long;
+use Readonly;
-# Protect backslashes in DATA().
- sed 's;^DATA(.*$;/*&*/;' |
+# Update for pg_bsd_indent version
+Readonly my $INDENT_VERSION => "1.1";
+Readonly my $devnull => File::Spec->devnull;
-# Protect wrapping in CATALOG().
- sed 's;^CATALOG(.*$;/*&*/;' >/tmp/$$a
+# Common indent settings
+my $indent_opts =
+ "-bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 -lp -nip -npro -bbb";
- egrep -v '^(FD_SET|date|interval|timestamp|ANY)$' "$TYPEDEFS" | sed -e '/^$/d' > /tmp/$$b
+# indent-dependant settings
+my $extra_opts = "";
-# We get the list of typedef's from /src/tools/find_typedef
- $INDENT -bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 \
- -lp -nip -npro -bbb $EXTRA_OPTS -U/tmp/$$b \
- /tmp/$$a >/tmp/$$ 2>&1
+my ($typedefs_file, $code_base, $excludes, $indent, $build);
- if [ "$?" -ne 0 -o -s /tmp/$$ ]
- then echo
- echo "$FILE"
- cat /tmp/$$
- fi
- cat /tmp/$$a |
+my %options = (
+ "typedefs=s" => \$typedefs_file,
+ "code-base=s" => \$code_base,
+ "excludes=s" => \$excludes,
+ "indent=s" => \$indent,
+ "build" => \$build,);
+GetOptions(%options) || die "bad command line";
-# Restore DATA/CATALOG lines.
- sed 's;^/\*\(DATA(.*\)\*/$;\1;' |
- sed 's;^/\*\(CATALOG(.*\)\*/$;\1;' |
+run_build($code_base) if ($build);
-# Remove tabs and retab with four spaces.
- detab -t8 -qc |
- entab -t4 -qc |
- sed 's;^/\* Open extern \"C\" \*/$;{;' |
- sed 's;^/\* Close extern \"C\" \*/$;};' |
- sed 's;/\*---X_X;/* ---;g' |
+# 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};
-# Workaround indent bug for 'static'.
- sed 's;^static[ ][ ]*;static ;g' |
+# build mode sets PGINDENT and PGENTAB
+$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent";
+my $entab = $ENV{PGENTAB} || "entab";
-# Remove too much indenting after closing brace.
- sed 's;^} [ ]*;} ;' |
+# no non-option arguments given. so do everything in the current directory
+$code_base ||= '.' unless @ARGV;
-# Indent single-line after-'else' comment by only one tab.
- sed 's;\([} ]\)else[ ]*\(/\*.*\*/\)[ ]*$;\1else \2;g' |
+# 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";
-# Pull in #endif comments.
- sed 's;^#endif[ ][ ]*/\*;#endif /*;' |
+# globals
+my @files;
+my $filtered_typedefs_fh;
-# Work around misindenting of function with no variables defined.
- awk '
+
+sub check_indent
+{
+ system("entab < $devnull");
+ if ($?)
{
- if ($0 ~ /^[ ]*int[ ]*pgindent_func_no_var_fix;/)
- {
- if (getline && $0 != "")
- print $0;
- }
- else print $0;
- }' |
-
-# Add space after comments that start on tab stops.
- sed 's;\([^ ]\)\(/\*.*\*/\)$;\1 \2;' |
-
-# Move trailing * in function return type.
- sed 's;^\([A-Za-z_][^ ]*\)[ ][ ]*\*$;\1 *;' |
-
-# Remove un-needed braces around single statements.
-# Do not use because it uglifies PG_TRY/PG_CATCH blocks and probably
-# isn't needed for general use.
-# awk '
-# {
-# line3 = $0;
-# if (skips > 0)
-# skips--;
-# if (line1 ~ / *{$/ &&
-# line2 ~ / *[^;{}]*;$/ &&
-# line3 ~ / *}$/)
-# {
-# print line2;
-# line2 = "";
-# line3 = "";
-# skips = 3;
-# }
-# else
-# if (skips == 0 && NR >= 3)
-# print line1;
-# line1 = line2;
-# line2 = line3;
-# }
-# END {
-# if (NR >= 2 && skips <= 1)
-# print line1;
-# if (NR >= 1 && skips <= 2)
-# print line2;
-# }' |
-
-# Remove blank line between opening brace and block comment.
- awk '
+ 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)
{
- line3 = $0;
- if (skips > 0)
- skips--;
- if (line1 ~ / *{$/ &&
- line2 ~ /^$/ &&
- line3 ~ / *\/[*]$/)
- {
- print line1;
- print line3;
- line2 = "";
- line3 = "";
- skips = 3;
- }
- else
- if (skips == 0 && NR >= 3)
- print line1;
- line1 = line2;
- line2 = line3;
- }
- END {
- if (NR >= 2 && skips <= 1)
- print line1;
- if (NR >= 1 && skips <= 2)
- print line2;
- }' |
-
-# Pull up single-line comment after 'else' that was pulled down above
- awk '
- {
- if (NR != 1)
- {
- if ($0 ~ "/[*] _PGMV")
- {
- # remove tag
- sub(" _PGMV", "", $0);
- # remove leading whitespace
- sub("^[ ]*", "", $0);
- # add comment with single tab prefix
- print prev_line" "$0;
- # throw away current line
- getline;
- }
- else
- print prev_line;
- }
- prev_line = $0;
- }
- END {
- if (NR >= 1)
- print prev_line;
- }' |
+ print STDERR
+ "You do not appear to have 'indent' installed on your system.\n";
+ exit 1;
+ }
-# Remove trailing blank lines, helps with adding blank before trailing #endif.
- awk ' BEGIN {blank_lines = 0;}
- {
- line1 = $0;
- if (line1 ~ /^$/)
- blank_lines++;
- else
- {
- for (; blank_lines > 0; blank_lines--)
- printf "\n";
- print line1;
- }
- }' |
-
-# Remove blank line before #else, #elif, and #endif.
- awk ' BEGIN {line1 = ""; line2 = ""; skips = 0}
- {
- line2 = $0;
- if (skips > 0)
- skips--;
- if (line1 ~ /^$/ &&
- (line2 ~ /^#else/ ||
- line2 ~ /^#elif/ ||
- line2 ~ /^#endif/))
- {
- print line2;
- line2 = "";
- skips = 2;
- }
- else
- if (skips == 0 && NR >= 2)
- print line1;
- line1 = line2;
- }
- END {
- if (NR >= 1 && skips <= 1)
- print line1;
- }' |
+ 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;
+ }
-# Add blank line before #endif if it is the last line in the file.
- awk ' BEGIN {line1 = ""; line2 = ""}
- {
- line2 = $0;
- if (NR >= 2)
- print line1;
- line1 = line2;
- }
- END {
- if (NR >= 1 && line2 ~ /^#endif/)
- printf "\n";
- print line1;
- }' |
-
-# 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.
- awk ' BEGIN {paren_level = 0}
+ system("$indent -gnu < $devnull > $devnull 2>&1");
+ if ($? == 0)
{
- if ($0 ~ /^[a-zA-Z_][a-zA-Z_0-9]*[^\(]*$/)
+ 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 "no typedefs file" unless $typedefs_file && -f $typedefs_file;
+
+ open(my $typedefs_fh, '<', $typedefs_file)
+ || die "opening $typedefs_file: $!";
+ my @typedefs = <$typedefs_fh>;
+ close($typedefs_fh);
+
+ # 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 "opening $excludes";
+ while (my $line = <$eh>)
{
- saved_len = 0;
- saved_lines[++saved_len] = $0;
- if ((getline saved_lines[++saved_len]) == 0)
- print saved_lines[1];
- else
- if (saved_lines[saved_len] !~ /^[a-zA-Z_][a-zA-Z_0-9]*\(/ ||
- saved_lines[saved_len] ~ /^[a-zA-Z_][a-zA-Z_0-9]*\(.*\)$/ ||
- saved_lines[saved_len] ~ /^[a-zA-Z_][a-zA-Z_0-9]*\(.*\);$/)
- {
- print saved_lines[1];
- print saved_lines[2];
- }
- else
- {
- while (1)
- {
- if ((getline saved_lines[++saved_len]) == 0)
- break;
- if (saved_lines[saved_len] ~ /^[^ ]/ ||
- saved_lines[saved_len] !~ /,$/)
- break;
- }
- for (i=1; i <= saved_len; i++)
- {
- if (i == 1 && saved_lines[saved_len] ~ /\);$/)
- {
- printf "%s", saved_lines[i];
- if (substr(saved_lines[i], length(saved_lines[i]),1) != "*")
- printf " ";
- }
- else print saved_lines[i];
- }
- }
+ chomp $line;
+ my $rgx;
+ eval " \$rgx = qr!$line!;";
+ @files = grep { $_ !~ /$rgx/ } @files if $rgx;
}
- else print $0;
- }' |
-
-# Fix indenting of typedef caused by __cplusplus in libpq-fe.h.
- (
- if echo "$FILE" | grep -q 'libpq-fe.h$'
- then sed 's/^[ ]*typedef enum/typedef enum/'
- else cat
- fi
- ) |
-# end
- cat >/tmp/$$ && cat /tmp/$$ >"$FILE"
-done
-
-# The 'for' loop makes these backup files useless so delete them
-rm -f *a.BAK
+ close($eh);
+ }
+}
+
+
+sub read_source
+{
+ my $source_filename = shift;
+ my $source;
+
+ open(my $src_fd, '<', $source_filename)
+ || die "opening $source_filename: $!";
+ 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 "opening $source_filename: $!";
+ print $src_fh $source;
+ close($src_fh);
+}
+
+
+sub pre_indent
+{
+ my $source = shift;
+
+ # remove trailing whitespace
+ $source =~ s/\h+$//gm;
+
+ ## Comments
+
+ # Convert // comments to /* */
+ $source =~ s!^(\h*)//(.*)$!$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!(\}|\h)else\h*(/\*)(.*\*/)\h*$!$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!(\}|\h)else\h*(/\*[^*]*)\h*$!$1else\n $2!gm;
+
+ # Mark some comments for special treatment later
+ $source =~ s!/\* +---!/*---X_X!g;
+
+ ## 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 =~ /^\{\h*$/;
+
+ # previous line has a closing paren
+ next unless $srclines[ $lno - 1 ] =~ /\)/;
+
+ # previous line was struct, etc.
+ next
+ if $srclines[ $lno - 1 ] =~
+ m!=|^(struct|enum|\h*typedef|extern\h+"C")!;
+
+ $srclines[$lno] = "$l2\nint pgindent_func_no_var_fix;";
+ }
+ $source = join("\n", @srclines) . "\n"; # make sure there's a final \n
+
+ # 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\h+__cplusplus.*\nextern\h+"C"\h*\n)\{\h*$!$1$extern_c_start!gm;
+ $source =~ s!(^#ifdef\h+__cplusplus.*\n)\}\h*$!$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;
+
+ # Pull up single-line comment after 'else' that was pulled down above
+ $source =~ s!else\n\h+/\* _PGMV!else\t/*!g;
+
+ # Indent single-line after-'else' comment by only one tab.
+ $source =~ s!(\}|\h)else\h+(/\*.*\*/)\h*$!$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(\h+/\*)$!$1$2!gm;
+
+ # cpp conditionals
+
+ # Reduce whitespace between #endif and comments to one tab
+ $source =~ s!^\#endif\h+/\*!#endif /*!gm;
+
+ # Remove blank line(s) before #else, #elif, and #endif
+ $source =~ s!\n\n+(\#else|\#elif|\#endif)!\n$1!g;
+
+ # Add blank line before #endif if it is the last line in the file
+ $source =~ s!\n(#endif.*)\n\z!\n\n$1\n!;
+
+ ## Functions
+
+ # Work around misindenting of function with no variables defined.
+ $source =~ s!^\h*int\h+pgindent_func_no_var_fix;\h*\n{1,2}!!gm;
+
+ # Use a single space before '*' in function return types
+ $source =~ s!^([A-Za-z_]\S*)\h+\*$!$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(
+ (.*,(\h*$comment)?\n)* # args b4 final ln
+ .*\);(\h*$comment)?$ # final line
+ )
+ !$1 . (substr($1,-1,1) eq '*' ? '' : ' ') . $2!gmxe;
+
+ ## Other
+
+ # Remove too much indenting after closing brace.
+ $source =~ s!^\}\t\h+!}\t!gm;
+
+ # Workaround indent bug that places excessive space before 'static'.
+ $source =~ s!^static\h+!static !gm;
+
+ # Remove leading whitespace from typedefs
+ $source =~ s!^\h+typedef enum!typedef enum!gm
+ if $source_filename =~ 'libpq-(fe|events).h$';
+
+ # Remove trailing blank lines
+ $source =~ s!\n+\z!\n!;
+
+ 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");
+ 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;";
+
+ 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 "no src/tools/pgindent directory in $code_base"
+ unless -d "$code_base/src/tools/pgindent";
+
+ chdir "$code_base/src/tools/pgindent";
+
+ my $rv = getstore("http://buildfarm.postgresql.org/cgi-bin/typedefs.pl",
+ "tmp_typedefs.list");
+
+ die "fetching typedefs.list" unless is_success($rv);
+
+ $ENV{PGTYPEDEFS} = abs_path('tmp_typedefs.list');
+
+ $rv =
+ getstore("ftp://ftp.postgresql.org/pub/dev/indent.netbsd.patched.tgz",
+ "indent.netbsd.patched.tgz");
+
+ die "fetching indent.netbsd.patched.tgz" unless is_success($rv);
+
+ # XXX add error checking here
+
+ mkdir "bsdindent";
+ chdir "bsdindent";
+ system("tar -z -xf ../indent.netbsd.patched.tgz");
+ system("make > $devnull 2>&1");
+
+ $ENV{PGINDENT} = abs_path('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 "no src/tools/pgindent directory in $code_base"
+ 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 = detab($source);
+ $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 = entab($source);
+
+ $source = post_indent($source, $source_filename);
+
+ write_source($source, $source_filename);
+}
+
+build_clean($code_base) if $build;