blob: 05f69ef1f3a84e92c34fcd2db5a400c4a7472664 [file] [log] [blame]
#!/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
if [ -z "$INDENT" ]
then
INDENT=indent
fi
trap "rm -f /tmp/$$ /tmp/$$a" 0 1 2 3 15
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
$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' |
# 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 \
`egrep -v '^(FD_SET|date|interval|timestamp|ANY)$' "$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