#!/bin/sh # $PostgreSQL: pgsql/src/tools/pgindent/pgindent,v 1.100 2008/11/03 15:56:47 momjian Exp $ # Known bugs: # # Blank line is added after, seen as a function definition, no space # after *: # y = (int) x *y; if [ "$#" -lt 2 ] then echo "Usage: $(basename $0) typedefs file [...]" 1>&2 exit 1 fi TYPEDEFS="$1" shift trap "rm -f /tmp/$$ /tmp/$$a" 0 1 2 3 15 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 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 echo "Hope you installed /src/tools/pgindent/indent.bsd.patch." >&2 EXTRA_OPTS="-cli1" fi for FILE do cat "$FILE" | # Convert // comments to /* */ sed 's;^\([ ]*\)//\(.*\)$;\1/* \2 */;g' | # Avoid bug that converts 'x =- 1' to 'x = -1' sed 's;=- ;-= ;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; }' | # 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 # 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 \ `cat "$TYPEDEFS" | sed -e '/^$/d' -e 's/.*/-T& /'` \ /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 ' { 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 ' { 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; }' | # 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 ($0 ~ /^[a-zA-Z_][a-zA-Z_0-9]*[^\(]*$/) { 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]; } } } 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