diff --git a/src/tools/pgindent/README b/src/tools/pgindent/README index fa64390baa..a89c5c214f 100644 --- a/src/tools/pgindent/README +++ b/src/tools/pgindent/README @@ -1,5 +1,3 @@ -src/tools/pgindent/README - pgindent ======== @@ -26,9 +24,7 @@ This can format all PostgreSQL *.c and *.h files, but excludes *.y, and 6) Run pgindent: - find . -name '*.[ch]' -type f -print | \ - egrep -v -f src/tools/pgindent/exclude_file_patterns | \ - xargs -n100 src/tools/pgindent/pgindent src/tools/pgindent/typedefs.list + pgindent 7) Remove any files that generate errors and restore their original versions. @@ -46,7 +42,14 @@ This can format all PostgreSQL *.c and *.h files, but excludes *.y, and 9) Indent the Perl code: - find . -name \*.pl -o -name \*.pm | + ( + find . -name \*.pl -o -name \*.pm + + find . -type f -exec file {} \; | + egrep -i ':.*perl[0-9]*\>' | + cut -d: -f1 + ) | + sort -u | xargs perltidy --profile=src/tools/pgindent/perltidyrc --------------------------------------------------------------------------- diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index 188803750a..00c9ac4755 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -1,393 +1,549 @@ -#!/bin/sh +#!/usr/bin/perl -# src/tools/pgindent/pgindent +use strict; +use warnings; -# 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); +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; -if [ "$#" -lt 2 ] -then echo "Usage: $(basename $0) typedefs file [...]" 1>&2 - exit 1 -fi +# Update for pg_bsd_indent version +Readonly my $INDENT_VERSION => "1.1"; +Readonly my $devnull => File::Spec->devnull; -TYPEDEFS="$1" -shift +# Common indent settings +my $indent_opts = + "-bad -bap -bc -bl -d0 -cdb -nce -nfc1 -di12 -i4 -l79 -lp -nip -npro -bbb"; -[ -z "$INDENT" ] && INDENT=pg_bsd_indent -INDENT_VERSION="1.1" +# indent-dependant settings +my $extra_opts = ""; -trap "rm -f /tmp/$$ /tmp/$$a" 0 1 2 3 15 +my ($typedefs_file, $code_base, $excludes, $indent, $build); -# check the environment +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"; -entab /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 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 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 +run_build($code_base) if ($build); -for FILE -do - cat "$FILE" | +# 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}; -# Convert // comments to /* */ - sed 's;^\([ ]*\)//\(.*\)$;\1/* \2 */;g' | +# build mode sets PGINDENT and PGENTAB +$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent"; +my $entab = $ENV{PGENTAB} || "entab"; -# Mark some comments for special treatment later - sed 's;/\* *---;/*---X_X;g' | +# no non-option arguments given. so do everything in the current directory +$code_base ||= '.' unless @ARGV; -# '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' | +# 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"; -# 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 | +# globals +my @files; +my $filtered_typedefs_fh; -# 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; - }' | -# 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; - }' | - -# Protect backslashes in DATA(). - sed 's;^DATA(.*$;/*&*/;' | - -# Protect wrapping in CATALOG(). - sed 's;^CATALOG(.*$;/*&*/;' >/tmp/$$a - - egrep -v '^(FD_SET|date|interval|timestamp|ANY)$' "$TYPEDEFS" | sed -e '/^$/d' > /tmp/$$b - -# 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 - - if [ "$?" -ne 0 -o -s /tmp/$$ ] - then echo - echo "$FILE" - cat /tmp/$$ - fi - cat /tmp/$$a | - -# Restore DATA/CATALOG lines. - sed 's;^/\*\(DATA(.*\)\*/$;\1;' | - sed 's;^/\*\(CATALOG(.*\)\*/$;\1;' | - -# 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' | - -# Workaround indent bug for 'static'. - sed 's;^static[ ][ ]*;static ;g' | - -# Remove too much indenting after closing brace. - sed 's;^} [ ]*;} ;' | - -# Indent single-line after-'else' comment by only one tab. - sed 's;\([} ]\)else[ ]*\(/\*.*\*/\)[ ]*$;\1else \2;g' | - -# Pull in #endif comments. - sed 's;^#endif[ ][ ]*/\*;#endif /*;' | - -# 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; - }' | + 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; + } -# 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 ' + 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; - }' | + print STDERR + "You do not appear to have 'indent' installed on your system.\n"; + exit 1; + } -# 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; - }' | - -# 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; - }' | - -# 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} + if (`$indent -V` !~ m/ $INDENT_VERSION$/) { - if ($0 ~ /^[a-zA-Z_][a-zA-Z_0-9]*[^\(]*$/) + 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 "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; - }' | + close($eh); + } +} -# 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 +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; diff --git a/src/tools/pgindent/pgindent.man b/src/tools/pgindent/pgindent.man new file mode 100644 index 0000000000..cff092ca7a --- /dev/null +++ b/src/tools/pgindent/pgindent.man @@ -0,0 +1,45 @@ +pgindent will indent .c and .h files according to the coding standards of +the PostgreSQL project. It needs several things to run, and tries to locate +or build them if possible. They can also be specified via command line switches +or the environment. + +In its simplest form, if all the required objects are installed, simply run +it without any parameters at the top of the source tree you want to process. + + pgindent + +If you don't have all the requirements installed, pgindent will fetch and build +them for you, if you're in a PostgreSQL source tree: + + + pgindent --build + +If your indent program is not installed in your path, you can specify it +by setting the environment variable INDENT, or PGINDENT, or by giving the +command line option --indent: + + pgindent --indent=/opt/extras/bsdindent + +Similarly, the entab program can be specified using the PGENTAB environment +variable, or using the --entab command line option. + +pgindent also needs a file containing a list of typedefs. This can be +specified using the PGTYPEDEFS environment variable, or via the command line +--typedefs option. If neither is used, it will look for it within the +current source tree, or in /usr/local/etc/typedefs.list. + +If you want to indent a source tree other than the current working directory, +you can specify it via the --code-base command line option. + +We don't want to indent certain files in the PostgreSQL source. pgindent +will honor a file containing a list of patterns of files to avoid. This +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. + +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 +of a typedefs file for legacy reasons, but this use is deprecated - use the +--typedefs option instead. +